ecoolper 8年前
コミット
897e139587
10個のファイルの変更688行の追加0行の削除
  1. +53
    -0
      weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/ApacheHttpClientBuilder.java
  2. +292
    -0
      weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/DefaultApacheHttpClientBuilder.java
  3. +28
    -0
      weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/InputStreamResponseHandler.java
  4. +76
    -0
      weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/MediaDownloadRequestExecutor.java
  5. +39
    -0
      weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/MediaUploadRequestExecutor.java
  6. +25
    -0
      weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/RequestExecutor.java
  7. +43
    -0
      weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/SimpleGetRequestExecutor.java
  8. +51
    -0
      weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/SimplePostRequestExecutor.java
  9. +48
    -0
      weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/URIUtil.java
  10. +33
    -0
      weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/Utf8ResponseHandler.java

+ 53
- 0
weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/ApacheHttpClientBuilder.java ファイルの表示

@@ -0,0 +1,53 @@
package me.chanjar.weixin.common.util.http.jodd;

import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;

/**
* httpclient build interface
* @author kakotor
*/
public interface ApacheHttpClientBuilder {

/**
* 构建httpclient实例
*
* @return new instance of CloseableHttpClient
*/
CloseableHttpClient build();

/**
* 代理服务器地址
*
* @param httpProxyHost
*/
ApacheHttpClientBuilder httpProxyHost(String httpProxyHost);

/**
* 代理服务器端口
*
* @param httpProxyPort
*/
ApacheHttpClientBuilder httpProxyPort(int httpProxyPort);

/**
* 代理服务器用户名
*
* @param httpProxyUsername
*/
ApacheHttpClientBuilder httpProxyUsername(String httpProxyUsername);

/**
* 代理服务器密码
*
* @param httpProxyPassword
*/
ApacheHttpClientBuilder httpProxyPassword(String httpProxyPassword);

/**
* ssl连接socket工厂
*
* @param sslConnectionSocketFactory
*/
ApacheHttpClientBuilder sslConnectionSocketFactory(SSLConnectionSocketFactory sslConnectionSocketFactory);
}

+ 292
- 0
weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/DefaultApacheHttpClientBuilder.java ファイルの表示

@@ -0,0 +1,292 @@
package me.chanjar.weixin.common.util.http.jodd;

import org.apache.commons.lang3.StringUtils;
import org.apache.http.annotation.NotThreadSafe;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.HttpClientConnectionManager;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.protocol.HttpContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

