HttpClient.java
/*
* Copyright 2016 The Lannister Project
*
* 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 net.anyflow.lannister.http;
import java.net.URISyntaxException;
import javax.net.ssl.TrustManagerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.epoll.EpollSocketChannel;
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.DefaultFullHttpRequest;
import io.netty.handler.codec.http.HttpClientCodec;
import io.netty.handler.codec.http.HttpContentDecompressor;
import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpHeaderValues;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;
import io.netty.util.concurrent.DefaultThreadFactory;
import net.anyflow.lannister.Literals;
import net.anyflow.lannister.Settings;
public class HttpClient implements IHttpClient {
private static final Logger logger = LoggerFactory.getLogger(HttpClient.class);
private final Bootstrap bootstrap;
private final HttpRequest httpRequest;
private final TrustManagerFactory trustManagerFactory;
public HttpClient(String uri) throws UnsupportedOperationException, URISyntaxException {
this(uri, false);
}
public HttpClient(String uri, boolean useInsecureTrustManagerFactory)
throws URISyntaxException, UnsupportedOperationException {
trustManagerFactory = useInsecureTrustManagerFactory ? InsecureTrustManagerFactory.INSTANCE : null;
bootstrap = new Bootstrap();
httpRequest = new HttpRequest(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, uri));
if (!httpRequest().uriObject().getScheme().equalsIgnoreCase("http")
&& !httpRequest().uriObject().getScheme().equalsIgnoreCase("https")) {
String message = "HTTP(S) is supported only.";
logger.error(message);
throw new UnsupportedOperationException(message);
}
}
@Override
public HttpRequest httpRequest() {
return httpRequest;
}
@Override
public HttpResponse get() {
return get(null);
}
@Override
public HttpResponse get(final MessageReceiver receiver) {
httpRequest().setMethod(HttpMethod.GET);
return request(receiver);
}
@Override
public HttpResponse post() {
return post(null);
}
@Override
public HttpResponse post(final MessageReceiver receiver) {
httpRequest().setMethod(HttpMethod.POST);
return request(receiver);
}
@Override
public HttpResponse put() {
return put(null);
}
@Override
public HttpResponse put(final MessageReceiver receiver) {
httpRequest().setMethod(HttpMethod.PUT);
return request(receiver);
}
@Override
public HttpResponse delete() {
return delete(null);
}
@Override
public HttpResponse delete(final MessageReceiver receiver) {
httpRequest().setMethod(HttpMethod.DELETE);
return request(receiver);
}
@Override
public <T> IHttpClient setOption(ChannelOption<T> option, T value) {
bootstrap.option(option, value);
return this;
}
private HttpResponse request(final MessageReceiver receiver) {
httpRequest().normalize();
setDefaultHeaders(httpRequest());
if (logger.isDebugEnabled()) {
logger.debug(httpRequest().toString());
}
final HttpClientHandler clientHandler = new HttpClientHandler(receiver, httpRequest);
EventLoopGroup group;
Class<? extends SocketChannel> socketChannelClass;
if (Literals.NETTY_EPOLL.equals(Settings.INSTANCE.nettyTransportMode())) {
group = new EpollEventLoopGroup(1, new DefaultThreadFactory("client"));
socketChannelClass = EpollSocketChannel.class;
}
else {
group = new NioEventLoopGroup(1, new DefaultThreadFactory("client"));
socketChannelClass = NioSocketChannel.class;
}
bootstrap.group(group).channel(socketChannelClass).handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
if ("true".equalsIgnoreCase(Settings.INSTANCE.getProperty("webserver.logging.writelogOfNettyLogger"))) {
ch.pipeline().addLast("log", new LoggingHandler("lannister.web/server", LogLevel.DEBUG));
}
if ("https".equalsIgnoreCase(httpRequest().uriObject().getScheme())) {
SslContext sslCtx = SslContextBuilder.forClient().trustManager(trustManagerFactory).build();
ch.pipeline().addLast(sslCtx.newHandler(ch.alloc(), httpRequest().uriObject().getHost(),
httpRequest().uriObject().getPort()));
}
ch.pipeline().addLast("codec", new HttpClientCodec());
ch.pipeline().addLast("inflater", new HttpContentDecompressor());
ch.pipeline().addLast("chunkAggregator", new HttpObjectAggregator(1048576));
ch.pipeline().addLast("handler", clientHandler);
}
});
try {
Channel channel = bootstrap
.connect(httpRequest().uriObject().getHost(), httpRequest().uriObject().getPort()).sync().channel();
channel.writeAndFlush(httpRequest);
if (receiver == null) {
channel.closeFuture().sync();
group.shutdownGracefully();
return clientHandler.httpResponse();
}
else {
channel.closeFuture().addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
group.shutdownGracefully();
}
});
return null;
}
}
catch (Exception e) {
group.shutdownGracefully();
logger.error(e.getMessage(), e);
return null;
}
}
protected static void setDefaultHeaders(HttpRequest httpRequest) {
if (!httpRequest.headers().contains(HttpHeaderNames.HOST)) {
httpRequest.headers().set(HttpHeaderNames.HOST, httpRequest.uriObject().getHost());
}
if (!httpRequest.headers().contains(HttpHeaderNames.CONNECTION)) {
httpRequest.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
}
if (!httpRequest.headers().contains(HttpHeaderNames.ACCEPT_ENCODING)) {
httpRequest.headers().set(HttpHeaderNames.ACCEPT_ENCODING,
HttpHeaderValues.GZIP + ", " + HttpHeaderValues.DEFLATE);
}
if (!httpRequest.headers().contains(HttpHeaderNames.ACCEPT_CHARSET)) {
httpRequest.headers().set(HttpHeaderNames.ACCEPT_CHARSET, "utf-8");
}
if (!httpRequest.headers().contains(HttpHeaderNames.CONTENT_TYPE)) {
httpRequest.headers().set(HttpHeaderNames.CONTENT_TYPE, HttpHeaderValues.APPLICATION_X_WWW_FORM_URLENCODED);
}
}
}