Skip to content

初步引入 Apache HttpClient 5.x #3604

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,12 @@
<version>4.5.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<version>5.5</version>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>org.apache.httpcomponents</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import me.chanjar.weixin.channel.bean.token.StableTokenParam;
import me.chanjar.weixin.channel.config.WxChannelConfig;
import me.chanjar.weixin.channel.util.JsonUtils;
import me.chanjar.weixin.common.util.http.HttpType;
import me.chanjar.weixin.common.util.http.HttpClientType;
import me.chanjar.weixin.common.util.http.apache.ApacheBasicResponseHandler;
import me.chanjar.weixin.common.util.http.apache.ApacheHttpClientBuilder;
import me.chanjar.weixin.common.util.http.apache.DefaultApacheHttpClientBuilder;
Expand Down Expand Up @@ -63,8 +63,8 @@ public HttpHost getRequestHttpProxy() {
}

@Override
public HttpType getRequestType() {
return HttpType.APACHE_HTTP;
public HttpClientType getRequestType() {
return HttpClientType.APACHE_HTTP;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import me.chanjar.weixin.channel.bean.token.StableTokenParam;
import me.chanjar.weixin.channel.config.WxChannelConfig;
import me.chanjar.weixin.channel.util.JsonUtils;
import me.chanjar.weixin.common.util.http.HttpType;
import me.chanjar.weixin.common.util.http.HttpClientType;
import me.chanjar.weixin.common.util.http.okhttp.DefaultOkHttpClientBuilder;
import me.chanjar.weixin.common.util.http.okhttp.OkHttpProxyInfo;
import okhttp3.Authenticator;
Expand Down Expand Up @@ -65,8 +65,8 @@ public OkHttpProxyInfo getRequestHttpProxy() {
}

@Override
public HttpType getRequestType() {
return HttpType.OK_HTTP;
public HttpClientType getRequestType() {
return HttpClientType.OK_HTTP;
}

@Override
Expand Down
5 changes: 5 additions & 0 deletions weixin-java-common/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@
<artifactId>okhttp</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents.client5</groupId>
<artifactId>httpclient5</artifactId>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>org.slf4j</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
/**
* Created by ecoolper on 2017/4/28.
*/
public enum HttpType {
public enum HttpClientType {
/**
* jodd-http.
*/
Expand All @@ -15,5 +15,9 @@ public enum HttpType {
/**
* okhttp.
*/
OK_HTTP
OK_HTTP,
/**
* apache httpclient5.
*/
APACHE_HTTP_5
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
package me.chanjar.weixin.common.util.http;

import jodd.http.HttpResponse;
import me.chanjar.weixin.common.error.WxErrorException;
import okhttp3.Response;
import org.apache.http.Header;
import org.apache.http.client.methods.CloseableHttpResponse;
import me.chanjar.weixin.common.util.http.apache.ApacheHttpResponseProxy;
import me.chanjar.weixin.common.util.http.hc5.ApacheHttpClient5ResponseProxy;
import me.chanjar.weixin.common.util.http.jodd.JoddHttpResponseProxy;
import me.chanjar.weixin.common.util.http.okhttp.OkHttpResponseProxy;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
Expand All @@ -14,68 +14,33 @@

/**
* <pre>
* 三种http框架的response代理类,方便提取公共方法
* http 框架的 response 代理类,方便提取公共方法
* Created by Binary Wang on 2017-8-3.
* </pre>
*
* @author <a href="https://github.com/binarywang">Binary Wang</a>
*/
public class HttpResponseProxy {
public interface HttpResponseProxy {

private CloseableHttpResponse apacheHttpResponse;
private HttpResponse joddHttpResponse;
private Response okHttpResponse;

public HttpResponseProxy(CloseableHttpResponse apacheHttpResponse) {
this.apacheHttpResponse = apacheHttpResponse;
}

public HttpResponseProxy(HttpResponse joddHttpResponse) {
this.joddHttpResponse = joddHttpResponse;
static ApacheHttpResponseProxy from(org.apache.http.client.methods.CloseableHttpResponse response) {
return new ApacheHttpResponseProxy(response);
}

public HttpResponseProxy(Response okHttpResponse) {
this.okHttpResponse = okHttpResponse;
}

public String getFileName() throws WxErrorException {
//由于对象只能由一个构造方法实现,因此三个response对象必定且只有一个不为空
if (this.apacheHttpResponse != null) {
return this.getFileName(this.apacheHttpResponse);
}

if (this.joddHttpResponse != null) {
return this.getFileName(this.joddHttpResponse);
}

if (this.okHttpResponse != null) {
return this.getFileName(this.okHttpResponse);
}

//cannot happen
return null;
static ApacheHttpClient5ResponseProxy from(org.apache.hc.client5.http.impl.classic.CloseableHttpResponse response) {
return new ApacheHttpClient5ResponseProxy(response);
}

private String getFileName(CloseableHttpResponse response) throws WxErrorException {
Header[] contentDispositionHeader = response.getHeaders("Content-disposition");
if (contentDispositionHeader == null || contentDispositionHeader.length == 0) {
throw new WxErrorException("无法获取到文件名,Content-disposition为空");
}

return extractFileNameFromContentString(contentDispositionHeader[0].getValue());
static JoddHttpResponseProxy from(jodd.http.HttpResponse response) {
return new JoddHttpResponseProxy(response);
}

private String getFileName(HttpResponse response) throws WxErrorException {
String content = response.header("Content-disposition");
return extractFileNameFromContentString(content);
static OkHttpResponseProxy from(okhttp3.Response response) {
return new OkHttpResponseProxy(response);
}

private String getFileName(Response response) throws WxErrorException {
String content = response.header("Content-disposition");
return extractFileNameFromContentString(content);
}
String getFileName() throws WxErrorException;

public static String extractFileNameFromContentString(String content) throws WxErrorException {
default String extractFileNameFromContentString(String content) throws WxErrorException {
if (content == null || content.isEmpty()) {
throw new WxErrorException("无法获取到文件名,content为空");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,6 @@ public interface RequestHttp<H, P> {
*
* @return HttpType
*/
HttpType getRequestType();
HttpClientType getRequestType();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package me.chanjar.weixin.common.util.http.apache;

import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.util.http.HttpResponseProxy;
import org.apache.http.Header;
import org.apache.http.client.methods.CloseableHttpResponse;

public class ApacheHttpResponseProxy implements HttpResponseProxy {

private final CloseableHttpResponse httpResponse;

public ApacheHttpResponseProxy(CloseableHttpResponse closeableHttpResponse) {
this.httpResponse = closeableHttpResponse;
}

@Override
public String getFileName() throws WxErrorException {
Header[] contentDispositionHeader = this.httpResponse.getHeaders("Content-disposition");
if (contentDispositionHeader == null || contentDispositionHeader.length == 0) {
throw new WxErrorException("无法获取到文件名,Content-disposition为空");
}

return extractFileNameFromContentString(contentDispositionHeader[0].getValue());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public File execute(String uri, String queryParam, WxType wxType) throws WxError
}
}

String fileName = new HttpResponseProxy(response).getFileName();
String fileName = HttpResponseProxy.from(response).getFileName();
if (StringUtils.isBlank(fileName)) {
fileName = String.valueOf(System.currentTimeMillis());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package me.chanjar.weixin.common.util.http.hc5;

import org.apache.hc.client5.http.impl.classic.BasicHttpClientResponseHandler;

/**
* ApacheBasicResponseHandler
*
* @author altusea
*/
public class ApacheBasicResponseHandler extends BasicHttpClientResponseHandler {

public static final ApacheBasicResponseHandler INSTANCE = new ApacheBasicResponseHandler();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package me.chanjar.weixin.common.util.http.hc5;

import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.util.http.HttpResponseProxy;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.core5.http.Header;

public class ApacheHttpClient5ResponseProxy implements HttpResponseProxy {

private final CloseableHttpResponse response;

public ApacheHttpClient5ResponseProxy(CloseableHttpResponse closeableHttpResponse) {
this.response = closeableHttpResponse;
}

@Override
public String getFileName() throws WxErrorException {
Header[] contentDispositionHeader = this.response.getHeaders("Content-disposition");
if (contentDispositionHeader == null || contentDispositionHeader.length == 0) {
throw new WxErrorException("无法获取到文件名,Content-disposition为空");
}

return extractFileNameFromContentString(contentDispositionHeader[0].getValue());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package me.chanjar.weixin.common.util.http.hc5;

import org.apache.hc.client5.http.ConnectionKeepAliveStrategy;
import org.apache.hc.client5.http.HttpRequestRetryStrategy;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;

/**
* httpclient build interface.
*
* @author altusea
*/
public interface ApacheHttpClientBuilder {

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

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

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

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

/**
* 代理服务器密码.
*/
ApacheHttpClientBuilder httpProxyPassword(char[] httpProxyPassword);

/**
* 重试策略.
*/
ApacheHttpClientBuilder httpRequestRetryStrategy(HttpRequestRetryStrategy httpRequestRetryStrategy);

/**
* 超时时间.
*/
ApacheHttpClientBuilder keepAliveStrategy(ConnectionKeepAliveStrategy keepAliveStrategy);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package me.chanjar.weixin.common.util.http.hc5;

import me.chanjar.weixin.common.enums.WxType;
import me.chanjar.weixin.common.error.WxError;
import me.chanjar.weixin.common.error.WxErrorException;
import me.chanjar.weixin.common.util.fs.FileUtils;
import me.chanjar.weixin.common.util.http.BaseMediaDownloadRequestExecutor;
import me.chanjar.weixin.common.util.http.HttpResponseProxy;
import me.chanjar.weixin.common.util.http.RequestHttp;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.hc.client5.http.ClientProtocolException;
import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.config.RequestConfig;
import org.apache.hc.client5.http.impl.classic.CloseableHttpClient;
import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse;
import org.apache.hc.core5.http.ContentType;
import org.apache.hc.core5.http.Header;
import org.apache.hc.core5.http.HttpException;
import org.apache.hc.core5.http.HttpHost;

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

/**
* ApacheMediaDownloadRequestExecutor
*
* @author altusea
*/
public class ApacheMediaDownloadRequestExecutor extends BaseMediaDownloadRequestExecutor<CloseableHttpClient, HttpHost> {

public ApacheMediaDownloadRequestExecutor(RequestHttp<CloseableHttpClient, HttpHost> requestHttp, File tmpDirFile) {
super(requestHttp, tmpDirFile);
}

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

HttpGet httpGet = new HttpGet(uri);
if (requestHttp.getRequestHttpProxy() != null) {
RequestConfig config = RequestConfig.custom().setProxy(requestHttp.getRequestHttpProxy()).build();
httpGet.setConfig(config);
}

try (CloseableHttpResponse response = requestHttp.getRequestHttpClient().execute(httpGet);
InputStream inputStream = InputStreamResponseHandler.INSTANCE.handleResponse(response)) {
Header[] contentTypeHeader = response.getHeaders("Content-Type");
if (contentTypeHeader != null && contentTypeHeader.length > 0) {
if (contentTypeHeader[0].getValue().startsWith(ContentType.APPLICATION_JSON.getMimeType())) {
// application/json; encoding=utf-8 下载媒体文件出错
String responseContent = Utf8ResponseHandler.INSTANCE.handleResponse(response);
throw new WxErrorException(WxError.fromJson(responseContent, wxType));
}
}

String fileName = HttpResponseProxy.from(response).getFileName();
if (StringUtils.isBlank(fileName)) {
fileName = String.valueOf(System.currentTimeMillis());
}

String baseName = FilenameUtils.getBaseName(fileName);
if (StringUtils.isBlank(fileName) || baseName.length() < 3) {
baseName = String.valueOf(System.currentTimeMillis());
}

return FileUtils.createTmpFile(inputStream, baseName, FilenameUtils.getExtension(fileName), super.tmpDirFile);
} catch (final HttpException httpException) {
throw new ClientProtocolException(httpException.getMessage(), httpException);
}
}

}
Loading