对Netty-Http-Client的支持

This commit is contained in:
jinyu 2015-09-10 21:28:50 +08:00
parent 8bef11f99b
commit bb18b98538
27 changed files with 889 additions and 212 deletions

View File

@ -445,8 +445,16 @@
+ `release`: weixin4j-[mp|qy] upgrade to 1.5.3,weixin4j-server upgrade to 1.0.5
+ **weixin4j-[mp|qy]**: 媒体接口类(MediaApi)查询素材接口调整:去掉offset,count替换为Pageable类
+ **weixin4j-[mp|qy]**: 媒体接口类(MediaApi)查询素材接口调整:去掉offset,count替换为Pageable类
* 2015-08-18
+ 比较大的改动:重构了HttpClient部分
+ 比较大的改动:重构了HttpClient部分
* 2015-09-08
+ weixin4j-mp:新增批量获取用户信息接口
* 2015-09-10
+ 对Netty-Http-Client的支持

View File

@ -29,5 +29,11 @@
<version>4.3</version>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.0.30.Final</version>
<optional>true</optional>
</dependency>
</dependencies>
</project>

View File

@ -106,4 +106,25 @@ public abstract class AbstractHttpClient implements HttpClient {
}
return execute(new HttpRequest(method, buf.toString()));
}
protected boolean hasError(HttpStatus status) {
return (status.series() == HttpStatus.Series.CLIENT_ERROR || status
.series() == HttpStatus.Series.SERVER_ERROR);
}
protected void handleResponse(HttpResponse response)
throws HttpClientException {
HttpStatus status = response.getStatus();
if (hasError(status)) {
switch (status.series()) {
case CLIENT_ERROR:
case SERVER_ERROR:
throw new HttpClientException(String.format("%d %s",
status.getStatusCode(), status.getStatusText()));
default:
throw new HttpClientException("Unknown status code [" + status
+ "]");
}
}
}
}

View File

@ -0,0 +1,31 @@
package com.foxinmy.weixin4j.http;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
/**
*
* @className AbstractHttpResponse
* @author jy
* @date 2015年9月7日
* @since JDK 1.7
* @see
*/
public abstract class AbstractHttpResponse implements HttpResponse {
private final byte[] content;
public AbstractHttpResponse(byte[] content) {
this.content = content;
}
@Override
public byte[] getContent() {
return content;
}
@Override
public InputStream getBody() {
return content != null ? new ByteArrayInputStream(content) : null;
}
}

View File

