0
votes

I have a pet project of man-in-the-middle proxy built with netty. Initially I used netty-all-5.0.0.Alpha1 and managed to build the pipeline for SSL handling. However, after upgrading to netty-all-5.0.0.Alpha2, the same SSL handling failed. Basically, when it was working, I could configure a browser to use the proxy and load https pages successfully. Right now with netty-all-5.0.0.Alpha2, the browser just doesn't get seem to get anything.

Below are details of the SSL handling I have.

// server bootstrap
serverBootstrap.group(bossGroup, workerGroup)
        .channel(NioServerSocketChannel.class)
        .childHandler(new ChannelInitializer<SocketChannel>() {
            @Override
            public void initChannel(SocketChannel ch) throws Exception {
                ChannelPipeline p = ch.pipeline();
                p
                        .addLast(SERVER_LOGGING_HANDLER, new LoggingHandler(SERVER_LOGGING_HANDLER))
                        .addLast(SERVER_HTTP_REQUEST_DECODER, new HttpRequestDecoder())
                        .addLast(SERVER_HTTP_RESPONSE_ENCODER, new HttpResponseEncoder())
                        .addLast(HTTP_CONTENT_AGGREGATOR, new HttpObjectAggregator(MAX_HTTP_CONTENT_LENGTH))
                        .addLast(SERVER_HANDLER, new ServerHandler(ch, portContextManager) );
            }
        });


// inside ServerHandler
private static final HttpResponse CONNECT_RESPONSE = new DefaultHttpResponse(HttpVersion.HTTP_1_1,
        new HttpResponseStatus(200, "Connection established"));

public void channelRead(ChannelHandlerContext context, Object message) throws Exception {
    log.debug("server pipeline handlers: " + context.pipeline().names());

    if (message instanceof FullHttpRequest) {
        FullHttpRequest request = (FullHttpRequest) message;
        if (request.method() == HttpMethod.CONNECT) {
            context.write(CONNECT_RESPONSE);
            log.debug("responded CONNECT method with {}", CONNECT_RESPONSE);
            context.pipeline().remove(SERVER_HTTP_REQUEST_DECODER);
            context.pipeline().remove(SERVER_HTTP_RESPONSE_ENCODER);
            context.pipeline().addBefore(HTTP_CONTENT_AGGREGATOR, SERVER_SSL_HANDLER, createServerSslHandler());
            context.pipeline().addBefore(HTTP_CONTENT_AGGREGATOR, HTTP_SERVER_CODEC, new HttpServerCodec());
            return;
        }
        log.debug("Received HTTP request:\n{}", request);
        chain.processRequest(request);
    } else {
        throw new Exception("Only FullHttpRequest is supported");
    }
}

private SslHandler createServerSslHandler() {
    try {
        SSLContext context = SslContextFactory.createServerSslContext();
        SSLEngine engine = context.createSSLEngine();
        engine.setUseClientMode(false);
        return new SslHandler(engine);
    } catch (Exception e) {
        log.error("Failed to create SslHandler with exception:", e);
        return null;
    }
}

With netty-all-5.0.0.Alpha2, there is nothing from the LoggingHandler after this:

2015-03-28 17:46:39.206 [nioEventLoopGroup-1-0] DEBUG SERVER_LOGGING_HANDLER#debug(): [id: 0x1b8d61d1, /0:0:0:0:0:0:0:1:63483 => /0:0:0:0:0:0:0:1:7000] WRITE: 39B
         +-------------------------------------------------+
         |  0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f |
+--------+-------------------------------------------------+----------------+
|00000000| 48 54 54 50 2f 31 2e 31 20 32 30 30 20 43 6f 6e |HTTP/1.1 200 Con|
|00000010| 6e 65 63 74 69 6f 6e 20 65 73 74 61 62 6c 69 73 |nection establis|
|00000020| 68 65 64 0d 0a 0d 0a                            |hed....         |
+--------+-------------------------------------------------+----------------+

So, what have been changed from 5.0.0.Alpha1 to 5.0.0.Alpha2 in terms of SSL handling and how can I make the SSL handling working again?

update
I now managed to make it working again. My only change is to use writeAndFlush() instead of write() when responding the initial CONNECT request. Namely, now I have:

context.writeAndFlush(CONNECT_RESPONSE);

However, I still would like to understand why. One more thing that confuses me is that I have to remove the initial http codec as in

        context.pipeline().remove(SERVER_HTTP_REQUEST_DECODER);
        context.pipeline().remove(SERVER_HTTP_RESPONSE_ENCODER);

and add a new set of codec to get the SSL handling working. Can someone explain why?

1

1 Answers

1
votes

In Netty 4, a write() operation merely puts the write request to a per-channel queue. It is the flush() operation that actually tells Netty to write the write requests in the per-channel queue out to the socket.

In an older version of Netty 4, SslHandler had a bug where it auto-flushes every write requests, and it has been fixed recently. It's probably your old code does not work anymore. (The hidden bug in your code has been revealed by our bug fix.)