├── .gitignore
├── README.md
├── project.clj
├── src
└── java
│ └── me
│ └── shenfeng
│ ├── AbstractResponseFuture.java
│ ├── PrefixThreadFactory.java
│ ├── ResponseFuture.java
│ ├── Utils.java
│ ├── dns
│ ├── DnsClientConstant.java
│ └── DnsPrefecher.java
│ ├── http
│ ├── ConnectionListener.java
│ ├── Decoder.java
│ ├── HttpClient.java
│ ├── HttpClientConfig.java
│ ├── HttpClientConstant.java
│ ├── HttpResponseFuture.java
│ ├── ResponseHandler.java
│ └── SocksHandler.java
│ └── ssl
│ ├── SslContextFactory.java
│ ├── SslKeyStore.java
│ └── TrustManagerFactory.java
└── test
├── java
├── DnsPrefetcherTest.java
├── HttpClientTest.java
├── TestSocket.java
├── UtilsTest.java
└── logback-test.xml
└── me
└── shenfeng
└── httpclient_test.clj
/.gitignore:
--------------------------------------------------------------------------------
1 | pom.xml
2 | .idea/
3 | target/
4 | .classpath
5 | .project
6 | /bin
7 | *jar
8 | /lib/
9 | /classes/
10 | .settings/
11 | .lein-deps-sum
12 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | An Async HTTP Client Based on [Netty](http://netty.io/)
2 |
3 | I write this for my personal part time project
4 | [RSSMiner](http://rssminer.net), for the web crawler and feed fetcher module.
5 |
6 | Please checkout out [http-kit](https://github.com/http-kit/http-kit), it should have better performance.
7 |
8 | ```xml
9 |
10 | clojars.org
11 | http://clojars.org/repo
12 |
13 |
14 |
15 | me.shenfeng
16 | async-http-client
17 | 1.1.0
18 |
19 | ```
20 |
21 | ```clj
22 | ; You may want to write an wrapper function to make it easier to use in Clojure project
23 | [me.shenfeng/async-http-client "1.1.0"]
24 | ```
25 |
26 | Features
27 | --------
28 |
29 | * Asynchronous
30 | * Minimum: just download webpages from Internet efficently.
31 | * Support SOCKS v5, HTTP proxy
32 | * HTTPS(trust all)
33 | * [Configurable](https://github.com/shenfeng/async-http-client/blob/master/src/java/me/shenfeng/http/HttpClientConfig.java)
34 | * [DNS prefetch](https://github.com/shenfeng/async-http-client/blob/master/src/java/me/shenfeng/dns/DnsPrefecher.java),
35 |
36 |
37 | Limitations:
38 | ------------
39 | * All Content are buffered in memory as byte array=> can not handle
40 | large file. Anyway, it's meant to download webpages(zipped if server
41 | support)
42 | * Dns prefetch is IPV4 only.
43 |
44 | Example
45 | -------
46 |
47 | ```java
48 | // Http client sample usage
49 | HttpClientConfig config = new HttpClientConfig();
50 | header = new HashMap();
51 | HttpClient client = new HttpClient(config);
52 | URI uri = new URI("http://onycloud.com");
53 | final HttpResponseFuture future = client.execGet(uri, header);
54 | resp.addListener(new Runnable() {
55 | public void run() {
56 | HttpResponse resp = future.get(); // async
57 | }
58 | });
59 | HttpResponse resp = future.get(); // blocking
60 | ```
61 |
62 | ### License
63 |
64 | Copyright © 2012 [Feng Shen](http://shenfeng.me/). Distributed under the [Apache License Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.html).
65 |
--------------------------------------------------------------------------------
/project.clj:
--------------------------------------------------------------------------------
1 | (defproject me.shenfeng/async-http-client "1.1.0"
2 | :description "HTTP client based on netty"
3 | :java-source-paths ["src/java"]
4 | :url "https://github.com/shenfeng/async-http-client"
5 | :javac-options ["-source" "1.6" "-target" "1.6" "-g"]
6 | :license {:name "Apache License, Version 2.0"
7 | :url "http://www.apache.org/licenses/LICENSE-2.0.html"}
8 | :warn-on-reflection true
9 | :repositories {"JBoss"
10 | "http://repository.jboss.org/nexus/content/groups/public/"}
11 | :dependencies [[io.netty/netty "3.6.3.Final"]
12 | [org.slf4j/slf4j-api "1.7.3"]]
13 | :profiles {:test {:java-source-paths ["test/java" "src/java"]}
14 | :dev {:dependencies [[ring/ring-jetty-adapter "0.3.11"]
15 | [com.google.guava/guava "10.0.1"]
16 | [ring/ring-core "0.3.11"]
17 | [org.clojure/clojure "1.4.0"]
18 | [compojure "1.1.5"]
19 | [ring/ring-jetty-adapter "1.1.8"]
20 | [ring/ring-core "1.1.8"]
21 | [junit/junit "4.8.2"]
22 | [ch.qos.logback/logback-classic "0.9.29"]]}})
23 |
--------------------------------------------------------------------------------
/src/java/me/shenfeng/AbstractResponseFuture.java:
--------------------------------------------------------------------------------
1 | package me.shenfeng;
2 |
3 | import java.util.ArrayList;
4 | import java.util.List;
5 | import java.util.concurrent.CountDownLatch;
6 | import java.util.concurrent.atomic.AtomicReference;
7 |
8 | import org.slf4j.Logger;
9 | import org.slf4j.LoggerFactory;
10 |
11 | public abstract class AbstractResponseFuture implements ResponseFuture {
12 |
13 | final static Logger logger = LoggerFactory
14 | .getLogger(AbstractResponseFuture.class);
15 |
16 | protected Runnable mFirstListener;
17 | protected final CountDownLatch mLatch = new CountDownLatch(1);
18 | protected final AtomicReference mResult = new AtomicReference();
19 |
20 | protected List mOtherListeners;
21 |
22 | public synchronized void addListener(Runnable listener) {
23 | if (mLatch.getCount() == 0) {
24 | notifyListener(listener);
25 | } else {
26 | if (mFirstListener == null) {
27 | mFirstListener = listener;
28 | } else {
29 | if (mOtherListeners == null) {
30 | mOtherListeners = new ArrayList(1);
31 | }
32 | mOtherListeners.add(listener);
33 | }
34 | }
35 | }
36 |
37 | protected void notifyListener(Runnable l) {
38 | try {
39 | l.run();// only called once
40 | } catch (Exception e) {
41 | logger.error(e.getMessage(), e);
42 | }
43 | }
44 |
45 | public boolean cancel(boolean mayInterruptIfRunning) {
46 | return false;
47 | }
48 |
49 | public boolean isCancelled() {
50 | return false;
51 | }
52 |
53 | public boolean isDone() {
54 | return mResult.get() != null;
55 | }
56 |
57 | public synchronized boolean done(V result) {
58 | if (mResult.compareAndSet(null, result)) {
59 | mLatch.countDown();
60 | if (mFirstListener != null) {
61 | notifyListener(mFirstListener);
62 | mFirstListener = null;
63 | if (mOtherListeners != null) {
64 | for (Runnable r : mOtherListeners) {
65 | notifyListener(r);
66 | }
67 | mOtherListeners = null;
68 | }
69 | }
70 | return true;
71 | }
72 | return false;
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/java/me/shenfeng/PrefixThreadFactory.java:
--------------------------------------------------------------------------------
1 | package me.shenfeng;
2 | import java.util.concurrent.ThreadFactory;
3 | import java.util.concurrent.atomic.AtomicInteger;
4 |
5 | public class PrefixThreadFactory implements ThreadFactory {
6 |
7 | private final String mPrefix;
8 | private final AtomicInteger mNum = new AtomicInteger(0);
9 |
10 | public PrefixThreadFactory(String prefix) {
11 | mPrefix = prefix;
12 | }
13 |
14 | public Thread newThread(Runnable r) {
15 | Thread t = new Thread(r, mPrefix + "#" + mNum.incrementAndGet());
16 | return t;
17 | }
18 | }
--------------------------------------------------------------------------------
/src/java/me/shenfeng/ResponseFuture.java:
--------------------------------------------------------------------------------
1 | package me.shenfeng;
2 |
3 | import java.util.concurrent.Future;
4 |
5 | public interface ResponseFuture extends Future {
6 |
7 | boolean isTimeout();
8 |
9 | boolean done(V result);
10 |
11 | void touch();
12 |
13 | boolean abort(Throwable t);
14 |
15 | void addListener(Runnable listener);
16 | }
17 |
--------------------------------------------------------------------------------
/src/java/me/shenfeng/Utils.java:
--------------------------------------------------------------------------------
1 | package me.shenfeng;
2 |
3 | import static org.jboss.netty.buffer.ChannelBuffers.wrappedBuffer;
4 | import static org.jboss.netty.handler.codec.compression.ZlibWrapper.GZIP;
5 | import static org.jboss.netty.handler.codec.compression.ZlibWrapper.ZLIB_OR_NONE;
6 | import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.CONTENT_ENCODING;
7 | import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE;
8 | import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.TRANSFER_ENCODING;
9 | import static org.jboss.netty.util.CharsetUtil.UTF_8;
10 |
11 | import java.net.URI;
12 | import java.nio.charset.Charset;
13 | import java.util.List;
14 |
15 | import org.jboss.netty.buffer.ChannelBuffer;
16 | import org.jboss.netty.handler.codec.compression.ZlibDecoder;
17 | import org.jboss.netty.handler.codec.embedder.CodecEmbedderException;
18 | import org.jboss.netty.handler.codec.embedder.DecoderEmbedder;
19 | import org.jboss.netty.handler.codec.http.HttpResponse;
20 | import org.slf4j.Logger;
21 | import org.slf4j.LoggerFactory;
22 |
23 | public class Utils {
24 | static Logger logger = LoggerFactory.getLogger(Utils.class);
25 |
26 | public static byte[] toBytes(int i) {
27 | return new byte[] { (byte) (i >> 8), (byte) (i & 0x00ff) };
28 | }
29 |
30 | public static int toInt(byte[] bytes) {
31 | return toInt(bytes, 0);
32 | }
33 |
34 | public static int toInt(byte[] bytes, int start) {
35 | return (toInt(bytes[start]) << 8) + toInt(bytes[start + 1]);
36 | }
37 |
38 | public static int toInt(int b) {
39 | if (b < 0)
40 | b += 256;
41 | return b;
42 | }
43 |
44 | public static boolean isIP(String host) {
45 | for (int i = 0; i < host.length(); ++i) {
46 | if (!(Character.isDigit(host.charAt(i)) || host.charAt(i) == '.')) {
47 | return false;
48 | }
49 | }
50 | return true;
51 | }
52 |
53 | private static final String CS = "charset=";
54 |
55 | public static String getPath(URI uri) {
56 | String path = uri.getPath();
57 | String query = uri.getRawQuery();
58 | if ("".equals(path))
59 | path = "/";
60 | if (query == null)
61 | return path;
62 | else
63 | return path + "?" + query;
64 | }
65 |
66 | public static List getNameServer() {
67 | return sun.net.dns.ResolverConfiguration.open().nameservers();
68 | }
69 |
70 | public static int getPort(URI uri) {
71 | int port = uri.getPort();
72 | if (port == -1) {
73 | if ("https".equals(uri.getScheme()))
74 | port = 443;
75 | else
76 | port = 80;
77 | }
78 | return port;
79 | }
80 |
81 | public static Charset parseCharset(String type) {
82 | if (type != null) {
83 | try {
84 | type = type.toLowerCase();
85 | int i = type.indexOf(CS);
86 | if (i != -1) {
87 | String charset = type.substring(i + CS.length()).trim();
88 | return Charset.forName(charset);
89 | }
90 | } catch (Exception ignore) {
91 | }
92 | }
93 | return null;
94 | }
95 |
96 | static Charset detectCharset(HttpResponse resp) {
97 | Charset result = parseCharset(resp.getHeader(CONTENT_TYPE));
98 | if (result == null) {
99 | // decode a little the find charset=???
100 | byte[] arr = resp.getContent().array();
101 | String s = new String(arr, 0, Math.min(350, arr.length), UTF_8);
102 | int idx = s.indexOf(CS);
103 | if (idx != -1) {
104 | int start = idx + CS.length();
105 | int end = s.indexOf('"', start);
106 | if (end != -1) {
107 | try {
108 | result = Charset.forName(s.substring(start, end));
109 | } catch (Exception ignore) {
110 | }
111 | }
112 | }
113 | }
114 | return result;
115 | }
116 |
117 | public static String bodyStr(HttpResponse m) {
118 | // TODO it's a bug, should not happen, "http://logos.md/2008/"
119 | m.removeHeader(TRANSFER_ENCODING);
120 |
121 | try {
122 | String contentEncoding = m.getHeader(CONTENT_ENCODING);
123 | DecoderEmbedder decoder = null;
124 | if ("gzip".equalsIgnoreCase(contentEncoding)
125 | || "x-gzip".equalsIgnoreCase(contentEncoding)) {
126 | decoder = new DecoderEmbedder(new ZlibDecoder(
127 | GZIP));
128 | } else if ("deflate".equalsIgnoreCase(contentEncoding)
129 | || "x-deflate".equalsIgnoreCase(contentEncoding)) {
130 | decoder = new DecoderEmbedder(new ZlibDecoder(
131 | ZLIB_OR_NONE));
132 | }
133 |
134 | ChannelBuffer body = m.getContent();
135 |
136 | if (decoder != null) {
137 | decoder.offer(body);
138 | ChannelBuffer b = wrappedBuffer(decoder
139 | .pollAll(new ChannelBuffer[decoder.size()]));
140 | if (decoder.finish()) {
141 | ChannelBuffer r = wrappedBuffer(decoder
142 | .pollAll(new ChannelBuffer[decoder.size()]));
143 | body = wrappedBuffer(b, r);
144 | } else {
145 | body = b;
146 | }
147 | m.setContent(body);// for detect charset
148 | }
149 | Charset ch = detectCharset(m);
150 | if (ch == null)
151 | ch = UTF_8;
152 | return new String(body.array(), 0, body.readableBytes(), ch);
153 | } catch (CodecEmbedderException e) {
154 | logger.trace(e.getMessage(), e); // incorrect CRC32 checksum
155 | return "";
156 | }
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/src/java/me/shenfeng/dns/DnsClientConstant.java:
--------------------------------------------------------------------------------
1 | package me.shenfeng.dns;
2 |
3 | public interface DnsClientConstant {
4 | static byte[] FLAGS_PARAMS = new byte[] { 1,// message is standard query,
5 | // not truncated, do recursively
6 | 0, // accept non-authenticated data
7 | 0, 1, // 1 question
8 | 0, 0, // 0 answer RRs
9 | 0, 0, // 0 authority RRs
10 | 0, 0 // 0 additional RRs
11 | };
12 | static byte[] A_IN = new byte[] { 0, 1, // type A(host address)
13 | 0, 1 // class IN
14 | };
15 | static final String DNS_UNKOWN_HOST = "unknown";
16 | static final String DNS_TIMEOUT = "timeout";
17 | static final String TIMER_NAME = "DNS-timer";
18 | }
19 |
--------------------------------------------------------------------------------
/src/java/me/shenfeng/dns/DnsPrefecher.java:
--------------------------------------------------------------------------------
1 | package me.shenfeng.dns;
2 |
3 | import static me.shenfeng.Utils.toBytes;
4 | import static org.jboss.netty.buffer.ChannelBuffers.dynamicBuffer;
5 |
6 | import java.io.IOException;
7 | import java.net.DatagramPacket;
8 | import java.net.DatagramSocket;
9 | import java.net.InetAddress;
10 | import java.net.InetSocketAddress;
11 | import java.net.SocketException;
12 | import java.net.URI;
13 | import java.util.ArrayList;
14 | import java.util.List;
15 | import java.util.concurrent.atomic.AtomicInteger;
16 |
17 | import me.shenfeng.Utils;
18 |
19 | import org.jboss.netty.buffer.ChannelBuffer;
20 | import org.slf4j.Logger;
21 | import org.slf4j.LoggerFactory;
22 |
23 | /**
24 | * Prefetching DNS A record, does not wait for resolution to be completed.
25 | *
26 | * Used when a local DNS cache server is present.
27 | *
28 | *
29 | * -Djava.net.preferIPv4Stack=true
30 | *
31 | * @see InetAddress
32 | * @author feng
33 | *
34 | */
35 | public class DnsPrefecher implements DnsClientConstant {
36 | final static Logger logger = LoggerFactory.getLogger(DnsPrefecher.class);
37 | private DatagramSocket mSocket;
38 | private final AtomicInteger mId = new AtomicInteger(0);
39 | private final ArrayList mServers;
40 |
41 | public static DnsPrefecher getInstance() {
42 |
43 | return Holder.instance;
44 | }
45 |
46 | private static class Holder {
47 | public static final DnsPrefecher instance = new DnsPrefecher();
48 | }
49 |
50 | public DnsPrefecher() {
51 | try {
52 | mSocket = new DatagramSocket();
53 | } catch (SocketException e) {
54 | logger.error("init DnsPrefecher error", e.getMessage());
55 | }
56 |
57 | List servers = Utils.getNameServer();
58 | mServers = new ArrayList(servers.size());
59 | for (String ns : servers) {
60 | mServers.add(new InetSocketAddress(ns, 53));
61 | }
62 | }
63 |
64 | public void prefetch(URI uri) {
65 | prefetch(uri.getHost());
66 | }
67 |
68 | public void prefetch(String domain) {
69 | if (Utils.isIP(domain))
70 | return;
71 |
72 | int id = mId.incrementAndGet();
73 | if (id > Short.MAX_VALUE) {
74 | id = Short.MAX_VALUE;
75 | mId.set(0);
76 | }
77 |
78 | final ChannelBuffer buffer = dynamicBuffer(64);
79 | buffer.writeBytes(toBytes(id));
80 | buffer.writeBytes(FLAGS_PARAMS);
81 |
82 | int start = 0;
83 | final int length = domain.length();
84 | for (int i = 0; i < length; ++i) {
85 | if (domain.charAt(i) == '.') {
86 | buffer.writeByte(i - start);
87 | buffer.writeBytes(domain.substring(start, i).getBytes());
88 | start = i + 1;
89 | }
90 | }
91 | buffer.writeByte(length - start);
92 | buffer.writeBytes(domain.substring(start).getBytes());
93 | buffer.writeByte(0);
94 | buffer.writeBytes(A_IN);
95 |
96 | try {
97 | for (InetSocketAddress server : mServers) {
98 | DatagramPacket packet = new DatagramPacket(buffer.array(),
99 | buffer.readerIndex(), buffer.readableBytes(), server);
100 | mSocket.send(packet);
101 | }
102 | } catch (IOException e) {
103 | logger.info("prefetch dns: " + domain, e);
104 | }
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/src/java/me/shenfeng/http/ConnectionListener.java:
--------------------------------------------------------------------------------
1 | package me.shenfeng.http;
2 |
3 | import static org.jboss.netty.buffer.ChannelBuffers.wrappedBuffer;
4 |
5 | import java.net.URI;
6 |
7 | import org.jboss.netty.channel.Channel;
8 | import org.jboss.netty.channel.ChannelFuture;
9 | import org.jboss.netty.channel.ChannelFutureListener;
10 | import org.slf4j.Logger;
11 | import org.slf4j.LoggerFactory;
12 |
13 | public class ConnectionListener implements ChannelFutureListener {
14 |
15 | private static Logger logger = LoggerFactory
16 | .getLogger(ConnectionListener.class);
17 | private boolean mIsSocks;
18 | private HttpResponseFuture mFuture;
19 | private URI mUri;
20 |
21 | public ConnectionListener(HttpResponseFuture future, URI uri,
22 | boolean isSocks) {
23 | mFuture = future;
24 | mIsSocks = isSocks;
25 | mUri = uri;
26 | }
27 |
28 | public void operationComplete(ChannelFuture f) throws Exception {
29 | Channel channel = f.getChannel();
30 | if (f.isSuccess()) {
31 | channel.getPipeline().getContext(ResponseHandler.class)
32 | .setAttachment(mFuture);
33 | if (mIsSocks) {
34 | channel.getPipeline().addFirst("socks",
35 | new SocksHandler(mFuture, mUri));
36 | // write version and authen info
37 | channel.write(wrappedBuffer(SocksHandler.VERSION_AUTH));
38 | } else {
39 | channel.write(mFuture.request).addListener(
40 | new ChannelFutureListener() {
41 | public void operationComplete(ChannelFuture future)
42 | throws Exception {
43 | mFuture.touch();
44 | }
45 | });
46 | }
47 |
48 | } else {
49 | channel.close();
50 | Throwable cause = f.getCause();
51 | mFuture.abort(cause);
52 | logger.trace(mFuture.request.toString(), cause);
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/java/me/shenfeng/http/Decoder.java:
--------------------------------------------------------------------------------
1 | package me.shenfeng.http;
2 |
3 | import static me.shenfeng.http.HttpClientConstant.TOO_LARGE;
4 | import static me.shenfeng.http.HttpClientConstant.UNKNOWN_CONTENT;
5 | import static org.jboss.netty.handler.codec.http.HttpHeaders.getContentLength;
6 | import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE;
7 |
8 | import org.jboss.netty.buffer.ChannelBuffer;
9 | import org.jboss.netty.channel.Channel;
10 | import org.jboss.netty.channel.ChannelHandlerContext;
11 | import org.jboss.netty.handler.codec.http.HttpMessage;
12 | import org.jboss.netty.handler.codec.http.HttpResponseDecoder;
13 |
14 | public class Decoder extends HttpResponseDecoder {
15 | final HttpClientConfig conf;
16 |
17 | protected Object decode(ChannelHandlerContext ctx, Channel channel,
18 | ChannelBuffer buffer, State state) throws Exception {
19 | Object o = super.decode(ctx, channel, buffer, state);
20 |
21 | HttpResponseFuture future = (HttpResponseFuture) ctx.getAttachment();
22 | if (o instanceof HttpMessage && future != null) {
23 | future.touch();
24 | HttpMessage msg = (HttpMessage) o;
25 | String type = msg.getHeader(CONTENT_TYPE);
26 | if (getContentLength(msg) > conf.maxLength) {
27 | future.done(TOO_LARGE);
28 | } else if (type != null && conf.acceptedContentTypes != null) {
29 | boolean accept = false;
30 | for (String t : conf.acceptedContentTypes) {
31 | if (type.contains(t)) {
32 | accept = true;
33 | break;
34 | }
35 | }
36 | if (!accept) {
37 | future.done(UNKNOWN_CONTENT);
38 | }
39 | }
40 | }
41 | return o;
42 | }
43 |
44 | public Decoder(HttpClientConfig conf) {
45 | super(4096, 8192, conf.maxChunkSize);
46 | this.conf = conf;
47 | }
48 | }
--------------------------------------------------------------------------------
/src/java/me/shenfeng/http/HttpClient.java:
--------------------------------------------------------------------------------
1 | package me.shenfeng.http;
2 |
3 | import static java.lang.System.currentTimeMillis;
4 | import static java.util.concurrent.Executors.newCachedThreadPool;
5 | import static me.shenfeng.Utils.getPort;
6 | import static org.jboss.netty.buffer.ChannelBuffers.wrappedBuffer;
7 | import static org.jboss.netty.channel.Channels.pipeline;
8 | import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.ACCEPT;
9 | import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.ACCEPT_ENCODING;
10 | import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.CONTENT_LENGTH;
11 | import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE;
12 | import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.HOST;
13 | import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.USER_AGENT;
14 | import static org.jboss.netty.handler.codec.http.HttpMethod.GET;
15 | import static org.jboss.netty.handler.codec.http.HttpMethod.POST;
16 | import static org.jboss.netty.handler.codec.http.HttpVersion.HTTP_1_1;
17 | import static org.jboss.netty.util.CharsetUtil.UTF_8;
18 | import static org.jboss.netty.util.ThreadNameDeterminer.CURRENT;
19 | import static org.jboss.netty.util.ThreadRenamingRunnable.setThreadNameDeterminer;
20 |
21 | import java.io.UnsupportedEncodingException;
22 | import java.net.InetAddress;
23 | import java.net.InetSocketAddress;
24 | import java.net.Proxy;
25 | import java.net.Proxy.Type;
26 | import java.net.URI;
27 | import java.net.URISyntaxException;
28 | import java.net.URLEncoder;
29 | import java.net.UnknownHostException;
30 | import java.util.Iterator;
31 | import java.util.Map;
32 | import java.util.Queue;
33 | import java.util.TreeMap;
34 | import java.util.concurrent.ConcurrentLinkedQueue;
35 | import java.util.concurrent.ExecutorService;
36 |
37 | import javax.net.ssl.SSLEngine;
38 |
39 | import me.shenfeng.PrefixThreadFactory;
40 | import me.shenfeng.Utils;
41 | import me.shenfeng.ssl.SslContextFactory;
42 |
43 | import org.jboss.netty.bootstrap.ClientBootstrap;
44 | import org.jboss.netty.channel.Channel;
45 | import org.jboss.netty.channel.ChannelFuture;
46 | import org.jboss.netty.channel.ChannelPipeline;
47 | import org.jboss.netty.channel.ChannelPipelineFactory;
48 | import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;
49 | import org.jboss.netty.handler.codec.http.DefaultHttpRequest;
50 | import org.jboss.netty.handler.codec.http.HttpChunkAggregator;
51 | import org.jboss.netty.handler.codec.http.HttpMethod;
52 | import org.jboss.netty.handler.codec.http.HttpRequest;
53 | import org.jboss.netty.handler.codec.http.HttpRequestEncoder;
54 | import org.jboss.netty.handler.ssl.SslHandler;
55 | import org.slf4j.Logger;
56 | import org.slf4j.LoggerFactory;
57 |
58 | public class HttpClient implements HttpClientConstant {
59 |
60 | static final Logger logger = LoggerFactory.getLogger(HttpClient.class);
61 | static final Map EMPTY = new TreeMap();
62 |
63 | private final ClientBootstrap mHttpBootstrap;
64 | private final ClientBootstrap mHttpsBootstrap;
65 | private volatile long mLastCheckTime = currentTimeMillis();
66 | private final HttpClientConfig mConf;
67 | private final Queue mFutures = new ConcurrentLinkedQueue();
68 |
69 | public HttpClient() {
70 | this(new HttpClientConfig());
71 | }
72 |
73 | public HttpClient(HttpClientConfig conf) {
74 | mConf = conf;
75 | setThreadNameDeterminer(CURRENT);
76 | ExecutorService boss = newCachedThreadPool(new PrefixThreadFactory(
77 | conf.bossNamePrefix));
78 | ExecutorService worker = newCachedThreadPool(new PrefixThreadFactory(
79 | conf.workerNamePrefix));
80 | NioClientSocketChannelFactory factory = new NioClientSocketChannelFactory(
81 | boss, worker, conf.workerThread);
82 |
83 | mHttpsBootstrap = new ClientBootstrap(factory);
84 | mHttpsBootstrap.setPipelineFactory(new HttpsClientPipelineFactory(
85 | mConf.maxLength, conf));
86 |
87 | mHttpBootstrap = new ClientBootstrap(factory);
88 | mHttpBootstrap.setPipelineFactory(new HttpClientPipelineFactory(
89 | mConf.maxLength, conf));
90 |
91 | conf(mHttpBootstrap);
92 | conf(mHttpsBootstrap);
93 |
94 | }
95 |
96 | private HttpRequest buildRequest(HttpMethod method, URI uri,
97 | Map headers, Map params,
98 | Proxy proxy) {
99 |
100 | String path = proxy.type() == Type.HTTP ? uri.toString() : Utils
101 | .getPath(uri);
102 |
103 | HttpRequest request = new DefaultHttpRequest(HTTP_1_1, method, path);
104 |
105 | request.setHeader(HOST, uri.getHost());
106 | request.setHeader(USER_AGENT, mConf.userAgent);
107 | request.setHeader(ACCEPT, "*/*");
108 | request.setHeader(ACCEPT_ENCODING, "gzip, deflate");
109 |
110 | for (Map.Entry entry : headers.entrySet()) {
111 | Object v = entry.getValue();
112 | if (v == null) {
113 | request.removeHeader(entry.getKey());
114 | } else {
115 | request.setHeader(entry.getKey(), entry.getValue());
116 | }
117 | }
118 |
119 | if (params != null) {
120 | StringBuilder sb = new StringBuilder(32);
121 | for (java.util.Map.Entry e : params.entrySet()) {
122 | if (sb.length() > 0) {
123 | sb.append("&");
124 | }
125 | try {
126 | sb.append(URLEncoder.encode(e.getKey(), "utf8"));
127 | sb.append("=");
128 | sb.append(URLEncoder.encode(e.getValue().toString(),
129 | "utf8"));
130 | } catch (UnsupportedEncodingException ignore) {
131 | }
132 | }
133 |
134 | byte[] data = sb.toString().getBytes(UTF_8);
135 | request.setHeader(CONTENT_TYPE,
136 | "application/x-www-form-urlencoded");
137 | request.setHeader(CONTENT_LENGTH, data.length);
138 | request.setContent(wrappedBuffer(data));
139 | }
140 |
141 | return request;
142 |
143 | }
144 |
145 | private void checkTimeoutIfNeeded() {
146 | if (currentTimeMillis() - mLastCheckTime > mConf.timerInterval) {
147 | // thread safe
148 | final Iterator it = mFutures.iterator();
149 | while (it.hasNext()) {
150 | HttpResponseFuture r = it.next();
151 | if (r.isDone() || r.isTimeout()) {
152 | it.remove();
153 | }
154 | }
155 | mLastCheckTime = currentTimeMillis();
156 | }
157 | }
158 |
159 | public void close() {
160 | mHttpBootstrap.releaseExternalResources();
161 | mHttpsBootstrap.releaseExternalResources();
162 | }
163 |
164 | private void conf(ClientBootstrap bootstrap) {
165 | bootstrap.setOption("connectTimeoutMillis",
166 | mConf.connectionTimeOutInMs);
167 | bootstrap.setOption("receiveBufferSize", mConf.receiveBuffer);
168 | bootstrap.setOption("sendBufferSize", mConf.sendBuffer);
169 | bootstrap.setOption("reuseAddress", true);
170 | }
171 |
172 | public HttpResponseFuture execGet(URI uri, Map headers) {
173 | return execGet(uri, headers, Proxy.NO_PROXY);
174 | }
175 |
176 | public HttpResponseFuture execGet(String uri) throws URISyntaxException {
177 | return execGet(new URI(uri), EMPTY, Proxy.NO_PROXY);
178 | }
179 |
180 | public HttpResponseFuture execGet(URI uri, Map headers,
181 | Proxy proxy) {
182 | HttpRequest request = buildRequest(GET, uri, headers, null, proxy);
183 | return execRequest(request, uri, proxy);
184 | }
185 |
186 | public HttpResponseFuture execPost(URI uri, Map headers,
187 | Map params) {
188 | HttpRequest request = buildRequest(POST, uri, headers, params,
189 | Proxy.NO_PROXY);
190 | return execRequest(request, uri, Proxy.NO_PROXY);
191 | }
192 |
193 | public HttpResponseFuture execPost(URI uri, Map headers,
194 | Map params, Proxy proxy) {
195 | HttpRequest request = buildRequest(POST, uri, headers, params, proxy);
196 | return execRequest(request, uri, proxy);
197 | }
198 |
199 | private HttpResponseFuture execRequest(HttpRequest request, URI uri,
200 | Proxy proxy) {
201 | checkTimeoutIfNeeded();
202 | if (proxy == null) {
203 | proxy = Proxy.NO_PROXY;
204 | }
205 | final HttpResponseFuture future = new HttpResponseFuture(
206 | mConf.requestTimeoutInMs, request);
207 | boolean ssl = "https".equals(uri.getScheme());
208 |
209 | try {
210 | InetSocketAddress addr = proxy.type() == Type.DIRECT ? new InetSocketAddress(
211 | InetAddress.getByName(uri.getHost()), getPort(uri))
212 | : (InetSocketAddress) proxy.address();
213 |
214 | ClientBootstrap bootstrap = ssl ? mHttpsBootstrap
215 | : mHttpBootstrap;
216 | ChannelFuture cf = bootstrap.connect(addr);
217 | Channel ch = cf.getChannel();
218 | future.setChannel(ch);
219 | ch.getPipeline().getContext(Decoder.class).setAttachment(future);
220 | cf.addListener(new ConnectionListener(future, uri,
221 | proxy.type() == Type.SOCKS));
222 | mFutures.add(future);
223 | } catch (UnknownHostException e) {
224 | future.done(UNKOWN_HOST);
225 | }
226 | return future;
227 | }
228 | }
229 |
230 | class HttpClientPipelineFactory implements ChannelPipelineFactory {
231 |
232 | final int maxLength;
233 | final HttpClientConfig conf;
234 |
235 | public HttpClientPipelineFactory(int maxLength, HttpClientConfig conf) {
236 | this.maxLength = maxLength;
237 | this.conf = conf;
238 | }
239 |
240 | public ChannelPipeline getPipeline() throws Exception {
241 | ChannelPipeline pipeline = pipeline();
242 | pipeline.addLast("decoder", new Decoder(conf));
243 | pipeline.addLast("encoder", new HttpRequestEncoder());
244 | pipeline.addLast("aggregator", new HttpChunkAggregator(maxLength));
245 | pipeline.addLast("handler", new ResponseHandler());
246 | return pipeline;
247 | }
248 | }
249 |
250 | class HttpsClientPipelineFactory implements ChannelPipelineFactory {
251 |
252 | final int maxLength;
253 | final HttpClientConfig conf;
254 |
255 | public HttpsClientPipelineFactory(int maxLength, HttpClientConfig conf) {
256 | this.maxLength = maxLength;
257 | this.conf = conf;
258 | }
259 |
260 | public ChannelPipeline getPipeline() throws Exception {
261 | ChannelPipeline pipeline = pipeline();
262 | SSLEngine engine = SslContextFactory.getClientContext()
263 | .createSSLEngine();
264 | engine.setUseClientMode(true);
265 | pipeline.addLast("ssl", new SslHandler(engine));
266 |
267 | pipeline.addLast("decoder", new Decoder(conf));
268 | pipeline.addLast("encoder", new HttpRequestEncoder());
269 | pipeline.addLast("aggregator", new HttpChunkAggregator(maxLength));
270 | pipeline.addLast("handler", new ResponseHandler());
271 | return pipeline;
272 | }
273 |
274 | }
275 |
--------------------------------------------------------------------------------
/src/java/me/shenfeng/http/HttpClientConfig.java:
--------------------------------------------------------------------------------
1 | package me.shenfeng.http;
2 |
3 | import java.util.List;
4 |
5 | import org.jboss.netty.handler.codec.http.HttpMessageDecoder;
6 |
7 | public class HttpClientConfig {
8 |
9 | protected List acceptedContentTypes;
10 | protected String bossNamePrefix = "Http Boss";
11 | protected int connectionTimeOutInMs = 4500;
12 | protected int maxChunkSize = 32 * 1024;
13 | protected int maxLength = 1024 * 512;
14 | protected int receiveBuffer = 1024 * 32;
15 | protected int sendBuffer = 1024 * 32;
16 | protected int requestTimeoutInMs = 20000;
17 | protected int timerInterval = 1500;
18 | protected String userAgent = "Async-java/1.0.0";
19 |
20 | protected String workerNamePrefix = "Http Worker";
21 | protected int workerThread = 1;
22 |
23 | public HttpClientConfig() {
24 | }
25 |
26 | public void setAcceptedContentTypes(List acceptedContentTypes) {
27 | this.acceptedContentTypes = acceptedContentTypes;
28 | }
29 |
30 | public void setBossNamePrefix(String bossNamePrefix) {
31 | this.bossNamePrefix = bossNamePrefix;
32 | }
33 |
34 | public void setConnectionTimeOutInMs(int connectionTimeOutInMs) {
35 | this.connectionTimeOutInMs = connectionTimeOutInMs;
36 | }
37 |
38 | /**
39 | * @see HttpMessageDecoder
40 | */
41 | public void setMaxChunkSize(int maxChunkSize) {
42 | this.maxChunkSize = maxChunkSize;
43 | }
44 |
45 | public void setMaxLength(int maxLength) {
46 | this.maxLength = maxLength;
47 | }
48 |
49 | public void setReceiveBuffer(int receiveBuffer) {
50 | this.receiveBuffer = receiveBuffer;
51 | }
52 |
53 | public void setRequestTimeoutInMs(int requestTimeoutInMs) {
54 | this.requestTimeoutInMs = requestTimeoutInMs;
55 | }
56 |
57 | public void setSendBuffer(int sendBuffer) {
58 | this.sendBuffer = sendBuffer;
59 | }
60 |
61 | public void setTimerInterval(int timerInterval) {
62 | this.timerInterval = timerInterval;
63 | }
64 |
65 | public void setUserAgent(String userAgent) {
66 | this.userAgent = userAgent;
67 | }
68 |
69 | public void setWorkerNamePrefix(String workerNamePrefix) {
70 | this.workerNamePrefix = workerNamePrefix;
71 | }
72 |
73 | public void setWorkerThread(int workerThread) {
74 | this.workerThread = workerThread;
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/java/me/shenfeng/http/HttpClientConstant.java:
--------------------------------------------------------------------------------
1 | package me.shenfeng.http;
2 |
3 | import static org.jboss.netty.handler.codec.http.HttpVersion.HTTP_1_1;
4 |
5 | import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
6 | import org.jboss.netty.handler.codec.http.HttpResponse;
7 | import org.jboss.netty.handler.codec.http.HttpResponseStatus;
8 |
9 | /**
10 | * home defined http status
11 | *
12 | * @author feng
13 | *
14 | */
15 | public interface HttpClientConstant {
16 |
17 | final static HttpResponse UNKOWN_ERROR = new DefaultHttpResponse(
18 | HTTP_1_1, new HttpResponseStatus(150, "unknow error"));
19 |
20 | final static HttpResponse UNKOWN_HOST = new DefaultHttpResponse(HTTP_1_1,
21 | new HttpResponseStatus(160, "unknow host"));
22 |
23 | final static HttpResponse CONN_ERROR = new DefaultHttpResponse(HTTP_1_1,
24 | new HttpResponseStatus(170, "connecton error"));
25 | final static HttpResponse CONN_TIMEOUT = new DefaultHttpResponse(
26 | HTTP_1_1, new HttpResponseStatus(172, "connecton timeout"));
27 | final static HttpResponse CONN_RESET = new DefaultHttpResponse(HTTP_1_1,
28 | new HttpResponseStatus(175, "connecton reset"));
29 |
30 | final static HttpResponse NULL_LOCATION = new DefaultHttpResponse(
31 | HTTP_1_1, new HttpResponseStatus(181, "null location"));
32 | final static HttpResponse BAD_URL = new DefaultHttpResponse(HTTP_1_1,
33 | new HttpResponseStatus(182, "bad url"));
34 | final static HttpResponse IGNORED_URL = new DefaultHttpResponse(HTTP_1_1,
35 | new HttpResponseStatus(183, "ignored url"));
36 |
37 | final static HttpResponse UNKNOWN_CONTENT = new DefaultHttpResponse(
38 | HTTP_1_1, new HttpResponseStatus(190, "unknow content type"));
39 |
40 | final static HttpResponse ABORT = new DefaultHttpResponse(HTTP_1_1,
41 | new HttpResponseStatus(471, "client abort"));
42 |
43 | final static HttpResponse TOO_LARGE = new DefaultHttpResponse(HTTP_1_1,
44 | new HttpResponseStatus(513, "body too large"));
45 | final static HttpResponse TIMEOUT = new DefaultHttpResponse(HTTP_1_1,
46 | new HttpResponseStatus(520, "server timeout"));
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/src/java/me/shenfeng/http/HttpResponseFuture.java:
--------------------------------------------------------------------------------
1 | package me.shenfeng.http;
2 |
3 | import static java.lang.System.currentTimeMillis;
4 | import static java.util.concurrent.TimeUnit.MILLISECONDS;
5 | import static me.shenfeng.http.HttpClientConstant.CONN_ERROR;
6 | import static me.shenfeng.http.HttpClientConstant.CONN_RESET;
7 | import static me.shenfeng.http.HttpClientConstant.CONN_TIMEOUT;
8 | import static me.shenfeng.http.HttpClientConstant.TIMEOUT;
9 | import static me.shenfeng.http.HttpClientConstant.TOO_LARGE;
10 | import static me.shenfeng.http.HttpClientConstant.UNKOWN_ERROR;
11 |
12 | import java.io.IOException;
13 | import java.net.ConnectException;
14 | import java.util.concurrent.ExecutionException;
15 | import java.util.concurrent.TimeUnit;
16 | import java.util.concurrent.TimeoutException;
17 |
18 | import me.shenfeng.AbstractResponseFuture;
19 |
20 | import org.jboss.netty.channel.Channel;
21 | import org.jboss.netty.handler.codec.frame.TooLongFrameException;
22 | import org.jboss.netty.handler.codec.http.HttpRequest;
23 | import org.jboss.netty.handler.codec.http.HttpResponse;
24 |
25 | public class HttpResponseFuture extends AbstractResponseFuture {
26 |
27 | private volatile Object mAttachment;
28 | private volatile Channel mChannel;
29 | private volatile long mTouchTime;
30 | private final int mTimeout;
31 | final HttpRequest request; // package private
32 |
33 | public HttpResponseFuture(int timeout, HttpRequest request) {
34 | mTimeout = timeout;
35 | this.request = request;
36 | mTouchTime = currentTimeMillis();
37 | }
38 |
39 | public boolean isTimeout() {
40 | if (mTimeout + mTouchTime - currentTimeMillis() < 0) {
41 | return done(TIMEOUT);
42 | }
43 | return false;
44 | }
45 |
46 | public boolean done(HttpResponse result) {
47 | if (mChannel != null)
48 | mChannel.close();
49 | return super.done(result);
50 | }
51 |
52 | public void setAttachment(Object att) {
53 | mAttachment = att;
54 | }
55 |
56 | public Object getAttachment() {
57 | return mAttachment;
58 | }
59 |
60 | public boolean abort(Throwable t) {
61 | HttpResponse resp = UNKOWN_ERROR;
62 | String msg = t.getMessage();
63 | if (t instanceof TooLongFrameException) {
64 | resp = TOO_LARGE;
65 | } else if (t instanceof ConnectException) {
66 | if (msg != null && msg.indexOf("timed out") != -1)
67 | resp = CONN_TIMEOUT;
68 | else
69 | resp = CONN_ERROR;
70 | } else if (t instanceof IOException && msg != null
71 | && msg.indexOf("reset") != -1) {
72 | resp = CONN_RESET;
73 | }
74 | return done(resp);
75 | }
76 |
77 | public HttpResponse get() throws InterruptedException, ExecutionException {
78 | try {
79 | return get(mTimeout, MILLISECONDS);
80 | } catch (TimeoutException e) {
81 | // ignore
82 | return TIMEOUT;
83 | }
84 | }
85 |
86 | public HttpResponse get(long timeout, TimeUnit unit)
87 | throws InterruptedException, ExecutionException, TimeoutException {
88 | long time = unit.toMillis(timeout);
89 | long wait = time + mTouchTime - currentTimeMillis();
90 | while (mLatch.getCount() > 0 && wait > 0) {
91 | mLatch.await(wait, MILLISECONDS);
92 | wait = time + mTouchTime - currentTimeMillis();
93 | }
94 | done(TIMEOUT);
95 | return mResult.get();
96 | }
97 |
98 | public void setChannel(Channel ch) {
99 | mChannel = ch;
100 | }
101 |
102 | public void touch() {
103 | mTouchTime = currentTimeMillis();
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/src/java/me/shenfeng/http/ResponseHandler.java:
--------------------------------------------------------------------------------
1 | package me.shenfeng.http;
2 |
3 | import org.jboss.netty.channel.ChannelHandlerContext;
4 | import org.jboss.netty.channel.ExceptionEvent;
5 | import org.jboss.netty.channel.MessageEvent;
6 | import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
7 | import org.jboss.netty.handler.codec.http.HttpResponse;
8 | import org.slf4j.Logger;
9 | import org.slf4j.LoggerFactory;
10 |
11 | public class ResponseHandler extends SimpleChannelUpstreamHandler {
12 |
13 | private static Logger logger = LoggerFactory
14 | .getLogger(ResponseHandler.class);
15 |
16 | public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
17 | throws Exception {
18 | ctx.getChannel().close();
19 | Throwable cause = e.getCause();
20 | HttpResponseFuture future = (HttpResponseFuture) ctx.getAttachment();
21 | if (future != null) {
22 | logger.trace(future.request.toString(), cause);
23 | future.abort(cause);
24 | } else {
25 | logger.trace(cause.getMessage(), cause);
26 | }
27 | }
28 |
29 | public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
30 | throws Exception {
31 | HttpResponseFuture future = (HttpResponseFuture) ctx.getAttachment();
32 | HttpResponse response = (HttpResponse) e.getMessage();
33 | ctx.getChannel().close();
34 | future.done(response);
35 | }
36 | }
--------------------------------------------------------------------------------
/src/java/me/shenfeng/http/SocksHandler.java:
--------------------------------------------------------------------------------
1 | package me.shenfeng.http;
2 |
3 | import static java.net.InetAddress.getByName;
4 | import static me.shenfeng.Utils.getPort;
5 |
6 | import java.net.URI;
7 |
8 | import org.jboss.netty.buffer.ChannelBuffer;
9 | import org.jboss.netty.buffer.ChannelBuffers;
10 | import org.jboss.netty.channel.Channel;
11 | import org.jboss.netty.channel.ChannelHandlerContext;
12 | import org.jboss.netty.channel.MessageEvent;
13 | import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
14 |
15 | public class SocksHandler extends SimpleChannelUpstreamHandler {
16 | static final byte PROTO_VER5 = 5;
17 | static final byte CONNECT = 1;
18 | static final byte NO_AUTH = 0;
19 | static final byte IPV4 = 1;
20 |
21 | static final byte[] VERSION_AUTH = new byte[] { PROTO_VER5, 1, NO_AUTH };
22 | static final byte[] CON = new byte[] { PROTO_VER5, CONNECT, 0, IPV4 };
23 |
24 | private State state = State.INIT;
25 | private final HttpResponseFuture mFuture;
26 | private final URI mUri;
27 |
28 | static enum State {
29 | INIT, CON_SENT
30 | }
31 |
32 | public SocksHandler(HttpResponseFuture future, URI uri) {
33 | mFuture = future;
34 | mUri = uri;
35 | }
36 |
37 | public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
38 | throws Exception {
39 | ChannelBuffer buffer = (ChannelBuffer) e.getMessage();
40 | Channel channel = ctx.getChannel();
41 | if (state == State.INIT && buffer.readableBytes() == 2) {
42 | buffer.readByte();
43 | buffer.readByte();
44 |
45 | ChannelBuffer send = ChannelBuffers.buffer(10);
46 | send.writeBytes(CON);
47 | send.writeBytes(getByName(mUri.getHost()).getAddress());
48 | send.writeShort(getPort(mUri));
49 | ctx.getChannel().write(send);
50 |
51 | state = State.CON_SENT;
52 | } else if (state == State.CON_SENT && buffer.readableBytes() == 10) {
53 | byte[] data = new byte[10];
54 | buffer.readBytes(data);
55 | if (data[1] != 0) {
56 | mFuture.done(HttpClientConstant.UNKOWN_ERROR);
57 | channel.close();
58 | } else {
59 | channel.write(mFuture.request);
60 | }
61 | } else {
62 | ctx.getPipeline().remove(this);
63 | ctx.getPipeline().sendUpstream(e);
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/java/me/shenfeng/ssl/SslContextFactory.java:
--------------------------------------------------------------------------------
1 | package me.shenfeng.ssl;
2 |
3 | import javax.net.ssl.SSLContext;
4 |
5 | /**
6 | * copied from netty
7 | *
8 | */
9 | public class SslContextFactory {
10 |
11 | private static final String PROTOCOL = "TLS";
12 | private static final SSLContext CLIENT_CONTEXT;
13 |
14 | static {
15 | SSLContext clientContext = null;
16 |
17 | try {
18 | clientContext = SSLContext.getInstance(PROTOCOL);
19 | clientContext.init(null, TrustManagerFactory.getTrustManagers(),
20 | null);
21 | } catch (Exception e) {
22 | throw new Error(
23 | "Failed to initialize the client-side SSLContext", e);
24 | }
25 |
26 | CLIENT_CONTEXT = clientContext;
27 | }
28 |
29 | public static SSLContext getClientContext() {
30 | return CLIENT_CONTEXT;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/java/me/shenfeng/ssl/SslKeyStore.java:
--------------------------------------------------------------------------------
1 | package me.shenfeng.ssl;
2 |
3 | import java.io.ByteArrayInputStream;
4 | import java.io.InputStream;
5 |
6 | /**
7 | * copied from netty
8 | *
9 | */
10 | public class SslKeyStore {
11 | private static final short[] DATA = new short[] { 0xfe, 0xed, 0xfe, 0xed,
12 | 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
13 | 0x01, 0x00, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x00,
14 | 0x00, 0x01, 0x1a, 0x9f, 0x57, 0xa5, 0x27, 0x00, 0x00, 0x01, 0x9a,
15 | 0x30, 0x82, 0x01, 0x96, 0x30, 0x0e, 0x06, 0x0a, 0x2b, 0x06, 0x01,
16 | 0x04, 0x01, 0x2a, 0x02, 0x11, 0x01, 0x01, 0x05, 0x00, 0x04, 0x82,
17 | 0x01, 0x82, 0x48, 0x6d, 0xcf, 0x16, 0xb5, 0x50, 0x95, 0x36, 0xbf,
18 | 0x47, 0x27, 0x50, 0x58, 0x0d, 0xa2, 0x52, 0x7e, 0x25, 0xab, 0x14,
19 | 0x1a, 0x26, 0x5e, 0x2d, 0x8a, 0x23, 0x90, 0x60, 0x7f, 0x12, 0x20,
20 | 0x56, 0xd1, 0x43, 0xa2, 0x6b, 0x47, 0x5d, 0xed, 0x9d, 0xd4, 0xe5,
21 | 0x83, 0x28, 0x89, 0xc2, 0x16, 0x4c, 0x76, 0x06, 0xad, 0x8e, 0x8c,
22 | 0x29, 0x1a, 0x9b, 0x0f, 0xdd, 0x60, 0x4b, 0xb4, 0x62, 0x82, 0x9e,
23 | 0x4a, 0x63, 0x83, 0x2e, 0xd2, 0x43, 0x78, 0xc2, 0x32, 0x1f, 0x60,
24 | 0xa9, 0x8a, 0x7f, 0x0f, 0x7c, 0xa6, 0x1d, 0xe6, 0x92, 0x9e, 0x52,
25 | 0xc7, 0x7d, 0xbb, 0x35, 0x3b, 0xaa, 0x89, 0x73, 0x4c, 0xfb, 0x99,
26 | 0x54, 0x97, 0x99, 0x28, 0x6e, 0x66, 0x5b, 0xf7, 0x9b, 0x7e, 0x6d,
27 | 0x8a, 0x2f, 0xfa, 0xc3, 0x1e, 0x71, 0xb9, 0xbd, 0x8f, 0xc5, 0x63,
28 | 0x25, 0x31, 0x20, 0x02, 0xff, 0x02, 0xf0, 0xc9, 0x2c, 0xdd, 0x3a,
29 | 0x10, 0x30, 0xab, 0xe5, 0xad, 0x3d, 0x1a, 0x82, 0x77, 0x46, 0xed,
30 | 0x03, 0x38, 0xa4, 0x73, 0x6d, 0x36, 0x36, 0x33, 0x70, 0xb2, 0x63,
31 | 0x20, 0xca, 0x03, 0xbf, 0x5a, 0xf4, 0x7c, 0x35, 0xf0, 0x63, 0x1a,
32 | 0x12, 0x33, 0x12, 0x58, 0xd9, 0xa2, 0x63, 0x6b, 0x63, 0x82, 0x41,
33 | 0x65, 0x70, 0x37, 0x4b, 0x99, 0x04, 0x9f, 0xdd, 0x5e, 0x07, 0x01,
34 | 0x95, 0x9f, 0x36, 0xe8, 0xc3, 0x66, 0x2a, 0x21, 0x69, 0x68, 0x40,
35 | 0xe6, 0xbc, 0xbb, 0x85, 0x81, 0x21, 0x13, 0xe6, 0xa4, 0xcf, 0xd3,
36 | 0x67, 0xe3, 0xfd, 0x75, 0xf0, 0xdf, 0x83, 0xe0, 0xc5, 0x36, 0x09,
37 | 0xac, 0x1b, 0xd4, 0xf7, 0x2a, 0x23, 0x57, 0x1c, 0x5c, 0x0f, 0xf4,
38 | 0xcf, 0xa2, 0xcf, 0xf5, 0xbd, 0x9c, 0x69, 0x98, 0x78, 0x3a, 0x25,
39 | 0xe4, 0xfd, 0x85, 0x11, 0xcc, 0x7d, 0xef, 0xeb, 0x74, 0x60, 0xb1,
40 | 0xb7, 0xfb, 0x1f, 0x0e, 0x62, 0xff, 0xfe, 0x09, 0x0a, 0xc3, 0x80,
41 | 0x2f, 0x10, 0x49, 0x89, 0x78, 0xd2, 0x08, 0xfa, 0x89, 0x22, 0x45,
42 | 0x91, 0x21, 0xbc, 0x90, 0x3e, 0xad, 0xb3, 0x0a, 0xb4, 0x0e, 0x1c,
43 | 0xa1, 0x93, 0x92, 0xd8, 0x72, 0x07, 0x54, 0x60, 0xe7, 0x91, 0xfc,
44 | 0xd9, 0x3c, 0xe1, 0x6f, 0x08, 0xe4, 0x56, 0xf6, 0x0b, 0xb0, 0x3c,
45 | 0x39, 0x8a, 0x2d, 0x48, 0x44, 0x28, 0x13, 0xca, 0xe9, 0xf7, 0xa3,
46 | 0xb6, 0x8a, 0x5f, 0x31, 0xa9, 0x72, 0xf2, 0xde, 0x96, 0xf2, 0xb1,
47 | 0x53, 0xb1, 0x3e, 0x24, 0x57, 0xfd, 0x18, 0x45, 0x1f, 0xc5, 0x33,
48 | 0x1b, 0xa4, 0xe8, 0x21, 0xfa, 0x0e, 0xb2, 0xb9, 0xcb, 0xc7, 0x07,
49 | 0x41, 0xdd, 0x2f, 0xb6, 0x6a, 0x23, 0x18, 0xed, 0xc1, 0xef, 0xe2,
50 | 0x4b, 0xec, 0xc9, 0xba, 0xfb, 0x46, 0x43, 0x90, 0xd7, 0xb5, 0x68,
51 | 0x28, 0x31, 0x2b, 0x8d, 0xa8, 0x51, 0x63, 0xf7, 0x53, 0x99, 0x19,
52 | 0x68, 0x85, 0x66, 0x00, 0x00, 0x00, 0x01, 0x00, 0x05, 0x58, 0x2e,
53 | 0x35, 0x30, 0x39, 0x00, 0x00, 0x02, 0x3a, 0x30, 0x82, 0x02, 0x36,
54 | 0x30, 0x82, 0x01, 0xe0, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x04,
55 | 0x48, 0x59, 0xf1, 0x92, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48,
56 | 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81, 0xa0,
57 | 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02,
58 | 0x4b, 0x52, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08,
59 | 0x13, 0x0a, 0x4b, 0x79, 0x75, 0x6e, 0x67, 0x67, 0x69, 0x2d, 0x64,
60 | 0x6f, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13,
61 | 0x0b, 0x53, 0x65, 0x6f, 0x6e, 0x67, 0x6e, 0x61, 0x6d, 0x2d, 0x73,
62 | 0x69, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13,
63 | 0x11, 0x54, 0x68, 0x65, 0x20, 0x4e, 0x65, 0x74, 0x74, 0x79, 0x20,
64 | 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x31, 0x18, 0x30, 0x16,
65 | 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x0f, 0x45, 0x78, 0x61, 0x6d,
66 | 0x70, 0x6c, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x73,
67 | 0x31, 0x30, 0x30, 0x2e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x27,
68 | 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x63, 0x68, 0x61, 0x74, 0x2e,
69 | 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x6e, 0x65, 0x74,
70 | 0x74, 0x79, 0x2e, 0x67, 0x6c, 0x65, 0x61, 0x6d, 0x79, 0x6e, 0x6f,
71 | 0x64, 0x65, 0x2e, 0x6e, 0x65, 0x74, 0x30, 0x20, 0x17, 0x0d, 0x30,
72 | 0x38, 0x30, 0x36, 0x31, 0x39, 0x30, 0x35, 0x34, 0x31, 0x33, 0x38,
73 | 0x5a, 0x18, 0x0f, 0x32, 0x31, 0x38, 0x37, 0x31, 0x31, 0x32, 0x34,
74 | 0x30, 0x35, 0x34, 0x31, 0x33, 0x38, 0x5a, 0x30, 0x81, 0xa0, 0x31,
75 | 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x4b,
76 | 0x52, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13,
77 | 0x0a, 0x4b, 0x79, 0x75, 0x6e, 0x67, 0x67, 0x69, 0x2d, 0x64, 0x6f,
78 | 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x07, 0x13, 0x0b,
79 | 0x53, 0x65, 0x6f, 0x6e, 0x67, 0x6e, 0x61, 0x6d, 0x2d, 0x73, 0x69,
80 | 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x11,
81 | 0x54, 0x68, 0x65, 0x20, 0x4e, 0x65, 0x74, 0x74, 0x79, 0x20, 0x50,
82 | 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x31, 0x18, 0x30, 0x16, 0x06,
83 | 0x03, 0x55, 0x04, 0x0b, 0x13, 0x0f, 0x45, 0x78, 0x61, 0x6d, 0x70,
84 | 0x6c, 0x65, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x73, 0x31,
85 | 0x30, 0x30, 0x2e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x27, 0x73,
86 | 0x65, 0x63, 0x75, 0x72, 0x65, 0x63, 0x68, 0x61, 0x74, 0x2e, 0x65,
87 | 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x6e, 0x65, 0x74, 0x74,
88 | 0x79, 0x2e, 0x67, 0x6c, 0x65, 0x61, 0x6d, 0x79, 0x6e, 0x6f, 0x64,
89 | 0x65, 0x2e, 0x6e, 0x65, 0x74, 0x30, 0x5c, 0x30, 0x0d, 0x06, 0x09,
90 | 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00,
91 | 0x03, 0x4b, 0x00, 0x30, 0x48, 0x02, 0x41, 0x00, 0xc3, 0xe3, 0x5e,
92 | 0x41, 0xa7, 0x87, 0x11, 0x00, 0x42, 0x2a, 0xb0, 0x4b, 0xed, 0xb2,
93 | 0xe0, 0x23, 0xdb, 0xb1, 0x3d, 0x58, 0x97, 0x35, 0x60, 0x0b, 0x82,
94 | 0x59, 0xd3, 0x00, 0xea, 0xd4, 0x61, 0xb8, 0x79, 0x3f, 0xb6, 0x3c,
95 | 0x12, 0x05, 0x93, 0x2e, 0x9a, 0x59, 0x68, 0x14, 0x77, 0x3a, 0xc8,
96 | 0x50, 0x25, 0x57, 0xa4, 0x49, 0x18, 0x63, 0x41, 0xf0, 0x2d, 0x28,
97 | 0xec, 0x06, 0xfb, 0xb4, 0x9f, 0xbf, 0x02, 0x03, 0x01, 0x00, 0x01,
98 | 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01,
99 | 0x01, 0x05, 0x05, 0x00, 0x03, 0x41, 0x00, 0x65, 0x6c, 0x30, 0x01,
100 | 0xc2, 0x8e, 0x3e, 0xcb, 0xb3, 0x77, 0x48, 0xe9, 0x66, 0x61, 0x9a,
101 | 0x40, 0x86, 0xaf, 0xf6, 0x03, 0xeb, 0xba, 0x6a, 0xf2, 0xfd, 0xe2,
102 | 0xaf, 0x36, 0x5e, 0x7b, 0xaa, 0x22, 0x04, 0xdd, 0x2c, 0x20, 0xc4,
103 | 0xfc, 0xdd, 0xd0, 0x82, 0x20, 0x1c, 0x3d, 0xd7, 0x9e, 0x5e, 0x5c,
104 | 0x92, 0x5a, 0x76, 0x71, 0x28, 0xf5, 0x07, 0x7d, 0xa2, 0x81, 0xba,
105 | 0x77, 0x9f, 0x2a, 0xd9, 0x44, 0x00, 0x00, 0x00, 0x01, 0x00, 0x05,
106 | 0x6d, 0x79, 0x6b, 0x65, 0x79, 0x00, 0x00, 0x01, 0x1a, 0x9f, 0x5b,
107 | 0x56, 0xa0, 0x00, 0x00, 0x01, 0x99, 0x30, 0x82, 0x01, 0x95, 0x30,
108 | 0x0e, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x2a, 0x02, 0x11,
109 | 0x01, 0x01, 0x05, 0x00, 0x04, 0x82, 0x01, 0x81, 0x29, 0xa8, 0xb6,
110 | 0x08, 0x0c, 0x85, 0x75, 0x3e, 0xdd, 0xb5, 0xe5, 0x1a, 0x87, 0x68,
111 | 0xd1, 0x90, 0x4b, 0x29, 0x31, 0xee, 0x90, 0xbc, 0x9d, 0x73, 0xa0,
112 | 0x3f, 0xe9, 0x0b, 0xa4, 0xef, 0x30, 0x9b, 0x36, 0x9a, 0xb2, 0x54,
113 | 0x77, 0x81, 0x07, 0x4b, 0xaa, 0xa5, 0x77, 0x98, 0xe1, 0xeb, 0xb5,
114 | 0x7c, 0x4e, 0x48, 0xd5, 0x08, 0xfc, 0x2c, 0x36, 0xe2, 0x65, 0x03,
115 | 0xac, 0xe5, 0xf3, 0x96, 0xb7, 0xd0, 0xb5, 0x3b, 0x92, 0xe4, 0x14,
116 | 0x05, 0x7a, 0x6a, 0x92, 0x56, 0xfe, 0x4e, 0xab, 0xd3, 0x0e, 0x32,
117 | 0x04, 0x22, 0x22, 0x74, 0x47, 0x7d, 0xec, 0x21, 0x99, 0x30, 0x31,
118 | 0x64, 0x46, 0x64, 0x9b, 0xc7, 0x13, 0xbf, 0xbe, 0xd0, 0x31, 0x49,
119 | 0xe7, 0x3c, 0xbf, 0xba, 0xb1, 0x20, 0xf9, 0x42, 0xf4, 0xa9, 0xa9,
120 | 0xe5, 0x13, 0x65, 0x32, 0xbf, 0x7c, 0xcc, 0x91, 0xd3, 0xfd, 0x24,
121 | 0x47, 0x0b, 0xe5, 0x53, 0xad, 0x50, 0x30, 0x56, 0xd1, 0xfa, 0x9c,
122 | 0x37, 0xa8, 0xc1, 0xce, 0xf6, 0x0b, 0x18, 0xaa, 0x7c, 0xab, 0xbd,
123 | 0x1f, 0xdf, 0xe4, 0x80, 0xb8, 0xa7, 0xe0, 0xad, 0x7d, 0x50, 0x74,
124 | 0xf1, 0x98, 0x78, 0xbc, 0x58, 0xb9, 0xc2, 0x52, 0xbe, 0xd2, 0x5b,
125 | 0x81, 0x94, 0x83, 0x8f, 0xb9, 0x4c, 0xee, 0x01, 0x2b, 0x5e, 0xc9,
126 | 0x6e, 0x9b, 0xf5, 0x63, 0x69, 0xe4, 0xd8, 0x0b, 0x47, 0xd8, 0xfd,
127 | 0xd8, 0xe0, 0xed, 0xa8, 0x27, 0x03, 0x74, 0x1e, 0x5d, 0x32, 0xe6,
128 | 0x5c, 0x63, 0xc2, 0xfb, 0x3f, 0xee, 0xb4, 0x13, 0xc6, 0x0e, 0x6e,
129 | 0x74, 0xe0, 0x22, 0xac, 0xce, 0x79, 0xf9, 0x43, 0x68, 0xc1, 0x03,
130 | 0x74, 0x2b, 0xe1, 0x18, 0xf8, 0x7f, 0x76, 0x9a, 0xea, 0x82, 0x3f,
131 | 0xc2, 0xa6, 0xa7, 0x4c, 0xfe, 0xae, 0x29, 0x3b, 0xc1, 0x10, 0x7c,
132 | 0xd5, 0x77, 0x17, 0x79, 0x5f, 0xcb, 0xad, 0x1f, 0xd8, 0xa1, 0xfd,
133 | 0x90, 0xe1, 0x6b, 0xb2, 0xef, 0xb9, 0x41, 0x26, 0xa4, 0x0b, 0x4f,
134 | 0xc6, 0x83, 0x05, 0x6f, 0xf0, 0x64, 0x40, 0xe1, 0x44, 0xc4, 0xf9,
135 | 0x40, 0x2b, 0x3b, 0x40, 0xdb, 0xaf, 0x35, 0xa4, 0x9b, 0x9f, 0xc4,
136 | 0x74, 0x07, 0xe5, 0x18, 0x60, 0xc5, 0xfe, 0x15, 0x0e, 0x3a, 0x25,
137 | 0x2a, 0x11, 0xee, 0x78, 0x2f, 0xb8, 0xd1, 0x6e, 0x4e, 0x3c, 0x0a,
138 | 0xb5, 0xb9, 0x40, 0x86, 0x27, 0x6d, 0x8f, 0x53, 0xb7, 0x77, 0x36,
139 | 0xec, 0x5d, 0xed, 0x32, 0x40, 0x43, 0x82, 0xc3, 0x52, 0x58, 0xc4,
140 | 0x26, 0x39, 0xf3, 0xb3, 0xad, 0x58, 0xab, 0xb7, 0xf7, 0x8e, 0x0e,
141 | 0xba, 0x8e, 0x78, 0x9d, 0xbf, 0x58, 0x34, 0xbd, 0x77, 0x73, 0xa6,
142 | 0x50, 0x55, 0x00, 0x60, 0x26, 0xbf, 0x6d, 0xb4, 0x98, 0x8a, 0x18,
143 | 0x83, 0x89, 0xf8, 0xcd, 0x0d, 0x49, 0x06, 0xae, 0x51, 0x6e, 0xaf,
144 | 0xbd, 0xe2, 0x07, 0x13, 0xd8, 0x64, 0xcc, 0xbf, 0x00, 0x00, 0x00,
145 | 0x01, 0x00, 0x05, 0x58, 0x2e, 0x35, 0x30, 0x39, 0x00, 0x00, 0x02,
146 | 0x34, 0x30, 0x82, 0x02, 0x30, 0x30, 0x82, 0x01, 0xda, 0xa0, 0x03,
147 | 0x02, 0x01, 0x02, 0x02, 0x04, 0x48, 0x59, 0xf2, 0x84, 0x30, 0x0d,
148 | 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05,
149 | 0x05, 0x00, 0x30, 0x81, 0x9d, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03,
150 | 0x55, 0x04, 0x06, 0x13, 0x02, 0x4b, 0x52, 0x31, 0x13, 0x30, 0x11,
151 | 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, 0x4b, 0x79, 0x75, 0x6e,
152 | 0x67, 0x67, 0x69, 0x2d, 0x64, 0x6f, 0x31, 0x14, 0x30, 0x12, 0x06,
153 | 0x03, 0x55, 0x04, 0x07, 0x13, 0x0b, 0x53, 0x65, 0x6f, 0x6e, 0x67,
154 | 0x6e, 0x61, 0x6d, 0x2d, 0x73, 0x69, 0x31, 0x1a, 0x30, 0x18, 0x06,
155 | 0x03, 0x55, 0x04, 0x0a, 0x13, 0x11, 0x54, 0x68, 0x65, 0x20, 0x4e,
156 | 0x65, 0x74, 0x74, 0x79, 0x20, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63,
157 | 0x74, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13,
158 | 0x0c, 0x43, 0x6f, 0x6e, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x6f,
159 | 0x72, 0x73, 0x31, 0x30, 0x30, 0x2e, 0x06, 0x03, 0x55, 0x04, 0x03,
160 | 0x13, 0x27, 0x73, 0x65, 0x63, 0x75, 0x72, 0x65, 0x63, 0x68, 0x61,
161 | 0x74, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x6e,
162 | 0x65, 0x74, 0x74, 0x79, 0x2e, 0x67, 0x6c, 0x65, 0x61, 0x6d, 0x79,
163 | 0x6e, 0x6f, 0x64, 0x65, 0x2e, 0x6e, 0x65, 0x74, 0x30, 0x20, 0x17,
164 | 0x0d, 0x30, 0x38, 0x30, 0x36, 0x31, 0x39, 0x30, 0x35, 0x34, 0x35,
165 | 0x34, 0x30, 0x5a, 0x18, 0x0f, 0x32, 0x31, 0x38, 0x37, 0x31, 0x31,
166 | 0x32, 0x33, 0x30, 0x35, 0x34, 0x35, 0x34, 0x30, 0x5a, 0x30, 0x81,
167 | 0x9d, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13,
168 | 0x02, 0x4b, 0x52, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04,
169 | 0x08, 0x13, 0x0a, 0x4b, 0x79, 0x75, 0x6e, 0x67, 0x67, 0x69, 0x2d,
170 | 0x64, 0x6f, 0x31, 0x14, 0x30, 0x12, 0x06, 0x03, 0x55, 0x04, 0x07,
171 | 0x13, 0x0b, 0x53, 0x65, 0x6f, 0x6e, 0x67, 0x6e, 0x61, 0x6d, 0x2d,
172 | 0x73, 0x69, 0x31, 0x1a, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0a,
173 | 0x13, 0x11, 0x54, 0x68, 0x65, 0x20, 0x4e, 0x65, 0x74, 0x74, 0x79,
174 | 0x20, 0x50, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x31, 0x15, 0x30,
175 | 0x13, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x0c, 0x43, 0x6f, 0x6e,
176 | 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x6f, 0x72, 0x73, 0x31, 0x30,
177 | 0x30, 0x2e, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x27, 0x73, 0x65,
178 | 0x63, 0x75, 0x72, 0x65, 0x63, 0x68, 0x61, 0x74, 0x2e, 0x65, 0x78,
179 | 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x6e, 0x65, 0x74, 0x74, 0x79,
180 | 0x2e, 0x67, 0x6c, 0x65, 0x61, 0x6d, 0x79, 0x6e, 0x6f, 0x64, 0x65,
181 | 0x2e, 0x6e, 0x65, 0x74, 0x30, 0x5c, 0x30, 0x0d, 0x06, 0x09, 0x2a,
182 | 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03,
183 | 0x4b, 0x00, 0x30, 0x48, 0x02, 0x41, 0x00, 0x95, 0xb3, 0x47, 0x17,
184 | 0x95, 0x0f, 0x57, 0xcf, 0x66, 0x72, 0x0a, 0x7e, 0x5b, 0x54, 0xea,
185 | 0x8c, 0x6f, 0x79, 0xde, 0x94, 0xac, 0x0b, 0x5a, 0xd4, 0xd6, 0x1b,
186 | 0x58, 0x12, 0x1a, 0x16, 0x3d, 0xfe, 0xdf, 0xa5, 0x2b, 0x86, 0xbc,
187 | 0x64, 0xd4, 0x80, 0x1e, 0x3f, 0xf9, 0xe2, 0x04, 0x03, 0x79, 0x9b,
188 | 0xc1, 0x5c, 0xf0, 0xf1, 0xf3, 0xf1, 0xe3, 0xbf, 0x3f, 0xc0, 0x1f,
189 | 0xdd, 0xdb, 0xc0, 0x5b, 0x21, 0x02, 0x03, 0x01, 0x00, 0x01, 0x30,
190 | 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01,
191 | 0x05, 0x05, 0x00, 0x03, 0x41, 0x00, 0x02, 0xd7, 0xdd, 0xbd, 0x0c,
192 | 0x8e, 0x21, 0x20, 0xef, 0x9e, 0x4f, 0x1f, 0xf5, 0x49, 0xf1, 0xae,
193 | 0x58, 0x9b, 0x94, 0x3a, 0x1f, 0x70, 0x33, 0xf0, 0x9b, 0xbb, 0xe9,
194 | 0xc0, 0xf3, 0x72, 0xcb, 0xde, 0xb6, 0x56, 0x72, 0xcc, 0x1c, 0xf0,
195 | 0xd6, 0x5a, 0x2a, 0xbc, 0xa1, 0x7e, 0x23, 0x83, 0xe9, 0xe7, 0xcf,
196 | 0x9e, 0xa5, 0xf9, 0xcc, 0xc2, 0x61, 0xf4, 0xdb, 0x40, 0x93, 0x1d,
197 | 0x63, 0x8a, 0x50, 0x4c, 0x11, 0x39, 0xb1, 0x91, 0xc1, 0xe6, 0x9d,
198 | 0xd9, 0x1a, 0x62, 0x1b, 0xb8, 0xd3, 0xd6, 0x9a, 0x6d, 0xb9, 0x8e,
199 | 0x15, 0x51 };
200 |
201 | public static InputStream asInputStream() {
202 | byte[] data = new byte[DATA.length];
203 | for (int i = 0; i < data.length; i++) {
204 | data[i] = (byte) DATA[i];
205 | }
206 | return new ByteArrayInputStream(data);
207 | }
208 |
209 | public static char[] getCertificatePassword() {
210 | return "secret".toCharArray();
211 | }
212 |
213 | public static char[] getKeyStorePassword() {
214 | return "secret".toCharArray();
215 | }
216 |
217 | private SslKeyStore() {
218 | // Unused
219 | }
220 | }
221 |
--------------------------------------------------------------------------------
/src/java/me/shenfeng/ssl/TrustManagerFactory.java:
--------------------------------------------------------------------------------
1 | package me.shenfeng.ssl;
2 |
3 | /**
4 | * copy from netty
5 | */
6 | import java.security.InvalidAlgorithmParameterException;
7 | import java.security.KeyStore;
8 | import java.security.KeyStoreException;
9 | import java.security.cert.CertificateException;
10 | import java.security.cert.X509Certificate;
11 |
12 | import javax.net.ssl.ManagerFactoryParameters;
13 | import javax.net.ssl.TrustManager;
14 | import javax.net.ssl.TrustManagerFactorySpi;
15 | import javax.net.ssl.X509TrustManager;
16 |
17 | public class TrustManagerFactory extends TrustManagerFactorySpi {
18 |
19 | private static final TrustManager DUMMY_TRUST_MANAGER = new X509TrustManager() {
20 | public X509Certificate[] getAcceptedIssuers() {
21 | return new X509Certificate[0];
22 | }
23 |
24 | public void checkClientTrusted(X509Certificate[] chain,
25 | String authType) throws CertificateException {
26 | // Always trust
27 | }
28 |
29 | public void checkServerTrusted(X509Certificate[] chain,
30 | String authType) throws CertificateException {
31 | // Always trust
32 | }
33 | };
34 |
35 | public static TrustManager[] getTrustManagers() {
36 | return new TrustManager[] { DUMMY_TRUST_MANAGER };
37 | }
38 |
39 | @Override
40 | protected TrustManager[] engineGetTrustManagers() {
41 | return getTrustManagers();
42 | }
43 |
44 | @Override
45 | protected void engineInit(KeyStore keystore) throws KeyStoreException {
46 | // Unused
47 | }
48 |
49 | @Override
50 | protected void engineInit(
51 | ManagerFactoryParameters managerFactoryParameters)
52 | throws InvalidAlgorithmParameterException {
53 | // Unused
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/test/java/DnsPrefetcherTest.java:
--------------------------------------------------------------------------------
1 | import java.io.IOException;
2 |
3 | import me.shenfeng.dns.DnsPrefecher;
4 |
5 | import org.junit.Test;
6 |
7 | public class DnsPrefetcherTest {
8 |
9 | @Test
10 | public void testPrefetch() throws IOException {
11 | DnsPrefecher prefecher = DnsPrefecher.getInstance();
12 | prefecher.prefetch("google.com");
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/test/java/HttpClientTest.java:
--------------------------------------------------------------------------------
1 | import java.net.InetSocketAddress;
2 | import java.net.Proxy;
3 | import java.net.Proxy.Type;
4 | import java.net.URI;
5 | import java.net.URISyntaxException;
6 | import java.util.HashMap;
7 | import java.util.Map;
8 | import java.util.concurrent.ExecutionException;
9 | import java.util.concurrent.Semaphore;
10 |
11 | import me.shenfeng.Utils;
12 | import me.shenfeng.http.HttpClient;
13 | import me.shenfeng.http.HttpClientConfig;
14 | import me.shenfeng.http.HttpResponseFuture;
15 |
16 | import org.jboss.netty.handler.codec.http.HttpResponse;
17 | import org.junit.Before;
18 | import org.junit.Test;
19 |
20 | public class HttpClientTest {
21 |
22 | HttpClient client;
23 | Proxy proxy;
24 | Map header;
25 | String urls[] = new String[] { "http://192.168.1.1:8088/",
26 | "http://192.168.1.1:8088/c.html",
27 | "http://192.168.1.1:8088/qunar.html",
28 | "http://192.168.1.1:8088/CHANGES", };
29 |
30 | @Before
31 | public void setup() {
32 | HttpClientConfig config = new HttpClientConfig();
33 | header = new HashMap();
34 | client = new HttpClient(config);
35 | proxy = new Proxy(Type.HTTP, new InetSocketAddress("127.0.0.1", 3128));
36 | }
37 |
38 | @Test
39 | public void testProxy() throws Exception {
40 | final URI uri = new URI("http://www.facebook.com");
41 | final HttpResponseFuture resp = client.execGet(uri, header, proxy);
42 | HttpResponse r = resp.get();
43 | System.out.println(r);
44 | }
45 |
46 | @Test
47 | public void testHttps() throws URISyntaxException, InterruptedException,
48 | ExecutionException {
49 | URI uri = new URI("https://trakrapp.com");
50 | HttpResponseFuture future = client.execGet(uri, header);
51 | System.out.println(future.get());
52 | System.out.println(Utils.bodyStr(future.get()));
53 | }
54 |
55 | @Test
56 | public void testHttpAndHttps() throws InterruptedException,
57 | ExecutionException, URISyntaxException {
58 | String[] urls = new String[] { "http://www.baidu.com",
59 | "https://www.google.com", "http://shenfeng.me",
60 | "https://trakrapp.com", "https://github.com" };
61 | for (String url : urls) {
62 | URI uri = new URI(url);
63 | HttpResponseFuture future = client.execGet(uri, header);
64 | System.out.println(future.get());
65 | System.out.println(Utils.bodyStr(future.get()).length() + "\t"
66 | + url);
67 | }
68 | }
69 |
70 | @Test
71 | public void testNoProxy() throws Exception {
72 | final URI uri = new URI("http://github.com");
73 | final HttpResponseFuture resp = client.execGet(uri, header);
74 | HttpResponse r = resp.get();
75 | String s = Utils.bodyStr(r);
76 | System.out.println(s);
77 | }
78 |
79 | @Test
80 | public void testCheckTimeout() throws Exception {
81 | for (int i = 0; i < 10; i++) {
82 | final URI uri = new URI("http://shenfeng.me");
83 | final HttpResponseFuture resp = client.execGet(uri, header);
84 | HttpResponse r = resp.get();
85 | String s = Utils.bodyStr(r);
86 | System.out.println(s.length());
87 | }
88 | }
89 |
90 | @Test
91 | public void testPotentialError() throws URISyntaxException,
92 | InterruptedException, ExecutionException {
93 | for (int i = 0; i < 1; ++i) {
94 | final URI uri = new URI("http://shenfeng.me");
95 | final HttpResponseFuture get = client.execGet(uri, header);
96 | get.addListener(new Runnable() {
97 | public void run() {
98 | try {
99 | HttpResponse resp = get.get();
100 | System.out.println(resp);
101 | // System.out.println(Utils.bodyString(resp));
102 | } catch (Exception e) {
103 | }
104 | }
105 | });
106 | get.get();
107 | }
108 | // Thread.sleep(210000);
109 | }
110 |
111 | @Test
112 | public void testConnectionRefused() throws URISyntaxException,
113 | InterruptedException, ExecutionException {
114 | HttpResponseFuture resp = client.execGet(new URI("http://127.0.0.1"),
115 | header);
116 |
117 | resp.get();
118 |
119 | // Thread.sleep(10000000);
120 | }
121 |
122 | @Test
123 | public void testGetBigFile() throws URISyntaxException,
124 | InterruptedException, ExecutionException {
125 |
126 | HttpResponseFuture resp = client.execGet(new URI(
127 | "http://192.168.1.1/videos/AdbeRdr940_zh_CN.exe"), header);
128 |
129 | resp.get();
130 | // Thread.sleep(10000000);
131 | }
132 |
133 | @Test
134 | public void testAsyncGet() throws URISyntaxException,
135 | InterruptedException, ExecutionException {
136 | final Semaphore semaphore = new Semaphore(200);
137 | String[] urls = new String[] { "/", "/browse", "/css/browse.css",
138 | "/js/lib/jquery.js" };
139 |
140 | while (true) {
141 | for (String url : urls) {
142 | final HttpResponseFuture resp = client.execGet(new URI(
143 | "http://127.0.0.1:8100" + url), header);
144 | resp.addListener(new Runnable() {
145 | public void run() {
146 | try {
147 | // System.out.println(resp.get().getStatus());
148 | semaphore.release();
149 | } catch (Exception e) {
150 | e.printStackTrace();
151 | }
152 | }
153 | });
154 | semaphore.acquire();
155 | }
156 | }
157 | }
158 |
159 | @Test
160 | public void testAsyncGet2() throws URISyntaxException,
161 | InterruptedException, ExecutionException {
162 | String urls[] = new String[] {
163 | "http://news.sohu.com/upload/cs/cswb001/beida0909/index.html",
164 | "http://news.sohu.com/photo/", "http://baike.baidu.com/",
165 | "http://tieba.baidu.com/tb/index/v2/niuba.html",
166 | "http://web.qq.com/", "http://im.qq.com/webqq/",
167 | "http://news.sina.com.cn/society/", "http://www.baidu.com/" };
168 | for (int i = 0; i < 10; ++i) {
169 | for (final String url : urls) {
170 | final HttpResponseFuture future = client.execGet(
171 | new URI(url), header);
172 | future.addListener(new Runnable() {
173 | public void run() {
174 | try {
175 | HttpResponse r = future.get();
176 | // if (r.getStatus().getCode() == 200)
177 | System.out.println(r.getStatus() + "\t"
178 | + r.getContentLength() + "\t" + url);
179 | } catch (Exception e) {
180 | e.printStackTrace();
181 | }
182 | }
183 | });
184 | }
185 | }
186 | Thread.sleep(10000);
187 | }
188 |
189 | @Test
190 | public void testCRCerror() throws URISyntaxException,
191 | InterruptedException, ExecutionException {
192 | HttpResponseFuture f = client.execGet(new URI(
193 | "http://www.allshizuo.com/index.php"), header);
194 | HttpResponse resp = f.get();
195 | String string = Utils.bodyStr(resp);
196 | System.out.println(string);
197 | }
198 |
199 | @Test
200 | public void testAsyncLoop() throws InterruptedException,
201 | ExecutionException, URISyntaxException {
202 | final Semaphore semaphore = new Semaphore(10);
203 |
204 | while (true) {
205 | for (String s : urls) {
206 | URI uri = new URI(s);
207 | HttpResponseFuture resp = client.execGet(uri, header);
208 | resp.addListener(new Runnable() {
209 | public void run() {
210 | // System.out.println("----------");
211 | semaphore.release();
212 | }
213 | });
214 | semaphore.acquire();
215 | }
216 | }
217 | }
218 | }
219 |
--------------------------------------------------------------------------------
/test/java/TestSocket.java:
--------------------------------------------------------------------------------
1 | import java.io.IOException;
2 | import java.net.Socket;
3 | import java.net.UnknownHostException;
4 |
5 | import org.junit.Test;
6 |
7 | public class TestSocket {
8 |
9 | @Test
10 | public void testBuffer() throws UnknownHostException, IOException {
11 | Socket socket = new Socket("127.0.0.1", 8100);
12 | int buffer = socket.getSendBufferSize();
13 |
14 | System.out.println(buffer / 1024);
15 |
16 | buffer = socket.getReceiveBufferSize();
17 |
18 | System.out.println(buffer / 1024);
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/test/java/UtilsTest.java:
--------------------------------------------------------------------------------
1 | import static org.jboss.netty.util.CharsetUtil.UTF_8;
2 |
3 | import java.net.URI;
4 | import java.net.URISyntaxException;
5 | import java.nio.charset.Charset;
6 |
7 | import me.shenfeng.Utils;
8 |
9 | import org.junit.Assert;
10 | import org.junit.Test;
11 |
12 | public class UtilsTest {
13 |
14 | @Test
15 | public void testGetPath() throws URISyntaxException {
16 | URI uri = new URI("http://shenfeng.me?a=b");
17 | String path = Utils.getPath(uri);
18 | Assert.assertTrue("/?a=b".equals(path));
19 | uri = new URI(
20 | "http://www.baidu.com/s?wd=%D5%AC%BC%B1%CB%CD&rsv_bp=0&inputT=3664");
21 | Assert.assertNotSame("should equal",
22 | "s?wd=%D5%AC%BC%B1%CB%CD&rsv_bp=0&inputT=3664",
23 | Utils.getPath(uri));
24 | }
25 |
26 | @Test
27 | public void testParseCharset() {
28 | Assert.assertEquals("default utf8", UTF_8, Utils.parseCharset(null));
29 | Assert.assertEquals("parse gbk", Charset.forName("gbk"),
30 | Utils.parseCharset("text/html;charset=gbk"));
31 | Assert.assertEquals("parse gb2312", Charset.forName("gb2312"),
32 | Utils.parseCharset("text/html;charset=gb2312"));
33 | }
34 |
35 | @Test
36 | public void testGetPort() throws URISyntaxException {
37 | Assert.assertEquals(80, Utils.getPort(new URI("http://google.com")));
38 | Assert.assertEquals(443, Utils.getPort(new URI("https://google.com")));
39 | }
40 |
41 | @Test
42 | public void testBytes() {
43 |
44 | for (int i = 0; i < 256; ++i) {
45 | System.out.println(i + "\t" + (byte) i);
46 | }
47 |
48 | int[] numbs = new int[] { 0x00000f0f, 0x0000ffff };
49 | byte[][] bytes = new byte[][] { new byte[] { 15, 15 },
50 | new byte[] { -1, -1 } };
51 |
52 | for (int i = 0; i < numbs.length; ++i) {
53 | int num = numbs[i];
54 | byte[] byts = bytes[i];
55 | Assert.assertArrayEquals(Utils.toBytes(num), byts);
56 | Assert.assertEquals(num, Utils.toInt(byts));
57 | }
58 | }
59 |
60 | @Test
61 | public void testIsIP() {
62 | Assert.assertTrue(Utils.isIP("12.1.1.1"));
63 | Assert.assertTrue(!Utils.isIP("shenfeng.me"));
64 |
65 | }
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/test/java/logback-test.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | %d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/test/me/shenfeng/httpclient_test.clj:
--------------------------------------------------------------------------------
1 | (ns me.shenfeng.httpclient-test
2 | (:use clojure.test
3 | [ring.adapter.jetty :only [run-jetty]]
4 | (compojure [core :only [defroutes GET PUT PATCH DELETE POST HEAD DELETE ANY context]]
5 | [handler :only [site]]
6 | [route :only [not-found]]))
7 | (:import me.shenfeng.http.HttpClient
8 | java.net.URI
9 | me.shenfeng.http.HttpClientConfig))
10 |
11 | (defroutes test-routes
12 | (GET "/get" [] "hello world")
13 | (POST "/post" [] "hello world"))
14 |
15 | (use-fixtures :once
16 | (fn [f]
17 | (let [jetty (run-jetty (site test-routes) {:port 14347
18 | :join? false})]
19 | (try (f) (finally (.stop jetty))))))
20 |
21 | (deftest test-http-client
22 | (let [host "http://127.0.0.1:14347"
23 | client (HttpClient. (HttpClientConfig.))]
24 | (is (= 200 (.getCode (.getStatus (.get (.execGet client (str host "/get")))))))
25 | (is (= 200 (.getCode (.getStatus (.get (.execPost client (URI. (str host "/post"))
26 | {} {}))))))))
27 |
--------------------------------------------------------------------------------