@ -24,14 +24,21 @@ public interface HttpResponse extends HttpMessage {
*
* @return
*/
HttpStatus getStatus() throws HttpClientException;
HttpStatus getStatus();
/**
* 响应内容
*
* @return
*/
InputStream getBody() throws HttpClientException;
InputStream getBody();
/**
* 响应内容
*
* @return
*/
byte[] getContent();
/**
* 释放资源

View File

@ -31,7 +31,6 @@ package com.foxinmy.weixin4j.http;
* Constants enumerating the HTTP status codes. All status codes defined in
* RFC1945 (HTTP/1.0), RFC2616 (HTTP/1.1), and RFC2518 (WebDAV) are listed.
*
* @see StatusLine
*
* @since 4.0
*/
@ -188,6 +187,54 @@ public final class HttpStatus {
return statusText;
}
/**
* Returns the HTTP status series of this status code.
*
* @see HttpStatus.Series
*/
public Series series() {
return Series.valueOf(this);
}
/**
* Java 5 enumeration of HTTP status series.
* <p>
* Retrievable via {@link HttpStatus#series()}.
*/
public static enum Series {
INFORMATIONAL(1), SUCCESSFUL(2), REDIRECTION(3), CLIENT_ERROR(4), SERVER_ERROR(
5);
private final int value;
private Series(int value) {
this.value = value;
}
/**
* Return the integer value of this status series. Ranges from 1 to 5.
*/
public int value() {
return this.value;
}
public static Series valueOf(int status) {
int seriesCode = status / 100;
for (Series series : values()) {
if (series.value == seriesCode) {
return series;
}
}
throw new IllegalArgumentException("No matching constant for ["
+ status + "]");
}
public static Series valueOf(HttpStatus status) {
return valueOf(status.statusCode);
}
}
@Override
public String toString() {
return "[" + statusCode + "," + statusText + "]";

View File

@ -1,29 +1,24 @@
package com.foxinmy.weixin4j.http;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.Proxy;
import java.net.URI;
import java.net.URLConnection;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.X509TrustManager;
import com.foxinmy.weixin4j.http.entity.HttpEntity;
import com.foxinmy.weixin4j.model.Consts;
import com.foxinmy.weixin4j.http.factory.HttpClientFactory;
import com.foxinmy.weixin4j.util.IOUtil;
import com.foxinmy.weixin4j.util.StringUtil;
/**
@ -46,27 +41,6 @@ public class SimpleHttpClient extends AbstractHttpClient implements HttpClient {
};
}
protected X509TrustManager createX509TrustManager() {
return new X509TrustManager() {
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
@Override
public void checkServerTrusted(
X509Certificate[] paramArrayOfX509Certificate,
String paramString) throws CertificateException {
}
@Override
public void checkClientTrusted(
X509Certificate[] paramArrayOfX509Certificate,
String paramString) throws CertificateException {
}
};
}
protected HttpURLConnection createHttpConnection(HttpRequest request)
throws IOException {
URI uri = request.getURI();
@ -83,11 +57,7 @@ public class SimpleHttpClient extends AbstractHttpClient implements HttpClient {
hostnameVerifier = params.getHostnameVerifier();
}
if (sslContext == null) {
sslContext = SSLContext.getInstance("TLS");
sslContext
.init(null,
new X509TrustManager[] { createX509TrustManager() },
new java.security.SecureRandom());
sslContext = HttpClientFactory.allowSSLContext();
}
if (hostnameVerifier == null) {
hostnameVerifier = createHostnameVerifier();
@ -96,23 +66,14 @@ public class SimpleHttpClient extends AbstractHttpClient implements HttpClient {
connection.setSSLSocketFactory(sslContext.getSocketFactory());
connection.setHostnameVerifier(hostnameVerifier);
return connection;
} catch (NoSuchAlgorithmException | KeyManagementException e) {
throw new IOException(e.getMessage());
} catch (HttpClientException e) {
throw new IOException(e);
}
} else {
return (HttpURLConnection) urlConnection;
}
}
protected Map<String, String> createDefualtHeader() {
Map<String, String> header = new HashMap<String, String>();
header.put("User-Agent", "simple-httpclient");
header.put("Accept", "text/xml,text/javascript");
header.put("Accept-Charset", Consts.UTF_8.name());
header.put("Accept-Encoding", Consts.UTF_8.name());
return header;
}
@Override
public HttpResponse execute(HttpRequest request) throws HttpClientException {
HttpResponse response = null;
@ -141,25 +102,31 @@ public class SimpleHttpClient extends AbstractHttpClient implements HttpClient {
connection.setDoOutput(false);
}
// set headers
for (Iterator<Entry<String, String>> headerIterator = createDefualtHeader()
.entrySet().iterator(); headerIterator.hasNext();) {
Entry<String, String> header = headerIterator.next();
connection.setRequestProperty(header.getKey(),
header.getValue());
}
HttpHeaders headers = request.getHeaders();
if (headers != null) {
for (Iterator<Entry<String, List<String>>> headerIterator = headers
.entrySet().iterator(); headerIterator.hasNext();) {
Entry<String, List<String>> header = headerIterator.next();
if (HttpHeaders.COOKIE.equalsIgnoreCase(header.getKey())) {
connection.setRequestProperty(header.getKey(),
StringUtil.join(header.getValue(), ';'));
} else {
for (String headerValue : header.getValue()) {
connection.addRequestProperty(header.getKey(),
headerValue != null ? headerValue : "");
}
if (headers == null) {
headers = new HttpHeaders();
if (!headers.containsKey(HttpHeaders.HOST)) {
headers.set(HttpHeaders.HOST, request.getURI().getHost());
}
// Add default accept headers
if (!headers.containsKey(HttpHeaders.ACCEPT)) {
headers.set(HttpHeaders.ACCEPT, "*/*");
}
// Add default user agent
if (!headers.containsKey(HttpHeaders.USER_AGENT)) {
headers.set(HttpHeaders.USER_AGENT, "jdk/httpclient");
}
}
for (Iterator<Entry<String, List<String>>> headerIterator = headers
.entrySet().iterator(); headerIterator.hasNext();) {
Entry<String, List<String>> header = headerIterator.next();
if (HttpHeaders.COOKIE.equalsIgnoreCase(header.getKey())) {
connection.setRequestProperty(header.getKey(),
StringUtil.join(header.getValue(), ';'));
} else {
for (String headerValue : header.getValue()) {
connection.addRequestProperty(header.getKey(),
headerValue != null ? headerValue : "");
}
}
}
@ -170,16 +137,15 @@ public class SimpleHttpClient extends AbstractHttpClient implements HttpClient {
if (httpEntity != null) {
// Read Out Exception when connection.disconnect();
/*
* if (httpEntity.getContentLength() > 0l) {
* connection.setFixedLengthStreamingMode(httpEntity
* .getContentLength()); } else { connection
* .setChunkedStreamingMode(params != null ? params
* .getChunkSize() : 4096); }
*/
if (httpEntity.getContentLength() > 0l) {
connection.setFixedLengthStreamingMode(httpEntity
.getContentLength());
} else {
connection
.setChunkedStreamingMode(params != null ? params
.getChunkSize() : 4096);
}*/
if (httpEntity.getContentLength() > 0l) {
connection.setRequestProperty(HttpHeaders.CONTENT_LENGTH,
connection.setRequestProperty(
HttpHeaders.CONTENT_LENGTH,
Long.toString(httpEntity.getContentLength()));
}
if (httpEntity.getContentType() != null) {
@ -198,7 +164,12 @@ public class SimpleHttpClient extends AbstractHttpClient implements HttpClient {
output.close();
}
// building response
response = new SimpleHttpResponse(connection);
InputStream input = connection.getErrorStream() != null ? connection
.getErrorStream() : connection.getInputStream();
byte[] content = IOUtil.toByteArray(input);
response = new SimpleHttpResponse(connection, content);
input.close();
handleResponse(response);
} catch (IOException e) {
throw new HttpClientException("I/O error on "
+ request.getMethod().name() + " request for \""

View File

@ -1,7 +1,6 @@
package com.foxinmy.weixin4j.http;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.util.Iterator;
import java.util.List;
@ -17,7 +16,7 @@ import java.util.Map.Entry;
* @since JDK 1.7
* @see
*/
public class SimpleHttpResponse implements HttpResponse {
public class SimpleHttpResponse extends AbstractHttpResponse {
private final HttpURLConnection connection;
@ -25,7 +24,8 @@ public class SimpleHttpResponse implements HttpResponse {
private HttpVersion protocol;
private HttpStatus status;
public SimpleHttpResponse(HttpURLConnection connection) {
public SimpleHttpResponse(HttpURLConnection connection, byte[] content) {
super(content);
this.connection = connection;
}
@ -65,28 +65,18 @@ public class SimpleHttpResponse implements HttpResponse {
}
@Override
public HttpStatus getStatus() throws HttpClientException {
public HttpStatus getStatus() {
if (status == null) {
try {
status = new HttpStatus(connection.getResponseCode(),
connection.getResponseMessage());
} catch (IOException e) {
throw new HttpClientException("I/O Error on getStatus", e);
throw new RuntimeException("I/O Error on getStatus", e);
}
}
return status;
}
@Override
public InputStream getBody() throws HttpClientException {
try {
return connection.getErrorStream() != null ? connection
.getErrorStream() : connection.getInputStream();
} catch (IOException e) {
throw new HttpClientException("I/O Error on getBody", e);
}
}
@Override
public void close() {
connection.disconnect();

View File

@ -1,6 +1,15 @@
package com.foxinmy.weixin4j.http.factory;
import java.security.KeyManagementException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import javax.net.ssl.SSLContext;
import javax.net.ssl.X509TrustManager;
import com.foxinmy.weixin4j.http.HttpClient;
import com.foxinmy.weixin4j.http.HttpClientException;
/**
* HttpClient工厂生产类:参考netty的InternalLoggerFactory
@ -26,12 +35,16 @@ public abstract class HttpClientFactory {
private static HttpClientFactory newDefaultFactory() {
HttpClientFactory f;
try {
f = new HttpComponent4Factory();
} catch (Throwable e2) {
f = new Netty4HttpClientFactory();
} catch (Throwable e1) {
try {
f = new HttpComponent3Factory();
} catch (Throwable e3) {
f = new SimpleHttpClientFactory();
f = new HttpComponent4Factory();
} catch (Throwable e2) {
try {
f = new HttpComponent3Factory();
} catch (Throwable e3) {
f = new SimpleHttpClientFactory();
}
}
}
return f;
@ -73,4 +86,37 @@ public abstract class HttpClientFactory {
* @return
*/
public abstract HttpClient newInstance();
public static SSLContext allowSSLContext() throws HttpClientException {
try {
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null,
new X509TrustManager[] { createX509TrustManager() },
new java.security.SecureRandom());
return sslContext;
} catch (NoSuchAlgorithmException | KeyManagementException e) {
throw new HttpClientException("Create SSLContext Error:", e);
}
}
protected static X509TrustManager createX509TrustManager() {
return new X509TrustManager() {
@Override
public X509Certificate[] getAcceptedIssuers() {
return null;
}
@Override
public void checkServerTrusted(
X509Certificate[] paramArrayOfX509Certificate,
String paramString) throws CertificateException {
}
@Override
public void checkClientTrusted(
X509Certificate[] paramArrayOfX509Certificate,
String paramString) throws CertificateException {
}
};
}
}

View File

@ -109,18 +109,30 @@ public class HttpComponent3 extends AbstractHttpClient {
}
com.foxinmy.weixin4j.http.HttpHeaders headers = request
.getHeaders();
if (headers != null) {
for (Iterator<Entry<String, List<String>>> headerIterator = headers
.entrySet().iterator(); headerIterator.hasNext();) {
Entry<String, List<String>> header = headerIterator.next();
if (HttpHeaders.COOKIE.equalsIgnoreCase(header.getKey())) {
httpMethod.setRequestHeader(header.getKey(),
StringUtil.join(header.getValue(), ';'));
} else {
for (String headerValue : header.getValue()) {
httpMethod.addRequestHeader(header.getKey(),
headerValue != null ? headerValue : "");
}
if (headers == null) {
headers = new HttpHeaders();
}
if (!headers.containsKey(HttpHeaders.HOST)) {
headers.set(HttpHeaders.HOST, uri.getHost());
}
// Add default accept headers
if (!headers.containsKey(HttpHeaders.ACCEPT)) {
headers.set(HttpHeaders.ACCEPT, "*/*");
}
// Add default user agent
if (!headers.containsKey(HttpHeaders.USER_AGENT)) {
headers.set(HttpHeaders.USER_AGENT, "apache/httpclient3");
}
for (Iterator<Entry<String, List<String>>> headerIterator = headers
.entrySet().iterator(); headerIterator.hasNext();) {
Entry<String, List<String>> header = headerIterator.next();
if (HttpHeaders.COOKIE.equalsIgnoreCase(header.getKey())) {
httpMethod.setRequestHeader(header.getKey(),
StringUtil.join(header.getValue(), ';'));
} else {
for (String headerValue : header.getValue()) {
httpMethod.addRequestHeader(header.getKey(),
headerValue != null ? headerValue : "");
}
}
}
@ -153,6 +165,7 @@ public class HttpComponent3 extends AbstractHttpClient {
}
httpClient.executeMethod(httpMethod);
response = new HttpComponent3Response(httpMethod);
handleResponse(response);
} catch (IOException e) {
throw new HttpClientException("I/O error on "
+ request.getMethod().name() + " request for \""

View File

@ -1,16 +1,13 @@
package com.foxinmy.weixin4j.http.factory;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.protocol.Protocol;
import com.foxinmy.weixin4j.http.HttpClientException;
import com.foxinmy.weixin4j.http.AbstractHttpResponse;
import com.foxinmy.weixin4j.http.HttpHeaders;
import com.foxinmy.weixin4j.http.HttpResponse;
import com.foxinmy.weixin4j.http.HttpStatus;
import com.foxinmy.weixin4j.http.HttpVersion;
@ -23,22 +20,17 @@ import com.foxinmy.weixin4j.http.HttpVersion;
* @since JDK 1.7
* @see
*/
public class HttpComponent3Response implements HttpResponse {
public class HttpComponent3Response extends AbstractHttpResponse {
private final HttpMethod httpMethod;
private HttpHeaders headers;
private HttpVersion protocol;
private HttpStatus status;
private InputStream body;
public HttpComponent3Response(HttpMethod httpMethod) {
public HttpComponent3Response(HttpMethod httpMethod) throws IOException {
super(httpMethod.getResponseBody());
this.httpMethod = httpMethod;
try {
this.body = new ByteArrayInputStream(httpMethod.getResponseBody());
} catch (IOException e) {
;
}
}
@Override
@ -71,7 +63,7 @@ public class HttpComponent3Response implements HttpResponse {
}
@Override
public HttpStatus getStatus() throws HttpClientException {
public HttpStatus getStatus() {
if (status == null) {
status = new HttpStatus(httpMethod.getStatusCode(),
httpMethod.getStatusText());
@ -79,11 +71,6 @@ public class HttpComponent3Response implements HttpResponse {
return status;
}
@Override
public InputStream getBody() throws HttpClientException {
return body;
}
@Override
public void close() {
httpMethod.releaseConnection();

View File

@ -26,6 +26,7 @@ import org.apache.http.client.methods.HttpTrace;
import org.apache.http.conn.ssl.X509HostnameVerifier;
import org.apache.http.entity.AbstractHttpEntity;
import org.apache.http.entity.InputStreamEntity;
import org.apache.http.util.EntityUtils;
import com.foxinmy.weixin4j.http.AbstractHttpClient;
import com.foxinmy.weixin4j.http.HttpHeaders;
@ -57,18 +58,27 @@ public abstract class HttpComponent4 extends AbstractHttpClient {
}
protected void addHeaders(HttpHeaders headers, HttpRequestBase uriRequest) {
if (headers != null) {
for (Iterator<Entry<String, List<String>>> headerIterator = headers
.entrySet().iterator(); headerIterator.hasNext();) {
Entry<String, List<String>> header = headerIterator.next();
if (HttpHeaders.COOKIE.equalsIgnoreCase(header.getKey())) {
if (headers == null) {
headers = new HttpHeaders();
}
// Add default accept headers
if (!headers.containsKey(HttpHeaders.ACCEPT)) {
headers.set(HttpHeaders.ACCEPT, "*/*");
}
// Add default user agent
if (!headers.containsKey(HttpHeaders.USER_AGENT)) {
headers.set(HttpHeaders.USER_AGENT, "apache/httpclient4");
}
for (Iterator<Entry<String, List<String>>> headerIterator = headers
.entrySet().iterator(); headerIterator.hasNext();) {
Entry<String, List<String>> header = headerIterator.next();
if (HttpHeaders.COOKIE.equalsIgnoreCase(header.getKey())) {
uriRequest.addHeader(header.getKey(),
StringUtil.join(header.getValue(), ';'));
} else {
for (String headerValue : header.getValue()) {
uriRequest.addHeader(header.getKey(),
StringUtil.join(header.getValue(), ';'));
} else {
for (String headerValue : header.getValue()) {
uriRequest.addHeader(header.getKey(),
headerValue != null ? headerValue : "");
}
headerValue != null ? headerValue : "");
}
}
}
@ -94,6 +104,11 @@ public abstract class HttpComponent4 extends AbstractHttpClient {
}
}
protected byte[] getContent(org.apache.http.HttpResponse httpResponse)
throws IOException {
return EntityUtils.toByteArray(httpResponse.getEntity());
}
protected static class CustomHostnameVerifier implements
X509HostnameVerifier {

View File

@ -13,7 +13,6 @@ import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.CoreConnectionPNames;
import org.apache.http.params.CoreProtocolPNames;
import com.foxinmy.weixin4j.http.HttpClient;
import com.foxinmy.weixin4j.http.HttpClientException;
import com.foxinmy.weixin4j.http.HttpHeaders;
import com.foxinmy.weixin4j.http.HttpParams;
@ -87,7 +86,9 @@ public class HttpComponent4_1 extends HttpComponent4 {
addEntity(request.getEntity(), uriRequest);
org.apache.http.HttpResponse httpResponse = httpClient
.execute(uriRequest);
response = new HttpComponent4_1Response(httpClient, httpResponse);
response = new HttpComponent4_1Response(httpResponse,
getContent(httpResponse));
handleResponse(response);
} catch (IOException e) {
throw new HttpClientException("I/O error on "
+ request.getMethod().name() + " request for \""

View File

@ -1,16 +1,13 @@
package com.foxinmy.weixin4j.http.factory;
import java.io.IOException;
import java.io.InputStream;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.ProtocolVersion;
import org.apache.http.StatusLine;
import com.foxinmy.weixin4j.http.HttpClientException;
import com.foxinmy.weixin4j.http.AbstractHttpResponse;
import com.foxinmy.weixin4j.http.HttpHeaders;
import com.foxinmy.weixin4j.http.HttpResponse;
import com.foxinmy.weixin4j.http.HttpStatus;
import com.foxinmy.weixin4j.http.HttpVersion;
@ -23,18 +20,16 @@ import com.foxinmy.weixin4j.http.HttpVersion;
* @since JDK 1.7
* @see
*/
public class HttpComponent4_1Response implements HttpResponse {
private final org.apache.http.client.HttpClient httpClient;
public class HttpComponent4_1Response extends AbstractHttpResponse {
private final org.apache.http.HttpResponse httpResponse;
private HttpHeaders headers;
private HttpVersion protocol;
private HttpStatus status;
public HttpComponent4_1Response(
org.apache.http.client.HttpClient httpClient,
org.apache.http.HttpResponse httpResponse) {
this.httpClient = httpClient;
public HttpComponent4_1Response(org.apache.http.HttpResponse httpResponse,
byte[] content) throws IOException {
super(content);
this.httpResponse = httpResponse;
}
@ -64,7 +59,7 @@ public class HttpComponent4_1Response implements HttpResponse {
}
@Override
public HttpStatus getStatus() throws HttpClientException {
public HttpStatus getStatus() {
if (status == null) {
StatusLine statusLine = httpResponse.getStatusLine();
status = new HttpStatus(statusLine.getStatusCode(),
@ -74,17 +69,11 @@ public class HttpComponent4_1Response implements HttpResponse {
}
@Override
public InputStream getBody() throws HttpClientException {
public void close() {
try {
HttpEntity entity = this.httpResponse.getEntity();
return (entity != null ? entity.getContent() : null);
httpResponse.getEntity().consumeContent();
} catch (IOException e) {
throw new HttpClientException("I/O Error on getBody", e);
;
}
}
@Override
public void close() {
httpClient.getConnectionManager().closeExpiredConnections();
}
}

View File

@ -69,7 +69,9 @@ public class HttpComponent4_2 extends HttpComponent4 {
addHeaders(request.getHeaders(), uriRequest);
addEntity(request.getEntity(), uriRequest);
CloseableHttpResponse httpResponse = httpClient.execute(uriRequest);
response = new HttpComponent4_2Response(httpResponse);
response = new HttpComponent4_2Response(httpResponse,
getContent(httpResponse));
handleResponse(response);
} catch (IOException e) {
throw new HttpClientException("I/O error on "
+ request.getMethod().name() + " request for \""

View File

@ -1,17 +1,15 @@
package com.foxinmy.weixin4j.http.factory;
import java.io.IOException;
import java.io.InputStream;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.ProtocolVersion;
import org.apache.http.StatusLine;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.util.EntityUtils;
import com.foxinmy.weixin4j.http.HttpClientException;
import com.foxinmy.weixin4j.http.AbstractHttpResponse;
import com.foxinmy.weixin4j.http.HttpHeaders;
import com.foxinmy.weixin4j.http.HttpResponse;
import com.foxinmy.weixin4j.http.HttpStatus;
import com.foxinmy.weixin4j.http.HttpVersion;
@ -24,7 +22,7 @@ import com.foxinmy.weixin4j.http.HttpVersion;
* @since JDK 1.7
* @see
*/
public class HttpComponent4_2Response implements HttpResponse {
public class HttpComponent4_2Response extends AbstractHttpResponse {
private final CloseableHttpResponse httpResponse;
@ -32,7 +30,9 @@ public class HttpComponent4_2Response implements HttpResponse {
private HttpVersion protocol;
private HttpStatus status;
public HttpComponent4_2Response(CloseableHttpResponse httpResponse) {
public HttpComponent4_2Response(CloseableHttpResponse httpResponse,
byte[] content) {
super(content);
this.httpResponse = httpResponse;
}
@ -62,7 +62,7 @@ public class HttpComponent4_2Response implements HttpResponse {
}
@Override
public HttpStatus getStatus() throws HttpClientException {
public HttpStatus getStatus() {
if (status == null) {
StatusLine statusLine = httpResponse.getStatusLine();
status = new HttpStatus(statusLine.getStatusCode(),
@ -71,20 +71,10 @@ public class HttpComponent4_2Response implements HttpResponse {
return status;
}
@Override
public InputStream getBody() throws HttpClientException {
try {
HttpEntity entity = this.httpResponse.getEntity();
return (entity != null ? entity.getContent() : null);
} catch (IOException e) {
throw new HttpClientException("I/O Error on getBody", e);
}
}
@Override
public void close() {
try {
// EntityUtils.consume(httpResponse.getEntity());
EntityUtils.consume(httpResponse.getEntity());
httpResponse.close();
} catch (IOException ex) {
;

View File

@ -0,0 +1,223 @@
package com.foxinmy.weixin4j.http.factory;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.ByteBufOutputStream;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOption;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpRequest;
import io.netty.handler.codec.http.DefaultHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.ssl.SslHandler;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.URI;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.concurrent.ExecutionException;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import com.foxinmy.weixin4j.http.AbstractHttpClient;
import com.foxinmy.weixin4j.http.HttpClientException;
import com.foxinmy.weixin4j.http.HttpHeaders;
import com.foxinmy.weixin4j.http.HttpParams;
import com.foxinmy.weixin4j.http.HttpRequest;
import com.foxinmy.weixin4j.http.HttpResponse;
import com.foxinmy.weixin4j.http.entity.HttpEntity;
import com.foxinmy.weixin4j.util.SettableFuture;
import com.foxinmy.weixin4j.util.StringUtil;
/**
* Netty 4.x
*
* @className Netty4HttpClient
* @author jy
* @date 2015年8月30日
* @since JDK 1.7
* @see
*/
public class Netty4HttpClient extends AbstractHttpClient {
private final Bootstrap bootstrap;
public Netty4HttpClient(Bootstrap bootstrap) {
this.bootstrap = bootstrap;
}
@Override
public HttpResponse execute(HttpRequest request) throws HttpClientException {
HttpResponse response = null;
try {
final URI uri = request.getURI();
final HttpParams params = request.getParams();
if (params != null) {
bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS,
params.getConnectTimeout());
}
final boolean useProxy = params != null
&& params.getProxy() != null;
final boolean useSSL = "https".equals(uri.getScheme()) && !useProxy;
final DefaultHttpRequest uriRequest = createRequest(request);
final SettableFuture<HttpResponse> future = new SettableFuture<HttpResponse>();
ChannelFutureListener listener = new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture channelFuture)
throws Exception {
if (channelFuture.isSuccess()) {
Channel channel = channelFuture.channel();
// ssl
SSLContext sslContext = null;
if (useSSL) {
if (params != null
&& params.getSSLContext() != null) {
sslContext = params.getSSLContext();
} else {
sslContext = HttpClientFactory
.allowSSLContext();
}
SSLEngine sslEngine = sslContext.createSSLEngine();
sslEngine.setUseClientMode(true);
channel.pipeline().addFirst(
new SslHandler(sslEngine));
}
channel.pipeline().addLast(new RequestHandler(future));
channel.writeAndFlush(uriRequest);
} else {
future.setException(channelFuture.cause());
}
}
};
InetSocketAddress address = new InetSocketAddress(
InetAddress.getByName(uri.getHost()), getPort(uri));
if (useProxy) {
address = (InetSocketAddress) params.getProxy().address();
}
bootstrap.connect(address).syncUninterruptibly()
.addListener(listener);
response = future.get();
handleResponse(response);
} catch (IOException | InterruptedException | ExecutionException e) {
throw new HttpClientException("I/O error on "
+ request.getMethod().name() + " request for \""
+ request.getURI().toString() + "\":" + e.getMessage(), e);
} finally {
if (response != null) {
response.close();
}
}
return response;
}
private DefaultHttpRequest createRequest(HttpRequest request)
throws IOException {
HttpMethod method = HttpMethod.valueOf(request.getMethod().name());
URI uri = request.getURI();
String url = StringUtil.isBlank(uri.getRawPath()) ? "/" : uri
.getRawPath();
if (StringUtil.isNotBlank(uri.getRawQuery())) {
url += "?" + uri.getRawQuery();
}
DefaultHttpRequest uriRequest = new DefaultHttpRequest(
HttpVersion.HTTP_1_1, method, url);
// entity
HttpEntity entity = request.getEntity();
if (entity != null) {
ByteBuf byteBuf = ByteBufAllocator.DEFAULT.buffer();
try (ByteBufOutputStream out = new ByteBufOutputStream(byteBuf)) {
entity.writeTo(out);
}
uriRequest = new DefaultFullHttpRequest(
uriRequest.getProtocolVersion(), uriRequest.getMethod(),
uriRequest.getUri(), byteBuf);
if (entity.getContentType() != null) {
uriRequest.headers().add(HttpHeaders.CONTENT_TYPE,
entity.getContentType().toString());
}
if (entity.getContentLength() < 0) {
uriRequest.headers().add(HttpHeaders.TRANSFER_ENCODING,
io.netty.handler.codec.http.HttpHeaders.Values.CHUNKED);
} else {
uriRequest.headers().add(HttpHeaders.CONTENT_LENGTH,
entity.getContentLength());
}
}
// header
HttpHeaders headers = request.getHeaders();
if (headers == null) {
headers = new HttpHeaders();
}
if (!headers.containsKey(HttpHeaders.HOST)) {
headers.set(HttpHeaders.HOST, uri.getHost());
}
// Add default accept headers
if (!headers.containsKey(HttpHeaders.ACCEPT)) {
headers.set(HttpHeaders.ACCEPT, "*/*");
}
// Add default user agent
if (!headers.containsKey(HttpHeaders.USER_AGENT)) {
headers.set(HttpHeaders.USER_AGENT, "netty/httpclient");
}
for (Iterator<Entry<String, List<String>>> headerIterator = headers
.entrySet().iterator(); headerIterator.hasNext();) {
Entry<String, List<String>> header = headerIterator.next();
uriRequest.headers().add(header.getKey(), header.getValue());
}
uriRequest.headers().set(HttpHeaders.CONNECTION,
io.netty.handler.codec.http.HttpHeaders.Values.CLOSE);
return uriRequest;
}
private int getPort(URI uri) {
int port = uri.getPort();
if (port == -1) {
if ("http".equalsIgnoreCase(uri.getScheme())) {
port = 80;
} else if ("https".equalsIgnoreCase(uri.getScheme())) {
port = 443;
}
}
return port;
}
private static class RequestHandler extends
SimpleChannelInboundHandler<FullHttpResponse> {
private final SettableFuture<HttpResponse> future;
public RequestHandler(SettableFuture<HttpResponse> future) {
this.future = future;
}
@Override
protected void channelRead0(ChannelHandlerContext context,
FullHttpResponse response) throws Exception {
byte[] content = null;
ByteBuf byteBuf = response.content();
if (byteBuf.hasArray()) {
content = byteBuf.array();
} else {
content = new byte[byteBuf.readableBytes()];
byteBuf.readBytes(content);
}
future.set(new Netty4HttpResponse(context, response, content));
}
@Override
public void exceptionCaught(ChannelHandlerContext context,
Throwable cause) throws Exception {
future.setException(cause);
}
}
}

View File

@ -0,0 +1,64 @@
package com.foxinmy.weixin4j.http.factory;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.http.HttpContentDecompressor;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpResponseDecoder;
import io.netty.handler.stream.ChunkedWriteHandler;
import com.foxinmy.weixin4j.http.HttpClient;
/**
* 使用Netty
*
* @className Netty4HttpClientFactory
* @author jy
* @date 2015年8月30日
* @since JDK 1.7
* @see
*/
public class Netty4HttpClientFactory extends HttpClientFactory {
/**
* worker线程数,默认设置为cpu的核数 * 4
*/
private final int workerThreads;
public Netty4HttpClientFactory() {
this(Runtime.getRuntime().availableProcessors() * 4);
}
public Netty4HttpClientFactory(int workerThreads) {
this.workerThreads = workerThreads;
}
@Override
public HttpClient newInstance() {
Bootstrap b = new Bootstrap();
b.option(ChannelOption.SO_KEEPALIVE, true).option(
ChannelOption.TCP_NODELAY, true);
EventLoopGroup workerGroup = new NioEventLoopGroup(workerThreads);
b.group(workerGroup).channel(NioSocketChannel.class)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel channel)
throws Exception {
ChannelPipeline pipeline = channel.pipeline();
pipeline.addLast(new HttpClientCodec());
pipeline.addLast(new HttpContentDecompressor());
pipeline.addLast(new ChunkedWriteHandler());
pipeline.addLast(new HttpResponseDecoder());
pipeline.addLast(new HttpObjectAggregator(
Integer.MAX_VALUE));
}
});
return new Netty4HttpClient(b);
}
}

View File

@ -0,0 +1,78 @@
package com.foxinmy.weixin4j.http.factory;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpResponseStatus;
import java.util.Map;
import com.foxinmy.weixin4j.http.AbstractHttpResponse;
import com.foxinmy.weixin4j.http.HttpHeaders;
import com.foxinmy.weixin4j.http.HttpStatus;
import com.foxinmy.weixin4j.http.HttpVersion;
/**
* Netty Respone::Requires Netty 4.x or higher
*
* @className Netty4HttpResponse
* @author jy
* @date 2015年8月30日
* @since JDK 1.7
* @see
*/
public class Netty4HttpResponse extends AbstractHttpResponse {
private final ChannelHandlerContext context;
private final FullHttpResponse response;
private HttpVersion protocol;
private HttpStatus status;
private volatile HttpHeaders headers;
public Netty4HttpResponse(ChannelHandlerContext context,
FullHttpResponse response, byte[] content) {
super(content);
this.context = context;
this.response = response;
this.response.retain();
}
@Override
public HttpHeaders getHeaders() {
if (this.headers == null) {
HttpHeaders headers = new HttpHeaders();
for (Map.Entry<String, String> entry : this.response.headers()) {
headers.add(entry.getKey(), entry.getValue());
}
this.headers = headers;
}
return this.headers;
}
@Override
public HttpVersion getProtocol() {
if (protocol == null) {
io.netty.handler.codec.http.HttpVersion version = response
.getProtocolVersion();
this.protocol = new HttpVersion(version.protocolName(),
version.majorVersion(), version.majorVersion(),
version.isKeepAliveDefault());
}
return protocol;
}
@Override
public HttpStatus getStatus() {
if (status == null) {
HttpResponseStatus status = response.getStatus();
this.status = new HttpStatus(status.code(), status.reasonPhrase());
}
return status;
}
@Override
public void close() {
this.response.release();
this.context.close();
}
}

View File

@ -20,6 +20,7 @@ import com.foxinmy.weixin4j.http.entity.FormUrlEntity;
import com.foxinmy.weixin4j.http.entity.HttpEntity;
import com.foxinmy.weixin4j.http.entity.StringEntity;
import com.foxinmy.weixin4j.http.factory.HttpClientFactory;
import com.foxinmy.weixin4j.http.factory.Netty4HttpClientFactory;
import com.foxinmy.weixin4j.model.Consts;
import com.foxinmy.weixin4j.util.StringUtil;
import com.foxinmy.weixin4j.util.WeixinErrorUtil;
@ -44,6 +45,7 @@ public class WeixinRequestExecutor {
}
public WeixinRequestExecutor(HttpParams params) {
HttpClientFactory.setDefaultFactory(new Netty4HttpClientFactory());
this.httpClient = HttpClientFactory.getInstance();
this.params = params;
}
@ -97,10 +99,6 @@ public class WeixinRequestExecutor {
HttpResponse httpResponse = httpClient.execute(request);
HttpStatus status = httpResponse.getStatus();
HttpHeaders headers = httpResponse.getHeaders();
if (status.getStatusCode() >= HttpStatus.SC_MULTIPLE_CHOICES) {
throw new WeixinException(String.format("request fail : %d-%s",
status.getStatusCode(), status.getStatusText()));
}
WeixinResponse response = new WeixinResponse(headers, status,
httpResponse.getBody());
String contentType = headers.getFirst(HttpHeaders.CONTENT_TYPE);

View File

@ -42,6 +42,7 @@ public class WeixinResponse {
try {
text = StringUtil.newStringUtf8(IOUtil.toByteArray(body));
} catch (IOException e) {
e.printStackTrace();
;
}
}

View File

@ -0,0 +1,191 @@
/*
* Copyright 2002-2014 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.foxinmy.weixin4j.util;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference;
/**
* A {@link org.springframework.util.concurrent.ListenableFuture
* ListenableFuture} whose value can be set via {@link #set(Object)} or
* {@link #setException(Throwable)}. It may also be cancelled.
*
* <p>
* Inspired by {@code com.google.common.util.concurrent.SettableFuture}.
*
* @author Mattias Severson
* @author Rossen Stoyanchev
* @since 4.1
*/
public class SettableFuture<T> implements Future<T> {
private final SettableTask<T> settableTask;
private final FutureTask<T> futureTask;
public SettableFuture() {
this.settableTask = new SettableTask<T>();
this.futureTask = new FutureTask<T>(this.settableTask);
}
/**
* Set the value of this future. This method will return {@code true} if the
* value was set successfully, or {@code false} if the future has already
* been set or cancelled.
*
* @param value
* the value that will be set.
* @return {@code true} if the value was successfully set, else
* {@code false}.
*/
public boolean set(T value) {
boolean success = this.settableTask.setValue(value);
if (success) {
this.futureTask.run();
}
return success;
}
/**
* Set the exception of this future. This method will return {@code true} if
* the exception was set successfully, or {@code false} if the future has
* already been set or cancelled.
*
* @param exception
* the value that will be set.
* @return {@code true} if the exception was successfully set, else
* {@code false}.
*/
public boolean setException(Throwable exception) {
boolean success = this.settableTask.setException(exception);
if (success) {
this.futureTask.run();
}
return success;
}
@Override
public boolean cancel(boolean mayInterruptIfRunning) {
this.settableTask.setCancelled();
boolean cancelled = this.futureTask.cancel(mayInterruptIfRunning);
if (cancelled && mayInterruptIfRunning) {
interruptTask();
}
return cancelled;
}
@Override
public boolean isCancelled() {
return this.futureTask.isCancelled();
}
@Override
public boolean isDone() {
return this.futureTask.isDone();
}
/**
* Retrieve the value.
* <p>
* Will return the value if it has been set via {@link #set(Object)}, throw
* an {@link java.util.concurrent.ExecutionException} if it has been set via
* {@link #setException(Throwable)} or throw a
* {@link java.util.concurrent.CancellationException} if it has been
* cancelled.
*
* @return The value associated with this future.
*/
@Override
public T get() throws InterruptedException, ExecutionException {
return this.futureTask.get();
}
/**
* Retrieve the value.
* <p>
* Will return the value if it has been set via {@link #set(Object)}, throw
* an {@link java.util.concurrent.ExecutionException} if it has been set via
* {@link #setException(Throwable)} or throw a
* {@link java.util.concurrent.CancellationException} if it has been
* cancelled.
*
* @param timeout
* the maximum time to wait.
* @param unit
* the time unit of the timeout argument.
* @return The value associated with this future.
*/
@Override
public T get(long timeout, TimeUnit unit) throws InterruptedException,
ExecutionException, TimeoutException {
return this.futureTask.get(timeout, unit);
}
/**
* Subclasses can override this method to implement interruption of the
* future's computation. The method is invoked automatically by a successful
* call to {@link #cancel(boolean) cancel(true)}.
* <p>
* The default implementation does nothing.
*/
protected void interruptTask() {
}
private static class SettableTask<T> implements Callable<T> {
private static final String NO_VALUE = SettableFuture.class
.getName() + ".NO_VALUE";
private final AtomicReference<Object> value = new AtomicReference<Object>(
NO_VALUE);
private volatile boolean cancelled = false;
public boolean setValue(T value) {
if (this.cancelled) {
return false;
}
return this.value.compareAndSet(NO_VALUE, value);
}
public boolean setException(Throwable exception) {
if (this.cancelled) {
return false;
}
return this.value.compareAndSet(NO_VALUE, exception);
}
public void setCancelled() {
this.cancelled = true;
}
@SuppressWarnings("unchecked")
@Override
public T call() throws Exception {
if (value.get() instanceof Exception) {
throw (Exception) value.get();
}
return (T) value.get();
}
}
}

View File

@ -1,6 +1,9 @@
package com.foxinmy.weixin4j.base.test;
import org.apache.commons.httpclient.methods.GetMethod;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.Proxy.Type;
import com.foxinmy.weixin4j.http.HttpClient;
import com.foxinmy.weixin4j.http.HttpClientException;
@ -11,11 +14,7 @@ import com.foxinmy.weixin4j.http.HttpResponse;
import com.foxinmy.weixin4j.http.factory.HttpClientFactory;
import com.foxinmy.weixin4j.http.factory.HttpComponent3Factory;
import com.foxinmy.weixin4j.http.factory.HttpComponent4Factory;
import com.foxinmy.weixin4j.http.factory.SimpleHttpClientFactory;
import com.foxinmy.weixin4j.model.WeixinAccount;
import com.foxinmy.weixin4j.token.FileTokenStorager;
import com.foxinmy.weixin4j.token.TokenHolder;
import com.foxinmy.weixin4j.util.Weixin4jConfigUtil;
import com.foxinmy.weixin4j.util.IOUtil;
public class HttpClientTest {
@ -23,13 +22,12 @@ public class HttpClientTest {
"https://www.baidu.com");
static {
HttpParams params = new HttpParams();
//params.setProxy(new Proxy(Type.HTTP, new InetSocketAddress(
// "218.92.227.170", 11095)));
params.setProxy(new Proxy(Type.HTTP, new InetSocketAddress(
"117.136.234.9", 80)));
request.setParams(params);
}
public static void test1() throws HttpClientException {
HttpClientFactory.setDefaultFactory(new SimpleHttpClientFactory());
HttpClient httpClient = HttpClientFactory.getInstance();
HttpResponse response = httpClient.execute(request);
print(response);
@ -51,28 +49,23 @@ public class HttpClientTest {
public static void print(HttpResponse response) throws HttpClientException {
System.err.println(response.getStatus());
System.err.println(response.getBody());
try {
System.err.println(new String(
IOUtil.toByteArray(response.getBody())));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.err.println(response.getHeaders());
System.err.println(response.getProtocol());
}
public static void test() {
org.apache.commons.httpclient.HttpClient hc = new org.apache.commons.httpclient.HttpClient();
hc.getHostConfiguration().setProxy("127.0.0.1", 1080);
org.apache.commons.httpclient.HttpMethod hm = new GetMethod(
"http://www.baidu.com");
try {
hc.executeMethod(hm);
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws Exception {
//test1();
test1();
System.out.println("---------------------");
//test2();
// test2();
System.out.println("---------------------");
//test3();
// test3();
// test4();
}
}

View File

@ -148,4 +148,8 @@
+ version upgrade to 1.5.3
+ 媒体接口类(MediaApi)查询素材接口调整:去掉offset,count替换为Pageable类
+ 媒体接口类(MediaApi)查询素材接口调整:去掉offset,count替换为Pageable类
* 2015-09-08
+ 新增批量获取用户信息接口

View File

@ -1,5 +1,6 @@
package com.foxinmy.weixin4j.mp.type;
/**
* 国家地区语言版本
* @className Lang

View File

@ -54,7 +54,7 @@ base on netty.
}
};
// 当消息类型为文本(text)时回复「HelloWorld」, 否则回复调试消息
new WeixinServerBootstrap(token,appid, aesKey).addHandler(
new WeixinServerBootstrap(token, appid, aesKey).addHandler(
messageHandler, DebugMessageHandler.global).startup();
}
}

View File

@ -47,7 +47,7 @@ public class ScanEventMessage extends EventMessage {
}
public String getParameter() {
return eventKey.replace("qrscene_", "");
return eventKey.replaceFirst("qrscene_", "");
}
@Override