/**
* httpclient 连接管理器
*
* @author kakotor
*/
@NotThreadSafe
public class DefaultApacheHttpClientBuilder implements ApacheHttpClientBuilder {
protected final Logger log = LoggerFactory.getLogger(DefaultApacheHttpClientBuilder.class);
private final AtomicBoolean prepared = new AtomicBoolean(false);
private int connectionRequestTimeout = 3000;
private int connectionTimeout = 5000;
private int soTimeout = 5000;
private int idleConnTimeout = 60000;
private int checkWaitTime = 60000;
private int maxConnPerHost = 10;
private int maxTotalConn = 50;
private String userAgent;
private HttpRequestRetryHandler httpRequestRetryHandler = new HttpRequestRetryHandler() {
@Override
public boolean retryRequest(IOException exception, int executionCount, HttpContext context) {
return false;
}
};
private SSLConnectionSocketFactory sslConnectionSocketFactory = SSLConnectionSocketFactory.getSocketFactory();
private PlainConnectionSocketFactory plainConnectionSocketFactory = PlainConnectionSocketFactory.getSocketFactory();
private String httpProxyHost;
private int httpProxyPort;
private String httpProxyUsername;
private String httpProxyPassword;
/**
* 闲置连接监控线程
*/
private IdleConnectionMonitorThread idleConnectionMonitorThread;
private HttpClientBuilder httpClientBuilder;

private DefaultApacheHttpClientBuilder() {
}

public static DefaultApacheHttpClientBuilder get() {
return new DefaultApacheHttpClientBuilder();
}

@Override
public ApacheHttpClientBuilder httpProxyHost(String httpProxyHost) {
this.httpProxyHost = httpProxyHost;
return this;
}

@Override
public ApacheHttpClientBuilder httpProxyPort(int httpProxyPort) {
this.httpProxyPort = httpProxyPort;
return this;
}

@Override
public ApacheHttpClientBuilder httpProxyUsername(String httpProxyUsername) {
this.httpProxyUsername = httpProxyUsername;
return this;
}

@Override
public ApacheHttpClientBuilder httpProxyPassword(String httpProxyPassword) {
this.httpProxyPassword = httpProxyPassword;
return this;
}

@Override
public ApacheHttpClientBuilder sslConnectionSocketFactory(SSLConnectionSocketFactory sslConnectionSocketFactory) {
this.sslConnectionSocketFactory = sslConnectionSocketFactory;
return this;
}

/**
* 获取链接的超时时间设置,默认3000ms
* <p>
* 设置为零时不超时,一直等待.
* 设置为负数是使用系统默认设置(非上述的3000ms的默认值,而是httpclient的默认设置).
* </p>
*
* @param connectionRequestTimeout 获取链接的超时时间设置(单位毫秒),默认3000ms
*/
public void setConnectionRequestTimeout(int connectionRequestTimeout) {
this.connectionRequestTimeout = connectionRequestTimeout;
}

/**
* 建立链接的超时时间,默认为5000ms.由于是在链接池获取链接,此设置应该并不起什么作用
* <p>
* 设置为零时不超时,一直等待.
* 设置为负数是使用系统默认设置(非上述的5000ms的默认值,而是httpclient的默认设置).
* </p>
*
* @param connectionTimeout 建立链接的超时时间设置(单位毫秒),默认5000ms
*/
public void setConnectionTimeout(int connectionTimeout) {
this.connectionTimeout = connectionTimeout;
}

/**
* 默认NIO的socket超时设置,默认5000ms.
*
* @param soTimeout 默认NIO的socket超时设置,默认5000ms.
* @see java.net.SocketOptions#SO_TIMEOUT
*/
public void setSoTimeout(int soTimeout) {
this.soTimeout = soTimeout;
}

/**
* 空闲链接的超时时间,默认60000ms.
* <p>
* 超时的链接将在下一次空闲链接检查是被销毁
* </p>
*
* @param idleConnTimeout 空闲链接的超时时间,默认60000ms.
*/
public void setIdleConnTimeout(int idleConnTimeout) {
this.idleConnTimeout = idleConnTimeout;
}

/**
* 检查空间链接的间隔周期,默认60000ms.
*
* @param checkWaitTime 检查空间链接的间隔周期,默认60000ms.
*/
public void setCheckWaitTime(int checkWaitTime) {
this.checkWaitTime = checkWaitTime;
}

/**
* 每路的最大链接数,默认10
*
* @param maxConnPerHost 每路的最大链接数,默认10
*/
public void setMaxConnPerHost(int maxConnPerHost) {
this.maxConnPerHost = maxConnPerHost;
}

/**
* 最大总连接数,默认50
*
* @param maxTotalConn 最大总连接数,默认50
*/
public void setMaxTotalConn(int maxTotalConn) {
this.maxTotalConn = maxTotalConn;
}

/**
* 自定义httpclient的User Agent
*
* @param userAgent User Agent
*/
public void setUserAgent(String userAgent) {
this.userAgent = userAgent;
}

public IdleConnectionMonitorThread getIdleConnectionMonitorThread() {
return this.idleConnectionMonitorThread;
}

private synchronized void prepare() {
if(prepared.get()){
return;
}
Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create()
.register("http", this.plainConnectionSocketFactory)
.register("https", this.sslConnectionSocketFactory)
.build();

@SuppressWarnings("resource")
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(registry);
connectionManager.setMaxTotal(this.maxTotalConn);
connectionManager.setDefaultMaxPerRoute(this.maxConnPerHost);
connectionManager.setDefaultSocketConfig(
SocketConfig.copy(SocketConfig.DEFAULT)
.setSoTimeout(this.soTimeout)
.build()
);

this.idleConnectionMonitorThread = new IdleConnectionMonitorThread(
connectionManager, this.idleConnTimeout, this.checkWaitTime);
this.idleConnectionMonitorThread.setDaemon(true);
this.idleConnectionMonitorThread.start();

this.httpClientBuilder = HttpClients.custom()
.setConnectionManager(connectionManager)
.setConnectionManagerShared(true)
.setDefaultRequestConfig(
RequestConfig.custom()
.setSocketTimeout(this.soTimeout)
.setConnectTimeout(this.connectionTimeout)
.setConnectionRequestTimeout(this.connectionRequestTimeout)
.build()
)
.setRetryHandler(this.httpRequestRetryHandler);

if (StringUtils.isNotBlank(this.httpProxyHost)
&& StringUtils.isNotBlank(this.httpProxyUsername)) {
// 使用代理服务器 需要用户认证的代理服务器
CredentialsProvider provider = new BasicCredentialsProvider();
provider.setCredentials(
new AuthScope(this.httpProxyHost, this.httpProxyPort),
new UsernamePasswordCredentials(this.httpProxyUsername,
this.httpProxyPassword));
this.httpClientBuilder.setDefaultCredentialsProvider(provider);
}

if (StringUtils.isNotBlank(this.userAgent)) {
this.httpClientBuilder.setUserAgent(this.userAgent);
}
prepared.set(true);
}

@Override
public CloseableHttpClient build() {
if(!prepared.get()){
prepare();
}
return this.httpClientBuilder.build();
}

public static class IdleConnectionMonitorThread extends Thread {
private final HttpClientConnectionManager connMgr;
private final int idleConnTimeout;
private final int checkWaitTime;
private volatile boolean shutdown;

public IdleConnectionMonitorThread(HttpClientConnectionManager connMgr, int idleConnTimeout, int checkWaitTime) {
super("IdleConnectionMonitorThread");
this.connMgr = connMgr;
this.idleConnTimeout = idleConnTimeout;
this.checkWaitTime = checkWaitTime;
}

@Override
public void run() {
try {
while (!this.shutdown) {
synchronized (this) {
wait(this.checkWaitTime);
this.connMgr.closeExpiredConnections();
this.connMgr.closeIdleConnections(this.idleConnTimeout,
TimeUnit.MILLISECONDS);
}
}
} catch (InterruptedException ignore) {
}
}

public void trigger() {
synchronized (this) {
notifyAll();
}
}

public void shutdown() {
this.shutdown = true;
synchronized (this) {
notifyAll();
}
}
}
}

