| @@ -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); | |||||
| } | |||||
| @@ -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(); | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| @@ -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(); | |||||
| } | |||||
| } | |||||
| @@ -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()); | |||||
| } | |||||
| } | |||||
| @@ -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); | |||||
| } | |||||
| } | |||||
| @@ -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; | |||||
| } | |||||
| @@ -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; | |||||
| } | |||||
| } | |||||
| @@ -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; | |||||
| } | |||||
| } | |||||
| @@ -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(); | |||||
| } | |||||
| } | |||||
| @@ -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); | |||||
| } | |||||
| } | |||||