/*
* Flazr <http://flazr.com> Copyright (C) 2009 Peter Thomas.
*
* This file is part of Flazr.
*
* Flazr is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Flazr is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with Flazr. If not, see <http://www.gnu.org/licenses/>.
*/
package com.flazr.rtmp.proxy;
import java.net.InetSocketAddress;
import org.jboss.netty.bootstrap.ClientBootstrap;
import org.jboss.netty.buffer.ChannelBuffer;
import org.jboss.netty.buffer.ChannelBuffers;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFuture;
import org.jboss.netty.channel.ChannelFutureListener;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelPipelineCoverage;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
import org.jboss.netty.channel.socket.ClientSocketChannelFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ChannelPipelineCoverage("one")
public class ProxyHandler extends SimpleChannelUpstreamHandler {
private static final Logger logger = LoggerFactory.getLogger(ProxyHandler.class);
private final ClientSocketChannelFactory cf;
private final String remoteHost;
private final int remotePort;
private volatile Channel outboundChannel;
public ProxyHandler(ClientSocketChannelFactory cf, String remoteHost, int remotePort) {
this.cf = cf;
this.remoteHost = remoteHost;
this.remotePort = remotePort;
}
@Override
public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e) {
final Channel inboundChannel = e.getChannel();
RtmpProxy.ALL_CHANNELS.add(inboundChannel);
inboundChannel.setReadable(false);
ClientBootstrap cb = new ClientBootstrap(cf);
cb.getPipeline().addLast("handshaker", new ProxyHandshakeHandler());
cb.getPipeline().addLast("handler", new OutboundHandler(e.getChannel()));
ChannelFuture f = cb.connect(new InetSocketAddress(remoteHost, remotePort));
outboundChannel = f.getChannel();
f.addListener(new ChannelFutureListener() {
@Override public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
logger.info("connected to remote host: {}, port: {}", remoteHost, remotePort);
inboundChannel.setReadable(true);
} else {
inboundChannel.close();
}
}
});
}
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
ChannelBuffer in = (ChannelBuffer) e.getMessage();
// logger.debug(">>> [{}] {}", in.readableBytes(), ChannelBuffers.hexDump(in));
outboundChannel.write(in);
}
@Override
public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) {
logger.info("closing inbound channel");
if (outboundChannel != null) {
closeOnFlush(outboundChannel);
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) {
logger.info("inbound exception: {}", e.getCause().getMessage());
closeOnFlush(e.getChannel());
}
@ChannelPipelineCoverage("one")
private class OutboundHandler extends SimpleChannelUpstreamHandler {
private final Channel inboundChannel;
public OutboundHandler(Channel inboundChannel) {
logger.info("opening outbound channel");
this.inboundChannel = inboundChannel;
}
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) {
ChannelBuffer in = (ChannelBuffer) e.getMessage();
// logger.debug("<<< [{}] {}", in.readableBytes(), ChannelBuffers.hexDump(in));
inboundChannel.write(in);
}
@Override
public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) {
logger.info("closing outbound channel");
closeOnFlush(inboundChannel);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) {
logger.info("outbound exception: {}", e.getCause().getMessage());
closeOnFlush(e.getChannel());
}
}
static void closeOnFlush(Channel ch) {
if (ch.isConnected()) {
ch.write(ChannelBuffers.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
}
}
}