+ 28
- 0
weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/InputStreamResponseHandler.java ファイルの表示

@@ -0,0 +1,28 @@
package me.chanjar.weixin.common.util.http.jodd;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpResponseException;
import org.apache.http.client.ResponseHandler;
import org.apache.http.util.EntityUtils;

import java.io.IOException;
import java.io.InputStream;

public class InputStreamResponseHandler implements ResponseHandler<InputStream> {

public static final ResponseHandler<InputStream> INSTANCE = new InputStreamResponseHandler();

@Override
public InputStream handleResponse(final HttpResponse response) throws IOException {
final StatusLine statusLine = response.getStatusLine();
final HttpEntity entity = response.getEntity();
if (statusLine.getStatusCode() >= 300) {
EntityUtils.consume(entity);
throw new HttpResponseException(statusLine.getStatusCode(), statusLine.getReasonPhrase());
}
return entity == null ? null : entity.getContent();
}

}

+ 76
- 0
weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/MediaDownloadRequestExecutor.java ファイルの表示

@@ -0,0 +1,76 @@
package me.chanjar.weixin.common.util.http.jodd;

import jodd.http.HttpRequest;
import jodd.http.HttpResponse;
import jodd.http.ProxyInfo;
import me.chanjar.weixin.common.bean.result.WxError;
import me.chanjar.weixin.common.exception.WxErrorException;
import me.chanjar.weixin.common.util.fs.FileUtils;
import org.apache.commons.lang3.StringUtils;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* 下载媒体文件请求执行器,请求的参数是String, 返回的结果是File
* 视频文件不支持下载
*
* @author Daniel Qian
*/
public class MediaDownloadRequestExecutor implements RequestExecutor<File, String> {

private File tmpDirFile;

public MediaDownloadRequestExecutor() {
}

public MediaDownloadRequestExecutor(File tmpDirFile) {
this.tmpDirFile = tmpDirFile;
}

@Override
public File execute(ProxyInfo httpProxy, String uri, String queryParam) throws WxErrorException, IOException {
if (queryParam != null) {
if (uri.indexOf('?') == -1) {
uri += '?';
}
uri += uri.endsWith("?") ? queryParam : '&' + queryParam;
}

HttpRequest httpRequest = HttpRequest.post(uri);
HttpResponse response = httpRequest.send();
String contentType = response.header("Content-Type");
if (contentType != null && contentType.startsWith("application/json")) {
// application/json; encoding=utf-8 下载媒体文件出错
throw new WxErrorException(WxError.fromJson(response.bodyText()));
}

String fileName = getFileName(response);
if (StringUtils.isBlank(fileName)) {
return null;
}

InputStream inputStream = new ByteArrayInputStream(response.bodyBytes());
String[] nameAndExt = fileName.split("\\.");
return FileUtils.createTmpFile(inputStream, nameAndExt[0], nameAndExt[1], this.tmpDirFile);
}

private String getFileName(HttpResponse response) throws WxErrorException {
String content = response.header("Content-disposition");
if (content == null || content.length() == 0) {
throw new WxErrorException(WxError.newBuilder().setErrorMsg("无法获取到文件名").build());
}

Pattern p = Pattern.compile(".*filename=\"(.*)\"");
Matcher m = p.matcher(content);
if (m.matches()) {
return m.group(1);
}
throw new WxErrorException(WxError.newBuilder().setErrorMsg("无法获取到文件名").build());
}

}

