com.xx_dev.apn.proxy.ApnProxyUserAgentTunnelHandler.java Source code

Java tutorial

Introduction

Here is the source code for com.xx_dev.apn.proxy.ApnProxyUserAgentTunnelHandler.java

Source

/*
 * Copyright (c) 2014 The APN-PROXY Project
 *
 * The APN-PROXY Project 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.
 */

package com.xx_dev.apn.proxy;

import com.xx_dev.apn.proxy.remotechooser.ApnProxyRemote;
import com.xx_dev.apn.proxy.utils.Base64;
import com.xx_dev.apn.proxy.utils.LoggerUtil;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.buffer.Unpooled;
import io.netty.channel.*;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.*;
import io.netty.util.CharsetUtil;
import io.netty.util.ReferenceCountUtil;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;

import java.io.UnsupportedEncodingException;
import java.net.InetSocketAddress;
import java.util.Set;

/**
 * @author xmx
 * @version $Id: com.xx_dev.apn.proxy.ApnProxyUserAgentTunnelHandler 14-1-8 16:13 (xmx) Exp $
 */
public class ApnProxyUserAgentTunnelHandler extends ChannelInboundHandlerAdapter {

    private static final Logger logger = Logger.getLogger(ApnProxyUserAgentTunnelHandler.class);

    public static final String HANDLER_NAME = "apnproxy.useragent.tunnel";

    @Override
    public void channelRead(final ChannelHandlerContext uaChannelCtx, Object msg) throws Exception {

        if (msg instanceof HttpRequest) {
            final HttpRequest httpRequest = (HttpRequest) msg;

            //Channel uaChannel = uaChannelCtx.channel();

            // connect remote
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(uaChannelCtx.channel().eventLoop()).channel(NioSocketChannel.class)
                    .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 10000)
                    .option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
                    .option(ChannelOption.AUTO_READ, false)
                    .handler(new ApnProxyTunnelChannelInitializer(uaChannelCtx.channel()));

            final ApnProxyRemote apnProxyRemote = uaChannelCtx.channel()
                    .attr(ApnProxyConnectionAttribute.ATTRIBUTE_KEY).get().getRemote();

            // set local address
            if (StringUtils.isNotBlank(ApnProxyLocalAddressChooser.choose(apnProxyRemote.getRemoteHost()))) {
                bootstrap.localAddress(new InetSocketAddress(
                        (ApnProxyLocalAddressChooser.choose(apnProxyRemote.getRemoteHost())), 0));
            }

            bootstrap.connect(apnProxyRemote.getRemoteHost(), apnProxyRemote.getRemotePort())
                    .addListener(new ChannelFutureListener() {
                        @Override
                        public void operationComplete(final ChannelFuture future1) throws Exception {
                            if (future1.isSuccess()) {
                                if (apnProxyRemote.isAppleyRemoteRule()) {
                                    uaChannelCtx.pipeline().remove("codec");
                                    uaChannelCtx.pipeline().remove(ApnProxyPreHandler.HANDLER_NAME);
                                    uaChannelCtx.pipeline().remove(ApnProxyUserAgentTunnelHandler.HANDLER_NAME);

                                    // add relay handler
                                    uaChannelCtx.pipeline()
                                            .addLast(new ApnProxyRelayHandler("UA --> Remote", future1.channel()));

                                    future1.channel()
                                            .writeAndFlush(Unpooled.copiedBuffer(
                                                    constructConnectRequestForProxy(httpRequest, apnProxyRemote),
                                                    CharsetUtil.UTF_8))
                                            .addListener(new ChannelFutureListener() {
                                                @Override
                                                public void operationComplete(ChannelFuture future2)
                                                        throws Exception {
                                                    if (!future2.channel().config()
                                                            .getOption(ChannelOption.AUTO_READ)) {
                                                        future2.channel().read();
                                                    }
                                                }
                                            });

                                } else {
                                    HttpResponse proxyConnectSuccessResponse = new DefaultFullHttpResponse(
                                            HttpVersion.HTTP_1_1,
                                            new HttpResponseStatus(200, "Connection established"));
                                    uaChannelCtx.writeAndFlush(proxyConnectSuccessResponse)
                                            .addListener(new ChannelFutureListener() {
                                                @Override
                                                public void operationComplete(ChannelFuture future2)
                                                        throws Exception {
                                                    // remove handlers
                                                    uaChannelCtx.pipeline().remove("codec");
                                                    uaChannelCtx.pipeline().remove(ApnProxyPreHandler.HANDLER_NAME);
                                                    uaChannelCtx.pipeline()
                                                            .remove(ApnProxyUserAgentTunnelHandler.HANDLER_NAME);

                                                    // add relay handler
                                                    uaChannelCtx.pipeline()
                                                            .addLast(new ApnProxyRelayHandler(
                                                                    "UA --> " + apnProxyRemote.getRemoteAddr(),
                                                                    future1.channel()));
                                                }

                                            });
                                }

                            } else {
                                if (uaChannelCtx.channel().isActive()) {
                                    uaChannelCtx.channel().writeAndFlush(Unpooled.EMPTY_BUFFER)
                                            .addListener(ChannelFutureListener.CLOSE);
                                }
                            }
                        }
                    });

        }
        ReferenceCountUtil.release(msg);
    }

    private String constructConnectRequestForProxy(HttpRequest httpRequest, ApnProxyRemote apnProxyRemote) {
        String CRLF = "\r\n";
        String url = httpRequest.getUri();
        StringBuilder sb = new StringBuilder();
        sb.append(httpRequest.getMethod().name()).append(" ").append(url).append(" ")
                .append(httpRequest.getProtocolVersion().text()).append(CRLF);

        Set<String> headerNames = httpRequest.headers().names();
        for (String headerName : headerNames) {
            if (StringUtils.equalsIgnoreCase(headerName, "Proxy-Connection")) {
                continue;
            }

            if (StringUtils.equalsIgnoreCase(headerName, HttpHeaders.Names.CONNECTION)) {
                continue;
            }

            for (String headerValue : httpRequest.headers().getAll(headerName)) {
                sb.append(headerName).append(": ").append(headerValue).append(CRLF);
            }
        }

        if (StringUtils.isNotBlank(apnProxyRemote.getProxyUserName())
                && StringUtils.isNotBlank(apnProxyRemote.getProxyPassword())) {
            String proxyAuthorization = apnProxyRemote.getProxyUserName() + ":" + apnProxyRemote.getProxyPassword();
            try {
                sb.append("Proxy-Authorization: Basic "
                        + Base64.encodeBase64String(proxyAuthorization.getBytes("UTF-8"))).append(CRLF);
            } catch (UnsupportedEncodingException e) {
            }

        }

        sb.append(CRLF);

        return sb.toString();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        LoggerUtil.error(logger, cause.getMessage(), cause, ctx.attr(ApnProxyConnectionAttribute.ATTRIBUTE_KEY));
        ctx.close();
    }
}