Java tutorial
/* * Licensed to CRATE Technology GmbH ("Crate") under one or more contributor * license agreements. See the NOTICE file distributed with this work for * additional information regarding copyright ownership. Crate licenses * this file to you 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. * * However, if you have executed another commercial license agreement * with Crate these terms will supersede the license and you may use the * software solely pursuant to the terms of the relevant commercial agreement. */ package io.crate.http; import com.fasterxml.jackson.core.JsonEncoding; import com.fasterxml.jackson.core.JsonFactory; import com.fasterxml.jackson.core.JsonGenerator; import com.fasterxml.jackson.core.JsonParser; import io.netty.bootstrap.ServerBootstrap; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.http.*; import io.netty.util.ReferenceCountUtil; import io.netty.util.ThreadDeathWatcher; import org.apache.logging.log4j.Logger; import org.elasticsearch.common.logging.Loggers; import java.io.ByteArrayOutputStream; import java.io.UnsupportedEncodingException; import java.net.InetSocketAddress; import java.net.URLDecoder; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; public class HttpTestServer { private final int port; private final boolean fail; private final static JsonFactory jsonFactory; private Channel channel; private NioEventLoopGroup group; public List<String> responses = new ArrayList<>(); static { jsonFactory = new JsonFactory(); jsonFactory.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true); jsonFactory.configure(JsonGenerator.Feature.QUOTE_FIELD_NAMES, true); jsonFactory.configure(JsonParser.Feature.ALLOW_COMMENTS, true); } /** * @param port the port to listen on * @param fail of set to true, the server will emit error responses */ public HttpTestServer(int port, boolean fail) { this.port = port; this.fail = fail; } public void run() throws InterruptedException { // Configure the server. ServerBootstrap bootstrap = new ServerBootstrap(); group = new NioEventLoopGroup(); bootstrap.group(group); bootstrap.channel(NioServerSocketChannel.class); bootstrap.childHandler(new ChannelInitializer<Channel>() { @Override protected void initChannel(Channel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast("decoder", new HttpRequestDecoder()); pipeline.addLast("encoder", new HttpResponseEncoder()); pipeline.addLast("deflater", new HttpContentCompressor()); pipeline.addLast("handler", new HttpTestServerHandler()); } }); // Bind and start to accept incoming connections. channel = bootstrap.bind(new InetSocketAddress(port)).sync().channel(); } public void shutDown() { channel.close().awaitUninterruptibly(); if (group != null) { group.shutdownGracefully().awaitUninterruptibly(); group.terminationFuture().awaitUninterruptibly(); group = null; } try { ThreadDeathWatcher.awaitInactivity(5, TimeUnit.SECONDS); } catch (InterruptedException e) { // ignore } } @ChannelHandler.Sharable public class HttpTestServerHandler extends SimpleChannelInboundHandler<Object> { private final Logger logger = Loggers.getLogger(HttpTestServerHandler.class.getName()); @Override protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { if (!(msg instanceof HttpRequest)) { ctx.fireChannelRead(msg); return; } try { handleHttpRequest(ctx, (HttpRequest) msg); } finally { ReferenceCountUtil.release(msg); } } private void handleHttpRequest(ChannelHandlerContext ctx, HttpRequest msg) throws UnsupportedEncodingException { String uri = msg.uri(); QueryStringDecoder decoder = new QueryStringDecoder(uri); logger.debug("Got Request for " + uri); HttpResponseStatus status = fail ? HttpResponseStatus.BAD_REQUEST : HttpResponseStatus.OK; ByteArrayOutputStream out = new ByteArrayOutputStream(); try { JsonGenerator generator = jsonFactory.createGenerator(out, JsonEncoding.UTF8); generator.writeStartObject(); for (Map.Entry<String, List<String>> entry : decoder.parameters().entrySet()) { if (entry.getValue().size() == 1) { generator.writeStringField(entry.getKey(), URLDecoder.decode(entry.getValue().get(0), "UTF-8")); } else { generator.writeArrayFieldStart(entry.getKey()); for (String value : entry.getValue()) { generator.writeString(URLDecoder.decode(value, "UTF-8")); } generator.writeEndArray(); } } generator.writeEndObject(); generator.close(); } catch (Exception ex) { status = HttpResponseStatus.INTERNAL_SERVER_ERROR; } ByteBuf byteBuf = Unpooled.wrappedBuffer(out.toByteArray()); responses.add(out.toString(StandardCharsets.UTF_8.name())); DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, status, byteBuf); ChannelFuture future = ctx.channel().writeAndFlush(response); future.addListener(ChannelFutureListener.CLOSE); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { logger.warn("Unexpected exception from downstream.", cause); ctx.close(); } } }