package org.yamcs.utils; import java.net.URI; import java.util.Base64; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import org.yamcs.security.UsernamePasswordToken; import io.netty.bootstrap.Bootstrap; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.EventLoopGroup; import io.netty.channel.SimpleChannelInboundHandler; 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.HttpContent; import io.netty.handler.codec.http.HttpContentDecompressor; import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpObject; import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpResponse; import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.LastHttpContent; import io.netty.util.CharsetUtil; public class HttpClient { URI uri; Exception exception; StringBuilder result = new StringBuilder(); // public Future<String> doAsyncRequest(String url, HttpMethod httpMethod, String body) throws Exception { // return doAsyncRequest(url, httpMethod, body, null); // } public Future<String> doAsyncRequest(String url, HttpMethod httpMethod, String body, UsernamePasswordToken authToken) throws Exception { uri = new URI(url); String scheme = uri.getScheme() == null? "http" : uri.getScheme(); String host = uri.getHost() == null? "127.0.0.1" : uri.getHost(); int port = uri.getPort(); if (port == -1) { port = 80; } if (!"http".equalsIgnoreCase(scheme)) { throw new IllegalArgumentException("Only HTTP is supported."); } exception = null; result.setLength(0); EventLoopGroup group = new NioEventLoopGroup(); Bootstrap b = new Bootstrap(); b.group(group).channel(NioSocketChannel.class) .handler(new ChannelInitializer<SocketChannel>() { @Override public void initChannel(SocketChannel ch) { ChannelPipeline p = ch.pipeline(); p.addLast(new HttpClientCodec()); p.addLast(new HttpContentDecompressor()); p.addLast(new MyChannelHandler()); } }); Channel ch = b.connect(host, port).sync().channel(); ByteBuf content = null; if(body!=null) { content = Unpooled.copiedBuffer(body, CharsetUtil.UTF_8); } else { content = Unpooled.EMPTY_BUFFER; } String fullUri = uri.getRawPath(); if (uri.getRawQuery() != null) { fullUri += "?" + uri.getRawQuery(); } HttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, httpMethod, fullUri, content); request.headers().set(HttpHeaders.Names.HOST, host); request.headers().set(HttpHeaders.Names.CONTENT_LENGTH, content.readableBytes()); request.headers().set(HttpHeaders.Names.CONNECTION, HttpHeaders.Values.CLOSE); if(authToken != null) { String credentialsClear = authToken.getUsername(); if(authToken.getPasswordS() != null) credentialsClear += ":" + authToken.getPasswordS(); String credentialsB64 = new String(Base64.getEncoder().encode(credentialsClear.getBytes())); String authorization = "Basic " + credentialsB64; request.headers().set(HttpHeaders.Names.AUTHORIZATION, authorization); } ch.writeAndFlush(request).await(1, TimeUnit.SECONDS); ResultFuture rf = new ResultFuture(ch.closeFuture()); return rf; } /* public String doRequest(String url, HttpMethod httpMethod, String body) throws Exception { return doRequest(url, httpMethod, body, null); }*/ public String doRequest(String url, HttpMethod httpMethod, String body, UsernamePasswordToken authToken) throws Exception { Future<String> f = doAsyncRequest(url, httpMethod, body, authToken); return f.get(5, TimeUnit.SECONDS); } /* public String doGetRequest(String url, String body) throws Exception { return doGetRequest(url, body, null); }*/ public String doGetRequest(String url, String body, UsernamePasswordToken authToken) throws Exception { return doRequest(url, HttpMethod.GET, body, authToken); } /* public String doPostRequest(String url, String body) throws Exception { return doPostRequest(url, body, null); }*/ public String doPostRequest(String url, String body, UsernamePasswordToken authToken) throws Exception { return doRequest(url, HttpMethod.POST, body, authToken); } class MyChannelHandler extends SimpleChannelInboundHandler<HttpObject> { @Override public void channelRead0(ChannelHandlerContext ctx, HttpObject msg) { if (msg instanceof HttpResponse) { // HttpResponse response = (HttpResponse) msg; } if (msg instanceof HttpContent) { HttpContent content = (HttpContent) msg; result.append(content.content().toString(CharsetUtil.UTF_8)); if (content instanceof LastHttpContent) { ctx.close(); } } } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { if(cause instanceof Exception) { exception = (Exception)cause; } else { exception = new RuntimeException(cause); } cause.printStackTrace(); ctx.close(); } } class ResultFuture implements Future<String> { ChannelFuture closeFuture; public ResultFuture(ChannelFuture closeFuture) { this.closeFuture = closeFuture; } @Override public boolean cancel(boolean mayInterruptIfRunning) { return closeFuture.cancel(mayInterruptIfRunning); } @Override public boolean isCancelled() { return closeFuture.isCancelled(); } @Override public boolean isDone() { return closeFuture.isDone(); } @Override public String get() throws InterruptedException, ExecutionException { closeFuture.await(); if(HttpClient.this.exception!=null) { throw new ExecutionException(HttpClient.this.exception); } return HttpClient.this.result.toString(); } @Override public String get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { if(closeFuture.await(timeout, unit)) { if(HttpClient.this.exception!=null) { throw new ExecutionException(HttpClient.this.exception); } } else { throw new TimeoutException(); } return HttpClient.this.result.toString(); } } }