1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package net.anyflow.lannister.http;
18
19 import java.net.URISyntaxException;
20
21 import javax.net.ssl.TrustManagerFactory;
22
23 import org.slf4j.Logger;
24 import org.slf4j.LoggerFactory;
25
26 import io.netty.bootstrap.Bootstrap;
27 import io.netty.channel.Channel;
28 import io.netty.channel.ChannelFuture;
29 import io.netty.channel.ChannelFutureListener;
30 import io.netty.channel.ChannelInitializer;
31 import io.netty.channel.ChannelOption;
32 import io.netty.channel.EventLoopGroup;
33 import io.netty.channel.epoll.EpollEventLoopGroup;
34 import io.netty.channel.epoll.EpollSocketChannel;
35 import io.netty.channel.nio.NioEventLoopGroup;
36 import io.netty.channel.socket.SocketChannel;
37 import io.netty.channel.socket.nio.NioSocketChannel;
38 import io.netty.handler.codec.http.DefaultFullHttpRequest;
39 import io.netty.handler.codec.http.HttpClientCodec;
40 import io.netty.handler.codec.http.HttpContentDecompressor;
41 import io.netty.handler.codec.http.HttpHeaderNames;
42 import io.netty.handler.codec.http.HttpHeaderValues;
43 import io.netty.handler.codec.http.HttpMethod;
44 import io.netty.handler.codec.http.HttpObjectAggregator;
45 import io.netty.handler.codec.http.HttpVersion;
46 import io.netty.handler.logging.LogLevel;
47 import io.netty.handler.logging.LoggingHandler;
48 import io.netty.handler.ssl.SslContext;
49 import io.netty.handler.ssl.SslContextBuilder;
50 import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
51 import io.netty.util.concurrent.DefaultThreadFactory;
52 import net.anyflow.lannister.Literals;
53 import net.anyflow.lannister.Settings;
54
55 public class HttpClient implements IHttpClient {
56
57 private static final Logger logger = LoggerFactory.getLogger(HttpClient.class);
58
59 private final Bootstrap bootstrap;
60 private final HttpRequest httpRequest;
61 private final TrustManagerFactory trustManagerFactory;
62
63 public HttpClient(String uri) throws UnsupportedOperationException, URISyntaxException {
64 this(uri, false);
65 }
66
67 public HttpClient(String uri, boolean useInsecureTrustManagerFactory)
68 throws URISyntaxException, UnsupportedOperationException {
69 trustManagerFactory = useInsecureTrustManagerFactory ? InsecureTrustManagerFactory.INSTANCE : null;
70
71 bootstrap = new Bootstrap();
72
73 httpRequest = new HttpRequest(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uri));
74
75 if (!httpRequest().uriObject().getScheme().equalsIgnoreCase("http")
76 && !httpRequest().uriObject().getScheme().equalsIgnoreCase("https")) {
77 String message = "HTTP(S) is supported only.";
78 logger.error(message);
79 throw new UnsupportedOperationException(message);
80 }
81 }
82
83 @Override
84 public HttpRequest httpRequest() {
85 return httpRequest;
86 }
87
88 @Override
89 public HttpResponse get() {
90 return get(null);
91 }
92
93 @Override
94 public HttpResponse get(final MessageReceiver receiver) {
95 httpRequest().setMethod(HttpMethod.GET);
96
97 return request(receiver);
98 }
99
100 @Override
101 public HttpResponse post() {
102 return post(null);
103 }
104
105 @Override
106 public HttpResponse post(final MessageReceiver receiver) {
107 httpRequest().setMethod(HttpMethod.POST);
108
109 return request(receiver);
110 }
111
112 @Override
113 public HttpResponse put() {
114 return put(null);
115 }
116
117 @Override
118 public HttpResponse put(final MessageReceiver receiver) {
119 httpRequest().setMethod(HttpMethod.PUT);
120
121 return request(receiver);
122 }
123
124 @Override
125 public HttpResponse delete() {
126 return delete(null);
127 }
128
129 @Override
130 public HttpResponse delete(final MessageReceiver receiver) {
131 httpRequest().setMethod(HttpMethod.DELETE);
132
133 return request(receiver);
134 }
135
136 @Override
137 public <T> IHttpClient setOption(ChannelOption<T> option, T value) {
138 bootstrap.option(option, value);
139
140 return this;
141 }
142
143 private HttpResponse request(final MessageReceiver receiver) {
144 httpRequest().normalize();
145 setDefaultHeaders(httpRequest());
146
147 if (logger.isDebugEnabled()) {
148 logger.debug(httpRequest().toString());
149 }
150
151 final HttpClientHandler clientHandler = new HttpClientHandler(receiver, httpRequest);
152
153 EventLoopGroup group;
154 Class<? extends SocketChannel> socketChannelClass;
155
156 if (Literals.NETTY_EPOLL.equals(Settings.INSTANCE.nettyTransportMode())) {
157 group = new EpollEventLoopGroup(1, new DefaultThreadFactory("client"));
158 socketChannelClass = EpollSocketChannel.class;
159 }
160 else {
161 group = new NioEventLoopGroup(1, new DefaultThreadFactory("client"));
162 socketChannelClass = NioSocketChannel.class;
163 }
164
165 bootstrap.group(group).channel(socketChannelClass).handler(new ChannelInitializer<SocketChannel>() {
166 @Override
167 protected void initChannel(SocketChannel ch) throws Exception {
168
169 if ("true".equalsIgnoreCase(Settings.INSTANCE.getProperty("webserver.logging.writelogOfNettyLogger"))) {
170 ch.pipeline().addLast("log", new LoggingHandler("lannister.web/server", LogLevel.DEBUG));
171 }
172
173 if ("https".equalsIgnoreCase(httpRequest().uriObject().getScheme())) {
174 SslContext sslCtx = SslContextBuilder.forClient().trustManager(trustManagerFactory).build();
175
176 ch.pipeline().addLast(sslCtx.newHandler(ch.alloc(), httpRequest().uriObject().getHost(),
177 httpRequest().uriObject().getPort()));
178 }
179
180 ch.pipeline().addLast("codec", new HttpClientCodec());
181 ch.pipeline().addLast("inflater", new HttpContentDecompressor());
182 ch.pipeline().addLast("chunkAggregator", new HttpObjectAggregator(1048576));
183 ch.pipeline().addLast("handler", clientHandler);
184 }
185 });
186
187 try {
188 Channel channel = bootstrap
189 .connect(httpRequest().uriObject().getHost(), httpRequest().uriObject().getPort()).sync().channel();
190 channel.writeAndFlush(httpRequest);
191
192 if (receiver == null) {
193 channel.closeFuture().sync();
194 group.shutdownGracefully();
195
196 return clientHandler.httpResponse();
197 }
198 else {
199 channel.closeFuture().addListener(new ChannelFutureListener() {
200
201 @Override
202 public void operationComplete(ChannelFuture future) throws Exception {
203 group.shutdownGracefully();
204 }
205 });
206
207 return null;
208 }
209 }
210 catch (Exception e) {
211 group.shutdownGracefully();
212 logger.error(e.getMessage(), e);
213
214 return null;
215 }
216 }
217
218 protected static void setDefaultHeaders(HttpRequest httpRequest) {
219 if (!httpRequest.headers().contains(HttpHeaderNames.HOST)) {
220 httpRequest.headers().set(HttpHeaderNames.HOST, httpRequest.uriObject().getHost());
221 }
222 if (!httpRequest.headers().contains(HttpHeaderNames.CONNECTION)) {
223 httpRequest.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
224 }
225 if (!httpRequest.headers().contains(HttpHeaderNames.ACCEPT_ENCODING)) {
226 httpRequest.headers().set(HttpHeaderNames.ACCEPT_ENCODING,
227 HttpHeaderValues.GZIP + ", " + HttpHeaderValues.DEFLATE);
228 }
229 if (!httpRequest.headers().contains(HttpHeaderNames.ACCEPT_CHARSET)) {
230 httpRequest.headers().set(HttpHeaderNames.ACCEPT_CHARSET, "utf-8");
231 }
232 if (!httpRequest.headers().contains(HttpHeaderNames.CONTENT_TYPE)) {
233 httpRequest.headers().set(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_X_WWW_FORM_URLENCODED);
234 }
235 }
236 }