com.hazelcast.openshift.TunnelServerConnector.java Source code

Java tutorial

Introduction

Here is the source code for com.hazelcast.openshift.TunnelServerConnector.java

Source

/*
 * Copyright (c) 2008-2015, Hazelcast, Inc. All Rights Reserved.
 *
 * 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 com.hazelcast.openshift;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.SimpleChannelInboundHandler;
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.HttpHeaderNames;
import io.netty.handler.codec.http.HttpHeaderValues;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpRequestEncoder;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseDecoder;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.util.InsecureTrustManagerFactory;

import java.util.concurrent.TimeUnit;

class TunnelServerConnector extends ChannelInboundHandlerAdapter {

    private final EventLoopGroup workerGroup;
    private final String httpHost;
    private final int httpPort;
    private final boolean ssl;

    TunnelServerConnector(EventLoopGroup workerGroup, String httpHost, int httpPort, boolean ssl) {
        this.workerGroup = workerGroup;
        this.httpHost = httpHost;
        this.httpPort = httpPort;
        this.ssl = ssl;
    }

    @Override
    public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
        Promise promise = new Promise();
        Bootstrap bootstrap = createBootstrap(ctx.channel(), promise);
        ChannelFuture future = bootstrap.connect(httpHost, httpPort);
        Channel forward = future.sync().channel();
        forward.closeFuture().addListener(f -> ctx.close());
        ctx.channel().closeFuture().addListener(f -> forward.close());

        HttpRequest request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.CONNECT, "/");
        request.headers().set(HttpHeaderNames.CONNECTION, HttpHeaderValues.KEEP_ALIVE);
        forward.writeAndFlush(request);

        // Wait for the initial http response (ssl established)
        promise.sync(10, TimeUnit.SECONDS);

        // Exchange the HazelcastClient -> TunnelClient handler to proxy
        ChannelPipeline pipeline = ctx.pipeline();
        pipeline.addLast(new ProxyForwardHandler(forward));
        pipeline.remove(this);

        ctx.fireChannelRegistered();
    }

    protected Bootstrap createBootstrap(Channel socket, Promise promise) throws Exception {
        SslContext sslContext;
        if (!ssl) {
            sslContext = null;

        } else {
            sslContext = SslContextBuilder.forClient().trustManager(InsecureTrustManagerFactory.INSTANCE).build();
        }

        return new Bootstrap().channel(NioSocketChannel.class).group(workerGroup)
                .option(ChannelOption.TCP_NODELAY, true).handler(new ChannelInitializer<SocketChannel>() {

                    @Override
                    protected void initChannel(SocketChannel channel) throws Exception {
                        System.out.println(
                                "Configure plain-socket: (" + socket + ") => (" + httpHost + ":" + httpPort + ")");
                        ChannelPipeline pipeline = channel.pipeline();
                        if (sslContext != null) {
                            pipeline.addLast("ssl", sslContext.newHandler(channel.alloc()));
                        }
                        pipeline.addLast("http-codec", new HttpClientCodec());
                        pipeline.addLast(new TunnelServerAcceptor(socket, promise));
                    }
                });
    }

    private class TunnelServerAcceptor extends SimpleChannelInboundHandler<HttpResponse> {

        private final Channel socket;
        private final Promise promise;

        TunnelServerAcceptor(Channel socket, Promise promise) {
            this.socket = socket;
            this.promise = promise;
        }

        @Override
        protected void channelRead0(ChannelHandlerContext ctx, HttpResponse msg) throws Exception {
            ChannelPipeline pipeline = ctx.pipeline();
            pipeline.addLast(new ProxyForwardHandler(socket));
            pipeline.remove(HttpRequestEncoder.class);
            pipeline.remove(HttpResponseDecoder.class);
            pipeline.remove(this);

            promise.setSuccess();
        }
    }
}