+ 39
- 0
weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/MediaUploadRequestExecutor.java ファイルの表示

@@ -0,0 +1,39 @@
package me.chanjar.weixin.common.util.http.jodd;

import jodd.http.HttpRequest;
import jodd.http.HttpResponse;
import jodd.http.ProxyInfo;
import jodd.http.net.SocketHttpConnectionProvider;
import me.chanjar.weixin.common.bean.result.WxError;
import me.chanjar.weixin.common.bean.result.WxMediaUploadResult;
import me.chanjar.weixin.common.exception.WxErrorException;

import java.io.File;
import java.io.IOException;

/**
* 上传媒体文件请求执行器,请求的参数是File, 返回的结果是String
*
* @author Daniel Qian
*/
public class MediaUploadRequestExecutor implements RequestExecutor<WxMediaUploadResult, File> {

@Override
public WxMediaUploadResult execute(ProxyInfo httpProxy, String uri, File file) throws WxErrorException, IOException {
HttpRequest request = HttpRequest.post(uri);
if (httpProxy != null) {
SocketHttpConnectionProvider provider = new SocketHttpConnectionProvider();
provider.useProxy(httpProxy);
request.withConnectionProvider(provider);
}
request.form("media", file);
HttpResponse response = request.send();
String responseContent =response.bodyText();
WxError error = WxError.fromJson(responseContent);
if (error.getErrorCode() != 0) {
throw new WxErrorException(error);
}
return WxMediaUploadResult.fromJson(responseContent);
}

}

+ 25
- 0
weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/RequestExecutor.java ファイルの表示

@@ -0,0 +1,25 @@
package me.chanjar.weixin.common.util.http.jodd;

import jodd.http.ProxyInfo;
import me.chanjar.weixin.common.exception.WxErrorException;

import java.io.IOException;

/**
* http请求执行器
*
* @param <T> 返回值类型
* @param <E> 请求参数类型
*/
public interface RequestExecutor<T, E> {

/**
* @param httpProxy http代理对象,如果没有配置代理则为空
* @param uri uri
* @param data 数据
* @throws WxErrorException
* @throws IOException
*/
T execute(ProxyInfo httpProxy, String uri, E data) throws WxErrorException, IOException;

}

+ 43
- 0
weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/SimpleGetRequestExecutor.java ファイルの表示

@@ -0,0 +1,43 @@
package me.chanjar.weixin.common.util.http.jodd;

import jodd.http.HttpRequest;
import jodd.http.HttpResponse;
import jodd.http.ProxyInfo;
import jodd.http.net.SocketHttpConnectionProvider;
import me.chanjar.weixin.common.bean.result.WxError;
import me.chanjar.weixin.common.exception.WxErrorException;

import java.io.IOException;

/**
* 简单的GET请求执行器,请求的参数是String, 返回的结果也是String
*
* @author Daniel Qian
*/
public class SimpleGetRequestExecutor implements RequestExecutor<String, String> {

@Override
public String execute(ProxyInfo httpProxy, String uri, String queryParam) throws WxErrorException, IOException {
if (queryParam != null) {
if (uri.indexOf('?') == -1) {
uri += '?';
}
uri += uri.endsWith("?") ? queryParam : '&' + queryParam;
}

HttpRequest request = HttpRequest.get(uri);
if (httpProxy != null) {
SocketHttpConnectionProvider provider = new SocketHttpConnectionProvider();
provider.useProxy(httpProxy);
request.withConnectionProvider(provider);
}
HttpResponse response = request.send();
String responseContent = response.bodyText();
WxError error = WxError.fromJson(responseContent);
if (error.getErrorCode() != 0) {
throw new WxErrorException(error);
}
return responseContent;
}

}

