Java tutorial
/* * 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(); } } }