| // Copyright 2016 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| package org.chromium.net.smoke; |
| |
| import static io.netty.handler.codec.http.HttpResponseStatus.OK; |
| import static io.netty.handler.codec.http.HttpVersion.HTTP_1_1; |
| |
| import android.os.ConditionVariable; |
| import android.util.Log; |
| |
| import io.netty.bootstrap.ServerBootstrap; |
| import io.netty.buffer.Unpooled; |
| import io.netty.channel.Channel; |
| import io.netty.channel.ChannelFutureListener; |
| 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.NioServerSocketChannel; |
| import io.netty.handler.codec.http.DefaultFullHttpResponse; |
| import io.netty.handler.codec.http.FullHttpResponse; |
| import io.netty.handler.codec.http.HttpRequestDecoder; |
| import io.netty.handler.codec.http.HttpResponseEncoder; |
| import io.netty.handler.logging.LogLevel; |
| import io.netty.handler.logging.LoggingHandler; |
| import io.netty.util.CharsetUtil; |
| |
| /** A simple HTTP server for testing. */ |
| public class HttpTestServer implements TestSupport.TestServer { |
| private static final String TAG = HttpTestServer.class.getSimpleName(); |
| private static final String HOST = "127.0.0.1"; |
| private static final int PORT = 8080; |
| |
| private Channel mServerChannel; |
| private ConditionVariable mStartBlock = new ConditionVariable(); |
| private ConditionVariable mShutdownBlock = new ConditionVariable(); |
| |
| @Override |
| public boolean start() { |
| new Thread( |
| new Runnable() { |
| @Override |
| public void run() { |
| try { |
| HttpTestServer.this.run(); |
| } catch (Exception e) { |
| Log.e(TAG, "Unable to start HttpTestServer", e); |
| } |
| } |
| }) |
| .start(); |
| // Return false if the server cannot start within 5 seconds. |
| return mStartBlock.block(5000); |
| } |
| |
| @Override |
| public void shutdown() { |
| if (mServerChannel != null) { |
| mServerChannel.close(); |
| boolean success = mShutdownBlock.block(10000); |
| if (!success) { |
| Log.e(TAG, "Unable to shutdown the server. Is it already dead?"); |
| } |
| mServerChannel = null; |
| } |
| } |
| |
| @Override |
| public String getSuccessURL() { |
| return getServerUrl() + "/success"; |
| } |
| |
| private String getServerUrl() { |
| return "http://" + HOST + ":" + PORT; |
| } |
| |
| private void run() throws Exception { |
| EventLoopGroup bossGroup = new NioEventLoopGroup(1); |
| EventLoopGroup workerGroup = new NioEventLoopGroup(4); |
| try { |
| ServerBootstrap b = new ServerBootstrap(); |
| b.group(bossGroup, workerGroup) |
| .channel(NioServerSocketChannel.class) |
| .handler(new LoggingHandler(LogLevel.INFO)) |
| .childHandler( |
| new ChannelInitializer<SocketChannel>() { |
| @Override |
| public void initChannel(SocketChannel ch) throws Exception { |
| ChannelPipeline p = ch.pipeline(); |
| p.addLast(new HttpRequestDecoder()); |
| p.addLast(new HttpResponseEncoder()); |
| p.addLast(new TestServerHandler()); |
| } |
| }); |
| |
| // Start listening fo incoming connections. |
| mServerChannel = b.bind(PORT).sync().channel(); |
| mStartBlock.open(); |
| // Block until the channel is closed. |
| mServerChannel.closeFuture().sync(); |
| mShutdownBlock.open(); |
| Log.i(TAG, "HttpServer stopped"); |
| } finally { |
| workerGroup.shutdownGracefully(); |
| bossGroup.shutdownGracefully(); |
| } |
| } |
| |
| private static class TestServerHandler extends SimpleChannelInboundHandler { |
| @Override |
| protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { |
| FullHttpResponse response = |
| new DefaultFullHttpResponse( |
| HTTP_1_1, OK, Unpooled.copiedBuffer("Hello!", CharsetUtil.UTF_8)); |
| ctx.writeAndFlush(response).addListener(ChannelFutureListener.CLOSE); |
| } |
| } |
| } |