+ 51
- 0
weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/SimplePostRequestExecutor.java ファイルの表示

@@ -0,0 +1,51 @@
package me.chanjar.weixin.common.util.http.jodd;

import jodd.http.HttpRequest;
import jodd.http.HttpResponse;
import jodd.http.ProxyInfo;
import jodd.http.net.SocketHttpConnectionProvider;
import me.chanjar.weixin.common.bean.result.WxError;
import me.chanjar.weixin.common.exception.WxErrorException;

import java.io.IOException;

/**
* 简单的POST请求执行器,请求的参数是String, 返回的结果也是String
*
* @author Daniel Qian
*/
public class SimplePostRequestExecutor implements RequestExecutor<String, String> {

@Override
public String execute(ProxyInfo httpProxy, String uri, String postEntity) throws WxErrorException, IOException {
HttpRequest request = HttpRequest.post(uri);
if (httpProxy != null) {
SocketHttpConnectionProvider provider = new SocketHttpConnectionProvider();
provider.useProxy(httpProxy);
request.withConnectionProvider(provider);
}
if (postEntity != null) {
request.bodyText(postEntity);
}
HttpResponse response = request.send();

String responseContent = response.bodyText();
if (responseContent.isEmpty()) {
throw new WxErrorException(
WxError.newBuilder().setErrorCode(9999).setErrorMsg("无响应内容")
.build());
}

if (responseContent.startsWith("<xml>")) {
//xml格式输出直接返回
return responseContent;
}

WxError error = WxError.fromJson(responseContent);
if (error.getErrorCode() != 0) {
throw new WxErrorException(error);
}
return responseContent;
}

}

+ 48
- 0
weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/URIUtil.java ファイルの表示

@@ -0,0 +1,48 @@
package me.chanjar.weixin.common.util.http.jodd;


import org.apache.commons.lang3.StringUtils;

import java.io.UnsupportedEncodingException;

public class URIUtil {

private static final String ALLOWED_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_.!~*'()";

public static String encodeURIComponent(String input) {
if (StringUtils.isEmpty(input)) {
return input;
}

int l = input.length();
StringBuilder o = new StringBuilder(l * 3);
try {
for (int i = 0; i < l; i++) {
String e = input.substring(i, i + 1);
if (ALLOWED_CHARS.indexOf(e) == -1) {
byte[] b = e.getBytes("utf-8");
o.append(getHex(b));
continue;
}
o.append(e);
}
return o.toString();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return input;
}

private static String getHex(byte buf[]) {
StringBuilder o = new StringBuilder(buf.length * 3);
for (int i = 0; i < buf.length; i++) {
int n = buf[i] & 0xff;
o.append("%");
if (n < 0x10) {
o.append("0");
}
o.append(Long.toString(n, 16).toUpperCase());
}
return o.toString();
}
}

+ 33
- 0
weixin-java-common/src/main/java/me/chanjar/weixin/common/util/http/jodd/Utf8ResponseHandler.java ファイルの表示

@@ -0,0 +1,33 @@
package me.chanjar.weixin.common.util.http.jodd;

import org.apache.http.Consts;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpResponseException;
import org.apache.http.client.ResponseHandler;
import org.apache.http.util.EntityUtils;

import java.io.IOException;

/**
* copy from {@link org.apache.http.impl.client.BasicResponseHandler}
*
* @author Daniel Qian
*/
public class Utf8ResponseHandler implements ResponseHandler<String> {

public static final ResponseHandler<String> INSTANCE = new Utf8ResponseHandler();

@Override
public String handleResponse(final HttpResponse response) throws IOException {
final StatusLine statusLine = response.getStatusLine();
final HttpEntity entity = response.getEntity();
if (statusLine.getStatusCode() >= 300) {
EntityUtils.consume(entity);
throw new HttpResponseException(statusLine.getStatusCode(), statusLine.getReasonPhrase());
}
return entity == null ? null : EntityUtils.toString(entity, Consts.UTF_8);
}

}

読み込み中…
キャンセル
保存