Fixed formatting to conform to project standards
This commit is contained in:
parent
f7729bc8bb
commit
a617bd3290
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
@ -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();
|
||||
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,5 @@ package org.jboss.netty.channel.socket.http;
|
||||
* @author OneDrum Ltd.
|
||||
*/
|
||||
enum SaturationStateChange {
|
||||
NO_CHANGE,
|
||||
DESATURATED,
|
||||
SATURATED
|
||||
NO_CHANGE, DESATURATED, SATURATED
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
}
|
@ -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
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
//
|
||||
// }
|
||||
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user