Fixed formatting to conform to project standards

This commit is contained in:
iainmcgin 2011-04-08 14:22:12 +01:00 committed by Trustin Lee
parent f7729bc8bb
commit a617bd3290
58 changed files with 4454 additions and 4801 deletions

View File

@ -32,25 +32,25 @@ import org.jboss.netty.handler.codec.http.HttpResponseEncoder;
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
* @author OneDrum Ltd.
*/
class AcceptedServerChannelPipelineFactory implements ChannelPipelineFactory
{
class AcceptedServerChannelPipelineFactory implements ChannelPipelineFactory {
private final ServerMessageSwitch messageSwitch;
private final ServerMessageSwitch messageSwitch;
public AcceptedServerChannelPipelineFactory(ServerMessageSwitch messageSwitch)
{
this.messageSwitch = messageSwitch;
}
public AcceptedServerChannelPipelineFactory(
ServerMessageSwitch messageSwitch) {
this.messageSwitch = messageSwitch;
}
public ChannelPipeline getPipeline() throws Exception
{
ChannelPipeline pipeline = Channels.pipeline();
public ChannelPipeline getPipeline() throws Exception {
ChannelPipeline pipeline = Channels.pipeline();
pipeline.addLast("httpResponseEncoder", new HttpResponseEncoder());
pipeline.addLast("httpRequestDecoder", new HttpRequestDecoder());
pipeline.addLast("httpChunkAggregator", new HttpChunkAggregator(HttpTunnelMessageUtils.MAX_BODY_SIZE));
pipeline.addLast("messageSwitchClient", new AcceptedServerChannelRequestDispatch(messageSwitch));
pipeline.addLast("httpResponseEncoder", new HttpResponseEncoder());
pipeline.addLast("httpRequestDecoder", new HttpRequestDecoder());
pipeline.addLast("httpChunkAggregator", new HttpChunkAggregator(
HttpTunnelMessageUtils.MAX_BODY_SIZE));
pipeline.addLast("messageSwitchClient",
new AcceptedServerChannelRequestDispatch(messageSwitch));
return pipeline;
}
return pipeline;
}
}

View File

@ -39,156 +39,144 @@ import org.jboss.netty.logging.InternalLoggerFactory;
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
* @author OneDrum Ltd.
*/
class AcceptedServerChannelRequestDispatch extends SimpleChannelUpstreamHandler
{
class AcceptedServerChannelRequestDispatch extends SimpleChannelUpstreamHandler {
public static final String NAME = "AcceptedServerChannelRequestDispatch";
public static final String NAME = "AcceptedServerChannelRequestDispatch";
private static final InternalLogger LOG = InternalLoggerFactory
.getInstance(AcceptedServerChannelRequestDispatch.class);
private static final InternalLogger LOG = InternalLoggerFactory
.getInstance(AcceptedServerChannelRequestDispatch.class);
private final ServerMessageSwitchUpstreamInterface messageSwitch;
private final ServerMessageSwitchUpstreamInterface messageSwitch;
public AcceptedServerChannelRequestDispatch(ServerMessageSwitchUpstreamInterface messageSwitch)
{
this.messageSwitch = messageSwitch;
}
public AcceptedServerChannelRequestDispatch(
ServerMessageSwitchUpstreamInterface messageSwitch) {
this.messageSwitch = messageSwitch;
}
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception
{
HttpRequest request = (HttpRequest) e.getMessage();
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
throws Exception {
HttpRequest request = (HttpRequest) e.getMessage();
if (HttpTunnelMessageUtils.isOpenTunnelRequest(request))
{
handleOpenTunnel(ctx);
}
else if (HttpTunnelMessageUtils.isSendDataRequest(request))
{
handleSendData(ctx, request);
}
else if (HttpTunnelMessageUtils.isReceiveDataRequest(request))
{
handleReceiveData(ctx, request);
}
else if (HttpTunnelMessageUtils.isCloseTunnelRequest(request))
{
handleCloseTunnel(ctx, request);
}
else
{
respondWithRejection(ctx, request, "invalid request to netty HTTP tunnel gateway");
}
}
if (HttpTunnelMessageUtils.isOpenTunnelRequest(request)) {
handleOpenTunnel(ctx);
} else if (HttpTunnelMessageUtils.isSendDataRequest(request)) {
handleSendData(ctx, request);
} else if (HttpTunnelMessageUtils.isReceiveDataRequest(request)) {
handleReceiveData(ctx, request);
} else if (HttpTunnelMessageUtils.isCloseTunnelRequest(request)) {
handleCloseTunnel(ctx, request);
} else {
respondWithRejection(ctx, request,
"invalid request to netty HTTP tunnel gateway");
}
}
private void handleOpenTunnel(ChannelHandlerContext ctx)
{
String tunnelId = messageSwitch.createTunnel((InetSocketAddress) ctx.getChannel().getRemoteAddress());
if (LOG.isDebugEnabled())
{
LOG.debug("open tunnel request received from " + ctx.getChannel().getRemoteAddress() + " - allocated ID "
+ tunnelId);
}
respondWith(ctx, HttpTunnelMessageUtils.createTunnelOpenResponse(tunnelId));
}
private void handleOpenTunnel(ChannelHandlerContext ctx) {
String tunnelId =
messageSwitch.createTunnel((InetSocketAddress) ctx.getChannel()
.getRemoteAddress());
if (LOG.isDebugEnabled()) {
LOG.debug("open tunnel request received from " +
ctx.getChannel().getRemoteAddress() + " - allocated ID " +
tunnelId);
}
respondWith(ctx,
HttpTunnelMessageUtils.createTunnelOpenResponse(tunnelId));
}
private void handleCloseTunnel(ChannelHandlerContext ctx, HttpRequest request)
{
String tunnelId = checkTunnelId(request, ctx);
if (tunnelId == null)
{
return;
}
private void handleCloseTunnel(ChannelHandlerContext ctx,
HttpRequest request) {
String tunnelId = checkTunnelId(request, ctx);
if (tunnelId == null) {
return;
}
if (LOG.isDebugEnabled())
{
LOG.debug("close tunnel request received for tunnel " + tunnelId);
}
messageSwitch.clientCloseTunnel(tunnelId);
respondWith(ctx, HttpTunnelMessageUtils.createTunnelCloseResponse()).addListener(ChannelFutureListener.CLOSE);
}
if (LOG.isDebugEnabled()) {
LOG.debug("close tunnel request received for tunnel " + tunnelId);
}
messageSwitch.clientCloseTunnel(tunnelId);
respondWith(ctx, HttpTunnelMessageUtils.createTunnelCloseResponse())
.addListener(ChannelFutureListener.CLOSE);
}
private void handleSendData(ChannelHandlerContext ctx, HttpRequest request)
{
String tunnelId = checkTunnelId(request, ctx);
if (tunnelId == null)
{
return;
}
if (LOG.isDebugEnabled())
{
LOG.debug("send data request received for tunnel " + tunnelId);
}
private void handleSendData(ChannelHandlerContext ctx, HttpRequest request) {
String tunnelId = checkTunnelId(request, ctx);
if (tunnelId == null) {
return;
}
if (LOG.isDebugEnabled()) {
LOG.debug("send data request received for tunnel " + tunnelId);
}
if (HttpHeaders.getContentLength(request) == 0 || request.getContent() == null
|| request.getContent().readableBytes() == 0)
{
respondWithRejection(ctx, request, "Send data requests must contain data");
return;
}
if (HttpHeaders.getContentLength(request) == 0 ||
request.getContent() == null ||
request.getContent().readableBytes() == 0) {
respondWithRejection(ctx, request,
"Send data requests must contain data");
return;
}
messageSwitch.routeInboundData(tunnelId, request.getContent());
respondWith(ctx, HttpTunnelMessageUtils.createSendDataResponse());
}
messageSwitch.routeInboundData(tunnelId, request.getContent());
respondWith(ctx, HttpTunnelMessageUtils.createSendDataResponse());
}
private void handleReceiveData(ChannelHandlerContext ctx, HttpRequest request)
{
String tunnelId = checkTunnelId(request, ctx);
if (tunnelId == null)
{
return;
}
if (LOG.isDebugEnabled())
{
LOG.debug("poll data request received for tunnel " + tunnelId);
}
messageSwitch.pollOutboundData(tunnelId, ctx.getChannel());
}
private void handleReceiveData(ChannelHandlerContext ctx,
HttpRequest request) {
String tunnelId = checkTunnelId(request, ctx);
if (tunnelId == null) {
return;
}
if (LOG.isDebugEnabled()) {
LOG.debug("poll data request received for tunnel " + tunnelId);
}
messageSwitch.pollOutboundData(tunnelId, ctx.getChannel());
}
private String checkTunnelId(HttpRequest request, ChannelHandlerContext ctx)
{
String tunnelId = HttpTunnelMessageUtils.extractTunnelId(request);
if (tunnelId == null)
{
respondWithRejection(ctx, request, "no tunnel id specified in request");
}
else if (!messageSwitch.isOpenTunnel(tunnelId))
{
respondWithRejection(ctx, request, "specified tunnel is either closed or does not exist");
return null;
}
private String checkTunnelId(HttpRequest request, ChannelHandlerContext ctx) {
String tunnelId = HttpTunnelMessageUtils.extractTunnelId(request);
if (tunnelId == null) {
respondWithRejection(ctx, request,
"no tunnel id specified in request");
} else if (!messageSwitch.isOpenTunnel(tunnelId)) {
respondWithRejection(ctx, request,
"specified tunnel is either closed or does not exist");
return null;
}
return tunnelId;
}
return tunnelId;
}
/**
* Sends the provided response back on the channel, returning the created ChannelFuture
* for this operation.
*/
private ChannelFuture respondWith(ChannelHandlerContext ctx, HttpResponse response)
{
ChannelFuture writeFuture = Channels.future(ctx.getChannel());
Channels.write(ctx, writeFuture, response);
return writeFuture;
}
/**
* Sends the provided response back on the channel, returning the created ChannelFuture
* for this operation.
*/
private ChannelFuture respondWith(ChannelHandlerContext ctx,
HttpResponse response) {
ChannelFuture writeFuture = Channels.future(ctx.getChannel());
Channels.write(ctx, writeFuture, response);
return writeFuture;
}
/**
* Sends an HTTP 400 message back to on the channel with the specified error message, and asynchronously
* closes the channel after this is successfully sent.
*/
private void respondWithRejection(ChannelHandlerContext ctx, HttpRequest rejectedRequest, String errorMessage)
{
if (LOG.isWarnEnabled())
{
SocketAddress remoteAddress = ctx.getChannel().getRemoteAddress();
String tunnelId = HttpTunnelMessageUtils.extractTunnelId(rejectedRequest);
if (tunnelId == null)
{
tunnelId = "<UNKNOWN>";
}
LOG.warn("Rejecting request from " + remoteAddress + " representing tunnel " + tunnelId + ": " + errorMessage);
}
HttpResponse rejection = HttpTunnelMessageUtils.createRejection(rejectedRequest, errorMessage);
respondWith(ctx, rejection).addListener(ChannelFutureListener.CLOSE);
}
/**
* Sends an HTTP 400 message back to on the channel with the specified error message, and asynchronously
* closes the channel after this is successfully sent.
*/
private void respondWithRejection(ChannelHandlerContext ctx,
HttpRequest rejectedRequest, String errorMessage) {
if (LOG.isWarnEnabled()) {
SocketAddress remoteAddress = ctx.getChannel().getRemoteAddress();
String tunnelId =
HttpTunnelMessageUtils.extractTunnelId(rejectedRequest);
if (tunnelId == null) {
tunnelId = "<UNKNOWN>";
}
LOG.warn("Rejecting request from " + remoteAddress +
" representing tunnel " + tunnelId + ": " + errorMessage);
}
HttpResponse rejection =
HttpTunnelMessageUtils.createRejection(rejectedRequest,
errorMessage);
respondWith(ctx, rejection).addListener(ChannelFutureListener.CLOSE);
}
}

View File

@ -31,48 +31,41 @@ import org.jboss.netty.channel.ChannelFutureListener;
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
* @author OneDrum Ltd.
*/
class ChannelFutureAggregator implements ChannelFutureListener
{
class ChannelFutureAggregator implements ChannelFutureListener {
private final ChannelFuture aggregateFuture;
private final ChannelFuture aggregateFuture;
private final Set<ChannelFuture> pendingFutures;
private final Set<ChannelFuture> pendingFutures;
public ChannelFutureAggregator(ChannelFuture aggregateFuture)
{
this.aggregateFuture = aggregateFuture;
pendingFutures = new HashSet<ChannelFuture>();
}
public ChannelFutureAggregator(ChannelFuture aggregateFuture) {
this.aggregateFuture = aggregateFuture;
pendingFutures = new HashSet<ChannelFuture>();
}
public void addFuture(ChannelFuture future)
{
pendingFutures.add(future);
future.addListener(this);
}
public void addFuture(ChannelFuture future) {
pendingFutures.add(future);
future.addListener(this);
}
public synchronized void operationComplete(ChannelFuture future) throws Exception
{
if (future.isCancelled())
{
// TODO: what should the correct behaviour be when a fragment is cancelled?
// cancel all outstanding fragments and cancel the aggregate?
return;
}
public synchronized void operationComplete(ChannelFuture future)
throws Exception {
if (future.isCancelled()) {
// TODO: what should the correct behaviour be when a fragment is cancelled?
// cancel all outstanding fragments and cancel the aggregate?
return;
}
pendingFutures.remove(future);
if (!future.isSuccess())
{
aggregateFuture.setFailure(future.getCause());
for (ChannelFuture pendingFuture : pendingFutures)
{
pendingFuture.cancel();
}
return;
}
pendingFutures.remove(future);
if (!future.isSuccess()) {
aggregateFuture.setFailure(future.getCause());
for (ChannelFuture pendingFuture: pendingFutures) {
pendingFuture.cancel();
}
return;
}
if (pendingFutures.isEmpty())
{
aggregateFuture.setSuccess();
}
}
if (pendingFutures.isEmpty()) {
aggregateFuture.setSuccess();
}
}
}

View File

@ -27,27 +27,23 @@ import java.security.SecureRandom;
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
* @author OneDrum Ltd.
*/
public class DefaultTunnelIdGenerator implements TunnelIdGenerator
{
public class DefaultTunnelIdGenerator implements TunnelIdGenerator {
private SecureRandom generator;
private SecureRandom generator;
public DefaultTunnelIdGenerator()
{
this(new SecureRandom());
}
public DefaultTunnelIdGenerator() {
this(new SecureRandom());
}
public DefaultTunnelIdGenerator(SecureRandom generator)
{
this.generator = generator;
}
public DefaultTunnelIdGenerator(SecureRandom generator) {
this.generator = generator;
}
public synchronized String generateId()
{
// synchronized to ensure that this code is thread safe. The Sun
// standard implementations seem to be synchronized or lock free
// but are not documented as guaranteeing this
return Integer.toHexString(generator.nextInt());
}
public synchronized String generateId() {
// synchronized to ensure that this code is thread safe. The Sun
// standard implementations seem to be synchronized or lock free
// but are not documented as guaranteeing this
return Integer.toHexString(generator.nextInt());
}
}

View File

@ -40,78 +40,78 @@ import org.jboss.netty.channel.socket.SocketChannelConfig;
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
* @author OneDrum Ltd.
*/
class HttpTunnelAcceptedChannel extends AbstractChannel implements SocketChannel, HttpTunnelAcceptedChannelReceiver
{
private final HttpTunnelAcceptedChannelConfig config;
class HttpTunnelAcceptedChannel extends AbstractChannel implements
SocketChannel, HttpTunnelAcceptedChannelReceiver {
private final HttpTunnelAcceptedChannelConfig config;
private final HttpTunnelAcceptedChannelSink sink;
private final HttpTunnelAcceptedChannelSink sink;
private final InetSocketAddress remoteAddress;
private final InetSocketAddress remoteAddress;
protected HttpTunnelAcceptedChannel(HttpTunnelServerChannel parent, ChannelFactory factory,
ChannelPipeline pipeline, HttpTunnelAcceptedChannelSink sink, InetSocketAddress remoteAddress, HttpTunnelAcceptedChannelConfig config)
{
super(parent, factory, pipeline, sink);
this.config = config;
this.sink = sink;
this.remoteAddress = remoteAddress;
fireChannelOpen(this);
fireChannelBound(this, getLocalAddress());
fireChannelConnected(this, getRemoteAddress());
}
protected HttpTunnelAcceptedChannel(HttpTunnelServerChannel parent,
ChannelFactory factory, ChannelPipeline pipeline,
HttpTunnelAcceptedChannelSink sink,
InetSocketAddress remoteAddress,
HttpTunnelAcceptedChannelConfig config) {
super(parent, factory, pipeline, sink);
this.config = config;
this.sink = sink;
this.remoteAddress = remoteAddress;
fireChannelOpen(this);
fireChannelBound(this, getLocalAddress());
fireChannelConnected(this, getRemoteAddress());
}
public SocketChannelConfig getConfig()
{
return config;
}
public SocketChannelConfig getConfig() {
return config;
}
public InetSocketAddress getLocalAddress()
{
return ((HttpTunnelServerChannel) getParent()).getLocalAddress();
}
public InetSocketAddress getLocalAddress() {
public InetSocketAddress getRemoteAddress()
{
return remoteAddress;
}
return ((HttpTunnelServerChannel) getParent()).getLocalAddress();
}
public boolean isBound()
{
return sink.isActive();
}
public InetSocketAddress getRemoteAddress() {
return remoteAddress;
}
public boolean isConnected()
{
return sink.isActive();
}
public boolean isBound() {
return sink.isActive();
}
public void clientClosed()
{
this.setClosed();
Channels.fireChannelClosed(this);
}
public boolean isConnected() {
return sink.isActive();
}
public void dataReceived(ChannelBuffer data)
{
Channels.fireMessageReceived(this, data);
}
public void clientClosed() {
this.setClosed();
Channels.fireChannelClosed(this);
}
public void updateInterestOps(SaturationStateChange transition) {
switch(transition) {
case SATURATED: fireWriteEnabled(false); break;
case DESATURATED: fireWriteEnabled(true); break;
case NO_CHANGE: break;
}
}
private void fireWriteEnabled(boolean enabled) {
int ops = OP_READ;
if(!enabled) {
ops |= OP_WRITE;
}
setInterestOpsNow(ops);
Channels.fireChannelInterestChanged(this);
}
public void dataReceived(ChannelBuffer data) {
Channels.fireMessageReceived(this, data);
}
public void updateInterestOps(SaturationStateChange transition) {
switch (transition) {
case SATURATED:
fireWriteEnabled(false);
break;
case DESATURATED:
fireWriteEnabled(true);
break;
case NO_CHANGE:
break;
}
}
private void fireWriteEnabled(boolean enabled) {
int ops = OP_READ;
if (!enabled) {
ops |= OP_WRITE;
}
setInterestOpsNow(ops);
Channels.fireChannelInterestChanged(this);
}
}

View File

@ -26,105 +26,90 @@ package org.jboss.netty.channel.socket.http;
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
* @author OneDrum Ltd.
*/
public class HttpTunnelAcceptedChannelConfig extends HttpTunnelChannelConfig
{
public class HttpTunnelAcceptedChannelConfig extends HttpTunnelChannelConfig {
private static final int SO_LINGER_DISABLED = -1;
private static final int SO_LINGER_DISABLED = -1;
private static final int FAKE_SEND_BUFFER_SIZE = 16 * 1024;
private static final int FAKE_SEND_BUFFER_SIZE = 16 * 1024;
private static final int FAKE_RECEIVE_BUFFER_SIZE = 16 * 1024;
private static final int FAKE_RECEIVE_BUFFER_SIZE = 16 * 1024;
// based on the values in RFC 791
private static final int DEFAULT_TRAFFIC_CLASS = 0;
// based on the values in RFC 791
private static final int DEFAULT_TRAFFIC_CLASS = 0;
@Override
public boolean isTcpNoDelay()
{
return true;
}
@Override
public boolean isTcpNoDelay() {
return true;
}
@Override
public void setTcpNoDelay(boolean tcpNoDelay)
{
// we do not allow the value to be changed, as it will not be honoured
}
@Override
public void setTcpNoDelay(boolean tcpNoDelay) {
// we do not allow the value to be changed, as it will not be honoured
}
@Override
public int getSoLinger()
{
return SO_LINGER_DISABLED;
}
@Override
public int getSoLinger() {
return SO_LINGER_DISABLED;
}
@Override
public void setSoLinger(int soLinger)
{
// we do not allow the value to be changed, as it will not be honoured
}
@Override
public void setSoLinger(int soLinger) {
// we do not allow the value to be changed, as it will not be honoured
}
@Override
public int getSendBufferSize()
{
return FAKE_SEND_BUFFER_SIZE;
}
@Override
public int getSendBufferSize() {
return FAKE_SEND_BUFFER_SIZE;
}
@Override
public void setSendBufferSize(int sendBufferSize)
{
// we do not allow the value to be changed, as it will not be honoured
}
@Override
public void setSendBufferSize(int sendBufferSize) {
// we do not allow the value to be changed, as it will not be honoured
}
@Override
public int getReceiveBufferSize()
{
return FAKE_RECEIVE_BUFFER_SIZE;
}
@Override
public int getReceiveBufferSize() {
return FAKE_RECEIVE_BUFFER_SIZE;
}
@Override
public void setReceiveBufferSize(int receiveBufferSize)
{
// we do not allow the value to be changed, as it will not be honoured
}
@Override
public void setReceiveBufferSize(int receiveBufferSize) {
// we do not allow the value to be changed, as it will not be honoured
}
@Override
public boolean isKeepAlive()
{
return true;
}
@Override
public boolean isKeepAlive() {
return true;
}
@Override
public void setKeepAlive(boolean keepAlive)
{
// we do not allow the value to be changed, as it will not be honoured
}
@Override
public void setKeepAlive(boolean keepAlive) {
// we do not allow the value to be changed, as it will not be honoured
}
@Override
public int getTrafficClass()
{
return DEFAULT_TRAFFIC_CLASS;
}
@Override
public int getTrafficClass() {
return DEFAULT_TRAFFIC_CLASS;
}
@Override
public void setTrafficClass(int trafficClass)
{
// we do not allow the value to be changed, as it will not be honoured
}
@Override
public void setTrafficClass(int trafficClass) {
// we do not allow the value to be changed, as it will not be honoured
}
@Override
public boolean isReuseAddress()
{
return false;
}
@Override
public boolean isReuseAddress() {
return false;
}
@Override
public void setReuseAddress(boolean reuseAddress)
{
// we do not allow the value to be changed, as it will not be honoured
}
@Override
public void setReuseAddress(boolean reuseAddress) {
// we do not allow the value to be changed, as it will not be honoured
}
@Override
public void setPerformancePreferences(int connectionTime, int latency, int bandwidth)
{
// we do not allow the value to be changed, as it will not be honoured
}
@Override
public void setPerformancePreferences(int connectionTime, int latency,
int bandwidth) {
// we do not allow the value to be changed, as it will not be honoured
}
}

View File

@ -26,9 +26,9 @@ import java.net.InetSocketAddress;
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
* @author OneDrum Ltd.
*/
interface HttpTunnelAcceptedChannelFactory
{
public HttpTunnelAcceptedChannelReceiver newChannel(String newTunnelId, InetSocketAddress remoteAddress);
interface HttpTunnelAcceptedChannelFactory {
public HttpTunnelAcceptedChannelReceiver newChannel(String newTunnelId,
InetSocketAddress remoteAddress);
public String generateTunnelId();
public String generateTunnelId();
}

View File

@ -25,13 +25,12 @@ import org.jboss.netty.buffer.ChannelBuffer;
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
* @author OneDrum Ltd.
*/
interface HttpTunnelAcceptedChannelReceiver
{
interface HttpTunnelAcceptedChannelReceiver {
public void updateInterestOps(SaturationStateChange transition);
public void dataReceived(ChannelBuffer data);
public void updateInterestOps(SaturationStateChange transition);
public void clientClosed();
public void dataReceived(ChannelBuffer data);
public void clientClosed();
}

View File

@ -37,97 +37,95 @@ import org.jboss.netty.channel.MessageEvent;
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
* @author OneDrum Ltd.
*/
class HttpTunnelAcceptedChannelSink extends AbstractChannelSink
{
private final SaturationManager saturationManager;
private final ServerMessageSwitchDownstreamInterface messageSwitch;
class HttpTunnelAcceptedChannelSink extends AbstractChannelSink {
private final String tunnelId;
private final SaturationManager saturationManager;
private AtomicBoolean active = new AtomicBoolean(false);
private HttpTunnelAcceptedChannelConfig config;
private final ServerMessageSwitchDownstreamInterface messageSwitch;
public HttpTunnelAcceptedChannelSink(ServerMessageSwitchDownstreamInterface messageSwitch, String tunnelId, HttpTunnelAcceptedChannelConfig config)
{
this.messageSwitch = messageSwitch;
this.tunnelId = tunnelId;
this.config = config;
this.saturationManager = new SaturationManager(config.getWriteBufferLowWaterMark(), config.getWriteBufferHighWaterMark());
}
private final String tunnelId;
public void eventSunk(ChannelPipeline pipeline, ChannelEvent e) throws Exception
{
if (e instanceof MessageEvent)
{
handleMessageEvent((MessageEvent) e);
}
else if (e instanceof ChannelStateEvent)
{
handleStateEvent((ChannelStateEvent) e);
}
}
private AtomicBoolean active = new AtomicBoolean(false);
private void handleMessageEvent(MessageEvent ev)
{
if (!(ev.getMessage() instanceof ChannelBuffer))
{
throw new IllegalArgumentException("Attempt to send data which is not a ChannelBuffer:" + ev.getMessage());
}
final HttpTunnelAcceptedChannelReceiver channel = (HttpTunnelAcceptedChannelReceiver) ev.getChannel();
final ChannelBuffer message = (ChannelBuffer) ev.getMessage();
final int messageSize = message.readableBytes();
final ChannelFuture future = ev.getFuture();
saturationManager.updateThresholds(config.getWriteBufferLowWaterMark(), config.getWriteBufferHighWaterMark());
channel.updateInterestOps(saturationManager.queueSizeChanged(messageSize));
future.addListener(new ChannelFutureListener()
{
@Override
public void operationComplete(ChannelFuture future) throws Exception
{
channel.updateInterestOps(saturationManager.queueSizeChanged(-messageSize));
}
});
messageSwitch.routeOutboundData(tunnelId, message, future);
}
private HttpTunnelAcceptedChannelConfig config;
private void handleStateEvent(ChannelStateEvent ev)
{
/* TODO: as any of disconnect, unbind or close destroys a server
channel, should we fire all three events always? */
Channel owner = ev.getChannel();
switch (ev.getState())
{
case OPEN :
if (Boolean.FALSE.equals(ev.getValue()))
{
messageSwitch.serverCloseTunnel(tunnelId);
active.set(false);
Channels.fireChannelClosed(owner);
public HttpTunnelAcceptedChannelSink(
ServerMessageSwitchDownstreamInterface messageSwitch,
String tunnelId, HttpTunnelAcceptedChannelConfig config) {
this.messageSwitch = messageSwitch;
this.tunnelId = tunnelId;
this.config = config;
this.saturationManager =
new SaturationManager(config.getWriteBufferLowWaterMark(),
config.getWriteBufferHighWaterMark());
}
public void eventSunk(ChannelPipeline pipeline, ChannelEvent e)
throws Exception {
if (e instanceof MessageEvent) {
handleMessageEvent((MessageEvent) e);
} else if (e instanceof ChannelStateEvent) {
handleStateEvent((ChannelStateEvent) e);
}
}
private void handleMessageEvent(MessageEvent ev) {
if (!(ev.getMessage() instanceof ChannelBuffer)) {
throw new IllegalArgumentException(
"Attempt to send data which is not a ChannelBuffer:" +
ev.getMessage());
}
final HttpTunnelAcceptedChannelReceiver channel =
(HttpTunnelAcceptedChannelReceiver) ev.getChannel();
final ChannelBuffer message = (ChannelBuffer) ev.getMessage();
final int messageSize = message.readableBytes();
final ChannelFuture future = ev.getFuture();
saturationManager.updateThresholds(config.getWriteBufferLowWaterMark(),
config.getWriteBufferHighWaterMark());
channel.updateInterestOps(saturationManager
.queueSizeChanged(messageSize));
future.addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future)
throws Exception {
channel.updateInterestOps(saturationManager
.queueSizeChanged(-messageSize));
}
});
messageSwitch.routeOutboundData(tunnelId, message, future);
}
private void handleStateEvent(ChannelStateEvent ev) {
/* TODO: as any of disconnect, unbind or close destroys a server
channel, should we fire all three events always? */
Channel owner = ev.getChannel();
switch (ev.getState()) {
case OPEN:
if (Boolean.FALSE.equals(ev.getValue())) {
messageSwitch.serverCloseTunnel(tunnelId);
active.set(false);
Channels.fireChannelClosed(owner);
}
break;
case BOUND :
if (ev.getValue() == null)
{
messageSwitch.serverCloseTunnel(tunnelId);
active.set(false);
Channels.fireChannelUnbound(owner);
case BOUND:
if (ev.getValue() == null) {
messageSwitch.serverCloseTunnel(tunnelId);
active.set(false);
Channels.fireChannelUnbound(owner);
}
case CONNECTED :
if (ev.getValue() == null)
{
messageSwitch.serverCloseTunnel(tunnelId);
active.set(false);
Channels.fireChannelDisconnected(owner);
case CONNECTED:
if (ev.getValue() == null) {
messageSwitch.serverCloseTunnel(tunnelId);
active.set(false);
Channels.fireChannelDisconnected(owner);
}
}
}
}
}
public boolean isActive()
{
return active.get();
}
public boolean isActive() {
return active.get();
}
}

View File

@ -37,128 +37,117 @@ import org.jboss.netty.channel.socket.SocketChannelConfig;
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
* @author OneDrum Ltd.
*/
public abstract class HttpTunnelChannelConfig extends DefaultChannelConfig implements SocketChannelConfig
{
public abstract class HttpTunnelChannelConfig extends DefaultChannelConfig
implements SocketChannelConfig {
/**
* The minimum value that the high water mark may be set to, in addition to the
* constraint that the high water mark must be strictly greater than the low
* water mark.
*/
public static final int MIN_HIGH_WATER_MARK = 1;
/**
* The minimum value that the high water mark may be set to, in addition to the
* constraint that the high water mark must be strictly greater than the low
* water mark.
*/
public static final int MIN_HIGH_WATER_MARK = 1;
/**
* The minimum value that the low water mark may be set to.
*/
public static final int MIN_LOW_WATER_MARK = 0;
/**
* The minimum value that the low water mark may be set to.
*/
public static final int MIN_LOW_WATER_MARK = 0;
/**
* The default level for the write buffer's high water mark, presently set to
* 64KByte.
*/
public static final int DEFAULT_HIGH_WATER_MARK = 64 * 1024;
/**
* The default level for the write buffer's high water mark, presently set to
* 64KByte.
*/
public static final int DEFAULT_HIGH_WATER_MARK = 64 * 1024;
/**
* The default level for the write buffer's low water mark, presently set to
* 32KByte.
*/
public static final int DEFAULT_LOW_WATER_MARK = 32 * 1024;
static final String HIGH_WATER_MARK_OPTION = "writeBufferhHighWaterMark";
static final String LOW_WATER_MARK_OPTION = "writeBufferLowWaterMark";
/**
* The default level for the write buffer's low water mark, presently set to
* 32KByte.
*/
public static final int DEFAULT_LOW_WATER_MARK = 32 * 1024;
protected volatile int writeBufferLowWaterMark = DEFAULT_LOW_WATER_MARK;
static final String HIGH_WATER_MARK_OPTION = "writeBufferhHighWaterMark";
protected volatile int writeBufferHighWaterMark = DEFAULT_HIGH_WATER_MARK;
static final String LOW_WATER_MARK_OPTION = "writeBufferLowWaterMark";
/**
* @return the current value (in bytes) of the high water mark.
*/
public int getWriteBufferHighWaterMark()
{
return writeBufferHighWaterMark;
}
protected volatile int writeBufferLowWaterMark = DEFAULT_LOW_WATER_MARK;
/**
* Similarly to {@link org.jboss.netty.channel.socket.nio.NioSocketChannelConfig#setWriteBufferHighWaterMark(int)
* NioSocketChannelConfig.setWriteBufferHighWaterMark()},
* the high water mark refers to the buffer size at which a user of the channel should stop writing. When the
* number of queued bytes exceeds the high water mark, {@link org.jboss.netty.channel.Channel#isWritable() Channel.isWritable()} will
* return false. Once the number of queued bytes falls below the {@link #setWriteBufferLowWaterMark(int) low water mark},
* {@link org.jboss.netty.channel.Channel#isWritable() Channel.isWritable()} will return true again, indicating that the client
* can begin to send more data.
*
* @param level the number of queued bytes required to flip {@link org.jboss.netty.channel.Channel#isWritable()} to
* false.
*
* @see {@link org.jboss.netty.channel.socket.nio.NioSocketChannelConfig#setWriteBufferHighWaterMark(int) NioSocketChannelConfig.setWriteBufferHighWaterMark()}
*/
public void setWriteBufferHighWaterMark(int level)
{
if (level <= writeBufferLowWaterMark)
{
throw new IllegalArgumentException(
"Write buffer high water mark must be strictly greater than the low water mark");
}
protected volatile int writeBufferHighWaterMark = DEFAULT_HIGH_WATER_MARK;
if (level < MIN_HIGH_WATER_MARK)
{
throw new IllegalArgumentException("Cannot set write buffer high water mark lower than " + MIN_HIGH_WATER_MARK);
}
/**
* @return the current value (in bytes) of the high water mark.
*/
public int getWriteBufferHighWaterMark() {
return writeBufferHighWaterMark;
}
writeBufferHighWaterMark = level;
}
/**
* @return the current value (in bytes) of the low water mark.
*/
public int getWriteBufferLowWaterMark()
{
return writeBufferLowWaterMark;
}
/**
* Similarly to {@link org.jboss.netty.channel.socket.nio.NioSocketChannelConfig#setWriteBufferHighWaterMark(int)
* NioSocketChannelConfig.setWriteBufferHighWaterMark()},
* the high water mark refers to the buffer size at which a user of the channel should stop writing. When the
* number of queued bytes exceeds the high water mark, {@link org.jboss.netty.channel.Channel#isWritable() Channel.isWritable()} will
* return false. Once the number of queued bytes falls below the {@link #setWriteBufferLowWaterMark(int) low water mark},
* {@link org.jboss.netty.channel.Channel#isWritable() Channel.isWritable()} will return true again, indicating that the client
* can begin to send more data.
*
* @param level the number of queued bytes required to flip {@link org.jboss.netty.channel.Channel#isWritable()} to
* false.
*
* @see {@link org.jboss.netty.channel.socket.nio.NioSocketChannelConfig#setWriteBufferHighWaterMark(int) NioSocketChannelConfig.setWriteBufferHighWaterMark()}
*/
public void setWriteBufferHighWaterMark(int level) {
if (level <= writeBufferLowWaterMark) {
throw new IllegalArgumentException(
"Write buffer high water mark must be strictly greater than the low water mark");
}
/**
* The low water mark refers to the "safe" size of the queued byte buffer at which more data can be enqueued. When
* the {@link #setWriteBufferHighWaterMark(int) high water mark} is exceeded, {@link org.jboss.netty.channel.Channel#isWritable() Channel.isWriteable()}
* will return false until the buffer drops below this level. By creating a sufficient gap between the high and low
* water marks, rapid oscillation between "write enabled" and "write disabled" can be avoided.
*
* @see {@link org.jboss.netty.channel.socket.nio.NioSocketChannelConfig#setWriteBufferLowWaterMark(int) NioSocketChannelConfig.setWriteBufferLowWaterMark()}
*/
public void setWriteBufferLowWaterMark(int level)
{
if (level >= writeBufferHighWaterMark)
{
throw new IllegalArgumentException(
"Write buffer low water mark must be strictly less than the high water mark");
}
if (level < MIN_HIGH_WATER_MARK) {
throw new IllegalArgumentException(
"Cannot set write buffer high water mark lower than " +
MIN_HIGH_WATER_MARK);
}
if (level < MIN_LOW_WATER_MARK)
{
throw new IllegalArgumentException("Cannot set write buffer low water mark lower than " + MIN_LOW_WATER_MARK);
}
writeBufferHighWaterMark = level;
}
writeBufferLowWaterMark = level;
}
@Override
public boolean setOption(String key, Object value)
{
if (HIGH_WATER_MARK_OPTION.equals(key))
{
setWriteBufferHighWaterMark((Integer) value);
}
else if (LOW_WATER_MARK_OPTION.equals(key))
{
setWriteBufferLowWaterMark((Integer) value);
}
else
{
return super.setOption(key, value);
}
/**
* @return the current value (in bytes) of the low water mark.
*/
public int getWriteBufferLowWaterMark() {
return writeBufferLowWaterMark;
}
return true;
}
/**
* The low water mark refers to the "safe" size of the queued byte buffer at which more data can be enqueued. When
* the {@link #setWriteBufferHighWaterMark(int) high water mark} is exceeded, {@link org.jboss.netty.channel.Channel#isWritable() Channel.isWriteable()}
* will return false until the buffer drops below this level. By creating a sufficient gap between the high and low
* water marks, rapid oscillation between "write enabled" and "write disabled" can be avoided.
*
* @see {@link org.jboss.netty.channel.socket.nio.NioSocketChannelConfig#setWriteBufferLowWaterMark(int) NioSocketChannelConfig.setWriteBufferLowWaterMark()}
*/
public void setWriteBufferLowWaterMark(int level) {
if (level >= writeBufferHighWaterMark) {
throw new IllegalArgumentException(
"Write buffer low water mark must be strictly less than the high water mark");
}
if (level < MIN_LOW_WATER_MARK) {
throw new IllegalArgumentException(
"Cannot set write buffer low water mark lower than " +
MIN_LOW_WATER_MARK);
}
writeBufferLowWaterMark = level;
}
@Override
public boolean setOption(String key, Object value) {
if (HIGH_WATER_MARK_OPTION.equals(key)) {
setWriteBufferHighWaterMark((Integer) value);
} else if (LOW_WATER_MARK_OPTION.equals(key)) {
setWriteBufferLowWaterMark((Integer) value);
} else {
return super.setOption(key, value);
}
return true;
}
}

View File

@ -45,341 +45,333 @@ import org.jboss.netty.logging.InternalLoggerFactory;
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
* @author OneDrum Ltd.
*/
public class HttpTunnelClientChannel extends AbstractChannel implements SocketChannel
{
public class HttpTunnelClientChannel extends AbstractChannel implements
SocketChannel {
private static final InternalLogger LOG = InternalLoggerFactory.getInstance(HttpTunnelClientChannel.class);
private static final InternalLogger LOG = InternalLoggerFactory
.getInstance(HttpTunnelClientChannel.class);
private final HttpTunnelClientChannelConfig config;
private final HttpTunnelClientChannelConfig config;
private final SocketChannel sendChannel;
private final SocketChannel sendChannel;
private final SocketChannel pollChannel;
private final SocketChannel pollChannel;
private volatile String tunnelId;
private volatile String tunnelId;
private volatile ChannelFuture connectFuture;
private volatile ChannelFuture connectFuture;
private volatile boolean connected;
private volatile boolean connected;
private volatile boolean bound;
private volatile boolean bound;
volatile InetSocketAddress serverAddress;
volatile InetSocketAddress serverAddress;
private volatile String serverHostName;
private volatile String serverHostName;
private final WorkerCallbacks callbackProxy;
private final WorkerCallbacks callbackProxy;
private final SaturationManager saturationManager;
private final SaturationManager saturationManager;
/**
* @see {@link HttpTunnelClientChannelFactory#newChannel(ChannelPipeline)}
*/
protected HttpTunnelClientChannel(ChannelFactory factory, ChannelPipeline pipeline,
HttpTunnelClientChannelSink sink, ClientSocketChannelFactory outboundFactory, ChannelGroup realConnections)
{
super(null, factory, pipeline, sink);
/**
* @see {@link HttpTunnelClientChannelFactory#newChannel(ChannelPipeline)}
*/
protected HttpTunnelClientChannel(ChannelFactory factory,
ChannelPipeline pipeline, HttpTunnelClientChannelSink sink,
ClientSocketChannelFactory outboundFactory,
ChannelGroup realConnections) {
super(null, factory, pipeline, sink);
this.callbackProxy = new WorkerCallbacks();
this.callbackProxy = new WorkerCallbacks();
sendChannel = outboundFactory.newChannel(createSendPipeline());
pollChannel = outboundFactory.newChannel(createPollPipeline());
config = new HttpTunnelClientChannelConfig(sendChannel.getConfig(), pollChannel.getConfig());
saturationManager = new SaturationManager(config.getWriteBufferLowWaterMark(), config.getWriteBufferHighWaterMark());
serverAddress = null;
sendChannel = outboundFactory.newChannel(createSendPipeline());
pollChannel = outboundFactory.newChannel(createPollPipeline());
config =
new HttpTunnelClientChannelConfig(sendChannel.getConfig(),
pollChannel.getConfig());
saturationManager =
new SaturationManager(config.getWriteBufferLowWaterMark(),
config.getWriteBufferHighWaterMark());
serverAddress = null;
realConnections.add(sendChannel);
realConnections.add(pollChannel);
realConnections.add(sendChannel);
realConnections.add(pollChannel);
Channels.fireChannelOpen(this);
}
Channels.fireChannelOpen(this);
}
public HttpTunnelClientChannelConfig getConfig()
{
return config;
}
public HttpTunnelClientChannelConfig getConfig() {
return config;
}
public boolean isBound()
{
return bound;
}
public boolean isBound() {
return bound;
}
public boolean isConnected()
{
return connected;
}
public boolean isConnected() {
return connected;
}
public InetSocketAddress getLocalAddress()
{
return sendChannel.getLocalAddress();
}
public InetSocketAddress getLocalAddress() {
return sendChannel.getLocalAddress();
}
public InetSocketAddress getRemoteAddress()
{
return serverAddress;
}
public InetSocketAddress getRemoteAddress() {
return serverAddress;
}
void onConnectRequest(ChannelFuture connectFuture, InetSocketAddress remoteAddress)
{
this.connectFuture = connectFuture;
/* if we are using a proxy, the remoteAddress is swapped here for the address of the proxy.
* The send and poll channels can later ask for the correct server address using
* getServerHostName().
*/
serverAddress = remoteAddress;
void onConnectRequest(ChannelFuture connectFuture,
InetSocketAddress remoteAddress) {
this.connectFuture = connectFuture;
/* if we are using a proxy, the remoteAddress is swapped here for the address of the proxy.
* The send and poll channels can later ask for the correct server address using
* getServerHostName().
*/
serverAddress = remoteAddress;
SocketAddress connectTarget;
if (config.getProxyAddress() != null)
{
connectTarget = config.getProxyAddress();
}
else
{
connectTarget = remoteAddress;
}
SocketAddress connectTarget;
if (config.getProxyAddress() != null) {
connectTarget = config.getProxyAddress();
} else {
connectTarget = remoteAddress;
}
Channels.connect(sendChannel, connectTarget);
}
Channels.connect(sendChannel, connectTarget);
}
void onDisconnectRequest(final ChannelFuture disconnectFuture)
{
ChannelFutureListener disconnectListener = new ConsolidatingFutureListener(disconnectFuture, 2);
sendChannel.disconnect().addListener(disconnectListener);
pollChannel.disconnect().addListener(disconnectListener);
void onDisconnectRequest(final ChannelFuture disconnectFuture) {
ChannelFutureListener disconnectListener =
new ConsolidatingFutureListener(disconnectFuture, 2);
sendChannel.disconnect().addListener(disconnectListener);
pollChannel.disconnect().addListener(disconnectListener);
disconnectFuture.addListener(new ChannelFutureListener()
{
public void operationComplete(ChannelFuture future) throws Exception
{
serverAddress = null;
}
});
}
void onBindRequest(InetSocketAddress localAddress, final ChannelFuture bindFuture)
{
ChannelFutureListener bindListener = new ConsolidatingFutureListener(bindFuture, 2);
// bind the send channel to the specified local address, and the poll channel to
// an ephemeral port on the same interface as the send channel
sendChannel.bind(localAddress).addListener(bindListener);
InetSocketAddress pollBindAddress;
if (localAddress.isUnresolved())
{
pollBindAddress = InetSocketAddress.createUnresolved(localAddress.getHostName(), 0);
}
else
{
pollBindAddress = new InetSocketAddress(localAddress.getAddress(), 0);
}
pollChannel.bind(pollBindAddress).addListener(bindListener);
}
void onUnbindRequest(final ChannelFuture unbindFuture)
{
ChannelFutureListener unbindListener = new ConsolidatingFutureListener(unbindFuture, 2);
sendChannel.unbind().addListener(unbindListener);
pollChannel.unbind().addListener(unbindListener);
}
void onCloseRequest(final ChannelFuture closeFuture)
{
ChannelFutureListener closeListener = new CloseConsolidatingFutureListener(closeFuture, 2);
sendChannel.close().addListener(closeListener);
pollChannel.close().addListener(closeListener);
}
private ChannelPipeline createSendPipeline()
{
ChannelPipeline pipeline = Channels.pipeline();
pipeline.addLast("reqencoder", new HttpRequestEncoder()); // downstream
pipeline.addLast("respdecoder", new HttpResponseDecoder()); // upstream
pipeline.addLast("aggregator", new HttpChunkAggregator(HttpTunnelMessageUtils.MAX_BODY_SIZE)); // upstream
pipeline.addLast("sendHandler", new HttpTunnelClientSendHandler(callbackProxy)); // both
pipeline.addLast("writeFragmenter", new WriteFragmenter(HttpTunnelMessageUtils.MAX_BODY_SIZE));
return pipeline;
}
private ChannelPipeline createPollPipeline()
{
ChannelPipeline pipeline = Channels.pipeline();
pipeline.addLast("reqencoder", new HttpRequestEncoder()); // downstream
pipeline.addLast("respdecoder", new HttpResponseDecoder()); // upstream
pipeline.addLast("aggregator", new HttpChunkAggregator(HttpTunnelMessageUtils.MAX_BODY_SIZE)); // upstream
pipeline.addLast(HttpTunnelClientPollHandler.NAME, new HttpTunnelClientPollHandler(callbackProxy)); // both
return pipeline;
}
private void setTunnelIdForPollChannel()
{
HttpTunnelClientPollHandler pollHandler = pollChannel.getPipeline().get(HttpTunnelClientPollHandler.class);
pollHandler.setTunnelId(tunnelId);
}
void sendData(final MessageEvent e)
{
saturationManager.updateThresholds(config.getWriteBufferLowWaterMark(),
config.getWriteBufferHighWaterMark());
final ChannelFuture originalFuture = e.getFuture();
final ChannelBuffer message = (ChannelBuffer) e.getMessage();
final int messageSize = message.readableBytes();
updateSaturationStatus(messageSize);
Channels.write(sendChannel, e.getMessage()).addListener(new ChannelFutureListener()
{
public void operationComplete(ChannelFuture future) throws Exception
{
if (future.isSuccess())
{
originalFuture.setSuccess();
disconnectFuture.addListener(new ChannelFutureListener() {
public void operationComplete(ChannelFuture future)
throws Exception {
serverAddress = null;
}
else
{
originalFuture.setFailure(future.getCause());
});
}
void onBindRequest(InetSocketAddress localAddress,
final ChannelFuture bindFuture) {
ChannelFutureListener bindListener =
new ConsolidatingFutureListener(bindFuture, 2);
// bind the send channel to the specified local address, and the poll channel to
// an ephemeral port on the same interface as the send channel
sendChannel.bind(localAddress).addListener(bindListener);
InetSocketAddress pollBindAddress;
if (localAddress.isUnresolved()) {
pollBindAddress =
InetSocketAddress.createUnresolved(
localAddress.getHostName(), 0);
} else {
pollBindAddress =
new InetSocketAddress(localAddress.getAddress(), 0);
}
pollChannel.bind(pollBindAddress).addListener(bindListener);
}
void onUnbindRequest(final ChannelFuture unbindFuture) {
ChannelFutureListener unbindListener =
new ConsolidatingFutureListener(unbindFuture, 2);
sendChannel.unbind().addListener(unbindListener);
pollChannel.unbind().addListener(unbindListener);
}
void onCloseRequest(final ChannelFuture closeFuture) {
ChannelFutureListener closeListener =
new CloseConsolidatingFutureListener(closeFuture, 2);
sendChannel.close().addListener(closeListener);
pollChannel.close().addListener(closeListener);
}
private ChannelPipeline createSendPipeline() {
ChannelPipeline pipeline = Channels.pipeline();
pipeline.addLast("reqencoder", new HttpRequestEncoder()); // downstream
pipeline.addLast("respdecoder", new HttpResponseDecoder()); // upstream
pipeline.addLast("aggregator", new HttpChunkAggregator(
HttpTunnelMessageUtils.MAX_BODY_SIZE)); // upstream
pipeline.addLast("sendHandler", new HttpTunnelClientSendHandler(
callbackProxy)); // both
pipeline.addLast("writeFragmenter", new WriteFragmenter(
HttpTunnelMessageUtils.MAX_BODY_SIZE));
return pipeline;
}
private ChannelPipeline createPollPipeline() {
ChannelPipeline pipeline = Channels.pipeline();
pipeline.addLast("reqencoder", new HttpRequestEncoder()); // downstream
pipeline.addLast("respdecoder", new HttpResponseDecoder()); // upstream
pipeline.addLast("aggregator", new HttpChunkAggregator(
HttpTunnelMessageUtils.MAX_BODY_SIZE)); // upstream
pipeline.addLast(HttpTunnelClientPollHandler.NAME,
new HttpTunnelClientPollHandler(callbackProxy)); // both
return pipeline;
}
private void setTunnelIdForPollChannel() {
HttpTunnelClientPollHandler pollHandler =
pollChannel.getPipeline()
.get(HttpTunnelClientPollHandler.class);
pollHandler.setTunnelId(tunnelId);
}
void sendData(final MessageEvent e) {
saturationManager.updateThresholds(config.getWriteBufferLowWaterMark(),
config.getWriteBufferHighWaterMark());
final ChannelFuture originalFuture = e.getFuture();
final ChannelBuffer message = (ChannelBuffer) e.getMessage();
final int messageSize = message.readableBytes();
updateSaturationStatus(messageSize);
Channels.write(sendChannel, e.getMessage()).addListener(
new ChannelFutureListener() {
public void operationComplete(ChannelFuture future)
throws Exception {
if (future.isSuccess()) {
originalFuture.setSuccess();
} else {
originalFuture.setFailure(future.getCause());
}
updateSaturationStatus(-messageSize);
}
});
}
private void updateSaturationStatus(int queueSizeDelta) {
SaturationStateChange transition =
saturationManager.queueSizeChanged(queueSizeDelta);
switch (transition) {
case SATURATED:
fireWriteEnabled(false);
break;
case DESATURATED:
fireWriteEnabled(true);
break;
case NO_CHANGE:
break;
}
}
private void fireWriteEnabled(boolean enabled) {
int ops = OP_READ;
if (!enabled) {
ops |= OP_WRITE;
}
setInterestOpsNow(ops);
Channels.fireChannelInterestChanged(this);
}
private class ConsolidatingFutureListener implements ChannelFutureListener {
private final ChannelFuture completionFuture;
private final AtomicInteger eventsLeft;
public ConsolidatingFutureListener(ChannelFuture completionFuture,
int numToConsolidate) {
this.completionFuture = completionFuture;
eventsLeft = new AtomicInteger(numToConsolidate);
}
public void operationComplete(ChannelFuture future) throws Exception {
if (!future.isSuccess()) {
futureFailed(future);
} else {
if (eventsLeft.decrementAndGet() == 0) {
allFuturesComplete();
}
}
updateSaturationStatus(-messageSize);
}
});
}
private void updateSaturationStatus(int queueSizeDelta) {
SaturationStateChange transition = saturationManager.queueSizeChanged(queueSizeDelta);
switch(transition) {
case SATURATED: fireWriteEnabled(false); break;
case DESATURATED: fireWriteEnabled(true); break;
case NO_CHANGE: break;
}
}
private void fireWriteEnabled(boolean enabled) {
int ops = OP_READ;
if(!enabled) {
ops |= OP_WRITE;
}
setInterestOpsNow(ops);
Channels.fireChannelInterestChanged(this);
}
}
private class ConsolidatingFutureListener implements ChannelFutureListener
{
protected void allFuturesComplete() {
completionFuture.setSuccess();
}
private final ChannelFuture completionFuture;
protected void futureFailed(ChannelFuture future) {
completionFuture.setFailure(future.getCause());
}
}
private final AtomicInteger eventsLeft;
/**
* Close futures are a special case, as marking them as successful or failed has no effect.
* Instead, we must call setClosed() on the channel itself, once all the child channels are
* closed or if we fail to close them for whatever reason.
*/
private final class CloseConsolidatingFutureListener extends
ConsolidatingFutureListener {
public ConsolidatingFutureListener(ChannelFuture completionFuture, int numToConsolidate)
{
this.completionFuture = completionFuture;
eventsLeft = new AtomicInteger(numToConsolidate);
}
public CloseConsolidatingFutureListener(ChannelFuture completionFuture,
int numToConsolidate) {
super(completionFuture, numToConsolidate);
}
public void operationComplete(ChannelFuture future) throws Exception
{
if (!future.isSuccess())
{
futureFailed(future);
}
else
{
if (eventsLeft.decrementAndGet() == 0)
{
allFuturesComplete();
@Override
protected void futureFailed(ChannelFuture future) {
LOG.warn("Failed to close one of the child channels of tunnel " +
tunnelId);
HttpTunnelClientChannel.this.setClosed();
}
@Override
protected void allFuturesComplete() {
if (LOG.isDebugEnabled()) {
LOG.debug("Tunnel " + tunnelId + " closed");
}
}
}
HttpTunnelClientChannel.this.setClosed();
}
protected void allFuturesComplete()
{
completionFuture.setSuccess();
}
}
protected void futureFailed(ChannelFuture future)
{
completionFuture.setFailure(future.getCause());
}
}
/**
* Contains the implementing methods of HttpTunnelClientWorkerOwner, so that these are hidden
* from the public API.
*/
private class WorkerCallbacks implements HttpTunnelClientWorkerOwner {
/**
* Close futures are a special case, as marking them as successful or failed has no effect.
* Instead, we must call setClosed() on the channel itself, once all the child channels are
* closed or if we fail to close them for whatever reason.
*/
private final class CloseConsolidatingFutureListener extends ConsolidatingFutureListener
{
public void onConnectRequest(ChannelFuture connectFuture,
InetSocketAddress remoteAddress) {
HttpTunnelClientChannel.this.onConnectRequest(connectFuture,
remoteAddress);
}
public CloseConsolidatingFutureListener(ChannelFuture completionFuture, int numToConsolidate)
{
super(completionFuture, numToConsolidate);
}
public void onTunnelOpened(String tunnelId) {
HttpTunnelClientChannel.this.tunnelId = tunnelId;
setTunnelIdForPollChannel();
Channels.connect(pollChannel, sendChannel.getRemoteAddress());
}
@Override
protected void futureFailed(ChannelFuture future)
{
LOG.warn("Failed to close one of the child channels of tunnel " + tunnelId);
HttpTunnelClientChannel.this.setClosed();
}
public void fullyEstablished() {
if (!bound) {
bound = true;
Channels.fireChannelBound(HttpTunnelClientChannel.this,
getLocalAddress());
}
@Override
protected void allFuturesComplete()
{
if (LOG.isDebugEnabled())
{
LOG.debug("Tunnel " + tunnelId + " closed");
}
HttpTunnelClientChannel.this.setClosed();
}
connected = true;
connectFuture.setSuccess();
Channels.fireChannelConnected(HttpTunnelClientChannel.this,
getRemoteAddress());
}
}
public void onMessageReceived(ChannelBuffer content) {
Channels.fireMessageReceived(HttpTunnelClientChannel.this, content);
}
/**
* Contains the implementing methods of HttpTunnelClientWorkerOwner, so that these are hidden
* from the public API.
*/
private class WorkerCallbacks implements HttpTunnelClientWorkerOwner
{
public String getServerHostName() {
if (serverHostName == null) {
serverHostName =
HttpTunnelMessageUtils
.convertToHostString(serverAddress);
}
public void onConnectRequest(ChannelFuture connectFuture, InetSocketAddress remoteAddress)
{
HttpTunnelClientChannel.this.onConnectRequest(connectFuture, remoteAddress);
}
return serverHostName;
}
public void onTunnelOpened(String tunnelId)
{
HttpTunnelClientChannel.this.tunnelId = tunnelId;
setTunnelIdForPollChannel();
Channels.connect(pollChannel, sendChannel.getRemoteAddress());
}
public void fullyEstablished()
{
if (!bound)
{
bound = true;
Channels.fireChannelBound(HttpTunnelClientChannel.this, getLocalAddress());
}
connected = true;
connectFuture.setSuccess();
Channels.fireChannelConnected(HttpTunnelClientChannel.this, getRemoteAddress());
}
public void onMessageReceived(ChannelBuffer content)
{
Channels.fireMessageReceived(HttpTunnelClientChannel.this, content);
}
public String getServerHostName()
{
if (serverHostName == null)
{
serverHostName = HttpTunnelMessageUtils.convertToHostString(serverAddress);
}
return serverHostName;
}
}
}
}

View File

@ -39,142 +39,123 @@ import org.jboss.netty.channel.socket.SocketChannelConfig;
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
* @author OneDrum Ltd.
*/
public class HttpTunnelClientChannelConfig extends HttpTunnelChannelConfig
{
public class HttpTunnelClientChannelConfig extends HttpTunnelChannelConfig {
static final String PROXY_ADDRESS_OPTION = "proxyAddress";
static final String PROXY_ADDRESS_OPTION = "proxyAddress";
private final SocketChannelConfig sendChannelConfig;
private final SocketChannelConfig sendChannelConfig;
private final SocketChannelConfig pollChannelConfig;
private final SocketChannelConfig pollChannelConfig;
private volatile SocketAddress proxyAddress;
private volatile SocketAddress proxyAddress;
HttpTunnelClientChannelConfig(SocketChannelConfig sendChannelConfig, SocketChannelConfig pollChannelConfig)
{
this.sendChannelConfig = sendChannelConfig;
this.pollChannelConfig = pollChannelConfig;
}
HttpTunnelClientChannelConfig(SocketChannelConfig sendChannelConfig,
SocketChannelConfig pollChannelConfig) {
this.sendChannelConfig = sendChannelConfig;
this.pollChannelConfig = pollChannelConfig;
}
/* HTTP TUNNEL SPECIFIC CONFIGURATION */
// TODO Support all options in the old tunnel (see HttpTunnelingSocketChannelConfig)
// Mostly SSL, virtual host, and URL prefix
@Override
public boolean setOption(String key, Object value)
{
if (PROXY_ADDRESS_OPTION.equals(key))
{
setProxyAddress((SocketAddress) value);
}
else
{
return super.setOption(key, value);
}
/* HTTP TUNNEL SPECIFIC CONFIGURATION */
// TODO Support all options in the old tunnel (see HttpTunnelingSocketChannelConfig)
// Mostly SSL, virtual host, and URL prefix
@Override
public boolean setOption(String key, Object value) {
if (PROXY_ADDRESS_OPTION.equals(key)) {
setProxyAddress((SocketAddress) value);
} else {
return super.setOption(key, value);
}
return true;
}
return true;
}
/**
* @return the address of the http proxy. If this is null, then no proxy
* should be used.
*/
public SocketAddress getProxyAddress()
{
return proxyAddress;
}
/**
* @return the address of the http proxy. If this is null, then no proxy
* should be used.
*/
public SocketAddress getProxyAddress() {
return proxyAddress;
}
/**
* Specify a proxy to be used for the http tunnel. If this is null, then
* no proxy should be used, otherwise this should be a directly accessible IPv4/IPv6
* address and port.
*/
public void setProxyAddress(SocketAddress proxyAddress)
{
this.proxyAddress = proxyAddress;
}
/**
* Specify a proxy to be used for the http tunnel. If this is null, then
* no proxy should be used, otherwise this should be a directly accessible IPv4/IPv6
* address and port.
*/
public void setProxyAddress(SocketAddress proxyAddress) {
this.proxyAddress = proxyAddress;
}
/* GENERIC SOCKET CHANNEL CONFIGURATION */
/* GENERIC SOCKET CHANNEL CONFIGURATION */
public int getReceiveBufferSize()
{
return pollChannelConfig.getReceiveBufferSize();
}
public int getReceiveBufferSize() {
return pollChannelConfig.getReceiveBufferSize();
}
public int getSendBufferSize()
{
return pollChannelConfig.getSendBufferSize();
}
public int getSendBufferSize() {
return pollChannelConfig.getSendBufferSize();
}
public int getSoLinger()
{
return pollChannelConfig.getSoLinger();
}
public int getSoLinger() {
return pollChannelConfig.getSoLinger();
}
public int getTrafficClass()
{
return pollChannelConfig.getTrafficClass();
}
public int getTrafficClass() {
return pollChannelConfig.getTrafficClass();
}
public boolean isKeepAlive()
{
return pollChannelConfig.isKeepAlive();
}
public boolean isKeepAlive() {
return pollChannelConfig.isKeepAlive();
}
public boolean isReuseAddress()
{
return pollChannelConfig.isReuseAddress();
}
public boolean isReuseAddress() {
return pollChannelConfig.isReuseAddress();
}
public boolean isTcpNoDelay()
{
return pollChannelConfig.isTcpNoDelay();
}
public boolean isTcpNoDelay() {
return pollChannelConfig.isTcpNoDelay();
}
public void setKeepAlive(boolean keepAlive)
{
pollChannelConfig.setKeepAlive(keepAlive);
sendChannelConfig.setKeepAlive(keepAlive);
}
public void setKeepAlive(boolean keepAlive) {
pollChannelConfig.setKeepAlive(keepAlive);
sendChannelConfig.setKeepAlive(keepAlive);
}
public void setPerformancePreferences(int connectionTime, int latency, int bandwidth)
{
pollChannelConfig.setPerformancePreferences(connectionTime, latency, bandwidth);
sendChannelConfig.setPerformancePreferences(connectionTime, latency, bandwidth);
}
public void setPerformancePreferences(int connectionTime, int latency,
int bandwidth) {
pollChannelConfig.setPerformancePreferences(connectionTime, latency,
bandwidth);
sendChannelConfig.setPerformancePreferences(connectionTime, latency,
bandwidth);
}
public void setReceiveBufferSize(int receiveBufferSize)
{
pollChannelConfig.setReceiveBufferSize(receiveBufferSize);
sendChannelConfig.setReceiveBufferSize(receiveBufferSize);
}
public void setReceiveBufferSize(int receiveBufferSize) {
pollChannelConfig.setReceiveBufferSize(receiveBufferSize);
sendChannelConfig.setReceiveBufferSize(receiveBufferSize);
}
public void setReuseAddress(boolean reuseAddress)
{
pollChannelConfig.setReuseAddress(reuseAddress);
sendChannelConfig.setReuseAddress(reuseAddress);
}
public void setReuseAddress(boolean reuseAddress) {
pollChannelConfig.setReuseAddress(reuseAddress);
sendChannelConfig.setReuseAddress(reuseAddress);
}
public void setSendBufferSize(int sendBufferSize)
{
pollChannelConfig.setSendBufferSize(sendBufferSize);
sendChannelConfig.setSendBufferSize(sendBufferSize);
}
public void setSendBufferSize(int sendBufferSize) {
pollChannelConfig.setSendBufferSize(sendBufferSize);
sendChannelConfig.setSendBufferSize(sendBufferSize);
}
public void setSoLinger(int soLinger)
{
pollChannelConfig.setSoLinger(soLinger);
sendChannelConfig.setSoLinger(soLinger);
}
public void setSoLinger(int soLinger) {
pollChannelConfig.setSoLinger(soLinger);
sendChannelConfig.setSoLinger(soLinger);
}
public void setTcpNoDelay(boolean tcpNoDelay)
{
pollChannelConfig.setTcpNoDelay(true);
sendChannelConfig.setTcpNoDelay(true);
}
public void setTcpNoDelay(boolean tcpNoDelay) {
pollChannelConfig.setTcpNoDelay(true);
sendChannelConfig.setTcpNoDelay(true);
}
public void setTrafficClass(int trafficClass)
{
pollChannelConfig.setTrafficClass(1);
sendChannelConfig.setTrafficClass(1);
}
public void setTrafficClass(int trafficClass) {
pollChannelConfig.setTrafficClass(1);
sendChannelConfig.setTrafficClass(1);
}
}

View File

@ -27,31 +27,28 @@ import org.jboss.netty.channel.socket.ClientSocketChannelFactory;
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
* @author OneDrum Ltd.
*/
public class HttpTunnelClientChannelFactory implements ClientSocketChannelFactory
{
public class HttpTunnelClientChannelFactory implements
ClientSocketChannelFactory {
private final ClientSocketChannelFactory factory;
private final ClientSocketChannelFactory factory;
private final ChannelGroup realConnections = new DefaultChannelGroup();
private final ChannelGroup realConnections = new DefaultChannelGroup();
public HttpTunnelClientChannelFactory(ClientSocketChannelFactory factory)
{
if (factory == null)
{
throw new NullPointerException("factory");
}
this.factory = factory;
}
public HttpTunnelClientChannelFactory(ClientSocketChannelFactory factory) {
if (factory == null) {
throw new NullPointerException("factory");
}
this.factory = factory;
}
public HttpTunnelClientChannel newChannel(ChannelPipeline pipeline)
{
return new HttpTunnelClientChannel(this, pipeline, new HttpTunnelClientChannelSink(), factory, realConnections);
}
public HttpTunnelClientChannel newChannel(ChannelPipeline pipeline) {
return new HttpTunnelClientChannel(this, pipeline,
new HttpTunnelClientChannelSink(), factory, realConnections);
}
public void releaseExternalResources()
{
realConnections.close().awaitUninterruptibly();
factory.releaseExternalResources();
}
public void releaseExternalResources() {
realConnections.close().awaitUninterruptibly();
factory.releaseExternalResources();
}
}

View File

@ -31,59 +31,49 @@ import org.jboss.netty.channel.MessageEvent;
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
* @author OneDrum Ltd.
*/
class HttpTunnelClientChannelSink extends AbstractChannelSink
{
class HttpTunnelClientChannelSink extends AbstractChannelSink {
public void eventSunk(ChannelPipeline pipeline, ChannelEvent e) throws Exception
{
if (e instanceof ChannelStateEvent)
{
handleChannelStateEvent((ChannelStateEvent) e);
}
else if (e instanceof MessageEvent)
{
handleMessageEvent((MessageEvent) e);
}
}
public void eventSunk(ChannelPipeline pipeline, ChannelEvent e)
throws Exception {
if (e instanceof ChannelStateEvent) {
handleChannelStateEvent((ChannelStateEvent) e);
} else if (e instanceof MessageEvent) {
handleMessageEvent((MessageEvent) e);
}
}
private void handleMessageEvent(MessageEvent e)
{
HttpTunnelClientChannel channel = (HttpTunnelClientChannel) e.getChannel();
channel.sendData(e);
}
private void handleMessageEvent(MessageEvent e) {
HttpTunnelClientChannel channel =
(HttpTunnelClientChannel) e.getChannel();
channel.sendData(e);
}
private void handleChannelStateEvent(ChannelStateEvent e)
{
HttpTunnelClientChannel channel = (HttpTunnelClientChannel) e.getChannel();
private void handleChannelStateEvent(ChannelStateEvent e) {
HttpTunnelClientChannel channel =
(HttpTunnelClientChannel) e.getChannel();
switch (e.getState())
{
case CONNECTED :
if (e.getValue() != null)
{
channel.onConnectRequest(e.getFuture(), (InetSocketAddress) e.getValue());
}
else
{
channel.onDisconnectRequest(e.getFuture());
switch (e.getState()) {
case CONNECTED:
if (e.getValue() != null) {
channel.onConnectRequest(e.getFuture(),
(InetSocketAddress) e.getValue());
} else {
channel.onDisconnectRequest(e.getFuture());
}
break;
case BOUND :
if (e.getValue() != null)
{
channel.onBindRequest((InetSocketAddress) e.getValue(), e.getFuture());
}
else
{
channel.onUnbindRequest(e.getFuture());
case BOUND:
if (e.getValue() != null) {
channel.onBindRequest((InetSocketAddress) e.getValue(),
e.getFuture());
} else {
channel.onUnbindRequest(e.getFuture());
}
break;
case OPEN :
if (Boolean.FALSE.equals(e.getValue()))
{
channel.onCloseRequest(e.getFuture());
case OPEN:
if (Boolean.FALSE.equals(e.getValue())) {
channel.onCloseRequest(e.getFuture());
}
break;
}
}
}
}
}

View File

@ -32,73 +32,66 @@ import org.jboss.netty.logging.InternalLoggerFactory;
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
* @author OneDrum Ltd.
*/
class HttpTunnelClientPollHandler extends SimpleChannelHandler
{
class HttpTunnelClientPollHandler extends SimpleChannelHandler {
public static final String NAME = "server2client";
public static final String NAME = "server2client";
private static final InternalLogger LOG = InternalLoggerFactory.getInstance(HttpTunnelClientPollHandler.class);
private static final InternalLogger LOG = InternalLoggerFactory
.getInstance(HttpTunnelClientPollHandler.class);
private String tunnelId;
private String tunnelId;
private final HttpTunnelClientWorkerOwner tunnelChannel;
private final HttpTunnelClientWorkerOwner tunnelChannel;
private long pollTime;
private long pollTime;
public HttpTunnelClientPollHandler(HttpTunnelClientWorkerOwner tunnelChannel)
{
this.tunnelChannel = tunnelChannel;
}
public HttpTunnelClientPollHandler(HttpTunnelClientWorkerOwner tunnelChannel) {
this.tunnelChannel = tunnelChannel;
}
public void setTunnelId(String tunnelId)
{
this.tunnelId = tunnelId;
}
public void setTunnelId(String tunnelId) {
this.tunnelId = tunnelId;
}
@Override
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception
{
if (LOG.isDebugEnabled())
{
LOG.debug("Poll channel for tunnel " + tunnelId + " established");
}
tunnelChannel.fullyEstablished();
sendPoll(ctx);
}
@Override
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e)
throws Exception {
if (LOG.isDebugEnabled()) {
LOG.debug("Poll channel for tunnel " + tunnelId + " established");
}
tunnelChannel.fullyEstablished();
sendPoll(ctx);
}
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception
{
HttpResponse response = (HttpResponse) e.getMessage();
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
throws Exception {
HttpResponse response = (HttpResponse) e.getMessage();
if (HttpTunnelMessageUtils.isOKResponse(response))
{
long rtTime = System.nanoTime() - pollTime;
if (LOG.isDebugEnabled())
{
LOG.debug("OK response received for poll on tunnel " + tunnelId + " after " + rtTime + " ns");
}
tunnelChannel.onMessageReceived(response.getContent());
sendPoll(ctx);
}
else
{
if (LOG.isWarnEnabled())
{
LOG.warn("non-OK response received for poll on tunnel " + tunnelId);
}
}
}
if (HttpTunnelMessageUtils.isOKResponse(response)) {
long rtTime = System.nanoTime() - pollTime;
if (LOG.isDebugEnabled()) {
LOG.debug("OK response received for poll on tunnel " +
tunnelId + " after " + rtTime + " ns");
}
tunnelChannel.onMessageReceived(response.getContent());
sendPoll(ctx);
} else {
if (LOG.isWarnEnabled()) {
LOG.warn("non-OK response received for poll on tunnel " +
tunnelId);
}
}
}
private void sendPoll(ChannelHandlerContext ctx)
{
pollTime = System.nanoTime();
if (LOG.isDebugEnabled())
{
LOG.debug("sending poll request for tunnel " + tunnelId);
}
HttpRequest request = HttpTunnelMessageUtils
.createReceiveDataRequest(tunnelChannel.getServerHostName(), tunnelId);
Channels.write(ctx, Channels.future(ctx.getChannel()), request);
}
private void sendPoll(ChannelHandlerContext ctx) {
pollTime = System.nanoTime();
if (LOG.isDebugEnabled()) {
LOG.debug("sending poll request for tunnel " + tunnelId);
}
HttpRequest request =
HttpTunnelMessageUtils.createReceiveDataRequest(
tunnelChannel.getServerHostName(), tunnelId);
Channels.write(ctx, Channels.future(ctx.getChannel()), request);
}
}

View File

@ -39,229 +39,207 @@ import org.jboss.netty.logging.InternalLoggerFactory;
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
* @author OneDrum Ltd.
*/
class HttpTunnelClientSendHandler extends SimpleChannelHandler
{
class HttpTunnelClientSendHandler extends SimpleChannelHandler {
public static final String NAME = "client2server";
public static final String NAME = "client2server";
private static final InternalLogger LOG = InternalLoggerFactory.getInstance(HttpTunnelClientSendHandler.class);
private static final InternalLogger LOG = InternalLoggerFactory
.getInstance(HttpTunnelClientSendHandler.class);
private final HttpTunnelClientWorkerOwner tunnelChannel;
private final HttpTunnelClientWorkerOwner tunnelChannel;
private String tunnelId = null;
private String tunnelId = null;
private final AtomicBoolean disconnecting;
private final AtomicBoolean disconnecting;
private ChannelStateEvent postShutdownEvent;
private ChannelStateEvent postShutdownEvent;
private final ConcurrentLinkedQueue<MessageEvent> queuedWrites;
private final ConcurrentLinkedQueue<MessageEvent> queuedWrites;
private final AtomicInteger pendingRequestCount;
private final AtomicInteger pendingRequestCount;
private long sendRequestTime;
private long sendRequestTime;
public HttpTunnelClientSendHandler(HttpTunnelClientWorkerOwner tunnelChannel)
{
this.tunnelChannel = tunnelChannel;
queuedWrites = new ConcurrentLinkedQueue<MessageEvent>();
pendingRequestCount = new AtomicInteger(0);
disconnecting = new AtomicBoolean(false);
}
public HttpTunnelClientSendHandler(HttpTunnelClientWorkerOwner tunnelChannel) {
this.tunnelChannel = tunnelChannel;
queuedWrites = new ConcurrentLinkedQueue<MessageEvent>();
pendingRequestCount = new AtomicInteger(0);
disconnecting = new AtomicBoolean(false);
}
@Override
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception
{
if (tunnelId == null)
{
if (LOG.isDebugEnabled())
{
LOG.debug("connection to " + e.getValue() + " succeeded - sending open tunnel request");
}
HttpRequest request = HttpTunnelMessageUtils.createOpenTunnelRequest(tunnelChannel.getServerHostName());
Channel thisChannel = ctx.getChannel();
DownstreamMessageEvent event = new DownstreamMessageEvent(thisChannel, Channels.future(thisChannel), request,
thisChannel.getRemoteAddress());
queuedWrites.offer(event);
pendingRequestCount.incrementAndGet();
sendQueuedData(ctx);
}
}
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception
{
HttpResponse response = (HttpResponse) e.getMessage();
if (HttpTunnelMessageUtils.isOKResponse(response))
{
long roundTripTime = System.nanoTime() - sendRequestTime;
if (LOG.isDebugEnabled())
{
LOG.debug("OK response received for tunnel " + tunnelId + ", after " + roundTripTime + " ns");
}
sendNextAfterResponse(ctx);
}
else if (HttpTunnelMessageUtils.isTunnelOpenResponse(response))
{
tunnelId = HttpTunnelMessageUtils.extractCookie(response);
if (LOG.isDebugEnabled())
{
LOG.debug("tunnel open request accepted - id " + tunnelId);
}
tunnelChannel.onTunnelOpened(tunnelId);
sendNextAfterResponse(ctx);
}
else if (HttpTunnelMessageUtils.isTunnelCloseResponse(response))
{
if (LOG.isDebugEnabled())
{
if (disconnecting.get())
{
LOG.debug("server acknowledged disconnect for tunnel " + tunnelId);
@Override
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e)
throws Exception {
if (tunnelId == null) {
if (LOG.isDebugEnabled()) {
LOG.debug("connection to " + e.getValue() +
" succeeded - sending open tunnel request");
}
else
{
LOG.debug("server closed tunnel " + tunnelId);
HttpRequest request =
HttpTunnelMessageUtils
.createOpenTunnelRequest(tunnelChannel
.getServerHostName());
Channel thisChannel = ctx.getChannel();
DownstreamMessageEvent event =
new DownstreamMessageEvent(thisChannel,
Channels.future(thisChannel), request,
thisChannel.getRemoteAddress());
queuedWrites.offer(event);
pendingRequestCount.incrementAndGet();
sendQueuedData(ctx);
}
}
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
throws Exception {
HttpResponse response = (HttpResponse) e.getMessage();
if (HttpTunnelMessageUtils.isOKResponse(response)) {
long roundTripTime = System.nanoTime() - sendRequestTime;
if (LOG.isDebugEnabled()) {
LOG.debug("OK response received for tunnel " + tunnelId +
", after " + roundTripTime + " ns");
}
}
ctx.sendDownstream(postShutdownEvent);
}
else
{
// TODO: kill connection
if (LOG.isWarnEnabled())
{
LOG.warn("unknown response received for tunnel " + tunnelId + ", closing connection");
}
Channels.close(ctx, ctx.getChannel().getCloseFuture());
}
}
sendNextAfterResponse(ctx);
} else if (HttpTunnelMessageUtils.isTunnelOpenResponse(response)) {
tunnelId = HttpTunnelMessageUtils.extractCookie(response);
if (LOG.isDebugEnabled()) {
LOG.debug("tunnel open request accepted - id " + tunnelId);
}
tunnelChannel.onTunnelOpened(tunnelId);
sendNextAfterResponse(ctx);
} else if (HttpTunnelMessageUtils.isTunnelCloseResponse(response)) {
if (LOG.isDebugEnabled()) {
if (disconnecting.get()) {
LOG.debug("server acknowledged disconnect for tunnel " +
tunnelId);
} else {
LOG.debug("server closed tunnel " + tunnelId);
}
}
ctx.sendDownstream(postShutdownEvent);
} else {
// TODO: kill connection
if (LOG.isWarnEnabled()) {
LOG.warn("unknown response received for tunnel " + tunnelId +
", closing connection");
}
Channels.close(ctx, ctx.getChannel().getCloseFuture());
}
}
private void sendNextAfterResponse(ChannelHandlerContext ctx)
{
if (pendingRequestCount.decrementAndGet() > 0)
{
if (LOG.isDebugEnabled())
{
LOG.debug("Immediately sending next send request for tunnel " + tunnelId);
}
sendQueuedData(ctx);
}
}
private void sendNextAfterResponse(ChannelHandlerContext ctx) {
if (pendingRequestCount.decrementAndGet() > 0) {
if (LOG.isDebugEnabled()) {
LOG.debug("Immediately sending next send request for tunnel " +
tunnelId);
}
sendQueuedData(ctx);
}
}
private synchronized void sendQueuedData(ChannelHandlerContext ctx)
{
if (disconnecting.get())
{
if (LOG.isDebugEnabled())
{
LOG.debug("sending close request for tunnel " + tunnelId);
}
HttpRequest closeRequest = HttpTunnelMessageUtils.createCloseTunnelRequest(tunnelChannel.getServerHostName(),
tunnelId);
Channels.write(ctx, Channels.future(ctx.getChannel()), closeRequest);
}
else
{
if (LOG.isDebugEnabled())
{
LOG.debug("sending next request for tunnel " + tunnelId);
}
MessageEvent nextWrite = queuedWrites.poll();
sendRequestTime = System.nanoTime();
ctx.sendDownstream(nextWrite);
}
}
private synchronized void sendQueuedData(ChannelHandlerContext ctx) {
if (disconnecting.get()) {
if (LOG.isDebugEnabled()) {
LOG.debug("sending close request for tunnel " + tunnelId);
}
HttpRequest closeRequest =
HttpTunnelMessageUtils.createCloseTunnelRequest(
tunnelChannel.getServerHostName(), tunnelId);
Channels.write(ctx, Channels.future(ctx.getChannel()), closeRequest);
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("sending next request for tunnel " + tunnelId);
}
MessageEvent nextWrite = queuedWrites.poll();
sendRequestTime = System.nanoTime();
ctx.sendDownstream(nextWrite);
}
}
@Override
public void writeRequested(ChannelHandlerContext ctx, MessageEvent e) throws Exception
{
if (LOG.isDebugEnabled())
{
LOG.debug("request to send data for tunnel " + tunnelId);
}
if (disconnecting.get())
{
if (LOG.isWarnEnabled())
{
LOG.warn("rejecting write request for tunnel " + tunnelId + " received after disconnect requested");
}
e.getFuture().setFailure(new IllegalStateException("tunnel is closing"));
return;
}
ChannelBuffer data = (ChannelBuffer) e.getMessage();
HttpRequest request = HttpTunnelMessageUtils.createSendDataRequest(tunnelChannel.getServerHostName(), tunnelId,
data);
DownstreamMessageEvent translatedEvent = new DownstreamMessageEvent(ctx.getChannel(), e.getFuture(), request, ctx
.getChannel().getRemoteAddress());
queuedWrites.offer(translatedEvent);
if (pendingRequestCount.incrementAndGet() == 1)
{
sendQueuedData(ctx);
}
else
{
if (LOG.isDebugEnabled())
{
LOG.debug("write request for tunnel " + tunnelId + " queued");
}
}
}
@Override
public void writeRequested(ChannelHandlerContext ctx, MessageEvent e)
throws Exception {
if (LOG.isDebugEnabled()) {
LOG.debug("request to send data for tunnel " + tunnelId);
}
if (disconnecting.get()) {
if (LOG.isWarnEnabled()) {
LOG.warn("rejecting write request for tunnel " + tunnelId +
" received after disconnect requested");
}
e.getFuture().setFailure(
new IllegalStateException("tunnel is closing"));
return;
}
ChannelBuffer data = (ChannelBuffer) e.getMessage();
HttpRequest request =
HttpTunnelMessageUtils.createSendDataRequest(
tunnelChannel.getServerHostName(), tunnelId, data);
DownstreamMessageEvent translatedEvent =
new DownstreamMessageEvent(ctx.getChannel(), e.getFuture(),
request, ctx.getChannel().getRemoteAddress());
queuedWrites.offer(translatedEvent);
if (pendingRequestCount.incrementAndGet() == 1) {
sendQueuedData(ctx);
} else {
if (LOG.isDebugEnabled()) {
LOG.debug("write request for tunnel " + tunnelId + " queued");
}
}
}
@Override
public void closeRequested(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception
{
shutdownTunnel(ctx, e);
}
@Override
public void closeRequested(ChannelHandlerContext ctx, ChannelStateEvent e)
throws Exception {
shutdownTunnel(ctx, e);
}
@Override
public void disconnectRequested(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception
{
shutdownTunnel(ctx, e);
}
@Override
public void disconnectRequested(ChannelHandlerContext ctx,
ChannelStateEvent e) throws Exception {
shutdownTunnel(ctx, e);
}
@Override
public void unbindRequested(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception
{
shutdownTunnel(ctx, e);
}
@Override
public void unbindRequested(ChannelHandlerContext ctx, ChannelStateEvent e)
throws Exception {
shutdownTunnel(ctx, e);
}
private void shutdownTunnel(ChannelHandlerContext ctx, ChannelStateEvent postShutdownEvent)
{
if (LOG.isDebugEnabled())
{
LOG.debug("tunnel shutdown requested for send channel of tunnel " + tunnelId);
}
if (!ctx.getChannel().isConnected())
{
if (LOG.isDebugEnabled())
{
LOG.debug("send channel of tunnel " + tunnelId + " is already disconnected");
}
ctx.sendDownstream(postShutdownEvent);
return;
}
private void shutdownTunnel(ChannelHandlerContext ctx,
ChannelStateEvent postShutdownEvent) {
if (LOG.isDebugEnabled()) {
LOG.debug("tunnel shutdown requested for send channel of tunnel " +
tunnelId);
}
if (!ctx.getChannel().isConnected()) {
if (LOG.isDebugEnabled()) {
LOG.debug("send channel of tunnel " + tunnelId +
" is already disconnected");
}
ctx.sendDownstream(postShutdownEvent);
return;
}
if (!disconnecting.compareAndSet(false, true))
{
if (LOG.isWarnEnabled())
{
LOG.warn("tunnel shutdown process already initiated for tunnel " + tunnelId);
}
return;
}
if (!disconnecting.compareAndSet(false, true)) {
if (LOG.isWarnEnabled()) {
LOG.warn("tunnel shutdown process already initiated for tunnel " +
tunnelId);
}
return;
}
this.postShutdownEvent = postShutdownEvent;
this.postShutdownEvent = postShutdownEvent;
// if the channel is idle, send a close request immediately
if (pendingRequestCount.incrementAndGet() == 1)
{
sendQueuedData(ctx);
}
}
// if the channel is idle, send a close request immediately
if (pendingRequestCount.incrementAndGet() == 1) {
sendQueuedData(ctx);
}
}
public String getTunnelId()
{
return tunnelId;
}
public String getTunnelId() {
return tunnelId;
}
}

View File

@ -30,39 +30,39 @@ import org.jboss.netty.channel.ChannelFuture;
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
* @author OneDrum Ltd.
*/
interface HttpTunnelClientWorkerOwner
{
/**
* The HTTP tunnel client sink invokes this when the application code requests the connection
* of an HTTP tunnel to the specified remote address.
*/
public void onConnectRequest(ChannelFuture connectFuture, InetSocketAddress remoteAddress);
interface HttpTunnelClientWorkerOwner {
/**
* The HTTP tunnel client sink invokes this when the application code requests the connection
* of an HTTP tunnel to the specified remote address.
*/
public void onConnectRequest(ChannelFuture connectFuture,
InetSocketAddress remoteAddress);
/**
* The send channel handler calls this method when the server accepts the open tunnel request,
* returning a unique tunnel ID.
*
* @param tunnelId the server allocated tunnel ID
*/
public void onTunnelOpened(String tunnelId);
/**
* The send channel handler calls this method when the server accepts the open tunnel request,
* returning a unique tunnel ID.
*
* @param tunnelId the server allocated tunnel ID
*/
public void onTunnelOpened(String tunnelId);
/**
* The poll channel handler calls this method when the poll channel is connected, indicating
* that full duplex communications are now possible.
*/
public void fullyEstablished();
/**
* The poll channel handler calls this method when the poll channel is connected, indicating
* that full duplex communications are now possible.
*/
public void fullyEstablished();
/**
* The poll handler calls this method when some data is received and decoded from the server.
* @param content the data received from the server
*/
public void onMessageReceived(ChannelBuffer content);
/**
* The poll handler calls this method when some data is received and decoded from the server.
* @param content the data received from the server
*/
public void onMessageReceived(ChannelBuffer content);
/**
* @return the name of the server with whom we are communicating with - this is used within
* the HOST HTTP header for all requests. This is particularly important for operation behind
* a proxy, where the HOST string is used to route the request.
*/
public String getServerHostName();
/**
* @return the name of the server with whom we are communicating with - this is used within
* the HOST HTTP header for all requests. This is particularly important for operation behind
* a proxy, where the HOST string is used to route the request.
*/
public String getServerHostName();
}

View File

@ -44,320 +44,299 @@ import org.jboss.netty.handler.codec.http.HttpVersion;
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
* @author OneDrum Ltd.
*/
public class HttpTunnelMessageUtils
{
public class HttpTunnelMessageUtils {
private static final String HTTP_URL_PREFIX = "http://";
private static final String HTTP_URL_PREFIX = "http://";
/**
* An upper bound is enforced on the size of message bodies, so as
* to ensure we do not dump large chunks of data on either peer.
*/
public static final int MAX_BODY_SIZE = 16 * 1024;
/**
* An upper bound is enforced on the size of message bodies, so as
* to ensure we do not dump large chunks of data on either peer.
*/
public static final int MAX_BODY_SIZE = 16 * 1024;
/**
* The tunnel will only accept connections from this specific user agent. This
* allows us to distinguish a legitimate tunnel connection from someone pointing
* a web browser or robot at the tunnel URL.
*/
static final String USER_AGENT = "HttpTunnelClient";
/**
* The tunnel will only accept connections from this specific user agent. This
* allows us to distinguish a legitimate tunnel connection from someone pointing
* a web browser or robot at the tunnel URL.
*/
static final String USER_AGENT = "HttpTunnelClient";
static final String OPEN_TUNNEL_REQUEST_URI = "/http-tunnel/open";
static final String OPEN_TUNNEL_REQUEST_URI = "/http-tunnel/open";
static final String CLOSE_TUNNEL_REQUEST_URI = "/http-tunnel/close";
static final String CLOSE_TUNNEL_REQUEST_URI = "/http-tunnel/close";
static final String CLIENT_SEND_REQUEST_URI = "/http-tunnel/send";
static final String CLIENT_SEND_REQUEST_URI = "/http-tunnel/send";
static final String CLIENT_RECV_REQUEST_URI = "/http-tunnel/poll";
static final String CLIENT_RECV_REQUEST_URI = "/http-tunnel/poll";
static final String CONTENT_TYPE = "application/octet-stream";
static final String CONTENT_TYPE = "application/octet-stream";
public static HttpRequest createOpenTunnelRequest(SocketAddress host)
{
return createOpenTunnelRequest(convertToHostString(host));
}
public static HttpRequest createOpenTunnelRequest(SocketAddress host) {
return createOpenTunnelRequest(convertToHostString(host));
}
public static HttpRequest createOpenTunnelRequest(String host)
{
HttpRequest request = createRequestTemplate(host, null, OPEN_TUNNEL_REQUEST_URI);
setNoData(request);
return request;
}
public static HttpRequest createOpenTunnelRequest(String host) {
HttpRequest request =
createRequestTemplate(host, null, OPEN_TUNNEL_REQUEST_URI);
setNoData(request);
return request;
}
public static boolean isOpenTunnelRequest(HttpRequest request)
{
return isRequestTo(request, OPEN_TUNNEL_REQUEST_URI);
}
public static boolean isOpenTunnelRequest(HttpRequest request) {
return isRequestTo(request, OPEN_TUNNEL_REQUEST_URI);
}
public static boolean checkHost(HttpRequest request, SocketAddress expectedHost)
{
String host = request.getHeader(HttpHeaders.Names.HOST);
return expectedHost == null ? host == null : HttpTunnelMessageUtils.convertToHostString(expectedHost)
.equals(host);
}
public static boolean checkHost(HttpRequest request,
SocketAddress expectedHost) {
String host = request.getHeader(HttpHeaders.Names.HOST);
return expectedHost == null? host == null : HttpTunnelMessageUtils
.convertToHostString(expectedHost).equals(host);
}
public static HttpRequest createSendDataRequest(SocketAddress host, String cookie, ChannelBuffer data)
{
return createSendDataRequest(convertToHostString(host), cookie, data);
}
public static HttpRequest createSendDataRequest(SocketAddress host,
String cookie, ChannelBuffer data) {
return createSendDataRequest(convertToHostString(host), cookie, data);
}
public static HttpRequest createSendDataRequest(String host, String cookie, ChannelBuffer data)
{
HttpRequest request = createRequestTemplate(host, cookie, CLIENT_SEND_REQUEST_URI);
request.setHeader(HttpHeaders.Names.CONTENT_LENGTH, Long.toString(data.readableBytes()));
request.setContent(data);
public static HttpRequest createSendDataRequest(String host, String cookie,
ChannelBuffer data) {
HttpRequest request =
createRequestTemplate(host, cookie, CLIENT_SEND_REQUEST_URI);
request.setHeader(HttpHeaders.Names.CONTENT_LENGTH,
Long.toString(data.readableBytes()));
request.setContent(data);
return request;
}
return request;
}
public static boolean isSendDataRequest(HttpRequest request)
{
return isRequestTo(request, CLIENT_SEND_REQUEST_URI);
}
public static boolean isSendDataRequest(HttpRequest request) {
return isRequestTo(request, CLIENT_SEND_REQUEST_URI);
}
public static HttpRequest createReceiveDataRequest(SocketAddress host, String tunnelId)
{
return createReceiveDataRequest(convertToHostString(host), tunnelId);
}
public static HttpRequest createReceiveDataRequest(SocketAddress host,
String tunnelId) {
return createReceiveDataRequest(convertToHostString(host), tunnelId);
}
public static HttpRequest createReceiveDataRequest(String host, String tunnelId)
{
HttpRequest request = createRequestTemplate(host, tunnelId, CLIENT_RECV_REQUEST_URI);
setNoData(request);
return request;
}
public static HttpRequest createReceiveDataRequest(String host,
String tunnelId) {
HttpRequest request =
createRequestTemplate(host, tunnelId, CLIENT_RECV_REQUEST_URI);
setNoData(request);
return request;
}
public static boolean isReceiveDataRequest(HttpRequest request)
{
return isRequestTo(request, CLIENT_RECV_REQUEST_URI);
}
public static boolean isReceiveDataRequest(HttpRequest request) {
return isRequestTo(request, CLIENT_RECV_REQUEST_URI);
}
public static HttpRequest createCloseTunnelRequest(String host, String tunnelId)
{
HttpRequest request = createRequestTemplate(host, tunnelId, CLOSE_TUNNEL_REQUEST_URI);
setNoData(request);
return request;
}
public static HttpRequest createCloseTunnelRequest(String host,
String tunnelId) {
HttpRequest request =
createRequestTemplate(host, tunnelId, CLOSE_TUNNEL_REQUEST_URI);
setNoData(request);
return request;
}
public static boolean isCloseTunnelRequest(HttpRequest request)
{
return isRequestTo(request, CLOSE_TUNNEL_REQUEST_URI);
}
public static boolean isCloseTunnelRequest(HttpRequest request) {
return isRequestTo(request, CLOSE_TUNNEL_REQUEST_URI);
}
public static boolean isServerToClientRequest(HttpRequest request)
{
return isRequestTo(request, CLIENT_RECV_REQUEST_URI);
}
public static boolean isServerToClientRequest(HttpRequest request) {
return isRequestTo(request, CLIENT_RECV_REQUEST_URI);
}
public static String convertToHostString(SocketAddress hostAddress)
{
StringWriter host = new StringWriter();
InetSocketAddress inetSocketAddr = (InetSocketAddress) hostAddress;
InetAddress addr = inetSocketAddr.getAddress();
if (addr instanceof Inet6Address)
{
host.append('[');
host.append(addr.getHostAddress());
host.append(']');
}
else if (addr != null)
{
host.append(addr.getHostAddress());
}
else
{
host.append(inetSocketAddr.getHostName());
}
public static String convertToHostString(SocketAddress hostAddress) {
StringWriter host = new StringWriter();
InetSocketAddress inetSocketAddr = (InetSocketAddress) hostAddress;
InetAddress addr = inetSocketAddr.getAddress();
if (addr instanceof Inet6Address) {
host.append('[');
host.append(addr.getHostAddress());
host.append(']');
} else if (addr != null) {
host.append(addr.getHostAddress());
} else {
host.append(inetSocketAddr.getHostName());
}
host.append(':');
host.append(Integer.toString(inetSocketAddr.getPort()));
return host.toString();
}
host.append(':');
host.append(Integer.toString(inetSocketAddr.getPort()));
return host.toString();
}
private static HttpRequest createRequestTemplate(String host, String tunnelId, String uri)
{
HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST, createCompleteUri(host, uri));
request.setHeader(HttpHeaders.Names.HOST, host);
request.setHeader(HttpHeaders.Names.USER_AGENT, USER_AGENT);
if (tunnelId != null)
{
request.setHeader(HttpHeaders.Names.COOKIE, tunnelId);
}
private static HttpRequest createRequestTemplate(String host,
String tunnelId, String uri) {
HttpRequest request =
new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.POST,
createCompleteUri(host, uri));
request.setHeader(HttpHeaders.Names.HOST, host);
request.setHeader(HttpHeaders.Names.USER_AGENT, USER_AGENT);
if (tunnelId != null) {
request.setHeader(HttpHeaders.Names.COOKIE, tunnelId);
}
return request;
}
return request;
}
private static String createCompleteUri(String host, String uri)
{
StringBuilder builder = new StringBuilder(HTTP_URL_PREFIX.length() + host.length() + uri.length());
builder.append(HTTP_URL_PREFIX);
builder.append(host);
builder.append(uri);
private static String createCompleteUri(String host, String uri) {
StringBuilder builder =
new StringBuilder(HTTP_URL_PREFIX.length() + host.length() +
uri.length());
builder.append(HTTP_URL_PREFIX);
builder.append(host);
builder.append(uri);
return builder.toString();
}
return builder.toString();
}
private static boolean isRequestTo(HttpRequest request, String uri)
{
URI decodedUri;
try
{
decodedUri = new URI(request.getUri());
}
catch (URISyntaxException e)
{
return false;
}
private static boolean isRequestTo(HttpRequest request, String uri) {
URI decodedUri;
try {
decodedUri = new URI(request.getUri());
} catch (URISyntaxException e) {
return false;
}
return HttpVersion.HTTP_1_1.equals(request.getProtocolVersion())
&& USER_AGENT.equals(request.getHeader(HttpHeaders.Names.USER_AGENT))
&& HttpMethod.POST.equals(request.getMethod()) && uri.equals(decodedUri.getPath());
}
return HttpVersion.HTTP_1_1.equals(request.getProtocolVersion()) &&
USER_AGENT.equals(request
.getHeader(HttpHeaders.Names.USER_AGENT)) &&
HttpMethod.POST.equals(request.getMethod()) &&
uri.equals(decodedUri.getPath());
}
private static void setNoData(HttpRequest request)
{
request.setHeader(HttpHeaders.Names.CONTENT_LENGTH, "0");
request.setContent(null);
}
private static void setNoData(HttpRequest request) {
request.setHeader(HttpHeaders.Names.CONTENT_LENGTH, "0");
request.setContent(null);
}
public static String extractTunnelId(HttpRequest request)
{
return request.getHeader(HttpHeaders.Names.COOKIE);
}
public static String extractTunnelId(HttpRequest request) {
return request.getHeader(HttpHeaders.Names.COOKIE);
}
private static byte[] toBytes(String string)
{
try
{
return string.getBytes("UTF-8");
}
catch (UnsupportedEncodingException e)
{
// UTF-8 is meant to be supported on all platforms
throw new RuntimeException("UTF-8 encoding not supported!");
}
}
private static byte[] toBytes(String string) {
try {
return string.getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
// UTF-8 is meant to be supported on all platforms
throw new RuntimeException("UTF-8 encoding not supported!");
}
}
public static HttpResponse createTunnelOpenResponse(String tunnelId)
{
HttpResponse response = createResponseTemplate(HttpResponseStatus.CREATED, null);
response.setHeader(HttpHeaders.Names.SET_COOKIE, tunnelId);
return response;
}
public static HttpResponse createTunnelOpenResponse(String tunnelId) {
HttpResponse response =
createResponseTemplate(HttpResponseStatus.CREATED, null);
response.setHeader(HttpHeaders.Names.SET_COOKIE, tunnelId);
return response;
}
public static boolean isTunnelOpenResponse(HttpResponse response)
{
return isResponseWithCode(response, HttpResponseStatus.CREATED);
}
public static boolean isTunnelOpenResponse(HttpResponse response) {
return isResponseWithCode(response, HttpResponseStatus.CREATED);
}
public static boolean isOKResponse(HttpResponse response)
{
return isResponseWithCode(response, HttpResponseStatus.OK);
}
public static boolean isOKResponse(HttpResponse response) {
return isResponseWithCode(response, HttpResponseStatus.OK);
}
public static boolean hasContents(HttpResponse response, byte[] expectedContents)
{
if (response.getContent() != null && HttpHeaders.getContentLength(response) == expectedContents.length
&& response.getContent().readableBytes() == expectedContents.length)
{
byte[] compareBytes = new byte[expectedContents.length];
response.getContent().readBytes(compareBytes);
return Arrays.equals(expectedContents, compareBytes);
}
public static boolean hasContents(HttpResponse response,
byte[] expectedContents) {
if (response.getContent() != null &&
HttpHeaders.getContentLength(response) == expectedContents.length &&
response.getContent().readableBytes() == expectedContents.length) {
byte[] compareBytes = new byte[expectedContents.length];
response.getContent().readBytes(compareBytes);
return Arrays.equals(expectedContents, compareBytes);
}
return false;
}
return false;
}
public static HttpResponse createTunnelCloseResponse()
{
HttpResponse response = createResponseTemplate(HttpResponseStatus.RESET_CONTENT, null);
return response;
}
public static HttpResponse createTunnelCloseResponse() {
HttpResponse response =
createResponseTemplate(HttpResponseStatus.RESET_CONTENT, null);
return response;
}
public static boolean isTunnelCloseResponse(HttpResponse response)
{
return isResponseWithCode(response, HttpResponseStatus.RESET_CONTENT);
}
public static boolean isTunnelCloseResponse(HttpResponse response) {
return isResponseWithCode(response, HttpResponseStatus.RESET_CONTENT);
}
public static String extractCookie(HttpResponse response)
{
if (response.containsHeader(HttpHeaders.Names.SET_COOKIE))
{
return response.getHeader(HttpHeaders.Names.SET_COOKIE);
}
public static String extractCookie(HttpResponse response) {
if (response.containsHeader(HttpHeaders.Names.SET_COOKIE)) {
return response.getHeader(HttpHeaders.Names.SET_COOKIE);
}
return null;
}
return null;
}
public static HttpResponse createSendDataResponse()
{
return createOKResponseTemplate(null);
}
public static HttpResponse createSendDataResponse() {
return createOKResponseTemplate(null);
}
public static HttpResponse createRecvDataResponse(ChannelBuffer data)
{
return createOKResponseTemplate(data);
}
public static HttpResponse createRecvDataResponse(ChannelBuffer data) {
return createOKResponseTemplate(data);
}
public static HttpResponse createRejection(HttpRequest request, String reason)
{
HttpVersion version = request != null ? request.getProtocolVersion() : HttpVersion.HTTP_1_1;
HttpResponse response = new DefaultHttpResponse(version, HttpResponseStatus.BAD_REQUEST);
response.setHeader(HttpHeaders.Names.CONTENT_TYPE, "text/plain; charset=\"utf-8\"");
ChannelBuffer reasonBuffer = ChannelBuffers.wrappedBuffer(toBytes(reason));
response.setHeader(HttpHeaders.Names.CONTENT_LENGTH, Integer.toString(reasonBuffer.readableBytes()));
response.setContent(reasonBuffer);
return response;
}
public static HttpResponse createRejection(HttpRequest request,
String reason) {
HttpVersion version =
request != null? request.getProtocolVersion()
: HttpVersion.HTTP_1_1;
HttpResponse response =
new DefaultHttpResponse(version, HttpResponseStatus.BAD_REQUEST);
response.setHeader(HttpHeaders.Names.CONTENT_TYPE,
"text/plain; charset=\"utf-8\"");
ChannelBuffer reasonBuffer =
ChannelBuffers.wrappedBuffer(toBytes(reason));
response.setHeader(HttpHeaders.Names.CONTENT_LENGTH,
Integer.toString(reasonBuffer.readableBytes()));
response.setContent(reasonBuffer);
return response;
}
public static boolean isRejection(HttpResponse response)
{
return !HttpResponseStatus.OK.equals(response.getStatus());
}
public static boolean isRejection(HttpResponse response) {
return !HttpResponseStatus.OK.equals(response.getStatus());
}
public static Object extractErrorMessage(HttpResponse response)
{
if (response.getContent() == null || HttpHeaders.getContentLength(response) == 0)
{
return "";
}
public static Object extractErrorMessage(HttpResponse response) {
if (response.getContent() == null ||
HttpHeaders.getContentLength(response) == 0) {
return "";
}
byte[] bytes = new byte[response.getContent().readableBytes()];
response.getContent().readBytes(bytes);
try
{
return new String(bytes, "UTF-8");
}
catch (UnsupportedEncodingException e)
{
return "";
}
}
byte[] bytes = new byte[response.getContent().readableBytes()];
response.getContent().readBytes(bytes);
try {
return new String(bytes, "UTF-8");
} catch (UnsupportedEncodingException e) {
return "";
}
}
private static boolean isResponseWithCode(HttpResponse response, HttpResponseStatus status)
{
return HttpVersion.HTTP_1_1.equals(response.getProtocolVersion()) && status.equals(response.getStatus());
}
private static boolean isResponseWithCode(HttpResponse response,
HttpResponseStatus status) {
return HttpVersion.HTTP_1_1.equals(response.getProtocolVersion()) &&
status.equals(response.getStatus());
}
private static HttpResponse createOKResponseTemplate(ChannelBuffer data)
{
return createResponseTemplate(HttpResponseStatus.OK, data);
}
private static HttpResponse createOKResponseTemplate(ChannelBuffer data) {
return createResponseTemplate(HttpResponseStatus.OK, data);
}
private static HttpResponse createResponseTemplate(HttpResponseStatus status, ChannelBuffer data)
{
HttpResponse response = new DefaultHttpResponse(HttpVersion.HTTP_1_1, status);
if (data != null)
{
response.setHeader(HttpHeaders.Names.CONTENT_LENGTH, Integer.toString(data.readableBytes()));
response.setHeader(HttpHeaders.Names.CONTENT_TYPE, "application/octet-stream");
response.setContent(data);
}
else
{
response.setHeader(HttpHeaders.Names.CONTENT_LENGTH, "0");
response.setContent(null);
}
return response;
}
private static HttpResponse createResponseTemplate(
HttpResponseStatus status, ChannelBuffer data) {
HttpResponse response =
new DefaultHttpResponse(HttpVersion.HTTP_1_1, status);
if (data != null) {
response.setHeader(HttpHeaders.Names.CONTENT_LENGTH,
Integer.toString(data.readableBytes()));
response.setHeader(HttpHeaders.Names.CONTENT_TYPE,
"application/octet-stream");
response.setContent(data);
} else {
response.setHeader(HttpHeaders.Names.CONTENT_LENGTH, "0");
response.setContent(null);
}
return response;
}
}

View File

@ -31,83 +31,80 @@ import org.jboss.netty.channel.socket.ServerSocketChannelConfig;
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
* @author OneDrum Ltd.
*/
public class HttpTunnelServerChannel extends AbstractServerChannel implements ServerSocketChannel
{
public class HttpTunnelServerChannel extends AbstractServerChannel implements
ServerSocketChannel {
private final ServerSocketChannel realChannel;
private final ServerSocketChannel realChannel;
private final HttpTunnelServerChannelConfig config;
private final HttpTunnelServerChannelConfig config;
private final ServerMessageSwitch messageSwitch;
private final ServerMessageSwitch messageSwitch;
private final ChannelFutureListener CLOSE_FUTURE_PROXY = new ChannelFutureListener()
{
public void operationComplete(ChannelFuture future) throws Exception
{
HttpTunnelServerChannel.this.setClosed();
}
};
private final ChannelFutureListener CLOSE_FUTURE_PROXY =
new ChannelFutureListener() {
public void operationComplete(ChannelFuture future)
throws Exception {
HttpTunnelServerChannel.this.setClosed();
}
};
protected HttpTunnelServerChannel(HttpTunnelServerChannelFactory factory, ChannelPipeline pipeline)
{
super(factory, pipeline, new HttpTunnelServerChannelSink());
protected HttpTunnelServerChannel(HttpTunnelServerChannelFactory factory,
ChannelPipeline pipeline) {
super(factory, pipeline, new HttpTunnelServerChannelSink());
messageSwitch = new ServerMessageSwitch(new TunnelCreator());
realChannel = factory.createRealChannel(this, messageSwitch);
HttpTunnelServerChannelSink sink = (HttpTunnelServerChannelSink) getPipeline().getSink();
sink.setRealChannel(realChannel);
sink.setCloseListener(CLOSE_FUTURE_PROXY);
config = new HttpTunnelServerChannelConfig(realChannel);
Channels.fireChannelOpen(this);
}
messageSwitch = new ServerMessageSwitch(new TunnelCreator());
realChannel = factory.createRealChannel(this, messageSwitch);
HttpTunnelServerChannelSink sink =
(HttpTunnelServerChannelSink) getPipeline().getSink();
sink.setRealChannel(realChannel);
sink.setCloseListener(CLOSE_FUTURE_PROXY);
config = new HttpTunnelServerChannelConfig(realChannel);
Channels.fireChannelOpen(this);
}
public ServerSocketChannelConfig getConfig()
{
return config;
}
public ServerSocketChannelConfig getConfig() {
return config;
}
public InetSocketAddress getLocalAddress()
{
return realChannel.getLocalAddress();
}
public InetSocketAddress getLocalAddress() {
return realChannel.getLocalAddress();
}
public InetSocketAddress getRemoteAddress()
{
// server channels never have a remote address
return null;
}
public InetSocketAddress getRemoteAddress() {
// server channels never have a remote address
return null;
}
public boolean isBound()
{
return realChannel.isBound();
}
public boolean isBound() {
return realChannel.isBound();
}
/**
* Used to hide the newChannel method from the public API.
*/
private final class TunnelCreator implements HttpTunnelAcceptedChannelFactory
{
/**
* Used to hide the newChannel method from the public API.
*/
private final class TunnelCreator implements
HttpTunnelAcceptedChannelFactory {
public HttpTunnelAcceptedChannelReceiver newChannel(String newTunnelId, InetSocketAddress remoteAddress)
{
ChannelPipeline childPipeline = null;
try
{
childPipeline = getConfig().getPipelineFactory().getPipeline();
}
catch (Exception e)
{
throw new ChannelPipelineException("Failed to initialize a pipeline.", e);
}
HttpTunnelAcceptedChannelConfig config = new HttpTunnelAcceptedChannelConfig();
HttpTunnelAcceptedChannelSink sink = new HttpTunnelAcceptedChannelSink(messageSwitch, newTunnelId, config);
return new HttpTunnelAcceptedChannel(HttpTunnelServerChannel.this, getFactory(), childPipeline, sink,
remoteAddress, config);
}
public HttpTunnelAcceptedChannelReceiver newChannel(String newTunnelId,
InetSocketAddress remoteAddress) {
ChannelPipeline childPipeline = null;
try {
childPipeline = getConfig().getPipelineFactory().getPipeline();
} catch (Exception e) {
throw new ChannelPipelineException(
"Failed to initialize a pipeline.", e);
}
HttpTunnelAcceptedChannelConfig config =
new HttpTunnelAcceptedChannelConfig();
HttpTunnelAcceptedChannelSink sink =
new HttpTunnelAcceptedChannelSink(messageSwitch,
newTunnelId, config);
return new HttpTunnelAcceptedChannel(HttpTunnelServerChannel.this,
getFactory(), childPipeline, sink, remoteAddress, config);
}
public String generateTunnelId()
{
return config.getTunnelIdGenerator().generateId();
}
}
public String generateTunnelId() {
return config.getTunnelIdGenerator().generateId();
}
}
}

View File

@ -28,123 +28,100 @@ import org.jboss.netty.channel.socket.ServerSocketChannelConfig;
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
* @author OneDrum Ltd.
*/
public class HttpTunnelServerChannelConfig implements ServerSocketChannelConfig
{
public class HttpTunnelServerChannelConfig implements ServerSocketChannelConfig {
private ChannelPipelineFactory pipelineFactory;
private ChannelPipelineFactory pipelineFactory;
private final ServerSocketChannel realChannel;
private final ServerSocketChannel realChannel;
private TunnelIdGenerator tunnelIdGenerator = new DefaultTunnelIdGenerator();
private TunnelIdGenerator tunnelIdGenerator =
new DefaultTunnelIdGenerator();
public HttpTunnelServerChannelConfig(ServerSocketChannel realChannel)
{
this.realChannel = realChannel;
}
public HttpTunnelServerChannelConfig(ServerSocketChannel realChannel) {
this.realChannel = realChannel;
}
private ServerSocketChannelConfig getWrappedConfig()
{
return realChannel.getConfig();
}
private ServerSocketChannelConfig getWrappedConfig() {
return realChannel.getConfig();
}
public int getBacklog()
{
return getWrappedConfig().getBacklog();
}
public int getBacklog() {
return getWrappedConfig().getBacklog();
}
public int getReceiveBufferSize()
{
return getWrappedConfig().getReceiveBufferSize();
}
public int getReceiveBufferSize() {
return getWrappedConfig().getReceiveBufferSize();
}
public boolean isReuseAddress()
{
return getWrappedConfig().isReuseAddress();
}
public boolean isReuseAddress() {
return getWrappedConfig().isReuseAddress();
}
public void setBacklog(int backlog)
{
getWrappedConfig().setBacklog(backlog);
}
public void setBacklog(int backlog) {
getWrappedConfig().setBacklog(backlog);
}
public void setPerformancePreferences(int connectionTime, int latency, int bandwidth)
{
getWrappedConfig().setPerformancePreferences(connectionTime, latency, bandwidth);
}
public void setPerformancePreferences(int connectionTime, int latency,
int bandwidth) {
getWrappedConfig().setPerformancePreferences(connectionTime, latency,
bandwidth);
}
public void setReceiveBufferSize(int receiveBufferSize)
{
getWrappedConfig().setReceiveBufferSize(receiveBufferSize);
}
public void setReceiveBufferSize(int receiveBufferSize) {
getWrappedConfig().setReceiveBufferSize(receiveBufferSize);
}
public void setReuseAddress(boolean reuseAddress)
{
getWrappedConfig().setReuseAddress(reuseAddress);
}
public void setReuseAddress(boolean reuseAddress) {
getWrappedConfig().setReuseAddress(reuseAddress);
}
public ChannelBufferFactory getBufferFactory()
{
return getWrappedConfig().getBufferFactory();
}
public ChannelBufferFactory getBufferFactory() {
return getWrappedConfig().getBufferFactory();
}
public int getConnectTimeoutMillis()
{
return getWrappedConfig().getConnectTimeoutMillis();
}
public int getConnectTimeoutMillis() {
return getWrappedConfig().getConnectTimeoutMillis();
}
public ChannelPipelineFactory getPipelineFactory()
{
return pipelineFactory;
}
public ChannelPipelineFactory getPipelineFactory() {
return pipelineFactory;
}
public void setBufferFactory(ChannelBufferFactory bufferFactory)
{
getWrappedConfig().setBufferFactory(bufferFactory);
}
public void setBufferFactory(ChannelBufferFactory bufferFactory) {
getWrappedConfig().setBufferFactory(bufferFactory);
}
public void setConnectTimeoutMillis(int connectTimeoutMillis)
{
getWrappedConfig().setConnectTimeoutMillis(connectTimeoutMillis);
}
public void setConnectTimeoutMillis(int connectTimeoutMillis) {
getWrappedConfig().setConnectTimeoutMillis(connectTimeoutMillis);
}
public boolean setOption(String name, Object value)
{
if (name.equals("pipelineFactory"))
{
setPipelineFactory((ChannelPipelineFactory) value);
return true;
}
else if (name.equals("tunnelIdGenerator"))
{
setTunnelIdGenerator((TunnelIdGenerator) value);
return true;
}
else
{
return getWrappedConfig().setOption(name, value);
}
}
public boolean setOption(String name, Object value) {
if (name.equals("pipelineFactory")) {
setPipelineFactory((ChannelPipelineFactory) value);
return true;
} else if (name.equals("tunnelIdGenerator")) {
setTunnelIdGenerator((TunnelIdGenerator) value);
return true;
} else {
return getWrappedConfig().setOption(name, value);
}
}
public void setOptions(Map<String, Object> options)
{
for (Entry<String, Object> e : options.entrySet())
{
setOption(e.getKey(), e.getValue());
}
}
public void setOptions(Map<String, Object> options) {
for (Entry<String, Object> e: options.entrySet()) {
setOption(e.getKey(), e.getValue());
}
}
public void setPipelineFactory(ChannelPipelineFactory pipelineFactory)
{
this.pipelineFactory = pipelineFactory;
}
public void setPipelineFactory(ChannelPipelineFactory pipelineFactory) {
this.pipelineFactory = pipelineFactory;
}
public void setTunnelIdGenerator(TunnelIdGenerator tunnelIdGenerator)
{
this.tunnelIdGenerator = tunnelIdGenerator;
}
public void setTunnelIdGenerator(TunnelIdGenerator tunnelIdGenerator) {
this.tunnelIdGenerator = tunnelIdGenerator;
}
public TunnelIdGenerator getTunnelIdGenerator()
{
return tunnelIdGenerator;
}
public TunnelIdGenerator getTunnelIdGenerator() {
return tunnelIdGenerator;
}
}

View File

@ -27,39 +27,40 @@ import org.jboss.netty.channel.socket.ServerSocketChannelFactory;
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
* @author OneDrum Ltd.
*/
public class HttpTunnelServerChannelFactory implements ServerSocketChannelFactory
{
public class HttpTunnelServerChannelFactory implements
ServerSocketChannelFactory {
private final ServerSocketChannelFactory realConnectionFactory;
private final ServerSocketChannelFactory realConnectionFactory;
private final ChannelGroup realConnections;
private final ChannelGroup realConnections;
public HttpTunnelServerChannelFactory(ServerSocketChannelFactory realConnectionFactory)
{
this.realConnectionFactory = realConnectionFactory;
realConnections = new DefaultChannelGroup();
}
public HttpTunnelServerChannelFactory(
ServerSocketChannelFactory realConnectionFactory) {
this.realConnectionFactory = realConnectionFactory;
realConnections = new DefaultChannelGroup();
}
public HttpTunnelServerChannel newChannel(ChannelPipeline pipeline)
{
return new HttpTunnelServerChannel(this, pipeline);
}
public HttpTunnelServerChannel newChannel(ChannelPipeline pipeline) {
return new HttpTunnelServerChannel(this, pipeline);
}
ServerSocketChannel createRealChannel(HttpTunnelServerChannel channel, ServerMessageSwitch messageSwitch)
{
ChannelPipeline realChannelPipeline = Channels.pipeline();
AcceptedServerChannelPipelineFactory realPipelineFactory = new AcceptedServerChannelPipelineFactory(messageSwitch);
realChannelPipeline.addFirst(TunnelWrappedServerChannelHandler.NAME, new TunnelWrappedServerChannelHandler(
channel, realPipelineFactory, realConnections));
ServerSocketChannel newChannel = realConnectionFactory.newChannel(realChannelPipeline);
realConnections.add(newChannel);
return newChannel;
}
ServerSocketChannel createRealChannel(HttpTunnelServerChannel channel,
ServerMessageSwitch messageSwitch) {
ChannelPipeline realChannelPipeline = Channels.pipeline();
AcceptedServerChannelPipelineFactory realPipelineFactory =
new AcceptedServerChannelPipelineFactory(messageSwitch);
realChannelPipeline.addFirst(TunnelWrappedServerChannelHandler.NAME,
new TunnelWrappedServerChannelHandler(channel,
realPipelineFactory, realConnections));
ServerSocketChannel newChannel =
realConnectionFactory.newChannel(realChannelPipeline);
realConnections.add(newChannel);
return newChannel;
}
public void releaseExternalResources()
{
realConnections.close().awaitUninterruptibly();
realConnectionFactory.releaseExternalResources();
}
public void releaseExternalResources() {
realConnections.close().awaitUninterruptibly();
realConnectionFactory.releaseExternalResources();
}
}

View File

@ -30,70 +30,57 @@ import org.jboss.netty.channel.socket.ServerSocketChannel;
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
* @author OneDrum Ltd.
*/
class HttpTunnelServerChannelSink extends AbstractChannelSink
{
class HttpTunnelServerChannelSink extends AbstractChannelSink {
private ChannelFutureListener closeHook;
private ChannelFutureListener closeHook;
private ServerSocketChannel realChannel;
private ServerSocketChannel realChannel;
public void eventSunk(ChannelPipeline pipeline, ChannelEvent e) throws Exception
{
public void eventSunk(ChannelPipeline pipeline, ChannelEvent e)
throws Exception {
if (e instanceof ChannelStateEvent)
{
ChannelStateEvent ev = (ChannelStateEvent) e;
switch (ev.getState())
{
case OPEN :
if (Boolean.FALSE.equals(ev.getValue()))
{
realChannel.close().addListener(closeHook);
}
break;
case BOUND :
if (ev.getValue() != null)
{
realChannel.bind((SocketAddress) ev.getValue()).addListener(new ChannelFutureProxy(e.getFuture()));
}
else
{
realChannel.unbind().addListener(new ChannelFutureProxy(e.getFuture()));
}
break;
}
}
}
if (e instanceof ChannelStateEvent) {
ChannelStateEvent ev = (ChannelStateEvent) e;
switch (ev.getState()) {
case OPEN:
if (Boolean.FALSE.equals(ev.getValue())) {
realChannel.close().addListener(closeHook);
}
break;
case BOUND:
if (ev.getValue() != null) {
realChannel.bind((SocketAddress) ev.getValue())
.addListener(new ChannelFutureProxy(e.getFuture()));
} else {
realChannel.unbind().addListener(
new ChannelFutureProxy(e.getFuture()));
}
break;
}
}
}
private final class ChannelFutureProxy implements ChannelFutureListener
{
private final ChannelFuture upstreamFuture;
private final class ChannelFutureProxy implements ChannelFutureListener {
private final ChannelFuture upstreamFuture;
ChannelFutureProxy(ChannelFuture upstreamFuture)
{
this.upstreamFuture = upstreamFuture;
}
ChannelFutureProxy(ChannelFuture upstreamFuture) {
this.upstreamFuture = upstreamFuture;
}
public void operationComplete(ChannelFuture future) throws Exception
{
if (future.isSuccess())
{
upstreamFuture.setSuccess();
}
else
{
upstreamFuture.setFailure(future.getCause());
}
}
}
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
upstreamFuture.setSuccess();
} else {
upstreamFuture.setFailure(future.getCause());
}
}
}
public void setRealChannel(ServerSocketChannel realChannel)
{
this.realChannel = realChannel;
}
public void setRealChannel(ServerSocketChannel realChannel) {
this.realChannel = realChannel;
}
public void setCloseListener(ChannelFutureListener closeHook)
{
this.closeHook = closeHook;
}
public void setCloseListener(ChannelFutureListener closeHook) {
this.closeHook = closeHook;
}
}

View File

@ -31,38 +31,39 @@ import java.util.concurrent.atomic.AtomicLong;
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
* @author OneDrum Ltd.
*/
class SaturationManager
{
private AtomicLong desaturationPoint;
private AtomicLong saturationPoint;
private final AtomicLong queueSize;
private final AtomicBoolean saturated;
public SaturationManager(long desaturationPoint, long saturationPoint)
{
this.desaturationPoint = new AtomicLong(desaturationPoint);
this.saturationPoint = new AtomicLong(saturationPoint);
queueSize = new AtomicLong(0);
saturated = new AtomicBoolean(false);
}
public SaturationStateChange queueSizeChanged(long sizeDelta) {
long newQueueSize = queueSize.addAndGet(sizeDelta);
if(newQueueSize <= desaturationPoint.get()) {
if(saturated.compareAndSet(true, false)) {
return DESATURATED;
}
} else if(newQueueSize > saturationPoint.get()) {
if(saturated.compareAndSet(false, true)) {
return SATURATED;
}
}
return NO_CHANGE;
}
public void updateThresholds(long desaturationPoint, long saturationPoint) {
this.desaturationPoint.set(desaturationPoint);
this.saturationPoint.set(saturationPoint);
}
class SaturationManager {
private AtomicLong desaturationPoint;
private AtomicLong saturationPoint;
private final AtomicLong queueSize;
private final AtomicBoolean saturated;
public SaturationManager(long desaturationPoint, long saturationPoint) {
this.desaturationPoint = new AtomicLong(desaturationPoint);
this.saturationPoint = new AtomicLong(saturationPoint);
queueSize = new AtomicLong(0);
saturated = new AtomicBoolean(false);
}
public SaturationStateChange queueSizeChanged(long sizeDelta) {
long newQueueSize = queueSize.addAndGet(sizeDelta);
if (newQueueSize <= desaturationPoint.get()) {
if (saturated.compareAndSet(true, false)) {
return DESATURATED;
}
} else if (newQueueSize > saturationPoint.get()) {
if (saturated.compareAndSet(false, true)) {
return SATURATED;
}
}
return NO_CHANGE;
}
public void updateThresholds(long desaturationPoint, long saturationPoint) {
this.desaturationPoint.set(desaturationPoint);
this.saturationPoint.set(saturationPoint);
}
}

View File

@ -25,7 +25,5 @@ package org.jboss.netty.channel.socket.http;
* @author OneDrum Ltd.
*/
enum SaturationStateChange {
NO_CHANGE,
DESATURATED,
SATURATED
NO_CHANGE, DESATURATED, SATURATED
}

View File

@ -43,226 +43,215 @@ import org.jboss.netty.logging.InternalLoggerFactory;
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
* @author OneDrum Ltd.
*/
class ServerMessageSwitch implements ServerMessageSwitchUpstreamInterface, ServerMessageSwitchDownstreamInterface
{
class ServerMessageSwitch implements ServerMessageSwitchUpstreamInterface,
ServerMessageSwitchDownstreamInterface {
private static final InternalLogger LOG = InternalLoggerFactory.getInstance(ServerMessageSwitch.class.getName());
private static final InternalLogger LOG = InternalLoggerFactory
.getInstance(ServerMessageSwitch.class.getName());
private final String tunnelIdPrefix;
private final String tunnelIdPrefix;
private final HttpTunnelAcceptedChannelFactory newChannelFactory;
private final HttpTunnelAcceptedChannelFactory newChannelFactory;
private final ConcurrentHashMap<String, TunnelInfo> tunnelsById;
private final ConcurrentHashMap<String, TunnelInfo> tunnelsById;
public ServerMessageSwitch(HttpTunnelAcceptedChannelFactory newChannelFactory)
{
this.newChannelFactory = newChannelFactory;
tunnelIdPrefix = Long.toHexString(new Random().nextLong());
tunnelsById = new ConcurrentHashMap<String, TunnelInfo>();
}
public ServerMessageSwitch(
HttpTunnelAcceptedChannelFactory newChannelFactory) {
this.newChannelFactory = newChannelFactory;
tunnelIdPrefix = Long.toHexString(new Random().nextLong());
tunnelsById = new ConcurrentHashMap<String, TunnelInfo>();
}
public String createTunnel(InetSocketAddress remoteAddress)
{
String newTunnelId = String.format("%s_%s", tunnelIdPrefix, newChannelFactory.generateTunnelId());
TunnelInfo newTunnel = new TunnelInfo();
newTunnel.tunnelId = newTunnelId;
tunnelsById.put(newTunnelId, newTunnel);
newTunnel.localChannel = newChannelFactory.newChannel(newTunnelId, remoteAddress);
return newTunnelId;
}
public String createTunnel(InetSocketAddress remoteAddress) {
String newTunnelId =
String.format("%s_%s", tunnelIdPrefix,
newChannelFactory.generateTunnelId());
TunnelInfo newTunnel = new TunnelInfo();
newTunnel.tunnelId = newTunnelId;
tunnelsById.put(newTunnelId, newTunnel);
newTunnel.localChannel =
newChannelFactory.newChannel(newTunnelId, remoteAddress);
return newTunnelId;
}
public boolean isOpenTunnel(String tunnelId)
{
TunnelInfo tunnel = tunnelsById.get(tunnelId);
return tunnel != null;
}
public boolean isOpenTunnel(String tunnelId) {
TunnelInfo tunnel = tunnelsById.get(tunnelId);
return tunnel != null;
}
public void pollOutboundData(String tunnelId, Channel channel)
{
TunnelInfo tunnel = tunnelsById.get(tunnelId);
if (tunnel == null)
{
if (LOG.isWarnEnabled())
{
LOG.warn("Poll request for tunnel " + tunnelId + " which does not exist or already closed");
}
respondAndClose(channel,
HttpTunnelMessageUtils.createRejection(null, "Unknown tunnel, possibly already closed"));
return;
}
public void pollOutboundData(String tunnelId, Channel channel) {
TunnelInfo tunnel = tunnelsById.get(tunnelId);
if (tunnel == null) {
if (LOG.isWarnEnabled()) {
LOG.warn("Poll request for tunnel " + tunnelId +
" which does not exist or already closed");
}
respondAndClose(channel, HttpTunnelMessageUtils.createRejection(
null, "Unknown tunnel, possibly already closed"));
return;
}
if (!tunnel.responseChannel.compareAndSet(null, channel))
{
if (LOG.isWarnEnabled())
{
LOG.warn("Duplicate poll request detected for tunnel " + tunnelId);
}
respondAndClose(channel,
HttpTunnelMessageUtils.createRejection(null, "Only one poll request at a time per tunnel allowed"));
return;
}
if (!tunnel.responseChannel.compareAndSet(null, channel)) {
if (LOG.isWarnEnabled()) {
LOG.warn("Duplicate poll request detected for tunnel " +
tunnelId);
}
respondAndClose(channel, HttpTunnelMessageUtils.createRejection(
null, "Only one poll request at a time per tunnel allowed"));
return;
}
sendQueuedData(tunnel);
}
sendQueuedData(tunnel);
}
private void respondAndClose(Channel channel, HttpResponse response)
{
Channels.write(channel, response).addListener(ChannelFutureListener.CLOSE);
}
private void respondAndClose(Channel channel, HttpResponse response) {
Channels.write(channel, response).addListener(
ChannelFutureListener.CLOSE);
}
private void sendQueuedData(TunnelInfo state)
{
Queue<QueuedResponse> queuedData = state.queuedResponses;
Channel responseChannel = state.responseChannel.getAndSet(null);
if (responseChannel == null)
{
// no response channel, or another thread has already used it
return;
}
private void sendQueuedData(TunnelInfo state) {
Queue<QueuedResponse> queuedData = state.queuedResponses;
Channel responseChannel = state.responseChannel.getAndSet(null);
if (responseChannel == null) {
// no response channel, or another thread has already used it
return;
}
if (LOG.isDebugEnabled())
{
LOG.debug("sending response for tunnel id " + state.tunnelId + " to " + responseChannel.getRemoteAddress());
}
QueuedResponse messageToSend = queuedData.poll();
if (messageToSend == null)
{
// no data to send, restore the response channel and bail out
state.responseChannel.set(responseChannel);
return;
}
if (LOG.isDebugEnabled()) {
LOG.debug("sending response for tunnel id " + state.tunnelId +
" to " + responseChannel.getRemoteAddress());
}
QueuedResponse messageToSend = queuedData.poll();
if (messageToSend == null) {
// no data to send, restore the response channel and bail out
state.responseChannel.set(responseChannel);
return;
}
HttpResponse response = HttpTunnelMessageUtils.createRecvDataResponse(messageToSend.data);
final ChannelFuture originalFuture = messageToSend.writeFuture;
Channels.write(responseChannel, response).addListener(new RelayedChannelFutureListener(originalFuture));
}
HttpResponse response =
HttpTunnelMessageUtils
.createRecvDataResponse(messageToSend.data);
final ChannelFuture originalFuture = messageToSend.writeFuture;
Channels.write(responseChannel, response).addListener(
new RelayedChannelFutureListener(originalFuture));
}
public TunnelStatus routeInboundData(String tunnelId, ChannelBuffer inboundData)
{
TunnelInfo tunnel = tunnelsById.get(tunnelId);
if (tunnel == null)
{
return TunnelStatus.CLOSED;
}
public TunnelStatus routeInboundData(String tunnelId,
ChannelBuffer inboundData) {
TunnelInfo tunnel = tunnelsById.get(tunnelId);
if (tunnel == null) {
return TunnelStatus.CLOSED;
}
if (tunnel.closing.get())
{
// client has now been notified, forget the tunnel
tunnelsById.remove(tunnel);
return TunnelStatus.CLOSED;
}
if (tunnel.closing.get()) {
// client has now been notified, forget the tunnel
tunnelsById.remove(tunnel);
return TunnelStatus.CLOSED;
}
if (LOG.isDebugEnabled())
{
LOG.debug("routing inbound data for tunnel " + tunnelId);
}
tunnel.localChannel.dataReceived(inboundData);
return TunnelStatus.ALIVE;
}
if (LOG.isDebugEnabled()) {
LOG.debug("routing inbound data for tunnel " + tunnelId);
}
tunnel.localChannel.dataReceived(inboundData);
return TunnelStatus.ALIVE;
}
public void clientCloseTunnel(String tunnelId)
{
TunnelInfo tunnel = tunnelsById.get(tunnelId);
tunnel.localChannel.clientClosed();
tunnelsById.remove(tunnelId);
}
public void clientCloseTunnel(String tunnelId) {
TunnelInfo tunnel = tunnelsById.get(tunnelId);
tunnel.localChannel.clientClosed();
tunnelsById.remove(tunnelId);
}
public void serverCloseTunnel(String tunnelId)
{
TunnelInfo tunnel = tunnelsById.get(tunnelId);
tunnel.closing.set(true);
public void serverCloseTunnel(String tunnelId) {
TunnelInfo tunnel = tunnelsById.get(tunnelId);
tunnel.closing.set(true);
Channel responseChannel = tunnel.responseChannel.getAndSet(null);
if (responseChannel == null)
{
// response channel is already in use, client will be notified
// of close at next opportunity
return;
}
Channel responseChannel = tunnel.responseChannel.getAndSet(null);
if (responseChannel == null) {
// response channel is already in use, client will be notified
// of close at next opportunity
return;
}
respondAndClose(responseChannel, HttpTunnelMessageUtils.createTunnelCloseResponse());
// client has been notified, forget the tunnel
tunnelsById.remove(tunnelId);
}
respondAndClose(responseChannel,
HttpTunnelMessageUtils.createTunnelCloseResponse());
// client has been notified, forget the tunnel
tunnelsById.remove(tunnelId);
}
public void routeOutboundData(String tunnelId, ChannelBuffer data, ChannelFuture writeFuture)
{
TunnelInfo tunnel = tunnelsById.get(tunnelId);
if (tunnel == null)
{
// tunnel is closed
if (LOG.isWarnEnabled())
{
LOG.warn("attempt made to send data out on tunnel id " + tunnelId + " which is unknown or closed");
}
return;
}
public void routeOutboundData(String tunnelId, ChannelBuffer data,
ChannelFuture writeFuture) {
TunnelInfo tunnel = tunnelsById.get(tunnelId);
if (tunnel == null) {
// tunnel is closed
if (LOG.isWarnEnabled()) {
LOG.warn("attempt made to send data out on tunnel id " +
tunnelId + " which is unknown or closed");
}
return;
}
ChannelFutureAggregator aggregator = new ChannelFutureAggregator(writeFuture);
List<ChannelBuffer> fragments = WriteSplitter.split(data, HttpTunnelMessageUtils.MAX_BODY_SIZE);
ChannelFutureAggregator aggregator =
new ChannelFutureAggregator(writeFuture);
List<ChannelBuffer> fragments =
WriteSplitter.split(data, HttpTunnelMessageUtils.MAX_BODY_SIZE);
if (LOG.isDebugEnabled())
{
LOG.debug("routing outbound data for tunnel " + tunnelId);
}
for (ChannelBuffer fragment : fragments)
{
ChannelFuture fragmentFuture = Channels.future(writeFuture.getChannel());
aggregator.addFuture(fragmentFuture);
tunnel.queuedResponses.offer(new QueuedResponse(fragment, fragmentFuture));
}
if (LOG.isDebugEnabled()) {
LOG.debug("routing outbound data for tunnel " + tunnelId);
}
for (ChannelBuffer fragment: fragments) {
ChannelFuture fragmentFuture =
Channels.future(writeFuture.getChannel());
aggregator.addFuture(fragmentFuture);
tunnel.queuedResponses.offer(new QueuedResponse(fragment,
fragmentFuture));
}
sendQueuedData(tunnel);
}
sendQueuedData(tunnel);
}
/**
* Used to pass the result received from one ChannelFutureListener to another verbatim.
*/
private final class RelayedChannelFutureListener implements ChannelFutureListener
{
private final ChannelFuture originalFuture;
/**
* Used to pass the result received from one ChannelFutureListener to another verbatim.
*/
private final class RelayedChannelFutureListener implements
ChannelFutureListener {
private final ChannelFuture originalFuture;
private RelayedChannelFutureListener(ChannelFuture originalFuture)
{
this.originalFuture = originalFuture;
}
private RelayedChannelFutureListener(ChannelFuture originalFuture) {
this.originalFuture = originalFuture;
}
public void operationComplete(ChannelFuture future) throws Exception
{
if (future.isSuccess())
{
originalFuture.setSuccess();
}
else
{
originalFuture.setFailure(future.getCause());
}
}
}
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
originalFuture.setSuccess();
} else {
originalFuture.setFailure(future.getCause());
}
}
}
private static final class TunnelInfo
{
public String tunnelId;
private static final class TunnelInfo {
public String tunnelId;
public HttpTunnelAcceptedChannelReceiver localChannel;
public HttpTunnelAcceptedChannelReceiver localChannel;
public final AtomicReference<Channel> responseChannel = new AtomicReference<Channel>(null);
public final AtomicReference<Channel> responseChannel =
new AtomicReference<Channel>(null);
public final Queue<QueuedResponse> queuedResponses = new ConcurrentLinkedQueue<QueuedResponse>();
public final Queue<QueuedResponse> queuedResponses =
new ConcurrentLinkedQueue<QueuedResponse>();
public final AtomicBoolean closing = new AtomicBoolean(false);
}
public final AtomicBoolean closing = new AtomicBoolean(false);
}
private static final class QueuedResponse
{
public ChannelBuffer data;
private static final class QueuedResponse {
public ChannelBuffer data;
public ChannelFuture writeFuture;
public ChannelFuture writeFuture;
QueuedResponse(ChannelBuffer data, ChannelFuture writeFuture)
{
this.data = data;
this.writeFuture = writeFuture;
}
}
QueuedResponse(ChannelBuffer data, ChannelFuture writeFuture) {
this.data = data;
this.writeFuture = writeFuture;
}
}
}

View File

@ -26,11 +26,11 @@ import org.jboss.netty.channel.ChannelFuture;
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
* @author OneDrum Ltd.
*/
interface ServerMessageSwitchDownstreamInterface
{
interface ServerMessageSwitchDownstreamInterface {
public void serverCloseTunnel(String tunnelId);
public void serverCloseTunnel(String tunnelId);
public void routeOutboundData(String tunnelId, ChannelBuffer data, ChannelFuture writeFuture);
public void routeOutboundData(String tunnelId, ChannelBuffer data,
ChannelFuture writeFuture);
}

View File

@ -30,28 +30,28 @@ import org.jboss.netty.channel.Channel;
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
* @author OneDrum Ltd.
*/
interface ServerMessageSwitchUpstreamInterface
{
interface ServerMessageSwitchUpstreamInterface {
public String createTunnel(InetSocketAddress remoteAddress);
public String createTunnel(InetSocketAddress remoteAddress);
public boolean isOpenTunnel(String tunnelId);
public boolean isOpenTunnel(String tunnelId);
public void clientCloseTunnel(String tunnelId);
public void clientCloseTunnel(String tunnelId);
/**
* Passes some received data from a client for forwarding to the server's view
* of the tunnel.
* @return the current status of the tunnel. ALIVE indicates the tunnel is still
* functional, CLOSED indicates it is closed and the client should be notified
* of this (and will be forgotten after this notification).
*/
public TunnelStatus routeInboundData(String tunnelId, ChannelBuffer inboundData);
/**
* Passes some received data from a client for forwarding to the server's view
* of the tunnel.
* @return the current status of the tunnel. ALIVE indicates the tunnel is still
* functional, CLOSED indicates it is closed and the client should be notified
* of this (and will be forgotten after this notification).
*/
public TunnelStatus routeInboundData(String tunnelId,
ChannelBuffer inboundData);
public void pollOutboundData(String tunnelId, Channel responseChannel);
public void pollOutboundData(String tunnelId, Channel responseChannel);
public static enum TunnelStatus {
ALIVE, CLOSED
}
public static enum TunnelStatus {
ALIVE, CLOSED
}
}

View File

@ -24,15 +24,14 @@ package org.jboss.netty.channel.socket.http;
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
* @author OneDrum Ltd.
*/
public interface TunnelIdGenerator
{
public interface TunnelIdGenerator {
/**
* Generates the next tunnel ID to be used, which must be unique
* (i.e. ensure with high probability that it will not clash with
* an existing tunnel ID). This method must be thread safe, and
* preferably lock free.
*/
public String generateId();
/**
* Generates the next tunnel ID to be used, which must be unique
* (i.e. ensure with high probability that it will not clash with
* an existing tunnel ID). This method must be thread safe, and
* preferably lock free.
*/
public String generateId();
}

View File

@ -29,56 +29,56 @@ import org.jboss.netty.channel.group.ChannelGroup;
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
* @author OneDrum Ltd.
*/
class TunnelWrappedServerChannelHandler extends SimpleChannelUpstreamHandler
{
class TunnelWrappedServerChannelHandler extends SimpleChannelUpstreamHandler {
public static final String NAME = "TunnelWrappedServerChannelHandler";
public static final String NAME = "TunnelWrappedServerChannelHandler";
private final HttpTunnelServerChannel tunnelChannel;
private final HttpTunnelServerChannel tunnelChannel;
private final AcceptedServerChannelPipelineFactory pipelineFactory;
private final AcceptedServerChannelPipelineFactory pipelineFactory;
private final ChannelGroup allChannels;
private final ChannelGroup allChannels;
public TunnelWrappedServerChannelHandler(HttpTunnelServerChannel tunnelChannel,
AcceptedServerChannelPipelineFactory pipelineFactory, ChannelGroup allChannels)
{
this.tunnelChannel = tunnelChannel;
this.pipelineFactory = pipelineFactory;
this.allChannels = allChannels;
}
public TunnelWrappedServerChannelHandler(
HttpTunnelServerChannel tunnelChannel,
AcceptedServerChannelPipelineFactory pipelineFactory,
ChannelGroup allChannels) {
this.tunnelChannel = tunnelChannel;
this.pipelineFactory = pipelineFactory;
this.allChannels = allChannels;
}
@Override
public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception
{
e.getChannel().getConfig().setPipelineFactory(pipelineFactory);
super.channelOpen(ctx, e);
}
@Override
public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e)
throws Exception {
e.getChannel().getConfig().setPipelineFactory(pipelineFactory);
super.channelOpen(ctx, e);
}
@Override
public void channelBound(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception
{
Channels.fireChannelBound(tunnelChannel, (SocketAddress) e.getValue());
super.channelBound(ctx, e);
}
@Override
public void channelBound(ChannelHandlerContext ctx, ChannelStateEvent e)
throws Exception {
Channels.fireChannelBound(tunnelChannel, (SocketAddress) e.getValue());
super.channelBound(ctx, e);
}
@Override
public void channelUnbound(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception
{
Channels.fireChannelUnbound(tunnelChannel);
super.channelUnbound(ctx, e);
}
@Override
public void channelUnbound(ChannelHandlerContext ctx, ChannelStateEvent e)
throws Exception {
Channels.fireChannelUnbound(tunnelChannel);
super.channelUnbound(ctx, e);
}
@Override
public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception
{
Channels.fireChannelClosed(tunnelChannel);
super.channelClosed(ctx, e);
}
@Override
public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e)
throws Exception {
Channels.fireChannelClosed(tunnelChannel);
super.channelClosed(ctx, e);
}
@Override
public void childChannelOpen(ChannelHandlerContext ctx, ChildChannelStateEvent e) throws Exception
{
allChannels.add(e.getChildChannel());
}
@Override
public void childChannelOpen(ChannelHandlerContext ctx,
ChildChannelStateEvent e) throws Exception {
allChannels.add(e.getChildChannel());
}
}

View File

@ -37,42 +37,38 @@ import org.jboss.netty.channel.SimpleChannelDownstreamHandler;
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
* @author OneDrum Ltd.
*/
public class WriteFragmenter extends SimpleChannelDownstreamHandler
{
public class WriteFragmenter extends SimpleChannelDownstreamHandler {
public static final String NAME = "writeFragmenter";
public static final String NAME = "writeFragmenter";
private int splitThreshold;
private int splitThreshold;
public WriteFragmenter(int splitThreshold)
{
this.splitThreshold = splitThreshold;
}
public WriteFragmenter(int splitThreshold) {
this.splitThreshold = splitThreshold;
}
public void setSplitThreshold(int splitThreshold)
{
this.splitThreshold = splitThreshold;
}
public void setSplitThreshold(int splitThreshold) {
this.splitThreshold = splitThreshold;
}
@Override
public void writeRequested(ChannelHandlerContext ctx, MessageEvent e) throws Exception
{
ChannelBuffer data = (ChannelBuffer) e.getMessage();
@Override
public void writeRequested(ChannelHandlerContext ctx, MessageEvent e)
throws Exception {
ChannelBuffer data = (ChannelBuffer) e.getMessage();
if (data.readableBytes() <= splitThreshold)
{
super.writeRequested(ctx, e);
}
else
{
List<ChannelBuffer> fragments = WriteSplitter.split(data, splitThreshold);
ChannelFutureAggregator aggregator = new ChannelFutureAggregator(e.getFuture());
for (ChannelBuffer fragment : fragments)
{
ChannelFuture fragmentFuture = Channels.future(ctx.getChannel(), true);
aggregator.addFuture(fragmentFuture);
Channels.write(ctx, fragmentFuture, fragment);
}
}
}
if (data.readableBytes() <= splitThreshold) {
super.writeRequested(ctx, e);
} else {
List<ChannelBuffer> fragments =
WriteSplitter.split(data, splitThreshold);
ChannelFutureAggregator aggregator =
new ChannelFutureAggregator(e.getFuture());
for (ChannelBuffer fragment: fragments) {
ChannelFuture fragmentFuture =
Channels.future(ctx.getChannel(), true);
aggregator.addFuture(fragmentFuture);
Channels.write(ctx, fragmentFuture, fragment);
}
}
}
}

View File

@ -29,31 +29,29 @@ import org.jboss.netty.buffer.ChannelBuffers;
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
* @author OneDrum Ltd.
*/
public final class WriteSplitter
{
public final class WriteSplitter {
public static List<ChannelBuffer> split(ChannelBuffer buffer, int splitThreshold)
{
int listSize = (int) ((float) buffer.readableBytes() / splitThreshold);
ArrayList<ChannelBuffer> fragmentList = new ArrayList<ChannelBuffer>(listSize);
public static List<ChannelBuffer> split(ChannelBuffer buffer,
int splitThreshold) {
int listSize = (int) ((float) buffer.readableBytes() / splitThreshold);
ArrayList<ChannelBuffer> fragmentList =
new ArrayList<ChannelBuffer>(listSize);
if (buffer.readableBytes() > splitThreshold)
{
int slicePosition = buffer.readerIndex();
while (slicePosition < buffer.writerIndex())
{
int chunkSize = Math.min(splitThreshold, buffer.writerIndex() - slicePosition);
ChannelBuffer chunk = buffer.slice(slicePosition, chunkSize);
fragmentList.add(chunk);
slicePosition += chunkSize;
}
}
else
{
fragmentList.add(ChannelBuffers.wrappedBuffer(buffer));
}
if (buffer.readableBytes() > splitThreshold) {
int slicePosition = buffer.readerIndex();
while (slicePosition < buffer.writerIndex()) {
int chunkSize =
Math.min(splitThreshold, buffer.writerIndex() -
slicePosition);
ChannelBuffer chunk = buffer.slice(slicePosition, chunkSize);
fragmentList.add(chunk);
slicePosition += chunkSize;
}
} else {
fragmentList.add(ChannelBuffers.wrappedBuffer(buffer));
}
return fragmentList;
}
return fragmentList;
}
}

View File

@ -41,207 +41,216 @@ import org.junit.runner.RunWith;
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
*/
@RunWith(JMock.class)
public class AcceptedServerChannelRequestDispatchTest
{
public class AcceptedServerChannelRequestDispatchTest {
private static final String HOST = "test.server.com";
private static final String HOST = "test.server.com";
private static final String KNOWN_TUNNEL_ID = "1";
private static final String KNOWN_TUNNEL_ID = "1";
protected static final String UNKNOWN_TUNNEL_ID = "unknownTunnel";
protected static final String UNKNOWN_TUNNEL_ID = "unknownTunnel";
JUnit4Mockery mockContext = new JUnit4Mockery();
JUnit4Mockery mockContext = new JUnit4Mockery();
private AcceptedServerChannelRequestDispatch handler;
private AcceptedServerChannelRequestDispatch handler;
FakeSocketChannel channel;
FakeSocketChannel channel;
private FakeChannelSink sink;
private FakeChannelSink sink;
ServerMessageSwitchUpstreamInterface messageSwitch;
ServerMessageSwitchUpstreamInterface messageSwitch;
@Before
public void setUp() throws Exception
{
ChannelPipeline pipeline = Channels.pipeline();
messageSwitch = mockContext.mock(ServerMessageSwitchUpstreamInterface.class);
handler = new AcceptedServerChannelRequestDispatch(messageSwitch);
pipeline.addLast(AcceptedServerChannelRequestDispatch.NAME, handler);
sink = new FakeChannelSink();
channel = new FakeSocketChannel(null, null, pipeline, sink);
channel.remoteAddress = InetSocketAddress.createUnresolved("test.client.com", 51231);
@Before
public void setUp() throws Exception {
ChannelPipeline pipeline = Channels.pipeline();
messageSwitch =
mockContext.mock(ServerMessageSwitchUpstreamInterface.class);
handler = new AcceptedServerChannelRequestDispatch(messageSwitch);
pipeline.addLast(AcceptedServerChannelRequestDispatch.NAME, handler);
sink = new FakeChannelSink();
channel = new FakeSocketChannel(null, null, pipeline, sink);
channel.remoteAddress =
InetSocketAddress.createUnresolved("test.client.com", 51231);
mockContext.checking(new Expectations()
{
{
ignoring(messageSwitch).isOpenTunnel(KNOWN_TUNNEL_ID);
will(returnValue(true));
}
});
}
mockContext.checking(new Expectations() {
{
ignoring(messageSwitch).isOpenTunnel(KNOWN_TUNNEL_ID);
will(returnValue(true));
}
});
}
@Test
public void testTunnelOpenRequest()
{
mockContext.checking(new Expectations()
{
{
one(messageSwitch).createTunnel(channel.remoteAddress);
will(returnValue(KNOWN_TUNNEL_ID));
}
});
@Test
public void testTunnelOpenRequest() {
mockContext.checking(new Expectations() {
{
one(messageSwitch).createTunnel(channel.remoteAddress);
will(returnValue(KNOWN_TUNNEL_ID));
}
});
Channels.fireMessageReceived(channel, HttpTunnelMessageUtils.createOpenTunnelRequest(HOST));
assertEquals(1, sink.events.size());
HttpResponse response = NettyTestUtils.checkIsDownstreamMessageEvent(sink.events.poll(), HttpResponse.class);
assertTrue(HttpTunnelMessageUtils.isTunnelOpenResponse(response));
}
Channels.fireMessageReceived(channel,
HttpTunnelMessageUtils.createOpenTunnelRequest(HOST));
assertEquals(1, sink.events.size());
HttpResponse response =
NettyTestUtils.checkIsDownstreamMessageEvent(
sink.events.poll(), HttpResponse.class);
assertTrue(HttpTunnelMessageUtils.isTunnelOpenResponse(response));
}
@Test
public void testTunnelCloseRequest()
{
mockContext.checking(new Expectations()
{
{
one(messageSwitch).clientCloseTunnel(KNOWN_TUNNEL_ID);
}
});
@Test
public void testTunnelCloseRequest() {
mockContext.checking(new Expectations() {
{
one(messageSwitch).clientCloseTunnel(KNOWN_TUNNEL_ID);
}
});
HttpRequest request = HttpTunnelMessageUtils.createCloseTunnelRequest(HOST, KNOWN_TUNNEL_ID);
Channels.fireMessageReceived(channel, request);
assertEquals(1, sink.events.size());
ChannelEvent responseEvent = sink.events.poll();
HttpResponse response = NettyTestUtils.checkIsDownstreamMessageEvent(responseEvent, HttpResponse.class);
assertTrue(HttpTunnelMessageUtils.isTunnelCloseResponse(response));
checkClosesAfterWrite(responseEvent);
}
HttpRequest request =
HttpTunnelMessageUtils.createCloseTunnelRequest(HOST,
KNOWN_TUNNEL_ID);
Channels.fireMessageReceived(channel, request);
assertEquals(1, sink.events.size());
ChannelEvent responseEvent = sink.events.poll();
HttpResponse response =
NettyTestUtils.checkIsDownstreamMessageEvent(responseEvent,
HttpResponse.class);
assertTrue(HttpTunnelMessageUtils.isTunnelCloseResponse(response));
checkClosesAfterWrite(responseEvent);
}
@Test
public void testTunnelCloseRequestWithoutTunnelIdRejected()
{
HttpRequest request = HttpTunnelMessageUtils.createCloseTunnelRequest(HOST, null);
checkRequestWithoutTunnelIdIsRejected(request);
}
@Test
public void testTunnelCloseRequestWithoutTunnelIdRejected() {
HttpRequest request =
HttpTunnelMessageUtils.createCloseTunnelRequest(HOST, null);
checkRequestWithoutTunnelIdIsRejected(request);
}
@Test
public void testTunnelCloseRequestWithUnknownTunnelId()
{
HttpRequest request = HttpTunnelMessageUtils.createCloseTunnelRequest(HOST, UNKNOWN_TUNNEL_ID);
checkRequestWithUnknownTunnelIdIsRejected(request);
}
@Test
public void testTunnelCloseRequestWithUnknownTunnelId() {
HttpRequest request =
HttpTunnelMessageUtils.createCloseTunnelRequest(HOST,
UNKNOWN_TUNNEL_ID);
checkRequestWithUnknownTunnelIdIsRejected(request);
}
@Test
public void testSendDataRequest()
{
final ChannelBuffer expectedData = ChannelBuffers.dynamicBuffer();
expectedData.writeLong(1234L);
mockContext.checking(new Expectations()
{
{
one(messageSwitch).routeInboundData(KNOWN_TUNNEL_ID, expectedData);
}
});
@Test
public void testSendDataRequest() {
final ChannelBuffer expectedData = ChannelBuffers.dynamicBuffer();
expectedData.writeLong(1234L);
mockContext.checking(new Expectations() {
{
one(messageSwitch).routeInboundData(KNOWN_TUNNEL_ID,
expectedData);
}
});
HttpRequest request = HttpTunnelMessageUtils.createSendDataRequest(HOST, KNOWN_TUNNEL_ID, expectedData);
Channels.fireMessageReceived(channel, request);
HttpRequest request =
HttpTunnelMessageUtils.createSendDataRequest(HOST,
KNOWN_TUNNEL_ID, expectedData);
Channels.fireMessageReceived(channel, request);
assertEquals(1, sink.events.size());
HttpResponse response = NettyTestUtils.checkIsDownstreamMessageEvent(sink.events.poll(), HttpResponse.class);
assertTrue(HttpTunnelMessageUtils.isOKResponse(response));
}
assertEquals(1, sink.events.size());
HttpResponse response =
NettyTestUtils.checkIsDownstreamMessageEvent(
sink.events.poll(), HttpResponse.class);
assertTrue(HttpTunnelMessageUtils.isOKResponse(response));
}
@Test
public void testSendDataRequestWithNoContentRejected()
{
HttpRequest request = HttpTunnelMessageUtils.createSendDataRequest(HOST, KNOWN_TUNNEL_ID,
ChannelBuffers.dynamicBuffer());
Channels.fireMessageReceived(channel, request);
@Test
public void testSendDataRequestWithNoContentRejected() {
HttpRequest request =
HttpTunnelMessageUtils.createSendDataRequest(HOST,
KNOWN_TUNNEL_ID, ChannelBuffers.dynamicBuffer());
Channels.fireMessageReceived(channel, request);
assertEquals(1, sink.events.size());
checkResponseIsRejection("Send data requests must contain data");
}
assertEquals(1, sink.events.size());
checkResponseIsRejection("Send data requests must contain data");
}
@Test
public void testSendDataRequestForUnknownTunnelIdRejected()
{
HttpRequest request = HttpTunnelMessageUtils.createSendDataRequest(HOST, UNKNOWN_TUNNEL_ID,
ChannelBuffers.dynamicBuffer());
checkRequestWithUnknownTunnelIdIsRejected(request);
}
@Test
public void testSendDataRequestForUnknownTunnelIdRejected() {
HttpRequest request =
HttpTunnelMessageUtils.createSendDataRequest(HOST,
UNKNOWN_TUNNEL_ID, ChannelBuffers.dynamicBuffer());
checkRequestWithUnknownTunnelIdIsRejected(request);
}
@Test
public void testSendDataRequestWithoutTunnelIdRejected()
{
HttpRequest request = HttpTunnelMessageUtils.createSendDataRequest(HOST, null, ChannelBuffers.dynamicBuffer());
checkRequestWithoutTunnelIdIsRejected(request);
}
@Test
public void testSendDataRequestWithoutTunnelIdRejected() {
HttpRequest request =
HttpTunnelMessageUtils.createSendDataRequest(HOST, null,
ChannelBuffers.dynamicBuffer());
checkRequestWithoutTunnelIdIsRejected(request);
}
@Test
public void testReceiveDataRequest()
{
mockContext.checking(new Expectations()
{
{
one(messageSwitch).pollOutboundData(KNOWN_TUNNEL_ID, channel);
}
});
HttpRequest request = HttpTunnelMessageUtils.createReceiveDataRequest(HOST, KNOWN_TUNNEL_ID);
Channels.fireMessageReceived(channel, request);
}
@Test
public void testReceiveDataRequest() {
mockContext.checking(new Expectations() {
{
one(messageSwitch).pollOutboundData(KNOWN_TUNNEL_ID, channel);
}
});
HttpRequest request =
HttpTunnelMessageUtils.createReceiveDataRequest(HOST,
KNOWN_TUNNEL_ID);
Channels.fireMessageReceived(channel, request);
}
@Test
public void testReceiveDataRequestWithoutTunnelIdRejected()
{
HttpRequest request = HttpTunnelMessageUtils.createReceiveDataRequest(HOST, null);
checkRequestWithoutTunnelIdIsRejected(request);
}
@Test
public void testReceiveDataRequestWithoutTunnelIdRejected() {
HttpRequest request =
HttpTunnelMessageUtils.createReceiveDataRequest(HOST, null);
checkRequestWithoutTunnelIdIsRejected(request);
}
@Test
public void testReceiveDataRequestForUnknownTunnelIdRejected()
{
HttpRequest request = HttpTunnelMessageUtils.createReceiveDataRequest(HOST, UNKNOWN_TUNNEL_ID);
checkRequestWithUnknownTunnelIdIsRejected(request);
}
@Test
public void testReceiveDataRequestForUnknownTunnelIdRejected() {
HttpRequest request =
HttpTunnelMessageUtils.createReceiveDataRequest(HOST,
UNKNOWN_TUNNEL_ID);
checkRequestWithUnknownTunnelIdIsRejected(request);
}
private void checkRequestWithoutTunnelIdIsRejected(HttpRequest request)
{
Channels.fireMessageReceived(channel, request);
assertEquals(1, sink.events.size());
ChannelEvent responseEvent = checkResponseIsRejection("no tunnel id specified in request");
checkClosesAfterWrite(responseEvent);
}
private void checkRequestWithoutTunnelIdIsRejected(HttpRequest request) {
Channels.fireMessageReceived(channel, request);
assertEquals(1, sink.events.size());
ChannelEvent responseEvent =
checkResponseIsRejection("no tunnel id specified in request");
checkClosesAfterWrite(responseEvent);
}
private void checkRequestWithUnknownTunnelIdIsRejected(HttpRequest request)
{
mockContext.checking(new Expectations()
{
{
one(messageSwitch).isOpenTunnel(UNKNOWN_TUNNEL_ID);
will(returnValue(false));
}
});
private void checkRequestWithUnknownTunnelIdIsRejected(HttpRequest request) {
mockContext.checking(new Expectations() {
{
one(messageSwitch).isOpenTunnel(UNKNOWN_TUNNEL_ID);
will(returnValue(false));
}
});
Channels.fireMessageReceived(channel, request);
assertEquals(1, sink.events.size());
ChannelEvent responseEvent = checkResponseIsRejection("specified tunnel is either closed or does not exist");
checkClosesAfterWrite(responseEvent);
}
Channels.fireMessageReceived(channel, request);
assertEquals(1, sink.events.size());
ChannelEvent responseEvent =
checkResponseIsRejection("specified tunnel is either closed or does not exist");
checkClosesAfterWrite(responseEvent);
}
private ChannelEvent checkResponseIsRejection(String errorMessage)
{
ChannelEvent responseEvent = sink.events.poll();
private ChannelEvent checkResponseIsRejection(String errorMessage) {
ChannelEvent responseEvent = sink.events.poll();
HttpResponse response = NettyTestUtils.checkIsDownstreamMessageEvent(responseEvent, HttpResponse.class);
assertTrue(HttpTunnelMessageUtils.isRejection(response));
assertEquals(errorMessage, HttpTunnelMessageUtils.extractErrorMessage(response));
HttpResponse response =
NettyTestUtils.checkIsDownstreamMessageEvent(responseEvent,
HttpResponse.class);
assertTrue(HttpTunnelMessageUtils.isRejection(response));
assertEquals(errorMessage,
HttpTunnelMessageUtils.extractErrorMessage(response));
return responseEvent;
}
return responseEvent;
}
private void checkClosesAfterWrite(ChannelEvent responseEvent)
{
responseEvent.getFuture().setSuccess();
assertEquals(1, sink.events.size());
NettyTestUtils.checkIsStateEvent(sink.events.poll(), ChannelState.OPEN, false);
}
private void checkClosesAfterWrite(ChannelEvent responseEvent) {
responseEvent.getFuture().setSuccess();
assertEquals(1, sink.events.size());
NettyTestUtils.checkIsStateEvent(sink.events.poll(), ChannelState.OPEN,
false);
}
}

View File

@ -31,207 +31,159 @@ import org.jboss.netty.util.internal.ConversionUtil;
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
*/
public class FakeChannelConfig implements SocketChannelConfig
{
public class FakeChannelConfig implements SocketChannelConfig {
private int receiveBufferSize = 1024;
private int receiveBufferSize = 1024;
private int sendBufferSize = 1024;
private int sendBufferSize = 1024;
private int soLinger = 500;
private int soLinger = 500;
private int trafficClass = 0;
private int trafficClass = 0;
private boolean keepAlive = true;
private boolean keepAlive = true;
private boolean reuseAddress = true;
private boolean reuseAddress = true;
private boolean tcpNoDelay = false;
private boolean tcpNoDelay = false;
private ChannelBufferFactory bufferFactory = new HeapChannelBufferFactory();
private ChannelBufferFactory bufferFactory = new HeapChannelBufferFactory();
private int connectTimeout = 5000;
private int connectTimeout = 5000;
private ChannelPipelineFactory pipelineFactory = new ChannelPipelineFactory()
{
public ChannelPipeline getPipeline() throws Exception
{
return Channels.pipeline();
}
};
private ChannelPipelineFactory pipelineFactory =
new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() throws Exception {
return Channels.pipeline();
}
};
private int writeTimeout = 3000;
private int writeTimeout = 3000;
public int getReceiveBufferSize()
{
return receiveBufferSize;
}
public int getReceiveBufferSize() {
return receiveBufferSize;
}
public void setReceiveBufferSize(int receiveBufferSize)
{
this.receiveBufferSize = receiveBufferSize;
}
public void setReceiveBufferSize(int receiveBufferSize) {
this.receiveBufferSize = receiveBufferSize;
}
public int getSendBufferSize()
{
return sendBufferSize;
}
public int getSendBufferSize() {
return sendBufferSize;
}
public void setSendBufferSize(int sendBufferSize)
{
this.sendBufferSize = sendBufferSize;
}
public void setSendBufferSize(int sendBufferSize) {
this.sendBufferSize = sendBufferSize;
}
public int getSoLinger()
{
return soLinger;
}
public int getSoLinger() {
return soLinger;
}
public void setSoLinger(int soLinger)
{
this.soLinger = soLinger;
}
public void setSoLinger(int soLinger) {
this.soLinger = soLinger;
}
public int getTrafficClass()
{
return trafficClass;
}
public int getTrafficClass() {
return trafficClass;
}
public void setTrafficClass(int trafficClass)
{
this.trafficClass = trafficClass;
}
public void setTrafficClass(int trafficClass) {
this.trafficClass = trafficClass;
}
public boolean isKeepAlive()
{
return keepAlive;
}
public boolean isKeepAlive() {
return keepAlive;
}
public void setKeepAlive(boolean keepAlive)
{
this.keepAlive = keepAlive;
}
public void setKeepAlive(boolean keepAlive) {
this.keepAlive = keepAlive;
}
public boolean isReuseAddress()
{
return reuseAddress;
}
public boolean isReuseAddress() {
return reuseAddress;
}
public void setReuseAddress(boolean reuseAddress)
{
this.reuseAddress = reuseAddress;
}
public void setReuseAddress(boolean reuseAddress) {
this.reuseAddress = reuseAddress;
}
public boolean isTcpNoDelay()
{
return tcpNoDelay;
}
public boolean isTcpNoDelay() {
return tcpNoDelay;
}
public void setTcpNoDelay(boolean tcpNoDelay)
{
this.tcpNoDelay = tcpNoDelay;
}
public void setTcpNoDelay(boolean tcpNoDelay) {
this.tcpNoDelay = tcpNoDelay;
}
public void setPerformancePreferences(int connectionTime, int latency, int bandwidth)
{
// do nothing
}
public void setPerformancePreferences(int connectionTime, int latency,
int bandwidth) {
// do nothing
}
public ChannelBufferFactory getBufferFactory()
{
return bufferFactory;
}
public ChannelBufferFactory getBufferFactory() {
return bufferFactory;
}
public void setBufferFactory(ChannelBufferFactory bufferFactory)
{
this.bufferFactory = bufferFactory;
}
public void setBufferFactory(ChannelBufferFactory bufferFactory) {
this.bufferFactory = bufferFactory;
}
public int getConnectTimeoutMillis()
{
return connectTimeout;
}
public int getConnectTimeoutMillis() {
return connectTimeout;
}
public void setConnectTimeoutMillis(int connectTimeoutMillis)
{
connectTimeout = connectTimeoutMillis;
}
public void setConnectTimeoutMillis(int connectTimeoutMillis) {
connectTimeout = connectTimeoutMillis;
}
public ChannelPipelineFactory getPipelineFactory()
{
return pipelineFactory;
}
public ChannelPipelineFactory getPipelineFactory() {
return pipelineFactory;
}
public void setPipelineFactory(ChannelPipelineFactory pipelineFactory)
{
this.pipelineFactory = pipelineFactory;
}
public void setPipelineFactory(ChannelPipelineFactory pipelineFactory) {
this.pipelineFactory = pipelineFactory;
}
public int getWriteTimeoutMillis()
{
return writeTimeout;
}
public int getWriteTimeoutMillis() {
return writeTimeout;
}
public void setWriteTimeoutMillis(int writeTimeoutMillis)
{
writeTimeout = writeTimeoutMillis;
}
public void setWriteTimeoutMillis(int writeTimeoutMillis) {
writeTimeout = writeTimeoutMillis;
}
public boolean setOption(String key, Object value)
{
if (key.equals("pipelineFactory"))
{
setPipelineFactory((ChannelPipelineFactory) value);
}
else if (key.equals("connectTimeoutMillis"))
{
setConnectTimeoutMillis(ConversionUtil.toInt(value));
}
else if (key.equals("bufferFactory"))
{
setBufferFactory((ChannelBufferFactory) value);
}
else if (key.equals("receiveBufferSize"))
{
setReceiveBufferSize(ConversionUtil.toInt(value));
}
else if (key.equals("sendBufferSize"))
{
setSendBufferSize(ConversionUtil.toInt(value));
}
else if (key.equals("tcpNoDelay"))
{
setTcpNoDelay(ConversionUtil.toBoolean(value));
}
else if (key.equals("keepAlive"))
{
setKeepAlive(ConversionUtil.toBoolean(value));
}
else if (key.equals("reuseAddress"))
{
setReuseAddress(ConversionUtil.toBoolean(value));
}
else if (key.equals("soLinger"))
{
setSoLinger(ConversionUtil.toInt(value));
}
else if (key.equals("trafficClass"))
{
setTrafficClass(ConversionUtil.toInt(value));
}
else
{
return false;
}
return true;
}
public boolean setOption(String key, Object value) {
if (key.equals("pipelineFactory")) {
setPipelineFactory((ChannelPipelineFactory) value);
} else if (key.equals("connectTimeoutMillis")) {
setConnectTimeoutMillis(ConversionUtil.toInt(value));
} else if (key.equals("bufferFactory")) {
setBufferFactory((ChannelBufferFactory) value);
} else if (key.equals("receiveBufferSize")) {
setReceiveBufferSize(ConversionUtil.toInt(value));
} else if (key.equals("sendBufferSize")) {
setSendBufferSize(ConversionUtil.toInt(value));
} else if (key.equals("tcpNoDelay")) {
setTcpNoDelay(ConversionUtil.toBoolean(value));
} else if (key.equals("keepAlive")) {
setKeepAlive(ConversionUtil.toBoolean(value));
} else if (key.equals("reuseAddress")) {
setReuseAddress(ConversionUtil.toBoolean(value));
} else if (key.equals("soLinger")) {
setSoLinger(ConversionUtil.toInt(value));
} else if (key.equals("trafficClass")) {
setTrafficClass(ConversionUtil.toInt(value));
} else {
return false;
}
return true;
}
public void setOptions(Map<String, Object> options)
{
for (Entry<String, Object> e : options.entrySet())
{
setOption(e.getKey(), e.getValue());
}
}
public void setOptions(Map<String, Object> options) {
for (Entry<String, Object> e: options.entrySet()) {
setOption(e.getKey(), e.getValue());
}
}
}

View File

@ -27,14 +27,13 @@ import org.jboss.netty.channel.ChannelPipeline;
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
*/
public class FakeChannelSink extends AbstractChannelSink
{
public class FakeChannelSink extends AbstractChannelSink {
public Queue<ChannelEvent> events = new LinkedList<ChannelEvent>();
public Queue<ChannelEvent> events = new LinkedList<ChannelEvent>();
public void eventSunk(ChannelPipeline pipeline, ChannelEvent e) throws Exception
{
events.add(e);
}
public void eventSunk(ChannelPipeline pipeline, ChannelEvent e)
throws Exception {
events.add(e);
}
}

View File

@ -27,26 +27,25 @@ import org.jboss.netty.channel.socket.SocketChannel;
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
*/
public class FakeClientSocketChannelFactory implements ClientSocketChannelFactory
{
public class FakeClientSocketChannelFactory implements
ClientSocketChannelFactory {
public List<FakeSocketChannel> createdChannels;
public List<FakeSocketChannel> createdChannels;
public FakeClientSocketChannelFactory()
{
createdChannels = new ArrayList<FakeSocketChannel>();
}
public FakeClientSocketChannelFactory() {
createdChannels = new ArrayList<FakeSocketChannel>();
}
public SocketChannel newChannel(ChannelPipeline pipeline)
{
FakeSocketChannel channel = new FakeSocketChannel(null, this, pipeline, new FakeChannelSink());
createdChannels.add(channel);
return channel;
}
public SocketChannel newChannel(ChannelPipeline pipeline) {
FakeSocketChannel channel =
new FakeSocketChannel(null, this, pipeline,
new FakeChannelSink());
createdChannels.add(channel);
return channel;
}
public void releaseExternalResources()
{
// nothing to do
}
public void releaseExternalResources() {
// nothing to do
}
}

View File

@ -33,60 +33,58 @@ import org.jboss.netty.channel.socket.ServerSocketChannelConfig;
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
*/
public class FakeServerSocketChannel extends AbstractChannel implements ServerSocketChannel
{
public class FakeServerSocketChannel extends AbstractChannel implements
ServerSocketChannel {
public boolean bound;
public boolean bound;
public boolean connected;
public boolean connected;
public InetSocketAddress remoteAddress;
public InetSocketAddress remoteAddress;
public InetSocketAddress localAddress;
public InetSocketAddress localAddress;
public ServerSocketChannelConfig config = new FakeServerSocketChannelConfig();
public ServerSocketChannelConfig config =
new FakeServerSocketChannelConfig();
public FakeServerSocketChannel(ChannelFactory factory, ChannelPipeline pipeline, ChannelSink sink)
{
super(null, factory, pipeline, sink);
}
public FakeServerSocketChannel(ChannelFactory factory,
ChannelPipeline pipeline, ChannelSink sink) {
super(null, factory, pipeline, sink);
}
public ServerSocketChannelConfig getConfig()
{
return config;
}
public ServerSocketChannelConfig getConfig() {
return config;
}
public InetSocketAddress getLocalAddress()
{
return localAddress;
}
public InetSocketAddress getLocalAddress() {
return localAddress;
}
public InetSocketAddress getRemoteAddress()
{
return remoteAddress;
}
public InetSocketAddress getRemoteAddress() {
return remoteAddress;
}
public boolean isBound()
{
return bound;
}
public boolean isBound() {
return bound;
}
public boolean isConnected()
{
return connected;
}
public boolean isConnected() {
return connected;
}
public FakeSocketChannel acceptNewConnection(InetSocketAddress remoteAddress, ChannelSink sink) throws Exception
{
ChannelPipeline newPipeline = getConfig().getPipelineFactory().getPipeline();
FakeSocketChannel newChannel = new FakeSocketChannel(this, getFactory(), newPipeline, sink);
newChannel.localAddress = localAddress;
newChannel.remoteAddress = remoteAddress;
fireChannelOpen(newChannel);
fireChannelBound(newChannel, newChannel.localAddress);
fireChannelConnected(this, newChannel.remoteAddress);
public FakeSocketChannel acceptNewConnection(
InetSocketAddress remoteAddress, ChannelSink sink) throws Exception {
ChannelPipeline newPipeline =
getConfig().getPipelineFactory().getPipeline();
FakeSocketChannel newChannel =
new FakeSocketChannel(this, getFactory(), newPipeline, sink);
newChannel.localAddress = localAddress;
newChannel.remoteAddress = remoteAddress;
fireChannelOpen(newChannel);
fireChannelBound(newChannel, newChannel.localAddress);
fireChannelConnected(this, newChannel.remoteAddress);
return newChannel;
}
return newChannel;
}
}

View File

@ -26,55 +26,49 @@ import org.jboss.netty.channel.socket.ServerSocketChannelConfig;
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
*/
public class FakeServerSocketChannelConfig extends DefaultChannelConfig implements ServerSocketChannelConfig
{
public class FakeServerSocketChannelConfig extends DefaultChannelConfig
implements ServerSocketChannelConfig {
public int backlog = 5;
public int backlog = 5;
public int receiveBufferSize = 1024;
public int receiveBufferSize = 1024;
public boolean reuseAddress = false;
public boolean reuseAddress = false;
public int connectionTimeout = 5000;
public int connectionTimeout = 5000;
public ChannelPipelineFactory pipelineFactory;
public ChannelPipelineFactory pipelineFactory;
public int writeTimeout = 5000;
public int writeTimeout = 5000;
public ChannelBufferFactory bufferFactory = new HeapChannelBufferFactory();
public ChannelBufferFactory bufferFactory = new HeapChannelBufferFactory();
public int getBacklog()
{
return backlog;
}
public int getBacklog() {
return backlog;
}
public void setBacklog(int backlog)
{
this.backlog = backlog;
}
public void setBacklog(int backlog) {
this.backlog = backlog;
}
public int getReceiveBufferSize()
{
return receiveBufferSize;
}
public int getReceiveBufferSize() {
return receiveBufferSize;
}
public void setReceiveBufferSize(int receiveBufferSize)
{
this.receiveBufferSize = receiveBufferSize;
}
public void setReceiveBufferSize(int receiveBufferSize) {
this.receiveBufferSize = receiveBufferSize;
}
public boolean isReuseAddress()
{
return reuseAddress;
}
public boolean isReuseAddress() {
return reuseAddress;
}
public void setReuseAddress(boolean reuseAddress)
{
this.reuseAddress = reuseAddress;
}
public void setReuseAddress(boolean reuseAddress) {
this.reuseAddress = reuseAddress;
}
public void setPerformancePreferences(int connectionTime, int latency, int bandwidth)
{
// ignore
}
public void setPerformancePreferences(int connectionTime, int latency,
int bandwidth) {
// ignore
}
}

View File

@ -25,22 +25,20 @@ import org.jboss.netty.channel.socket.ServerSocketChannelFactory;
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
*/
public class FakeServerSocketChannelFactory implements ServerSocketChannelFactory
{
public class FakeServerSocketChannelFactory implements
ServerSocketChannelFactory {
public ChannelSink sink = new FakeChannelSink();
public ChannelSink sink = new FakeChannelSink();
public FakeServerSocketChannel createdChannel;
public FakeServerSocketChannel createdChannel;
public ServerSocketChannel newChannel(ChannelPipeline pipeline)
{
createdChannel = new FakeServerSocketChannel(this, pipeline, sink);
return createdChannel;
}
public ServerSocketChannel newChannel(ChannelPipeline pipeline) {
createdChannel = new FakeServerSocketChannel(this, pipeline, sink);
return createdChannel;
}
public void releaseExternalResources()
{
// nothing to do
}
public void releaseExternalResources() {
// nothing to do
}
}

View File

@ -32,84 +32,73 @@ import org.jboss.netty.channel.socket.SocketChannelConfig;
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
*/
public class FakeSocketChannel extends AbstractChannel implements SocketChannel
{
public class FakeSocketChannel extends AbstractChannel implements SocketChannel {
public InetSocketAddress localAddress;
public InetSocketAddress localAddress;
public InetSocketAddress remoteAddress;
public InetSocketAddress remoteAddress;
public SocketChannelConfig config = new FakeChannelConfig();
public SocketChannelConfig config = new FakeChannelConfig();
public boolean bound = false;
public boolean bound = false;
public boolean connected = false;
public boolean connected = false;
public ChannelSink sink;
public ChannelSink sink;
public FakeSocketChannel(Channel parent, ChannelFactory factory, ChannelPipeline pipeline, ChannelSink sink)
{
super(parent, factory, pipeline, sink);
this.sink = sink;
}
public FakeSocketChannel(Channel parent, ChannelFactory factory,
ChannelPipeline pipeline, ChannelSink sink) {
super(parent, factory, pipeline, sink);
this.sink = sink;
}
public InetSocketAddress getLocalAddress()
{
return localAddress;
}
public InetSocketAddress getLocalAddress() {
return localAddress;
}
public SocketChannelConfig getConfig()
{
return config;
}
public SocketChannelConfig getConfig() {
return config;
}
public InetSocketAddress getRemoteAddress()
{
return remoteAddress;
}
public InetSocketAddress getRemoteAddress() {
return remoteAddress;
}
public boolean isBound()
{
return bound;
}
public boolean isBound() {
return bound;
}
public boolean isConnected()
{
return connected;
}
public boolean isConnected() {
return connected;
}
public void emulateConnected(InetSocketAddress localAddress, InetSocketAddress remoteAddress,
ChannelFuture connectedFuture)
{
if (connected)
{
return;
}
public void emulateConnected(InetSocketAddress localAddress,
InetSocketAddress remoteAddress, ChannelFuture connectedFuture) {
if (connected) {
return;
}
emulateBound(localAddress, null);
this.remoteAddress = remoteAddress;
connected = true;
Channels.fireChannelConnected(this, remoteAddress);
if (connectedFuture != null)
{
connectedFuture.setSuccess();
}
}
emulateBound(localAddress, null);
this.remoteAddress = remoteAddress;
connected = true;
Channels.fireChannelConnected(this, remoteAddress);
if (connectedFuture != null) {
connectedFuture.setSuccess();
}
}
public void emulateBound(InetSocketAddress localAddress, ChannelFuture boundFuture)
{
if (bound)
{
return;
}
public void emulateBound(InetSocketAddress localAddress,
ChannelFuture boundFuture) {
if (bound) {
return;
}
bound = true;
this.localAddress = localAddress;
Channels.fireChannelBound(this, localAddress);
if (boundFuture != null)
{
boundFuture.setSuccess();
}
}
bound = true;
this.localAddress = localAddress;
Channels.fireChannelBound(this, localAddress);
if (boundFuture != null) {
boundFuture.setSuccess();
}
}
}

View File

@ -32,63 +32,59 @@ import org.junit.runner.RunWith;
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
*/
@RunWith(JMock.class)
public class HttpTunnelAcceptedChannelSinkTest
{
public class HttpTunnelAcceptedChannelSinkTest {
private static final String TUNNEL_ID = "1";
private static final String TUNNEL_ID = "1";
private final JUnit4Mockery mockContext = new JUnit4Mockery();
private final JUnit4Mockery mockContext = new JUnit4Mockery();
ServerMessageSwitchDownstreamInterface messageSwitch;
ServerMessageSwitchDownstreamInterface messageSwitch;
private HttpTunnelAcceptedChannelSink sink;
private HttpTunnelAcceptedChannelSink sink;
private FakeSocketChannel channel;
private FakeSocketChannel channel;
private UpstreamEventCatcher upstreamCatcher;
private UpstreamEventCatcher upstreamCatcher;
@Before
public void setUp() throws Exception
{
messageSwitch = mockContext.mock(ServerMessageSwitchDownstreamInterface.class);
sink = new HttpTunnelAcceptedChannelSink(messageSwitch, TUNNEL_ID, new HttpTunnelAcceptedChannelConfig());
ChannelPipeline pipeline = Channels.pipeline();
upstreamCatcher = new UpstreamEventCatcher();
pipeline.addLast(UpstreamEventCatcher.NAME, upstreamCatcher);
channel = new FakeSocketChannel(null, null, pipeline, sink);
upstreamCatcher.events.clear();
}
@Before
public void setUp() throws Exception {
messageSwitch =
mockContext.mock(ServerMessageSwitchDownstreamInterface.class);
sink =
new HttpTunnelAcceptedChannelSink(messageSwitch, TUNNEL_ID,
new HttpTunnelAcceptedChannelConfig());
ChannelPipeline pipeline = Channels.pipeline();
upstreamCatcher = new UpstreamEventCatcher();
pipeline.addLast(UpstreamEventCatcher.NAME, upstreamCatcher);
channel = new FakeSocketChannel(null, null, pipeline, sink);
upstreamCatcher.events.clear();
}
@Test
public void testSendInvalidDataType()
{
Channels.write(channel, new Object());
assertEquals(1, upstreamCatcher.events.size());
NettyTestUtils.checkIsExceptionEvent(upstreamCatcher.events.poll());
}
@Test
public void testSendInvalidDataType() {
Channels.write(channel, new Object());
assertEquals(1, upstreamCatcher.events.size());
NettyTestUtils.checkIsExceptionEvent(upstreamCatcher.events.poll());
}
@Test
public void testUnbind()
{
mockContext.checking(new Expectations()
{
{
one(messageSwitch).serverCloseTunnel(TUNNEL_ID);
}
});
Channels.unbind(channel);
}
@Test
public void testUnbind() {
mockContext.checking(new Expectations() {
{
one(messageSwitch).serverCloseTunnel(TUNNEL_ID);
}
});
Channels.unbind(channel);
}
@Test
public void testDisconnect()
{
mockContext.checking(new Expectations()
{
{
one(messageSwitch).serverCloseTunnel(TUNNEL_ID);
}
});
@Test
public void testDisconnect() {
mockContext.checking(new Expectations() {
{
one(messageSwitch).serverCloseTunnel(TUNNEL_ID);
}
});
Channels.disconnect(channel);
}
Channels.disconnect(channel);
}
}

View File

@ -33,297 +33,262 @@ import org.junit.runner.RunWith;
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
*/
@RunWith(JMock.class)
public class HttpTunnelClientChannelConfigTest
{
public class HttpTunnelClientChannelConfigTest {
JUnit4Mockery mockContext = new JUnit4Mockery();
JUnit4Mockery mockContext = new JUnit4Mockery();
SocketChannelConfig sendChannelConfig;
SocketChannelConfig sendChannelConfig;
SocketChannelConfig pollChannelConfig;
SocketChannelConfig pollChannelConfig;
HttpTunnelClientChannelConfig config;
HttpTunnelClientChannelConfig config;
@Before
public void setUp()
{
sendChannelConfig = mockContext.mock(SocketChannelConfig.class, "sendChannelConfig");
pollChannelConfig = mockContext.mock(SocketChannelConfig.class, "pollChannelConfig");
@Before
public void setUp() {
sendChannelConfig =
mockContext
.mock(SocketChannelConfig.class, "sendChannelConfig");
pollChannelConfig =
mockContext
.mock(SocketChannelConfig.class, "pollChannelConfig");
config = new HttpTunnelClientChannelConfig(sendChannelConfig, pollChannelConfig);
}
config =
new HttpTunnelClientChannelConfig(sendChannelConfig,
pollChannelConfig);
}
@Test
public void testGetReceiveBufferSize()
{
mockContext.checking(new Expectations()
{
{
one(pollChannelConfig).getReceiveBufferSize();
will(returnValue(100));
}
});
@Test
public void testGetReceiveBufferSize() {
mockContext.checking(new Expectations() {
{
one(pollChannelConfig).getReceiveBufferSize();
will(returnValue(100));
}
});
assertEquals(100, config.getReceiveBufferSize());
}
assertEquals(100, config.getReceiveBufferSize());
}
@Test
public void testGetSendBufferSize()
{
mockContext.checking(new Expectations()
{
{
one(pollChannelConfig).getSendBufferSize();
will(returnValue(100));
}
});
@Test
public void testGetSendBufferSize() {
mockContext.checking(new Expectations() {
{
one(pollChannelConfig).getSendBufferSize();
will(returnValue(100));
}
});
assertEquals(100, config.getSendBufferSize());
}
assertEquals(100, config.getSendBufferSize());
}
@Test
public void testGetSoLinger()
{
mockContext.checking(new Expectations()
{
{
one(pollChannelConfig).getSoLinger();
will(returnValue(100));
}
});
@Test
public void testGetSoLinger() {
mockContext.checking(new Expectations() {
{
one(pollChannelConfig).getSoLinger();
will(returnValue(100));
}
});
assertEquals(100, config.getSoLinger());
}
assertEquals(100, config.getSoLinger());
}
@Test
public void testTrafficClass()
{
mockContext.checking(new Expectations()
{
{
one(pollChannelConfig).getTrafficClass();
will(returnValue(1));
}
});
@Test
public void testTrafficClass() {
mockContext.checking(new Expectations() {
{
one(pollChannelConfig).getTrafficClass();
will(returnValue(1));
}
});
assertEquals(1, config.getTrafficClass());
}
assertEquals(1, config.getTrafficClass());
}
@Test
public void testIsKeepAlive()
{
mockContext.checking(new Expectations()
{
{
one(pollChannelConfig).isKeepAlive();
will(returnValue(true));
}
});
@Test
public void testIsKeepAlive() {
mockContext.checking(new Expectations() {
{
one(pollChannelConfig).isKeepAlive();
will(returnValue(true));
}
});
assertTrue(config.isKeepAlive());
}
assertTrue(config.isKeepAlive());
}
@Test
public void testIsReuseAddress()
{
mockContext.checking(new Expectations()
{
{
one(pollChannelConfig).isReuseAddress();
will(returnValue(true));
}
});
@Test
public void testIsReuseAddress() {
mockContext.checking(new Expectations() {
{
one(pollChannelConfig).isReuseAddress();
will(returnValue(true));
}
});
assertTrue(config.isReuseAddress());
}
assertTrue(config.isReuseAddress());
}
@Test
public void testIsTcpNoDelay()
{
mockContext.checking(new Expectations()
{
{
one(pollChannelConfig).isTcpNoDelay();
will(returnValue(true));
}
});
@Test
public void testIsTcpNoDelay() {
mockContext.checking(new Expectations() {
{
one(pollChannelConfig).isTcpNoDelay();
will(returnValue(true));
}
});
assertTrue(config.isTcpNoDelay());
}
assertTrue(config.isTcpNoDelay());
}
@Test
public void testSetKeepAlive()
{
mockContext.checking(new Expectations()
{
{
one(pollChannelConfig).setKeepAlive(true);
one(sendChannelConfig).setKeepAlive(true);
}
});
@Test
public void testSetKeepAlive() {
mockContext.checking(new Expectations() {
{
one(pollChannelConfig).setKeepAlive(true);
one(sendChannelConfig).setKeepAlive(true);
}
});
config.setKeepAlive(true);
}
config.setKeepAlive(true);
}
@Test
public void testSetPerformancePreferences()
{
mockContext.checking(new Expectations()
{
{
one(pollChannelConfig).setPerformancePreferences(100, 200, 300);
one(sendChannelConfig).setPerformancePreferences(100, 200, 300);
}
});
@Test
public void testSetPerformancePreferences() {
mockContext.checking(new Expectations() {
{
one(pollChannelConfig).setPerformancePreferences(100, 200, 300);
one(sendChannelConfig).setPerformancePreferences(100, 200, 300);
}
});
config.setPerformancePreferences(100, 200, 300);
}
config.setPerformancePreferences(100, 200, 300);
}
@Test
public void testSetReceiveBufferSize()
{
mockContext.checking(new Expectations()
{
{
one(pollChannelConfig).setReceiveBufferSize(100);
one(sendChannelConfig).setReceiveBufferSize(100);
}
});
@Test
public void testSetReceiveBufferSize() {
mockContext.checking(new Expectations() {
{
one(pollChannelConfig).setReceiveBufferSize(100);
one(sendChannelConfig).setReceiveBufferSize(100);
}
});
config.setReceiveBufferSize(100);
}
config.setReceiveBufferSize(100);
}
@Test
public void testSetReuseAddress()
{
mockContext.checking(new Expectations()
{
{
one(pollChannelConfig).setReuseAddress(true);
one(sendChannelConfig).setReuseAddress(true);
}
});
@Test
public void testSetReuseAddress() {
mockContext.checking(new Expectations() {
{
one(pollChannelConfig).setReuseAddress(true);
one(sendChannelConfig).setReuseAddress(true);
}
});
config.setReuseAddress(true);
}
config.setReuseAddress(true);
}
@Test
public void testSetSendBufferSize()
{
mockContext.checking(new Expectations()
{
{
one(pollChannelConfig).setSendBufferSize(100);
one(sendChannelConfig).setSendBufferSize(100);
}
});
@Test
public void testSetSendBufferSize() {
mockContext.checking(new Expectations() {
{
one(pollChannelConfig).setSendBufferSize(100);
one(sendChannelConfig).setSendBufferSize(100);
}
});
config.setSendBufferSize(100);
}
config.setSendBufferSize(100);
}
@Test
public void testSetSoLinger()
{
mockContext.checking(new Expectations()
{
{
one(pollChannelConfig).setSoLinger(100);
one(sendChannelConfig).setSoLinger(100);
}
});
@Test
public void testSetSoLinger() {
mockContext.checking(new Expectations() {
{
one(pollChannelConfig).setSoLinger(100);
one(sendChannelConfig).setSoLinger(100);
}
});
config.setSoLinger(100);
}
config.setSoLinger(100);
}
@Test
public void testTcpNoDelay()
{
mockContext.checking(new Expectations()
{
{
one(pollChannelConfig).setTcpNoDelay(true);
one(sendChannelConfig).setTcpNoDelay(true);
}
});
@Test
public void testTcpNoDelay() {
mockContext.checking(new Expectations() {
{
one(pollChannelConfig).setTcpNoDelay(true);
one(sendChannelConfig).setTcpNoDelay(true);
}
});
config.setTcpNoDelay(true);
}
config.setTcpNoDelay(true);
}
@Test
public void testSetTrafficClass()
{
mockContext.checking(new Expectations()
{
{
one(pollChannelConfig).setTrafficClass(1);
one(sendChannelConfig).setTrafficClass(1);
}
});
@Test
public void testSetTrafficClass() {
mockContext.checking(new Expectations() {
{
one(pollChannelConfig).setTrafficClass(1);
one(sendChannelConfig).setTrafficClass(1);
}
});
config.setTrafficClass(1);
}
config.setTrafficClass(1);
}
@Test
public void testSetHighWaterMark()
{
config.setWriteBufferHighWaterMark(128 * 1024);
assertEquals(128 * 1024, config.getWriteBufferHighWaterMark());
}
@Test
public void testSetHighWaterMark() {
config.setWriteBufferHighWaterMark(128 * 1024);
assertEquals(128 * 1024, config.getWriteBufferHighWaterMark());
}
@Test(expected = IllegalArgumentException.class)
public void testSetHighWaterMark_negative()
{
config.setWriteBufferHighWaterMark(-1);
}
@Test(expected = IllegalArgumentException.class)
public void testSetHighWaterMark_negative() {
config.setWriteBufferHighWaterMark(-1);
}
@Test(expected = IllegalArgumentException.class)
public void testSetHighWaterMark_zero()
{
config.setWriteBufferHighWaterMark(0);
}
@Test(expected = IllegalArgumentException.class)
public void testSetHighWaterMark_zero() {
config.setWriteBufferHighWaterMark(0);
}
@Test
public void testSetLowWaterMark()
{
config.setWriteBufferLowWaterMark(100);
assertEquals(100, config.getWriteBufferLowWaterMark());
}
@Test
public void testSetLowWaterMark() {
config.setWriteBufferLowWaterMark(100);
assertEquals(100, config.getWriteBufferLowWaterMark());
}
@Test
public void testSetLowWaterMark_zero()
{
// zero is permitted for the low water mark, unlike high water mark
config.setWriteBufferLowWaterMark(0);
assertEquals(0, config.getWriteBufferLowWaterMark());
}
@Test
public void testSetLowWaterMark_zero() {
// zero is permitted for the low water mark, unlike high water mark
config.setWriteBufferLowWaterMark(0);
assertEquals(0, config.getWriteBufferLowWaterMark());
}
@Test
public void testSetHighWaterMark_lowerThanLow()
{
config.setWriteBufferLowWaterMark(100);
try
{
config.setWriteBufferHighWaterMark(80);
fail("expected IllegalArgumentException");
}
catch (IllegalArgumentException e)
{
assertEquals("Write buffer high water mark must be strictly greater than the low water mark", e.getMessage());
}
}
@Test
public void testSetHighWaterMark_lowerThanLow() {
config.setWriteBufferLowWaterMark(100);
try {
config.setWriteBufferHighWaterMark(80);
fail("expected IllegalArgumentException");
} catch (IllegalArgumentException e) {
assertEquals(
"Write buffer high water mark must be strictly greater than the low water mark",
e.getMessage());
}
}
@Test
public void testSetLowWaterMark_higherThanHigh()
{
config.setWriteBufferHighWaterMark(128 * 1024);
try
{
config.setWriteBufferLowWaterMark(256 * 1024);
fail("expected IllegalArgumentException");
}
catch (IllegalArgumentException e)
{
assertEquals("Write buffer low water mark must be strictly less than the high water mark", e.getMessage());
}
}
@Test
public void testSetLowWaterMark_higherThanHigh() {
config.setWriteBufferHighWaterMark(128 * 1024);
try {
config.setWriteBufferLowWaterMark(256 * 1024);
fail("expected IllegalArgumentException");
} catch (IllegalArgumentException e) {
assertEquals(
"Write buffer low water mark must be strictly less than the high water mark",
e.getMessage());
}
}
}

View File

@ -39,217 +39,232 @@ import org.junit.Test;
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
*/
public class HttpTunnelClientChannelTest
{
public class HttpTunnelClientChannelTest {
public static final int LOCAL_PORT = 50123;
public static final int LOCAL_PORT = 50123;
/** used to emulate the selection of a random port in response to a bind request
* on an ephemeral port.
*/
public static final int OTHER_LOCAL_PORT = 40652;
/** used to emulate the selection of a random port in response to a bind request
* on an ephemeral port.
*/
public static final int OTHER_LOCAL_PORT = 40652;
public static final InetSocketAddress LOCAL_ADDRESS = InetSocketAddress.createUnresolved("localhost", LOCAL_PORT);
public static final InetSocketAddress LOCAL_ADDRESS = InetSocketAddress
.createUnresolved("localhost", LOCAL_PORT);
public static final InetSocketAddress LOCAL_ADDRESS_EPHEMERAL_PORT = InetSocketAddress.createUnresolved("localhost",
0);
public static final InetSocketAddress LOCAL_ADDRESS_EPHEMERAL_PORT =
InetSocketAddress.createUnresolved("localhost", 0);
public static final InetSocketAddress REMOTE_ADDRESS = InetSocketAddress.createUnresolved("test.server.com", 12345);
public static final InetSocketAddress REMOTE_ADDRESS = InetSocketAddress
.createUnresolved("test.server.com", 12345);
public static final InetSocketAddress RESOLVED_LOCAL_ADDRESS_IPV4;
public static final InetSocketAddress RESOLVED_LOCAL_ADDRESS_IPV4;
public static final InetSocketAddress RESOLVED_LOCAL_ADDRESS_IPV6;
public static final InetSocketAddress RESOLVED_LOCAL_ADDRESS_IPV6;
public static final InetSocketAddress RESOLVED_LOCAL_ADDRESS_IPV4_EPHEMERAL_PORT;
public static final InetSocketAddress RESOLVED_LOCAL_ADDRESS_IPV4_EPHEMERAL_PORT;
public static final InetSocketAddress RESOLVED_LOCAL_ADDRESS_IPV6_EPHEMERAL_PORT;
public static final InetSocketAddress RESOLVED_LOCAL_ADDRESS_IPV6_EPHEMERAL_PORT;
public static final InetSocketAddress RESOLVED_LOCAL_ADDRESS_IPV4_SELECTED_PORT;
public static final InetSocketAddress RESOLVED_LOCAL_ADDRESS_IPV4_SELECTED_PORT;
public static final InetSocketAddress RESOLVED_LOCAL_ADDRESS_IPV6_SELECTED_PORT;
public static final InetSocketAddress RESOLVED_LOCAL_ADDRESS_IPV6_SELECTED_PORT;
static
{
try
{
InetAddress localhostIPV4 = InetAddress.getByAddress(new byte[]
{127, 0, 0, 1});
InetAddress localhostIPV6 = InetAddress.getByAddress(new byte[]
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1});
RESOLVED_LOCAL_ADDRESS_IPV4 = new InetSocketAddress(localhostIPV4, LOCAL_PORT);
RESOLVED_LOCAL_ADDRESS_IPV6 = new InetSocketAddress(localhostIPV6, LOCAL_PORT);
RESOLVED_LOCAL_ADDRESS_IPV4_EPHEMERAL_PORT = new InetSocketAddress(localhostIPV4, 0);
RESOLVED_LOCAL_ADDRESS_IPV6_EPHEMERAL_PORT = new InetSocketAddress(localhostIPV6, 0);
RESOLVED_LOCAL_ADDRESS_IPV4_SELECTED_PORT = new InetSocketAddress(localhostIPV4, OTHER_LOCAL_PORT);
RESOLVED_LOCAL_ADDRESS_IPV6_SELECTED_PORT = new InetSocketAddress(localhostIPV6, OTHER_LOCAL_PORT);
}
catch (UnknownHostException e)
{
throw new RuntimeException(
"Creation of InetAddresses should not fail when explicitly specified and the correct length", e);
}
}
static {
try {
InetAddress localhostIPV4 =
InetAddress.getByAddress(new byte[] { 127, 0, 0, 1 });
InetAddress localhostIPV6 =
InetAddress.getByAddress(new byte[] { 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 1 });
RESOLVED_LOCAL_ADDRESS_IPV4 =
new InetSocketAddress(localhostIPV4, LOCAL_PORT);
RESOLVED_LOCAL_ADDRESS_IPV6 =
new InetSocketAddress(localhostIPV6, LOCAL_PORT);
RESOLVED_LOCAL_ADDRESS_IPV4_EPHEMERAL_PORT =
new InetSocketAddress(localhostIPV4, 0);
RESOLVED_LOCAL_ADDRESS_IPV6_EPHEMERAL_PORT =
new InetSocketAddress(localhostIPV6, 0);
RESOLVED_LOCAL_ADDRESS_IPV4_SELECTED_PORT =
new InetSocketAddress(localhostIPV4, OTHER_LOCAL_PORT);
RESOLVED_LOCAL_ADDRESS_IPV6_SELECTED_PORT =
new InetSocketAddress(localhostIPV6, OTHER_LOCAL_PORT);
} catch (UnknownHostException e) {
throw new RuntimeException(
"Creation of InetAddresses should not fail when explicitly specified and the correct length",
e);
}
}
private UpstreamEventCatcher upstreamCatcher;
private UpstreamEventCatcher upstreamCatcher;
private HttpTunnelClientChannel channel;
private HttpTunnelClientChannel channel;
private FakeClientSocketChannelFactory outboundFactory;
private FakeClientSocketChannelFactory outboundFactory;
private FakeSocketChannel sendChannel;
private FakeSocketChannel sendChannel;
private FakeSocketChannel pollChannel;
private FakeSocketChannel pollChannel;
private FakeChannelSink sendSink;
private FakeChannelSink sendSink;
private FakeChannelSink pollSink;
private FakeChannelSink pollSink;
@Before
public void setUp() throws Exception
{
ChannelPipeline pipeline = Channels.pipeline();
upstreamCatcher = new UpstreamEventCatcher();
pipeline.addLast(UpstreamEventCatcher.NAME, upstreamCatcher);
@Before
public void setUp() throws Exception {
ChannelPipeline pipeline = Channels.pipeline();
upstreamCatcher = new UpstreamEventCatcher();
pipeline.addLast(UpstreamEventCatcher.NAME, upstreamCatcher);
outboundFactory = new FakeClientSocketChannelFactory();
outboundFactory = new FakeClientSocketChannelFactory();
HttpTunnelClientChannelFactory factory = new HttpTunnelClientChannelFactory(outboundFactory);
channel = factory.newChannel(pipeline);
HttpTunnelClientChannelFactory factory =
new HttpTunnelClientChannelFactory(outboundFactory);
channel = factory.newChannel(pipeline);
assertEquals(2, outboundFactory.createdChannels.size());
assertEquals(2, outboundFactory.createdChannels.size());
sendChannel = outboundFactory.createdChannels.get(0);
pollChannel = outboundFactory.createdChannels.get(1);
sendSink = (FakeChannelSink) sendChannel.sink;
pollSink = (FakeChannelSink) pollChannel.sink;
}
sendChannel = outboundFactory.createdChannels.get(0);
pollChannel = outboundFactory.createdChannels.get(1);
sendSink = (FakeChannelSink) sendChannel.sink;
pollSink = (FakeChannelSink) pollChannel.sink;
}
@Test
public void testConnect()
{
Channels.connect(channel, REMOTE_ADDRESS);
@Test
public void testConnect() {
Channels.connect(channel, REMOTE_ADDRESS);
// this should result in a CONNECTED state event on the send channel, but not on the poll
// channel just yet
assertEquals(1, sendSink.events.size());
assertEquals(0, pollSink.events.size());
ChannelEvent sendChannelEvent = sendSink.events.poll();
NettyTestUtils.checkIsStateEvent(sendChannelEvent, ChannelState.CONNECTED, REMOTE_ADDRESS);
// this should result in a CONNECTED state event on the send channel, but not on the poll
// channel just yet
assertEquals(1, sendSink.events.size());
assertEquals(0, pollSink.events.size());
ChannelEvent sendChannelEvent = sendSink.events.poll();
NettyTestUtils.checkIsStateEvent(sendChannelEvent,
ChannelState.CONNECTED, REMOTE_ADDRESS);
// once the send channel indicates that it is connected, we should see the tunnel open request
// being sent
sendChannel.emulateConnected(LOCAL_ADDRESS, REMOTE_ADDRESS, ((ChannelStateEvent) sendChannelEvent).getFuture());
assertEquals(1, sendSink.events.size());
ChannelEvent openTunnelRequest = sendSink.events.poll();
NettyTestUtils.checkIsDownstreamMessageEvent(openTunnelRequest, ChannelBuffer.class);
}
// once the send channel indicates that it is connected, we should see the tunnel open request
// being sent
sendChannel.emulateConnected(LOCAL_ADDRESS, REMOTE_ADDRESS,
((ChannelStateEvent) sendChannelEvent).getFuture());
assertEquals(1, sendSink.events.size());
ChannelEvent openTunnelRequest = sendSink.events.poll();
NettyTestUtils.checkIsDownstreamMessageEvent(openTunnelRequest,
ChannelBuffer.class);
}
@Test
public void testBind_unresolvedAddress()
{
// requesting a binding with an unresolved local address
// should attempt to bind the send channel with that address unaltered
// and attempt to bind the poll address with the same host name but
// an ephemeral port. We emulate a resolved IPV4 address for the bind
// response.
checkBinding(LOCAL_ADDRESS, LOCAL_ADDRESS, LOCAL_ADDRESS_EPHEMERAL_PORT, RESOLVED_LOCAL_ADDRESS_IPV4,
RESOLVED_LOCAL_ADDRESS_IPV4_SELECTED_PORT);
}
@Test
public void testBind_unresolvedAddress() {
// requesting a binding with an unresolved local address
// should attempt to bind the send channel with that address unaltered
// and attempt to bind the poll address with the same host name but
// an ephemeral port. We emulate a resolved IPV4 address for the bind
// response.
checkBinding(LOCAL_ADDRESS, LOCAL_ADDRESS,
LOCAL_ADDRESS_EPHEMERAL_PORT, RESOLVED_LOCAL_ADDRESS_IPV4,
RESOLVED_LOCAL_ADDRESS_IPV4_SELECTED_PORT);
}
@Test
public void testBind_resolvedAddress_ipv4()
{
// variant that uses resolved addresses. The bind request
// for the poll channel should also use a resolved address,
// built from the provided resolved address.
checkBinding(RESOLVED_LOCAL_ADDRESS_IPV4, RESOLVED_LOCAL_ADDRESS_IPV4,
RESOLVED_LOCAL_ADDRESS_IPV4_EPHEMERAL_PORT, RESOLVED_LOCAL_ADDRESS_IPV4,
RESOLVED_LOCAL_ADDRESS_IPV4_SELECTED_PORT);
}
@Test
public void testBind_resolvedAddress_ipv4() {
// variant that uses resolved addresses. The bind request
// for the poll channel should also use a resolved address,
// built from the provided resolved address.
checkBinding(RESOLVED_LOCAL_ADDRESS_IPV4, RESOLVED_LOCAL_ADDRESS_IPV4,
RESOLVED_LOCAL_ADDRESS_IPV4_EPHEMERAL_PORT,
RESOLVED_LOCAL_ADDRESS_IPV4,
RESOLVED_LOCAL_ADDRESS_IPV4_SELECTED_PORT);
}
@Test
public void testBind_resolvedAddress_ipv6()
{
// variant that uses a resolved IPV6 address.
// bind request on the poll channel should use the same
// IPv6 host, with an ephemeral port.
checkBinding(RESOLVED_LOCAL_ADDRESS_IPV6, RESOLVED_LOCAL_ADDRESS_IPV6,
RESOLVED_LOCAL_ADDRESS_IPV6_EPHEMERAL_PORT, RESOLVED_LOCAL_ADDRESS_IPV6,
RESOLVED_LOCAL_ADDRESS_IPV6_SELECTED_PORT);
}
@Test
public void testBind_resolvedAddress_ipv6() {
// variant that uses a resolved IPV6 address.
// bind request on the poll channel should use the same
// IPv6 host, with an ephemeral port.
checkBinding(RESOLVED_LOCAL_ADDRESS_IPV6, RESOLVED_LOCAL_ADDRESS_IPV6,
RESOLVED_LOCAL_ADDRESS_IPV6_EPHEMERAL_PORT,
RESOLVED_LOCAL_ADDRESS_IPV6,
RESOLVED_LOCAL_ADDRESS_IPV6_SELECTED_PORT);
}
private void checkBinding(InetSocketAddress requestedBindAddress, InetSocketAddress expectedPollBindRequest,
InetSocketAddress expectedSendBindRequest, InetSocketAddress emulatedPollBindAddress,
InetSocketAddress emulatedSendBindAddress)
{
private void checkBinding(InetSocketAddress requestedBindAddress,
InetSocketAddress expectedPollBindRequest,
InetSocketAddress expectedSendBindRequest,
InetSocketAddress emulatedPollBindAddress,
InetSocketAddress emulatedSendBindAddress) {
ChannelFuture bindFuture = Channels.bind(channel, requestedBindAddress);
assertFalse(bindFuture.isDone());
ChannelFuture bindFuture = Channels.bind(channel, requestedBindAddress);
assertFalse(bindFuture.isDone());
assertEquals(1, sendSink.events.size());
assertEquals(1, pollSink.events.size());
assertEquals(1, sendSink.events.size());
assertEquals(1, pollSink.events.size());
ChannelEvent sendChannelEvent = sendSink.events.poll();
NettyTestUtils.checkIsStateEvent(sendChannelEvent, ChannelState.BOUND, expectedPollBindRequest);
ChannelEvent pollChannelEvent = pollSink.events.poll();
NettyTestUtils.checkIsStateEvent(pollChannelEvent, ChannelState.BOUND, expectedSendBindRequest);
ChannelEvent sendChannelEvent = sendSink.events.poll();
NettyTestUtils.checkIsStateEvent(sendChannelEvent, ChannelState.BOUND,
expectedPollBindRequest);
ChannelEvent pollChannelEvent = pollSink.events.poll();
NettyTestUtils.checkIsStateEvent(pollChannelEvent, ChannelState.BOUND,
expectedSendBindRequest);
sendChannel.emulateBound(emulatedPollBindAddress, sendChannelEvent.getFuture());
assertFalse(bindFuture.isDone());
pollChannel.emulateBound(emulatedSendBindAddress, pollChannelEvent.getFuture());
assertTrue(bindFuture.isDone());
assertTrue(bindFuture.isSuccess());
sendChannel.emulateBound(emulatedPollBindAddress,
sendChannelEvent.getFuture());
assertFalse(bindFuture.isDone());
pollChannel.emulateBound(emulatedSendBindAddress,
pollChannelEvent.getFuture());
assertTrue(bindFuture.isDone());
assertTrue(bindFuture.isSuccess());
assertEquals(channel.getLocalAddress(), emulatedPollBindAddress);
}
assertEquals(channel.getLocalAddress(), emulatedPollBindAddress);
}
@Test
public void testBind_preResolvedAddress_ipv6()
{
ChannelFuture bindFuture = Channels.bind(channel, RESOLVED_LOCAL_ADDRESS_IPV6);
assertFalse(bindFuture.isDone());
@Test
public void testBind_preResolvedAddress_ipv6() {
ChannelFuture bindFuture =
Channels.bind(channel, RESOLVED_LOCAL_ADDRESS_IPV6);
assertFalse(bindFuture.isDone());
assertEquals(1, sendSink.events.size());
assertEquals(1, pollSink.events.size());
assertEquals(1, sendSink.events.size());
assertEquals(1, pollSink.events.size());
ChannelEvent sendChannelEvent = sendSink.events.poll();
NettyTestUtils.checkIsStateEvent(sendChannelEvent, ChannelState.BOUND, RESOLVED_LOCAL_ADDRESS_IPV6);
ChannelEvent pollChannelEvent = pollSink.events.poll();
NettyTestUtils
.checkIsStateEvent(pollChannelEvent, ChannelState.BOUND, RESOLVED_LOCAL_ADDRESS_IPV6_EPHEMERAL_PORT);
ChannelEvent sendChannelEvent = sendSink.events.poll();
NettyTestUtils.checkIsStateEvent(sendChannelEvent, ChannelState.BOUND,
RESOLVED_LOCAL_ADDRESS_IPV6);
ChannelEvent pollChannelEvent = pollSink.events.poll();
NettyTestUtils.checkIsStateEvent(pollChannelEvent, ChannelState.BOUND,
RESOLVED_LOCAL_ADDRESS_IPV6_EPHEMERAL_PORT);
sendChannel.emulateBound(RESOLVED_LOCAL_ADDRESS_IPV6, sendChannelEvent.getFuture());
assertFalse(bindFuture.isDone());
pollChannel.emulateBound(RESOLVED_LOCAL_ADDRESS_IPV4_SELECTED_PORT, pollChannelEvent.getFuture());
assertTrue(bindFuture.isDone());
assertTrue(bindFuture.isSuccess());
sendChannel.emulateBound(RESOLVED_LOCAL_ADDRESS_IPV6,
sendChannelEvent.getFuture());
assertFalse(bindFuture.isDone());
pollChannel.emulateBound(RESOLVED_LOCAL_ADDRESS_IPV4_SELECTED_PORT,
pollChannelEvent.getFuture());
assertTrue(bindFuture.isDone());
assertTrue(bindFuture.isSuccess());
assertEquals(channel.getLocalAddress(), RESOLVED_LOCAL_ADDRESS_IPV6);
}
assertEquals(channel.getLocalAddress(), RESOLVED_LOCAL_ADDRESS_IPV6);
}
@Test
public void testBind_sendBindFails()
{
ChannelFuture bindFuture = Channels.bind(channel, LOCAL_ADDRESS);
assertFalse(bindFuture.isDone());
@Test
public void testBind_sendBindFails() {
ChannelFuture bindFuture = Channels.bind(channel, LOCAL_ADDRESS);
assertFalse(bindFuture.isDone());
Exception bindFailureReason = new Exception("could not bind");
((ChannelStateEvent) sendSink.events.poll()).getFuture().setFailure(bindFailureReason);
assertTrue(bindFuture.isDone());
assertFalse(bindFuture.isSuccess());
assertSame(bindFailureReason, bindFuture.getCause());
}
Exception bindFailureReason = new Exception("could not bind");
((ChannelStateEvent) sendSink.events.poll()).getFuture().setFailure(
bindFailureReason);
assertTrue(bindFuture.isDone());
assertFalse(bindFuture.isSuccess());
assertSame(bindFailureReason, bindFuture.getCause());
}
@Test
public void testBind_pollBindFails()
{
ChannelFuture bindFuture = Channels.bind(channel, LOCAL_ADDRESS);
assertFalse(bindFuture.isDone());
@Test
public void testBind_pollBindFails() {
ChannelFuture bindFuture = Channels.bind(channel, LOCAL_ADDRESS);
assertFalse(bindFuture.isDone());
Exception bindFailureReason = new Exception("could not bind");
((ChannelStateEvent) pollSink.events.poll()).getFuture().setFailure(bindFailureReason);
assertTrue(bindFuture.isDone());
assertFalse(bindFuture.isSuccess());
assertSame(bindFailureReason, bindFuture.getCause());
}
Exception bindFailureReason = new Exception("could not bind");
((ChannelStateEvent) pollSink.events.poll()).getFuture().setFailure(
bindFailureReason);
assertTrue(bindFuture.isDone());
assertFalse(bindFuture.isSuccess());
assertSame(bindFailureReason, bindFuture.getCause());
}
}

View File

@ -36,91 +36,88 @@ import org.junit.Test;
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
*/
public class HttpTunnelClientPollHandlerTest
{
public class HttpTunnelClientPollHandlerTest {
private static final String TUNNEL_ID = "1";
private static final String TUNNEL_ID = "1";
private static final InetSocketAddress SERVER_ADDRESS = createAddress(new byte[]
{10, 0, 0, 3}, 12345);
private static final InetSocketAddress SERVER_ADDRESS = createAddress(
new byte[] { 10, 0, 0, 3 }, 12345);
private static final InetSocketAddress PROXY_ADDRESS = createAddress(new byte[]
{10, 0, 0, 2}, 8888);
private static final InetSocketAddress PROXY_ADDRESS = createAddress(
new byte[] { 10, 0, 0, 2 }, 8888);
private static final InetSocketAddress LOCAL_ADDRESS = createAddress(new byte[]
{10, 0, 0, 1}, 54321);
private static final InetSocketAddress LOCAL_ADDRESS = createAddress(
new byte[] { 10, 0, 0, 1 }, 54321);
private FakeSocketChannel channel;
private FakeSocketChannel channel;
private FakeChannelSink sink;
private FakeChannelSink sink;
private HttpTunnelClientPollHandler handler;
private HttpTunnelClientPollHandler handler;
private MockChannelStateListener listener;
private MockChannelStateListener listener;
private static InetSocketAddress createAddress(byte[] addr, int port)
{
try
{
return new InetSocketAddress(InetAddress.getByAddress(addr), port);
}
catch (UnknownHostException e)
{
throw new RuntimeException("Bad address in test");
}
}
private static InetSocketAddress createAddress(byte[] addr, int port) {
try {
return new InetSocketAddress(InetAddress.getByAddress(addr), port);
} catch (UnknownHostException e) {
throw new RuntimeException("Bad address in test");
}
}
@Before
public void setUp() throws Exception
{
sink = new FakeChannelSink();
@Before
public void setUp() throws Exception {
sink = new FakeChannelSink();
ChannelPipeline pipeline = Channels.pipeline();
listener = new MockChannelStateListener();
listener.serverHostName = HttpTunnelMessageUtils.convertToHostString(SERVER_ADDRESS);
handler = new HttpTunnelClientPollHandler(listener);
handler.setTunnelId(TUNNEL_ID);
pipeline.addLast(HttpTunnelClientPollHandler.NAME, handler);
ChannelPipeline pipeline = Channels.pipeline();
listener = new MockChannelStateListener();
listener.serverHostName =
HttpTunnelMessageUtils.convertToHostString(SERVER_ADDRESS);
handler = new HttpTunnelClientPollHandler(listener);
handler.setTunnelId(TUNNEL_ID);
pipeline.addLast(HttpTunnelClientPollHandler.NAME, handler);
channel = new FakeSocketChannel(null, null, pipeline, sink);
channel.remoteAddress = PROXY_ADDRESS;
channel.localAddress = LOCAL_ADDRESS;
}
channel = new FakeSocketChannel(null, null, pipeline, sink);
channel.remoteAddress = PROXY_ADDRESS;
channel.localAddress = LOCAL_ADDRESS;
}
@Test
public void testSendsRequestOnConnect()
{
Channels.fireChannelConnected(channel, PROXY_ADDRESS);
assertEquals(1, sink.events.size());
HttpRequest request = checkIsMessageEventContainingHttpRequest(sink.events.poll());
assertTrue(HttpTunnelMessageUtils.isServerToClientRequest(request));
assertTrue(HttpTunnelMessageUtils.checkHost(request, SERVER_ADDRESS));
assertTrue(listener.fullyEstablished);
}
@Test
public void testSendsRequestOnConnect() {
Channels.fireChannelConnected(channel, PROXY_ADDRESS);
assertEquals(1, sink.events.size());
HttpRequest request =
checkIsMessageEventContainingHttpRequest(sink.events.poll());
assertTrue(HttpTunnelMessageUtils.isServerToClientRequest(request));
assertTrue(HttpTunnelMessageUtils.checkHost(request, SERVER_ADDRESS));
assertTrue(listener.fullyEstablished);
}
@Test
public void testSendsReceivedDataSentUpstream()
{
HttpResponse response = HttpTunnelMessageUtils.createRecvDataResponse(NettyTestUtils.createData(1234L));
Channels.fireMessageReceived(channel, response);
assertEquals(1, listener.messages.size());
assertEquals(1234L, listener.messages.get(0).readLong());
}
@Test
public void testSendsReceivedDataSentUpstream() {
HttpResponse response =
HttpTunnelMessageUtils.createRecvDataResponse(NettyTestUtils
.createData(1234L));
Channels.fireMessageReceived(channel, response);
assertEquals(1, listener.messages.size());
assertEquals(1234L, listener.messages.get(0).readLong());
}
@Test
public void testSendsAnotherRequestAfterResponse()
{
HttpResponse response = HttpTunnelMessageUtils.createRecvDataResponse(NettyTestUtils.createData(1234L));
Channels.fireMessageReceived(channel, response);
assertEquals(1, sink.events.size());
checkIsMessageEventContainingHttpRequest(sink.events.poll());
}
@Test
public void testSendsAnotherRequestAfterResponse() {
HttpResponse response =
HttpTunnelMessageUtils.createRecvDataResponse(NettyTestUtils
.createData(1234L));
Channels.fireMessageReceived(channel, response);
assertEquals(1, sink.events.size());
checkIsMessageEventContainingHttpRequest(sink.events.poll());
}
private HttpRequest checkIsMessageEventContainingHttpRequest(ChannelEvent event)
{
assertTrue(event instanceof DownstreamMessageEvent);
DownstreamMessageEvent messageEvent = (DownstreamMessageEvent) event;
assertTrue(messageEvent.getMessage() instanceof HttpRequest);
return (HttpRequest) messageEvent.getMessage();
}
private HttpRequest checkIsMessageEventContainingHttpRequest(
ChannelEvent event) {
assertTrue(event instanceof DownstreamMessageEvent);
DownstreamMessageEvent messageEvent = (DownstreamMessageEvent) event;
assertTrue(messageEvent.getMessage() instanceof HttpRequest);
return (HttpRequest) messageEvent.getMessage();
}
}

View File

@ -39,179 +39,185 @@ import org.junit.Test;
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
*/
public class HttpTunnelClientSendHandlerTest
{
public class HttpTunnelClientSendHandlerTest {
private static final InetSocketAddress SERVER_ADDRESS = createAddress(new byte[]
{10, 0, 0, 3}, 12345);
private static final InetSocketAddress SERVER_ADDRESS = createAddress(
new byte[] { 10, 0, 0, 3 }, 12345);
private static final InetSocketAddress PROXY_ADDRESS = createAddress(new byte[]
{10, 0, 0, 2}, 8888);
private static final InetSocketAddress PROXY_ADDRESS = createAddress(
new byte[] { 10, 0, 0, 2 }, 8888);
private static final InetSocketAddress LOCAL_ADDRESS = createAddress(new byte[]
{10, 0, 0, 1}, 54321);
private static final InetSocketAddress LOCAL_ADDRESS = createAddress(
new byte[] { 10, 0, 0, 1 }, 54321);
private FakeSocketChannel channel;
private FakeSocketChannel channel;
private FakeChannelSink sink;
private FakeChannelSink sink;
private HttpTunnelClientSendHandler handler;
private HttpTunnelClientSendHandler handler;
private MockChannelStateListener listener;
private MockChannelStateListener listener;
@Before
public void setUp()
{
sink = new FakeChannelSink();
ChannelPipeline pipeline = Channels.pipeline();
listener = new MockChannelStateListener();
listener.serverHostName = HttpTunnelMessageUtils.convertToHostString(SERVER_ADDRESS);
handler = new HttpTunnelClientSendHandler(listener);
pipeline.addLast(HttpTunnelClientSendHandler.NAME, handler);
channel = new FakeSocketChannel(null, null, pipeline, sink);
channel.remoteAddress = PROXY_ADDRESS;
channel.localAddress = LOCAL_ADDRESS;
}
@Before
public void setUp() {
sink = new FakeChannelSink();
ChannelPipeline pipeline = Channels.pipeline();
listener = new MockChannelStateListener();
listener.serverHostName =
HttpTunnelMessageUtils.convertToHostString(SERVER_ADDRESS);
handler = new HttpTunnelClientSendHandler(listener);
pipeline.addLast(HttpTunnelClientSendHandler.NAME, handler);
channel = new FakeSocketChannel(null, null, pipeline, sink);
channel.remoteAddress = PROXY_ADDRESS;
channel.localAddress = LOCAL_ADDRESS;
}
private static InetSocketAddress createAddress(byte[] addr, int port)
{
try
{
return new InetSocketAddress(InetAddress.getByAddress(addr), port);
}
catch (UnknownHostException e)
{
throw new RuntimeException("Bad address in test");
}
}
private static InetSocketAddress createAddress(byte[] addr, int port) {
try {
return new InetSocketAddress(InetAddress.getByAddress(addr), port);
} catch (UnknownHostException e) {
throw new RuntimeException("Bad address in test");
}
}
@Test
public void testSendsTunnelOpen() throws Exception
{
Channels.fireChannelConnected(channel, PROXY_ADDRESS);
assertEquals(1, sink.events.size());
HttpRequest request = NettyTestUtils.checkIsDownstreamMessageEvent(sink.events.poll(), HttpRequest.class);
assertTrue(HttpTunnelMessageUtils.isOpenTunnelRequest(request));
assertTrue(HttpTunnelMessageUtils.checkHost(request, SERVER_ADDRESS));
}
@Test
public void testSendsTunnelOpen() throws Exception {
Channels.fireChannelConnected(channel, PROXY_ADDRESS);
assertEquals(1, sink.events.size());
HttpRequest request =
NettyTestUtils.checkIsDownstreamMessageEvent(
sink.events.poll(), HttpRequest.class);
assertTrue(HttpTunnelMessageUtils.isOpenTunnelRequest(request));
assertTrue(HttpTunnelMessageUtils.checkHost(request, SERVER_ADDRESS));
}
@Test
public void testStoresTunnelId() throws Exception
{
emulateConnect();
Channels.fireMessageReceived(channel, HttpTunnelMessageUtils.createTunnelOpenResponse("newTunnel"));
assertEquals("newTunnel", handler.getTunnelId());
assertEquals("newTunnel", listener.tunnelId);
}
@Test
public void testStoresTunnelId() throws Exception {
emulateConnect();
Channels.fireMessageReceived(channel,
HttpTunnelMessageUtils.createTunnelOpenResponse("newTunnel"));
assertEquals("newTunnel", handler.getTunnelId());
assertEquals("newTunnel", listener.tunnelId);
}
@Test
public void testSendData()
{
emulateConnectAndOpen();
channel.write(NettyTestUtils.createData(1234L));
assertEquals(1, sink.events.size());
ChannelEvent sentEvent = sink.events.poll();
checkIsSendDataRequestWithData(sentEvent, NettyTestUtils.createData(1234L));
}
@Test
public void testSendData() {
emulateConnectAndOpen();
channel.write(NettyTestUtils.createData(1234L));
assertEquals(1, sink.events.size());
ChannelEvent sentEvent = sink.events.poll();
checkIsSendDataRequestWithData(sentEvent,
NettyTestUtils.createData(1234L));
}
@Test
public void testWillNotSendDataUntilTunnelIdSet()
{
emulateConnect();
channel.write(NettyTestUtils.createData(1234L));
@Test
public void testWillNotSendDataUntilTunnelIdSet() {
emulateConnect();
channel.write(NettyTestUtils.createData(1234L));
assertEquals(0, sink.events.size());
assertEquals(0, sink.events.size());
Channels.fireChannelConnected(channel, PROXY_ADDRESS);
assertEquals(1, sink.events.size());
}
Channels.fireChannelConnected(channel, PROXY_ADDRESS);
assertEquals(1, sink.events.size());
}
@Test
public void testOnlyOneRequestAtATime()
{
emulateConnectAndOpen();
@Test
public void testOnlyOneRequestAtATime() {
emulateConnectAndOpen();
channel.write(NettyTestUtils.createData(1234L));
assertEquals(1, sink.events.size());
checkIsSendDataRequestWithData(sink.events.poll(), NettyTestUtils.createData(1234L));
channel.write(NettyTestUtils.createData(1234L));
assertEquals(1, sink.events.size());
checkIsSendDataRequestWithData(sink.events.poll(),
NettyTestUtils.createData(1234L));
channel.write(NettyTestUtils.createData(5678L));
assertEquals(0, sink.events.size());
channel.write(NettyTestUtils.createData(5678L));
assertEquals(0, sink.events.size());
Channels.fireMessageReceived(channel, HttpTunnelMessageUtils.createSendDataResponse());
assertEquals(1, sink.events.size());
checkIsSendDataRequestWithData(sink.events.poll(), NettyTestUtils.createData(5678L));
}
Channels.fireMessageReceived(channel,
HttpTunnelMessageUtils.createSendDataResponse());
assertEquals(1, sink.events.size());
checkIsSendDataRequestWithData(sink.events.poll(),
NettyTestUtils.createData(5678L));
}
@Test
public void testDisconnect()
{
emulateConnectAndOpen();
@Test
public void testDisconnect() {
emulateConnectAndOpen();
channel.write(NettyTestUtils.createData(1234L));
assertEquals(1, sink.events.size());
checkIsSendDataRequestWithData(sink.events.poll(), NettyTestUtils.createData(1234L));
channel.write(NettyTestUtils.createData(1234L));
assertEquals(1, sink.events.size());
checkIsSendDataRequestWithData(sink.events.poll(),
NettyTestUtils.createData(1234L));
channel.disconnect();
Channels.fireMessageReceived(channel, HttpTunnelMessageUtils.createSendDataResponse());
assertEquals(1, sink.events.size());
channel.disconnect();
Channels.fireMessageReceived(channel,
HttpTunnelMessageUtils.createSendDataResponse());
assertEquals(1, sink.events.size());
HttpRequest request = NettyTestUtils.checkIsDownstreamMessageEvent(sink.events.poll(), HttpRequest.class);
assertTrue(HttpTunnelMessageUtils.isCloseTunnelRequest(request));
assertEquals("newTunnel", HttpTunnelMessageUtils.extractTunnelId(request));
Channels.fireMessageReceived(channel, HttpTunnelMessageUtils.createTunnelCloseResponse());
assertEquals(1, sink.events.size());
NettyTestUtils.checkIsStateEvent(sink.events.poll(), ChannelState.CONNECTED, null);
}
HttpRequest request =
NettyTestUtils.checkIsDownstreamMessageEvent(
sink.events.poll(), HttpRequest.class);
assertTrue(HttpTunnelMessageUtils.isCloseTunnelRequest(request));
assertEquals("newTunnel",
HttpTunnelMessageUtils.extractTunnelId(request));
Channels.fireMessageReceived(channel,
HttpTunnelMessageUtils.createTunnelCloseResponse());
assertEquals(1, sink.events.size());
NettyTestUtils.checkIsStateEvent(sink.events.poll(),
ChannelState.CONNECTED, null);
}
@Test
public void testClose()
{
emulateConnectAndOpen();
@Test
public void testClose() {
emulateConnectAndOpen();
channel.close();
assertEquals(1, sink.events.size());
HttpRequest request = NettyTestUtils.checkIsDownstreamMessageEvent(sink.events.poll(), HttpRequest.class);
assertTrue(HttpTunnelMessageUtils.isCloseTunnelRequest(request));
assertEquals("newTunnel", HttpTunnelMessageUtils.extractTunnelId(request));
Channels.fireMessageReceived(channel, HttpTunnelMessageUtils.createTunnelCloseResponse());
assertEquals(1, sink.events.size());
NettyTestUtils.checkIsStateEvent(sink.events.poll(), ChannelState.OPEN, false);
}
channel.close();
assertEquals(1, sink.events.size());
HttpRequest request =
NettyTestUtils.checkIsDownstreamMessageEvent(
sink.events.poll(), HttpRequest.class);
assertTrue(HttpTunnelMessageUtils.isCloseTunnelRequest(request));
assertEquals("newTunnel",
HttpTunnelMessageUtils.extractTunnelId(request));
Channels.fireMessageReceived(channel,
HttpTunnelMessageUtils.createTunnelCloseResponse());
assertEquals(1, sink.events.size());
NettyTestUtils.checkIsStateEvent(sink.events.poll(), ChannelState.OPEN,
false);
}
@Test
public void testWritesAfterCloseAreRejected()
{
emulateConnectAndOpen();
@Test
public void testWritesAfterCloseAreRejected() {
emulateConnectAndOpen();
channel.close();
assertFalse(channel.write(NettyTestUtils.createData(1234L)).isSuccess());
}
channel.close();
assertFalse(channel.write(NettyTestUtils.createData(1234L)).isSuccess());
}
private void checkIsSendDataRequestWithData(ChannelEvent event, ChannelBuffer data)
{
assertTrue(event instanceof DownstreamMessageEvent);
DownstreamMessageEvent messageEvent = (DownstreamMessageEvent) event;
assertTrue(messageEvent.getMessage() instanceof HttpRequest);
HttpRequest request = (HttpRequest) messageEvent.getMessage();
assertTrue(HttpTunnelMessageUtils.isSendDataRequest(request));
assertEquals(data.readableBytes(), HttpHeaders.getContentLength(request));
private void checkIsSendDataRequestWithData(ChannelEvent event,
ChannelBuffer data) {
assertTrue(event instanceof DownstreamMessageEvent);
DownstreamMessageEvent messageEvent = (DownstreamMessageEvent) event;
assertTrue(messageEvent.getMessage() instanceof HttpRequest);
HttpRequest request = (HttpRequest) messageEvent.getMessage();
assertTrue(HttpTunnelMessageUtils.isSendDataRequest(request));
assertEquals(data.readableBytes(),
HttpHeaders.getContentLength(request));
ChannelBuffer content = request.getContent();
NettyTestUtils.assertEquals(data, content);
}
ChannelBuffer content = request.getContent();
NettyTestUtils.assertEquals(data, content);
}
private void emulateConnect()
{
channel.emulateConnected(LOCAL_ADDRESS, PROXY_ADDRESS, null);
sink.events.clear();
}
private void emulateConnect() {
channel.emulateConnected(LOCAL_ADDRESS, PROXY_ADDRESS, null);
sink.events.clear();
}
private void emulateConnectAndOpen()
{
emulateConnect();
Channels.fireMessageReceived(channel, HttpTunnelMessageUtils.createTunnelOpenResponse("newTunnel"));
private void emulateConnectAndOpen() {
emulateConnect();
Channels.fireMessageReceived(channel,
HttpTunnelMessageUtils.createTunnelOpenResponse("newTunnel"));
sink.events.clear();
}
sink.events.clear();
}
}

View File

@ -37,75 +37,70 @@ import org.junit.runner.RunWith;
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
*/
@RunWith(JMock.class)
public class HttpTunnelServerChannelFactoryTest
{
public class HttpTunnelServerChannelFactoryTest {
private final JUnit4Mockery mockContext = new JUnit4Mockery();
private final JUnit4Mockery mockContext = new JUnit4Mockery();
ServerSocketChannelFactory realChannelFactory;
ServerSocketChannelFactory realChannelFactory;
private HttpTunnelServerChannelFactory factory;
private HttpTunnelServerChannelFactory factory;
ServerSocketChannel realChannel;
ServerSocketChannel realChannel;
@Before
public void setUp() throws Exception
{
realChannelFactory = mockContext.mock(ServerSocketChannelFactory.class);
factory = new HttpTunnelServerChannelFactory(realChannelFactory);
ChannelPipeline pipeline = Channels.pipeline();
realChannel = new FakeServerSocketChannel(factory, pipeline, new FakeChannelSink());
}
@Before
public void setUp() throws Exception {
realChannelFactory = mockContext.mock(ServerSocketChannelFactory.class);
factory = new HttpTunnelServerChannelFactory(realChannelFactory);
ChannelPipeline pipeline = Channels.pipeline();
realChannel =
new FakeServerSocketChannel(factory, pipeline,
new FakeChannelSink());
}
@Test
public void testNewChannel()
{
mockContext.checking(new Expectations()
{
{
one(realChannelFactory).newChannel(with(any(ChannelPipeline.class)));
will(returnValue(realChannel));
}
});
ChannelPipeline pipeline = Channels.pipeline();
HttpTunnelServerChannel newChannel = factory.newChannel(pipeline);
assertNotNull(newChannel);
assertSame(pipeline, newChannel.getPipeline());
}
@Test
public void testNewChannel() {
mockContext.checking(new Expectations() {
{
one(realChannelFactory).newChannel(
with(any(ChannelPipeline.class)));
will(returnValue(realChannel));
}
});
ChannelPipeline pipeline = Channels.pipeline();
HttpTunnelServerChannel newChannel = factory.newChannel(pipeline);
assertNotNull(newChannel);
assertSame(pipeline, newChannel.getPipeline());
}
@Test
public void testNewChannel_forwardsWrappedFactoryFailure()
{
final ChannelException innerException = new ChannelException();
mockContext.checking(new Expectations()
{
{
one(realChannelFactory).newChannel(with(any(ChannelPipeline.class)));
will(throwException(innerException));
}
});
@Test
public void testNewChannel_forwardsWrappedFactoryFailure() {
final ChannelException innerException = new ChannelException();
mockContext.checking(new Expectations() {
{
one(realChannelFactory).newChannel(
with(any(ChannelPipeline.class)));
will(throwException(innerException));
}
});
try
{
factory.newChannel(Channels.pipeline());
fail("Expected ChannelException");
}
catch (ChannelException e)
{
assertSame(innerException, e);
}
}
try {
factory.newChannel(Channels.pipeline());
fail("Expected ChannelException");
} catch (ChannelException e) {
assertSame(innerException, e);
}
}
// @Test
// public void testChannelCreation_withServerBootstrap() {
// mockContext.checking(new Expectations() {{
// one(realChannelFactory).newChannel(with(any(ChannelPipeline.class))); will(returnValue(realChannel));
// }});
//
// ServerBootstrap bootstrap = new ServerBootstrap(factory);
// Channel newChannel = bootstrap.bind(new InetSocketAddress(80));
// assertNotNull(newChannel);
//
// }
// @Test
// public void testChannelCreation_withServerBootstrap() {
// mockContext.checking(new Expectations() {{
// one(realChannelFactory).newChannel(with(any(ChannelPipeline.class))); will(returnValue(realChannel));
// }});
//
// ServerBootstrap bootstrap = new ServerBootstrap(factory);
// Channel newChannel = bootstrap.bind(new InetSocketAddress(80));
// assertNotNull(newChannel);
//
// }
}

View File

@ -42,132 +42,118 @@ import org.junit.runner.RunWith;
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
*/
@RunWith(JMock.class)
public class HttpTunnelServerChannelSinkTest
{
public class HttpTunnelServerChannelSinkTest {
private final JUnit4Mockery mockContext = new JUnit4Mockery();
private final JUnit4Mockery mockContext = new JUnit4Mockery();
private HttpTunnelServerChannelSink sink;
private HttpTunnelServerChannelSink sink;
private ChannelPipeline pipeline;
private ChannelPipeline pipeline;
private FakeSocketChannel channel;
private FakeSocketChannel channel;
ServerSocketChannel realChannel;
ServerSocketChannel realChannel;
ChannelFuture realFuture;
ChannelFuture realFuture;
Throwable exceptionInPipeline;
Throwable exceptionInPipeline;
@Before
public void setUp() throws Exception
{
realChannel = mockContext.mock(ServerSocketChannel.class);
pipeline = Channels.pipeline();
pipeline.addLast("exceptioncatcher", new ExceptionCatcher());
sink = new HttpTunnelServerChannelSink();
sink.setRealChannel(realChannel);
channel = new FakeSocketChannel(null, null, pipeline, sink);
realFuture = Channels.future(realChannel);
}
@Before
public void setUp() throws Exception {
realChannel = mockContext.mock(ServerSocketChannel.class);
pipeline = Channels.pipeline();
pipeline.addLast("exceptioncatcher", new ExceptionCatcher());
sink = new HttpTunnelServerChannelSink();
sink.setRealChannel(realChannel);
channel = new FakeSocketChannel(null, null, pipeline, sink);
realFuture = Channels.future(realChannel);
}
@After
public void teardown() throws Exception
{
assertTrue("exception caught in pipeline: " + exceptionInPipeline, exceptionInPipeline == null);
}
@After
public void teardown() throws Exception {
assertTrue("exception caught in pipeline: " + exceptionInPipeline,
exceptionInPipeline == null);
}
public void testCloseRequest() throws Exception
{
mockContext.checking(new Expectations()
{
{
one(realChannel).close();
will(returnValue(realFuture));
}
});
public void testCloseRequest() throws Exception {
mockContext.checking(new Expectations() {
{
one(realChannel).close();
will(returnValue(realFuture));
}
});
ChannelFuture virtualFuture1 = Channels.close(channel);
mockContext.assertIsSatisfied();
ChannelFuture virtualFuture = virtualFuture1;
realFuture.setSuccess();
assertTrue(virtualFuture.isSuccess());
}
ChannelFuture virtualFuture1 = Channels.close(channel);
mockContext.assertIsSatisfied();
ChannelFuture virtualFuture = virtualFuture1;
realFuture.setSuccess();
assertTrue(virtualFuture.isSuccess());
}
@Test
public void testUnbindRequest_withSuccess() throws Exception
{
ChannelFuture virtualFuture = checkUnbind();
realFuture.setSuccess();
assertTrue(virtualFuture.isSuccess());
}
@Test
public void testUnbindRequest_withSuccess() throws Exception {
ChannelFuture virtualFuture = checkUnbind();
realFuture.setSuccess();
assertTrue(virtualFuture.isSuccess());
}
@Test
public void testUnbindRequest_withFailure() throws Exception
{
ChannelFuture virtualFuture = checkUnbind();
realFuture.setFailure(new Exception("Something bad happened"));
assertFalse(virtualFuture.isSuccess());
}
@Test
public void testUnbindRequest_withFailure() throws Exception {
ChannelFuture virtualFuture = checkUnbind();
realFuture.setFailure(new Exception("Something bad happened"));
assertFalse(virtualFuture.isSuccess());
}
private ChannelFuture checkUnbind()
{
mockContext.checking(new Expectations()
{
{
one(realChannel).unbind();
will(returnValue(realFuture));
}
});
private ChannelFuture checkUnbind() {
mockContext.checking(new Expectations() {
{
one(realChannel).unbind();
will(returnValue(realFuture));
}
});
ChannelFuture virtualFuture = Channels.unbind(channel);
mockContext.assertIsSatisfied();
return virtualFuture;
}
ChannelFuture virtualFuture = Channels.unbind(channel);
mockContext.assertIsSatisfied();
return virtualFuture;
}
@Test
public void testBindRequest_withSuccess()
{
ChannelFuture virtualFuture = checkBind();
realFuture.setSuccess();
assertTrue(virtualFuture.isSuccess());
}
@Test
public void testBindRequest_withSuccess() {
ChannelFuture virtualFuture = checkBind();
realFuture.setSuccess();
assertTrue(virtualFuture.isSuccess());
}
@Test
public void testBindRequest_withFailure()
{
ChannelFuture virtualFuture = checkBind();
realFuture.setFailure(new Exception("Something bad happened"));
assertFalse(virtualFuture.isSuccess());
}
@Test
public void testBindRequest_withFailure() {
ChannelFuture virtualFuture = checkBind();
realFuture.setFailure(new Exception("Something bad happened"));
assertFalse(virtualFuture.isSuccess());
}
private ChannelFuture checkBind()
{
final SocketAddress toAddress = new InetSocketAddress(80);
mockContext.checking(new Expectations()
{
{
one(realChannel).bind(toAddress);
will(returnValue(realFuture));
}
});
private ChannelFuture checkBind() {
final SocketAddress toAddress = new InetSocketAddress(80);
mockContext.checking(new Expectations() {
{
one(realChannel).bind(toAddress);
will(returnValue(realFuture));
}
});
ChannelFuture virtualFuture = Channels.bind(channel, toAddress);
return virtualFuture;
}
ChannelFuture virtualFuture = Channels.bind(channel, toAddress);
return virtualFuture;
}
private final class ExceptionCatcher extends SimpleChannelUpstreamHandler
{
private final class ExceptionCatcher extends SimpleChannelUpstreamHandler {
ExceptionCatcher()
{
super();
}
ExceptionCatcher() {
super();
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception
{
exceptionInPipeline = e.getCause();
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
throws Exception {
exceptionInPipeline = e.getCause();
}
}
}

View File

@ -46,197 +46,195 @@ import org.junit.runner.RunWith;
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
*/
@RunWith(JMock.class)
public class HttpTunnelServerChannelTest
{
public class HttpTunnelServerChannelTest {
JUnit4Mockery mockContext = new JUnit4Mockery();
JUnit4Mockery mockContext = new JUnit4Mockery();
private HttpTunnelServerChannel virtualChannel;
private HttpTunnelServerChannel virtualChannel;
private UpstreamEventCatcher upstreamEvents;
private UpstreamEventCatcher upstreamEvents;
private FakeServerSocketChannelFactory realChannelFactory;
private FakeServerSocketChannelFactory realChannelFactory;
@Before
public void setUp() throws Exception
{
realChannelFactory = new FakeServerSocketChannelFactory();
realChannelFactory.sink = new FakeChannelSink();
@Before
public void setUp() throws Exception {
realChannelFactory = new FakeServerSocketChannelFactory();
realChannelFactory.sink = new FakeChannelSink();
HttpTunnelServerChannelFactory factory = new HttpTunnelServerChannelFactory(realChannelFactory);
virtualChannel = factory.newChannel(createVirtualChannelPipeline());
}
HttpTunnelServerChannelFactory factory =
new HttpTunnelServerChannelFactory(realChannelFactory);
virtualChannel = factory.newChannel(createVirtualChannelPipeline());
}
private ChannelPipeline createVirtualChannelPipeline()
{
ChannelPipeline pipeline = Channels.pipeline();
upstreamEvents = new UpstreamEventCatcher();
pipeline.addLast(UpstreamEventCatcher.NAME, upstreamEvents);
return pipeline;
}
private ChannelPipeline createVirtualChannelPipeline() {
ChannelPipeline pipeline = Channels.pipeline();
upstreamEvents = new UpstreamEventCatcher();
pipeline.addLast(UpstreamEventCatcher.NAME, upstreamEvents);
return pipeline;
}
@Test
public void testGetLocalAddress_delegatedToRealChannel()
{
realChannelFactory.createdChannel.localAddress = InetSocketAddress.createUnresolved("mycomputer", 80);
SocketAddress returned = virtualChannel.getLocalAddress();
assertSame(realChannelFactory.createdChannel.localAddress, returned);
}
@Test
public void testGetLocalAddress_delegatedToRealChannel() {
realChannelFactory.createdChannel.localAddress =
InetSocketAddress.createUnresolved("mycomputer", 80);
SocketAddress returned = virtualChannel.getLocalAddress();
assertSame(realChannelFactory.createdChannel.localAddress, returned);
}
@Test
public void testGetRemoteAddress_returnsNull()
{
assertNull(virtualChannel.getRemoteAddress());
}
@Test
public void testGetRemoteAddress_returnsNull() {
assertNull(virtualChannel.getRemoteAddress());
}
@Test
public void testIsBound_delegatedToRealChannel()
{
realChannelFactory.createdChannel.bound = true;
assertTrue(virtualChannel.isBound());
realChannelFactory.createdChannel.bound = false;
assertFalse(virtualChannel.isBound());
}
@Test
public void testIsBound_delegatedToRealChannel() {
realChannelFactory.createdChannel.bound = true;
assertTrue(virtualChannel.isBound());
realChannelFactory.createdChannel.bound = false;
assertFalse(virtualChannel.isBound());
}
@Test
public void testConstruction_firesOpenEvent()
{
assertTrue(upstreamEvents.events.size() > 0);
checkIsUpstreamChannelStateEvent(upstreamEvents.events.poll(), virtualChannel, ChannelState.OPEN, Boolean.TRUE);
}
@Test
public void testConstruction_firesOpenEvent() {
assertTrue(upstreamEvents.events.size() > 0);
checkIsUpstreamChannelStateEvent(upstreamEvents.events.poll(),
virtualChannel, ChannelState.OPEN, Boolean.TRUE);
}
@Test
public void testChannelBoundEventFromReal_replicatedOnVirtual()
{
upstreamEvents.events.clear();
InetSocketAddress boundAddr = InetSocketAddress.createUnresolved("mycomputer", 12345);
Channels.fireChannelBound(realChannelFactory.createdChannel, boundAddr);
assertEquals(1, upstreamEvents.events.size());
checkIsUpstreamChannelStateEvent(upstreamEvents.events.poll(), virtualChannel, ChannelState.BOUND, boundAddr);
}
@Test
public void testChannelBoundEventFromReal_replicatedOnVirtual() {
upstreamEvents.events.clear();
InetSocketAddress boundAddr =
InetSocketAddress.createUnresolved("mycomputer", 12345);
Channels.fireChannelBound(realChannelFactory.createdChannel, boundAddr);
assertEquals(1, upstreamEvents.events.size());
checkIsUpstreamChannelStateEvent(upstreamEvents.events.poll(),
virtualChannel, ChannelState.BOUND, boundAddr);
}
@Test
public void testChannelUnboundEventFromReal_replicatedOnVirtual()
{
upstreamEvents.events.clear();
Channels.fireChannelUnbound(realChannelFactory.createdChannel);
assertEquals(1, upstreamEvents.events.size());
checkIsUpstreamChannelStateEvent(upstreamEvents.events.poll(), virtualChannel, ChannelState.BOUND, null);
}
@Test
public void testChannelUnboundEventFromReal_replicatedOnVirtual() {
upstreamEvents.events.clear();
Channels.fireChannelUnbound(realChannelFactory.createdChannel);
assertEquals(1, upstreamEvents.events.size());
checkIsUpstreamChannelStateEvent(upstreamEvents.events.poll(),
virtualChannel, ChannelState.BOUND, null);
}
@Test
public void testChannelClosedEventFromReal_replicatedOnVirtual()
{
upstreamEvents.events.clear();
Channels.fireChannelClosed(realChannelFactory.createdChannel);
assertEquals(1, upstreamEvents.events.size());
checkIsUpstreamChannelStateEvent(upstreamEvents.events.poll(), virtualChannel, ChannelState.OPEN, Boolean.FALSE);
}
@Test
public void testChannelClosedEventFromReal_replicatedOnVirtual() {
upstreamEvents.events.clear();
Channels.fireChannelClosed(realChannelFactory.createdChannel);
assertEquals(1, upstreamEvents.events.size());
checkIsUpstreamChannelStateEvent(upstreamEvents.events.poll(),
virtualChannel, ChannelState.OPEN, Boolean.FALSE);
}
@Test
public void testHasConfiguration()
{
assertNotNull(virtualChannel.getConfig());
}
@Test
public void testHasConfiguration() {
assertNotNull(virtualChannel.getConfig());
}
@Test
public void testChangePipelineFactoryDoesNotAffectRealChannel()
{
ChannelPipelineFactory realPipelineFactory = realChannelFactory.createdChannel.getConfig().getPipelineFactory();
ChannelPipelineFactory virtualPipelineFactory = mockContext.mock(ChannelPipelineFactory.class);
virtualChannel.getConfig().setPipelineFactory(virtualPipelineFactory);
assertSame(virtualPipelineFactory, virtualChannel.getConfig().getPipelineFactory());
@Test
public void testChangePipelineFactoryDoesNotAffectRealChannel() {
ChannelPipelineFactory realPipelineFactory =
realChannelFactory.createdChannel.getConfig()
.getPipelineFactory();
ChannelPipelineFactory virtualPipelineFactory =
mockContext.mock(ChannelPipelineFactory.class);
virtualChannel.getConfig().setPipelineFactory(virtualPipelineFactory);
assertSame(virtualPipelineFactory, virtualChannel.getConfig()
.getPipelineFactory());
// channel pipeline factory is a special case: we do not want it set on the configuration
// of the underlying factory
assertSame(realPipelineFactory, realChannelFactory.createdChannel.getConfig().getPipelineFactory());
}
// channel pipeline factory is a special case: we do not want it set on the configuration
// of the underlying factory
assertSame(realPipelineFactory, realChannelFactory.createdChannel
.getConfig().getPipelineFactory());
}
@Test
public void testChangingBacklogAffectsRealChannel()
{
virtualChannel.getConfig().setBacklog(1234);
assertEquals(1234, realChannelFactory.createdChannel.getConfig().getBacklog());
}
@Test
public void testChangingBacklogAffectsRealChannel() {
virtualChannel.getConfig().setBacklog(1234);
assertEquals(1234, realChannelFactory.createdChannel.getConfig()
.getBacklog());
}
@Test
public void testChangingConnectTimeoutMillisAffectsRealChannel()
{
virtualChannel.getConfig().setConnectTimeoutMillis(54321);
assertEquals(54321, realChannelFactory.createdChannel.getConfig().getConnectTimeoutMillis());
}
@Test
public void testChangingConnectTimeoutMillisAffectsRealChannel() {
virtualChannel.getConfig().setConnectTimeoutMillis(54321);
assertEquals(54321, realChannelFactory.createdChannel.getConfig()
.getConnectTimeoutMillis());
}
@Test
public void testChangingPerformancePreferencesAffectsRealChannel()
{
final ServerSocketChannelConfig mockConfig = mockContext.mock(ServerSocketChannelConfig.class);
realChannelFactory.createdChannel.config = mockConfig;
mockContext.checking(new Expectations()
{
{
one(mockConfig).setPerformancePreferences(100, 200, 300);
}
});
virtualChannel.getConfig().setPerformancePreferences(100, 200, 300);
mockContext.assertIsSatisfied();
}
@Test
public void testChangingPerformancePreferencesAffectsRealChannel() {
final ServerSocketChannelConfig mockConfig =
mockContext.mock(ServerSocketChannelConfig.class);
realChannelFactory.createdChannel.config = mockConfig;
mockContext.checking(new Expectations() {
{
one(mockConfig).setPerformancePreferences(100, 200, 300);
}
});
virtualChannel.getConfig().setPerformancePreferences(100, 200, 300);
mockContext.assertIsSatisfied();
}
@Test
public void testChangingReceiveBufferSizeAffectsRealChannel()
{
virtualChannel.getConfig().setReceiveBufferSize(10101);
assertEquals(10101, realChannelFactory.createdChannel.getConfig().getReceiveBufferSize());
}
@Test
public void testChangingReceiveBufferSizeAffectsRealChannel() {
virtualChannel.getConfig().setReceiveBufferSize(10101);
assertEquals(10101, realChannelFactory.createdChannel.getConfig()
.getReceiveBufferSize());
}
@Test
public void testChangingReuseAddressAffectsRealChannel()
{
virtualChannel.getConfig().setReuseAddress(true);
assertEquals(true, realChannelFactory.createdChannel.getConfig().isReuseAddress());
}
@Test
public void testChangingReuseAddressAffectsRealChannel() {
virtualChannel.getConfig().setReuseAddress(true);
assertEquals(true, realChannelFactory.createdChannel.getConfig()
.isReuseAddress());
}
@Test
public void testSetChannelPipelineFactoryViaOption()
{
final ServerSocketChannelConfig mockConfig = mockContext.mock(ServerSocketChannelConfig.class);
realChannelFactory.createdChannel.config = mockConfig;
@Test
public void testSetChannelPipelineFactoryViaOption() {
final ServerSocketChannelConfig mockConfig =
mockContext.mock(ServerSocketChannelConfig.class);
realChannelFactory.createdChannel.config = mockConfig;
mockContext.checking(new Expectations()
{
{
never(mockConfig);
}
});
mockContext.checking(new Expectations() {
{
never(mockConfig);
}
});
ChannelPipelineFactory factory = mockContext.mock(ChannelPipelineFactory.class);
virtualChannel.getConfig().setOption("pipelineFactory", factory);
assertSame(factory, virtualChannel.getConfig().getPipelineFactory());
}
ChannelPipelineFactory factory =
mockContext.mock(ChannelPipelineFactory.class);
virtualChannel.getConfig().setOption("pipelineFactory", factory);
assertSame(factory, virtualChannel.getConfig().getPipelineFactory());
}
@Test
public void testSetOptionAffectsRealChannel()
{
final ServerSocketChannelConfig mockConfig = mockContext.mock(ServerSocketChannelConfig.class);
realChannelFactory.createdChannel.config = mockConfig;
@Test
public void testSetOptionAffectsRealChannel() {
final ServerSocketChannelConfig mockConfig =
mockContext.mock(ServerSocketChannelConfig.class);
realChannelFactory.createdChannel.config = mockConfig;
mockContext.checking(new Expectations()
{
{
one(mockConfig).setOption("testOption", "testValue");
}
});
mockContext.checking(new Expectations() {
{
one(mockConfig).setOption("testOption", "testValue");
}
});
virtualChannel.getConfig().setOption("testOption", "testValue");
}
virtualChannel.getConfig().setOption("testOption", "testValue");
}
private void checkIsUpstreamChannelStateEvent(ChannelEvent ev, Channel expectedChannel, ChannelState expectedState,
Object expectedValue)
{
assertTrue(ev instanceof UpstreamChannelStateEvent);
UpstreamChannelStateEvent checkedEv = (UpstreamChannelStateEvent) ev;
assertSame(expectedChannel, checkedEv.getChannel());
assertEquals(expectedState, checkedEv.getState());
assertEquals(expectedValue, checkedEv.getValue());
}
private void checkIsUpstreamChannelStateEvent(ChannelEvent ev,
Channel expectedChannel, ChannelState expectedState,
Object expectedValue) {
assertTrue(ev instanceof UpstreamChannelStateEvent);
UpstreamChannelStateEvent checkedEv = (UpstreamChannelStateEvent) ev;
assertSame(expectedChannel, checkedEv.getChannel());
assertEquals(expectedState, checkedEv.getState());
assertEquals(expectedValue, checkedEv.getValue());
}
}

View File

@ -56,432 +56,425 @@ import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
*/
public class HttpTunnelSoakTester
{
public class HttpTunnelSoakTester {
private static final int SERVER_PORT = 20100;
private static final int SERVER_PORT = 20100;
static final Logger LOG = Logger.getLogger(HttpTunnelSoakTester.class.getName());
static final Logger LOG = Logger.getLogger(HttpTunnelSoakTester.class
.getName());
private static final long BYTES_TO_SEND = 1024 * 1024 * 1024;
private static final long BYTES_TO_SEND = 1024 * 1024 * 1024;
private static final int MAX_WRITE_SIZE = 64 * 1024;
private static final int MAX_WRITE_SIZE = 64 * 1024;
private final ServerBootstrap serverBootstrap;
private final ServerBootstrap serverBootstrap;
private final ClientBootstrap clientBootstrap;
private final ClientBootstrap clientBootstrap;
final ChannelGroup channels;
final ChannelGroup channels;
private final ExecutorService executor;
private final ExecutorService executor;
final ScheduledExecutorService scheduledExecutor;
final DataSender c2sDataSender = new DataSender("C2S");
final DataSender s2cDataSender = new DataSender("S2C");
private DataVerifier c2sVerifier = new DataVerifier("C2S-Verifier");
private DataVerifier s2cVerifier = new DataVerifier("S2C-Verifier");
private static byte[] SEND_STREAM;
static {
SEND_STREAM = new byte[MAX_WRITE_SIZE + 127];
for(int i=0; i < SEND_STREAM.length; i++) {
SEND_STREAM[i] = (byte)(i % 127);
}
}
final ScheduledExecutorService scheduledExecutor;
public HttpTunnelSoakTester()
{
scheduledExecutor = Executors.newSingleThreadScheduledExecutor();
executor = Executors.newCachedThreadPool();
ServerSocketChannelFactory serverChannelFactory = new NioServerSocketChannelFactory(executor, executor);
HttpTunnelServerChannelFactory serverTunnelFactory = new HttpTunnelServerChannelFactory(serverChannelFactory);
final DataSender c2sDataSender = new DataSender("C2S");
serverBootstrap = new ServerBootstrap(serverTunnelFactory);
serverBootstrap.setPipelineFactory(createServerPipelineFactory());
final DataSender s2cDataSender = new DataSender("S2C");
ClientSocketChannelFactory clientChannelFactory = new NioClientSocketChannelFactory(executor, executor);
HttpTunnelClientChannelFactory clientTunnelFactory = new HttpTunnelClientChannelFactory(clientChannelFactory);
private DataVerifier c2sVerifier = new DataVerifier("C2S-Verifier");
clientBootstrap = new ClientBootstrap(clientTunnelFactory);
clientBootstrap.setPipelineFactory(createClientPipelineFactory());
configureProxy();
private DataVerifier s2cVerifier = new DataVerifier("S2C-Verifier");
channels = new DefaultChannelGroup();
}
private static byte[] SEND_STREAM;
private void configureProxy()
{
String proxyHost = System.getProperty("http.proxyHost");
if (proxyHost != null && proxyHost.length() != 0)
{
int proxyPort = Integer.getInteger("http.proxyPort", 80);
InetAddress chosenAddress = chooseAddress(proxyHost);
InetSocketAddress proxyAddress = new InetSocketAddress(chosenAddress, proxyPort);
if (!proxyAddress.isUnresolved())
{
clientBootstrap.setOption(HttpTunnelClientChannelConfig.PROXY_ADDRESS_OPTION, proxyAddress);
System.out.println("Using " + proxyAddress + " as a proxy for this test run");
}
else
{
System.err.println("Failed to resolve proxy address " + proxyAddress);
}
}
else
{
System.out.println("No proxy specified, will connect to server directly");
}
}
static {
SEND_STREAM = new byte[MAX_WRITE_SIZE + 127];
for (int i = 0; i < SEND_STREAM.length; i ++) {
SEND_STREAM[i] = (byte) (i % 127);
}
}
private InetAddress chooseAddress(String proxyHost)
{
try
{
InetAddress[] allByName = InetAddress.getAllByName(proxyHost);
for (InetAddress address : allByName)
{
if (address.isAnyLocalAddress() || address.isLinkLocalAddress())
{
continue;
public HttpTunnelSoakTester() {
scheduledExecutor = Executors.newSingleThreadScheduledExecutor();
executor = Executors.newCachedThreadPool();
ServerSocketChannelFactory serverChannelFactory =
new NioServerSocketChannelFactory(executor, executor);
HttpTunnelServerChannelFactory serverTunnelFactory =
new HttpTunnelServerChannelFactory(serverChannelFactory);
serverBootstrap = new ServerBootstrap(serverTunnelFactory);
serverBootstrap.setPipelineFactory(createServerPipelineFactory());
ClientSocketChannelFactory clientChannelFactory =
new NioClientSocketChannelFactory(executor, executor);
HttpTunnelClientChannelFactory clientTunnelFactory =
new HttpTunnelClientChannelFactory(clientChannelFactory);
clientBootstrap = new ClientBootstrap(clientTunnelFactory);
clientBootstrap.setPipelineFactory(createClientPipelineFactory());
configureProxy();
channels = new DefaultChannelGroup();
}
private void configureProxy() {
String proxyHost = System.getProperty("http.proxyHost");
if (proxyHost != null && proxyHost.length() != 0) {
int proxyPort = Integer.getInteger("http.proxyPort", 80);
InetAddress chosenAddress = chooseAddress(proxyHost);
InetSocketAddress proxyAddress =
new InetSocketAddress(chosenAddress, proxyPort);
if (!proxyAddress.isUnresolved()) {
clientBootstrap.setOption(
HttpTunnelClientChannelConfig.PROXY_ADDRESS_OPTION,
proxyAddress);
System.out.println("Using " + proxyAddress +
" as a proxy for this test run");
} else {
System.err.println("Failed to resolve proxy address " +
proxyAddress);
}
} else {
System.out
.println("No proxy specified, will connect to server directly");
}
}
private InetAddress chooseAddress(String proxyHost) {
try {
InetAddress[] allByName = InetAddress.getAllByName(proxyHost);
for (InetAddress address: allByName) {
if (address.isAnyLocalAddress() || address.isLinkLocalAddress()) {
continue;
}
return address;
}
return address;
}
return null;
}
catch (UnknownHostException e)
{
return null;
}
}
protected ChannelPipelineFactory createClientPipelineFactory()
{
return new ChannelPipelineFactory()
{
public ChannelPipeline getPipeline() throws Exception
{
ChannelPipeline pipeline = Channels.pipeline();
pipeline.addLast("s2cVerifier", s2cVerifier);
pipeline.addLast("throttleControl", new SendThrottle(c2sDataSender));
return pipeline;
}
};
}
protected ChannelPipelineFactory createServerPipelineFactory()
{
return new ChannelPipelineFactory()
{
public ChannelPipeline getPipeline() throws Exception
{
ChannelPipeline pipeline = Channels.pipeline();
pipeline.addLast("c2sVerifier", c2sVerifier);
pipeline.addLast("throttleControl", new SendThrottle(s2cDataSender));
pipeline.addLast("sendStarter", new SimpleChannelUpstreamHandler() {
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception {
Channel childChannel = e.getChannel();
channels.add(childChannel);
s2cDataSender.setChannel(childChannel);
executor.execute(s2cDataSender);
};
});
return pipeline;
}
};
}
public void run() throws InterruptedException
{
LOG.info("binding server channel");
Channel serverChannel = serverBootstrap.bind(new InetSocketAddress(SERVER_PORT));
channels.add(serverChannel);
LOG.log(Level.INFO, "server channel bound to {0}", serverChannel.getLocalAddress());
SocketChannel clientChannel = createClientChannel();
if (clientChannel == null)
{
LOG.severe("no client channel - bailing out");
return;
}
channels.add(clientChannel);
c2sDataSender.setChannel(clientChannel);
executor.execute(c2sDataSender);
if(!c2sDataSender.waitForFinish(5, TimeUnit.MINUTES)) {
LOG.severe("Data send from client to server failed");
}
if(!s2cDataSender.waitForFinish(5, TimeUnit.MINUTES)) {
LOG.severe("Data send from server to client failed");
}
LOG.log(Level.INFO, "Waiting for verification to complete");
if (!c2sVerifier.waitForCompletion(30L, TimeUnit.SECONDS))
{
LOG.warning("Timed out waiting for verification of client-to-server stream");
}
if (!s2cVerifier.waitForCompletion(30L, TimeUnit.SECONDS))
{
LOG.warning("Timed out waiting for verification of server-to-client stream");
}
LOG.info("closing client channel");
closeChannel(clientChannel);
LOG.info("server channel status: " + (serverChannel.isOpen() ? "open" : "closed"));
LOG.info("closing server channel");
closeChannel(serverChannel);
}
private void closeChannel(Channel channel)
{
try
{
if (!channel.close().await(5L, TimeUnit.SECONDS))
{
LOG.warning("Failed to close connection within reasonable amount of time");
}
}
catch (InterruptedException e)
{
LOG.severe("Interrupted while closing connection");
}
}
private SocketChannel createClientChannel()
{
InetSocketAddress serverAddress = new InetSocketAddress("localhost", SERVER_PORT);
ChannelFuture clientChannelFuture = clientBootstrap.connect(serverAddress);
try
{
if (!clientChannelFuture.await(1000, TimeUnit.MILLISECONDS))
{
LOG.severe("did not connect within acceptable time period");
return null;
}
}
catch (InterruptedException e)
{
LOG.severe("Interrupted while waiting for client connect to be established");
return null;
}
} catch (UnknownHostException e) {
return null;
}
}
if (!clientChannelFuture.isSuccess())
{
LOG.log(Level.SEVERE, "did not connect successfully", clientChannelFuture.getCause());
return null;
}
HttpTunnelClientChannelConfig config = ((HttpTunnelClientChannelConfig)clientChannelFuture.getChannel().getConfig());
config.setWriteBufferHighWaterMark(2 * 1024 * 1024);
config.setWriteBufferLowWaterMark(1024 * 1024);
protected ChannelPipelineFactory createClientPipelineFactory() {
return new ChannelPipelineFactory() {
return (SocketChannel) clientChannelFuture.getChannel();
}
private ChannelBuffer createRandomSizeBuffer(AtomicInteger nextWriteByte)
{
Random random = new Random();
int arraySize = random.nextInt(MAX_WRITE_SIZE) + 1;
// cheaply create the buffer by wrapping an appropriately sized section of the pre-built array
ChannelBuffer buffer = ChannelBuffers.wrappedBuffer(SEND_STREAM, nextWriteByte.get(), arraySize);
nextWriteByte.set((nextWriteByte.get() + arraySize) % 127);
return buffer;
}
public static void main(String[] args) throws Exception
{
HttpTunnelSoakTester soakTester = new HttpTunnelSoakTester();
try
{
soakTester.run();
}
finally
{
soakTester.shutdown();
}
}
private void shutdown()
{
serverBootstrap.releaseExternalResources();
clientBootstrap.releaseExternalResources();
executor.shutdownNow();
scheduledExecutor.shutdownNow();
}
private class DataVerifier extends SimpleChannelUpstreamHandler
{
private String name;
private int expectedNext = 0;
private int verifiedBytes = 0;
private CountDownLatch completionLatch = new CountDownLatch(1);
public DataVerifier(String name)
{
this.name = name;
}
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception
{
ChannelBuffer bytesToVerify = (ChannelBuffer)e.getMessage();
while (bytesToVerify.readable())
{
byte readByte = bytesToVerify.readByte();
if (readByte != expectedNext)
{
LOG.log(Level.SEVERE, "{0}: received a byte out of sequence. Expected {1}, got {2}", new Object[] { name, expectedNext, readByte });
System.exit(-1);
return;
public ChannelPipeline getPipeline() throws Exception {
ChannelPipeline pipeline = Channels.pipeline();
pipeline.addLast("s2cVerifier", s2cVerifier);
pipeline.addLast("throttleControl", new SendThrottle(
c2sDataSender));
return pipeline;
}
};
}
expectedNext = (expectedNext + 1) % 127;
verifiedBytes++;
}
protected ChannelPipelineFactory createServerPipelineFactory() {
return new ChannelPipelineFactory() {
if (verifiedBytes >= BYTES_TO_SEND)
{
completionLatch.countDown();
public ChannelPipeline getPipeline() throws Exception {
ChannelPipeline pipeline = Channels.pipeline();
pipeline.addLast("c2sVerifier", c2sVerifier);
pipeline.addLast("throttleControl", new SendThrottle(
s2cDataSender));
pipeline.addLast("sendStarter",
new SimpleChannelUpstreamHandler() {
public void channelConnected(
ChannelHandlerContext ctx,
ChannelStateEvent e) throws Exception {
Channel childChannel = e.getChannel();
channels.add(childChannel);
s2cDataSender.setChannel(childChannel);
executor.execute(s2cDataSender);
};
});
return pipeline;
}
};
}
public void run() throws InterruptedException {
LOG.info("binding server channel");
Channel serverChannel =
serverBootstrap.bind(new InetSocketAddress(SERVER_PORT));
channels.add(serverChannel);
LOG.log(Level.INFO, "server channel bound to {0}",
serverChannel.getLocalAddress());
SocketChannel clientChannel = createClientChannel();
if (clientChannel == null) {
LOG.severe("no client channel - bailing out");
return;
}
}
}
@Override
public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception
{
channels.add(ctx.getChannel());
}
public boolean waitForCompletion(long timeout, TimeUnit timeoutUnit) throws InterruptedException {
return completionLatch.await(timeout, timeoutUnit);
}
}
private class SendThrottle extends SimpleChannelUpstreamHandler
{
private final DataSender sender;
public SendThrottle(DataSender sender)
{
this.sender = sender;
}
@Override
public void channelInterestChanged(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception
{
boolean writeEnabled = ctx.getChannel().isWritable();
sender.setWriteEnabled(writeEnabled);
}
}
private class DataSender implements Runnable {
channels.add(clientChannel);
c2sDataSender.setChannel(clientChannel);
private AtomicReference<Channel> channel = new AtomicReference<Channel>();
private long totalBytesSent = 0;
private long numWrites = 0;
private long runStartTime = System.currentTimeMillis();
private boolean firstRun = true;
private AtomicBoolean writeEnabled = new AtomicBoolean(true);
private AtomicBoolean running = new AtomicBoolean(false);
private CountDownLatch finishLatch = new CountDownLatch(1);
private String name;
private AtomicInteger nextWriteByte = new AtomicInteger(0);
public DataSender(String name) {
this.name = name;
}
public void setChannel(Channel channel)
{
this.channel.set(channel);
}
public void setWriteEnabled(boolean enabled)
{
writeEnabled.set(enabled);
if(enabled && !this.isRunning() && finishLatch.getCount() > 0) {
executor.execute(this);
}
}
@Override
public void run()
{
if(!running.compareAndSet(false, true)) {
LOG.log(Level.WARNING, "{0}: Attempt made to run duplicate sender!", name);
return;
}
if(finishLatch.getCount() == 0) {
LOG.log(Level.SEVERE, "{0}: Attempt made to run after completion!", name);
}
if(firstRun) {
firstRun = false;
runStartTime = System.currentTimeMillis();
LOG.log(Level.INFO, "{0}: sending data", name);
}
while (totalBytesSent < BYTES_TO_SEND)
{
if(!writeEnabled.get()) {
running.set(false);
return;
executor.execute(c2sDataSender);
if (!c2sDataSender.waitForFinish(5, TimeUnit.MINUTES)) {
LOG.severe("Data send from client to server failed");
}
if (!s2cDataSender.waitForFinish(5, TimeUnit.MINUTES)) {
LOG.severe("Data send from server to client failed");
}
LOG.log(Level.INFO, "Waiting for verification to complete");
if (!c2sVerifier.waitForCompletion(30L, TimeUnit.SECONDS)) {
LOG.warning("Timed out waiting for verification of client-to-server stream");
}
if (!s2cVerifier.waitForCompletion(30L, TimeUnit.SECONDS)) {
LOG.warning("Timed out waiting for verification of server-to-client stream");
}
LOG.info("closing client channel");
closeChannel(clientChannel);
LOG.info("server channel status: " +
(serverChannel.isOpen()? "open" : "closed"));
LOG.info("closing server channel");
closeChannel(serverChannel);
}
private void closeChannel(Channel channel) {
try {
if (!channel.close().await(5L, TimeUnit.SECONDS)) {
LOG.warning("Failed to close connection within reasonable amount of time");
}
ChannelBuffer randomBytesForSend = createRandomSizeBuffer(nextWriteByte);
totalBytesSent += randomBytesForSend.readableBytes();
} catch (InterruptedException e) {
LOG.severe("Interrupted while closing connection");
}
channel.get().write(ChannelBuffers.wrappedBuffer(randomBytesForSend));
}
numWrites++;
if (numWrites % 100 == 0)
{
LOG.log(Level.INFO, "{0}: {1} writes dispatched, totalling {2} bytes", new Object[]
{name, numWrites, totalBytesSent});
private SocketChannel createClientChannel() {
InetSocketAddress serverAddress =
new InetSocketAddress("localhost", SERVER_PORT);
ChannelFuture clientChannelFuture =
clientBootstrap.connect(serverAddress);
try {
if (!clientChannelFuture.await(1000, TimeUnit.MILLISECONDS)) {
LOG.severe("did not connect within acceptable time period");
return null;
}
}
} catch (InterruptedException e) {
LOG.severe("Interrupted while waiting for client connect to be established");
return null;
}
LOG.log(Level.INFO, "{0}: completed send cycle", name);
long runEndTime = System.currentTimeMillis();
long totalTime = runEndTime - runStartTime;
long totalKB = totalBytesSent / 1024;
double rate = totalKB / (totalTime / 1000.0);
LOG.log(Level.INFO, "{0}: Sent {1} bytes", new Object[] { name, totalBytesSent } );
LOG.log(Level.INFO, "{0}: Average throughput: {1} KB/s", new Object[] { name, rate } );
if (!clientChannelFuture.isSuccess()) {
LOG.log(Level.SEVERE, "did not connect successfully",
clientChannelFuture.getCause());
return null;
}
finishLatch.countDown();
running.set(false);
}
public boolean isRunning() {
return running.get();
}
public boolean waitForFinish(long timeout, TimeUnit timeoutUnit) throws InterruptedException {
return finishLatch.await(timeout, timeoutUnit);
}
}
HttpTunnelClientChannelConfig config =
((HttpTunnelClientChannelConfig) clientChannelFuture
.getChannel().getConfig());
config.setWriteBufferHighWaterMark(2 * 1024 * 1024);
config.setWriteBufferLowWaterMark(1024 * 1024);
return (SocketChannel) clientChannelFuture.getChannel();
}
private ChannelBuffer createRandomSizeBuffer(AtomicInteger nextWriteByte) {
Random random = new Random();
int arraySize = random.nextInt(MAX_WRITE_SIZE) + 1;
// cheaply create the buffer by wrapping an appropriately sized section of the pre-built array
ChannelBuffer buffer =
ChannelBuffers.wrappedBuffer(SEND_STREAM, nextWriteByte.get(),
arraySize);
nextWriteByte.set((nextWriteByte.get() + arraySize) % 127);
return buffer;
}
public static void main(String[] args) throws Exception {
HttpTunnelSoakTester soakTester = new HttpTunnelSoakTester();
try {
soakTester.run();
} finally {
soakTester.shutdown();
}
}
private void shutdown() {
serverBootstrap.releaseExternalResources();
clientBootstrap.releaseExternalResources();
executor.shutdownNow();
scheduledExecutor.shutdownNow();
}
private class DataVerifier extends SimpleChannelUpstreamHandler {
private String name;
private int expectedNext = 0;
private int verifiedBytes = 0;
private CountDownLatch completionLatch = new CountDownLatch(1);
public DataVerifier(String name) {
this.name = name;
}
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
throws Exception {
ChannelBuffer bytesToVerify = (ChannelBuffer) e.getMessage();
while (bytesToVerify.readable()) {
byte readByte = bytesToVerify.readByte();
if (readByte != expectedNext) {
LOG.log(Level.SEVERE,
"{0}: received a byte out of sequence. Expected {1}, got {2}",
new Object[] { name, expectedNext, readByte });
System.exit(-1);
return;
}
expectedNext = (expectedNext + 1) % 127;
verifiedBytes ++;
}
if (verifiedBytes >= BYTES_TO_SEND) {
completionLatch.countDown();
return;
}
}
@Override
public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e)
throws Exception {
channels.add(ctx.getChannel());
}
public boolean waitForCompletion(long timeout, TimeUnit timeoutUnit)
throws InterruptedException {
return completionLatch.await(timeout, timeoutUnit);
}
}
private class SendThrottle extends SimpleChannelUpstreamHandler {
private final DataSender sender;
public SendThrottle(DataSender sender) {
this.sender = sender;
}
@Override
public void channelInterestChanged(ChannelHandlerContext ctx,
ChannelStateEvent e) throws Exception {
boolean writeEnabled = ctx.getChannel().isWritable();
sender.setWriteEnabled(writeEnabled);
}
}
private class DataSender implements Runnable {
private AtomicReference<Channel> channel =
new AtomicReference<Channel>();
private long totalBytesSent = 0;
private long numWrites = 0;
private long runStartTime = System.currentTimeMillis();
private boolean firstRun = true;
private AtomicBoolean writeEnabled = new AtomicBoolean(true);
private AtomicBoolean running = new AtomicBoolean(false);
private CountDownLatch finishLatch = new CountDownLatch(1);
private String name;
private AtomicInteger nextWriteByte = new AtomicInteger(0);
public DataSender(String name) {
this.name = name;
}
public void setChannel(Channel channel) {
this.channel.set(channel);
}
public void setWriteEnabled(boolean enabled) {
writeEnabled.set(enabled);
if (enabled && !this.isRunning() && finishLatch.getCount() > 0) {
executor.execute(this);
}
}
@Override
public void run() {
if (!running.compareAndSet(false, true)) {
LOG.log(Level.WARNING,
"{0}: Attempt made to run duplicate sender!", name);
return;
}
if (finishLatch.getCount() == 0) {
LOG.log(Level.SEVERE,
"{0}: Attempt made to run after completion!", name);
}
if (firstRun) {
firstRun = false;
runStartTime = System.currentTimeMillis();
LOG.log(Level.INFO, "{0}: sending data", name);
}
while (totalBytesSent < BYTES_TO_SEND) {
if (!writeEnabled.get()) {
running.set(false);
return;
}
ChannelBuffer randomBytesForSend =
createRandomSizeBuffer(nextWriteByte);
totalBytesSent += randomBytesForSend.readableBytes();
channel.get().write(
ChannelBuffers.wrappedBuffer(randomBytesForSend));
numWrites ++;
if (numWrites % 100 == 0) {
LOG.log(Level.INFO,
"{0}: {1} writes dispatched, totalling {2} bytes",
new Object[] { name, numWrites, totalBytesSent });
}
}
LOG.log(Level.INFO, "{0}: completed send cycle", name);
long runEndTime = System.currentTimeMillis();
long totalTime = runEndTime - runStartTime;
long totalKB = totalBytesSent / 1024;
double rate = totalKB / (totalTime / 1000.0);
LOG.log(Level.INFO, "{0}: Sent {1} bytes", new Object[] { name,
totalBytesSent });
LOG.log(Level.INFO, "{0}: Average throughput: {1} KB/s",
new Object[] { name, rate });
finishLatch.countDown();
running.set(false);
}
public boolean isRunning() {
return running.get();
}
public boolean waitForFinish(long timeout, TimeUnit timeoutUnit)
throws InterruptedException {
return finishLatch.await(timeout, timeoutUnit);
}
}
}

View File

@ -53,176 +53,183 @@ import org.junit.Test;
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
*/
public class HttpTunnelTest
{
public class HttpTunnelTest {
private HttpTunnelClientChannelFactory clientFactory;
private HttpTunnelClientChannelFactory clientFactory;
private HttpTunnelServerChannelFactory serverFactory;
private HttpTunnelServerChannelFactory serverFactory;
private ClientBootstrap clientBootstrap;
private ClientBootstrap clientBootstrap;
private ServerBootstrap serverBootstrap;
private ServerBootstrap serverBootstrap;
ChannelGroup activeConnections;
ChannelGroup activeConnections;
ChannelHandler clientCaptureHandler;
ChannelHandler clientCaptureHandler;
ServerEndHandler connectionCaptureHandler;
ServerEndHandler connectionCaptureHandler;
Channel serverEnd;
Channel serverEnd;
CountDownLatch serverEndLatch;
CountDownLatch serverEndLatch;
ChannelBuffer receivedBytes;
ChannelBuffer receivedBytes;
CountDownLatch messageReceivedLatch;
CountDownLatch messageReceivedLatch;
ChannelBuffer clientReceivedBytes;
ChannelBuffer clientReceivedBytes;
CountDownLatch clientMessageReceivedLatch;
CountDownLatch clientMessageReceivedLatch;
private Channel serverChannel;
private Channel serverChannel;
@Before
public void setUp() throws UnknownHostException
{
activeConnections = new DefaultChannelGroup();
clientFactory = new HttpTunnelClientChannelFactory(new NioClientSocketChannelFactory(
Executors.newCachedThreadPool(), Executors.newCachedThreadPool()));
serverFactory = new HttpTunnelServerChannelFactory(new NioServerSocketChannelFactory(
Executors.newCachedThreadPool(), Executors.newCachedThreadPool()));
@Before
public void setUp() throws UnknownHostException {
activeConnections = new DefaultChannelGroup();
clientFactory =
new HttpTunnelClientChannelFactory(
new NioClientSocketChannelFactory(
Executors.newCachedThreadPool(),
Executors.newCachedThreadPool()));
serverFactory =
new HttpTunnelServerChannelFactory(
new NioServerSocketChannelFactory(
Executors.newCachedThreadPool(),
Executors.newCachedThreadPool()));
clientBootstrap = new ClientBootstrap(clientFactory);
clientBootstrap = new ClientBootstrap(clientFactory);
clientCaptureHandler = new ClientEndHandler();
clientBootstrap.setPipelineFactory(new ChannelPipelineFactory()
{
clientCaptureHandler = new ClientEndHandler();
clientBootstrap.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() throws Exception
{
ChannelPipeline pipeline = Channels.pipeline();
pipeline.addLast("clientCapture", clientCaptureHandler);
return pipeline;
}
});
public ChannelPipeline getPipeline() throws Exception {
ChannelPipeline pipeline = Channels.pipeline();
pipeline.addLast("clientCapture", clientCaptureHandler);
return pipeline;
}
});
clientReceivedBytes = ChannelBuffers.dynamicBuffer();
clientMessageReceivedLatch = new CountDownLatch(1);
clientReceivedBytes = ChannelBuffers.dynamicBuffer();
clientMessageReceivedLatch = new CountDownLatch(1);
serverBootstrap = new ServerBootstrap(serverFactory);
serverBootstrap = new ServerBootstrap(serverFactory);
connectionCaptureHandler = new ServerEndHandler();
serverBootstrap.setPipelineFactory(new ChannelPipelineFactory()
{
connectionCaptureHandler = new ServerEndHandler();
serverBootstrap.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() throws Exception
{
ChannelPipeline pipeline = Channels.pipeline();
pipeline.addLast("capture", connectionCaptureHandler);
return pipeline;
}
});
public ChannelPipeline getPipeline() throws Exception {
ChannelPipeline pipeline = Channels.pipeline();
pipeline.addLast("capture", connectionCaptureHandler);
return pipeline;
}
});
serverEndLatch = new CountDownLatch(1);
receivedBytes = ChannelBuffers.dynamicBuffer();
messageReceivedLatch = new CountDownLatch(1);
serverEndLatch = new CountDownLatch(1);
receivedBytes = ChannelBuffers.dynamicBuffer();
messageReceivedLatch = new CountDownLatch(1);
serverChannel = serverBootstrap.bind(new InetSocketAddress(InetAddress.getLocalHost(), 12345));
activeConnections.add(serverChannel);
}
serverChannel =
serverBootstrap.bind(new InetSocketAddress(InetAddress
.getLocalHost(), 12345));
activeConnections.add(serverChannel);
}
@After
public void tearDown() throws Exception
{
activeConnections.disconnect().await(1000L);
clientBootstrap.releaseExternalResources();
serverBootstrap.releaseExternalResources();
}
@After
public void tearDown() throws Exception {
activeConnections.disconnect().await(1000L);
clientBootstrap.releaseExternalResources();
serverBootstrap.releaseExternalResources();
}
@Test(timeout = 2000)
public void testConnectClientToServer() throws Exception
{
ChannelFuture connectFuture = clientBootstrap.connect(new InetSocketAddress(InetAddress.getLocalHost(), 12345));
assertTrue(connectFuture.await(1000L));
assertTrue(connectFuture.isSuccess());
assertNotNull(connectFuture.getChannel());
@Test(timeout = 2000)
public void testConnectClientToServer() throws Exception {
ChannelFuture connectFuture =
clientBootstrap.connect(new InetSocketAddress(InetAddress
.getLocalHost(), 12345));
assertTrue(connectFuture.await(1000L));
assertTrue(connectFuture.isSuccess());
assertNotNull(connectFuture.getChannel());
Channel clientChannel = connectFuture.getChannel();
activeConnections.add(clientChannel);
assertEquals(serverChannel.getLocalAddress(), clientChannel.getRemoteAddress());
Channel clientChannel = connectFuture.getChannel();
activeConnections.add(clientChannel);
assertEquals(serverChannel.getLocalAddress(),
clientChannel.getRemoteAddress());
assertTrue(serverEndLatch.await(1000, TimeUnit.MILLISECONDS));
assertNotNull(serverEnd);
assertEquals(clientChannel.getLocalAddress(), serverEnd.getRemoteAddress());
}
assertTrue(serverEndLatch.await(1000, TimeUnit.MILLISECONDS));
assertNotNull(serverEnd);
assertEquals(clientChannel.getLocalAddress(),
serverEnd.getRemoteAddress());
}
@Test
public void testSendDataFromClientToServer() throws Exception
{
ChannelFuture connectFuture = clientBootstrap.connect(new InetSocketAddress(InetAddress.getLocalHost(), 12345));
assertTrue(connectFuture.await(1000L));
@Test
public void testSendDataFromClientToServer() throws Exception {
ChannelFuture connectFuture =
clientBootstrap.connect(new InetSocketAddress(InetAddress
.getLocalHost(), 12345));
assertTrue(connectFuture.await(1000L));
Channel clientEnd = connectFuture.getChannel();
activeConnections.add(clientEnd);
Channel clientEnd = connectFuture.getChannel();
activeConnections.add(clientEnd);
assertTrue(serverEndLatch.await(1000, TimeUnit.MILLISECONDS));
assertTrue(serverEndLatch.await(1000, TimeUnit.MILLISECONDS));
ChannelFuture writeFuture = Channels.write(clientEnd, NettyTestUtils.createData(100L));
assertTrue(writeFuture.await(1000L));
assertTrue(writeFuture.isSuccess());
ChannelFuture writeFuture =
Channels.write(clientEnd, NettyTestUtils.createData(100L));
assertTrue(writeFuture.await(1000L));
assertTrue(writeFuture.isSuccess());
assertTrue(messageReceivedLatch.await(1000L, TimeUnit.MILLISECONDS));
assertEquals(100L, receivedBytes.readLong());
}
assertTrue(messageReceivedLatch.await(1000L, TimeUnit.MILLISECONDS));
assertEquals(100L, receivedBytes.readLong());
}
@Test
public void testSendDataFromServerToClient() throws Exception
{
ChannelFuture connectFuture = clientBootstrap.connect(new InetSocketAddress(InetAddress.getLocalHost(), 12345));
assertTrue(connectFuture.await(1000L));
@Test
public void testSendDataFromServerToClient() throws Exception {
ChannelFuture connectFuture =
clientBootstrap.connect(new InetSocketAddress(InetAddress
.getLocalHost(), 12345));
assertTrue(connectFuture.await(1000L));
Channel clientEnd = connectFuture.getChannel();
activeConnections.add(clientEnd);
Channel clientEnd = connectFuture.getChannel();
activeConnections.add(clientEnd);
assertTrue(serverEndLatch.await(1000, TimeUnit.MILLISECONDS));
assertTrue(serverEndLatch.await(1000, TimeUnit.MILLISECONDS));
ChannelFuture writeFuture = Channels.write(serverEnd, NettyTestUtils.createData(4321L));
assertTrue(writeFuture.await(1000L));
assertTrue(writeFuture.isSuccess());
ChannelFuture writeFuture =
Channels.write(serverEnd, NettyTestUtils.createData(4321L));
assertTrue(writeFuture.await(1000L));
assertTrue(writeFuture.isSuccess());
assertTrue(clientMessageReceivedLatch.await(1000, TimeUnit.MILLISECONDS));
assertEquals(4321L, clientReceivedBytes.readLong());
}
assertTrue(clientMessageReceivedLatch
.await(1000, TimeUnit.MILLISECONDS));
assertEquals(4321L, clientReceivedBytes.readLong());
}
class ServerEndHandler extends SimpleChannelUpstreamHandler
{
class ServerEndHandler extends SimpleChannelUpstreamHandler {
@Override
public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception
{
serverEnd = e.getChannel();
activeConnections.add(serverEnd);
serverEndLatch.countDown();
super.channelConnected(ctx, e);
}
@Override
public void channelConnected(ChannelHandlerContext ctx,
ChannelStateEvent e) throws Exception {
serverEnd = e.getChannel();
activeConnections.add(serverEnd);
serverEndLatch.countDown();
super.channelConnected(ctx, e);
}
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception
{
receivedBytes.writeBytes((ChannelBuffer) e.getMessage());
messageReceivedLatch.countDown();
}
}
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
throws Exception {
receivedBytes.writeBytes((ChannelBuffer) e.getMessage());
messageReceivedLatch.countDown();
}
}
class ClientEndHandler extends SimpleChannelUpstreamHandler
{
class ClientEndHandler extends SimpleChannelUpstreamHandler {
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception
{
clientReceivedBytes.writeBytes((ChannelBuffer) e.getMessage());
clientMessageReceivedLatch.countDown();
}
}
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e)
throws Exception {
clientReceivedBytes.writeBytes((ChannelBuffer) e.getMessage());
clientMessageReceivedLatch.countDown();
}
}
}

View File

@ -27,40 +27,35 @@ import org.jboss.netty.channel.ChannelFuture;
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
*/
public class MockChannelStateListener implements HttpTunnelClientWorkerOwner
{
public class MockChannelStateListener implements HttpTunnelClientWorkerOwner {
public boolean fullyEstablished = false;
public boolean fullyEstablished = false;
public List<ChannelBuffer> messages = new ArrayList<ChannelBuffer>();
public List<ChannelBuffer> messages = new ArrayList<ChannelBuffer>();
public String tunnelId = null;
public String tunnelId = null;
public String serverHostName = null;
public String serverHostName = null;
public void fullyEstablished()
{
fullyEstablished = true;
}
public void fullyEstablished() {
fullyEstablished = true;
}
public void onConnectRequest(ChannelFuture connectFuture, InetSocketAddress remoteAddress)
{
// not relevant for test
}
public void onConnectRequest(ChannelFuture connectFuture,
InetSocketAddress remoteAddress) {
// not relevant for test
}
public void onMessageReceived(ChannelBuffer content)
{
messages.add(content);
}
public void onMessageReceived(ChannelBuffer content) {
messages.add(content);
}
public void onTunnelOpened(String tunnelId)
{
this.tunnelId = tunnelId;
}
public void onTunnelOpened(String tunnelId) {
this.tunnelId = tunnelId;
}
public String getServerHostName()
{
return serverHostName;
}
public String getServerHostName() {
return serverHostName;
}
}

View File

@ -43,158 +43,143 @@ import org.jboss.netty.channel.UpstreamMessageEvent;
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
*/
public class NettyTestUtils
{
public class NettyTestUtils {
public static ByteBuffer convertReadable(ChannelBuffer b)
{
int startIndex = b.readerIndex();
ByteBuffer converted = ByteBuffer.allocate(b.readableBytes());
b.readBytes(converted);
b.readerIndex(startIndex);
converted.flip();
return converted;
}
public static ByteBuffer convertReadable(ChannelBuffer b) {
int startIndex = b.readerIndex();
ByteBuffer converted = ByteBuffer.allocate(b.readableBytes());
b.readBytes(converted);
b.readerIndex(startIndex);
converted.flip();
return converted;
}
public static void assertEquals(ChannelBuffer expected, ChannelBuffer actual)
{
if (expected.readableBytes() != actual.readableBytes())
{
Assert.failNotEquals("channel buffers have differing readable sizes", expected.readableBytes(),
actual.readableBytes());
}
public static void assertEquals(ChannelBuffer expected, ChannelBuffer actual) {
if (expected.readableBytes() != actual.readableBytes()) {
Assert.failNotEquals(
"channel buffers have differing readable sizes",
expected.readableBytes(), actual.readableBytes());
}
int startPositionExpected = expected.readerIndex();
int startPositionActual = actual.readerIndex();
int position = 0;
while (expected.readable())
{
byte expectedByte = expected.readByte();
byte actualByte = actual.readByte();
if (expectedByte != actualByte)
{
Assert.failNotEquals("channel buffers differ at position " + position, expectedByte, actualByte);
}
position++;
}
expected.readerIndex(startPositionExpected);
actual.readerIndex(startPositionActual);
}
public static boolean checkEquals(ChannelBuffer expected, ChannelBuffer actual)
{
if (expected.readableBytes() != actual.readableBytes())
{
return false;
}
int position = 0;
while (expected.readable())
{
byte expectedByte = expected.readByte();
byte actualByte = actual.readByte();
if (expectedByte != actualByte)
{
return false;
}
position++;
}
return true;
}
public static List<ChannelBuffer> splitIntoChunks(int chunkSize, ChannelBuffer... buffers)
{
LinkedList<ChannelBuffer> chunks = new LinkedList<ChannelBuffer>();
ArrayList<ChannelBuffer> sourceBuffers = new ArrayList<ChannelBuffer>();
Collections.addAll(sourceBuffers, buffers);
Iterator<ChannelBuffer> sourceIter = sourceBuffers.iterator();
ChannelBuffer chunk = ChannelBuffers.buffer(chunkSize);
while (sourceIter.hasNext())
{
ChannelBuffer source = sourceIter.next();
int index = source.readerIndex();
while (source.writerIndex() > index)
{
int fragmentSize = Math.min(source.writerIndex() - index, chunk.writableBytes());
chunk.writeBytes(source, index, fragmentSize);
if (!chunk.writable())
{
chunks.add(chunk);
chunk = ChannelBuffers.buffer(chunkSize);
int startPositionExpected = expected.readerIndex();
int startPositionActual = actual.readerIndex();
int position = 0;
while (expected.readable()) {
byte expectedByte = expected.readByte();
byte actualByte = actual.readByte();
if (expectedByte != actualByte) {
Assert.failNotEquals("channel buffers differ at position " +
position, expectedByte, actualByte);
}
index += fragmentSize;
}
}
if (chunk.readable())
{
chunks.add(chunk);
}
position ++;
}
return chunks;
}
expected.readerIndex(startPositionExpected);
actual.readerIndex(startPositionActual);
}
public static ChannelBuffer createData(long containedNumber)
{
ChannelBuffer data = ChannelBuffers.dynamicBuffer();
data.writeLong(containedNumber);
return data;
}
public static boolean checkEquals(ChannelBuffer expected,
ChannelBuffer actual) {
if (expected.readableBytes() != actual.readableBytes()) {
return false;
}
public static void checkIsUpstreamMessageEventContainingData(ChannelEvent event, ChannelBuffer expectedData)
{
ChannelBuffer data = checkIsUpstreamMessageEvent(event, ChannelBuffer.class);
assertEquals(expectedData, data);
}
int position = 0;
while (expected.readable()) {
byte expectedByte = expected.readByte();
byte actualByte = actual.readByte();
if (expectedByte != actualByte) {
return false;
}
public static <T> T checkIsUpstreamMessageEvent(ChannelEvent event, Class<T> expectedMessageType)
{
assertTrue(event instanceof UpstreamMessageEvent);
UpstreamMessageEvent messageEvent = (UpstreamMessageEvent) event;
assertTrue(expectedMessageType.isInstance(messageEvent.getMessage()));
return expectedMessageType.cast(messageEvent.getMessage());
}
position ++;
}
public static <T> T checkIsDownstreamMessageEvent(ChannelEvent event, Class<T> expectedMessageType)
{
assertTrue(event instanceof DownstreamMessageEvent);
DownstreamMessageEvent messageEvent = (DownstreamMessageEvent) event;
assertTrue(expectedMessageType.isInstance(messageEvent.getMessage()));
return expectedMessageType.cast(messageEvent.getMessage());
}
return true;
}
public static InetSocketAddress createAddress(byte[] addr, int port)
{
try
{
return new InetSocketAddress(InetAddress.getByAddress(addr), port);
}
catch (UnknownHostException e)
{
throw new RuntimeException("Bad address in test");
}
}
public static List<ChannelBuffer> splitIntoChunks(int chunkSize,
ChannelBuffer... buffers) {
LinkedList<ChannelBuffer> chunks = new LinkedList<ChannelBuffer>();
public static Throwable checkIsExceptionEvent(ChannelEvent ev)
{
assertTrue(ev instanceof ExceptionEvent);
ExceptionEvent exceptionEv = (ExceptionEvent) ev;
return exceptionEv.getCause();
}
ArrayList<ChannelBuffer> sourceBuffers = new ArrayList<ChannelBuffer>();
Collections.addAll(sourceBuffers, buffers);
Iterator<ChannelBuffer> sourceIter = sourceBuffers.iterator();
ChannelBuffer chunk = ChannelBuffers.buffer(chunkSize);
while (sourceIter.hasNext()) {
ChannelBuffer source = sourceIter.next();
public static ChannelStateEvent checkIsStateEvent(ChannelEvent event, ChannelState expectedState,
Object expectedValue)
{
assertTrue(event instanceof ChannelStateEvent);
ChannelStateEvent stateEvent = (ChannelStateEvent) event;
Assert.assertEquals(expectedState, stateEvent.getState());
Assert.assertEquals(expectedValue, stateEvent.getValue());
return stateEvent;
}
int index = source.readerIndex();
while (source.writerIndex() > index) {
int fragmentSize =
Math.min(source.writerIndex() - index,
chunk.writableBytes());
chunk.writeBytes(source, index, fragmentSize);
if (!chunk.writable()) {
chunks.add(chunk);
chunk = ChannelBuffers.buffer(chunkSize);
}
index += fragmentSize;
}
}
if (chunk.readable()) {
chunks.add(chunk);
}
return chunks;
}
public static ChannelBuffer createData(long containedNumber) {
ChannelBuffer data = ChannelBuffers.dynamicBuffer();
data.writeLong(containedNumber);
return data;
}
public static void checkIsUpstreamMessageEventContainingData(
ChannelEvent event, ChannelBuffer expectedData) {
ChannelBuffer data =
checkIsUpstreamMessageEvent(event, ChannelBuffer.class);
assertEquals(expectedData, data);
}
public static <T> T checkIsUpstreamMessageEvent(ChannelEvent event,
Class<T> expectedMessageType) {
assertTrue(event instanceof UpstreamMessageEvent);
UpstreamMessageEvent messageEvent = (UpstreamMessageEvent) event;
assertTrue(expectedMessageType.isInstance(messageEvent.getMessage()));
return expectedMessageType.cast(messageEvent.getMessage());
}
public static <T> T checkIsDownstreamMessageEvent(ChannelEvent event,
Class<T> expectedMessageType) {
assertTrue(event instanceof DownstreamMessageEvent);
DownstreamMessageEvent messageEvent = (DownstreamMessageEvent) event;
assertTrue(expectedMessageType.isInstance(messageEvent.getMessage()));
return expectedMessageType.cast(messageEvent.getMessage());
}
public static InetSocketAddress createAddress(byte[] addr, int port) {
try {
return new InetSocketAddress(InetAddress.getByAddress(addr), port);
} catch (UnknownHostException e) {
throw new RuntimeException("Bad address in test");
}
}
public static Throwable checkIsExceptionEvent(ChannelEvent ev) {
assertTrue(ev instanceof ExceptionEvent);
ExceptionEvent exceptionEv = (ExceptionEvent) ev;
return exceptionEv.getCause();
}
public static ChannelStateEvent checkIsStateEvent(ChannelEvent event,
ChannelState expectedState, Object expectedValue) {
assertTrue(event instanceof ChannelStateEvent);
ChannelStateEvent stateEvent = (ChannelStateEvent) event;
Assert.assertEquals(expectedState, stateEvent.getState());
Assert.assertEquals(expectedValue, stateEvent.getValue());
return stateEvent;
}
}

View File

@ -29,116 +29,96 @@ import org.junit.Test;
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
*/
public class NettyTestUtilsTest
{
public class NettyTestUtilsTest {
@Test
public void testSplitIntoChunks()
{
ChannelBuffer a = createFullBuffer(20, (byte) 0);
ChannelBuffer b = createFullBuffer(20, (byte) 1);
ChannelBuffer c = createFullBuffer(20, (byte) 2);
@Test
public void testSplitIntoChunks() {
ChannelBuffer a = createFullBuffer(20, (byte) 0);
ChannelBuffer b = createFullBuffer(20, (byte) 1);
ChannelBuffer c = createFullBuffer(20, (byte) 2);
List<ChannelBuffer> chunks = NettyTestUtils.splitIntoChunks(10, a, b, c);
assertEquals(6, chunks.size());
for (ChannelBuffer chunk : chunks)
{
assertEquals(10, chunk.readableBytes());
}
List<ChannelBuffer> chunks =
NettyTestUtils.splitIntoChunks(10, a, b, c);
assertEquals(6, chunks.size());
for (ChannelBuffer chunk: chunks) {
assertEquals(10, chunk.readableBytes());
}
// reader index should not be modified by splitIntoChunks()
assertEquals(0, a.readerIndex());
assertEquals(0, b.readerIndex());
assertEquals(0, c.readerIndex());
}
// reader index should not be modified by splitIntoChunks()
assertEquals(0, a.readerIndex());
assertEquals(0, b.readerIndex());
assertEquals(0, c.readerIndex());
}
@Test
public void testSplitIntoChunks_chunksCrossBoundaries()
{
ChannelBuffer a = createFullBuffer(5, (byte) 0);
ChannelBuffer b = createFullBuffer(5, (byte) 1);
ChannelBuffer c = createFullBuffer(5, (byte) 2);
@Test
public void testSplitIntoChunks_chunksCrossBoundaries() {
ChannelBuffer a = createFullBuffer(5, (byte) 0);
ChannelBuffer b = createFullBuffer(5, (byte) 1);
ChannelBuffer c = createFullBuffer(5, (byte) 2);
List<ChannelBuffer> chunks = NettyTestUtils.splitIntoChunks(4, a, b, c);
assertEquals(4, chunks.size());
checkBufferContains(chunks.get(0), new byte[]
{0, 0, 0, 0});
checkBufferContains(chunks.get(1), new byte[]
{0, 1, 1, 1});
checkBufferContains(chunks.get(2), new byte[]
{1, 1, 2, 2});
checkBufferContains(chunks.get(3), new byte[]
{2, 2, 2});
}
List<ChannelBuffer> chunks = NettyTestUtils.splitIntoChunks(4, a, b, c);
assertEquals(4, chunks.size());
checkBufferContains(chunks.get(0), new byte[] { 0, 0, 0, 0 });
checkBufferContains(chunks.get(1), new byte[] { 0, 1, 1, 1 });
checkBufferContains(chunks.get(2), new byte[] { 1, 1, 2, 2 });
checkBufferContains(chunks.get(3), new byte[] { 2, 2, 2 });
}
@Test
public void testSplitIntoChunks_smallestChunksPossible()
{
ChannelBuffer a = createFullBuffer(5, (byte) 0);
ChannelBuffer b = createFullBuffer(5, (byte) 1);
ChannelBuffer c = createFullBuffer(5, (byte) 2);
@Test
public void testSplitIntoChunks_smallestChunksPossible() {
ChannelBuffer a = createFullBuffer(5, (byte) 0);
ChannelBuffer b = createFullBuffer(5, (byte) 1);
ChannelBuffer c = createFullBuffer(5, (byte) 2);
List<ChannelBuffer> chunks = NettyTestUtils.splitIntoChunks(1, a, b, c);
assertEquals(15, chunks.size());
checkBufferContains(chunks.get(0), new byte[]
{0});
checkBufferContains(chunks.get(5), new byte[]
{1});
checkBufferContains(chunks.get(10), new byte[]
{2});
}
List<ChannelBuffer> chunks = NettyTestUtils.splitIntoChunks(1, a, b, c);
assertEquals(15, chunks.size());
checkBufferContains(chunks.get(0), new byte[] { 0 });
checkBufferContains(chunks.get(5), new byte[] { 1 });
checkBufferContains(chunks.get(10), new byte[] { 2 });
}
@Test
public void testSplitIntoChunks_sourceBuffersArePartiallyRead()
{
ChannelBuffer a = createFullBuffer(5, (byte) 0);
a.readerIndex(1);
ChannelBuffer b = createFullBuffer(5, (byte) 1);
b.readerIndex(2);
ChannelBuffer c = createFullBuffer(5, (byte) 2);
@Test
public void testSplitIntoChunks_sourceBuffersArePartiallyRead() {
ChannelBuffer a = createFullBuffer(5, (byte) 0);
a.readerIndex(1);
ChannelBuffer b = createFullBuffer(5, (byte) 1);
b.readerIndex(2);
ChannelBuffer c = createFullBuffer(5, (byte) 2);
// will be ignored, as fully read
ChannelBuffer d = createFullBuffer(5, (byte) 3);
d.readerIndex(5);
ChannelBuffer e = createFullBuffer(5, (byte) 4);
e.readerIndex(4);
// will be ignored, as fully read
ChannelBuffer d = createFullBuffer(5, (byte) 3);
d.readerIndex(5);
ChannelBuffer e = createFullBuffer(5, (byte) 4);
e.readerIndex(4);
List<ChannelBuffer> chunks = NettyTestUtils.splitIntoChunks(3, a, b, c, d, e);
checkBufferContains(chunks.get(0), new byte[]
{0, 0, 0});
checkBufferContains(chunks.get(1), new byte[]
{0, 1, 1});
checkBufferContains(chunks.get(2), new byte[]
{1, 2, 2});
checkBufferContains(chunks.get(3), new byte[]
{2, 2, 2});
checkBufferContains(chunks.get(4), new byte[]
{4});
}
List<ChannelBuffer> chunks =
NettyTestUtils.splitIntoChunks(3, a, b, c, d, e);
checkBufferContains(chunks.get(0), new byte[] { 0, 0, 0 });
checkBufferContains(chunks.get(1), new byte[] { 0, 1, 1 });
checkBufferContains(chunks.get(2), new byte[] { 1, 2, 2 });
checkBufferContains(chunks.get(3), new byte[] { 2, 2, 2 });
checkBufferContains(chunks.get(4), new byte[] { 4 });
}
private void checkBufferContains(ChannelBuffer channelBuffer, byte[] bs)
{
if (channelBuffer.readableBytes() != bs.length)
{
fail("buffer does not have enough bytes");
}
private void checkBufferContains(ChannelBuffer channelBuffer, byte[] bs) {
if (channelBuffer.readableBytes() != bs.length) {
fail("buffer does not have enough bytes");
}
for (int i = 0; i < bs.length; i++)
{
assertEquals("byte at position " + i + " does not match", bs[i], channelBuffer.getByte(i));
}
}
for (int i = 0; i < bs.length; i ++) {
assertEquals("byte at position " + i + " does not match", bs[i],
channelBuffer.getByte(i));
}
}
private ChannelBuffer createFullBuffer(int size, byte value)
{
ChannelBuffer buffer = ChannelBuffers.buffer(size);
byte[] contents = new byte[size];
for (int i = 0; i < contents.length; i++)
{
contents[i] = value;
}
buffer.writeBytes(contents);
return buffer;
}
private ChannelBuffer createFullBuffer(int size, byte value) {
ChannelBuffer buffer = ChannelBuffers.buffer(size);
byte[] contents = new byte[size];
for (int i = 0; i < contents.length; i ++) {
contents[i] = value;
}
buffer.writeBytes(contents);
return buffer;
}
}

View File

@ -25,17 +25,17 @@ import org.jboss.netty.channel.ChannelUpstreamHandler;
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
*/
public class NullChannelHandler implements ChannelUpstreamHandler, ChannelDownstreamHandler
{
public class NullChannelHandler implements ChannelUpstreamHandler,
ChannelDownstreamHandler {
public void handleUpstream(ChannelHandlerContext ctx, ChannelEvent e) throws Exception
{
ctx.sendUpstream(e);
}
public void handleUpstream(ChannelHandlerContext ctx, ChannelEvent e)
throws Exception {
ctx.sendUpstream(e);
}
public void handleDownstream(ChannelHandlerContext ctx, ChannelEvent e) throws Exception
{
ctx.sendDownstream(e);
}
public void handleDownstream(ChannelHandlerContext ctx, ChannelEvent e)
throws Exception {
ctx.sendDownstream(e);
}
}

View File

@ -6,29 +6,27 @@ import static org.jboss.netty.channel.socket.http.SaturationStateChange.*;
import org.junit.Before;
import org.junit.Test;
public class SaturationManagerTest
{
public class SaturationManagerTest {
private SaturationManager manager;
@Before
public void setUp() {
manager = new SaturationManager(100L, 200L);
}
@Test
public void testQueueSizeChanged()
{
assertEquals(NO_CHANGE, manager.queueSizeChanged(100L));
assertEquals(NO_CHANGE, manager.queueSizeChanged(99L));
assertEquals(NO_CHANGE, manager.queueSizeChanged(1L));
assertEquals(SATURATED, manager.queueSizeChanged(1L));
assertEquals(NO_CHANGE, manager.queueSizeChanged(10L));
assertEquals(NO_CHANGE, manager.queueSizeChanged(-10L));
assertEquals(NO_CHANGE, manager.queueSizeChanged(-1L));
assertEquals(NO_CHANGE, manager.queueSizeChanged(-1L));
assertEquals(DESATURATED, manager.queueSizeChanged(-99L));
assertEquals(NO_CHANGE, manager.queueSizeChanged(-100L));
}
private SaturationManager manager;
@Before
public void setUp() {
manager = new SaturationManager(100L, 200L);
}
@Test
public void testQueueSizeChanged() {
assertEquals(NO_CHANGE, manager.queueSizeChanged(100L));
assertEquals(NO_CHANGE, manager.queueSizeChanged(99L));
assertEquals(NO_CHANGE, manager.queueSizeChanged(1L));
assertEquals(SATURATED, manager.queueSizeChanged(1L));
assertEquals(NO_CHANGE, manager.queueSizeChanged(10L));
assertEquals(NO_CHANGE, manager.queueSizeChanged(-10L));
assertEquals(NO_CHANGE, manager.queueSizeChanged(-1L));
assertEquals(NO_CHANGE, manager.queueSizeChanged(-1L));
assertEquals(DESATURATED, manager.queueSizeChanged(-99L));
assertEquals(NO_CHANGE, manager.queueSizeChanged(-100L));
}
}

View File

@ -38,144 +38,147 @@ import org.junit.runner.RunWith;
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
*/
@RunWith(JMock.class)
public class ServerMessageSwitchTest
{
public class ServerMessageSwitchTest {
public static final InetSocketAddress REMOTE_ADDRESS = InetSocketAddress.createUnresolved("test.client.com", 52354);
public static final InetSocketAddress REMOTE_ADDRESS = InetSocketAddress
.createUnresolved("test.client.com", 52354);
private final JUnit4Mockery mockContext = new JUnit4Mockery();
private final JUnit4Mockery mockContext = new JUnit4Mockery();
private ServerMessageSwitch messageSwitch;
private ServerMessageSwitch messageSwitch;
HttpTunnelAcceptedChannelFactory newChannelFactory;
HttpTunnelAcceptedChannelFactory newChannelFactory;
private FakeChannelSink responseCatcher;
private FakeChannelSink responseCatcher;
private FakeSocketChannel htunChannel;
private FakeSocketChannel htunChannel;
private FakeSocketChannel requesterChannel;
private FakeSocketChannel requesterChannel;
private HttpTunnelAcceptedChannelReceiver htunAcceptedChannel;
private HttpTunnelAcceptedChannelReceiver htunAcceptedChannel;
@Before
public void setUp() throws Exception
{
newChannelFactory = mockContext.mock(HttpTunnelAcceptedChannelFactory.class);
messageSwitch = new ServerMessageSwitch(newChannelFactory);
@Before
public void setUp() throws Exception {
newChannelFactory =
mockContext.mock(HttpTunnelAcceptedChannelFactory.class);
messageSwitch = new ServerMessageSwitch(newChannelFactory);
htunAcceptedChannel = mockContext.mock(HttpTunnelAcceptedChannelReceiver.class);
createRequesterChannel();
htunAcceptedChannel =
mockContext.mock(HttpTunnelAcceptedChannelReceiver.class);
createRequesterChannel();
mockContext.checking(new Expectations()
{
{
one(newChannelFactory).newChannel(with(any(String.class)), with(equal(REMOTE_ADDRESS)));
will(returnValue(htunAcceptedChannel));
ignoring(newChannelFactory).generateTunnelId();
will(returnValue("TEST_TUNNEL"));
}
});
}
mockContext.checking(new Expectations() {
{
one(newChannelFactory).newChannel(with(any(String.class)),
with(equal(REMOTE_ADDRESS)));
will(returnValue(htunAcceptedChannel));
ignoring(newChannelFactory).generateTunnelId();
will(returnValue("TEST_TUNNEL"));
}
});
}
private FakeSocketChannel createRequesterChannel()
{
ChannelPipeline requesterChannelPipeline = Channels.pipeline();
responseCatcher = new FakeChannelSink();
requesterChannel = new FakeSocketChannel(null, null, requesterChannelPipeline, responseCatcher);
responseCatcher.events.clear();
private FakeSocketChannel createRequesterChannel() {
ChannelPipeline requesterChannelPipeline = Channels.pipeline();
responseCatcher = new FakeChannelSink();
requesterChannel =
new FakeSocketChannel(null, null, requesterChannelPipeline,
responseCatcher);
responseCatcher.events.clear();
return requesterChannel;
}
return requesterChannel;
}
@Test
public void testRouteInboundData()
{
final ChannelBuffer inboundData = ChannelBuffers.dynamicBuffer();
inboundData.writeLong(1234L);
@Test
public void testRouteInboundData() {
final ChannelBuffer inboundData = ChannelBuffers.dynamicBuffer();
inboundData.writeLong(1234L);
mockContext.checking(new Expectations()
{
{
one(htunAcceptedChannel).dataReceived(with(same(inboundData)));
}
});
mockContext.checking(new Expectations() {
{
one(htunAcceptedChannel).dataReceived(with(same(inboundData)));
}
});
String tunnelId = messageSwitch.createTunnel(REMOTE_ADDRESS);
messageSwitch.routeInboundData(tunnelId, inboundData);
mockContext.assertIsSatisfied();
}
String tunnelId = messageSwitch.createTunnel(REMOTE_ADDRESS);
messageSwitch.routeInboundData(tunnelId, inboundData);
mockContext.assertIsSatisfied();
}
@Test
public void testRouteOutboundData_onPoll()
{
ChannelBuffer outboundData = ChannelBuffers.dynamicBuffer();
outboundData.writeLong(1234L);
@Test
public void testRouteOutboundData_onPoll() {
ChannelBuffer outboundData = ChannelBuffers.dynamicBuffer();
outboundData.writeLong(1234L);
String tunnelId = messageSwitch.createTunnel(REMOTE_ADDRESS);
messageSwitch.routeOutboundData(tunnelId, outboundData, Channels.future(htunChannel));
messageSwitch.pollOutboundData(tunnelId, requesterChannel);
String tunnelId = messageSwitch.createTunnel(REMOTE_ADDRESS);
messageSwitch.routeOutboundData(tunnelId, outboundData,
Channels.future(htunChannel));
messageSwitch.pollOutboundData(tunnelId, requesterChannel);
assertEquals(1, responseCatcher.events.size());
HttpResponse response = NettyTestUtils.checkIsDownstreamMessageEvent(responseCatcher.events.poll(),
HttpResponse.class);
NettyTestUtils.assertEquals(outboundData, response.getContent());
}
assertEquals(1, responseCatcher.events.size());
HttpResponse response =
NettyTestUtils.checkIsDownstreamMessageEvent(
responseCatcher.events.poll(), HttpResponse.class);
NettyTestUtils.assertEquals(outboundData, response.getContent());
}
@Test
public void testRouteOutboundData_withDanglingRequest()
{
String tunnelId = messageSwitch.createTunnel(REMOTE_ADDRESS);
messageSwitch.pollOutboundData(tunnelId, requesterChannel);
assertEquals(0, responseCatcher.events.size());
@Test
public void testRouteOutboundData_withDanglingRequest() {
String tunnelId = messageSwitch.createTunnel(REMOTE_ADDRESS);
messageSwitch.pollOutboundData(tunnelId, requesterChannel);
assertEquals(0, responseCatcher.events.size());
ChannelBuffer outboundData = ChannelBuffers.dynamicBuffer();
outboundData.writeLong(1234L);
ChannelBuffer outboundData = ChannelBuffers.dynamicBuffer();
outboundData.writeLong(1234L);
messageSwitch.routeOutboundData(tunnelId, outboundData, Channels.future(htunChannel));
assertEquals(1, responseCatcher.events.size());
HttpResponse response = NettyTestUtils.checkIsDownstreamMessageEvent(responseCatcher.events.poll(),
HttpResponse.class);
NettyTestUtils.assertEquals(outboundData, response.getContent());
}
messageSwitch.routeOutboundData(tunnelId, outboundData,
Channels.future(htunChannel));
assertEquals(1, responseCatcher.events.size());
HttpResponse response =
NettyTestUtils.checkIsDownstreamMessageEvent(
responseCatcher.events.poll(), HttpResponse.class);
NettyTestUtils.assertEquals(outboundData, response.getContent());
}
@Test
public void testCloseTunnel()
{
String tunnelId = messageSwitch.createTunnel(REMOTE_ADDRESS);
messageSwitch.serverCloseTunnel(tunnelId);
assertEquals(TunnelStatus.CLOSED, messageSwitch.routeInboundData(tunnelId, ChannelBuffers.dynamicBuffer()));
}
@Test
public void testCloseTunnel() {
String tunnelId = messageSwitch.createTunnel(REMOTE_ADDRESS);
messageSwitch.serverCloseTunnel(tunnelId);
assertEquals(
TunnelStatus.CLOSED,
messageSwitch.routeInboundData(tunnelId,
ChannelBuffers.dynamicBuffer()));
}
/* TODO: require tests that check the various permutations of a client sending or polling
data after the server has closed the connection */
/* TODO: require tests that check the various permutations of a client sending or polling
data after the server has closed the connection */
/* TODO: require tests that check what happens when a client closes a connection */
/* TODO: require tests that check what happens when a client closes a connection */
@Test
public void testRouteInboundDataIgnoredAfterClose()
{
ChannelBuffer data = NettyTestUtils.createData(1234L);
String tunnelId = messageSwitch.createTunnel(REMOTE_ADDRESS);
messageSwitch.serverCloseTunnel(tunnelId);
@Test
public void testRouteInboundDataIgnoredAfterClose() {
ChannelBuffer data = NettyTestUtils.createData(1234L);
String tunnelId = messageSwitch.createTunnel(REMOTE_ADDRESS);
messageSwitch.serverCloseTunnel(tunnelId);
mockContext.checking(new Expectations()
{
{
never(htunAcceptedChannel).dataReceived(with(any(ChannelBuffer.class)));
}
});
mockContext.checking(new Expectations() {
{
never(htunAcceptedChannel).dataReceived(
with(any(ChannelBuffer.class)));
}
});
messageSwitch.routeInboundData(tunnelId, data);
mockContext.assertIsSatisfied();
}
messageSwitch.routeInboundData(tunnelId, data);
mockContext.assertIsSatisfied();
}
@Test
public void testRouteOutboundDataIgnoredAfterClose()
{
ChannelBuffer data = NettyTestUtils.createData(1234L);
String tunnelId = messageSwitch.createTunnel(REMOTE_ADDRESS);
messageSwitch.serverCloseTunnel(tunnelId);
messageSwitch.routeOutboundData(tunnelId, data, Channels.future(htunChannel));
assertEquals(0, responseCatcher.events.size());
}
@Test
public void testRouteOutboundDataIgnoredAfterClose() {
ChannelBuffer data = NettyTestUtils.createData(1234L);
String tunnelId = messageSwitch.createTunnel(REMOTE_ADDRESS);
messageSwitch.serverCloseTunnel(tunnelId);
messageSwitch.routeOutboundData(tunnelId, data,
Channels.future(htunChannel));
assertEquals(0, responseCatcher.events.size());
}
}

View File

@ -27,16 +27,15 @@ import org.jboss.netty.channel.ChannelUpstreamHandler;
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
*/
public class UpstreamEventCatcher implements ChannelUpstreamHandler
{
public class UpstreamEventCatcher implements ChannelUpstreamHandler {
public static final String NAME = "upstreamCatcher";
public static final String NAME = "upstreamCatcher";
public Queue<ChannelEvent> events = new LinkedList<ChannelEvent>();
public Queue<ChannelEvent> events = new LinkedList<ChannelEvent>();
public void handleUpstream(ChannelHandlerContext ctx, ChannelEvent e) throws Exception
{
events.add(e);
}
public void handleUpstream(ChannelHandlerContext ctx, ChannelEvent e)
throws Exception {
events.add(e);
}
}

View File

@ -34,123 +34,124 @@ import org.junit.Test;
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
*/
public class WriteFragmenterTest
{
public class WriteFragmenterTest {
private FakeSocketChannel channel;
private FakeSocketChannel channel;
private WriteFragmenter fragmenter;
private WriteFragmenter fragmenter;
private FakeChannelSink downstreamCatcher;
private FakeChannelSink downstreamCatcher;
@Before
public void setUp() throws Exception
{
fragmenter = new WriteFragmenter(100);
@Before
public void setUp() throws Exception {
fragmenter = new WriteFragmenter(100);
ChannelPipeline pipeline = Channels.pipeline();
pipeline.addLast(WriteFragmenter.NAME, fragmenter);
downstreamCatcher = new FakeChannelSink();
channel = new FakeSocketChannel(null, null, pipeline, downstreamCatcher);
}
ChannelPipeline pipeline = Channels.pipeline();
pipeline.addLast(WriteFragmenter.NAME, fragmenter);
downstreamCatcher = new FakeChannelSink();
channel =
new FakeSocketChannel(null, null, pipeline, downstreamCatcher);
}
@Test
public void testLeavesWritesBeneathThresholdUntouched()
{
ChannelBuffer data = ChannelBuffers.wrappedBuffer(new byte[99]);
Channels.write(channel, data);
@Test
public void testLeavesWritesBeneathThresholdUntouched() {
ChannelBuffer data = ChannelBuffers.wrappedBuffer(new byte[99]);
Channels.write(channel, data);
assertEquals(1, downstreamCatcher.events.size());
ChannelBuffer sentData = NettyTestUtils.checkIsDownstreamMessageEvent(downstreamCatcher.events.poll(),
ChannelBuffer.class);
assertSame(data, sentData);
}
assertEquals(1, downstreamCatcher.events.size());
ChannelBuffer sentData =
NettyTestUtils.checkIsDownstreamMessageEvent(
downstreamCatcher.events.poll(), ChannelBuffer.class);
assertSame(data, sentData);
}
@Test
public void testLeavesMessagesOnThresholdUntouched()
{
ChannelBuffer data = ChannelBuffers.wrappedBuffer(new byte[100]);
Channels.write(channel, data);
@Test
public void testLeavesMessagesOnThresholdUntouched() {
ChannelBuffer data = ChannelBuffers.wrappedBuffer(new byte[100]);
Channels.write(channel, data);
assertEquals(1, downstreamCatcher.events.size());
ChannelBuffer sentData = NettyTestUtils.checkIsDownstreamMessageEvent(downstreamCatcher.events.poll(),
ChannelBuffer.class);
assertSame(data, sentData);
}
assertEquals(1, downstreamCatcher.events.size());
ChannelBuffer sentData =
NettyTestUtils.checkIsDownstreamMessageEvent(
downstreamCatcher.events.poll(), ChannelBuffer.class);
assertSame(data, sentData);
}
@Test
public void testSplitsMessagesAboveThreshold_twoChunks()
{
ChannelBuffer data = ChannelBuffers.wrappedBuffer(new byte[101]);
Channels.write(channel, data);
@Test
public void testSplitsMessagesAboveThreshold_twoChunks() {
ChannelBuffer data = ChannelBuffers.wrappedBuffer(new byte[101]);
Channels.write(channel, data);
assertEquals(2, downstreamCatcher.events.size());
ChannelBuffer chunk1 = NettyTestUtils.checkIsDownstreamMessageEvent(downstreamCatcher.events.poll(),
ChannelBuffer.class);
ChannelBuffer chunk2 = NettyTestUtils.checkIsDownstreamMessageEvent(downstreamCatcher.events.poll(),
ChannelBuffer.class);
assertEquals(100, chunk1.readableBytes());
assertEquals(1, chunk2.readableBytes());
}
assertEquals(2, downstreamCatcher.events.size());
ChannelBuffer chunk1 =
NettyTestUtils.checkIsDownstreamMessageEvent(
downstreamCatcher.events.poll(), ChannelBuffer.class);
ChannelBuffer chunk2 =
NettyTestUtils.checkIsDownstreamMessageEvent(
downstreamCatcher.events.poll(), ChannelBuffer.class);
assertEquals(100, chunk1.readableBytes());
assertEquals(1, chunk2.readableBytes());
}
@Test
public void testSplitsMessagesAboveThreshold_multipleChunks()
{
ChannelBuffer data = ChannelBuffers.wrappedBuffer(new byte[2540]);
Channels.write(channel, data);
@Test
public void testSplitsMessagesAboveThreshold_multipleChunks() {
ChannelBuffer data = ChannelBuffers.wrappedBuffer(new byte[2540]);
Channels.write(channel, data);
assertEquals(26, downstreamCatcher.events.size());
for (int i = 0; i < 25; i++)
{
ChannelBuffer chunk = NettyTestUtils.checkIsDownstreamMessageEvent(downstreamCatcher.events.poll(),
ChannelBuffer.class);
assertEquals(100, chunk.readableBytes());
}
assertEquals(26, downstreamCatcher.events.size());
for (int i = 0; i < 25; i ++) {
ChannelBuffer chunk =
NettyTestUtils.checkIsDownstreamMessageEvent(
downstreamCatcher.events.poll(),
ChannelBuffer.class);
assertEquals(100, chunk.readableBytes());
}
ChannelBuffer endChunk = NettyTestUtils.checkIsDownstreamMessageEvent(downstreamCatcher.events.poll(),
ChannelBuffer.class);
assertEquals(40, endChunk.readableBytes());
}
ChannelBuffer endChunk =
NettyTestUtils.checkIsDownstreamMessageEvent(
downstreamCatcher.events.poll(), ChannelBuffer.class);
assertEquals(40, endChunk.readableBytes());
}
@Test
public void testChannelFutureTriggeredOnlyWhenAllChunksWritten()
{
ChannelBuffer data = ChannelBuffers.wrappedBuffer(new byte[2540]);
ChannelFuture mainWriteFuture = Channels.write(channel, data);
@Test
public void testChannelFutureTriggeredOnlyWhenAllChunksWritten() {
ChannelBuffer data = ChannelBuffers.wrappedBuffer(new byte[2540]);
ChannelFuture mainWriteFuture = Channels.write(channel, data);
assertEquals(26, downstreamCatcher.events.size());
for (int i = 0; i < 25; i++)
{
((MessageEvent) downstreamCatcher.events.poll()).getFuture().setSuccess();
assertFalse(mainWriteFuture.isDone());
}
assertEquals(26, downstreamCatcher.events.size());
for (int i = 0; i < 25; i ++) {
((MessageEvent) downstreamCatcher.events.poll()).getFuture()
.setSuccess();
assertFalse(mainWriteFuture.isDone());
}
((MessageEvent) downstreamCatcher.events.poll()).getFuture().setSuccess();
assertTrue(mainWriteFuture.isDone());
assertTrue(mainWriteFuture.isSuccess());
}
((MessageEvent) downstreamCatcher.events.poll()).getFuture()
.setSuccess();
assertTrue(mainWriteFuture.isDone());
assertTrue(mainWriteFuture.isSuccess());
}
@Test
public void testChannelFutureFailsOnFirstWriteFailure()
{
ChannelBuffer data = ChannelBuffers.wrappedBuffer(new byte[2540]);
ChannelFuture mainWriteFuture = Channels.write(channel, data);
@Test
public void testChannelFutureFailsOnFirstWriteFailure() {
ChannelBuffer data = ChannelBuffers.wrappedBuffer(new byte[2540]);
ChannelFuture mainWriteFuture = Channels.write(channel, data);
assertEquals(26, downstreamCatcher.events.size());
for (int i = 0; i < 10; i++)
{
((MessageEvent) downstreamCatcher.events.poll()).getFuture().setSuccess();
assertFalse(mainWriteFuture.isDone());
}
assertEquals(26, downstreamCatcher.events.size());
for (int i = 0; i < 10; i ++) {
((MessageEvent) downstreamCatcher.events.poll()).getFuture()
.setSuccess();
assertFalse(mainWriteFuture.isDone());
}
((MessageEvent) downstreamCatcher.events.poll()).getFuture().setFailure(new Exception("Something bad happened"));
assertTrue(mainWriteFuture.isDone());
assertFalse(mainWriteFuture.isSuccess());
((MessageEvent) downstreamCatcher.events.poll()).getFuture()
.setFailure(new Exception("Something bad happened"));
assertTrue(mainWriteFuture.isDone());
assertFalse(mainWriteFuture.isSuccess());
// check all the subsequent writes got cancelled
for (int i = 0; i < 15; i++)
{
assertTrue(((MessageEvent) downstreamCatcher.events.poll()).getFuture().isCancelled());
}
}
// check all the subsequent writes got cancelled
for (int i = 0; i < 15; i ++) {
assertTrue(((MessageEvent) downstreamCatcher.events.poll())
.getFuture().isCancelled());
}
}
}

View File

@ -30,75 +30,70 @@ import org.junit.Test;
* @author The Netty Project (netty-dev@lists.jboss.org)
* @author Iain McGinniss (iain.mcginniss@onedrum.com)
*/
public class WriteSplitterTest
{
public class WriteSplitterTest {
private static final int SPLIT_THRESHOLD = 1024;
private static final int SPLIT_THRESHOLD = 1024;
@Test
public void testSplit_bufferUnderThreshold()
{
ChannelBuffer buffer = createBufferWithContents(800);
List<ChannelBuffer> fragments = WriteSplitter.split(buffer, SPLIT_THRESHOLD);
assertNotNull(fragments);
assertEquals(1, fragments.size());
}
@Test
public void testSplit_bufferUnderThreshold() {
ChannelBuffer buffer = createBufferWithContents(800);
List<ChannelBuffer> fragments =
WriteSplitter.split(buffer, SPLIT_THRESHOLD);
assertNotNull(fragments);
assertEquals(1, fragments.size());
}
@Test
public void testSplit_bufferMatchesThreshold()
{
ChannelBuffer buffer = createBufferWithContents(SPLIT_THRESHOLD);
List<ChannelBuffer> fragments = WriteSplitter.split(buffer, SPLIT_THRESHOLD);
assertNotNull(fragments);
assertEquals(1, fragments.size());
}
@Test
public void testSplit_bufferMatchesThreshold() {
ChannelBuffer buffer = createBufferWithContents(SPLIT_THRESHOLD);
List<ChannelBuffer> fragments =
WriteSplitter.split(buffer, SPLIT_THRESHOLD);
assertNotNull(fragments);
assertEquals(1, fragments.size());
}
@Test
public void testSplit_bufferOverThreshold()
{
ChannelBuffer buffer = createBufferWithContents((int) (SPLIT_THRESHOLD * 1.5));
List<ChannelBuffer> fragments = WriteSplitter.split(buffer, SPLIT_THRESHOLD);
assertNotNull(fragments);
assertEquals(2, fragments.size());
@Test
public void testSplit_bufferOverThreshold() {
ChannelBuffer buffer =
createBufferWithContents((int) (SPLIT_THRESHOLD * 1.5));
List<ChannelBuffer> fragments =
WriteSplitter.split(buffer, SPLIT_THRESHOLD);
assertNotNull(fragments);
assertEquals(2, fragments.size());
ChannelBuffer fragment1 = fragments.get(0);
checkMatches(buffer, fragment1);
ChannelBuffer fragment2 = fragments.get(1);
checkMatches(buffer, fragment2);
}
ChannelBuffer fragment1 = fragments.get(0);
checkMatches(buffer, fragment1);
ChannelBuffer fragment2 = fragments.get(1);
checkMatches(buffer, fragment2);
}
@Test
public void testSplit_largeNumberOfFragments()
{
ChannelBuffer buffer = createBufferWithContents(SPLIT_THRESHOLD * 250);
List<ChannelBuffer> fragments = WriteSplitter.split(buffer, SPLIT_THRESHOLD);
assertNotNull(fragments);
assertEquals(250, fragments.size());
@Test
public void testSplit_largeNumberOfFragments() {
ChannelBuffer buffer = createBufferWithContents(SPLIT_THRESHOLD * 250);
List<ChannelBuffer> fragments =
WriteSplitter.split(buffer, SPLIT_THRESHOLD);
assertNotNull(fragments);
assertEquals(250, fragments.size());
for (ChannelBuffer fragment : fragments)
{
checkMatches(buffer, fragment);
}
}
for (ChannelBuffer fragment: fragments) {
checkMatches(buffer, fragment);
}
}
private void checkMatches(ChannelBuffer mainBuffer, ChannelBuffer fragment)
{
assertTrue(mainBuffer.readableBytes() >= fragment.readableBytes());
while (fragment.readable())
{
assertEquals(mainBuffer.readByte(), fragment.readByte());
}
}
private void checkMatches(ChannelBuffer mainBuffer, ChannelBuffer fragment) {
assertTrue(mainBuffer.readableBytes() >= fragment.readableBytes());
while (fragment.readable()) {
assertEquals(mainBuffer.readByte(), fragment.readByte());
}
}
private ChannelBuffer createBufferWithContents(int size)
{
byte[] contents = new byte[size];
for (int i = 0; i < contents.length; i++)
{
contents[i] = (byte) (i % 10);
}
private ChannelBuffer createBufferWithContents(int size) {
byte[] contents = new byte[size];
for (int i = 0; i < contents.length; i ++) {
contents[i] = (byte) (i % 10);
}
return ChannelBuffers.copiedBuffer(contents);
}
return ChannelBuffers.copiedBuffer(contents);
}
}