From 032912d9389087bf909b7236637d60e0e0be1740 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 13 Jun 2012 22:23:21 +0200 Subject: [PATCH 01/87] Commit first round of classes to support nio2/async channel api. Still work in progress.. See #396 --- pom.xml | 5 + .../io/netty/channel/AbstractChannel.java | 30 ++- .../netty/channel/AbstractServerChannel.java | 2 +- .../channel/embedded/EmbeddedByteChannel.java | 3 +- .../socket/nio/AbstractNioByteChannel.java | 5 +- .../socket/nio2/AbstractAsyncChannel.java | 176 +++++++++++++ .../socket/nio2/AsyncServerSocketChannel.java | 120 +++++++++ .../socket/nio2/AsyncSocketchannel.java | 246 ++++++++++++++++++ .../channel/socket/nio2/package-info.java | 21 ++ .../socket/oio/AbstractOioByteChannel.java | 3 +- 10 files changed, 592 insertions(+), 19 deletions(-) create mode 100755 transport/src/main/java/io/netty/channel/socket/nio2/AbstractAsyncChannel.java create mode 100755 transport/src/main/java/io/netty/channel/socket/nio2/AsyncServerSocketChannel.java create mode 100755 transport/src/main/java/io/netty/channel/socket/nio2/AsyncSocketchannel.java create mode 100644 transport/src/main/java/io/netty/channel/socket/nio2/package-info.java diff --git a/pom.xml b/pom.xml index ec40816349..ae53662ead 100644 --- a/pom.xml +++ b/pom.xml @@ -280,6 +280,11 @@ java.nio.channels.MembershipKey java.net.StandardSocketOptions java.net.StandardProtocolFamily + + java.nio.channels.AsynchronousChannel + java.nio.channels.AsynchronousSocketChannel + java.nio.channels.AsynchronousServerSocketChannel + java.nio.channels.AsynchronousChannelGroup diff --git a/transport/src/main/java/io/netty/channel/AbstractChannel.java b/transport/src/main/java/io/netty/channel/AbstractChannel.java index 42890f3c8b..98d7fb7aff 100644 --- a/transport/src/main/java/io/netty/channel/AbstractChannel.java +++ b/transport/src/main/java/io/netty/channel/AbstractChannel.java @@ -85,7 +85,7 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha private ClosedChannelException closedChannelException; private final Deque flushCheckpoints = new ArrayDeque(); private long writeCounter; - private boolean inFlushNow; + protected boolean inFlushNow; private boolean flushNowPending; /** Cache for the string representation of this channel */ @@ -623,7 +623,7 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha } @Override - public final void flushNow() { + public void flushNow() { if (inFlushNow) { return; } @@ -631,12 +631,13 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha inFlushNow = true; ChannelHandlerContext ctx = directOutboundContext(); Throwable cause = null; + boolean handleFlush = true; try { if (ctx.hasOutboundByteBuffer()) { ByteBuf out = ctx.outboundByteBuffer(); int oldSize = out.readableBytes(); try { - doFlushByteBuffer(out); + handleFlush = doFlushByteBuffer(out); } catch (Throwable t) { cause = t; } finally { @@ -657,14 +658,15 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha writeCounter += oldSize - out.size(); } } - - if (cause == null) { - notifyFlushFutures(); - } else { - notifyFlushFutures(cause); - pipeline.fireExceptionCaught(cause); - if (cause instanceof IOException) { - close(voidFuture()); + if (handleFlush) { + if (cause == null) { + notifyFlushFutures(); + } else { + notifyFlushFutures(cause); + pipeline.fireExceptionCaught(cause); + if (cause instanceof IOException) { + close(voidFuture()); + } } } } finally { @@ -713,7 +715,7 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha protected abstract void doClose() throws Exception; protected abstract void doDeregister() throws Exception; - protected void doFlushByteBuffer(ByteBuf buf) throws Exception { + protected boolean doFlushByteBuffer(ByteBuf buf) throws Exception { throw new UnsupportedOperationException(); } protected void doFlushMessageBuffer(MessageBuf buf) throws Exception { @@ -722,7 +724,7 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha protected abstract boolean isFlushPending(); - private void notifyFlushFutures() { + protected final void notifyFlushFutures() { if (flushCheckpoints.isEmpty()) { return; } @@ -760,7 +762,7 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha } } - private void notifyFlushFutures(Throwable cause) { + protected final void notifyFlushFutures(Throwable cause) { notifyFlushFutures(); for (;;) { FlushCheckpoint cp = flushCheckpoints.poll(); diff --git a/transport/src/main/java/io/netty/channel/AbstractServerChannel.java b/transport/src/main/java/io/netty/channel/AbstractServerChannel.java index 0c42167eeb..6ac92e60b3 100644 --- a/transport/src/main/java/io/netty/channel/AbstractServerChannel.java +++ b/transport/src/main/java/io/netty/channel/AbstractServerChannel.java @@ -77,7 +77,7 @@ public abstract class AbstractServerChannel extends AbstractChannel implements S } @Override - protected void doFlushByteBuffer(ByteBuf buf) throws Exception { + protected boolean doFlushByteBuffer(ByteBuf buf) throws Exception { throw new UnsupportedOperationException(); } diff --git a/transport/src/main/java/io/netty/channel/embedded/EmbeddedByteChannel.java b/transport/src/main/java/io/netty/channel/embedded/EmbeddedByteChannel.java index 07e8ac5313..8c1f7e3499 100644 --- a/transport/src/main/java/io/netty/channel/embedded/EmbeddedByteChannel.java +++ b/transport/src/main/java/io/netty/channel/embedded/EmbeddedByteChannel.java @@ -71,10 +71,11 @@ public class EmbeddedByteChannel extends AbstractEmbeddedChannel { } @Override - protected void doFlushByteBuffer(ByteBuf buf) throws Exception { + protected boolean doFlushByteBuffer(ByteBuf buf) throws Exception { if (!lastOutboundBuffer().readable()) { lastOutboundBuffer().discardReadBytes(); } lastOutboundBuffer().writeBytes(buf); + return true; } } diff --git a/transport/src/main/java/io/netty/channel/socket/nio/AbstractNioByteChannel.java b/transport/src/main/java/io/netty/channel/socket/nio/AbstractNioByteChannel.java index fded47226c..99489e3ae7 100644 --- a/transport/src/main/java/io/netty/channel/socket/nio/AbstractNioByteChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/nio/AbstractNioByteChannel.java @@ -85,11 +85,11 @@ abstract class AbstractNioByteChannel extends AbstractNioChannel { } @Override - protected void doFlushByteBuffer(ByteBuf buf) throws Exception { + protected boolean doFlushByteBuffer(ByteBuf buf) throws Exception { if (!buf.readable()) { // Reset reader/writerIndex to 0 if the buffer is empty. buf.clear(); - return; + return true; } for (int i = config().getWriteSpinCount() - 1; i >= 0; i --) { @@ -103,6 +103,7 @@ abstract class AbstractNioByteChannel extends AbstractNioChannel { break; } } + return true; } protected abstract int doReadBytes(ByteBuf buf) throws Exception; diff --git a/transport/src/main/java/io/netty/channel/socket/nio2/AbstractAsyncChannel.java b/transport/src/main/java/io/netty/channel/socket/nio2/AbstractAsyncChannel.java new file mode 100755 index 0000000000..f2371239b1 --- /dev/null +++ b/transport/src/main/java/io/netty/channel/socket/nio2/AbstractAsyncChannel.java @@ -0,0 +1,176 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.channel.socket.nio2; + +import io.netty.channel.AbstractChannel; +import io.netty.channel.Channel; +import io.netty.channel.ChannelConfig; +import io.netty.channel.ChannelFuture; +import io.netty.channel.EventLoop; + +import java.net.ConnectException; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.nio.channels.AsynchronousChannel; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +public abstract class AbstractAsyncChannel extends AbstractChannel { + + protected volatile AsynchronousChannel ch; + + /** + * The future of the current connection attempt. If not null, subsequent + * connection attempts will fail. + */ + protected ChannelFuture connectFuture; + protected ScheduledFuture connectTimeoutFuture; + private ConnectException connectTimeoutException; + + protected AbstractAsyncChannel(Channel parent, Integer id) { + super(parent, id); + } + + + @Override + public InetSocketAddress localAddress() { + if (ch == null) { + return null; + } + return (InetSocketAddress) super.localAddress(); + } + + @Override + public InetSocketAddress remoteAddress() { + if (ch == null) { + return null; + } + return (InetSocketAddress) super.remoteAddress(); + } + + protected AsynchronousChannel javaChannel() { + return ch; + } + + @Override + public ChannelConfig config() { + // TODO: Fix me + return null; + } + + @Override + public boolean isOpen() { + return ch == null || ch.isOpen(); + } + + @Override + protected boolean isCompatible(EventLoop loop) { + // TODO: Fix me + return true; + } + + + @Override + protected void doDeregister() throws Exception { + throw new UnsupportedOperationException("Deregistration is not supported by AbstractAsyncChannel"); + } + + @Override + protected AsyncUnsafe newUnsafe() { + return new AsyncUnsafe(); + } + + protected class AsyncUnsafe extends AbstractUnsafe { + + @Override + public void connect(final SocketAddress remoteAddress, + final SocketAddress localAddress, final ChannelFuture future) { + if (eventLoop().inEventLoop()) { + if (!ensureOpen(future)) { + return; + } + + try { + if (connectFuture != null) { + throw new IllegalStateException("connection attempt already made"); + } + connectFuture = future; + + doConnect(remoteAddress, localAddress, future); + + // Schedule connect timeout. + int connectTimeoutMillis = config().getConnectTimeoutMillis(); + if (connectTimeoutMillis > 0) { + connectTimeoutFuture = eventLoop().schedule(new Runnable() { + @Override + public void run() { + if (connectTimeoutException == null) { + connectTimeoutException = new ConnectException("connection timed out"); + } + ChannelFuture connectFuture = AbstractAsyncChannel.this.connectFuture; + if (connectFuture != null && + connectFuture.setFailure(connectTimeoutException)) { + pipeline().fireExceptionCaught(connectTimeoutException); + close(voidFuture()); + } + } + }, connectTimeoutMillis, TimeUnit.MILLISECONDS); + } + + } catch (Throwable t) { + future.setFailure(t); + pipeline().fireExceptionCaught(t); + closeIfClosed(); + } + } else { + eventLoop().execute(new Runnable() { + @Override + public void run() { + connect(remoteAddress, localAddress, future); + } + }); + } + } + + protected final void connectFailed(Throwable t) { + connectFuture.setFailure(t); + pipeline().fireExceptionCaught(t); + closeIfClosed(); + } + + protected final void connectSuccess() { + assert eventLoop().inEventLoop(); + assert connectFuture != null; + try { + boolean wasActive = isActive(); + connectFuture.setSuccess(); + if (!wasActive && isActive()) { + pipeline().fireChannelActive(); + } + } catch (Throwable t) { + connectFuture.setFailure(t); + pipeline().fireExceptionCaught(t); + closeIfClosed(); + } finally { + connectTimeoutFuture.cancel(false); + connectFuture = null; + } + } + } + protected abstract void doConnect(SocketAddress remoteAddress, + SocketAddress localAddress, ChannelFuture connectFuture); + +} diff --git a/transport/src/main/java/io/netty/channel/socket/nio2/AsyncServerSocketChannel.java b/transport/src/main/java/io/netty/channel/socket/nio2/AsyncServerSocketChannel.java new file mode 100755 index 0000000000..816d7f6be6 --- /dev/null +++ b/transport/src/main/java/io/netty/channel/socket/nio2/AsyncServerSocketChannel.java @@ -0,0 +1,120 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.channel.socket.nio2; + +import io.netty.buffer.ChannelBufType; +import io.netty.channel.ChannelException; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ServerChannel; +import io.netty.logging.InternalLogger; +import io.netty.logging.InternalLoggerFactory; + +import java.io.IOException; +import java.net.SocketAddress; +import java.nio.channels.AsynchronousChannelGroup; +import java.nio.channels.AsynchronousServerSocketChannel; +import java.nio.channels.AsynchronousSocketChannel; +import java.nio.channels.CompletionHandler; + +public class AsyncServerSocketChannel extends AbstractAsyncChannel implements ServerChannel { + + private static final AcceptHandler ACCEPT_HANDLER = new AcceptHandler(); + private static final InternalLogger logger = + InternalLoggerFactory.getInstance(AsyncServerSocketChannel.class); + + public AsyncServerSocketChannel() { + super(null, null); + } + + + @Override + protected AsynchronousServerSocketChannel javaChannel() { + return (AsynchronousServerSocketChannel) super.javaChannel(); + } + + @Override + public boolean isActive() { + return localAddress0() != null; + } + + @Override + public ChannelBufType bufferType() { + return ChannelBufType.MESSAGE; + } + + @Override + protected SocketAddress localAddress0() { + try { + return javaChannel().getLocalAddress(); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @Override + protected SocketAddress remoteAddress0() { + return null; + } + + @Override + protected void doBind(SocketAddress localAddress) throws Exception { + javaChannel().bind(localAddress); + } + + @Override + protected void doClose() throws Exception { + javaChannel().close(); + } + + @Override + protected boolean isFlushPending() { + return false; + } + + @Override + protected void doConnect( + SocketAddress remoteAddress, SocketAddress localAddress, ChannelFuture future) { + future.setFailure(new UnsupportedOperationException()); + } + + @Override + protected void doDisconnect() throws Exception { + throw new UnsupportedOperationException(); + } + + @Override + protected Runnable doRegister() throws Exception { + ch = AsynchronousServerSocketChannel.open(AsynchronousChannelGroup.withThreadPool(eventLoop())); + javaChannel().accept(this, ACCEPT_HANDLER); + return null; + } + + private static final class AcceptHandler + implements CompletionHandler { + public void completed(AsynchronousSocketChannel ch, AsyncServerSocketChannel channel) { + // register again this handler to accept new connections + channel.javaChannel().accept(channel, this); + + // create the socket add it to the buffer and fire the event + channel.outboundMessageBuffer().add(new AsyncSocketchannel(channel, null)); + channel.pipeline().fireInboundBufferUpdated(); + } + + public void failed(Throwable t, AsyncServerSocketChannel channel) { + logger.warn("Failed to create a new channel from an accepted socket.", t); + } + } +} diff --git a/transport/src/main/java/io/netty/channel/socket/nio2/AsyncSocketchannel.java b/transport/src/main/java/io/netty/channel/socket/nio2/AsyncSocketchannel.java new file mode 100755 index 0000000000..4c8f63ea90 --- /dev/null +++ b/transport/src/main/java/io/netty/channel/socket/nio2/AsyncSocketchannel.java @@ -0,0 +1,246 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.channel.socket.nio2; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ChannelBufType; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelPipeline; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.nio.channels.AsynchronousChannelGroup; +import java.nio.channels.AsynchronousSocketChannel; +import java.nio.channels.CompletionHandler; +import java.util.concurrent.atomic.AtomicBoolean; + +public class AsyncSocketchannel extends AbstractAsyncChannel { + + private static final CompletionHandler CONNECT_HANDLER = new ConnectHandler(); + private static final CompletionHandler READ_HANDLER = new ReadHandler(); + private static final CompletionHandler WRITE_HANDLER = new WriteHandler(); + + private final AtomicBoolean flushing = new AtomicBoolean(false); + + public AsyncSocketchannel() { + this(null, null); + } + + public AsyncSocketchannel(AsyncServerSocketChannel parent, Integer id) { + super(parent, id); + } + + @Override + public boolean isActive() { + AsynchronousSocketChannel ch = javaChannel(); + return ch.isOpen(); + } + + @Override + protected AsynchronousSocketChannel javaChannel() { + return (AsynchronousSocketChannel) super.javaChannel(); + } + + @Override + public ChannelBufType bufferType() { + return ChannelBufType.BYTE; + } + + @Override + protected void doConnect(SocketAddress remoteAddress, SocketAddress localAddress, final ChannelFuture future) { + assert ch != null; + if (localAddress != null) { + try { + javaChannel().bind(localAddress); + } catch (IOException e) { + future.setFailure(e); + return; + } + } + + javaChannel().connect(remoteAddress, this, CONNECT_HANDLER); + } + + @Override + protected InetSocketAddress localAddress0() { + try { + return (InetSocketAddress) javaChannel().getLocalAddress(); + } catch (IOException e) { + return null; + } + } + + @Override + protected InetSocketAddress remoteAddress0() { + try { + return (InetSocketAddress) javaChannel().getRemoteAddress(); + } catch (IOException e) { + return null; + } + } + + @Override + protected Runnable doRegister() throws Exception { + assert ch == null; + ch = AsynchronousSocketChannel.open(AsynchronousChannelGroup.withThreadPool(eventLoop())); + return null; + } + + private void read() { + javaChannel().read(pipeline().inboundByteBuffer().nioBuffer(), this, READ_HANDLER); + } + + @Override + protected void doBind(SocketAddress localAddress) throws Exception { + javaChannel().bind(localAddress); + } + + @Override + protected void doDisconnect() throws Exception { + doClose(); + } + + @Override + protected void doClose() throws Exception { + javaChannel().close(); + } + + @Override + protected boolean isFlushPending() { + // TODO: Fix me + return true; + } + + protected boolean doFlushByteBuffer(ByteBuf buf) throws Exception { + if (flushing.compareAndSet(false, true)) { + javaChannel().write(buf.nioBuffer(), this, WRITE_HANDLER); + } + return false; + } + + private static boolean expandReadBuffer(ByteBuf byteBuf) { + if (!byteBuf.writable()) { + // FIXME: Magic number + byteBuf.ensureWritableBytes(4096); + return true; + } + return false; + } + + private static final class WriteHandler implements CompletionHandler { + + @Override + public void completed(Integer result, AsyncSocketchannel channel) { + ByteBuf buf = channel.pipeline().outboundByteBuffer(); + if (!buf.readable()) { + buf.discardReadBytes(); + } + + if (result > 0) { + channel.notifyFlushFutures(); + } + channel.flushing.set(false); + } + + @Override + public void failed(Throwable cause, AsyncSocketchannel channel) { + ByteBuf buf = channel.pipeline().outboundByteBuffer(); + if (!buf.readable()) { + buf.discardReadBytes(); + } + + channel.notifyFlushFutures(cause); + channel.pipeline().fireExceptionCaught(cause); + if (cause instanceof IOException) { + channel.close(channel.unsafe().voidFuture()); + } + channel.flushing.set(false); + } + } + + private static final class ReadHandler implements CompletionHandler { + + @Override + public void completed(Integer result, AsyncSocketchannel channel) { + assert channel.eventLoop().inEventLoop(); + + final ChannelPipeline pipeline = channel.pipeline(); + final ByteBuf byteBuf = pipeline.inboundByteBuffer(); + boolean closed = false; + boolean read = false; + try { + expandReadBuffer(byteBuf); + for (;;) { + int localReadAmount = result.intValue(); + if (localReadAmount > 0) { + read = true; + } else if (localReadAmount < 0) { + closed = true; + break; + } + if (!expandReadBuffer(byteBuf)) { + break; + } + } + } catch (Throwable t) { + if (read) { + read = false; + pipeline.fireInboundBufferUpdated(); + } + pipeline.fireExceptionCaught(t); + if (t instanceof IOException) { + channel.close(channel.unsafe().voidFuture()); + } + } finally { + if (read) { + pipeline.fireInboundBufferUpdated(); + } + if (closed && channel.isOpen()) { + channel.close(channel.unsafe().voidFuture()); + } else { + // start the next read + channel.read(); + } + } + } + + @Override + public void failed(Throwable t, AsyncSocketchannel channel) { + channel.pipeline().fireExceptionCaught(t); + if (t instanceof IOException) { + channel.close(channel.unsafe().voidFuture()); + } else { + // start the next read + channel.read(); + } + } + } + + private static final class ConnectHandler implements CompletionHandler { + + @Override + public void completed(Void result, AsyncSocketchannel channel) { + ((AsyncUnsafe) channel.unsafe()).connectSuccess(); + } + + @Override + public void failed(Throwable exc, AsyncSocketchannel channel) { + ((AsyncUnsafe) channel.unsafe()).connectFailed(exc); + } + } + +} diff --git a/transport/src/main/java/io/netty/channel/socket/nio2/package-info.java b/transport/src/main/java/io/netty/channel/socket/nio2/package-info.java new file mode 100644 index 0000000000..f3c3c4d1ff --- /dev/null +++ b/transport/src/main/java/io/netty/channel/socket/nio2/package-info.java @@ -0,0 +1,21 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +/** + * NIO2-based socket channel + * API implementation - recommended for a large number of connections (>= 1000). + */ +package io.netty.channel.socket.nio2; diff --git a/transport/src/main/java/io/netty/channel/socket/oio/AbstractOioByteChannel.java b/transport/src/main/java/io/netty/channel/socket/oio/AbstractOioByteChannel.java index 12697f74aa..563286b3b9 100644 --- a/transport/src/main/java/io/netty/channel/socket/oio/AbstractOioByteChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/oio/AbstractOioByteChannel.java @@ -86,11 +86,12 @@ abstract class AbstractOioByteChannel extends AbstractOioChannel { } @Override - protected void doFlushByteBuffer(ByteBuf buf) throws Exception { + protected boolean doFlushByteBuffer(ByteBuf buf) throws Exception { while (buf.readable()) { doWriteBytes(buf); } buf.clear(); + return true; } protected abstract int available(); From f8ef5d5d782383970e2bdf35c978e872242817b3 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Thu, 14 Jun 2012 21:02:47 +0200 Subject: [PATCH 02/87] Next round for async channel api support a.k.a nio2. See See #396 --- .../java/io/netty/bootstrap/Bootstrap.java | 0 .../io/netty/bootstrap/ServerBootstrap.java | 13 +- .../java/io/netty/bootstrap/package-info.java | 0 .../io/netty/channel/AbstractChannel.java | 0 .../netty/channel/AbstractServerChannel.java | 0 .../channel/BlockingOperationException.java | 0 .../main/java/io/netty/channel/Channel.java | 0 .../java/io/netty/channel/ChannelConfig.java | 0 .../io/netty/channel/ChannelException.java | 0 .../java/io/netty/channel/ChannelFuture.java | 0 .../channel/ChannelFutureAggregator.java | 0 .../netty/channel/ChannelFutureFactory.java | 0 .../netty/channel/ChannelFutureListener.java | 0 .../ChannelFutureProgressListener.java | 0 .../java/io/netty/channel/ChannelHandler.java | 0 .../netty/channel/ChannelHandlerAdapter.java | 0 .../netty/channel/ChannelHandlerContext.java | 0 .../ChannelHandlerLifeCycleException.java | 0 .../io/netty/channel/ChannelHandlerType.java | 0 .../channel/ChannelInboundByteHandler.java | 0 .../ChannelInboundByteHandlerAdapter.java | 0 .../netty/channel/ChannelInboundHandler.java | 0 .../channel/ChannelInboundHandlerAdapter.java | 0 .../netty/channel/ChannelInboundInvoker.java | 0 .../channel/ChannelInboundMessageHandler.java | 0 .../ChannelInboundMessageHandlerAdapter.java | 0 .../io/netty/channel/ChannelInitializer.java | 0 .../channel/ChannelOperationHandler.java | 0 .../ChannelOperationHandlerAdapter.java | 0 .../java/io/netty/channel/ChannelOption.java | 0 .../channel/ChannelOutboundByteHandler.java | 0 .../ChannelOutboundByteHandlerAdapter.java | 0 .../netty/channel/ChannelOutboundHandler.java | 0 .../ChannelOutboundHandlerAdapter.java | 0 .../netty/channel/ChannelOutboundInvoker.java | 0 .../ChannelOutboundMessageHandler.java | 0 .../ChannelOutboundMessageHandlerAdapter.java | 0 .../io/netty/channel/ChannelPipeline.java | 0 .../channel/ChannelPipelineException.java | 0 .../io/netty/channel/ChannelStateHandler.java | 0 .../channel/ChannelStateHandlerAdapter.java | 0 .../netty/channel/CombinedChannelHandler.java | 0 .../netty/channel/CompleteChannelFuture.java | 0 .../netty/channel/DefaultChannelConfig.java | 0 .../netty/channel/DefaultChannelFuture.java | 0 .../channel/DefaultChannelHandlerContext.java | 0 .../netty/channel/DefaultChannelPipeline.java | 0 ...efaultChannelPipelineModificationTask.java | 0 .../channel/DefaultChildEventExecutor.java | 0 .../netty/channel/DefaultEventExecutor.java | 0 .../java/io/netty/channel/EventExecutor.java | 0 .../main/java/io/netty/channel/EventLoop.java | 0 .../io/netty/channel/EventLoopException.java | 0 .../io/netty/channel/FailedChannelFuture.java | 0 .../channel/MultithreadEventExecutor.java | 0 .../netty/channel/MultithreadEventLoop.java | 0 .../netty/channel/NoSuchBufferException.java | 0 .../java/io/netty/channel/ServerChannel.java | 0 .../channel/SingleThreadEventExecutor.java | 0 .../netty/channel/SingleThreadEventLoop.java | 0 .../netty/channel/SucceededChannelFuture.java | 0 .../io/netty/channel/VoidChannelFuture.java | 0 .../embedded/AbstractEmbeddedChannel.java | 0 .../channel/embedded/EmbeddedByteChannel.java | 0 .../channel/embedded/EmbeddedEventLoop.java | 0 .../embedded/EmbeddedMessageChannel.java | 0 .../embedded/EmbeddedSocketAddress.java | 0 .../netty/channel/embedded/package-info.java | 0 .../io/netty/channel/group/ChannelGroup.java | 0 .../channel/group/ChannelGroupFuture.java | 0 .../group/ChannelGroupFutureListener.java | 0 .../netty/channel/group/CombinedIterator.java | 0 .../channel/group/DefaultChannelGroup.java | 0 .../group/DefaultChannelGroupFuture.java | 0 .../io/netty/channel/group/package-info.java | 0 .../io/netty/channel/local/LocalAddress.java | 0 .../io/netty/channel/local/LocalChannel.java | 0 .../channel/local/LocalChannelRegistry.java | 0 .../channel/local/LocalChildEventLoop.java | 0 .../netty/channel/local/LocalEventLoop.java | 0 .../channel/local/LocalServerChannel.java | 0 .../io/netty/channel/local/package-info.java | 0 .../java/io/netty/channel/package-info.java | 0 .../netty/channel/socket/DatagramChannel.java | 0 .../channel/socket/DatagramChannelConfig.java | 0 .../netty/channel/socket/DatagramPacket.java | 0 .../socket/DefaultDatagramChannelConfig.java | 0 .../DefaultServerSocketChannelConfig.java | 0 .../socket/DefaultSocketChannelConfig.java | 0 .../socket/InternetProtocolFamily.java | 0 .../channel/socket/ServerSocketChannel.java | 0 .../socket/ServerSocketChannelConfig.java | 0 .../netty/channel/socket/SocketChannel.java | 0 .../channel/socket/SocketChannelConfig.java | 0 .../socket/nio/AbstractNioByteChannel.java | 0 .../socket/nio/AbstractNioChannel.java | 0 .../socket/nio/AbstractNioMessageChannel.java | 0 .../channel/socket/nio/NioChildEventLoop.java | 0 .../socket/nio/NioDatagramChannel.java | 0 .../socket/nio/NioDatagramChannelConfig.java | 0 .../channel/socket/nio/NioEventLoop.java | 0 .../socket/nio/NioServerSocketChannel.java | 0 .../channel/socket/nio/NioSocketChannel.java | 0 .../socket/nio/ProtocolFamilyConverter.java | 0 .../channel/socket/nio/SelectorUtil.java | 0 .../channel/socket/nio/package-info.java | 0 .../socket/nio2/AbstractAsyncChannel.java | 18 +- .../socket/nio2/AsyncChildEventLoop.java | 51 ++++ .../channel/socket/nio2/AsyncEventLoop.java | 41 +++ .../socket/nio2/AsyncNetworkChannel.java | 52 ++++ .../socket/nio2/AsyncServerSocketChannel.java | 26 +- .../nio2/AsyncServerSocketChannelConfig.java | 138 ++++++++++ .../socket/nio2/AsyncSocketChannelConfig.java | 237 ++++++++++++++++++ .../socket/nio2/AsyncSocketchannel.java | 77 ++++-- .../channel/socket/nio2/package-info.java | 0 .../socket/oio/AbstractOioByteChannel.java | 0 .../socket/oio/AbstractOioChannel.java | 0 .../socket/oio/AbstractOioMessageChannel.java | 0 .../channel/socket/oio/OioChildEventLoop.java | 0 .../socket/oio/OioDatagramChannel.java | 0 .../channel/socket/oio/OioEventLoop.java | 0 .../socket/oio/OioServerSocketChannel.java | 0 .../channel/socket/oio/OioSocketChannel.java | 0 .../channel/socket/oio/package-info.java | 0 .../io/netty/channel/socket/package-info.java | 0 .../io/netty/channel/AsyncTransportTest.java | 42 ++++ .../channel/CompleteChannelFutureTest.java | 0 .../channel/DefaultChannelPipelineTest.java | 0 .../channel/FailedChannelFutureTest.java | 0 .../channel/SingleThreadEventLoopTest.java | 0 .../channel/SucceededChannelFutureTest.java | 0 .../local/LocalChannelRegistryTest.java | 0 .../local/LocalTransportThreadModelTest.java | 0 133 files changed, 655 insertions(+), 40 deletions(-) mode change 100644 => 100755 transport/src/main/java/io/netty/bootstrap/Bootstrap.java mode change 100644 => 100755 transport/src/main/java/io/netty/bootstrap/ServerBootstrap.java mode change 100644 => 100755 transport/src/main/java/io/netty/bootstrap/package-info.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/AbstractChannel.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/AbstractServerChannel.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/BlockingOperationException.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/Channel.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/ChannelConfig.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/ChannelException.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/ChannelFuture.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/ChannelFutureAggregator.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/ChannelFutureFactory.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/ChannelFutureListener.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/ChannelFutureProgressListener.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/ChannelHandler.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/ChannelHandlerAdapter.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/ChannelHandlerContext.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/ChannelHandlerLifeCycleException.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/ChannelHandlerType.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/ChannelInboundByteHandler.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/ChannelInboundByteHandlerAdapter.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/ChannelInboundHandler.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/ChannelInboundHandlerAdapter.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/ChannelInboundInvoker.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/ChannelInboundMessageHandler.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/ChannelInboundMessageHandlerAdapter.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/ChannelInitializer.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/ChannelOperationHandler.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/ChannelOperationHandlerAdapter.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/ChannelOption.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/ChannelOutboundByteHandler.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/ChannelOutboundByteHandlerAdapter.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/ChannelOutboundHandler.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/ChannelOutboundHandlerAdapter.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/ChannelOutboundInvoker.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/ChannelOutboundMessageHandler.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/ChannelOutboundMessageHandlerAdapter.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/ChannelPipeline.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/ChannelPipelineException.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/ChannelStateHandler.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/ChannelStateHandlerAdapter.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/CombinedChannelHandler.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/CompleteChannelFuture.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/DefaultChannelConfig.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/DefaultChannelFuture.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/DefaultChannelHandlerContext.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/DefaultChannelPipeline.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/DefaultChannelPipelineModificationTask.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/DefaultChildEventExecutor.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/DefaultEventExecutor.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/EventExecutor.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/EventLoop.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/EventLoopException.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/FailedChannelFuture.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/MultithreadEventExecutor.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/MultithreadEventLoop.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/NoSuchBufferException.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/ServerChannel.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/SingleThreadEventExecutor.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/SingleThreadEventLoop.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/SucceededChannelFuture.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/VoidChannelFuture.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/embedded/AbstractEmbeddedChannel.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/embedded/EmbeddedByteChannel.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/embedded/EmbeddedEventLoop.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/embedded/EmbeddedMessageChannel.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/embedded/EmbeddedSocketAddress.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/embedded/package-info.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/group/ChannelGroup.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/group/ChannelGroupFuture.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/group/ChannelGroupFutureListener.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/group/CombinedIterator.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/group/DefaultChannelGroup.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/group/DefaultChannelGroupFuture.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/group/package-info.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/local/LocalAddress.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/local/LocalChannel.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/local/LocalChannelRegistry.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/local/LocalChildEventLoop.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/local/LocalEventLoop.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/local/LocalServerChannel.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/local/package-info.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/package-info.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/socket/DatagramChannel.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/socket/DatagramChannelConfig.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/socket/DatagramPacket.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/socket/DefaultDatagramChannelConfig.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/socket/DefaultServerSocketChannelConfig.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/socket/DefaultSocketChannelConfig.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/socket/InternetProtocolFamily.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/socket/ServerSocketChannel.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/socket/ServerSocketChannelConfig.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/socket/SocketChannel.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/socket/SocketChannelConfig.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/socket/nio/AbstractNioByteChannel.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/socket/nio/AbstractNioChannel.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/socket/nio/AbstractNioMessageChannel.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/socket/nio/NioChildEventLoop.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/socket/nio/NioDatagramChannel.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/socket/nio/NioDatagramChannelConfig.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/socket/nio/NioEventLoop.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/socket/nio/NioServerSocketChannel.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/socket/nio/NioSocketChannel.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/socket/nio/ProtocolFamilyConverter.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/socket/nio/SelectorUtil.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/socket/nio/package-info.java create mode 100755 transport/src/main/java/io/netty/channel/socket/nio2/AsyncChildEventLoop.java create mode 100755 transport/src/main/java/io/netty/channel/socket/nio2/AsyncEventLoop.java create mode 100755 transport/src/main/java/io/netty/channel/socket/nio2/AsyncNetworkChannel.java create mode 100755 transport/src/main/java/io/netty/channel/socket/nio2/AsyncServerSocketChannelConfig.java create mode 100755 transport/src/main/java/io/netty/channel/socket/nio2/AsyncSocketChannelConfig.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/socket/nio2/package-info.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/socket/oio/AbstractOioByteChannel.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/socket/oio/AbstractOioChannel.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/socket/oio/AbstractOioMessageChannel.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/socket/oio/OioChildEventLoop.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/socket/oio/OioDatagramChannel.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/socket/oio/OioEventLoop.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/socket/oio/OioServerSocketChannel.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/socket/oio/OioSocketChannel.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/socket/oio/package-info.java mode change 100644 => 100755 transport/src/main/java/io/netty/channel/socket/package-info.java create mode 100755 transport/src/test/java/io/netty/channel/AsyncTransportTest.java mode change 100644 => 100755 transport/src/test/java/io/netty/channel/CompleteChannelFutureTest.java mode change 100644 => 100755 transport/src/test/java/io/netty/channel/DefaultChannelPipelineTest.java mode change 100644 => 100755 transport/src/test/java/io/netty/channel/FailedChannelFutureTest.java mode change 100644 => 100755 transport/src/test/java/io/netty/channel/SingleThreadEventLoopTest.java mode change 100644 => 100755 transport/src/test/java/io/netty/channel/SucceededChannelFutureTest.java mode change 100644 => 100755 transport/src/test/java/io/netty/channel/local/LocalChannelRegistryTest.java mode change 100644 => 100755 transport/src/test/java/io/netty/channel/local/LocalTransportThreadModelTest.java diff --git a/transport/src/main/java/io/netty/bootstrap/Bootstrap.java b/transport/src/main/java/io/netty/bootstrap/Bootstrap.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/bootstrap/ServerBootstrap.java b/transport/src/main/java/io/netty/bootstrap/ServerBootstrap.java old mode 100644 new mode 100755 index 15c076b64b..098ed8a1a3 --- a/transport/src/main/java/io/netty/bootstrap/ServerBootstrap.java +++ b/transport/src/main/java/io/netty/bootstrap/ServerBootstrap.java @@ -167,13 +167,6 @@ public class ServerBootstrap { return future; } - try { - channel.config().setOptions(parentOptions); - } catch (Exception e) { - future.setFailure(e); - return future; - } - ChannelPipeline p = channel.pipeline(); if (handler != null) { p.addLast(handler); @@ -185,6 +178,12 @@ public class ServerBootstrap { future.setFailure(f.cause()); return future; } + try { + channel.config().setOptions(parentOptions); + } catch (Exception e) { + future.setFailure(e); + return future; + } if (!channel.isOpen()) { // Registration was successful but the channel was closed due to some failure in diff --git a/transport/src/main/java/io/netty/bootstrap/package-info.java b/transport/src/main/java/io/netty/bootstrap/package-info.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/AbstractChannel.java b/transport/src/main/java/io/netty/channel/AbstractChannel.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/AbstractServerChannel.java b/transport/src/main/java/io/netty/channel/AbstractServerChannel.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/BlockingOperationException.java b/transport/src/main/java/io/netty/channel/BlockingOperationException.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/Channel.java b/transport/src/main/java/io/netty/channel/Channel.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/ChannelConfig.java b/transport/src/main/java/io/netty/channel/ChannelConfig.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/ChannelException.java b/transport/src/main/java/io/netty/channel/ChannelException.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/ChannelFuture.java b/transport/src/main/java/io/netty/channel/ChannelFuture.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/ChannelFutureAggregator.java b/transport/src/main/java/io/netty/channel/ChannelFutureAggregator.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/ChannelFutureFactory.java b/transport/src/main/java/io/netty/channel/ChannelFutureFactory.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/ChannelFutureListener.java b/transport/src/main/java/io/netty/channel/ChannelFutureListener.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/ChannelFutureProgressListener.java b/transport/src/main/java/io/netty/channel/ChannelFutureProgressListener.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/ChannelHandler.java b/transport/src/main/java/io/netty/channel/ChannelHandler.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/ChannelHandlerAdapter.java b/transport/src/main/java/io/netty/channel/ChannelHandlerAdapter.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/ChannelHandlerContext.java b/transport/src/main/java/io/netty/channel/ChannelHandlerContext.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/ChannelHandlerLifeCycleException.java b/transport/src/main/java/io/netty/channel/ChannelHandlerLifeCycleException.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/ChannelHandlerType.java b/transport/src/main/java/io/netty/channel/ChannelHandlerType.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/ChannelInboundByteHandler.java b/transport/src/main/java/io/netty/channel/ChannelInboundByteHandler.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/ChannelInboundByteHandlerAdapter.java b/transport/src/main/java/io/netty/channel/ChannelInboundByteHandlerAdapter.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/ChannelInboundHandler.java b/transport/src/main/java/io/netty/channel/ChannelInboundHandler.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/ChannelInboundHandlerAdapter.java b/transport/src/main/java/io/netty/channel/ChannelInboundHandlerAdapter.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/ChannelInboundInvoker.java b/transport/src/main/java/io/netty/channel/ChannelInboundInvoker.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/ChannelInboundMessageHandler.java b/transport/src/main/java/io/netty/channel/ChannelInboundMessageHandler.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/ChannelInboundMessageHandlerAdapter.java b/transport/src/main/java/io/netty/channel/ChannelInboundMessageHandlerAdapter.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/ChannelInitializer.java b/transport/src/main/java/io/netty/channel/ChannelInitializer.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/ChannelOperationHandler.java b/transport/src/main/java/io/netty/channel/ChannelOperationHandler.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/ChannelOperationHandlerAdapter.java b/transport/src/main/java/io/netty/channel/ChannelOperationHandlerAdapter.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/ChannelOption.java b/transport/src/main/java/io/netty/channel/ChannelOption.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/ChannelOutboundByteHandler.java b/transport/src/main/java/io/netty/channel/ChannelOutboundByteHandler.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/ChannelOutboundByteHandlerAdapter.java b/transport/src/main/java/io/netty/channel/ChannelOutboundByteHandlerAdapter.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/ChannelOutboundHandler.java b/transport/src/main/java/io/netty/channel/ChannelOutboundHandler.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/ChannelOutboundHandlerAdapter.java b/transport/src/main/java/io/netty/channel/ChannelOutboundHandlerAdapter.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/ChannelOutboundInvoker.java b/transport/src/main/java/io/netty/channel/ChannelOutboundInvoker.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/ChannelOutboundMessageHandler.java b/transport/src/main/java/io/netty/channel/ChannelOutboundMessageHandler.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/ChannelOutboundMessageHandlerAdapter.java b/transport/src/main/java/io/netty/channel/ChannelOutboundMessageHandlerAdapter.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/ChannelPipeline.java b/transport/src/main/java/io/netty/channel/ChannelPipeline.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/ChannelPipelineException.java b/transport/src/main/java/io/netty/channel/ChannelPipelineException.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/ChannelStateHandler.java b/transport/src/main/java/io/netty/channel/ChannelStateHandler.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/ChannelStateHandlerAdapter.java b/transport/src/main/java/io/netty/channel/ChannelStateHandlerAdapter.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/CombinedChannelHandler.java b/transport/src/main/java/io/netty/channel/CombinedChannelHandler.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/CompleteChannelFuture.java b/transport/src/main/java/io/netty/channel/CompleteChannelFuture.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/DefaultChannelConfig.java b/transport/src/main/java/io/netty/channel/DefaultChannelConfig.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/DefaultChannelFuture.java b/transport/src/main/java/io/netty/channel/DefaultChannelFuture.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/DefaultChannelHandlerContext.java b/transport/src/main/java/io/netty/channel/DefaultChannelHandlerContext.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/DefaultChannelPipeline.java b/transport/src/main/java/io/netty/channel/DefaultChannelPipeline.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/DefaultChannelPipelineModificationTask.java b/transport/src/main/java/io/netty/channel/DefaultChannelPipelineModificationTask.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/DefaultChildEventExecutor.java b/transport/src/main/java/io/netty/channel/DefaultChildEventExecutor.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/DefaultEventExecutor.java b/transport/src/main/java/io/netty/channel/DefaultEventExecutor.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/EventExecutor.java b/transport/src/main/java/io/netty/channel/EventExecutor.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/EventLoop.java b/transport/src/main/java/io/netty/channel/EventLoop.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/EventLoopException.java b/transport/src/main/java/io/netty/channel/EventLoopException.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/FailedChannelFuture.java b/transport/src/main/java/io/netty/channel/FailedChannelFuture.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/MultithreadEventExecutor.java b/transport/src/main/java/io/netty/channel/MultithreadEventExecutor.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/MultithreadEventLoop.java b/transport/src/main/java/io/netty/channel/MultithreadEventLoop.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/NoSuchBufferException.java b/transport/src/main/java/io/netty/channel/NoSuchBufferException.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/ServerChannel.java b/transport/src/main/java/io/netty/channel/ServerChannel.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/SingleThreadEventExecutor.java b/transport/src/main/java/io/netty/channel/SingleThreadEventExecutor.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/SingleThreadEventLoop.java b/transport/src/main/java/io/netty/channel/SingleThreadEventLoop.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/SucceededChannelFuture.java b/transport/src/main/java/io/netty/channel/SucceededChannelFuture.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/VoidChannelFuture.java b/transport/src/main/java/io/netty/channel/VoidChannelFuture.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/embedded/AbstractEmbeddedChannel.java b/transport/src/main/java/io/netty/channel/embedded/AbstractEmbeddedChannel.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/embedded/EmbeddedByteChannel.java b/transport/src/main/java/io/netty/channel/embedded/EmbeddedByteChannel.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/embedded/EmbeddedEventLoop.java b/transport/src/main/java/io/netty/channel/embedded/EmbeddedEventLoop.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/embedded/EmbeddedMessageChannel.java b/transport/src/main/java/io/netty/channel/embedded/EmbeddedMessageChannel.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/embedded/EmbeddedSocketAddress.java b/transport/src/main/java/io/netty/channel/embedded/EmbeddedSocketAddress.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/embedded/package-info.java b/transport/src/main/java/io/netty/channel/embedded/package-info.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/group/ChannelGroup.java b/transport/src/main/java/io/netty/channel/group/ChannelGroup.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/group/ChannelGroupFuture.java b/transport/src/main/java/io/netty/channel/group/ChannelGroupFuture.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/group/ChannelGroupFutureListener.java b/transport/src/main/java/io/netty/channel/group/ChannelGroupFutureListener.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/group/CombinedIterator.java b/transport/src/main/java/io/netty/channel/group/CombinedIterator.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/group/DefaultChannelGroup.java b/transport/src/main/java/io/netty/channel/group/DefaultChannelGroup.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/group/DefaultChannelGroupFuture.java b/transport/src/main/java/io/netty/channel/group/DefaultChannelGroupFuture.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/group/package-info.java b/transport/src/main/java/io/netty/channel/group/package-info.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/local/LocalAddress.java b/transport/src/main/java/io/netty/channel/local/LocalAddress.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/local/LocalChannel.java b/transport/src/main/java/io/netty/channel/local/LocalChannel.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/local/LocalChannelRegistry.java b/transport/src/main/java/io/netty/channel/local/LocalChannelRegistry.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/local/LocalChildEventLoop.java b/transport/src/main/java/io/netty/channel/local/LocalChildEventLoop.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/local/LocalEventLoop.java b/transport/src/main/java/io/netty/channel/local/LocalEventLoop.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/local/LocalServerChannel.java b/transport/src/main/java/io/netty/channel/local/LocalServerChannel.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/local/package-info.java b/transport/src/main/java/io/netty/channel/local/package-info.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/package-info.java b/transport/src/main/java/io/netty/channel/package-info.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/socket/DatagramChannel.java b/transport/src/main/java/io/netty/channel/socket/DatagramChannel.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/socket/DatagramChannelConfig.java b/transport/src/main/java/io/netty/channel/socket/DatagramChannelConfig.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/socket/DatagramPacket.java b/transport/src/main/java/io/netty/channel/socket/DatagramPacket.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/socket/DefaultDatagramChannelConfig.java b/transport/src/main/java/io/netty/channel/socket/DefaultDatagramChannelConfig.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/socket/DefaultServerSocketChannelConfig.java b/transport/src/main/java/io/netty/channel/socket/DefaultServerSocketChannelConfig.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/socket/DefaultSocketChannelConfig.java b/transport/src/main/java/io/netty/channel/socket/DefaultSocketChannelConfig.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/socket/InternetProtocolFamily.java b/transport/src/main/java/io/netty/channel/socket/InternetProtocolFamily.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/socket/ServerSocketChannel.java b/transport/src/main/java/io/netty/channel/socket/ServerSocketChannel.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/socket/ServerSocketChannelConfig.java b/transport/src/main/java/io/netty/channel/socket/ServerSocketChannelConfig.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/socket/SocketChannel.java b/transport/src/main/java/io/netty/channel/socket/SocketChannel.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/socket/SocketChannelConfig.java b/transport/src/main/java/io/netty/channel/socket/SocketChannelConfig.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/socket/nio/AbstractNioByteChannel.java b/transport/src/main/java/io/netty/channel/socket/nio/AbstractNioByteChannel.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/socket/nio/AbstractNioChannel.java b/transport/src/main/java/io/netty/channel/socket/nio/AbstractNioChannel.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/socket/nio/AbstractNioMessageChannel.java b/transport/src/main/java/io/netty/channel/socket/nio/AbstractNioMessageChannel.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/socket/nio/NioChildEventLoop.java b/transport/src/main/java/io/netty/channel/socket/nio/NioChildEventLoop.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/socket/nio/NioDatagramChannel.java b/transport/src/main/java/io/netty/channel/socket/nio/NioDatagramChannel.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/socket/nio/NioDatagramChannelConfig.java b/transport/src/main/java/io/netty/channel/socket/nio/NioDatagramChannelConfig.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/socket/nio/NioEventLoop.java b/transport/src/main/java/io/netty/channel/socket/nio/NioEventLoop.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/socket/nio/NioServerSocketChannel.java b/transport/src/main/java/io/netty/channel/socket/nio/NioServerSocketChannel.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/socket/nio/NioSocketChannel.java b/transport/src/main/java/io/netty/channel/socket/nio/NioSocketChannel.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/socket/nio/ProtocolFamilyConverter.java b/transport/src/main/java/io/netty/channel/socket/nio/ProtocolFamilyConverter.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/socket/nio/SelectorUtil.java b/transport/src/main/java/io/netty/channel/socket/nio/SelectorUtil.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/socket/nio/package-info.java b/transport/src/main/java/io/netty/channel/socket/nio/package-info.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/socket/nio2/AbstractAsyncChannel.java b/transport/src/main/java/io/netty/channel/socket/nio2/AbstractAsyncChannel.java index f2371239b1..01a85958bb 100755 --- a/transport/src/main/java/io/netty/channel/socket/nio2/AbstractAsyncChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/nio2/AbstractAsyncChannel.java @@ -17,7 +17,6 @@ package io.netty.channel.socket.nio2; import io.netty.channel.AbstractChannel; import io.netty.channel.Channel; -import io.netty.channel.ChannelConfig; import io.netty.channel.ChannelFuture; import io.netty.channel.EventLoop; @@ -65,24 +64,12 @@ public abstract class AbstractAsyncChannel extends AbstractChannel { return ch; } - @Override - public ChannelConfig config() { - // TODO: Fix me - return null; - } @Override public boolean isOpen() { return ch == null || ch.isOpen(); } - @Override - protected boolean isCompatible(EventLoop loop) { - // TODO: Fix me - return true; - } - - @Override protected void doDeregister() throws Exception { throw new UnsupportedOperationException("Deregistration is not supported by AbstractAsyncChannel"); @@ -93,6 +80,11 @@ public abstract class AbstractAsyncChannel extends AbstractChannel { return new AsyncUnsafe(); } + @Override + protected boolean isCompatible(EventLoop loop) { + return loop instanceof AsyncChildEventLoop; + } + protected class AsyncUnsafe extends AbstractUnsafe { @Override diff --git a/transport/src/main/java/io/netty/channel/socket/nio2/AsyncChildEventLoop.java b/transport/src/main/java/io/netty/channel/socket/nio2/AsyncChildEventLoop.java new file mode 100755 index 0000000000..779f075dd3 --- /dev/null +++ b/transport/src/main/java/io/netty/channel/socket/nio2/AsyncChildEventLoop.java @@ -0,0 +1,51 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.channel.socket.nio2; + +import io.netty.channel.SingleThreadEventLoop; + +import java.util.concurrent.ThreadFactory; + +final class AsyncChildEventLoop extends SingleThreadEventLoop { + + AsyncChildEventLoop(ThreadFactory threadFactory) { + super(threadFactory); + } + + @Override + protected void run() { + for (;;) { + Runnable task; + try { + task = takeTask(); + task.run(); + } catch (InterruptedException e) { + // Waken up by interruptThread() + } + + if (isShutdown() && peekTask() == null) { + break; + } + } + } + + @Override + protected void wakeup(boolean inEventLoop) { + if (!inEventLoop) { + interruptThread(); + } + } +} diff --git a/transport/src/main/java/io/netty/channel/socket/nio2/AsyncEventLoop.java b/transport/src/main/java/io/netty/channel/socket/nio2/AsyncEventLoop.java new file mode 100755 index 0000000000..8c4ecd40b5 --- /dev/null +++ b/transport/src/main/java/io/netty/channel/socket/nio2/AsyncEventLoop.java @@ -0,0 +1,41 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.channel.socket.nio2; + +import io.netty.channel.EventExecutor; +import io.netty.channel.MultithreadEventLoop; + +import java.util.concurrent.ThreadFactory; + +public class AsyncEventLoop extends MultithreadEventLoop { + + public AsyncEventLoop() { + this(0); + } + + public AsyncEventLoop(int nThreads) { + this(nThreads, null); + } + + public AsyncEventLoop(int nThreads, ThreadFactory threadFactory) { + super(nThreads, threadFactory); + } + + @Override + protected EventExecutor newChild(ThreadFactory threadFactory, Object... args) throws Exception { + return new AsyncChildEventLoop(threadFactory); + } +} diff --git a/transport/src/main/java/io/netty/channel/socket/nio2/AsyncNetworkChannel.java b/transport/src/main/java/io/netty/channel/socket/nio2/AsyncNetworkChannel.java new file mode 100755 index 0000000000..eed1b6f5de --- /dev/null +++ b/transport/src/main/java/io/netty/channel/socket/nio2/AsyncNetworkChannel.java @@ -0,0 +1,52 @@ +package io.netty.channel.socket.nio2; + +import java.io.IOException; +import java.net.SocketAddress; +import java.net.SocketOption; +import java.nio.channels.NetworkChannel; +import java.util.Map; +import java.util.Set; + +public class AsyncNetworkChannel implements NetworkChannel { + + private Map, Object> options; + + @Override + public void close() throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isOpen() { + throw new UnsupportedOperationException(); + + } + + @Override + public NetworkChannel bind(SocketAddress local) throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public SocketAddress getLocalAddress() throws IOException { + throw new UnsupportedOperationException(); + } + + @Override + public synchronized T getOption(SocketOption name) throws IOException { + // TODO Auto-generated method stub + return null; + } + + @Override + public synchronized NetworkChannel setOption(SocketOption name, T value) throws IOException { + // TODO Auto-generated method stub + return null; + } + + @Override + public Set> supportedOptions() { + throw new UnsupportedOperationException(); + } + +} diff --git a/transport/src/main/java/io/netty/channel/socket/nio2/AsyncServerSocketChannel.java b/transport/src/main/java/io/netty/channel/socket/nio2/AsyncServerSocketChannel.java index 816d7f6be6..81cc8802e9 100755 --- a/transport/src/main/java/io/netty/channel/socket/nio2/AsyncServerSocketChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/nio2/AsyncServerSocketChannel.java @@ -34,6 +34,7 @@ public class AsyncServerSocketChannel extends AbstractAsyncChannel implements Se private static final AcceptHandler ACCEPT_HANDLER = new AcceptHandler(); private static final InternalLogger logger = InternalLoggerFactory.getInstance(AsyncServerSocketChannel.class); + private volatile AsyncServerSocketChannelConfig config; public AsyncServerSocketChannel() { super(null, null); @@ -47,7 +48,15 @@ public class AsyncServerSocketChannel extends AbstractAsyncChannel implements Se @Override public boolean isActive() { - return localAddress0() != null; + AsynchronousServerSocketChannel channel = javaChannel(); + try { + if (channel != null && channel.getLocalAddress() != null) { + return true; + } + } catch (IOException e) { + return true; + } + return false; } @Override @@ -72,6 +81,8 @@ public class AsyncServerSocketChannel extends AbstractAsyncChannel implements Se @Override protected void doBind(SocketAddress localAddress) throws Exception { javaChannel().bind(localAddress); + javaChannel().accept(this, ACCEPT_HANDLER); + } @Override @@ -98,7 +109,8 @@ public class AsyncServerSocketChannel extends AbstractAsyncChannel implements Se @Override protected Runnable doRegister() throws Exception { ch = AsynchronousServerSocketChannel.open(AsynchronousChannelGroup.withThreadPool(eventLoop())); - javaChannel().accept(this, ACCEPT_HANDLER); + config = new AsyncServerSocketChannelConfig(javaChannel()); + return null; } @@ -109,7 +121,7 @@ public class AsyncServerSocketChannel extends AbstractAsyncChannel implements Se channel.javaChannel().accept(channel, this); // create the socket add it to the buffer and fire the event - channel.outboundMessageBuffer().add(new AsyncSocketchannel(channel, null)); + channel.pipeline().inboundMessageBuffer().add(new AsyncSocketchannel(channel, null, ch)); channel.pipeline().fireInboundBufferUpdated(); } @@ -117,4 +129,12 @@ public class AsyncServerSocketChannel extends AbstractAsyncChannel implements Se logger.warn("Failed to create a new channel from an accepted socket.", t); } } + + @Override + public AsyncServerSocketChannelConfig config() { + if (config == null) { + throw new IllegalStateException("Channel not registered yet"); + } + return config; + } } diff --git a/transport/src/main/java/io/netty/channel/socket/nio2/AsyncServerSocketChannelConfig.java b/transport/src/main/java/io/netty/channel/socket/nio2/AsyncServerSocketChannelConfig.java new file mode 100755 index 0000000000..55f62b51ce --- /dev/null +++ b/transport/src/main/java/io/netty/channel/socket/nio2/AsyncServerSocketChannelConfig.java @@ -0,0 +1,138 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.channel.socket.nio2; + +import static io.netty.channel.ChannelOption.*; +import io.netty.channel.ChannelException; +import io.netty.channel.ChannelOption; +import io.netty.channel.DefaultChannelConfig; +import io.netty.channel.socket.ServerSocketChannelConfig; + +import java.io.IOException; +import java.net.StandardSocketOptions; +import java.nio.channels.AsynchronousServerSocketChannel; +import java.util.Map; + +/** + * The Async {@link ServerSocketChannelConfig} implementation. + */ +public class AsyncServerSocketChannelConfig extends DefaultChannelConfig + implements ServerSocketChannelConfig { + + private final AsynchronousServerSocketChannel channel; + private volatile int backlog; + + /** + * Creates a new instance. + */ + public AsyncServerSocketChannelConfig(AsynchronousServerSocketChannel channel) { + if (channel == null) { + throw new NullPointerException("channel"); + } + this.channel = channel; + } + + @Override + public Map, Object> getOptions() { + return getOptions(super.getOptions(), SO_RCVBUF, SO_REUSEADDR, SO_BACKLOG); + } + + @Override + public T getOption(ChannelOption option) { + if (option == SO_RCVBUF) { + return (T) Integer.valueOf(getReceiveBufferSize()); + } + if (option == SO_REUSEADDR) { + return (T) Boolean.valueOf(isReuseAddress()); + } + if (option == SO_BACKLOG) { + return (T) Integer.valueOf(getBacklog()); + } + + return super.getOption(option); + } + + @Override + public boolean setOption(ChannelOption option, T value) { + validate(option, value); + + if (option == SO_RCVBUF) { + setReceiveBufferSize((Integer) value); + } else if (option == SO_REUSEADDR) { + setReuseAddress((Boolean) value); + } else if (option == SO_BACKLOG) { + setBacklog((Integer) value); + } else { + return super.setOption(option, value); + } + + return true; + } + + @Override + public boolean isReuseAddress() { + try { + return channel.getOption(StandardSocketOptions.SO_REUSEADDR); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @Override + public void setReuseAddress(boolean reuseAddress) { + try { + channel.setOption(StandardSocketOptions.SO_REUSEADDR, reuseAddress); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @Override + public int getReceiveBufferSize() { + try { + return channel.getOption(StandardSocketOptions.SO_RCVBUF); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @Override + public void setReceiveBufferSize(int receiveBufferSize) { + try { + channel.setOption(StandardSocketOptions.SO_RCVBUF, receiveBufferSize); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @Override + public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) { + throw new UnsupportedOperationException(); + } + + @Override + public int getBacklog() { + return backlog; + } + + @Override + public void setBacklog(int backlog) { + if (backlog < 0) { + throw new IllegalArgumentException("backlog: " + backlog); + } + this.backlog = backlog; + } +} diff --git a/transport/src/main/java/io/netty/channel/socket/nio2/AsyncSocketChannelConfig.java b/transport/src/main/java/io/netty/channel/socket/nio2/AsyncSocketChannelConfig.java new file mode 100755 index 0000000000..d89b61acf0 --- /dev/null +++ b/transport/src/main/java/io/netty/channel/socket/nio2/AsyncSocketChannelConfig.java @@ -0,0 +1,237 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.channel.socket.nio2; + +import static io.netty.channel.ChannelOption.*; +import io.netty.channel.ChannelException; +import io.netty.channel.ChannelOption; +import io.netty.channel.DefaultChannelConfig; +import io.netty.channel.socket.SocketChannelConfig; + +import java.io.IOException; +import java.net.StandardSocketOptions; +import java.nio.channels.NetworkChannel; +import java.util.Map; + +/** + * The default {@link SocketChannelConfig} implementation. + */ +public class AsyncSocketChannelConfig extends DefaultChannelConfig + implements SocketChannelConfig { + + private final NetworkChannel channel; + + /** + * Creates a new instance. + */ + public AsyncSocketChannelConfig(NetworkChannel channel) { + if (channel == null) { + throw new NullPointerException("channel"); + } + this.channel = channel; + } + + @Override + public Map, Object> getOptions() { + return getOptions( + super.getOptions(), + SO_RCVBUF, SO_SNDBUF, TCP_NODELAY, SO_KEEPALIVE, SO_REUSEADDR, SO_LINGER, IP_TOS); + } + + @Override + public T getOption(ChannelOption option) { + if (option == SO_RCVBUF) { + return (T) Integer.valueOf(getReceiveBufferSize()); + } + if (option == SO_SNDBUF) { + return (T) Integer.valueOf(getSendBufferSize()); + } + if (option == TCP_NODELAY) { + return (T) Boolean.valueOf(isTcpNoDelay()); + } + if (option == SO_KEEPALIVE) { + return (T) Boolean.valueOf(isKeepAlive()); + } + if (option == SO_REUSEADDR) { + return (T) Boolean.valueOf(isReuseAddress()); + } + if (option == SO_LINGER) { + return (T) Integer.valueOf(getSoLinger()); + } + if (option == IP_TOS) { + return (T) Integer.valueOf(getTrafficClass()); + } + + return super.getOption(option); + } + + @Override + public boolean setOption(ChannelOption option, T value) { + validate(option, value); + + if (option == SO_RCVBUF) { + setReceiveBufferSize((Integer) value); + } else if (option == SO_SNDBUF) { + setSendBufferSize((Integer) value); + } else if (option == TCP_NODELAY) { + setTcpNoDelay((Boolean) value); + } else if (option == SO_KEEPALIVE) { + setKeepAlive((Boolean) value); + } else if (option == SO_REUSEADDR) { + setReuseAddress((Boolean) value); + } else if (option == SO_LINGER) { + setSoLinger((Integer) value); + } else if (option == IP_TOS) { + setTrafficClass((Integer) value); + } else { + return super.setOption(option, value); + } + + return true; + } + + @Override + public int getReceiveBufferSize() { + try { + return (int) channel.getOption(StandardSocketOptions.SO_RCVBUF); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @Override + public int getSendBufferSize() { + try { + return channel.getOption(StandardSocketOptions.SO_SNDBUF); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @Override + public int getSoLinger() { + try { + return channel.getOption(StandardSocketOptions.SO_LINGER); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @Override + public int getTrafficClass() { + try { + return channel.getOption(StandardSocketOptions.IP_TOS); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @Override + public boolean isKeepAlive() { + try { + return channel.getOption(StandardSocketOptions.SO_KEEPALIVE); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @Override + public boolean isReuseAddress() { + try { + return channel.getOption(StandardSocketOptions.SO_REUSEADDR); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @Override + public boolean isTcpNoDelay() { + try { + return channel.getOption(StandardSocketOptions.SO_REUSEADDR); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @Override + public void setKeepAlive(boolean keepAlive) { + try { + channel.setOption(StandardSocketOptions.SO_KEEPALIVE, keepAlive); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @Override + public void setPerformancePreferences( + int connectionTime, int latency, int bandwidth) { + throw new UnsupportedOperationException(); + } + + @Override + public void setReceiveBufferSize(int receiveBufferSize) { + try { + channel.setOption(StandardSocketOptions.SO_RCVBUF, receiveBufferSize); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @Override + public void setReuseAddress(boolean reuseAddress) { + try { + channel.setOption(StandardSocketOptions.SO_REUSEADDR, reuseAddress); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @Override + public void setSendBufferSize(int sendBufferSize) { + try { + channel.setOption(StandardSocketOptions.SO_SNDBUF, sendBufferSize); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @Override + public void setSoLinger(int soLinger) { + try { + channel.setOption(StandardSocketOptions.SO_LINGER, soLinger); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @Override + public void setTcpNoDelay(boolean tcpNoDelay) { + try { + channel.setOption(StandardSocketOptions.TCP_NODELAY, tcpNoDelay); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @Override + public void setTrafficClass(int trafficClass) { + try { + channel.setOption(StandardSocketOptions.IP_TOS, trafficClass); + } catch (IOException e) { + throw new ChannelException(e); + } + } +} diff --git a/transport/src/main/java/io/netty/channel/socket/nio2/AsyncSocketchannel.java b/transport/src/main/java/io/netty/channel/socket/nio2/AsyncSocketchannel.java index 4c8f63ea90..cd1e8619cc 100755 --- a/transport/src/main/java/io/netty/channel/socket/nio2/AsyncSocketchannel.java +++ b/transport/src/main/java/io/netty/channel/socket/nio2/AsyncSocketchannel.java @@ -18,7 +18,10 @@ package io.netty.channel.socket.nio2; import io.netty.buffer.ByteBuf; import io.netty.buffer.ChannelBufType; import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPipeline; +import io.netty.channel.ChannelStateHandler; +import io.netty.channel.ChannelStateHandlerAdapter; import java.io.IOException; import java.net.InetSocketAddress; @@ -33,21 +36,42 @@ public class AsyncSocketchannel extends AbstractAsyncChannel { private static final CompletionHandler CONNECT_HANDLER = new ConnectHandler(); private static final CompletionHandler READ_HANDLER = new ReadHandler(); private static final CompletionHandler WRITE_HANDLER = new WriteHandler(); + private static final ChannelStateHandler READ_START_HANDLER = new ChannelStateHandlerAdapter() { + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + try { + super.channelActive(ctx); + AsyncSocketchannel.read((AsyncSocketchannel)ctx.channel()); + + } finally { + ctx.pipeline().remove(this); + } + + + } + + }; private final AtomicBoolean flushing = new AtomicBoolean(false); + private volatile AsyncSocketChannelConfig config; public AsyncSocketchannel() { - this(null, null); + this(null, null, null); } - public AsyncSocketchannel(AsyncServerSocketChannel parent, Integer id) { + public AsyncSocketchannel(AsyncServerSocketChannel parent, Integer id, AsynchronousSocketChannel channel) { super(parent, id); + this.ch = channel; + if (ch != null) { + config = new AsyncSocketChannelConfig(javaChannel()); + pipeline().addLast(READ_START_HANDLER); + } } @Override public boolean isActive() { AsynchronousSocketChannel ch = javaChannel(); - return ch.isOpen(); + return ch.isOpen() && remoteAddress() != null; } @Override @@ -95,13 +119,18 @@ public class AsyncSocketchannel extends AbstractAsyncChannel { @Override protected Runnable doRegister() throws Exception { - assert ch == null; - ch = AsynchronousSocketChannel.open(AsynchronousChannelGroup.withThreadPool(eventLoop())); + if (ch == null) { + ch = AsynchronousSocketChannel.open(AsynchronousChannelGroup.withThreadPool(eventLoop())); + config = new AsyncSocketChannelConfig(javaChannel()); + pipeline().addLast(READ_START_HANDLER); + } + + return null; } - private void read() { - javaChannel().read(pipeline().inboundByteBuffer().nioBuffer(), this, READ_HANDLER); + private static void read(AsyncSocketchannel channel) { + channel.javaChannel().read(channel.pipeline().inboundByteBuffer().nioBuffer(), channel, READ_HANDLER); } @Override @@ -132,14 +161,6 @@ public class AsyncSocketchannel extends AbstractAsyncChannel { return false; } - private static boolean expandReadBuffer(ByteBuf byteBuf) { - if (!byteBuf.writable()) { - // FIXME: Magic number - byteBuf.ensureWritableBytes(4096); - return true; - } - return false; - } private static final class WriteHandler implements CompletionHandler { @@ -213,10 +234,20 @@ public class AsyncSocketchannel extends AbstractAsyncChannel { channel.close(channel.unsafe().voidFuture()); } else { // start the next read - channel.read(); + AsyncSocketchannel.read(channel); } } } + + private static boolean expandReadBuffer(ByteBuf byteBuf) { + if (!byteBuf.writable()) { + // FIXME: Magic number + byteBuf.ensureWritableBytes(4096); + return true; + } + return false; + } + @Override public void failed(Throwable t, AsyncSocketchannel channel) { @@ -225,7 +256,7 @@ public class AsyncSocketchannel extends AbstractAsyncChannel { channel.close(channel.unsafe().voidFuture()); } else { // start the next read - channel.read(); + AsyncSocketchannel.read(channel); } } } @@ -235,6 +266,9 @@ public class AsyncSocketchannel extends AbstractAsyncChannel { @Override public void completed(Void result, AsyncSocketchannel channel) { ((AsyncUnsafe) channel.unsafe()).connectSuccess(); + + // start reading from channel + AsyncSocketchannel.read(channel); } @Override @@ -243,4 +277,13 @@ public class AsyncSocketchannel extends AbstractAsyncChannel { } } + @Override + public AsyncSocketChannelConfig config() { + if (config == null) { + throw new IllegalStateException("Channel not registered yet"); + } + return config; + } + + } diff --git a/transport/src/main/java/io/netty/channel/socket/nio2/package-info.java b/transport/src/main/java/io/netty/channel/socket/nio2/package-info.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/socket/oio/AbstractOioByteChannel.java b/transport/src/main/java/io/netty/channel/socket/oio/AbstractOioByteChannel.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/socket/oio/AbstractOioChannel.java b/transport/src/main/java/io/netty/channel/socket/oio/AbstractOioChannel.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/socket/oio/AbstractOioMessageChannel.java b/transport/src/main/java/io/netty/channel/socket/oio/AbstractOioMessageChannel.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/socket/oio/OioChildEventLoop.java b/transport/src/main/java/io/netty/channel/socket/oio/OioChildEventLoop.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/socket/oio/OioDatagramChannel.java b/transport/src/main/java/io/netty/channel/socket/oio/OioDatagramChannel.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/socket/oio/OioEventLoop.java b/transport/src/main/java/io/netty/channel/socket/oio/OioEventLoop.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/socket/oio/OioServerSocketChannel.java b/transport/src/main/java/io/netty/channel/socket/oio/OioServerSocketChannel.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/socket/oio/OioSocketChannel.java b/transport/src/main/java/io/netty/channel/socket/oio/OioSocketChannel.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/socket/oio/package-info.java b/transport/src/main/java/io/netty/channel/socket/oio/package-info.java old mode 100644 new mode 100755 diff --git a/transport/src/main/java/io/netty/channel/socket/package-info.java b/transport/src/main/java/io/netty/channel/socket/package-info.java old mode 100644 new mode 100755 diff --git a/transport/src/test/java/io/netty/channel/AsyncTransportTest.java b/transport/src/test/java/io/netty/channel/AsyncTransportTest.java new file mode 100755 index 0000000000..a8153e26f1 --- /dev/null +++ b/transport/src/test/java/io/netty/channel/AsyncTransportTest.java @@ -0,0 +1,42 @@ +package io.netty.channel; + +import java.net.InetSocketAddress; + +import io.netty.bootstrap.ServerBootstrap; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundByteHandlerAdapter; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.socket.nio2.AsyncEventLoop; +import io.netty.channel.socket.nio2.AsyncServerSocketChannel; +import io.netty.channel.socket.nio2.AsyncSocketchannel; +import io.netty.util.CharsetUtil; + +public class AsyncTransportTest { + + public static void main(String args[]) { + // Configure a test server + ServerBootstrap sb = new ServerBootstrap(); + sb.eventLoop(new AsyncEventLoop(), new AsyncEventLoop()) + .channel(new AsyncServerSocketChannel()) + .localAddress(new InetSocketAddress(9999)) + .childHandler(new ChannelInitializer() { + @Override + public void initChannel(AsyncSocketchannel ch) throws Exception { + ch.pipeline().addLast(new ChannelInboundByteHandlerAdapter() { + + @Override + public void inboundBufferUpdated(ChannelHandlerContext ctx, ByteBuf in) throws Exception { + System.out.print(in.toString(CharsetUtil.US_ASCII)); + } + }); + } + }); + ChannelFuture future = sb.bind().awaitUninterruptibly(); + if (!future.isSuccess()) { + future.cause().printStackTrace(); + } + future.channel().closeFuture().awaitUninterruptibly(); + } +} diff --git a/transport/src/test/java/io/netty/channel/CompleteChannelFutureTest.java b/transport/src/test/java/io/netty/channel/CompleteChannelFutureTest.java old mode 100644 new mode 100755 diff --git a/transport/src/test/java/io/netty/channel/DefaultChannelPipelineTest.java b/transport/src/test/java/io/netty/channel/DefaultChannelPipelineTest.java old mode 100644 new mode 100755 diff --git a/transport/src/test/java/io/netty/channel/FailedChannelFutureTest.java b/transport/src/test/java/io/netty/channel/FailedChannelFutureTest.java old mode 100644 new mode 100755 diff --git a/transport/src/test/java/io/netty/channel/SingleThreadEventLoopTest.java b/transport/src/test/java/io/netty/channel/SingleThreadEventLoopTest.java old mode 100644 new mode 100755 diff --git a/transport/src/test/java/io/netty/channel/SucceededChannelFutureTest.java b/transport/src/test/java/io/netty/channel/SucceededChannelFutureTest.java old mode 100644 new mode 100755 diff --git a/transport/src/test/java/io/netty/channel/local/LocalChannelRegistryTest.java b/transport/src/test/java/io/netty/channel/local/LocalChannelRegistryTest.java old mode 100644 new mode 100755 diff --git a/transport/src/test/java/io/netty/channel/local/LocalTransportThreadModelTest.java b/transport/src/test/java/io/netty/channel/local/LocalTransportThreadModelTest.java old mode 100644 new mode 100755 From 70baea35dac0c2ac9bc4bdb4da6dff089e16207e Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Sat, 16 Jun 2012 20:51:45 +0200 Subject: [PATCH 03/87] Make reads work like expected with AOI. See #396 --- .../socket/nio2/AsyncNetworkChannel.java | 52 -------------- .../socket/nio2/AsyncSocketchannel.java | 68 ++++++++++++------- 2 files changed, 42 insertions(+), 78 deletions(-) delete mode 100755 transport/src/main/java/io/netty/channel/socket/nio2/AsyncNetworkChannel.java diff --git a/transport/src/main/java/io/netty/channel/socket/nio2/AsyncNetworkChannel.java b/transport/src/main/java/io/netty/channel/socket/nio2/AsyncNetworkChannel.java deleted file mode 100755 index eed1b6f5de..0000000000 --- a/transport/src/main/java/io/netty/channel/socket/nio2/AsyncNetworkChannel.java +++ /dev/null @@ -1,52 +0,0 @@ -package io.netty.channel.socket.nio2; - -import java.io.IOException; -import java.net.SocketAddress; -import java.net.SocketOption; -import java.nio.channels.NetworkChannel; -import java.util.Map; -import java.util.Set; - -public class AsyncNetworkChannel implements NetworkChannel { - - private Map, Object> options; - - @Override - public void close() throws IOException { - throw new UnsupportedOperationException(); - } - - @Override - public boolean isOpen() { - throw new UnsupportedOperationException(); - - } - - @Override - public NetworkChannel bind(SocketAddress local) throws IOException { - throw new UnsupportedOperationException(); - } - - @Override - public SocketAddress getLocalAddress() throws IOException { - throw new UnsupportedOperationException(); - } - - @Override - public synchronized T getOption(SocketOption name) throws IOException { - // TODO Auto-generated method stub - return null; - } - - @Override - public synchronized NetworkChannel setOption(SocketOption name, T value) throws IOException { - // TODO Auto-generated method stub - return null; - } - - @Override - public Set> supportedOptions() { - throw new UnsupportedOperationException(); - } - -} diff --git a/transport/src/main/java/io/netty/channel/socket/nio2/AsyncSocketchannel.java b/transport/src/main/java/io/netty/channel/socket/nio2/AsyncSocketchannel.java index cd1e8619cc..98946f9882 100755 --- a/transport/src/main/java/io/netty/channel/socket/nio2/AsyncSocketchannel.java +++ b/transport/src/main/java/io/netty/channel/socket/nio2/AsyncSocketchannel.java @@ -26,6 +26,7 @@ import io.netty.channel.ChannelStateHandlerAdapter; import java.io.IOException; import java.net.InetSocketAddress; import java.net.SocketAddress; +import java.nio.ByteBuffer; import java.nio.channels.AsynchronousChannelGroup; import java.nio.channels.AsynchronousSocketChannel; import java.nio.channels.CompletionHandler; @@ -42,8 +43,10 @@ public class AsyncSocketchannel extends AbstractAsyncChannel { public void channelActive(ChannelHandlerContext ctx) throws Exception { try { super.channelActive(ctx); + + // once the channel is active, the first read is scheduled AsyncSocketchannel.read((AsyncSocketchannel)ctx.channel()); - + } finally { ctx.pipeline().remove(this); } @@ -129,10 +132,30 @@ public class AsyncSocketchannel extends AbstractAsyncChannel { return null; } + /** + * Trigger a read from the {@link AsyncSocketchannel} + * + */ private static void read(AsyncSocketchannel channel) { - channel.javaChannel().read(channel.pipeline().inboundByteBuffer().nioBuffer(), channel, READ_HANDLER); + ByteBuf byteBuf = channel.pipeline().inboundByteBuffer(); + expandReadBuffer(byteBuf); + + // Get a ByteBuffer view on the ByteBuf and clear it before try to read + ByteBuffer buffer = byteBuf.nioBuffer(); + buffer.clear(); + channel.javaChannel().read(buffer, channel, READ_HANDLER); } + + private static boolean expandReadBuffer(ByteBuf byteBuf) { + if (!byteBuf.writable()) { + // FIXME: Magic number + byteBuf.ensureWritableBytes(4096); + return true; + } + return false; + } + @Override protected void doBind(SocketAddress localAddress) throws Exception { javaChannel().bind(localAddress); @@ -200,23 +223,26 @@ public class AsyncSocketchannel extends AbstractAsyncChannel { assert channel.eventLoop().inEventLoop(); final ChannelPipeline pipeline = channel.pipeline(); - final ByteBuf byteBuf = pipeline.inboundByteBuffer(); boolean closed = false; boolean read = false; try { - expandReadBuffer(byteBuf); - for (;;) { - int localReadAmount = result.intValue(); - if (localReadAmount > 0) { - read = true; - } else if (localReadAmount < 0) { - closed = true; - break; - } - if (!expandReadBuffer(byteBuf)) { - break; - } + + int localReadAmount = result.intValue(); + if (localReadAmount > 0) { + //Set the writerIndex of the buffer correctly to the + // current writerIndex + read amount of bytes. + // + // This is needed as the ByteBuffer and the ByteBuf does not share + // each others index + final ByteBuf byteBuf = pipeline.inboundByteBuffer(); + byteBuf.writerIndex(byteBuf.writerIndex() + result); + + read = true; + + } else if (localReadAmount < 0) { + closed = true; } + } catch (Throwable t) { if (read) { read = false; @@ -238,16 +264,6 @@ public class AsyncSocketchannel extends AbstractAsyncChannel { } } } - - private static boolean expandReadBuffer(ByteBuf byteBuf) { - if (!byteBuf.writable()) { - // FIXME: Magic number - byteBuf.ensureWritableBytes(4096); - return true; - } - return false; - } - @Override public void failed(Throwable t, AsyncSocketchannel channel) { @@ -280,7 +296,7 @@ public class AsyncSocketchannel extends AbstractAsyncChannel { @Override public AsyncSocketChannelConfig config() { if (config == null) { - throw new IllegalStateException("Channel not registered yet"); + throw new IllegalStateException("Channel not open yet"); } return config; } From 2ff22ff4c3246befd19b073aa3ddca48733c47cf Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Sat, 16 Jun 2012 21:16:54 +0200 Subject: [PATCH 04/87] Add javadocs and also handle writes correctly. See #396 --- .../socket/nio2/AbstractAsyncChannel.java | 2 +- .../socket/nio2/AsyncSocketchannel.java | 25 ++++++++++++++----- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/transport/src/main/java/io/netty/channel/socket/nio2/AbstractAsyncChannel.java b/transport/src/main/java/io/netty/channel/socket/nio2/AbstractAsyncChannel.java index 01a85958bb..7ac6884a85 100755 --- a/transport/src/main/java/io/netty/channel/socket/nio2/AbstractAsyncChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/nio2/AbstractAsyncChannel.java @@ -72,7 +72,7 @@ public abstract class AbstractAsyncChannel extends AbstractChannel { @Override protected void doDeregister() throws Exception { - throw new UnsupportedOperationException("Deregistration is not supported by AbstractAsyncChannel"); + // NOOP } @Override diff --git a/transport/src/main/java/io/netty/channel/socket/nio2/AsyncSocketchannel.java b/transport/src/main/java/io/netty/channel/socket/nio2/AsyncSocketchannel.java index 98946f9882..60da25504a 100755 --- a/transport/src/main/java/io/netty/channel/socket/nio2/AsyncSocketchannel.java +++ b/transport/src/main/java/io/netty/channel/socket/nio2/AsyncSocketchannel.java @@ -173,13 +173,17 @@ public class AsyncSocketchannel extends AbstractAsyncChannel { @Override protected boolean isFlushPending() { - // TODO: Fix me - return true; + return false; } + @Override protected boolean doFlushByteBuffer(ByteBuf buf) throws Exception { + // Only one pending write can be scheduled at one time. Otherwise + // a PendingWriteException will be thrown. So use CAS to not run + // into this if (flushing.compareAndSet(false, true)) { - javaChannel().write(buf.nioBuffer(), this, WRITE_HANDLER); + ByteBuffer buffer = (ByteBuffer)buf.nioBuffer(); + javaChannel().write(buffer, this, WRITE_HANDLER); } return false; } @@ -190,13 +194,21 @@ public class AsyncSocketchannel extends AbstractAsyncChannel { @Override public void completed(Integer result, AsyncSocketchannel channel) { ByteBuf buf = channel.pipeline().outboundByteBuffer(); - if (!buf.readable()) { - buf.discardReadBytes(); - } if (result > 0) { + if (result < buf.readableBytes()) { + // Update the readerIndex with the amount of read bytes + buf.readerIndex(buf.readerIndex() + result); + } else { + // not enough space in the buffer anymore so discard everything that + // was read already + buf.discardReadBytes(); + + } channel.notifyFlushFutures(); } + + // Allow to have the next write pending channel.flushing.set(false); } @@ -212,6 +224,7 @@ public class AsyncSocketchannel extends AbstractAsyncChannel { if (cause instanceof IOException) { channel.close(channel.unsafe().voidFuture()); } + // Allow to have the next write pending channel.flushing.set(false); } } From ffc6551acc30eae445c746c5af32ddbe5aefcc13 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Sat, 16 Jun 2012 21:17:45 +0200 Subject: [PATCH 05/87] Adjust name. See #396 --- .../socket/nio2/AsyncServerSocketChannel.java | 2 +- .../socket/nio2/AsyncSocketchannel.java | 42 +++++++++---------- .../io/netty/channel/AsyncTransportTest.java | 14 +++---- 3 files changed, 29 insertions(+), 29 deletions(-) diff --git a/transport/src/main/java/io/netty/channel/socket/nio2/AsyncServerSocketChannel.java b/transport/src/main/java/io/netty/channel/socket/nio2/AsyncServerSocketChannel.java index 81cc8802e9..74a804a52a 100755 --- a/transport/src/main/java/io/netty/channel/socket/nio2/AsyncServerSocketChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/nio2/AsyncServerSocketChannel.java @@ -121,7 +121,7 @@ public class AsyncServerSocketChannel extends AbstractAsyncChannel implements Se channel.javaChannel().accept(channel, this); // create the socket add it to the buffer and fire the event - channel.pipeline().inboundMessageBuffer().add(new AsyncSocketchannel(channel, null, ch)); + channel.pipeline().inboundMessageBuffer().add(new AsyncSocketChannel(channel, null, ch)); channel.pipeline().fireInboundBufferUpdated(); } diff --git a/transport/src/main/java/io/netty/channel/socket/nio2/AsyncSocketchannel.java b/transport/src/main/java/io/netty/channel/socket/nio2/AsyncSocketchannel.java index 60da25504a..0308eaf9e4 100755 --- a/transport/src/main/java/io/netty/channel/socket/nio2/AsyncSocketchannel.java +++ b/transport/src/main/java/io/netty/channel/socket/nio2/AsyncSocketchannel.java @@ -32,11 +32,11 @@ import java.nio.channels.AsynchronousSocketChannel; import java.nio.channels.CompletionHandler; import java.util.concurrent.atomic.AtomicBoolean; -public class AsyncSocketchannel extends AbstractAsyncChannel { +public class AsyncSocketChannel extends AbstractAsyncChannel { - private static final CompletionHandler CONNECT_HANDLER = new ConnectHandler(); - private static final CompletionHandler READ_HANDLER = new ReadHandler(); - private static final CompletionHandler WRITE_HANDLER = new WriteHandler(); + private static final CompletionHandler CONNECT_HANDLER = new ConnectHandler(); + private static final CompletionHandler READ_HANDLER = new ReadHandler(); + private static final CompletionHandler WRITE_HANDLER = new WriteHandler(); private static final ChannelStateHandler READ_START_HANDLER = new ChannelStateHandlerAdapter() { @Override @@ -45,7 +45,7 @@ public class AsyncSocketchannel extends AbstractAsyncChannel { super.channelActive(ctx); // once the channel is active, the first read is scheduled - AsyncSocketchannel.read((AsyncSocketchannel)ctx.channel()); + AsyncSocketChannel.read((AsyncSocketChannel)ctx.channel()); } finally { ctx.pipeline().remove(this); @@ -58,11 +58,11 @@ public class AsyncSocketchannel extends AbstractAsyncChannel { private final AtomicBoolean flushing = new AtomicBoolean(false); private volatile AsyncSocketChannelConfig config; - public AsyncSocketchannel() { + public AsyncSocketChannel() { this(null, null, null); } - public AsyncSocketchannel(AsyncServerSocketChannel parent, Integer id, AsynchronousSocketChannel channel) { + public AsyncSocketChannel(AsyncServerSocketChannel parent, Integer id, AsynchronousSocketChannel channel) { super(parent, id); this.ch = channel; if (ch != null) { @@ -133,10 +133,10 @@ public class AsyncSocketchannel extends AbstractAsyncChannel { } /** - * Trigger a read from the {@link AsyncSocketchannel} + * Trigger a read from the {@link AsyncSocketChannel} * */ - private static void read(AsyncSocketchannel channel) { + private static void read(AsyncSocketChannel channel) { ByteBuf byteBuf = channel.pipeline().inboundByteBuffer(); expandReadBuffer(byteBuf); @@ -189,10 +189,10 @@ public class AsyncSocketchannel extends AbstractAsyncChannel { } - private static final class WriteHandler implements CompletionHandler { + private static final class WriteHandler implements CompletionHandler { @Override - public void completed(Integer result, AsyncSocketchannel channel) { + public void completed(Integer result, AsyncSocketChannel channel) { ByteBuf buf = channel.pipeline().outboundByteBuffer(); if (result > 0) { @@ -213,7 +213,7 @@ public class AsyncSocketchannel extends AbstractAsyncChannel { } @Override - public void failed(Throwable cause, AsyncSocketchannel channel) { + public void failed(Throwable cause, AsyncSocketChannel channel) { ByteBuf buf = channel.pipeline().outboundByteBuffer(); if (!buf.readable()) { buf.discardReadBytes(); @@ -229,10 +229,10 @@ public class AsyncSocketchannel extends AbstractAsyncChannel { } } - private static final class ReadHandler implements CompletionHandler { + private static final class ReadHandler implements CompletionHandler { @Override - public void completed(Integer result, AsyncSocketchannel channel) { + public void completed(Integer result, AsyncSocketChannel channel) { assert channel.eventLoop().inEventLoop(); final ChannelPipeline pipeline = channel.pipeline(); @@ -273,35 +273,35 @@ public class AsyncSocketchannel extends AbstractAsyncChannel { channel.close(channel.unsafe().voidFuture()); } else { // start the next read - AsyncSocketchannel.read(channel); + AsyncSocketChannel.read(channel); } } } @Override - public void failed(Throwable t, AsyncSocketchannel channel) { + public void failed(Throwable t, AsyncSocketChannel channel) { channel.pipeline().fireExceptionCaught(t); if (t instanceof IOException) { channel.close(channel.unsafe().voidFuture()); } else { // start the next read - AsyncSocketchannel.read(channel); + AsyncSocketChannel.read(channel); } } } - private static final class ConnectHandler implements CompletionHandler { + private static final class ConnectHandler implements CompletionHandler { @Override - public void completed(Void result, AsyncSocketchannel channel) { + public void completed(Void result, AsyncSocketChannel channel) { ((AsyncUnsafe) channel.unsafe()).connectSuccess(); // start reading from channel - AsyncSocketchannel.read(channel); + AsyncSocketChannel.read(channel); } @Override - public void failed(Throwable exc, AsyncSocketchannel channel) { + public void failed(Throwable exc, AsyncSocketChannel channel) { ((AsyncUnsafe) channel.unsafe()).connectFailed(exc); } } diff --git a/transport/src/test/java/io/netty/channel/AsyncTransportTest.java b/transport/src/test/java/io/netty/channel/AsyncTransportTest.java index a8153e26f1..e5ea2631d6 100755 --- a/transport/src/test/java/io/netty/channel/AsyncTransportTest.java +++ b/transport/src/test/java/io/netty/channel/AsyncTransportTest.java @@ -10,25 +10,25 @@ import io.netty.channel.ChannelInboundByteHandlerAdapter; import io.netty.channel.ChannelInitializer; import io.netty.channel.socket.nio2.AsyncEventLoop; import io.netty.channel.socket.nio2.AsyncServerSocketChannel; -import io.netty.channel.socket.nio2.AsyncSocketchannel; -import io.netty.util.CharsetUtil; +import io.netty.channel.socket.nio2.AsyncSocketChannel; public class AsyncTransportTest { public static void main(String args[]) { + AsyncEventLoop loop = new AsyncEventLoop(); // Configure a test server ServerBootstrap sb = new ServerBootstrap(); - sb.eventLoop(new AsyncEventLoop(), new AsyncEventLoop()) + sb.eventLoop(loop, loop) .channel(new AsyncServerSocketChannel()) - .localAddress(new InetSocketAddress(9999)) - .childHandler(new ChannelInitializer() { + .localAddress(new InetSocketAddress(9191)) + .childHandler(new ChannelInitializer() { @Override - public void initChannel(AsyncSocketchannel ch) throws Exception { + public void initChannel(AsyncSocketChannel ch) throws Exception { ch.pipeline().addLast(new ChannelInboundByteHandlerAdapter() { @Override public void inboundBufferUpdated(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - System.out.print(in.toString(CharsetUtil.US_ASCII)); + ctx.write(in.slice()); } }); } From 67be5aeda8a8415bf610f09a011ba61c3adb9f19 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Sat, 16 Jun 2012 21:18:25 +0200 Subject: [PATCH 06/87] Rename package from nio2 -> aio. See #396 --- .../socket/aio/AbstractAsyncChannel.java | 168 +++++++++ .../socket/aio/AsyncChildEventLoop.java | 51 +++ .../channel/socket/aio/AsyncEventLoop.java | 41 +++ .../socket/aio/AsyncServerSocketChannel.java | 140 ++++++++ .../aio/AsyncServerSocketChannelConfig.java | 138 ++++++++ .../socket/aio/AsyncSocketChannel.java | 318 ++++++++++++++++++ .../socket/aio/AsyncSocketChannelConfig.java | 237 +++++++++++++ .../channel/socket/aio/package-info.java | 21 ++ .../io/netty/channel/AsyncTransportTest.java | 6 +- 9 files changed, 1117 insertions(+), 3 deletions(-) create mode 100755 transport/src/main/java/io/netty/channel/socket/aio/AbstractAsyncChannel.java create mode 100755 transport/src/main/java/io/netty/channel/socket/aio/AsyncChildEventLoop.java create mode 100755 transport/src/main/java/io/netty/channel/socket/aio/AsyncEventLoop.java create mode 100755 transport/src/main/java/io/netty/channel/socket/aio/AsyncServerSocketChannel.java create mode 100755 transport/src/main/java/io/netty/channel/socket/aio/AsyncServerSocketChannelConfig.java create mode 100755 transport/src/main/java/io/netty/channel/socket/aio/AsyncSocketChannel.java create mode 100755 transport/src/main/java/io/netty/channel/socket/aio/AsyncSocketChannelConfig.java create mode 100755 transport/src/main/java/io/netty/channel/socket/aio/package-info.java diff --git a/transport/src/main/java/io/netty/channel/socket/aio/AbstractAsyncChannel.java b/transport/src/main/java/io/netty/channel/socket/aio/AbstractAsyncChannel.java new file mode 100755 index 0000000000..c32e1cf9bc --- /dev/null +++ b/transport/src/main/java/io/netty/channel/socket/aio/AbstractAsyncChannel.java @@ -0,0 +1,168 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.channel.socket.aio; + +import io.netty.channel.AbstractChannel; +import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; +import io.netty.channel.EventLoop; + +import java.net.ConnectException; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.nio.channels.AsynchronousChannel; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +public abstract class AbstractAsyncChannel extends AbstractChannel { + + protected volatile AsynchronousChannel ch; + + /** + * The future of the current connection attempt. If not null, subsequent + * connection attempts will fail. + */ + protected ChannelFuture connectFuture; + protected ScheduledFuture connectTimeoutFuture; + private ConnectException connectTimeoutException; + + protected AbstractAsyncChannel(Channel parent, Integer id) { + super(parent, id); + } + + + @Override + public InetSocketAddress localAddress() { + if (ch == null) { + return null; + } + return (InetSocketAddress) super.localAddress(); + } + + @Override + public InetSocketAddress remoteAddress() { + if (ch == null) { + return null; + } + return (InetSocketAddress) super.remoteAddress(); + } + + protected AsynchronousChannel javaChannel() { + return ch; + } + + + @Override + public boolean isOpen() { + return ch == null || ch.isOpen(); + } + + @Override + protected void doDeregister() throws Exception { + // NOOP + } + + @Override + protected AsyncUnsafe newUnsafe() { + return new AsyncUnsafe(); + } + + @Override + protected boolean isCompatible(EventLoop loop) { + return loop instanceof AsyncChildEventLoop; + } + + protected class AsyncUnsafe extends AbstractUnsafe { + + @Override + public void connect(final SocketAddress remoteAddress, + final SocketAddress localAddress, final ChannelFuture future) { + if (eventLoop().inEventLoop()) { + if (!ensureOpen(future)) { + return; + } + + try { + if (connectFuture != null) { + throw new IllegalStateException("connection attempt already made"); + } + connectFuture = future; + + doConnect(remoteAddress, localAddress, future); + + // Schedule connect timeout. + int connectTimeoutMillis = config().getConnectTimeoutMillis(); + if (connectTimeoutMillis > 0) { + connectTimeoutFuture = eventLoop().schedule(new Runnable() { + @Override + public void run() { + if (connectTimeoutException == null) { + connectTimeoutException = new ConnectException("connection timed out"); + } + ChannelFuture connectFuture = AbstractAsyncChannel.this.connectFuture; + if (connectFuture != null && + connectFuture.setFailure(connectTimeoutException)) { + pipeline().fireExceptionCaught(connectTimeoutException); + close(voidFuture()); + } + } + }, connectTimeoutMillis, TimeUnit.MILLISECONDS); + } + + } catch (Throwable t) { + future.setFailure(t); + pipeline().fireExceptionCaught(t); + closeIfClosed(); + } + } else { + eventLoop().execute(new Runnable() { + @Override + public void run() { + connect(remoteAddress, localAddress, future); + } + }); + } + } + + protected final void connectFailed(Throwable t) { + connectFuture.setFailure(t); + pipeline().fireExceptionCaught(t); + closeIfClosed(); + } + + protected final void connectSuccess() { + assert eventLoop().inEventLoop(); + assert connectFuture != null; + try { + boolean wasActive = isActive(); + connectFuture.setSuccess(); + if (!wasActive && isActive()) { + pipeline().fireChannelActive(); + } + } catch (Throwable t) { + connectFuture.setFailure(t); + pipeline().fireExceptionCaught(t); + closeIfClosed(); + } finally { + connectTimeoutFuture.cancel(false); + connectFuture = null; + } + } + } + protected abstract void doConnect(SocketAddress remoteAddress, + SocketAddress localAddress, ChannelFuture connectFuture); + +} diff --git a/transport/src/main/java/io/netty/channel/socket/aio/AsyncChildEventLoop.java b/transport/src/main/java/io/netty/channel/socket/aio/AsyncChildEventLoop.java new file mode 100755 index 0000000000..aad21eb795 --- /dev/null +++ b/transport/src/main/java/io/netty/channel/socket/aio/AsyncChildEventLoop.java @@ -0,0 +1,51 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.channel.socket.aio; + +import io.netty.channel.SingleThreadEventLoop; + +import java.util.concurrent.ThreadFactory; + +final class AsyncChildEventLoop extends SingleThreadEventLoop { + + AsyncChildEventLoop(ThreadFactory threadFactory) { + super(threadFactory); + } + + @Override + protected void run() { + for (;;) { + Runnable task; + try { + task = takeTask(); + task.run(); + } catch (InterruptedException e) { + // Waken up by interruptThread() + } + + if (isShutdown() && peekTask() == null) { + break; + } + } + } + + @Override + protected void wakeup(boolean inEventLoop) { + if (!inEventLoop) { + interruptThread(); + } + } +} diff --git a/transport/src/main/java/io/netty/channel/socket/aio/AsyncEventLoop.java b/transport/src/main/java/io/netty/channel/socket/aio/AsyncEventLoop.java new file mode 100755 index 0000000000..55dbba1f32 --- /dev/null +++ b/transport/src/main/java/io/netty/channel/socket/aio/AsyncEventLoop.java @@ -0,0 +1,41 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.channel.socket.aio; + +import io.netty.channel.EventExecutor; +import io.netty.channel.MultithreadEventLoop; + +import java.util.concurrent.ThreadFactory; + +public class AsyncEventLoop extends MultithreadEventLoop { + + public AsyncEventLoop() { + this(0); + } + + public AsyncEventLoop(int nThreads) { + this(nThreads, null); + } + + public AsyncEventLoop(int nThreads, ThreadFactory threadFactory) { + super(nThreads, threadFactory); + } + + @Override + protected EventExecutor newChild(ThreadFactory threadFactory, Object... args) throws Exception { + return new AsyncChildEventLoop(threadFactory); + } +} diff --git a/transport/src/main/java/io/netty/channel/socket/aio/AsyncServerSocketChannel.java b/transport/src/main/java/io/netty/channel/socket/aio/AsyncServerSocketChannel.java new file mode 100755 index 0000000000..a6ac81bf3a --- /dev/null +++ b/transport/src/main/java/io/netty/channel/socket/aio/AsyncServerSocketChannel.java @@ -0,0 +1,140 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.channel.socket.aio; + +import io.netty.buffer.ChannelBufType; +import io.netty.channel.ChannelException; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ServerChannel; +import io.netty.logging.InternalLogger; +import io.netty.logging.InternalLoggerFactory; + +import java.io.IOException; +import java.net.SocketAddress; +import java.nio.channels.AsynchronousChannelGroup; +import java.nio.channels.AsynchronousServerSocketChannel; +import java.nio.channels.AsynchronousSocketChannel; +import java.nio.channels.CompletionHandler; + +public class AsyncServerSocketChannel extends AbstractAsyncChannel implements ServerChannel { + + private static final AcceptHandler ACCEPT_HANDLER = new AcceptHandler(); + private static final InternalLogger logger = + InternalLoggerFactory.getInstance(AsyncServerSocketChannel.class); + private volatile AsyncServerSocketChannelConfig config; + + public AsyncServerSocketChannel() { + super(null, null); + } + + + @Override + protected AsynchronousServerSocketChannel javaChannel() { + return (AsynchronousServerSocketChannel) super.javaChannel(); + } + + @Override + public boolean isActive() { + AsynchronousServerSocketChannel channel = javaChannel(); + try { + if (channel != null && channel.getLocalAddress() != null) { + return true; + } + } catch (IOException e) { + return true; + } + return false; + } + + @Override + public ChannelBufType bufferType() { + return ChannelBufType.MESSAGE; + } + + @Override + protected SocketAddress localAddress0() { + try { + return javaChannel().getLocalAddress(); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @Override + protected SocketAddress remoteAddress0() { + return null; + } + + @Override + protected void doBind(SocketAddress localAddress) throws Exception { + javaChannel().bind(localAddress); + javaChannel().accept(this, ACCEPT_HANDLER); + + } + + @Override + protected void doClose() throws Exception { + javaChannel().close(); + } + + @Override + protected boolean isFlushPending() { + return false; + } + + @Override + protected void doConnect( + SocketAddress remoteAddress, SocketAddress localAddress, ChannelFuture future) { + future.setFailure(new UnsupportedOperationException()); + } + + @Override + protected void doDisconnect() throws Exception { + throw new UnsupportedOperationException(); + } + + @Override + protected Runnable doRegister() throws Exception { + ch = AsynchronousServerSocketChannel.open(AsynchronousChannelGroup.withThreadPool(eventLoop())); + config = new AsyncServerSocketChannelConfig(javaChannel()); + + return null; + } + + private static final class AcceptHandler + implements CompletionHandler { + public void completed(AsynchronousSocketChannel ch, AsyncServerSocketChannel channel) { + // register again this handler to accept new connections + channel.javaChannel().accept(channel, this); + + // create the socket add it to the buffer and fire the event + channel.pipeline().inboundMessageBuffer().add(new AsyncSocketChannel(channel, null, ch)); + channel.pipeline().fireInboundBufferUpdated(); + } + + public void failed(Throwable t, AsyncServerSocketChannel channel) { + logger.warn("Failed to create a new channel from an accepted socket.", t); + } + } + + @Override + public AsyncServerSocketChannelConfig config() { + if (config == null) { + throw new IllegalStateException("Channel not registered yet"); + } + return config; + } +} diff --git a/transport/src/main/java/io/netty/channel/socket/aio/AsyncServerSocketChannelConfig.java b/transport/src/main/java/io/netty/channel/socket/aio/AsyncServerSocketChannelConfig.java new file mode 100755 index 0000000000..58dfff1219 --- /dev/null +++ b/transport/src/main/java/io/netty/channel/socket/aio/AsyncServerSocketChannelConfig.java @@ -0,0 +1,138 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.channel.socket.aio; + +import static io.netty.channel.ChannelOption.*; +import io.netty.channel.ChannelException; +import io.netty.channel.ChannelOption; +import io.netty.channel.DefaultChannelConfig; +import io.netty.channel.socket.ServerSocketChannelConfig; + +import java.io.IOException; +import java.net.StandardSocketOptions; +import java.nio.channels.AsynchronousServerSocketChannel; +import java.util.Map; + +/** + * The Async {@link ServerSocketChannelConfig} implementation. + */ +public class AsyncServerSocketChannelConfig extends DefaultChannelConfig + implements ServerSocketChannelConfig { + + private final AsynchronousServerSocketChannel channel; + private volatile int backlog; + + /** + * Creates a new instance. + */ + public AsyncServerSocketChannelConfig(AsynchronousServerSocketChannel channel) { + if (channel == null) { + throw new NullPointerException("channel"); + } + this.channel = channel; + } + + @Override + public Map, Object> getOptions() { + return getOptions(super.getOptions(), SO_RCVBUF, SO_REUSEADDR, SO_BACKLOG); + } + + @Override + public T getOption(ChannelOption option) { + if (option == SO_RCVBUF) { + return (T) Integer.valueOf(getReceiveBufferSize()); + } + if (option == SO_REUSEADDR) { + return (T) Boolean.valueOf(isReuseAddress()); + } + if (option == SO_BACKLOG) { + return (T) Integer.valueOf(getBacklog()); + } + + return super.getOption(option); + } + + @Override + public boolean setOption(ChannelOption option, T value) { + validate(option, value); + + if (option == SO_RCVBUF) { + setReceiveBufferSize((Integer) value); + } else if (option == SO_REUSEADDR) { + setReuseAddress((Boolean) value); + } else if (option == SO_BACKLOG) { + setBacklog((Integer) value); + } else { + return super.setOption(option, value); + } + + return true; + } + + @Override + public boolean isReuseAddress() { + try { + return channel.getOption(StandardSocketOptions.SO_REUSEADDR); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @Override + public void setReuseAddress(boolean reuseAddress) { + try { + channel.setOption(StandardSocketOptions.SO_REUSEADDR, reuseAddress); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @Override + public int getReceiveBufferSize() { + try { + return channel.getOption(StandardSocketOptions.SO_RCVBUF); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @Override + public void setReceiveBufferSize(int receiveBufferSize) { + try { + channel.setOption(StandardSocketOptions.SO_RCVBUF, receiveBufferSize); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @Override + public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) { + throw new UnsupportedOperationException(); + } + + @Override + public int getBacklog() { + return backlog; + } + + @Override + public void setBacklog(int backlog) { + if (backlog < 0) { + throw new IllegalArgumentException("backlog: " + backlog); + } + this.backlog = backlog; + } +} diff --git a/transport/src/main/java/io/netty/channel/socket/aio/AsyncSocketChannel.java b/transport/src/main/java/io/netty/channel/socket/aio/AsyncSocketChannel.java new file mode 100755 index 0000000000..f561cd1486 --- /dev/null +++ b/transport/src/main/java/io/netty/channel/socket/aio/AsyncSocketChannel.java @@ -0,0 +1,318 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.channel.socket.aio; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ChannelBufType; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.ChannelStateHandler; +import io.netty.channel.ChannelStateHandlerAdapter; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.AsynchronousChannelGroup; +import java.nio.channels.AsynchronousSocketChannel; +import java.nio.channels.CompletionHandler; +import java.util.concurrent.atomic.AtomicBoolean; + +public class AsyncSocketChannel extends AbstractAsyncChannel { + + private static final CompletionHandler CONNECT_HANDLER = new ConnectHandler(); + private static final CompletionHandler READ_HANDLER = new ReadHandler(); + private static final CompletionHandler WRITE_HANDLER = new WriteHandler(); + private static final ChannelStateHandler READ_START_HANDLER = new ChannelStateHandlerAdapter() { + + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + try { + super.channelActive(ctx); + + // once the channel is active, the first read is scheduled + AsyncSocketChannel.read((AsyncSocketChannel)ctx.channel()); + + } finally { + ctx.pipeline().remove(this); + } + + + } + + }; + private final AtomicBoolean flushing = new AtomicBoolean(false); + private volatile AsyncSocketChannelConfig config; + + public AsyncSocketChannel() { + this(null, null, null); + } + + public AsyncSocketChannel(AsyncServerSocketChannel parent, Integer id, AsynchronousSocketChannel channel) { + super(parent, id); + this.ch = channel; + if (ch != null) { + config = new AsyncSocketChannelConfig(javaChannel()); + pipeline().addLast(READ_START_HANDLER); + } + } + + @Override + public boolean isActive() { + AsynchronousSocketChannel ch = javaChannel(); + return ch.isOpen() && remoteAddress() != null; + } + + @Override + protected AsynchronousSocketChannel javaChannel() { + return (AsynchronousSocketChannel) super.javaChannel(); + } + + @Override + public ChannelBufType bufferType() { + return ChannelBufType.BYTE; + } + + @Override + protected void doConnect(SocketAddress remoteAddress, SocketAddress localAddress, final ChannelFuture future) { + assert ch != null; + if (localAddress != null) { + try { + javaChannel().bind(localAddress); + } catch (IOException e) { + future.setFailure(e); + return; + } + } + + javaChannel().connect(remoteAddress, this, CONNECT_HANDLER); + } + + @Override + protected InetSocketAddress localAddress0() { + try { + return (InetSocketAddress) javaChannel().getLocalAddress(); + } catch (IOException e) { + return null; + } + } + + @Override + protected InetSocketAddress remoteAddress0() { + try { + return (InetSocketAddress) javaChannel().getRemoteAddress(); + } catch (IOException e) { + return null; + } + } + + @Override + protected Runnable doRegister() throws Exception { + if (ch == null) { + ch = AsynchronousSocketChannel.open(AsynchronousChannelGroup.withThreadPool(eventLoop())); + config = new AsyncSocketChannelConfig(javaChannel()); + pipeline().addLast(READ_START_HANDLER); + } + + + return null; + } + + /** + * Trigger a read from the {@link AsyncSocketChannel} + * + */ + private static void read(AsyncSocketChannel channel) { + ByteBuf byteBuf = channel.pipeline().inboundByteBuffer(); + expandReadBuffer(byteBuf); + + // Get a ByteBuffer view on the ByteBuf and clear it before try to read + ByteBuffer buffer = byteBuf.nioBuffer(); + buffer.clear(); + channel.javaChannel().read(buffer, channel, READ_HANDLER); + } + + + private static boolean expandReadBuffer(ByteBuf byteBuf) { + if (!byteBuf.writable()) { + // FIXME: Magic number + byteBuf.ensureWritableBytes(4096); + return true; + } + return false; + } + + @Override + protected void doBind(SocketAddress localAddress) throws Exception { + javaChannel().bind(localAddress); + } + + @Override + protected void doDisconnect() throws Exception { + doClose(); + } + + @Override + protected void doClose() throws Exception { + javaChannel().close(); + } + + @Override + protected boolean isFlushPending() { + return false; + } + + @Override + protected boolean doFlushByteBuffer(ByteBuf buf) throws Exception { + // Only one pending write can be scheduled at one time. Otherwise + // a PendingWriteException will be thrown. So use CAS to not run + // into this + if (flushing.compareAndSet(false, true)) { + ByteBuffer buffer = (ByteBuffer)buf.nioBuffer(); + javaChannel().write(buffer, this, WRITE_HANDLER); + } + return false; + } + + + private static final class WriteHandler implements CompletionHandler { + + @Override + public void completed(Integer result, AsyncSocketChannel channel) { + ByteBuf buf = channel.pipeline().outboundByteBuffer(); + + if (result > 0) { + if (result < buf.readableBytes()) { + // Update the readerIndex with the amount of read bytes + buf.readerIndex(buf.readerIndex() + result); + } else { + // not enough space in the buffer anymore so discard everything that + // was read already + buf.discardReadBytes(); + + } + channel.notifyFlushFutures(); + } + + // Allow to have the next write pending + channel.flushing.set(false); + } + + @Override + public void failed(Throwable cause, AsyncSocketChannel channel) { + ByteBuf buf = channel.pipeline().outboundByteBuffer(); + if (!buf.readable()) { + buf.discardReadBytes(); + } + + channel.notifyFlushFutures(cause); + channel.pipeline().fireExceptionCaught(cause); + if (cause instanceof IOException) { + channel.close(channel.unsafe().voidFuture()); + } + // Allow to have the next write pending + channel.flushing.set(false); + } + } + + private static final class ReadHandler implements CompletionHandler { + + @Override + public void completed(Integer result, AsyncSocketChannel channel) { + assert channel.eventLoop().inEventLoop(); + + final ChannelPipeline pipeline = channel.pipeline(); + boolean closed = false; + boolean read = false; + try { + + int localReadAmount = result.intValue(); + if (localReadAmount > 0) { + //Set the writerIndex of the buffer correctly to the + // current writerIndex + read amount of bytes. + // + // This is needed as the ByteBuffer and the ByteBuf does not share + // each others index + final ByteBuf byteBuf = pipeline.inboundByteBuffer(); + byteBuf.writerIndex(byteBuf.writerIndex() + result); + + read = true; + + } else if (localReadAmount < 0) { + closed = true; + } + + } catch (Throwable t) { + if (read) { + read = false; + pipeline.fireInboundBufferUpdated(); + } + pipeline.fireExceptionCaught(t); + if (t instanceof IOException) { + channel.close(channel.unsafe().voidFuture()); + } + } finally { + if (read) { + pipeline.fireInboundBufferUpdated(); + } + if (closed && channel.isOpen()) { + channel.close(channel.unsafe().voidFuture()); + } else { + // start the next read + AsyncSocketChannel.read(channel); + } + } + } + + @Override + public void failed(Throwable t, AsyncSocketChannel channel) { + channel.pipeline().fireExceptionCaught(t); + if (t instanceof IOException) { + channel.close(channel.unsafe().voidFuture()); + } else { + // start the next read + AsyncSocketChannel.read(channel); + } + } + } + + private static final class ConnectHandler implements CompletionHandler { + + @Override + public void completed(Void result, AsyncSocketChannel channel) { + ((AsyncUnsafe) channel.unsafe()).connectSuccess(); + + // start reading from channel + AsyncSocketChannel.read(channel); + } + + @Override + public void failed(Throwable exc, AsyncSocketChannel channel) { + ((AsyncUnsafe) channel.unsafe()).connectFailed(exc); + } + } + + @Override + public AsyncSocketChannelConfig config() { + if (config == null) { + throw new IllegalStateException("Channel not open yet"); + } + return config; + } + + +} diff --git a/transport/src/main/java/io/netty/channel/socket/aio/AsyncSocketChannelConfig.java b/transport/src/main/java/io/netty/channel/socket/aio/AsyncSocketChannelConfig.java new file mode 100755 index 0000000000..481a606d63 --- /dev/null +++ b/transport/src/main/java/io/netty/channel/socket/aio/AsyncSocketChannelConfig.java @@ -0,0 +1,237 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.channel.socket.aio; + +import static io.netty.channel.ChannelOption.*; +import io.netty.channel.ChannelException; +import io.netty.channel.ChannelOption; +import io.netty.channel.DefaultChannelConfig; +import io.netty.channel.socket.SocketChannelConfig; + +import java.io.IOException; +import java.net.StandardSocketOptions; +import java.nio.channels.NetworkChannel; +import java.util.Map; + +/** + * The default {@link SocketChannelConfig} implementation. + */ +public class AsyncSocketChannelConfig extends DefaultChannelConfig + implements SocketChannelConfig { + + private final NetworkChannel channel; + + /** + * Creates a new instance. + */ + public AsyncSocketChannelConfig(NetworkChannel channel) { + if (channel == null) { + throw new NullPointerException("channel"); + } + this.channel = channel; + } + + @Override + public Map, Object> getOptions() { + return getOptions( + super.getOptions(), + SO_RCVBUF, SO_SNDBUF, TCP_NODELAY, SO_KEEPALIVE, SO_REUSEADDR, SO_LINGER, IP_TOS); + } + + @Override + public T getOption(ChannelOption option) { + if (option == SO_RCVBUF) { + return (T) Integer.valueOf(getReceiveBufferSize()); + } + if (option == SO_SNDBUF) { + return (T) Integer.valueOf(getSendBufferSize()); + } + if (option == TCP_NODELAY) { + return (T) Boolean.valueOf(isTcpNoDelay()); + } + if (option == SO_KEEPALIVE) { + return (T) Boolean.valueOf(isKeepAlive()); + } + if (option == SO_REUSEADDR) { + return (T) Boolean.valueOf(isReuseAddress()); + } + if (option == SO_LINGER) { + return (T) Integer.valueOf(getSoLinger()); + } + if (option == IP_TOS) { + return (T) Integer.valueOf(getTrafficClass()); + } + + return super.getOption(option); + } + + @Override + public boolean setOption(ChannelOption option, T value) { + validate(option, value); + + if (option == SO_RCVBUF) { + setReceiveBufferSize((Integer) value); + } else if (option == SO_SNDBUF) { + setSendBufferSize((Integer) value); + } else if (option == TCP_NODELAY) { + setTcpNoDelay((Boolean) value); + } else if (option == SO_KEEPALIVE) { + setKeepAlive((Boolean) value); + } else if (option == SO_REUSEADDR) { + setReuseAddress((Boolean) value); + } else if (option == SO_LINGER) { + setSoLinger((Integer) value); + } else if (option == IP_TOS) { + setTrafficClass((Integer) value); + } else { + return super.setOption(option, value); + } + + return true; + } + + @Override + public int getReceiveBufferSize() { + try { + return (int) channel.getOption(StandardSocketOptions.SO_RCVBUF); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @Override + public int getSendBufferSize() { + try { + return channel.getOption(StandardSocketOptions.SO_SNDBUF); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @Override + public int getSoLinger() { + try { + return channel.getOption(StandardSocketOptions.SO_LINGER); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @Override + public int getTrafficClass() { + try { + return channel.getOption(StandardSocketOptions.IP_TOS); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @Override + public boolean isKeepAlive() { + try { + return channel.getOption(StandardSocketOptions.SO_KEEPALIVE); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @Override + public boolean isReuseAddress() { + try { + return channel.getOption(StandardSocketOptions.SO_REUSEADDR); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @Override + public boolean isTcpNoDelay() { + try { + return channel.getOption(StandardSocketOptions.SO_REUSEADDR); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @Override + public void setKeepAlive(boolean keepAlive) { + try { + channel.setOption(StandardSocketOptions.SO_KEEPALIVE, keepAlive); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @Override + public void setPerformancePreferences( + int connectionTime, int latency, int bandwidth) { + throw new UnsupportedOperationException(); + } + + @Override + public void setReceiveBufferSize(int receiveBufferSize) { + try { + channel.setOption(StandardSocketOptions.SO_RCVBUF, receiveBufferSize); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @Override + public void setReuseAddress(boolean reuseAddress) { + try { + channel.setOption(StandardSocketOptions.SO_REUSEADDR, reuseAddress); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @Override + public void setSendBufferSize(int sendBufferSize) { + try { + channel.setOption(StandardSocketOptions.SO_SNDBUF, sendBufferSize); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @Override + public void setSoLinger(int soLinger) { + try { + channel.setOption(StandardSocketOptions.SO_LINGER, soLinger); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @Override + public void setTcpNoDelay(boolean tcpNoDelay) { + try { + channel.setOption(StandardSocketOptions.TCP_NODELAY, tcpNoDelay); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @Override + public void setTrafficClass(int trafficClass) { + try { + channel.setOption(StandardSocketOptions.IP_TOS, trafficClass); + } catch (IOException e) { + throw new ChannelException(e); + } + } +} diff --git a/transport/src/main/java/io/netty/channel/socket/aio/package-info.java b/transport/src/main/java/io/netty/channel/socket/aio/package-info.java new file mode 100755 index 0000000000..5d32f31449 --- /dev/null +++ b/transport/src/main/java/io/netty/channel/socket/aio/package-info.java @@ -0,0 +1,21 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +/** + * NIO2-based socket channel + * API implementation - recommended for a large number of connections (>= 1000). + */ +package io.netty.channel.socket.aio; diff --git a/transport/src/test/java/io/netty/channel/AsyncTransportTest.java b/transport/src/test/java/io/netty/channel/AsyncTransportTest.java index e5ea2631d6..791cba81b8 100755 --- a/transport/src/test/java/io/netty/channel/AsyncTransportTest.java +++ b/transport/src/test/java/io/netty/channel/AsyncTransportTest.java @@ -8,9 +8,9 @@ import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundByteHandlerAdapter; import io.netty.channel.ChannelInitializer; -import io.netty.channel.socket.nio2.AsyncEventLoop; -import io.netty.channel.socket.nio2.AsyncServerSocketChannel; -import io.netty.channel.socket.nio2.AsyncSocketChannel; +import io.netty.channel.socket.aio.AsyncEventLoop; +import io.netty.channel.socket.aio.AsyncServerSocketChannel; +import io.netty.channel.socket.aio.AsyncSocketChannel; public class AsyncTransportTest { From 314ac377321e252644d9a3f9beb59d5f9941eff1 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Sat, 16 Jun 2012 21:20:57 +0200 Subject: [PATCH 07/87] Rename classes from Async* -> Aio*. See #396 --- .../socket/aio/AbstractAioChannel.java | 168 +++++++++ .../channel/socket/aio/AioChildEventLoop.java | 51 +++ .../channel/socket/aio/AioEventLoop.java | 41 +++ .../socket/aio/AioServerSocketChannel.java | 140 ++++++++ .../aio/AioServerSocketChannelConfig.java | 138 ++++++++ .../channel/socket/aio/AioSocketChannel.java | 318 ++++++++++++++++++ .../socket/aio/AioSocketChannelConfig.java | 237 +++++++++++++ .../io/netty/channel/AsyncTransportTest.java | 14 +- 8 files changed, 1100 insertions(+), 7 deletions(-) create mode 100755 transport/src/main/java/io/netty/channel/socket/aio/AbstractAioChannel.java create mode 100755 transport/src/main/java/io/netty/channel/socket/aio/AioChildEventLoop.java create mode 100755 transport/src/main/java/io/netty/channel/socket/aio/AioEventLoop.java create mode 100755 transport/src/main/java/io/netty/channel/socket/aio/AioServerSocketChannel.java create mode 100755 transport/src/main/java/io/netty/channel/socket/aio/AioServerSocketChannelConfig.java create mode 100755 transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java create mode 100755 transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannelConfig.java diff --git a/transport/src/main/java/io/netty/channel/socket/aio/AbstractAioChannel.java b/transport/src/main/java/io/netty/channel/socket/aio/AbstractAioChannel.java new file mode 100755 index 0000000000..3fab6e445a --- /dev/null +++ b/transport/src/main/java/io/netty/channel/socket/aio/AbstractAioChannel.java @@ -0,0 +1,168 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.channel.socket.aio; + +import io.netty.channel.AbstractChannel; +import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; +import io.netty.channel.EventLoop; + +import java.net.ConnectException; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.nio.channels.AsynchronousChannel; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; + +public abstract class AbstractAioChannel extends AbstractChannel { + + protected volatile AsynchronousChannel ch; + + /** + * The future of the current connection attempt. If not null, subsequent + * connection attempts will fail. + */ + protected ChannelFuture connectFuture; + protected ScheduledFuture connectTimeoutFuture; + private ConnectException connectTimeoutException; + + protected AbstractAioChannel(Channel parent, Integer id) { + super(parent, id); + } + + + @Override + public InetSocketAddress localAddress() { + if (ch == null) { + return null; + } + return (InetSocketAddress) super.localAddress(); + } + + @Override + public InetSocketAddress remoteAddress() { + if (ch == null) { + return null; + } + return (InetSocketAddress) super.remoteAddress(); + } + + protected AsynchronousChannel javaChannel() { + return ch; + } + + + @Override + public boolean isOpen() { + return ch == null || ch.isOpen(); + } + + @Override + protected void doDeregister() throws Exception { + // NOOP + } + + @Override + protected AsyncUnsafe newUnsafe() { + return new AsyncUnsafe(); + } + + @Override + protected boolean isCompatible(EventLoop loop) { + return loop instanceof AioChildEventLoop; + } + + protected class AsyncUnsafe extends AbstractUnsafe { + + @Override + public void connect(final SocketAddress remoteAddress, + final SocketAddress localAddress, final ChannelFuture future) { + if (eventLoop().inEventLoop()) { + if (!ensureOpen(future)) { + return; + } + + try { + if (connectFuture != null) { + throw new IllegalStateException("connection attempt already made"); + } + connectFuture = future; + + doConnect(remoteAddress, localAddress, future); + + // Schedule connect timeout. + int connectTimeoutMillis = config().getConnectTimeoutMillis(); + if (connectTimeoutMillis > 0) { + connectTimeoutFuture = eventLoop().schedule(new Runnable() { + @Override + public void run() { + if (connectTimeoutException == null) { + connectTimeoutException = new ConnectException("connection timed out"); + } + ChannelFuture connectFuture = AbstractAioChannel.this.connectFuture; + if (connectFuture != null && + connectFuture.setFailure(connectTimeoutException)) { + pipeline().fireExceptionCaught(connectTimeoutException); + close(voidFuture()); + } + } + }, connectTimeoutMillis, TimeUnit.MILLISECONDS); + } + + } catch (Throwable t) { + future.setFailure(t); + pipeline().fireExceptionCaught(t); + closeIfClosed(); + } + } else { + eventLoop().execute(new Runnable() { + @Override + public void run() { + connect(remoteAddress, localAddress, future); + } + }); + } + } + + protected final void connectFailed(Throwable t) { + connectFuture.setFailure(t); + pipeline().fireExceptionCaught(t); + closeIfClosed(); + } + + protected final void connectSuccess() { + assert eventLoop().inEventLoop(); + assert connectFuture != null; + try { + boolean wasActive = isActive(); + connectFuture.setSuccess(); + if (!wasActive && isActive()) { + pipeline().fireChannelActive(); + } + } catch (Throwable t) { + connectFuture.setFailure(t); + pipeline().fireExceptionCaught(t); + closeIfClosed(); + } finally { + connectTimeoutFuture.cancel(false); + connectFuture = null; + } + } + } + protected abstract void doConnect(SocketAddress remoteAddress, + SocketAddress localAddress, ChannelFuture connectFuture); + +} diff --git a/transport/src/main/java/io/netty/channel/socket/aio/AioChildEventLoop.java b/transport/src/main/java/io/netty/channel/socket/aio/AioChildEventLoop.java new file mode 100755 index 0000000000..9b53267910 --- /dev/null +++ b/transport/src/main/java/io/netty/channel/socket/aio/AioChildEventLoop.java @@ -0,0 +1,51 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.channel.socket.aio; + +import io.netty.channel.SingleThreadEventLoop; + +import java.util.concurrent.ThreadFactory; + +final class AioChildEventLoop extends SingleThreadEventLoop { + + AioChildEventLoop(ThreadFactory threadFactory) { + super(threadFactory); + } + + @Override + protected void run() { + for (;;) { + Runnable task; + try { + task = takeTask(); + task.run(); + } catch (InterruptedException e) { + // Waken up by interruptThread() + } + + if (isShutdown() && peekTask() == null) { + break; + } + } + } + + @Override + protected void wakeup(boolean inEventLoop) { + if (!inEventLoop) { + interruptThread(); + } + } +} diff --git a/transport/src/main/java/io/netty/channel/socket/aio/AioEventLoop.java b/transport/src/main/java/io/netty/channel/socket/aio/AioEventLoop.java new file mode 100755 index 0000000000..8d568bbd59 --- /dev/null +++ b/transport/src/main/java/io/netty/channel/socket/aio/AioEventLoop.java @@ -0,0 +1,41 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.channel.socket.aio; + +import io.netty.channel.EventExecutor; +import io.netty.channel.MultithreadEventLoop; + +import java.util.concurrent.ThreadFactory; + +public class AioEventLoop extends MultithreadEventLoop { + + public AioEventLoop() { + this(0); + } + + public AioEventLoop(int nThreads) { + this(nThreads, null); + } + + public AioEventLoop(int nThreads, ThreadFactory threadFactory) { + super(nThreads, threadFactory); + } + + @Override + protected EventExecutor newChild(ThreadFactory threadFactory, Object... args) throws Exception { + return new AioChildEventLoop(threadFactory); + } +} diff --git a/transport/src/main/java/io/netty/channel/socket/aio/AioServerSocketChannel.java b/transport/src/main/java/io/netty/channel/socket/aio/AioServerSocketChannel.java new file mode 100755 index 0000000000..de39783c22 --- /dev/null +++ b/transport/src/main/java/io/netty/channel/socket/aio/AioServerSocketChannel.java @@ -0,0 +1,140 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.channel.socket.aio; + +import io.netty.buffer.ChannelBufType; +import io.netty.channel.ChannelException; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ServerChannel; +import io.netty.logging.InternalLogger; +import io.netty.logging.InternalLoggerFactory; + +import java.io.IOException; +import java.net.SocketAddress; +import java.nio.channels.AsynchronousChannelGroup; +import java.nio.channels.AsynchronousServerSocketChannel; +import java.nio.channels.AsynchronousSocketChannel; +import java.nio.channels.CompletionHandler; + +public class AioServerSocketChannel extends AbstractAioChannel implements ServerChannel { + + private static final AcceptHandler ACCEPT_HANDLER = new AcceptHandler(); + private static final InternalLogger logger = + InternalLoggerFactory.getInstance(AioServerSocketChannel.class); + private volatile AioServerSocketChannelConfig config; + + public AioServerSocketChannel() { + super(null, null); + } + + + @Override + protected AsynchronousServerSocketChannel javaChannel() { + return (AsynchronousServerSocketChannel) super.javaChannel(); + } + + @Override + public boolean isActive() { + AsynchronousServerSocketChannel channel = javaChannel(); + try { + if (channel != null && channel.getLocalAddress() != null) { + return true; + } + } catch (IOException e) { + return true; + } + return false; + } + + @Override + public ChannelBufType bufferType() { + return ChannelBufType.MESSAGE; + } + + @Override + protected SocketAddress localAddress0() { + try { + return javaChannel().getLocalAddress(); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @Override + protected SocketAddress remoteAddress0() { + return null; + } + + @Override + protected void doBind(SocketAddress localAddress) throws Exception { + javaChannel().bind(localAddress); + javaChannel().accept(this, ACCEPT_HANDLER); + + } + + @Override + protected void doClose() throws Exception { + javaChannel().close(); + } + + @Override + protected boolean isFlushPending() { + return false; + } + + @Override + protected void doConnect( + SocketAddress remoteAddress, SocketAddress localAddress, ChannelFuture future) { + future.setFailure(new UnsupportedOperationException()); + } + + @Override + protected void doDisconnect() throws Exception { + throw new UnsupportedOperationException(); + } + + @Override + protected Runnable doRegister() throws Exception { + ch = AsynchronousServerSocketChannel.open(AsynchronousChannelGroup.withThreadPool(eventLoop())); + config = new AioServerSocketChannelConfig(javaChannel()); + + return null; + } + + private static final class AcceptHandler + implements CompletionHandler { + public void completed(AsynchronousSocketChannel ch, AioServerSocketChannel channel) { + // register again this handler to accept new connections + channel.javaChannel().accept(channel, this); + + // create the socket add it to the buffer and fire the event + channel.pipeline().inboundMessageBuffer().add(new AioSocketChannel(channel, null, ch)); + channel.pipeline().fireInboundBufferUpdated(); + } + + public void failed(Throwable t, AioServerSocketChannel channel) { + logger.warn("Failed to create a new channel from an accepted socket.", t); + } + } + + @Override + public AioServerSocketChannelConfig config() { + if (config == null) { + throw new IllegalStateException("Channel not registered yet"); + } + return config; + } +} diff --git a/transport/src/main/java/io/netty/channel/socket/aio/AioServerSocketChannelConfig.java b/transport/src/main/java/io/netty/channel/socket/aio/AioServerSocketChannelConfig.java new file mode 100755 index 0000000000..116b173b23 --- /dev/null +++ b/transport/src/main/java/io/netty/channel/socket/aio/AioServerSocketChannelConfig.java @@ -0,0 +1,138 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.channel.socket.aio; + +import static io.netty.channel.ChannelOption.*; +import io.netty.channel.ChannelException; +import io.netty.channel.ChannelOption; +import io.netty.channel.DefaultChannelConfig; +import io.netty.channel.socket.ServerSocketChannelConfig; + +import java.io.IOException; +import java.net.StandardSocketOptions; +import java.nio.channels.AsynchronousServerSocketChannel; +import java.util.Map; + +/** + * The Async {@link ServerSocketChannelConfig} implementation. + */ +public class AioServerSocketChannelConfig extends DefaultChannelConfig + implements ServerSocketChannelConfig { + + private final AsynchronousServerSocketChannel channel; + private volatile int backlog; + + /** + * Creates a new instance. + */ + public AioServerSocketChannelConfig(AsynchronousServerSocketChannel channel) { + if (channel == null) { + throw new NullPointerException("channel"); + } + this.channel = channel; + } + + @Override + public Map, Object> getOptions() { + return getOptions(super.getOptions(), SO_RCVBUF, SO_REUSEADDR, SO_BACKLOG); + } + + @Override + public T getOption(ChannelOption option) { + if (option == SO_RCVBUF) { + return (T) Integer.valueOf(getReceiveBufferSize()); + } + if (option == SO_REUSEADDR) { + return (T) Boolean.valueOf(isReuseAddress()); + } + if (option == SO_BACKLOG) { + return (T) Integer.valueOf(getBacklog()); + } + + return super.getOption(option); + } + + @Override + public boolean setOption(ChannelOption option, T value) { + validate(option, value); + + if (option == SO_RCVBUF) { + setReceiveBufferSize((Integer) value); + } else if (option == SO_REUSEADDR) { + setReuseAddress((Boolean) value); + } else if (option == SO_BACKLOG) { + setBacklog((Integer) value); + } else { + return super.setOption(option, value); + } + + return true; + } + + @Override + public boolean isReuseAddress() { + try { + return channel.getOption(StandardSocketOptions.SO_REUSEADDR); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @Override + public void setReuseAddress(boolean reuseAddress) { + try { + channel.setOption(StandardSocketOptions.SO_REUSEADDR, reuseAddress); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @Override + public int getReceiveBufferSize() { + try { + return channel.getOption(StandardSocketOptions.SO_RCVBUF); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @Override + public void setReceiveBufferSize(int receiveBufferSize) { + try { + channel.setOption(StandardSocketOptions.SO_RCVBUF, receiveBufferSize); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @Override + public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) { + throw new UnsupportedOperationException(); + } + + @Override + public int getBacklog() { + return backlog; + } + + @Override + public void setBacklog(int backlog) { + if (backlog < 0) { + throw new IllegalArgumentException("backlog: " + backlog); + } + this.backlog = backlog; + } +} diff --git a/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java b/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java new file mode 100755 index 0000000000..c1520febf8 --- /dev/null +++ b/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java @@ -0,0 +1,318 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.channel.socket.aio; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ChannelBufType; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.ChannelStateHandler; +import io.netty.channel.ChannelStateHandlerAdapter; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.AsynchronousChannelGroup; +import java.nio.channels.AsynchronousSocketChannel; +import java.nio.channels.CompletionHandler; +import java.util.concurrent.atomic.AtomicBoolean; + +public class AioSocketChannel extends AbstractAioChannel { + + private static final CompletionHandler CONNECT_HANDLER = new ConnectHandler(); + private static final CompletionHandler READ_HANDLER = new ReadHandler(); + private static final CompletionHandler WRITE_HANDLER = new WriteHandler(); + private static final ChannelStateHandler READ_START_HANDLER = new ChannelStateHandlerAdapter() { + + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { + try { + super.channelActive(ctx); + + // once the channel is active, the first read is scheduled + AioSocketChannel.read((AioSocketChannel)ctx.channel()); + + } finally { + ctx.pipeline().remove(this); + } + + + } + + }; + private final AtomicBoolean flushing = new AtomicBoolean(false); + private volatile AioSocketChannelConfig config; + + public AioSocketChannel() { + this(null, null, null); + } + + public AioSocketChannel(AioServerSocketChannel parent, Integer id, AsynchronousSocketChannel channel) { + super(parent, id); + this.ch = channel; + if (ch != null) { + config = new AioSocketChannelConfig(javaChannel()); + pipeline().addLast(READ_START_HANDLER); + } + } + + @Override + public boolean isActive() { + AsynchronousSocketChannel ch = javaChannel(); + return ch.isOpen() && remoteAddress() != null; + } + + @Override + protected AsynchronousSocketChannel javaChannel() { + return (AsynchronousSocketChannel) super.javaChannel(); + } + + @Override + public ChannelBufType bufferType() { + return ChannelBufType.BYTE; + } + + @Override + protected void doConnect(SocketAddress remoteAddress, SocketAddress localAddress, final ChannelFuture future) { + assert ch != null; + if (localAddress != null) { + try { + javaChannel().bind(localAddress); + } catch (IOException e) { + future.setFailure(e); + return; + } + } + + javaChannel().connect(remoteAddress, this, CONNECT_HANDLER); + } + + @Override + protected InetSocketAddress localAddress0() { + try { + return (InetSocketAddress) javaChannel().getLocalAddress(); + } catch (IOException e) { + return null; + } + } + + @Override + protected InetSocketAddress remoteAddress0() { + try { + return (InetSocketAddress) javaChannel().getRemoteAddress(); + } catch (IOException e) { + return null; + } + } + + @Override + protected Runnable doRegister() throws Exception { + if (ch == null) { + ch = AsynchronousSocketChannel.open(AsynchronousChannelGroup.withThreadPool(eventLoop())); + config = new AioSocketChannelConfig(javaChannel()); + pipeline().addLast(READ_START_HANDLER); + } + + + return null; + } + + /** + * Trigger a read from the {@link AioSocketChannel} + * + */ + private static void read(AioSocketChannel channel) { + ByteBuf byteBuf = channel.pipeline().inboundByteBuffer(); + expandReadBuffer(byteBuf); + + // Get a ByteBuffer view on the ByteBuf and clear it before try to read + ByteBuffer buffer = byteBuf.nioBuffer(); + buffer.clear(); + channel.javaChannel().read(buffer, channel, READ_HANDLER); + } + + + private static boolean expandReadBuffer(ByteBuf byteBuf) { + if (!byteBuf.writable()) { + // FIXME: Magic number + byteBuf.ensureWritableBytes(4096); + return true; + } + return false; + } + + @Override + protected void doBind(SocketAddress localAddress) throws Exception { + javaChannel().bind(localAddress); + } + + @Override + protected void doDisconnect() throws Exception { + doClose(); + } + + @Override + protected void doClose() throws Exception { + javaChannel().close(); + } + + @Override + protected boolean isFlushPending() { + return false; + } + + @Override + protected boolean doFlushByteBuffer(ByteBuf buf) throws Exception { + // Only one pending write can be scheduled at one time. Otherwise + // a PendingWriteException will be thrown. So use CAS to not run + // into this + if (flushing.compareAndSet(false, true)) { + ByteBuffer buffer = (ByteBuffer)buf.nioBuffer(); + javaChannel().write(buffer, this, WRITE_HANDLER); + } + return false; + } + + + private static final class WriteHandler implements CompletionHandler { + + @Override + public void completed(Integer result, AioSocketChannel channel) { + ByteBuf buf = channel.pipeline().outboundByteBuffer(); + + if (result > 0) { + if (result < buf.readableBytes()) { + // Update the readerIndex with the amount of read bytes + buf.readerIndex(buf.readerIndex() + result); + } else { + // not enough space in the buffer anymore so discard everything that + // was read already + buf.discardReadBytes(); + + } + channel.notifyFlushFutures(); + } + + // Allow to have the next write pending + channel.flushing.set(false); + } + + @Override + public void failed(Throwable cause, AioSocketChannel channel) { + ByteBuf buf = channel.pipeline().outboundByteBuffer(); + if (!buf.readable()) { + buf.discardReadBytes(); + } + + channel.notifyFlushFutures(cause); + channel.pipeline().fireExceptionCaught(cause); + if (cause instanceof IOException) { + channel.close(channel.unsafe().voidFuture()); + } + // Allow to have the next write pending + channel.flushing.set(false); + } + } + + private static final class ReadHandler implements CompletionHandler { + + @Override + public void completed(Integer result, AioSocketChannel channel) { + assert channel.eventLoop().inEventLoop(); + + final ChannelPipeline pipeline = channel.pipeline(); + boolean closed = false; + boolean read = false; + try { + + int localReadAmount = result.intValue(); + if (localReadAmount > 0) { + //Set the writerIndex of the buffer correctly to the + // current writerIndex + read amount of bytes. + // + // This is needed as the ByteBuffer and the ByteBuf does not share + // each others index + final ByteBuf byteBuf = pipeline.inboundByteBuffer(); + byteBuf.writerIndex(byteBuf.writerIndex() + result); + + read = true; + + } else if (localReadAmount < 0) { + closed = true; + } + + } catch (Throwable t) { + if (read) { + read = false; + pipeline.fireInboundBufferUpdated(); + } + pipeline.fireExceptionCaught(t); + if (t instanceof IOException) { + channel.close(channel.unsafe().voidFuture()); + } + } finally { + if (read) { + pipeline.fireInboundBufferUpdated(); + } + if (closed && channel.isOpen()) { + channel.close(channel.unsafe().voidFuture()); + } else { + // start the next read + AioSocketChannel.read(channel); + } + } + } + + @Override + public void failed(Throwable t, AioSocketChannel channel) { + channel.pipeline().fireExceptionCaught(t); + if (t instanceof IOException) { + channel.close(channel.unsafe().voidFuture()); + } else { + // start the next read + AioSocketChannel.read(channel); + } + } + } + + private static final class ConnectHandler implements CompletionHandler { + + @Override + public void completed(Void result, AioSocketChannel channel) { + ((AsyncUnsafe) channel.unsafe()).connectSuccess(); + + // start reading from channel + AioSocketChannel.read(channel); + } + + @Override + public void failed(Throwable exc, AioSocketChannel channel) { + ((AsyncUnsafe) channel.unsafe()).connectFailed(exc); + } + } + + @Override + public AioSocketChannelConfig config() { + if (config == null) { + throw new IllegalStateException("Channel not open yet"); + } + return config; + } + + +} diff --git a/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannelConfig.java b/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannelConfig.java new file mode 100755 index 0000000000..0fe1a244d0 --- /dev/null +++ b/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannelConfig.java @@ -0,0 +1,237 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.channel.socket.aio; + +import static io.netty.channel.ChannelOption.*; +import io.netty.channel.ChannelException; +import io.netty.channel.ChannelOption; +import io.netty.channel.DefaultChannelConfig; +import io.netty.channel.socket.SocketChannelConfig; + +import java.io.IOException; +import java.net.StandardSocketOptions; +import java.nio.channels.NetworkChannel; +import java.util.Map; + +/** + * The default {@link SocketChannelConfig} implementation. + */ +public class AioSocketChannelConfig extends DefaultChannelConfig + implements SocketChannelConfig { + + private final NetworkChannel channel; + + /** + * Creates a new instance. + */ + public AioSocketChannelConfig(NetworkChannel channel) { + if (channel == null) { + throw new NullPointerException("channel"); + } + this.channel = channel; + } + + @Override + public Map, Object> getOptions() { + return getOptions( + super.getOptions(), + SO_RCVBUF, SO_SNDBUF, TCP_NODELAY, SO_KEEPALIVE, SO_REUSEADDR, SO_LINGER, IP_TOS); + } + + @Override + public T getOption(ChannelOption option) { + if (option == SO_RCVBUF) { + return (T) Integer.valueOf(getReceiveBufferSize()); + } + if (option == SO_SNDBUF) { + return (T) Integer.valueOf(getSendBufferSize()); + } + if (option == TCP_NODELAY) { + return (T) Boolean.valueOf(isTcpNoDelay()); + } + if (option == SO_KEEPALIVE) { + return (T) Boolean.valueOf(isKeepAlive()); + } + if (option == SO_REUSEADDR) { + return (T) Boolean.valueOf(isReuseAddress()); + } + if (option == SO_LINGER) { + return (T) Integer.valueOf(getSoLinger()); + } + if (option == IP_TOS) { + return (T) Integer.valueOf(getTrafficClass()); + } + + return super.getOption(option); + } + + @Override + public boolean setOption(ChannelOption option, T value) { + validate(option, value); + + if (option == SO_RCVBUF) { + setReceiveBufferSize((Integer) value); + } else if (option == SO_SNDBUF) { + setSendBufferSize((Integer) value); + } else if (option == TCP_NODELAY) { + setTcpNoDelay((Boolean) value); + } else if (option == SO_KEEPALIVE) { + setKeepAlive((Boolean) value); + } else if (option == SO_REUSEADDR) { + setReuseAddress((Boolean) value); + } else if (option == SO_LINGER) { + setSoLinger((Integer) value); + } else if (option == IP_TOS) { + setTrafficClass((Integer) value); + } else { + return super.setOption(option, value); + } + + return true; + } + + @Override + public int getReceiveBufferSize() { + try { + return (int) channel.getOption(StandardSocketOptions.SO_RCVBUF); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @Override + public int getSendBufferSize() { + try { + return channel.getOption(StandardSocketOptions.SO_SNDBUF); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @Override + public int getSoLinger() { + try { + return channel.getOption(StandardSocketOptions.SO_LINGER); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @Override + public int getTrafficClass() { + try { + return channel.getOption(StandardSocketOptions.IP_TOS); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @Override + public boolean isKeepAlive() { + try { + return channel.getOption(StandardSocketOptions.SO_KEEPALIVE); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @Override + public boolean isReuseAddress() { + try { + return channel.getOption(StandardSocketOptions.SO_REUSEADDR); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @Override + public boolean isTcpNoDelay() { + try { + return channel.getOption(StandardSocketOptions.SO_REUSEADDR); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @Override + public void setKeepAlive(boolean keepAlive) { + try { + channel.setOption(StandardSocketOptions.SO_KEEPALIVE, keepAlive); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @Override + public void setPerformancePreferences( + int connectionTime, int latency, int bandwidth) { + throw new UnsupportedOperationException(); + } + + @Override + public void setReceiveBufferSize(int receiveBufferSize) { + try { + channel.setOption(StandardSocketOptions.SO_RCVBUF, receiveBufferSize); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @Override + public void setReuseAddress(boolean reuseAddress) { + try { + channel.setOption(StandardSocketOptions.SO_REUSEADDR, reuseAddress); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @Override + public void setSendBufferSize(int sendBufferSize) { + try { + channel.setOption(StandardSocketOptions.SO_SNDBUF, sendBufferSize); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @Override + public void setSoLinger(int soLinger) { + try { + channel.setOption(StandardSocketOptions.SO_LINGER, soLinger); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @Override + public void setTcpNoDelay(boolean tcpNoDelay) { + try { + channel.setOption(StandardSocketOptions.TCP_NODELAY, tcpNoDelay); + } catch (IOException e) { + throw new ChannelException(e); + } + } + + @Override + public void setTrafficClass(int trafficClass) { + try { + channel.setOption(StandardSocketOptions.IP_TOS, trafficClass); + } catch (IOException e) { + throw new ChannelException(e); + } + } +} diff --git a/transport/src/test/java/io/netty/channel/AsyncTransportTest.java b/transport/src/test/java/io/netty/channel/AsyncTransportTest.java index 791cba81b8..2f6eeb7318 100755 --- a/transport/src/test/java/io/netty/channel/AsyncTransportTest.java +++ b/transport/src/test/java/io/netty/channel/AsyncTransportTest.java @@ -8,22 +8,22 @@ import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundByteHandlerAdapter; import io.netty.channel.ChannelInitializer; -import io.netty.channel.socket.aio.AsyncEventLoop; -import io.netty.channel.socket.aio.AsyncServerSocketChannel; -import io.netty.channel.socket.aio.AsyncSocketChannel; +import io.netty.channel.socket.aio.AioEventLoop; +import io.netty.channel.socket.aio.AioServerSocketChannel; +import io.netty.channel.socket.aio.AioSocketChannel; public class AsyncTransportTest { public static void main(String args[]) { - AsyncEventLoop loop = new AsyncEventLoop(); + AioEventLoop loop = new AioEventLoop(); // Configure a test server ServerBootstrap sb = new ServerBootstrap(); sb.eventLoop(loop, loop) - .channel(new AsyncServerSocketChannel()) + .channel(new AioServerSocketChannel()) .localAddress(new InetSocketAddress(9191)) - .childHandler(new ChannelInitializer() { + .childHandler(new ChannelInitializer() { @Override - public void initChannel(AsyncSocketChannel ch) throws Exception { + public void initChannel(AioSocketChannel ch) throws Exception { ch.pipeline().addLast(new ChannelInboundByteHandlerAdapter() { @Override From 70c4f59c452fdc3de0fc8f10ef94f0f00139e1d4 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Sat, 16 Jun 2012 22:29:25 +0200 Subject: [PATCH 08/87] Some clean. See #396 --- .../channel/socket/aio/AioSocketChannel.java | 32 +++++++++++++------ 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java b/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java index c1520febf8..3d25529e60 100755 --- a/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java @@ -45,7 +45,7 @@ public class AioSocketChannel extends AbstractAioChannel { super.channelActive(ctx); // once the channel is active, the first read is scheduled - AioSocketChannel.read((AioSocketChannel)ctx.channel()); + ((AioSocketChannel)ctx.channel()).read(); } finally { ctx.pipeline().remove(this); @@ -136,14 +136,14 @@ public class AioSocketChannel extends AbstractAioChannel { * Trigger a read from the {@link AioSocketChannel} * */ - private static void read(AioSocketChannel channel) { - ByteBuf byteBuf = channel.pipeline().inboundByteBuffer(); + void read() { + ByteBuf byteBuf = pipeline().inboundByteBuffer(); expandReadBuffer(byteBuf); // Get a ByteBuffer view on the ByteBuf and clear it before try to read ByteBuffer buffer = byteBuf.nioBuffer(); buffer.clear(); - channel.javaChannel().read(buffer, channel, READ_HANDLER); + javaChannel().read(buffer, this, READ_HANDLER); } @@ -178,6 +178,12 @@ public class AioSocketChannel extends AbstractAioChannel { @Override protected boolean doFlushByteBuffer(ByteBuf buf) throws Exception { + if (!buf.readable()) { + // Reset reader/writerIndex to 0 if the buffer is empty. + buf.clear(); + return true; + } + // Only one pending write can be scheduled at one time. Otherwise // a PendingWriteException will be thrown. So use CAS to not run // into this @@ -200,9 +206,15 @@ public class AioSocketChannel extends AbstractAioChannel { // Update the readerIndex with the amount of read bytes buf.readerIndex(buf.readerIndex() + result); } else { - // not enough space in the buffer anymore so discard everything that - // was read already - buf.discardReadBytes(); + if (!buf.readable()) { + // Reset reader/writerIndex to 0 if the buffer is empty. + buf.clear(); + } else { + // not enough space in the buffer anymore so discard everything that + // was read already + buf.discardReadBytes(); + } + } channel.notifyFlushFutures(); @@ -273,7 +285,7 @@ public class AioSocketChannel extends AbstractAioChannel { channel.close(channel.unsafe().voidFuture()); } else { // start the next read - AioSocketChannel.read(channel); + channel.read(); } } } @@ -285,7 +297,7 @@ public class AioSocketChannel extends AbstractAioChannel { channel.close(channel.unsafe().voidFuture()); } else { // start the next read - AioSocketChannel.read(channel); + channel.read(); } } } @@ -297,7 +309,7 @@ public class AioSocketChannel extends AbstractAioChannel { ((AsyncUnsafe) channel.unsafe()).connectSuccess(); // start reading from channel - AioSocketChannel.read(channel); + channel.read(); } @Override From 5d1e710adcc79ef86804ac065338d0900d544a76 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Sat, 16 Jun 2012 22:31:43 +0200 Subject: [PATCH 09/87] Package was renamed. See #396 --- .../socket/nio2/AbstractAsyncChannel.java | 168 --------- .../socket/nio2/AsyncChildEventLoop.java | 51 --- .../channel/socket/nio2/AsyncEventLoop.java | 41 --- .../socket/nio2/AsyncServerSocketChannel.java | 140 -------- .../nio2/AsyncServerSocketChannelConfig.java | 138 -------- .../socket/nio2/AsyncSocketChannelConfig.java | 237 ------------- .../socket/nio2/AsyncSocketchannel.java | 318 ------------------ .../channel/socket/nio2/package-info.java | 21 -- 8 files changed, 1114 deletions(-) delete mode 100755 transport/src/main/java/io/netty/channel/socket/nio2/AbstractAsyncChannel.java delete mode 100755 transport/src/main/java/io/netty/channel/socket/nio2/AsyncChildEventLoop.java delete mode 100755 transport/src/main/java/io/netty/channel/socket/nio2/AsyncEventLoop.java delete mode 100755 transport/src/main/java/io/netty/channel/socket/nio2/AsyncServerSocketChannel.java delete mode 100755 transport/src/main/java/io/netty/channel/socket/nio2/AsyncServerSocketChannelConfig.java delete mode 100755 transport/src/main/java/io/netty/channel/socket/nio2/AsyncSocketChannelConfig.java delete mode 100755 transport/src/main/java/io/netty/channel/socket/nio2/AsyncSocketchannel.java delete mode 100755 transport/src/main/java/io/netty/channel/socket/nio2/package-info.java diff --git a/transport/src/main/java/io/netty/channel/socket/nio2/AbstractAsyncChannel.java b/transport/src/main/java/io/netty/channel/socket/nio2/AbstractAsyncChannel.java deleted file mode 100755 index 7ac6884a85..0000000000 --- a/transport/src/main/java/io/netty/channel/socket/nio2/AbstractAsyncChannel.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.channel.socket.nio2; - -import io.netty.channel.AbstractChannel; -import io.netty.channel.Channel; -import io.netty.channel.ChannelFuture; -import io.netty.channel.EventLoop; - -import java.net.ConnectException; -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.nio.channels.AsynchronousChannel; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; - -public abstract class AbstractAsyncChannel extends AbstractChannel { - - protected volatile AsynchronousChannel ch; - - /** - * The future of the current connection attempt. If not null, subsequent - * connection attempts will fail. - */ - protected ChannelFuture connectFuture; - protected ScheduledFuture connectTimeoutFuture; - private ConnectException connectTimeoutException; - - protected AbstractAsyncChannel(Channel parent, Integer id) { - super(parent, id); - } - - - @Override - public InetSocketAddress localAddress() { - if (ch == null) { - return null; - } - return (InetSocketAddress) super.localAddress(); - } - - @Override - public InetSocketAddress remoteAddress() { - if (ch == null) { - return null; - } - return (InetSocketAddress) super.remoteAddress(); - } - - protected AsynchronousChannel javaChannel() { - return ch; - } - - - @Override - public boolean isOpen() { - return ch == null || ch.isOpen(); - } - - @Override - protected void doDeregister() throws Exception { - // NOOP - } - - @Override - protected AsyncUnsafe newUnsafe() { - return new AsyncUnsafe(); - } - - @Override - protected boolean isCompatible(EventLoop loop) { - return loop instanceof AsyncChildEventLoop; - } - - protected class AsyncUnsafe extends AbstractUnsafe { - - @Override - public void connect(final SocketAddress remoteAddress, - final SocketAddress localAddress, final ChannelFuture future) { - if (eventLoop().inEventLoop()) { - if (!ensureOpen(future)) { - return; - } - - try { - if (connectFuture != null) { - throw new IllegalStateException("connection attempt already made"); - } - connectFuture = future; - - doConnect(remoteAddress, localAddress, future); - - // Schedule connect timeout. - int connectTimeoutMillis = config().getConnectTimeoutMillis(); - if (connectTimeoutMillis > 0) { - connectTimeoutFuture = eventLoop().schedule(new Runnable() { - @Override - public void run() { - if (connectTimeoutException == null) { - connectTimeoutException = new ConnectException("connection timed out"); - } - ChannelFuture connectFuture = AbstractAsyncChannel.this.connectFuture; - if (connectFuture != null && - connectFuture.setFailure(connectTimeoutException)) { - pipeline().fireExceptionCaught(connectTimeoutException); - close(voidFuture()); - } - } - }, connectTimeoutMillis, TimeUnit.MILLISECONDS); - } - - } catch (Throwable t) { - future.setFailure(t); - pipeline().fireExceptionCaught(t); - closeIfClosed(); - } - } else { - eventLoop().execute(new Runnable() { - @Override - public void run() { - connect(remoteAddress, localAddress, future); - } - }); - } - } - - protected final void connectFailed(Throwable t) { - connectFuture.setFailure(t); - pipeline().fireExceptionCaught(t); - closeIfClosed(); - } - - protected final void connectSuccess() { - assert eventLoop().inEventLoop(); - assert connectFuture != null; - try { - boolean wasActive = isActive(); - connectFuture.setSuccess(); - if (!wasActive && isActive()) { - pipeline().fireChannelActive(); - } - } catch (Throwable t) { - connectFuture.setFailure(t); - pipeline().fireExceptionCaught(t); - closeIfClosed(); - } finally { - connectTimeoutFuture.cancel(false); - connectFuture = null; - } - } - } - protected abstract void doConnect(SocketAddress remoteAddress, - SocketAddress localAddress, ChannelFuture connectFuture); - -} diff --git a/transport/src/main/java/io/netty/channel/socket/nio2/AsyncChildEventLoop.java b/transport/src/main/java/io/netty/channel/socket/nio2/AsyncChildEventLoop.java deleted file mode 100755 index 779f075dd3..0000000000 --- a/transport/src/main/java/io/netty/channel/socket/nio2/AsyncChildEventLoop.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.channel.socket.nio2; - -import io.netty.channel.SingleThreadEventLoop; - -import java.util.concurrent.ThreadFactory; - -final class AsyncChildEventLoop extends SingleThreadEventLoop { - - AsyncChildEventLoop(ThreadFactory threadFactory) { - super(threadFactory); - } - - @Override - protected void run() { - for (;;) { - Runnable task; - try { - task = takeTask(); - task.run(); - } catch (InterruptedException e) { - // Waken up by interruptThread() - } - - if (isShutdown() && peekTask() == null) { - break; - } - } - } - - @Override - protected void wakeup(boolean inEventLoop) { - if (!inEventLoop) { - interruptThread(); - } - } -} diff --git a/transport/src/main/java/io/netty/channel/socket/nio2/AsyncEventLoop.java b/transport/src/main/java/io/netty/channel/socket/nio2/AsyncEventLoop.java deleted file mode 100755 index 8c4ecd40b5..0000000000 --- a/transport/src/main/java/io/netty/channel/socket/nio2/AsyncEventLoop.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.channel.socket.nio2; - -import io.netty.channel.EventExecutor; -import io.netty.channel.MultithreadEventLoop; - -import java.util.concurrent.ThreadFactory; - -public class AsyncEventLoop extends MultithreadEventLoop { - - public AsyncEventLoop() { - this(0); - } - - public AsyncEventLoop(int nThreads) { - this(nThreads, null); - } - - public AsyncEventLoop(int nThreads, ThreadFactory threadFactory) { - super(nThreads, threadFactory); - } - - @Override - protected EventExecutor newChild(ThreadFactory threadFactory, Object... args) throws Exception { - return new AsyncChildEventLoop(threadFactory); - } -} diff --git a/transport/src/main/java/io/netty/channel/socket/nio2/AsyncServerSocketChannel.java b/transport/src/main/java/io/netty/channel/socket/nio2/AsyncServerSocketChannel.java deleted file mode 100755 index 74a804a52a..0000000000 --- a/transport/src/main/java/io/netty/channel/socket/nio2/AsyncServerSocketChannel.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.channel.socket.nio2; - -import io.netty.buffer.ChannelBufType; -import io.netty.channel.ChannelException; -import io.netty.channel.ChannelFuture; -import io.netty.channel.ServerChannel; -import io.netty.logging.InternalLogger; -import io.netty.logging.InternalLoggerFactory; - -import java.io.IOException; -import java.net.SocketAddress; -import java.nio.channels.AsynchronousChannelGroup; -import java.nio.channels.AsynchronousServerSocketChannel; -import java.nio.channels.AsynchronousSocketChannel; -import java.nio.channels.CompletionHandler; - -public class AsyncServerSocketChannel extends AbstractAsyncChannel implements ServerChannel { - - private static final AcceptHandler ACCEPT_HANDLER = new AcceptHandler(); - private static final InternalLogger logger = - InternalLoggerFactory.getInstance(AsyncServerSocketChannel.class); - private volatile AsyncServerSocketChannelConfig config; - - public AsyncServerSocketChannel() { - super(null, null); - } - - - @Override - protected AsynchronousServerSocketChannel javaChannel() { - return (AsynchronousServerSocketChannel) super.javaChannel(); - } - - @Override - public boolean isActive() { - AsynchronousServerSocketChannel channel = javaChannel(); - try { - if (channel != null && channel.getLocalAddress() != null) { - return true; - } - } catch (IOException e) { - return true; - } - return false; - } - - @Override - public ChannelBufType bufferType() { - return ChannelBufType.MESSAGE; - } - - @Override - protected SocketAddress localAddress0() { - try { - return javaChannel().getLocalAddress(); - } catch (IOException e) { - throw new ChannelException(e); - } - } - - @Override - protected SocketAddress remoteAddress0() { - return null; - } - - @Override - protected void doBind(SocketAddress localAddress) throws Exception { - javaChannel().bind(localAddress); - javaChannel().accept(this, ACCEPT_HANDLER); - - } - - @Override - protected void doClose() throws Exception { - javaChannel().close(); - } - - @Override - protected boolean isFlushPending() { - return false; - } - - @Override - protected void doConnect( - SocketAddress remoteAddress, SocketAddress localAddress, ChannelFuture future) { - future.setFailure(new UnsupportedOperationException()); - } - - @Override - protected void doDisconnect() throws Exception { - throw new UnsupportedOperationException(); - } - - @Override - protected Runnable doRegister() throws Exception { - ch = AsynchronousServerSocketChannel.open(AsynchronousChannelGroup.withThreadPool(eventLoop())); - config = new AsyncServerSocketChannelConfig(javaChannel()); - - return null; - } - - private static final class AcceptHandler - implements CompletionHandler { - public void completed(AsynchronousSocketChannel ch, AsyncServerSocketChannel channel) { - // register again this handler to accept new connections - channel.javaChannel().accept(channel, this); - - // create the socket add it to the buffer and fire the event - channel.pipeline().inboundMessageBuffer().add(new AsyncSocketChannel(channel, null, ch)); - channel.pipeline().fireInboundBufferUpdated(); - } - - public void failed(Throwable t, AsyncServerSocketChannel channel) { - logger.warn("Failed to create a new channel from an accepted socket.", t); - } - } - - @Override - public AsyncServerSocketChannelConfig config() { - if (config == null) { - throw new IllegalStateException("Channel not registered yet"); - } - return config; - } -} diff --git a/transport/src/main/java/io/netty/channel/socket/nio2/AsyncServerSocketChannelConfig.java b/transport/src/main/java/io/netty/channel/socket/nio2/AsyncServerSocketChannelConfig.java deleted file mode 100755 index 55f62b51ce..0000000000 --- a/transport/src/main/java/io/netty/channel/socket/nio2/AsyncServerSocketChannelConfig.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.channel.socket.nio2; - -import static io.netty.channel.ChannelOption.*; -import io.netty.channel.ChannelException; -import io.netty.channel.ChannelOption; -import io.netty.channel.DefaultChannelConfig; -import io.netty.channel.socket.ServerSocketChannelConfig; - -import java.io.IOException; -import java.net.StandardSocketOptions; -import java.nio.channels.AsynchronousServerSocketChannel; -import java.util.Map; - -/** - * The Async {@link ServerSocketChannelConfig} implementation. - */ -public class AsyncServerSocketChannelConfig extends DefaultChannelConfig - implements ServerSocketChannelConfig { - - private final AsynchronousServerSocketChannel channel; - private volatile int backlog; - - /** - * Creates a new instance. - */ - public AsyncServerSocketChannelConfig(AsynchronousServerSocketChannel channel) { - if (channel == null) { - throw new NullPointerException("channel"); - } - this.channel = channel; - } - - @Override - public Map, Object> getOptions() { - return getOptions(super.getOptions(), SO_RCVBUF, SO_REUSEADDR, SO_BACKLOG); - } - - @Override - public T getOption(ChannelOption option) { - if (option == SO_RCVBUF) { - return (T) Integer.valueOf(getReceiveBufferSize()); - } - if (option == SO_REUSEADDR) { - return (T) Boolean.valueOf(isReuseAddress()); - } - if (option == SO_BACKLOG) { - return (T) Integer.valueOf(getBacklog()); - } - - return super.getOption(option); - } - - @Override - public boolean setOption(ChannelOption option, T value) { - validate(option, value); - - if (option == SO_RCVBUF) { - setReceiveBufferSize((Integer) value); - } else if (option == SO_REUSEADDR) { - setReuseAddress((Boolean) value); - } else if (option == SO_BACKLOG) { - setBacklog((Integer) value); - } else { - return super.setOption(option, value); - } - - return true; - } - - @Override - public boolean isReuseAddress() { - try { - return channel.getOption(StandardSocketOptions.SO_REUSEADDR); - } catch (IOException e) { - throw new ChannelException(e); - } - } - - @Override - public void setReuseAddress(boolean reuseAddress) { - try { - channel.setOption(StandardSocketOptions.SO_REUSEADDR, reuseAddress); - } catch (IOException e) { - throw new ChannelException(e); - } - } - - @Override - public int getReceiveBufferSize() { - try { - return channel.getOption(StandardSocketOptions.SO_RCVBUF); - } catch (IOException e) { - throw new ChannelException(e); - } - } - - @Override - public void setReceiveBufferSize(int receiveBufferSize) { - try { - channel.setOption(StandardSocketOptions.SO_RCVBUF, receiveBufferSize); - } catch (IOException e) { - throw new ChannelException(e); - } - } - - @Override - public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) { - throw new UnsupportedOperationException(); - } - - @Override - public int getBacklog() { - return backlog; - } - - @Override - public void setBacklog(int backlog) { - if (backlog < 0) { - throw new IllegalArgumentException("backlog: " + backlog); - } - this.backlog = backlog; - } -} diff --git a/transport/src/main/java/io/netty/channel/socket/nio2/AsyncSocketChannelConfig.java b/transport/src/main/java/io/netty/channel/socket/nio2/AsyncSocketChannelConfig.java deleted file mode 100755 index d89b61acf0..0000000000 --- a/transport/src/main/java/io/netty/channel/socket/nio2/AsyncSocketChannelConfig.java +++ /dev/null @@ -1,237 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.channel.socket.nio2; - -import static io.netty.channel.ChannelOption.*; -import io.netty.channel.ChannelException; -import io.netty.channel.ChannelOption; -import io.netty.channel.DefaultChannelConfig; -import io.netty.channel.socket.SocketChannelConfig; - -import java.io.IOException; -import java.net.StandardSocketOptions; -import java.nio.channels.NetworkChannel; -import java.util.Map; - -/** - * The default {@link SocketChannelConfig} implementation. - */ -public class AsyncSocketChannelConfig extends DefaultChannelConfig - implements SocketChannelConfig { - - private final NetworkChannel channel; - - /** - * Creates a new instance. - */ - public AsyncSocketChannelConfig(NetworkChannel channel) { - if (channel == null) { - throw new NullPointerException("channel"); - } - this.channel = channel; - } - - @Override - public Map, Object> getOptions() { - return getOptions( - super.getOptions(), - SO_RCVBUF, SO_SNDBUF, TCP_NODELAY, SO_KEEPALIVE, SO_REUSEADDR, SO_LINGER, IP_TOS); - } - - @Override - public T getOption(ChannelOption option) { - if (option == SO_RCVBUF) { - return (T) Integer.valueOf(getReceiveBufferSize()); - } - if (option == SO_SNDBUF) { - return (T) Integer.valueOf(getSendBufferSize()); - } - if (option == TCP_NODELAY) { - return (T) Boolean.valueOf(isTcpNoDelay()); - } - if (option == SO_KEEPALIVE) { - return (T) Boolean.valueOf(isKeepAlive()); - } - if (option == SO_REUSEADDR) { - return (T) Boolean.valueOf(isReuseAddress()); - } - if (option == SO_LINGER) { - return (T) Integer.valueOf(getSoLinger()); - } - if (option == IP_TOS) { - return (T) Integer.valueOf(getTrafficClass()); - } - - return super.getOption(option); - } - - @Override - public boolean setOption(ChannelOption option, T value) { - validate(option, value); - - if (option == SO_RCVBUF) { - setReceiveBufferSize((Integer) value); - } else if (option == SO_SNDBUF) { - setSendBufferSize((Integer) value); - } else if (option == TCP_NODELAY) { - setTcpNoDelay((Boolean) value); - } else if (option == SO_KEEPALIVE) { - setKeepAlive((Boolean) value); - } else if (option == SO_REUSEADDR) { - setReuseAddress((Boolean) value); - } else if (option == SO_LINGER) { - setSoLinger((Integer) value); - } else if (option == IP_TOS) { - setTrafficClass((Integer) value); - } else { - return super.setOption(option, value); - } - - return true; - } - - @Override - public int getReceiveBufferSize() { - try { - return (int) channel.getOption(StandardSocketOptions.SO_RCVBUF); - } catch (IOException e) { - throw new ChannelException(e); - } - } - - @Override - public int getSendBufferSize() { - try { - return channel.getOption(StandardSocketOptions.SO_SNDBUF); - } catch (IOException e) { - throw new ChannelException(e); - } - } - - @Override - public int getSoLinger() { - try { - return channel.getOption(StandardSocketOptions.SO_LINGER); - } catch (IOException e) { - throw new ChannelException(e); - } - } - - @Override - public int getTrafficClass() { - try { - return channel.getOption(StandardSocketOptions.IP_TOS); - } catch (IOException e) { - throw new ChannelException(e); - } - } - - @Override - public boolean isKeepAlive() { - try { - return channel.getOption(StandardSocketOptions.SO_KEEPALIVE); - } catch (IOException e) { - throw new ChannelException(e); - } - } - - @Override - public boolean isReuseAddress() { - try { - return channel.getOption(StandardSocketOptions.SO_REUSEADDR); - } catch (IOException e) { - throw new ChannelException(e); - } - } - - @Override - public boolean isTcpNoDelay() { - try { - return channel.getOption(StandardSocketOptions.SO_REUSEADDR); - } catch (IOException e) { - throw new ChannelException(e); - } - } - - @Override - public void setKeepAlive(boolean keepAlive) { - try { - channel.setOption(StandardSocketOptions.SO_KEEPALIVE, keepAlive); - } catch (IOException e) { - throw new ChannelException(e); - } - } - - @Override - public void setPerformancePreferences( - int connectionTime, int latency, int bandwidth) { - throw new UnsupportedOperationException(); - } - - @Override - public void setReceiveBufferSize(int receiveBufferSize) { - try { - channel.setOption(StandardSocketOptions.SO_RCVBUF, receiveBufferSize); - } catch (IOException e) { - throw new ChannelException(e); - } - } - - @Override - public void setReuseAddress(boolean reuseAddress) { - try { - channel.setOption(StandardSocketOptions.SO_REUSEADDR, reuseAddress); - } catch (IOException e) { - throw new ChannelException(e); - } - } - - @Override - public void setSendBufferSize(int sendBufferSize) { - try { - channel.setOption(StandardSocketOptions.SO_SNDBUF, sendBufferSize); - } catch (IOException e) { - throw new ChannelException(e); - } - } - - @Override - public void setSoLinger(int soLinger) { - try { - channel.setOption(StandardSocketOptions.SO_LINGER, soLinger); - } catch (IOException e) { - throw new ChannelException(e); - } - } - - @Override - public void setTcpNoDelay(boolean tcpNoDelay) { - try { - channel.setOption(StandardSocketOptions.TCP_NODELAY, tcpNoDelay); - } catch (IOException e) { - throw new ChannelException(e); - } - } - - @Override - public void setTrafficClass(int trafficClass) { - try { - channel.setOption(StandardSocketOptions.IP_TOS, trafficClass); - } catch (IOException e) { - throw new ChannelException(e); - } - } -} diff --git a/transport/src/main/java/io/netty/channel/socket/nio2/AsyncSocketchannel.java b/transport/src/main/java/io/netty/channel/socket/nio2/AsyncSocketchannel.java deleted file mode 100755 index 0308eaf9e4..0000000000 --- a/transport/src/main/java/io/netty/channel/socket/nio2/AsyncSocketchannel.java +++ /dev/null @@ -1,318 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.channel.socket.nio2; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ChannelBufType; -import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.ChannelStateHandler; -import io.netty.channel.ChannelStateHandlerAdapter; - -import java.io.IOException; -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.nio.ByteBuffer; -import java.nio.channels.AsynchronousChannelGroup; -import java.nio.channels.AsynchronousSocketChannel; -import java.nio.channels.CompletionHandler; -import java.util.concurrent.atomic.AtomicBoolean; - -public class AsyncSocketChannel extends AbstractAsyncChannel { - - private static final CompletionHandler CONNECT_HANDLER = new ConnectHandler(); - private static final CompletionHandler READ_HANDLER = new ReadHandler(); - private static final CompletionHandler WRITE_HANDLER = new WriteHandler(); - private static final ChannelStateHandler READ_START_HANDLER = new ChannelStateHandlerAdapter() { - - @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { - try { - super.channelActive(ctx); - - // once the channel is active, the first read is scheduled - AsyncSocketChannel.read((AsyncSocketChannel)ctx.channel()); - - } finally { - ctx.pipeline().remove(this); - } - - - } - - }; - private final AtomicBoolean flushing = new AtomicBoolean(false); - private volatile AsyncSocketChannelConfig config; - - public AsyncSocketChannel() { - this(null, null, null); - } - - public AsyncSocketChannel(AsyncServerSocketChannel parent, Integer id, AsynchronousSocketChannel channel) { - super(parent, id); - this.ch = channel; - if (ch != null) { - config = new AsyncSocketChannelConfig(javaChannel()); - pipeline().addLast(READ_START_HANDLER); - } - } - - @Override - public boolean isActive() { - AsynchronousSocketChannel ch = javaChannel(); - return ch.isOpen() && remoteAddress() != null; - } - - @Override - protected AsynchronousSocketChannel javaChannel() { - return (AsynchronousSocketChannel) super.javaChannel(); - } - - @Override - public ChannelBufType bufferType() { - return ChannelBufType.BYTE; - } - - @Override - protected void doConnect(SocketAddress remoteAddress, SocketAddress localAddress, final ChannelFuture future) { - assert ch != null; - if (localAddress != null) { - try { - javaChannel().bind(localAddress); - } catch (IOException e) { - future.setFailure(e); - return; - } - } - - javaChannel().connect(remoteAddress, this, CONNECT_HANDLER); - } - - @Override - protected InetSocketAddress localAddress0() { - try { - return (InetSocketAddress) javaChannel().getLocalAddress(); - } catch (IOException e) { - return null; - } - } - - @Override - protected InetSocketAddress remoteAddress0() { - try { - return (InetSocketAddress) javaChannel().getRemoteAddress(); - } catch (IOException e) { - return null; - } - } - - @Override - protected Runnable doRegister() throws Exception { - if (ch == null) { - ch = AsynchronousSocketChannel.open(AsynchronousChannelGroup.withThreadPool(eventLoop())); - config = new AsyncSocketChannelConfig(javaChannel()); - pipeline().addLast(READ_START_HANDLER); - } - - - return null; - } - - /** - * Trigger a read from the {@link AsyncSocketChannel} - * - */ - private static void read(AsyncSocketChannel channel) { - ByteBuf byteBuf = channel.pipeline().inboundByteBuffer(); - expandReadBuffer(byteBuf); - - // Get a ByteBuffer view on the ByteBuf and clear it before try to read - ByteBuffer buffer = byteBuf.nioBuffer(); - buffer.clear(); - channel.javaChannel().read(buffer, channel, READ_HANDLER); - } - - - private static boolean expandReadBuffer(ByteBuf byteBuf) { - if (!byteBuf.writable()) { - // FIXME: Magic number - byteBuf.ensureWritableBytes(4096); - return true; - } - return false; - } - - @Override - protected void doBind(SocketAddress localAddress) throws Exception { - javaChannel().bind(localAddress); - } - - @Override - protected void doDisconnect() throws Exception { - doClose(); - } - - @Override - protected void doClose() throws Exception { - javaChannel().close(); - } - - @Override - protected boolean isFlushPending() { - return false; - } - - @Override - protected boolean doFlushByteBuffer(ByteBuf buf) throws Exception { - // Only one pending write can be scheduled at one time. Otherwise - // a PendingWriteException will be thrown. So use CAS to not run - // into this - if (flushing.compareAndSet(false, true)) { - ByteBuffer buffer = (ByteBuffer)buf.nioBuffer(); - javaChannel().write(buffer, this, WRITE_HANDLER); - } - return false; - } - - - private static final class WriteHandler implements CompletionHandler { - - @Override - public void completed(Integer result, AsyncSocketChannel channel) { - ByteBuf buf = channel.pipeline().outboundByteBuffer(); - - if (result > 0) { - if (result < buf.readableBytes()) { - // Update the readerIndex with the amount of read bytes - buf.readerIndex(buf.readerIndex() + result); - } else { - // not enough space in the buffer anymore so discard everything that - // was read already - buf.discardReadBytes(); - - } - channel.notifyFlushFutures(); - } - - // Allow to have the next write pending - channel.flushing.set(false); - } - - @Override - public void failed(Throwable cause, AsyncSocketChannel channel) { - ByteBuf buf = channel.pipeline().outboundByteBuffer(); - if (!buf.readable()) { - buf.discardReadBytes(); - } - - channel.notifyFlushFutures(cause); - channel.pipeline().fireExceptionCaught(cause); - if (cause instanceof IOException) { - channel.close(channel.unsafe().voidFuture()); - } - // Allow to have the next write pending - channel.flushing.set(false); - } - } - - private static final class ReadHandler implements CompletionHandler { - - @Override - public void completed(Integer result, AsyncSocketChannel channel) { - assert channel.eventLoop().inEventLoop(); - - final ChannelPipeline pipeline = channel.pipeline(); - boolean closed = false; - boolean read = false; - try { - - int localReadAmount = result.intValue(); - if (localReadAmount > 0) { - //Set the writerIndex of the buffer correctly to the - // current writerIndex + read amount of bytes. - // - // This is needed as the ByteBuffer and the ByteBuf does not share - // each others index - final ByteBuf byteBuf = pipeline.inboundByteBuffer(); - byteBuf.writerIndex(byteBuf.writerIndex() + result); - - read = true; - - } else if (localReadAmount < 0) { - closed = true; - } - - } catch (Throwable t) { - if (read) { - read = false; - pipeline.fireInboundBufferUpdated(); - } - pipeline.fireExceptionCaught(t); - if (t instanceof IOException) { - channel.close(channel.unsafe().voidFuture()); - } - } finally { - if (read) { - pipeline.fireInboundBufferUpdated(); - } - if (closed && channel.isOpen()) { - channel.close(channel.unsafe().voidFuture()); - } else { - // start the next read - AsyncSocketChannel.read(channel); - } - } - } - - @Override - public void failed(Throwable t, AsyncSocketChannel channel) { - channel.pipeline().fireExceptionCaught(t); - if (t instanceof IOException) { - channel.close(channel.unsafe().voidFuture()); - } else { - // start the next read - AsyncSocketChannel.read(channel); - } - } - } - - private static final class ConnectHandler implements CompletionHandler { - - @Override - public void completed(Void result, AsyncSocketChannel channel) { - ((AsyncUnsafe) channel.unsafe()).connectSuccess(); - - // start reading from channel - AsyncSocketChannel.read(channel); - } - - @Override - public void failed(Throwable exc, AsyncSocketChannel channel) { - ((AsyncUnsafe) channel.unsafe()).connectFailed(exc); - } - } - - @Override - public AsyncSocketChannelConfig config() { - if (config == null) { - throw new IllegalStateException("Channel not open yet"); - } - return config; - } - - -} diff --git a/transport/src/main/java/io/netty/channel/socket/nio2/package-info.java b/transport/src/main/java/io/netty/channel/socket/nio2/package-info.java deleted file mode 100755 index f3c3c4d1ff..0000000000 --- a/transport/src/main/java/io/netty/channel/socket/nio2/package-info.java +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -/** - * NIO2-based socket channel - * API implementation - recommended for a large number of connections (>= 1000). - */ -package io.netty.channel.socket.nio2; From 22bc8fecca298781841197288010c0bfb97ec6d1 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Sun, 17 Jun 2012 11:07:21 +0200 Subject: [PATCH 10/87] Implement the right interfaces --- .../io/netty/channel/socket/aio/AioServerSocketChannel.java | 4 ++-- .../java/io/netty/channel/socket/aio/AioSocketChannel.java | 6 +++++- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/transport/src/main/java/io/netty/channel/socket/aio/AioServerSocketChannel.java b/transport/src/main/java/io/netty/channel/socket/aio/AioServerSocketChannel.java index de39783c22..9aee5a1ae7 100755 --- a/transport/src/main/java/io/netty/channel/socket/aio/AioServerSocketChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/aio/AioServerSocketChannel.java @@ -18,7 +18,7 @@ package io.netty.channel.socket.aio; import io.netty.buffer.ChannelBufType; import io.netty.channel.ChannelException; import io.netty.channel.ChannelFuture; -import io.netty.channel.ServerChannel; +import io.netty.channel.socket.ServerSocketChannel; import io.netty.logging.InternalLogger; import io.netty.logging.InternalLoggerFactory; @@ -29,7 +29,7 @@ import java.nio.channels.AsynchronousServerSocketChannel; import java.nio.channels.AsynchronousSocketChannel; import java.nio.channels.CompletionHandler; -public class AioServerSocketChannel extends AbstractAioChannel implements ServerChannel { +public class AioServerSocketChannel extends AbstractAioChannel implements ServerSocketChannel { private static final AcceptHandler ACCEPT_HANDLER = new AcceptHandler(); private static final InternalLogger logger = diff --git a/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java b/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java index 3d25529e60..fe7fc8356b 100755 --- a/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java @@ -22,6 +22,7 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelStateHandler; import io.netty.channel.ChannelStateHandlerAdapter; +import io.netty.channel.socket.SocketChannel; import java.io.IOException; import java.net.InetSocketAddress; @@ -32,7 +33,7 @@ import java.nio.channels.AsynchronousSocketChannel; import java.nio.channels.CompletionHandler; import java.util.concurrent.atomic.AtomicBoolean; -public class AioSocketChannel extends AbstractAioChannel { +public class AioSocketChannel extends AbstractAioChannel implements SocketChannel { private static final CompletionHandler CONNECT_HANDLER = new ConnectHandler(); private static final CompletionHandler READ_HANDLER = new ReadHandler(); @@ -73,6 +74,9 @@ public class AioSocketChannel extends AbstractAioChannel { @Override public boolean isActive() { + if (ch == null) { + return false; + } AsynchronousSocketChannel ch = javaChannel(); return ch.isOpen() && remoteAddress() != null; } From 7412c371f2e4a74f64c400490afdbc3e930e83d9 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Sun, 17 Jun 2012 13:12:15 +0200 Subject: [PATCH 11/87] Fix writes. See #396 --- .../channel/socket/aio/AioSocketChannel.java | 43 ++++++++----------- 1 file changed, 17 insertions(+), 26 deletions(-) diff --git a/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java b/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java index fe7fc8356b..05f1f616e9 100755 --- a/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java @@ -145,8 +145,7 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne expandReadBuffer(byteBuf); // Get a ByteBuffer view on the ByteBuf and clear it before try to read - ByteBuffer buffer = byteBuf.nioBuffer(); - buffer.clear(); + ByteBuffer buffer = (ByteBuffer) byteBuf.nioBuffer(byteBuf.writerIndex(), byteBuf.writableBytes()).clear(); javaChannel().read(buffer, this, READ_HANDLER); } @@ -192,7 +191,7 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne // a PendingWriteException will be thrown. So use CAS to not run // into this if (flushing.compareAndSet(false, true)) { - ByteBuffer buffer = (ByteBuffer)buf.nioBuffer(); + ByteBuffer buffer = (ByteBuffer) buf.nioBuffer(); javaChannel().write(buffer, this, WRITE_HANDLER); } return false; @@ -206,22 +205,13 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne ByteBuf buf = channel.pipeline().outboundByteBuffer(); if (result > 0) { - if (result < buf.readableBytes()) { - // Update the readerIndex with the amount of read bytes - buf.readerIndex(buf.readerIndex() + result); - } else { - if (!buf.readable()) { - // Reset reader/writerIndex to 0 if the buffer is empty. - buf.clear(); - } else { - // not enough space in the buffer anymore so discard everything that - // was read already - buf.discardReadBytes(); - } - - - } + // Update the readerIndex with the amount of read bytes + buf.readerIndex(buf.readerIndex() + result); + channel.notifyFlushFutures(); + if (!buf.readable()) { + buf.discardReadBytes(); + } } // Allow to have the next write pending @@ -230,15 +220,16 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne @Override public void failed(Throwable cause, AioSocketChannel channel) { - ByteBuf buf = channel.pipeline().outboundByteBuffer(); - if (!buf.readable()) { - buf.discardReadBytes(); - } channel.notifyFlushFutures(cause); channel.pipeline().fireExceptionCaught(cause); if (cause instanceof IOException) { - channel.close(channel.unsafe().voidFuture()); + channel.unsafe().close(channel.unsafe().voidFuture()); + } else { + ByteBuf buf = channel.pipeline().outboundByteBuffer(); + if (!buf.readable()) { + buf.discardReadBytes(); + } } // Allow to have the next write pending channel.flushing.set(false); @@ -279,14 +270,14 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne } pipeline.fireExceptionCaught(t); if (t instanceof IOException) { - channel.close(channel.unsafe().voidFuture()); + channel.unsafe().close(channel.unsafe().voidFuture()); } } finally { if (read) { pipeline.fireInboundBufferUpdated(); } if (closed && channel.isOpen()) { - channel.close(channel.unsafe().voidFuture()); + channel.unsafe().close(channel.unsafe().voidFuture()); } else { // start the next read channel.read(); @@ -298,7 +289,7 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne public void failed(Throwable t, AioSocketChannel channel) { channel.pipeline().fireExceptionCaught(t); if (t instanceof IOException) { - channel.close(channel.unsafe().voidFuture()); + channel.unsafe().close(channel.unsafe().voidFuture()); } else { // start the next read channel.read(); From 127e9c1d1e8405257cdc6df8c5646dbd40a10c36 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Sun, 17 Jun 2012 20:28:47 +0200 Subject: [PATCH 12/87] Calling clear is wrong. See #396 --- .../io/netty/channel/socket/aio/AioSocketChannel.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java b/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java index 05f1f616e9..b142323f92 100755 --- a/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java @@ -143,9 +143,8 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne void read() { ByteBuf byteBuf = pipeline().inboundByteBuffer(); expandReadBuffer(byteBuf); - - // Get a ByteBuffer view on the ByteBuf and clear it before try to read - ByteBuffer buffer = (ByteBuffer) byteBuf.nioBuffer(byteBuf.writerIndex(), byteBuf.writableBytes()).clear(); + // Get a ByteBuffer view on the ByteBuf + ByteBuffer buffer = byteBuf.nioBuffer(byteBuf.writerIndex(), byteBuf.writableBytes()); javaChannel().read(buffer, this, READ_HANDLER); } @@ -255,8 +254,8 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne // This is needed as the ByteBuffer and the ByteBuf does not share // each others index final ByteBuf byteBuf = pipeline.inboundByteBuffer(); - byteBuf.writerIndex(byteBuf.writerIndex() + result); - + byteBuf.writerIndex(byteBuf.writerIndex() + localReadAmount); + read = true; } else if (localReadAmount < 0) { From 3e2953cf92577a0ba2e12417457d91bfbfaeb767 Mon Sep 17 00:00:00 2001 From: Trustin Lee Date: Mon, 18 Jun 2012 16:21:05 +0900 Subject: [PATCH 13/87] Add AIO transport to the test suite --- .../socket/SocketTestPermutation.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketTestPermutation.java b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketTestPermutation.java index 9e94f64638..188fa2b835 100644 --- a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketTestPermutation.java +++ b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketTestPermutation.java @@ -17,7 +17,11 @@ package io.netty.testsuite.transport.socket; import io.netty.bootstrap.Bootstrap; import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.EventLoop; import io.netty.channel.socket.InternetProtocolFamily; +import io.netty.channel.socket.aio.AioEventLoop; +import io.netty.channel.socket.aio.AioServerSocketChannel; +import io.netty.channel.socket.aio.AioSocketChannel; import io.netty.channel.socket.nio.NioDatagramChannel; import io.netty.channel.socket.nio.NioEventLoop; import io.netty.channel.socket.nio.NioServerSocketChannel; @@ -48,6 +52,15 @@ final class SocketTestPermutation { channel(new NioServerSocketChannel()); } }); + sbfs.add(new Factory() { + @Override + public ServerBootstrap newInstance() { + EventLoop loop = new AioEventLoop(); + return new ServerBootstrap(). + eventLoop(loop, loop). + channel(new AioServerSocketChannel()); + } + }); sbfs.add(new Factory() { @Override public ServerBootstrap newInstance() { @@ -66,6 +79,12 @@ final class SocketTestPermutation { return new Bootstrap().eventLoop(new NioEventLoop()).channel(new NioSocketChannel()); } }); + cbfs.add(new Factory() { + @Override + public Bootstrap newInstance() { + return new Bootstrap().eventLoop(new AioEventLoop()).channel(new AioSocketChannel()); + } + }); cbfs.add(new Factory() { @Override public Bootstrap newInstance() { From 5caf78acc07c323a7cac85a72adf231a2fabb987 Mon Sep 17 00:00:00 2001 From: Trustin Lee Date: Mon, 18 Jun 2012 17:22:06 +0900 Subject: [PATCH 14/87] Fix a bug where channelActive is not fired for an accepted channel --- .../channel/socket/aio/AioSocketChannel.java | 53 ++++++------------- 1 file changed, 16 insertions(+), 37 deletions(-) diff --git a/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java b/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java index b142323f92..5c699cc049 100755 --- a/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java @@ -18,10 +18,7 @@ package io.netty.channel.socket.aio; import io.netty.buffer.ByteBuf; import io.netty.buffer.ChannelBufType; import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPipeline; -import io.netty.channel.ChannelStateHandler; -import io.netty.channel.ChannelStateHandlerAdapter; import io.netty.channel.socket.SocketChannel; import java.io.IOException; @@ -38,24 +35,7 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne private static final CompletionHandler CONNECT_HANDLER = new ConnectHandler(); private static final CompletionHandler READ_HANDLER = new ReadHandler(); private static final CompletionHandler WRITE_HANDLER = new WriteHandler(); - private static final ChannelStateHandler READ_START_HANDLER = new ChannelStateHandlerAdapter() { - @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { - try { - super.channelActive(ctx); - - // once the channel is active, the first read is scheduled - ((AioSocketChannel)ctx.channel()).read(); - - } finally { - ctx.pipeline().remove(this); - } - - - } - - }; private final AtomicBoolean flushing = new AtomicBoolean(false); private volatile AioSocketChannelConfig config; @@ -65,10 +45,9 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne public AioSocketChannel(AioServerSocketChannel parent, Integer id, AsynchronousSocketChannel channel) { super(parent, id); - this.ch = channel; + ch = channel; if (ch != null) { config = new AioSocketChannelConfig(javaChannel()); - pipeline().addLast(READ_START_HANDLER); } } @@ -129,18 +108,17 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne if (ch == null) { ch = AsynchronousSocketChannel.open(AsynchronousChannelGroup.withThreadPool(eventLoop())); config = new AioSocketChannelConfig(javaChannel()); - pipeline().addLast(READ_START_HANDLER); + } else if (remoteAddress() != null) { + read(); } - - return null; } /** * Trigger a read from the {@link AioSocketChannel} - * + * */ - void read() { + void read() { ByteBuf byteBuf = pipeline().inboundByteBuffer(); expandReadBuffer(byteBuf); // Get a ByteBuffer view on the ByteBuf @@ -148,7 +126,7 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne javaChannel().read(buffer, this, READ_HANDLER); } - + private static boolean expandReadBuffer(ByteBuf byteBuf) { if (!byteBuf.writable()) { // FIXME: Magic number @@ -157,7 +135,7 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne } return false; } - + @Override protected void doBind(SocketAddress localAddress) throws Exception { javaChannel().bind(localAddress); @@ -185,12 +163,13 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne buf.clear(); return true; } - + // Only one pending write can be scheduled at one time. Otherwise // a PendingWriteException will be thrown. So use CAS to not run // into this if (flushing.compareAndSet(false, true)) { - ByteBuffer buffer = (ByteBuffer) buf.nioBuffer(); + ByteBuffer buffer = buf.nioBuffer(); + System.err.println("WRITE: " + buffer); javaChannel().write(buffer, this, WRITE_HANDLER); } return false; @@ -206,13 +185,13 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne if (result > 0) { // Update the readerIndex with the amount of read bytes buf.readerIndex(buf.readerIndex() + result); - + channel.notifyFlushFutures(); if (!buf.readable()) { buf.discardReadBytes(); } } - + // Allow to have the next write pending channel.flushing.set(false); } @@ -245,10 +224,9 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne boolean closed = false; boolean read = false; try { - int localReadAmount = result.intValue(); if (localReadAmount > 0) { - //Set the writerIndex of the buffer correctly to the + // Set the writerIndex of the buffer correctly to the // current writerIndex + read amount of bytes. // // This is needed as the ByteBuffer and the ByteBuf does not share @@ -261,7 +239,7 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne } else if (localReadAmount < 0) { closed = true; } - + } catch (Throwable t) { if (read) { read = false; @@ -301,7 +279,8 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne @Override public void completed(Void result, AioSocketChannel channel) { ((AsyncUnsafe) channel.unsafe()).connectSuccess(); - + channel.pipeline().fireChannelActive(); + // start reading from channel channel.read(); } From e1cbcd645686c8c0196ce94b3fdc1b0e3f781a46 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Sun, 1 Jul 2012 21:50:17 +0200 Subject: [PATCH 15/87] Remove left-overs. See #396 --- .../socket/aio/AbstractAsyncChannel.java | 168 --------- .../socket/aio/AsyncChildEventLoop.java | 51 --- .../channel/socket/aio/AsyncEventLoop.java | 41 --- .../socket/aio/AsyncServerSocketChannel.java | 140 -------- .../aio/AsyncServerSocketChannelConfig.java | 138 -------- .../socket/aio/AsyncSocketChannel.java | 318 ------------------ .../socket/aio/AsyncSocketChannelConfig.java | 237 ------------- 7 files changed, 1093 deletions(-) delete mode 100755 transport/src/main/java/io/netty/channel/socket/aio/AbstractAsyncChannel.java delete mode 100755 transport/src/main/java/io/netty/channel/socket/aio/AsyncChildEventLoop.java delete mode 100755 transport/src/main/java/io/netty/channel/socket/aio/AsyncEventLoop.java delete mode 100755 transport/src/main/java/io/netty/channel/socket/aio/AsyncServerSocketChannel.java delete mode 100755 transport/src/main/java/io/netty/channel/socket/aio/AsyncServerSocketChannelConfig.java delete mode 100755 transport/src/main/java/io/netty/channel/socket/aio/AsyncSocketChannel.java delete mode 100755 transport/src/main/java/io/netty/channel/socket/aio/AsyncSocketChannelConfig.java diff --git a/transport/src/main/java/io/netty/channel/socket/aio/AbstractAsyncChannel.java b/transport/src/main/java/io/netty/channel/socket/aio/AbstractAsyncChannel.java deleted file mode 100755 index c32e1cf9bc..0000000000 --- a/transport/src/main/java/io/netty/channel/socket/aio/AbstractAsyncChannel.java +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.channel.socket.aio; - -import io.netty.channel.AbstractChannel; -import io.netty.channel.Channel; -import io.netty.channel.ChannelFuture; -import io.netty.channel.EventLoop; - -import java.net.ConnectException; -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.nio.channels.AsynchronousChannel; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; - -public abstract class AbstractAsyncChannel extends AbstractChannel { - - protected volatile AsynchronousChannel ch; - - /** - * The future of the current connection attempt. If not null, subsequent - * connection attempts will fail. - */ - protected ChannelFuture connectFuture; - protected ScheduledFuture connectTimeoutFuture; - private ConnectException connectTimeoutException; - - protected AbstractAsyncChannel(Channel parent, Integer id) { - super(parent, id); - } - - - @Override - public InetSocketAddress localAddress() { - if (ch == null) { - return null; - } - return (InetSocketAddress) super.localAddress(); - } - - @Override - public InetSocketAddress remoteAddress() { - if (ch == null) { - return null; - } - return (InetSocketAddress) super.remoteAddress(); - } - - protected AsynchronousChannel javaChannel() { - return ch; - } - - - @Override - public boolean isOpen() { - return ch == null || ch.isOpen(); - } - - @Override - protected void doDeregister() throws Exception { - // NOOP - } - - @Override - protected AsyncUnsafe newUnsafe() { - return new AsyncUnsafe(); - } - - @Override - protected boolean isCompatible(EventLoop loop) { - return loop instanceof AsyncChildEventLoop; - } - - protected class AsyncUnsafe extends AbstractUnsafe { - - @Override - public void connect(final SocketAddress remoteAddress, - final SocketAddress localAddress, final ChannelFuture future) { - if (eventLoop().inEventLoop()) { - if (!ensureOpen(future)) { - return; - } - - try { - if (connectFuture != null) { - throw new IllegalStateException("connection attempt already made"); - } - connectFuture = future; - - doConnect(remoteAddress, localAddress, future); - - // Schedule connect timeout. - int connectTimeoutMillis = config().getConnectTimeoutMillis(); - if (connectTimeoutMillis > 0) { - connectTimeoutFuture = eventLoop().schedule(new Runnable() { - @Override - public void run() { - if (connectTimeoutException == null) { - connectTimeoutException = new ConnectException("connection timed out"); - } - ChannelFuture connectFuture = AbstractAsyncChannel.this.connectFuture; - if (connectFuture != null && - connectFuture.setFailure(connectTimeoutException)) { - pipeline().fireExceptionCaught(connectTimeoutException); - close(voidFuture()); - } - } - }, connectTimeoutMillis, TimeUnit.MILLISECONDS); - } - - } catch (Throwable t) { - future.setFailure(t); - pipeline().fireExceptionCaught(t); - closeIfClosed(); - } - } else { - eventLoop().execute(new Runnable() { - @Override - public void run() { - connect(remoteAddress, localAddress, future); - } - }); - } - } - - protected final void connectFailed(Throwable t) { - connectFuture.setFailure(t); - pipeline().fireExceptionCaught(t); - closeIfClosed(); - } - - protected final void connectSuccess() { - assert eventLoop().inEventLoop(); - assert connectFuture != null; - try { - boolean wasActive = isActive(); - connectFuture.setSuccess(); - if (!wasActive && isActive()) { - pipeline().fireChannelActive(); - } - } catch (Throwable t) { - connectFuture.setFailure(t); - pipeline().fireExceptionCaught(t); - closeIfClosed(); - } finally { - connectTimeoutFuture.cancel(false); - connectFuture = null; - } - } - } - protected abstract void doConnect(SocketAddress remoteAddress, - SocketAddress localAddress, ChannelFuture connectFuture); - -} diff --git a/transport/src/main/java/io/netty/channel/socket/aio/AsyncChildEventLoop.java b/transport/src/main/java/io/netty/channel/socket/aio/AsyncChildEventLoop.java deleted file mode 100755 index aad21eb795..0000000000 --- a/transport/src/main/java/io/netty/channel/socket/aio/AsyncChildEventLoop.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.channel.socket.aio; - -import io.netty.channel.SingleThreadEventLoop; - -import java.util.concurrent.ThreadFactory; - -final class AsyncChildEventLoop extends SingleThreadEventLoop { - - AsyncChildEventLoop(ThreadFactory threadFactory) { - super(threadFactory); - } - - @Override - protected void run() { - for (;;) { - Runnable task; - try { - task = takeTask(); - task.run(); - } catch (InterruptedException e) { - // Waken up by interruptThread() - } - - if (isShutdown() && peekTask() == null) { - break; - } - } - } - - @Override - protected void wakeup(boolean inEventLoop) { - if (!inEventLoop) { - interruptThread(); - } - } -} diff --git a/transport/src/main/java/io/netty/channel/socket/aio/AsyncEventLoop.java b/transport/src/main/java/io/netty/channel/socket/aio/AsyncEventLoop.java deleted file mode 100755 index 55dbba1f32..0000000000 --- a/transport/src/main/java/io/netty/channel/socket/aio/AsyncEventLoop.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.channel.socket.aio; - -import io.netty.channel.EventExecutor; -import io.netty.channel.MultithreadEventLoop; - -import java.util.concurrent.ThreadFactory; - -public class AsyncEventLoop extends MultithreadEventLoop { - - public AsyncEventLoop() { - this(0); - } - - public AsyncEventLoop(int nThreads) { - this(nThreads, null); - } - - public AsyncEventLoop(int nThreads, ThreadFactory threadFactory) { - super(nThreads, threadFactory); - } - - @Override - protected EventExecutor newChild(ThreadFactory threadFactory, Object... args) throws Exception { - return new AsyncChildEventLoop(threadFactory); - } -} diff --git a/transport/src/main/java/io/netty/channel/socket/aio/AsyncServerSocketChannel.java b/transport/src/main/java/io/netty/channel/socket/aio/AsyncServerSocketChannel.java deleted file mode 100755 index a6ac81bf3a..0000000000 --- a/transport/src/main/java/io/netty/channel/socket/aio/AsyncServerSocketChannel.java +++ /dev/null @@ -1,140 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.channel.socket.aio; - -import io.netty.buffer.ChannelBufType; -import io.netty.channel.ChannelException; -import io.netty.channel.ChannelFuture; -import io.netty.channel.ServerChannel; -import io.netty.logging.InternalLogger; -import io.netty.logging.InternalLoggerFactory; - -import java.io.IOException; -import java.net.SocketAddress; -import java.nio.channels.AsynchronousChannelGroup; -import java.nio.channels.AsynchronousServerSocketChannel; -import java.nio.channels.AsynchronousSocketChannel; -import java.nio.channels.CompletionHandler; - -public class AsyncServerSocketChannel extends AbstractAsyncChannel implements ServerChannel { - - private static final AcceptHandler ACCEPT_HANDLER = new AcceptHandler(); - private static final InternalLogger logger = - InternalLoggerFactory.getInstance(AsyncServerSocketChannel.class); - private volatile AsyncServerSocketChannelConfig config; - - public AsyncServerSocketChannel() { - super(null, null); - } - - - @Override - protected AsynchronousServerSocketChannel javaChannel() { - return (AsynchronousServerSocketChannel) super.javaChannel(); - } - - @Override - public boolean isActive() { - AsynchronousServerSocketChannel channel = javaChannel(); - try { - if (channel != null && channel.getLocalAddress() != null) { - return true; - } - } catch (IOException e) { - return true; - } - return false; - } - - @Override - public ChannelBufType bufferType() { - return ChannelBufType.MESSAGE; - } - - @Override - protected SocketAddress localAddress0() { - try { - return javaChannel().getLocalAddress(); - } catch (IOException e) { - throw new ChannelException(e); - } - } - - @Override - protected SocketAddress remoteAddress0() { - return null; - } - - @Override - protected void doBind(SocketAddress localAddress) throws Exception { - javaChannel().bind(localAddress); - javaChannel().accept(this, ACCEPT_HANDLER); - - } - - @Override - protected void doClose() throws Exception { - javaChannel().close(); - } - - @Override - protected boolean isFlushPending() { - return false; - } - - @Override - protected void doConnect( - SocketAddress remoteAddress, SocketAddress localAddress, ChannelFuture future) { - future.setFailure(new UnsupportedOperationException()); - } - - @Override - protected void doDisconnect() throws Exception { - throw new UnsupportedOperationException(); - } - - @Override - protected Runnable doRegister() throws Exception { - ch = AsynchronousServerSocketChannel.open(AsynchronousChannelGroup.withThreadPool(eventLoop())); - config = new AsyncServerSocketChannelConfig(javaChannel()); - - return null; - } - - private static final class AcceptHandler - implements CompletionHandler { - public void completed(AsynchronousSocketChannel ch, AsyncServerSocketChannel channel) { - // register again this handler to accept new connections - channel.javaChannel().accept(channel, this); - - // create the socket add it to the buffer and fire the event - channel.pipeline().inboundMessageBuffer().add(new AsyncSocketChannel(channel, null, ch)); - channel.pipeline().fireInboundBufferUpdated(); - } - - public void failed(Throwable t, AsyncServerSocketChannel channel) { - logger.warn("Failed to create a new channel from an accepted socket.", t); - } - } - - @Override - public AsyncServerSocketChannelConfig config() { - if (config == null) { - throw new IllegalStateException("Channel not registered yet"); - } - return config; - } -} diff --git a/transport/src/main/java/io/netty/channel/socket/aio/AsyncServerSocketChannelConfig.java b/transport/src/main/java/io/netty/channel/socket/aio/AsyncServerSocketChannelConfig.java deleted file mode 100755 index 58dfff1219..0000000000 --- a/transport/src/main/java/io/netty/channel/socket/aio/AsyncServerSocketChannelConfig.java +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.channel.socket.aio; - -import static io.netty.channel.ChannelOption.*; -import io.netty.channel.ChannelException; -import io.netty.channel.ChannelOption; -import io.netty.channel.DefaultChannelConfig; -import io.netty.channel.socket.ServerSocketChannelConfig; - -import java.io.IOException; -import java.net.StandardSocketOptions; -import java.nio.channels.AsynchronousServerSocketChannel; -import java.util.Map; - -/** - * The Async {@link ServerSocketChannelConfig} implementation. - */ -public class AsyncServerSocketChannelConfig extends DefaultChannelConfig - implements ServerSocketChannelConfig { - - private final AsynchronousServerSocketChannel channel; - private volatile int backlog; - - /** - * Creates a new instance. - */ - public AsyncServerSocketChannelConfig(AsynchronousServerSocketChannel channel) { - if (channel == null) { - throw new NullPointerException("channel"); - } - this.channel = channel; - } - - @Override - public Map, Object> getOptions() { - return getOptions(super.getOptions(), SO_RCVBUF, SO_REUSEADDR, SO_BACKLOG); - } - - @Override - public T getOption(ChannelOption option) { - if (option == SO_RCVBUF) { - return (T) Integer.valueOf(getReceiveBufferSize()); - } - if (option == SO_REUSEADDR) { - return (T) Boolean.valueOf(isReuseAddress()); - } - if (option == SO_BACKLOG) { - return (T) Integer.valueOf(getBacklog()); - } - - return super.getOption(option); - } - - @Override - public boolean setOption(ChannelOption option, T value) { - validate(option, value); - - if (option == SO_RCVBUF) { - setReceiveBufferSize((Integer) value); - } else if (option == SO_REUSEADDR) { - setReuseAddress((Boolean) value); - } else if (option == SO_BACKLOG) { - setBacklog((Integer) value); - } else { - return super.setOption(option, value); - } - - return true; - } - - @Override - public boolean isReuseAddress() { - try { - return channel.getOption(StandardSocketOptions.SO_REUSEADDR); - } catch (IOException e) { - throw new ChannelException(e); - } - } - - @Override - public void setReuseAddress(boolean reuseAddress) { - try { - channel.setOption(StandardSocketOptions.SO_REUSEADDR, reuseAddress); - } catch (IOException e) { - throw new ChannelException(e); - } - } - - @Override - public int getReceiveBufferSize() { - try { - return channel.getOption(StandardSocketOptions.SO_RCVBUF); - } catch (IOException e) { - throw new ChannelException(e); - } - } - - @Override - public void setReceiveBufferSize(int receiveBufferSize) { - try { - channel.setOption(StandardSocketOptions.SO_RCVBUF, receiveBufferSize); - } catch (IOException e) { - throw new ChannelException(e); - } - } - - @Override - public void setPerformancePreferences(int connectionTime, int latency, int bandwidth) { - throw new UnsupportedOperationException(); - } - - @Override - public int getBacklog() { - return backlog; - } - - @Override - public void setBacklog(int backlog) { - if (backlog < 0) { - throw new IllegalArgumentException("backlog: " + backlog); - } - this.backlog = backlog; - } -} diff --git a/transport/src/main/java/io/netty/channel/socket/aio/AsyncSocketChannel.java b/transport/src/main/java/io/netty/channel/socket/aio/AsyncSocketChannel.java deleted file mode 100755 index f561cd1486..0000000000 --- a/transport/src/main/java/io/netty/channel/socket/aio/AsyncSocketChannel.java +++ /dev/null @@ -1,318 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.channel.socket.aio; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ChannelBufType; -import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.ChannelStateHandler; -import io.netty.channel.ChannelStateHandlerAdapter; - -import java.io.IOException; -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.nio.ByteBuffer; -import java.nio.channels.AsynchronousChannelGroup; -import java.nio.channels.AsynchronousSocketChannel; -import java.nio.channels.CompletionHandler; -import java.util.concurrent.atomic.AtomicBoolean; - -public class AsyncSocketChannel extends AbstractAsyncChannel { - - private static final CompletionHandler CONNECT_HANDLER = new ConnectHandler(); - private static final CompletionHandler READ_HANDLER = new ReadHandler(); - private static final CompletionHandler WRITE_HANDLER = new WriteHandler(); - private static final ChannelStateHandler READ_START_HANDLER = new ChannelStateHandlerAdapter() { - - @Override - public void channelActive(ChannelHandlerContext ctx) throws Exception { - try { - super.channelActive(ctx); - - // once the channel is active, the first read is scheduled - AsyncSocketChannel.read((AsyncSocketChannel)ctx.channel()); - - } finally { - ctx.pipeline().remove(this); - } - - - } - - }; - private final AtomicBoolean flushing = new AtomicBoolean(false); - private volatile AsyncSocketChannelConfig config; - - public AsyncSocketChannel() { - this(null, null, null); - } - - public AsyncSocketChannel(AsyncServerSocketChannel parent, Integer id, AsynchronousSocketChannel channel) { - super(parent, id); - this.ch = channel; - if (ch != null) { - config = new AsyncSocketChannelConfig(javaChannel()); - pipeline().addLast(READ_START_HANDLER); - } - } - - @Override - public boolean isActive() { - AsynchronousSocketChannel ch = javaChannel(); - return ch.isOpen() && remoteAddress() != null; - } - - @Override - protected AsynchronousSocketChannel javaChannel() { - return (AsynchronousSocketChannel) super.javaChannel(); - } - - @Override - public ChannelBufType bufferType() { - return ChannelBufType.BYTE; - } - - @Override - protected void doConnect(SocketAddress remoteAddress, SocketAddress localAddress, final ChannelFuture future) { - assert ch != null; - if (localAddress != null) { - try { - javaChannel().bind(localAddress); - } catch (IOException e) { - future.setFailure(e); - return; - } - } - - javaChannel().connect(remoteAddress, this, CONNECT_HANDLER); - } - - @Override - protected InetSocketAddress localAddress0() { - try { - return (InetSocketAddress) javaChannel().getLocalAddress(); - } catch (IOException e) { - return null; - } - } - - @Override - protected InetSocketAddress remoteAddress0() { - try { - return (InetSocketAddress) javaChannel().getRemoteAddress(); - } catch (IOException e) { - return null; - } - } - - @Override - protected Runnable doRegister() throws Exception { - if (ch == null) { - ch = AsynchronousSocketChannel.open(AsynchronousChannelGroup.withThreadPool(eventLoop())); - config = new AsyncSocketChannelConfig(javaChannel()); - pipeline().addLast(READ_START_HANDLER); - } - - - return null; - } - - /** - * Trigger a read from the {@link AsyncSocketChannel} - * - */ - private static void read(AsyncSocketChannel channel) { - ByteBuf byteBuf = channel.pipeline().inboundByteBuffer(); - expandReadBuffer(byteBuf); - - // Get a ByteBuffer view on the ByteBuf and clear it before try to read - ByteBuffer buffer = byteBuf.nioBuffer(); - buffer.clear(); - channel.javaChannel().read(buffer, channel, READ_HANDLER); - } - - - private static boolean expandReadBuffer(ByteBuf byteBuf) { - if (!byteBuf.writable()) { - // FIXME: Magic number - byteBuf.ensureWritableBytes(4096); - return true; - } - return false; - } - - @Override - protected void doBind(SocketAddress localAddress) throws Exception { - javaChannel().bind(localAddress); - } - - @Override - protected void doDisconnect() throws Exception { - doClose(); - } - - @Override - protected void doClose() throws Exception { - javaChannel().close(); - } - - @Override - protected boolean isFlushPending() { - return false; - } - - @Override - protected boolean doFlushByteBuffer(ByteBuf buf) throws Exception { - // Only one pending write can be scheduled at one time. Otherwise - // a PendingWriteException will be thrown. So use CAS to not run - // into this - if (flushing.compareAndSet(false, true)) { - ByteBuffer buffer = (ByteBuffer)buf.nioBuffer(); - javaChannel().write(buffer, this, WRITE_HANDLER); - } - return false; - } - - - private static final class WriteHandler implements CompletionHandler { - - @Override - public void completed(Integer result, AsyncSocketChannel channel) { - ByteBuf buf = channel.pipeline().outboundByteBuffer(); - - if (result > 0) { - if (result < buf.readableBytes()) { - // Update the readerIndex with the amount of read bytes - buf.readerIndex(buf.readerIndex() + result); - } else { - // not enough space in the buffer anymore so discard everything that - // was read already - buf.discardReadBytes(); - - } - channel.notifyFlushFutures(); - } - - // Allow to have the next write pending - channel.flushing.set(false); - } - - @Override - public void failed(Throwable cause, AsyncSocketChannel channel) { - ByteBuf buf = channel.pipeline().outboundByteBuffer(); - if (!buf.readable()) { - buf.discardReadBytes(); - } - - channel.notifyFlushFutures(cause); - channel.pipeline().fireExceptionCaught(cause); - if (cause instanceof IOException) { - channel.close(channel.unsafe().voidFuture()); - } - // Allow to have the next write pending - channel.flushing.set(false); - } - } - - private static final class ReadHandler implements CompletionHandler { - - @Override - public void completed(Integer result, AsyncSocketChannel channel) { - assert channel.eventLoop().inEventLoop(); - - final ChannelPipeline pipeline = channel.pipeline(); - boolean closed = false; - boolean read = false; - try { - - int localReadAmount = result.intValue(); - if (localReadAmount > 0) { - //Set the writerIndex of the buffer correctly to the - // current writerIndex + read amount of bytes. - // - // This is needed as the ByteBuffer and the ByteBuf does not share - // each others index - final ByteBuf byteBuf = pipeline.inboundByteBuffer(); - byteBuf.writerIndex(byteBuf.writerIndex() + result); - - read = true; - - } else if (localReadAmount < 0) { - closed = true; - } - - } catch (Throwable t) { - if (read) { - read = false; - pipeline.fireInboundBufferUpdated(); - } - pipeline.fireExceptionCaught(t); - if (t instanceof IOException) { - channel.close(channel.unsafe().voidFuture()); - } - } finally { - if (read) { - pipeline.fireInboundBufferUpdated(); - } - if (closed && channel.isOpen()) { - channel.close(channel.unsafe().voidFuture()); - } else { - // start the next read - AsyncSocketChannel.read(channel); - } - } - } - - @Override - public void failed(Throwable t, AsyncSocketChannel channel) { - channel.pipeline().fireExceptionCaught(t); - if (t instanceof IOException) { - channel.close(channel.unsafe().voidFuture()); - } else { - // start the next read - AsyncSocketChannel.read(channel); - } - } - } - - private static final class ConnectHandler implements CompletionHandler { - - @Override - public void completed(Void result, AsyncSocketChannel channel) { - ((AsyncUnsafe) channel.unsafe()).connectSuccess(); - - // start reading from channel - AsyncSocketChannel.read(channel); - } - - @Override - public void failed(Throwable exc, AsyncSocketChannel channel) { - ((AsyncUnsafe) channel.unsafe()).connectFailed(exc); - } - } - - @Override - public AsyncSocketChannelConfig config() { - if (config == null) { - throw new IllegalStateException("Channel not open yet"); - } - return config; - } - - -} diff --git a/transport/src/main/java/io/netty/channel/socket/aio/AsyncSocketChannelConfig.java b/transport/src/main/java/io/netty/channel/socket/aio/AsyncSocketChannelConfig.java deleted file mode 100755 index 481a606d63..0000000000 --- a/transport/src/main/java/io/netty/channel/socket/aio/AsyncSocketChannelConfig.java +++ /dev/null @@ -1,237 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.channel.socket.aio; - -import static io.netty.channel.ChannelOption.*; -import io.netty.channel.ChannelException; -import io.netty.channel.ChannelOption; -import io.netty.channel.DefaultChannelConfig; -import io.netty.channel.socket.SocketChannelConfig; - -import java.io.IOException; -import java.net.StandardSocketOptions; -import java.nio.channels.NetworkChannel; -import java.util.Map; - -/** - * The default {@link SocketChannelConfig} implementation. - */ -public class AsyncSocketChannelConfig extends DefaultChannelConfig - implements SocketChannelConfig { - - private final NetworkChannel channel; - - /** - * Creates a new instance. - */ - public AsyncSocketChannelConfig(NetworkChannel channel) { - if (channel == null) { - throw new NullPointerException("channel"); - } - this.channel = channel; - } - - @Override - public Map, Object> getOptions() { - return getOptions( - super.getOptions(), - SO_RCVBUF, SO_SNDBUF, TCP_NODELAY, SO_KEEPALIVE, SO_REUSEADDR, SO_LINGER, IP_TOS); - } - - @Override - public T getOption(ChannelOption option) { - if (option == SO_RCVBUF) { - return (T) Integer.valueOf(getReceiveBufferSize()); - } - if (option == SO_SNDBUF) { - return (T) Integer.valueOf(getSendBufferSize()); - } - if (option == TCP_NODELAY) { - return (T) Boolean.valueOf(isTcpNoDelay()); - } - if (option == SO_KEEPALIVE) { - return (T) Boolean.valueOf(isKeepAlive()); - } - if (option == SO_REUSEADDR) { - return (T) Boolean.valueOf(isReuseAddress()); - } - if (option == SO_LINGER) { - return (T) Integer.valueOf(getSoLinger()); - } - if (option == IP_TOS) { - return (T) Integer.valueOf(getTrafficClass()); - } - - return super.getOption(option); - } - - @Override - public boolean setOption(ChannelOption option, T value) { - validate(option, value); - - if (option == SO_RCVBUF) { - setReceiveBufferSize((Integer) value); - } else if (option == SO_SNDBUF) { - setSendBufferSize((Integer) value); - } else if (option == TCP_NODELAY) { - setTcpNoDelay((Boolean) value); - } else if (option == SO_KEEPALIVE) { - setKeepAlive((Boolean) value); - } else if (option == SO_REUSEADDR) { - setReuseAddress((Boolean) value); - } else if (option == SO_LINGER) { - setSoLinger((Integer) value); - } else if (option == IP_TOS) { - setTrafficClass((Integer) value); - } else { - return super.setOption(option, value); - } - - return true; - } - - @Override - public int getReceiveBufferSize() { - try { - return (int) channel.getOption(StandardSocketOptions.SO_RCVBUF); - } catch (IOException e) { - throw new ChannelException(e); - } - } - - @Override - public int getSendBufferSize() { - try { - return channel.getOption(StandardSocketOptions.SO_SNDBUF); - } catch (IOException e) { - throw new ChannelException(e); - } - } - - @Override - public int getSoLinger() { - try { - return channel.getOption(StandardSocketOptions.SO_LINGER); - } catch (IOException e) { - throw new ChannelException(e); - } - } - - @Override - public int getTrafficClass() { - try { - return channel.getOption(StandardSocketOptions.IP_TOS); - } catch (IOException e) { - throw new ChannelException(e); - } - } - - @Override - public boolean isKeepAlive() { - try { - return channel.getOption(StandardSocketOptions.SO_KEEPALIVE); - } catch (IOException e) { - throw new ChannelException(e); - } - } - - @Override - public boolean isReuseAddress() { - try { - return channel.getOption(StandardSocketOptions.SO_REUSEADDR); - } catch (IOException e) { - throw new ChannelException(e); - } - } - - @Override - public boolean isTcpNoDelay() { - try { - return channel.getOption(StandardSocketOptions.SO_REUSEADDR); - } catch (IOException e) { - throw new ChannelException(e); - } - } - - @Override - public void setKeepAlive(boolean keepAlive) { - try { - channel.setOption(StandardSocketOptions.SO_KEEPALIVE, keepAlive); - } catch (IOException e) { - throw new ChannelException(e); - } - } - - @Override - public void setPerformancePreferences( - int connectionTime, int latency, int bandwidth) { - throw new UnsupportedOperationException(); - } - - @Override - public void setReceiveBufferSize(int receiveBufferSize) { - try { - channel.setOption(StandardSocketOptions.SO_RCVBUF, receiveBufferSize); - } catch (IOException e) { - throw new ChannelException(e); - } - } - - @Override - public void setReuseAddress(boolean reuseAddress) { - try { - channel.setOption(StandardSocketOptions.SO_REUSEADDR, reuseAddress); - } catch (IOException e) { - throw new ChannelException(e); - } - } - - @Override - public void setSendBufferSize(int sendBufferSize) { - try { - channel.setOption(StandardSocketOptions.SO_SNDBUF, sendBufferSize); - } catch (IOException e) { - throw new ChannelException(e); - } - } - - @Override - public void setSoLinger(int soLinger) { - try { - channel.setOption(StandardSocketOptions.SO_LINGER, soLinger); - } catch (IOException e) { - throw new ChannelException(e); - } - } - - @Override - public void setTcpNoDelay(boolean tcpNoDelay) { - try { - channel.setOption(StandardSocketOptions.TCP_NODELAY, tcpNoDelay); - } catch (IOException e) { - throw new ChannelException(e); - } - } - - @Override - public void setTrafficClass(int trafficClass) { - try { - channel.setOption(StandardSocketOptions.IP_TOS, trafficClass); - } catch (IOException e) { - throw new ChannelException(e); - } - } -} From 8c0146fbd8f77cdbe365661a5750bdcf201acbcc Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Sun, 1 Jul 2012 21:50:44 +0200 Subject: [PATCH 16/87] Supress warnings --- .../netty/channel/socket/aio/AioServerSocketChannelConfig.java | 1 + .../java/io/netty/channel/socket/aio/AioSocketChannelConfig.java | 1 + 2 files changed, 2 insertions(+) diff --git a/transport/src/main/java/io/netty/channel/socket/aio/AioServerSocketChannelConfig.java b/transport/src/main/java/io/netty/channel/socket/aio/AioServerSocketChannelConfig.java index 116b173b23..3a7b1a24b8 100755 --- a/transport/src/main/java/io/netty/channel/socket/aio/AioServerSocketChannelConfig.java +++ b/transport/src/main/java/io/netty/channel/socket/aio/AioServerSocketChannelConfig.java @@ -50,6 +50,7 @@ public class AioServerSocketChannelConfig extends DefaultChannelConfig return getOptions(super.getOptions(), SO_RCVBUF, SO_REUSEADDR, SO_BACKLOG); } + @SuppressWarnings("unchecked") @Override public T getOption(ChannelOption option) { if (option == SO_RCVBUF) { diff --git a/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannelConfig.java b/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannelConfig.java index 0fe1a244d0..8722206054 100755 --- a/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannelConfig.java +++ b/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannelConfig.java @@ -51,6 +51,7 @@ public class AioSocketChannelConfig extends DefaultChannelConfig SO_RCVBUF, SO_SNDBUF, TCP_NODELAY, SO_KEEPALIVE, SO_REUSEADDR, SO_LINGER, IP_TOS); } + @SuppressWarnings("unchecked") @Override public T getOption(ChannelOption option) { if (option == SO_RCVBUF) { From 0562aad91ffa816b7fdfc0c467c7a57a27dda7b4 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Sun, 1 Jul 2012 22:20:36 +0200 Subject: [PATCH 17/87] Make sure all pending writes are flushed. See #396 --- .../io/netty/channel/socket/aio/AioSocketChannel.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java b/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java index 5c699cc049..8451ba3a33 100755 --- a/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java @@ -123,6 +123,7 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne expandReadBuffer(byteBuf); // Get a ByteBuffer view on the ByteBuf ByteBuffer buffer = byteBuf.nioBuffer(byteBuf.writerIndex(), byteBuf.writableBytes()); + javaChannel().read(buffer, this, READ_HANDLER); } @@ -169,7 +170,6 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne // into this if (flushing.compareAndSet(false, true)) { ByteBuffer buffer = buf.nioBuffer(); - System.err.println("WRITE: " + buffer); javaChannel().write(buffer, this, WRITE_HANDLER); } return false; @@ -194,6 +194,13 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne // Allow to have the next write pending channel.flushing.set(false); + try { + // try to flush it again if nothing is left it will return fast here + channel.doFlushByteBuffer(buf); + } catch (Exception e) { + // Should never happen, anyway call failed just in case + failed(e, channel); + } } @Override From 12112ac8575170bbe62f59b968ddd323e5103cdc Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Sun, 1 Jul 2012 22:32:45 +0200 Subject: [PATCH 18/87] Use the correct outbound buffer for flush the writes. See #396 --- .../java/io/netty/channel/socket/aio/AioSocketChannel.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java b/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java index 8451ba3a33..f474288bf7 100755 --- a/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java @@ -180,9 +180,9 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne @Override public void completed(Integer result, AioSocketChannel channel) { - ByteBuf buf = channel.pipeline().outboundByteBuffer(); - + ByteBuf buf = channel.unsafe().directOutboundContext().outboundByteBuffer(); if (result > 0) { + // Update the readerIndex with the amount of read bytes buf.readerIndex(buf.readerIndex() + result); From 82834c2f348d35f8d9a224c3b9a51e3aa085839d Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Sun, 1 Jul 2012 22:39:33 +0200 Subject: [PATCH 19/87] Check that eventloop's were setup correctly. See #396 --- .../io/netty/channel/socket/aio/AioSocketChannel.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java b/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java index f474288bf7..4b3b3dfc2e 100755 --- a/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java @@ -17,6 +17,7 @@ package io.netty.channel.socket.aio; import io.netty.buffer.ByteBuf; import io.netty.buffer.ChannelBufType; +import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelPipeline; import io.netty.channel.socket.SocketChannel; @@ -105,6 +106,14 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne @Override protected Runnable doRegister() throws Exception { + Channel parent = parent(); + if (parent != null) { + // check that the eventloop instance is shared for the parent and the child + // if not throw an IllegalStateException + if (parent.eventLoop() != eventLoop()) { + throw new IllegalStateException("eventLoop and childEventLoop must be the same!"); + } + } if (ch == null) { ch = AsynchronousSocketChannel.open(AsynchronousChannelGroup.withThreadPool(eventLoop())); config = new AioSocketChannelConfig(javaChannel()); From c3770a0fba42173960662833514564aaf7705d32 Mon Sep 17 00:00:00 2001 From: norman Date: Tue, 3 Jul 2012 11:50:50 +0200 Subject: [PATCH 20/87] Revert "Check that eventloop's were setup correctly. See #396" This reverts commit 82834c2f348d35f8d9a224c3b9a51e3aa085839d. --- .../io/netty/channel/socket/aio/AioSocketChannel.java | 9 --------- 1 file changed, 9 deletions(-) diff --git a/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java b/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java index 4b3b3dfc2e..f474288bf7 100755 --- a/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java @@ -17,7 +17,6 @@ package io.netty.channel.socket.aio; import io.netty.buffer.ByteBuf; import io.netty.buffer.ChannelBufType; -import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelPipeline; import io.netty.channel.socket.SocketChannel; @@ -106,14 +105,6 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne @Override protected Runnable doRegister() throws Exception { - Channel parent = parent(); - if (parent != null) { - // check that the eventloop instance is shared for the parent and the child - // if not throw an IllegalStateException - if (parent.eventLoop() != eventLoop()) { - throw new IllegalStateException("eventLoop and childEventLoop must be the same!"); - } - } if (ch == null) { ch = AsynchronousSocketChannel.open(AsynchronousChannelGroup.withThreadPool(eventLoop())); config = new AioSocketChannelConfig(javaChannel()); From e58d657421a6ca9e6ebd7d34f9d6cce7345b7ab3 Mon Sep 17 00:00:00 2001 From: norman Date: Tue, 3 Jul 2012 12:01:52 +0200 Subject: [PATCH 21/87] Supress exception logging if the exception was expected. See #396 --- .../netty/channel/socket/aio/AioServerSocketChannel.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/transport/src/main/java/io/netty/channel/socket/aio/AioServerSocketChannel.java b/transport/src/main/java/io/netty/channel/socket/aio/AioServerSocketChannel.java index 9aee5a1ae7..1f63c92387 100755 --- a/transport/src/main/java/io/netty/channel/socket/aio/AioServerSocketChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/aio/AioServerSocketChannel.java @@ -25,6 +25,7 @@ import io.netty.logging.InternalLoggerFactory; import java.io.IOException; import java.net.SocketAddress; import java.nio.channels.AsynchronousChannelGroup; +import java.nio.channels.AsynchronousCloseException; import java.nio.channels.AsynchronousServerSocketChannel; import java.nio.channels.AsynchronousSocketChannel; import java.nio.channels.CompletionHandler; @@ -123,10 +124,15 @@ public class AioServerSocketChannel extends AbstractAioChannel implements Server // create the socket add it to the buffer and fire the event channel.pipeline().inboundMessageBuffer().add(new AioSocketChannel(channel, null, ch)); channel.pipeline().fireInboundBufferUpdated(); + } public void failed(Throwable t, AioServerSocketChannel channel) { - logger.warn("Failed to create a new channel from an accepted socket.", t); + // check if the exception was thrown because the channel was closed before + // log something + if (channel.isOpen() && !(t instanceof AsynchronousCloseException)) { + logger.warn("Failed to create a new channel from an accepted socket.", t); + } } } From 7d33846690dec9a3e39b8312ab4f6f74c4c8b2c4 Mon Sep 17 00:00:00 2001 From: norman Date: Tue, 3 Jul 2012 13:10:29 +0200 Subject: [PATCH 22/87] Trigger the read() after the registration was complete. See #396 --- .../io/netty/channel/socket/aio/AioSocketChannel.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java b/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java index f474288bf7..08deb6f66f 100755 --- a/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java @@ -108,8 +108,15 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne if (ch == null) { ch = AsynchronousSocketChannel.open(AsynchronousChannelGroup.withThreadPool(eventLoop())); config = new AioSocketChannelConfig(javaChannel()); + return null; } else if (remoteAddress() != null) { - read(); + return new Runnable() { + + @Override + public void run() { + read(); + } + }; } return null; } From a5b6f1d6150b4f1aa50fe9109f98a4fde7b96ff5 Mon Sep 17 00:00:00 2001 From: norman Date: Tue, 3 Jul 2012 13:52:33 +0200 Subject: [PATCH 23/87] Ignore NetworkChannel class. See #396 --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index ae53662ead..647ed5e365 100644 --- a/pom.xml +++ b/pom.xml @@ -285,6 +285,7 @@ java.nio.channels.AsynchronousSocketChannel java.nio.channels.AsynchronousServerSocketChannel java.nio.channels.AsynchronousChannelGroup + java.nio.channels.NetworkChannel From eccc28965e60029a9a4df93c1f5495baaa38d0ec Mon Sep 17 00:00:00 2001 From: norman Date: Tue, 3 Jul 2012 15:25:28 +0200 Subject: [PATCH 24/87] Make sure the ComplationHandler stuff is handled in the eventloop. See #396 --- .../socket/aio/AioCompletionHandler.java | 69 +++++++++++++++++++ .../socket/aio/AioServerSocketChannel.java | 28 +++++--- .../channel/socket/aio/AioSocketChannel.java | 58 +++++++++++----- 3 files changed, 129 insertions(+), 26 deletions(-) create mode 100644 transport/src/main/java/io/netty/channel/socket/aio/AioCompletionHandler.java diff --git a/transport/src/main/java/io/netty/channel/socket/aio/AioCompletionHandler.java b/transport/src/main/java/io/netty/channel/socket/aio/AioCompletionHandler.java new file mode 100644 index 0000000000..a78235b71b --- /dev/null +++ b/transport/src/main/java/io/netty/channel/socket/aio/AioCompletionHandler.java @@ -0,0 +1,69 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.channel.socket.aio; + +import io.netty.channel.Channel; +import io.netty.channel.EventLoop; + +import java.nio.channels.CompletionHandler; + +/** + * Special {@link CompletionHandler} which makes sure that the callback methods gets executed in the {@link EventLoop} + * + * + */ +abstract class AioCompletionHandler implements CompletionHandler { + + /** + * See {@link CompletionHandler#completed(Object, Object)} + */ + protected abstract void completed0(V result, A channel); + + /** + * Set {@link CompletionHandler#failed(Throwable, Object)} + */ + protected abstract void failed0(Throwable exc, A channel); + + @Override + public final void completed(final V result, final A channel) { + if (channel.eventLoop().inEventLoop()) { + completed0(result, channel); + } else { + channel.eventLoop().execute(new Runnable() { + + @Override + public void run() { + completed0(result, channel); + } + }); + } + } + + @Override + public final void failed(final Throwable exc, final A channel) { + if (channel.eventLoop().inEventLoop()) { + failed0(exc, channel); + } else { + channel.eventLoop().execute(new Runnable() { + + @Override + public void run() { + failed0(exc, channel); + } + }); + } + } +} diff --git a/transport/src/main/java/io/netty/channel/socket/aio/AioServerSocketChannel.java b/transport/src/main/java/io/netty/channel/socket/aio/AioServerSocketChannel.java index 1f63c92387..418377b5fa 100755 --- a/transport/src/main/java/io/netty/channel/socket/aio/AioServerSocketChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/aio/AioServerSocketChannel.java @@ -28,7 +28,7 @@ import java.nio.channels.AsynchronousChannelGroup; import java.nio.channels.AsynchronousCloseException; import java.nio.channels.AsynchronousServerSocketChannel; import java.nio.channels.AsynchronousSocketChannel; -import java.nio.channels.CompletionHandler; +import java.util.concurrent.atomic.AtomicBoolean; public class AioServerSocketChannel extends AbstractAioChannel implements ServerSocketChannel { @@ -36,6 +36,7 @@ public class AioServerSocketChannel extends AbstractAioChannel implements Server private static final InternalLogger logger = InternalLoggerFactory.getInstance(AioServerSocketChannel.class); private volatile AioServerSocketChannelConfig config; + final AtomicBoolean closed = new AtomicBoolean(false); public AioServerSocketChannel() { super(null, null); @@ -88,7 +89,9 @@ public class AioServerSocketChannel extends AbstractAioChannel implements Server @Override protected void doClose() throws Exception { - javaChannel().close(); + if (closed.compareAndSet(false, true)) { + javaChannel().close(); + } } @Override @@ -116,21 +119,28 @@ public class AioServerSocketChannel extends AbstractAioChannel implements Server } private static final class AcceptHandler - implements CompletionHandler { - public void completed(AsynchronousSocketChannel ch, AioServerSocketChannel channel) { - // register again this handler to accept new connections + extends AioCompletionHandler { + + @Override + protected void completed0(AsynchronousSocketChannel ch, AioServerSocketChannel channel) { + // register again this handler to accept new connections channel.javaChannel().accept(channel, this); // create the socket add it to the buffer and fire the event channel.pipeline().inboundMessageBuffer().add(new AioSocketChannel(channel, null, ch)); channel.pipeline().fireInboundBufferUpdated(); - } - public void failed(Throwable t, AioServerSocketChannel channel) { - // check if the exception was thrown because the channel was closed before + @Override + protected void failed0(Throwable t, AioServerSocketChannel channel) { + boolean asyncClosed = false; + if (t instanceof AsynchronousCloseException) { + asyncClosed = true; + channel.closed.set(true); + } + // check if the exception was thrown because the channel was closed before // log something - if (channel.isOpen() && !(t instanceof AsynchronousCloseException)) { + if (channel.isOpen() && ! asyncClosed) { logger.warn("Failed to create a new channel from an accepted socket.", t); } } diff --git a/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java b/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java index 08deb6f66f..14ed119e67 100755 --- a/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java @@ -26,16 +26,19 @@ import java.net.InetSocketAddress; import java.net.SocketAddress; import java.nio.ByteBuffer; import java.nio.channels.AsynchronousChannelGroup; +import java.nio.channels.AsynchronousCloseException; import java.nio.channels.AsynchronousSocketChannel; import java.nio.channels.CompletionHandler; import java.util.concurrent.atomic.AtomicBoolean; + public class AioSocketChannel extends AbstractAioChannel implements SocketChannel { private static final CompletionHandler CONNECT_HANDLER = new ConnectHandler(); private static final CompletionHandler READ_HANDLER = new ReadHandler(); private static final CompletionHandler WRITE_HANDLER = new WriteHandler(); + private final AtomicBoolean closed = new AtomicBoolean(false); private final AtomicBoolean flushing = new AtomicBoolean(false); private volatile AioSocketChannelConfig config; @@ -111,7 +114,7 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne return null; } else if (remoteAddress() != null) { return new Runnable() { - + @Override public void run() { read(); @@ -130,12 +133,11 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne expandReadBuffer(byteBuf); // Get a ByteBuffer view on the ByteBuf ByteBuffer buffer = byteBuf.nioBuffer(byteBuf.writerIndex(), byteBuf.writableBytes()); - javaChannel().read(buffer, this, READ_HANDLER); } - private static boolean expandReadBuffer(ByteBuf byteBuf) { + private boolean expandReadBuffer(ByteBuf byteBuf) { if (!byteBuf.writable()) { // FIXME: Magic number byteBuf.ensureWritableBytes(4096); @@ -156,7 +158,9 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne @Override protected void doClose() throws Exception { - javaChannel().close(); + if (closed.compareAndSet(false, true)) { + javaChannel().close(); + } } @Override @@ -183,13 +187,13 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne } - private static final class WriteHandler implements CompletionHandler { + private static final class WriteHandler extends AioCompletionHandler { @Override - public void completed(Integer result, AioSocketChannel channel) { + protected void completed0(Integer result, AioSocketChannel channel) { ByteBuf buf = channel.unsafe().directOutboundContext().outboundByteBuffer(); if (result > 0) { - + // Update the readerIndex with the amount of read bytes buf.readerIndex(buf.readerIndex() + result); @@ -211,11 +215,15 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne } @Override - public void failed(Throwable cause, AioSocketChannel channel) { + protected void failed0(Throwable cause, AioSocketChannel channel) { + if (cause instanceof AsynchronousCloseException) { + channel.closed.set(true); + } channel.notifyFlushFutures(cause); channel.pipeline().fireExceptionCaught(cause); if (cause instanceof IOException) { + channel.unsafe().close(channel.unsafe().voidFuture()); } else { ByteBuf buf = channel.pipeline().outboundByteBuffer(); @@ -228,13 +236,13 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne } } - private static final class ReadHandler implements CompletionHandler { + private static final class ReadHandler extends AioCompletionHandler { @Override - public void completed(Integer result, AioSocketChannel channel) { - assert channel.eventLoop().inEventLoop(); - + protected void completed0(Integer result, AioSocketChannel channel) { final ChannelPipeline pipeline = channel.pipeline(); + final ByteBuf byteBuf = pipeline.inboundByteBuffer(); + boolean closed = false; boolean read = false; try { @@ -245,7 +253,6 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne // // This is needed as the ByteBuffer and the ByteBuf does not share // each others index - final ByteBuf byteBuf = pipeline.inboundByteBuffer(); byteBuf.writerIndex(byteBuf.writerIndex() + localReadAmount); read = true; @@ -255,11 +262,17 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne } } catch (Throwable t) { + if (t instanceof AsynchronousCloseException) { + channel.closed.set(true); + } + if (read) { read = false; pipeline.fireInboundBufferUpdated(); } + pipeline.fireExceptionCaught(t); + if (t instanceof IOException) { channel.unsafe().close(channel.unsafe().voidFuture()); } @@ -272,12 +285,20 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne } else { // start the next read channel.read(); + } } } @Override - public void failed(Throwable t, AioSocketChannel channel) { + protected void failed0(Throwable t, AioSocketChannel channel) { + if (t instanceof AsynchronousCloseException) { + channel.closed.set(true); + + // TODO: This seems wrong! + return; + } + channel.pipeline().fireExceptionCaught(t); if (t instanceof IOException) { channel.unsafe().close(channel.unsafe().voidFuture()); @@ -288,10 +309,10 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne } } - private static final class ConnectHandler implements CompletionHandler { + private static final class ConnectHandler extends AioCompletionHandler { @Override - public void completed(Void result, AioSocketChannel channel) { + protected void completed0(Void result, AioSocketChannel channel) { ((AsyncUnsafe) channel.unsafe()).connectSuccess(); channel.pipeline().fireChannelActive(); @@ -300,7 +321,10 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne } @Override - public void failed(Throwable exc, AioSocketChannel channel) { + protected void failed0(Throwable exc, AioSocketChannel channel) { + if (exc instanceof AsynchronousCloseException) { + channel.closed.set(true); + } ((AsyncUnsafe) channel.unsafe()).connectFailed(exc); } } From 31cebd7ce28764e0c519a8298c71b2bad3333ef0 Mon Sep 17 00:00:00 2001 From: norman Date: Tue, 3 Jul 2012 15:30:15 +0200 Subject: [PATCH 25/87] No need for AtomicBoolean as we run in the eventloop. See #396 --- .../socket/aio/AioServerSocketChannel.java | 8 +++---- .../channel/socket/aio/AioSocketChannel.java | 23 ++++++++++--------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/transport/src/main/java/io/netty/channel/socket/aio/AioServerSocketChannel.java b/transport/src/main/java/io/netty/channel/socket/aio/AioServerSocketChannel.java index 418377b5fa..2dea0b580c 100755 --- a/transport/src/main/java/io/netty/channel/socket/aio/AioServerSocketChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/aio/AioServerSocketChannel.java @@ -28,7 +28,6 @@ import java.nio.channels.AsynchronousChannelGroup; import java.nio.channels.AsynchronousCloseException; import java.nio.channels.AsynchronousServerSocketChannel; import java.nio.channels.AsynchronousSocketChannel; -import java.util.concurrent.atomic.AtomicBoolean; public class AioServerSocketChannel extends AbstractAioChannel implements ServerSocketChannel { @@ -36,7 +35,7 @@ public class AioServerSocketChannel extends AbstractAioChannel implements Server private static final InternalLogger logger = InternalLoggerFactory.getInstance(AioServerSocketChannel.class); private volatile AioServerSocketChannelConfig config; - final AtomicBoolean closed = new AtomicBoolean(false); + private boolean closed; public AioServerSocketChannel() { super(null, null); @@ -89,7 +88,8 @@ public class AioServerSocketChannel extends AbstractAioChannel implements Server @Override protected void doClose() throws Exception { - if (closed.compareAndSet(false, true)) { + if (!closed) { + closed = true; javaChannel().close(); } } @@ -136,7 +136,7 @@ public class AioServerSocketChannel extends AbstractAioChannel implements Server boolean asyncClosed = false; if (t instanceof AsynchronousCloseException) { asyncClosed = true; - channel.closed.set(true); + channel.closed = true; } // check if the exception was thrown because the channel was closed before // log something diff --git a/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java b/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java index 14ed119e67..67365df082 100755 --- a/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java @@ -29,7 +29,6 @@ import java.nio.channels.AsynchronousChannelGroup; import java.nio.channels.AsynchronousCloseException; import java.nio.channels.AsynchronousSocketChannel; import java.nio.channels.CompletionHandler; -import java.util.concurrent.atomic.AtomicBoolean; public class AioSocketChannel extends AbstractAioChannel implements SocketChannel { @@ -38,8 +37,8 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne private static final CompletionHandler READ_HANDLER = new ReadHandler(); private static final CompletionHandler WRITE_HANDLER = new WriteHandler(); - private final AtomicBoolean closed = new AtomicBoolean(false); - private final AtomicBoolean flushing = new AtomicBoolean(false); + private boolean closed; + private boolean flushing; private volatile AioSocketChannelConfig config; public AioSocketChannel() { @@ -158,7 +157,8 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne @Override protected void doClose() throws Exception { - if (closed.compareAndSet(false, true)) { + if (!closed) { + closed = true; javaChannel().close(); } } @@ -179,7 +179,8 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne // Only one pending write can be scheduled at one time. Otherwise // a PendingWriteException will be thrown. So use CAS to not run // into this - if (flushing.compareAndSet(false, true)) { + if (!flushing) { + flushing = true; ByteBuffer buffer = buf.nioBuffer(); javaChannel().write(buffer, this, WRITE_HANDLER); } @@ -204,7 +205,7 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne } // Allow to have the next write pending - channel.flushing.set(false); + channel.flushing = false; try { // try to flush it again if nothing is left it will return fast here channel.doFlushByteBuffer(buf); @@ -217,7 +218,7 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne @Override protected void failed0(Throwable cause, AioSocketChannel channel) { if (cause instanceof AsynchronousCloseException) { - channel.closed.set(true); + channel.closed = true; } channel.notifyFlushFutures(cause); @@ -232,7 +233,7 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne } } // Allow to have the next write pending - channel.flushing.set(false); + channel.flushing = false; } } @@ -263,7 +264,7 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne } catch (Throwable t) { if (t instanceof AsynchronousCloseException) { - channel.closed.set(true); + channel.closed = true; } if (read) { @@ -293,7 +294,7 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne @Override protected void failed0(Throwable t, AioSocketChannel channel) { if (t instanceof AsynchronousCloseException) { - channel.closed.set(true); + channel.closed = true; // TODO: This seems wrong! return; @@ -323,7 +324,7 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne @Override protected void failed0(Throwable exc, AioSocketChannel channel) { if (exc instanceof AsynchronousCloseException) { - channel.closed.set(true); + channel.closed = true; } ((AsyncUnsafe) channel.unsafe()).connectFailed(exc); } From aa3be3e1e8746380a30dc49ee926f3087c137047 Mon Sep 17 00:00:00 2001 From: Trustin Lee Date: Wed, 13 Jun 2012 14:03:57 +0900 Subject: [PATCH 26/87] Build all-in-one JAR on non-fullbuild. --- all/pom.xml | 160 ++++++++++++++++++++++++++++------------------------ pom.xml | 15 +++-- 2 files changed, 94 insertions(+), 81 deletions(-) diff --git a/all/pom.xml b/all/pom.xml index c2dd20703b..e651924e63 100644 --- a/all/pom.xml +++ b/all/pom.xml @@ -171,81 +171,6 @@ - - maven-jxr-plugin - 2.2 - - - generate-xref - package - - jxr - - - - - UTF-8 - UTF-8 - true - ${project.build.directory}/xref - ${project.build.directory}/api - Netty Source Xref (${project.version}) - Netty Source Xref (${project.version}) - - - - maven-javadoc-plugin - 2.8.1 - - - javadoc - package - - jar - - - false - *.internal,*.example - org.jboss.apiviz.APIviz - ${project.basedir}/lib/apiviz-1.3.1-jdk7.jar - true - true - ${project.build.directory}/api - ${project.build.directory} - api - UTF-8 - UTF-8 - true - false - false - true - ${basedir}/src/javadoc/overview.html - Netty API Reference (${project.version}) - Netty API Reference (${project.version}) - - -link http://docs.oracle.com/javase/7/docs/api/ - -link http://code.google.com/apis/protocolbuffers/docs/reference/java/ - -link http://docs.oracle.com/javaee/6/api/ - -link http://www.osgi.org/javadoc/r4v43/core/ - -link http://www.slf4j.org/apidocs/ - -link http://commons.apache.org/logging/commons-logging-1.1.1/apidocs/ - -link http://logging.apache.org/log4j/1.2/apidocs/ - - -group "Low-level data representation" io.netty.buffer* - -group "Central interface for all I/O operations" io.netty.channel* - -group "Client & Server bootstrapping utilities" io.netty.bootstrap* - -group "Reusable I/O event interceptors" io.netty.handler* - -group "Miscellaneous" io.netty.logging*:io.netty.util* - - -sourceclasspath ${project.build.outputDirectory} - -nopackagediagram - - UTF-8 - en_US - - - - @@ -307,5 +232,90 @@ + + + + fullbuild + + + + maven-jxr-plugin + 2.2 + + + generate-xref + package + + jxr + + + + + UTF-8 + UTF-8 + true + ${project.build.directory}/xref + ${project.build.directory}/api + Netty Source Xref (${project.version}) + Netty Source Xref (${project.version}) + + + + maven-javadoc-plugin + 2.8.1 + + + javadoc + package + + jar + + + false + *.internal,*.example + org.jboss.apiviz.APIviz + ${project.basedir}/lib/apiviz-1.3.1-jdk7.jar + true + true + ${project.build.directory}/api + ${project.build.directory} + api + UTF-8 + UTF-8 + true + false + false + true + ${basedir}/src/javadoc/overview.html + Netty API Reference (${project.version}) + Netty API Reference (${project.version}) + + -link http://docs.oracle.com/javase/7/docs/api/ + -link http://code.google.com/apis/protocolbuffers/docs/reference/java/ + -link http://docs.oracle.com/javaee/6/api/ + -link http://www.osgi.org/javadoc/r4v43/core/ + -link http://www.slf4j.org/apidocs/ + -link http://commons.apache.org/logging/commons-logging-1.1.1/apidocs/ + -link http://logging.apache.org/log4j/1.2/apidocs/ + + -group "Low-level data representation" io.netty.buffer* + -group "Central interface for all I/O operations" io.netty.channel* + -group "Client & Server bootstrapping utilities" io.netty.bootstrap* + -group "Reusable I/O event interceptors" io.netty.handler* + -group "Miscellaneous" io.netty.logging*:io.netty.util* + + -sourceclasspath ${project.build.outputDirectory} + -nopackagediagram + + UTF-8 + en_US + + + + + + + + diff --git a/pom.xml b/pom.xml index 647ed5e365..e1e0324493 100644 --- a/pom.xml +++ b/pom.xml @@ -66,6 +66,10 @@ + + 1.3.14.GA + + common buffer @@ -75,15 +79,18 @@ handler example testsuite + all - + fullbuild + @@ -217,10 +224,6 @@ - - 1.3.14.GA - - From 98fb86fbf2683a4d313b2d539a440b7ebcc2f122 Mon Sep 17 00:00:00 2001 From: Trustin Lee Date: Wed, 13 Jun 2012 17:12:51 +0900 Subject: [PATCH 27/87] Use build-helper-maven-plugin instead of messy hack --- all/.gitignore | 1 - all/pom.xml | 57 +++++++++++++++++++------------------------------- 2 files changed, 22 insertions(+), 36 deletions(-) delete mode 100644 all/.gitignore diff --git a/all/.gitignore b/all/.gitignore deleted file mode 100644 index 85de9cf933..0000000000 --- a/all/.gitignore +++ /dev/null @@ -1 +0,0 @@ -src diff --git a/all/pom.xml b/all/pom.xml index e651924e63..e9ceba465b 100644 --- a/all/pom.xml +++ b/all/pom.xml @@ -95,44 +95,11 @@ 2.5 - clean-src - clean - - clean - - - - - ${project.basedir}/src - false - false - - - ${project.basedir}/target - false - - - - - - clean-all + clean-first generate-resources clean - - - - ${project.basedir}/src - false - false - - - ${project.basedir}/target - false - - - @@ -150,7 +117,7 @@ sources io/netty/** io.netty - ${project.basedir}/src/main/java + ${project.build.directory}/src false true @@ -171,6 +138,25 @@ + + org.codehaus.mojo + build-helper-maven-plugin + 1.7 + + + add-source + generate-sources + + add-source + + + + target/src + + + + + @@ -289,6 +275,7 @@ ${basedir}/src/javadoc/overview.html Netty API Reference (${project.version}) Netty API Reference (${project.version}) + false -link http://docs.oracle.com/javase/7/docs/api/ -link http://code.google.com/apis/protocolbuffers/docs/reference/java/ From 4c2893e95297fdb1f5c4c3164fb696a381226846 Mon Sep 17 00:00:00 2001 From: Trustin Lee Date: Wed, 13 Jun 2012 18:30:31 +0900 Subject: [PATCH 28/87] Add the tarball module - Activated only when -Pfullbuild option is set or during release --- all/pom.xml | 7 +- pom.xml | 2 - tarball/assembly.xml | 44 +++++++++++ tarball/pom.xml | 175 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 220 insertions(+), 8 deletions(-) create mode 100644 tarball/assembly.xml create mode 100644 tarball/pom.xml diff --git a/all/pom.xml b/all/pom.xml index e9ceba465b..575ed00df4 100644 --- a/all/pom.xml +++ b/all/pom.xml @@ -116,10 +116,7 @@ sources io/netty/** - io.netty - ${project.build.directory}/src - false - true + ${project.groupId} @@ -132,8 +129,6 @@ io/netty/** io/netty/example/** ${project.build.outputDirectory} - false - true diff --git a/pom.xml b/pom.xml index e1e0324493..4cbb1b4f07 100644 --- a/pom.xml +++ b/pom.xml @@ -86,11 +86,9 @@ fullbuild - diff --git a/tarball/assembly.xml b/tarball/assembly.xml new file mode 100644 index 0000000000..f33866fb56 --- /dev/null +++ b/tarball/assembly.xml @@ -0,0 +1,44 @@ + + + tarball + + tar.bz2 + + false + + + ${project.basedir}/.. + + + *.txt + *.md + + + + ${project.basedir}/.. + + + license/**/*.txt + license/**/*.md + + + + target/jars + jar + + **/*.jar + + + + target/jars-all + jar/all-in-one + + **/*.jar + + + + + diff --git a/tarball/pom.xml b/tarball/pom.xml new file mode 100644 index 0000000000..61cbe62fdf --- /dev/null +++ b/tarball/pom.xml @@ -0,0 +1,175 @@ + + + + + 4.0.0 + + io.netty + netty-parent + 4.0.0.Alpha1-SNAPSHOT + + + netty-tarball + pom + + Netty/Tarball + + + + + ${project.groupId} + netty-example + ${project.version} + compile + true + + + + ${project.groupId} + netty + ${project.version} + compile + true + + + + + + + maven-clean-plugin + 2.5 + + + clean-first + generate-resources + + clean + + + + + + maven-dependency-plugin + 2.4 + + + copy-jars + generate-resources + + copy-dependencies + + + ${project.build.directory}/jars + ${project.groupId} + netty + + + + copy-sources + generate-resources + + copy-dependencies + + + sources + ${project.build.directory}/jars + ${project.groupId} + netty + + + + copy-jars-all-in-one + generate-resources + + copy-dependencies + + + ${project.build.directory}/jars-all + ${project.groupId} + netty + + + + copy-sources-all-in-one + generate-resources + + copy-dependencies + + + sources + ${project.build.directory}/jars-all + ${project.groupId} + netty + + + + + + maven-assembly-plugin + 2.3 + + + build-tarball + package + + single + + + netty-${project.version} + false + false + + ${project.basedir}/assembly.xml + + + + + + + + + org.codehaus.mojo + animal-sniffer-maven-plugin + + + default + none + + + + + + maven-checkstyle-plugin + + + check-style + none + + + + + + maven-deploy-plugin + 2.7 + + true + + + + + + From 0ee0751fec20014f9606050120efc4f2361a5f8c Mon Sep 17 00:00:00 2001 From: Trustin Lee Date: Wed, 13 Jun 2012 18:47:57 +0900 Subject: [PATCH 29/87] Fix a problem where source jar is not attached --- all/pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/all/pom.xml b/all/pom.xml index 575ed00df4..957257bea5 100644 --- a/all/pom.xml +++ b/all/pom.xml @@ -117,6 +117,7 @@ sources io/netty/** ${project.groupId} + ${project.build.directory}/src From 41a9c66f669a08cc6af67f996719a9f53a482583 Mon Sep 17 00:00:00 2001 From: Trustin Lee Date: Wed, 13 Jun 2012 18:51:44 +0900 Subject: [PATCH 30/87] fullbuild -> full --- all/pom.xml | 2 +- pom.xml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/all/pom.xml b/all/pom.xml index 957257bea5..a1d12565aa 100644 --- a/all/pom.xml +++ b/all/pom.xml @@ -217,7 +217,7 @@ - fullbuild + full diff --git a/pom.xml b/pom.xml index 4cbb1b4f07..98da41cd56 100644 --- a/pom.xml +++ b/pom.xml @@ -85,7 +85,7 @@ - fullbuild + full tarball @@ -353,7 +353,7 @@ maven-release-plugin 2.3.1 - release,fullbuild + release,full From c165a38e15c93a0b8345aa46c678cc6cbb5d4fa6 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Wed, 13 Jun 2012 22:24:32 +0200 Subject: [PATCH 31/87] Revert as it should be in nio2 branch "Commit first round of classes to support nio2/async channel api. Still work in progress.. See #396" This reverts commit 18aaae3c2e7462853298c991c7f95040e47dae96. --- pom.xml | 4 ++- .../io/netty/channel/AbstractChannel.java | 30 +++++++++---------- .../netty/channel/AbstractServerChannel.java | 2 +- .../channel/embedded/EmbeddedByteChannel.java | 3 +- .../socket/nio/AbstractNioByteChannel.java | 5 ++-- .../socket/oio/AbstractOioByteChannel.java | 3 +- 6 files changed, 22 insertions(+), 25 deletions(-) diff --git a/pom.xml b/pom.xml index 98da41cd56..20ef2a7f56 100644 --- a/pom.xml +++ b/pom.xml @@ -276,12 +276,14 @@ sun.misc.Unsafe java.util.zip.Deflater + java.nio.channels.DatagramChannel java.nio.channels.MembershipKey java.net.StandardSocketOptions java.net.StandardProtocolFamily - + + java.nio.channels.AsynchronousChannel java.nio.channels.AsynchronousSocketChannel java.nio.channels.AsynchronousServerSocketChannel diff --git a/transport/src/main/java/io/netty/channel/AbstractChannel.java b/transport/src/main/java/io/netty/channel/AbstractChannel.java index 98d7fb7aff..42890f3c8b 100755 --- a/transport/src/main/java/io/netty/channel/AbstractChannel.java +++ b/transport/src/main/java/io/netty/channel/AbstractChannel.java @@ -85,7 +85,7 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha private ClosedChannelException closedChannelException; private final Deque flushCheckpoints = new ArrayDeque(); private long writeCounter; - protected boolean inFlushNow; + private boolean inFlushNow; private boolean flushNowPending; /** Cache for the string representation of this channel */ @@ -623,7 +623,7 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha } @Override - public void flushNow() { + public final void flushNow() { if (inFlushNow) { return; } @@ -631,13 +631,12 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha inFlushNow = true; ChannelHandlerContext ctx = directOutboundContext(); Throwable cause = null; - boolean handleFlush = true; try { if (ctx.hasOutboundByteBuffer()) { ByteBuf out = ctx.outboundByteBuffer(); int oldSize = out.readableBytes(); try { - handleFlush = doFlushByteBuffer(out); + doFlushByteBuffer(out); } catch (Throwable t) { cause = t; } finally { @@ -658,15 +657,14 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha writeCounter += oldSize - out.size(); } } - if (handleFlush) { - if (cause == null) { - notifyFlushFutures(); - } else { - notifyFlushFutures(cause); - pipeline.fireExceptionCaught(cause); - if (cause instanceof IOException) { - close(voidFuture()); - } + + if (cause == null) { + notifyFlushFutures(); + } else { + notifyFlushFutures(cause); + pipeline.fireExceptionCaught(cause); + if (cause instanceof IOException) { + close(voidFuture()); } } } finally { @@ -715,7 +713,7 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha protected abstract void doClose() throws Exception; protected abstract void doDeregister() throws Exception; - protected boolean doFlushByteBuffer(ByteBuf buf) throws Exception { + protected void doFlushByteBuffer(ByteBuf buf) throws Exception { throw new UnsupportedOperationException(); } protected void doFlushMessageBuffer(MessageBuf buf) throws Exception { @@ -724,7 +722,7 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha protected abstract boolean isFlushPending(); - protected final void notifyFlushFutures() { + private void notifyFlushFutures() { if (flushCheckpoints.isEmpty()) { return; } @@ -762,7 +760,7 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha } } - protected final void notifyFlushFutures(Throwable cause) { + private void notifyFlushFutures(Throwable cause) { notifyFlushFutures(); for (;;) { FlushCheckpoint cp = flushCheckpoints.poll(); diff --git a/transport/src/main/java/io/netty/channel/AbstractServerChannel.java b/transport/src/main/java/io/netty/channel/AbstractServerChannel.java index 6ac92e60b3..0c42167eeb 100755 --- a/transport/src/main/java/io/netty/channel/AbstractServerChannel.java +++ b/transport/src/main/java/io/netty/channel/AbstractServerChannel.java @@ -77,7 +77,7 @@ public abstract class AbstractServerChannel extends AbstractChannel implements S } @Override - protected boolean doFlushByteBuffer(ByteBuf buf) throws Exception { + protected void doFlushByteBuffer(ByteBuf buf) throws Exception { throw new UnsupportedOperationException(); } diff --git a/transport/src/main/java/io/netty/channel/embedded/EmbeddedByteChannel.java b/transport/src/main/java/io/netty/channel/embedded/EmbeddedByteChannel.java index 8c1f7e3499..07e8ac5313 100755 --- a/transport/src/main/java/io/netty/channel/embedded/EmbeddedByteChannel.java +++ b/transport/src/main/java/io/netty/channel/embedded/EmbeddedByteChannel.java @@ -71,11 +71,10 @@ public class EmbeddedByteChannel extends AbstractEmbeddedChannel { } @Override - protected boolean doFlushByteBuffer(ByteBuf buf) throws Exception { + protected void doFlushByteBuffer(ByteBuf buf) throws Exception { if (!lastOutboundBuffer().readable()) { lastOutboundBuffer().discardReadBytes(); } lastOutboundBuffer().writeBytes(buf); - return true; } } diff --git a/transport/src/main/java/io/netty/channel/socket/nio/AbstractNioByteChannel.java b/transport/src/main/java/io/netty/channel/socket/nio/AbstractNioByteChannel.java index 99489e3ae7..fded47226c 100755 --- a/transport/src/main/java/io/netty/channel/socket/nio/AbstractNioByteChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/nio/AbstractNioByteChannel.java @@ -85,11 +85,11 @@ abstract class AbstractNioByteChannel extends AbstractNioChannel { } @Override - protected boolean doFlushByteBuffer(ByteBuf buf) throws Exception { + protected void doFlushByteBuffer(ByteBuf buf) throws Exception { if (!buf.readable()) { // Reset reader/writerIndex to 0 if the buffer is empty. buf.clear(); - return true; + return; } for (int i = config().getWriteSpinCount() - 1; i >= 0; i --) { @@ -103,7 +103,6 @@ abstract class AbstractNioByteChannel extends AbstractNioChannel { break; } } - return true; } protected abstract int doReadBytes(ByteBuf buf) throws Exception; diff --git a/transport/src/main/java/io/netty/channel/socket/oio/AbstractOioByteChannel.java b/transport/src/main/java/io/netty/channel/socket/oio/AbstractOioByteChannel.java index 563286b3b9..12697f74aa 100755 --- a/transport/src/main/java/io/netty/channel/socket/oio/AbstractOioByteChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/oio/AbstractOioByteChannel.java @@ -86,12 +86,11 @@ abstract class AbstractOioByteChannel extends AbstractOioChannel { } @Override - protected boolean doFlushByteBuffer(ByteBuf buf) throws Exception { + protected void doFlushByteBuffer(ByteBuf buf) throws Exception { while (buf.readable()) { doWriteBytes(buf); } buf.clear(); - return true; } protected abstract int available(); From faf529166fa4298aaaa5a42528aa74091a7dfc4a Mon Sep 17 00:00:00 2001 From: Trustin Lee Date: Sun, 17 Jun 2012 11:17:16 +0900 Subject: [PATCH 32/87] Increase the timeout of LocalTransportThreadModelTest to 1 minute --- .../io/netty/channel/local/LocalTransportThreadModelTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/transport/src/test/java/io/netty/channel/local/LocalTransportThreadModelTest.java b/transport/src/test/java/io/netty/channel/local/LocalTransportThreadModelTest.java index fdb4a6a300..e11908e27a 100755 --- a/transport/src/test/java/io/netty/channel/local/LocalTransportThreadModelTest.java +++ b/transport/src/test/java/io/netty/channel/local/LocalTransportThreadModelTest.java @@ -204,7 +204,7 @@ public class LocalTransportThreadModelTest { } } - @Test(timeout = 30000) + @Test(timeout = 60000) public void testConcurrentMessageBufferAccess() throws Throwable { EventLoop l = new LocalEventLoop(4, new PrefixThreadFactory("l")); EventExecutor e1 = new DefaultEventExecutor(4, new PrefixThreadFactory("e1")); From 7a4e3742941b7fbe476403161d8a25f3f524430c Mon Sep 17 00:00:00 2001 From: Trustin Lee Date: Mon, 18 Jun 2012 13:38:59 +0900 Subject: [PATCH 33/87] Fix a bug where timeout handlers sometimes generate events too early --- .../handler/timeout/IdleStateHandler.java | 23 ++++++++++++---- .../handler/timeout/ReadTimeoutHandler.java | 26 ++++++++++++++----- 2 files changed, 37 insertions(+), 12 deletions(-) diff --git a/handler/src/main/java/io/netty/handler/timeout/IdleStateHandler.java b/handler/src/main/java/io/netty/handler/timeout/IdleStateHandler.java index 6e3df84265..0a4d177d0b 100644 --- a/handler/src/main/java/io/netty/handler/timeout/IdleStateHandler.java +++ b/handler/src/main/java/io/netty/handler/timeout/IdleStateHandler.java @@ -129,7 +129,7 @@ public class IdleStateHandler extends ChannelHandlerAdapter { volatile ScheduledFuture allIdleTimeout; int allIdleCount; - volatile boolean destroyed; + private volatile int state; // 0 - none, 1 - initialized, 2 - destroyed /** * Creates a new instance. @@ -202,12 +202,12 @@ public class IdleStateHandler extends ChannelHandlerAdapter { @Override public void beforeAdd(ChannelHandlerContext ctx) throws Exception { - if (ctx.channel().isActive()) { + if (ctx.channel().isActive() & ctx.channel().isRegistered()) { // channelActvie() event has been fired already, which means this.channelActive() will // not be invoked. We have to initialize here instead. initialize(ctx); } else { - // channelActive() event has not been fired yet. this.channelOpen() will be invoked + // channelActive() event has not been fired yet. this.channelActive() will be invoked // and initialization will occur there. } } @@ -217,6 +217,15 @@ public class IdleStateHandler extends ChannelHandlerAdapter { destroy(); } + @Override + public void channelRegistered(ChannelHandlerContext ctx) throws Exception { + // Initialize early if channel is active already. + if (ctx.channel().isActive()) { + initialize(ctx); + } + super.channelRegistered(ctx); + } + @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { // This method will be invoked only if this handler was added @@ -256,10 +265,14 @@ public class IdleStateHandler extends ChannelHandlerAdapter { private void initialize(ChannelHandlerContext ctx) { // Avoid the case where destroy() is called before scheduling timeouts. // See: https://github.com/netty/netty/issues/143 - if (destroyed) { + switch (state) { + case 1: + case 2: return; } + state = 1; + EventExecutor loop = ctx.executor(); lastReadTime = lastWriteTime = System.currentTimeMillis(); @@ -281,7 +294,7 @@ public class IdleStateHandler extends ChannelHandlerAdapter { } private void destroy() { - destroyed = true; + state = 2; if (readerIdleTimeout != null) { readerIdleTimeout.cancel(false); diff --git a/handler/src/main/java/io/netty/handler/timeout/ReadTimeoutHandler.java b/handler/src/main/java/io/netty/handler/timeout/ReadTimeoutHandler.java index 99816a7c9c..eb998744da 100644 --- a/handler/src/main/java/io/netty/handler/timeout/ReadTimeoutHandler.java +++ b/handler/src/main/java/io/netty/handler/timeout/ReadTimeoutHandler.java @@ -74,7 +74,7 @@ public class ReadTimeoutHandler extends ChannelStateHandlerAdapter { private volatile ScheduledFuture timeout; private volatile long lastReadTime; - private volatile boolean destroyed; + private volatile int state; // 0 - none, 1 - Initialized, 2 - Destroyed; private boolean closed; @@ -110,12 +110,12 @@ public class ReadTimeoutHandler extends ChannelStateHandlerAdapter { @Override public void beforeAdd(ChannelHandlerContext ctx) throws Exception { - if (ctx.channel().isActive()) { + if (ctx.channel().isActive() && ctx.channel().isRegistered()) { // channelActvie() event has been fired already, which means this.channelActive() will // not be invoked. We have to initialize here instead. initialize(ctx); } else { - // channelActive() event has not been fired yet. this.channelOpen() will be invoked + // channelActive() event has not been fired yet. this.channelActive() will be invoked // and initialization will occur there. } } @@ -126,8 +126,16 @@ public class ReadTimeoutHandler extends ChannelStateHandlerAdapter { } @Override - public void channelActive(ChannelHandlerContext ctx) - throws Exception { + public void channelRegistered(ChannelHandlerContext ctx) throws Exception { + // Initialize early if channel is active already. + if (ctx.channel().isActive()) { + initialize(ctx); + } + super.channelRegistered(ctx); + } + + @Override + public void channelActive(ChannelHandlerContext ctx) throws Exception { // This method will be invoked only if this handler was added // before channelActive() event is fired. If a user adds this handler // after the channelActive() event, initialize() will be called by beforeAdd(). @@ -150,10 +158,14 @@ public class ReadTimeoutHandler extends ChannelStateHandlerAdapter { private void initialize(ChannelHandlerContext ctx) { // Avoid the case where destroy() is called before scheduling timeouts. // See: https://github.com/netty/netty/issues/143 - if (destroyed) { + switch (state) { + case 1: + case 2: return; } + state = 1; + lastReadTime = System.currentTimeMillis(); if (timeoutMillis > 0) { timeout = ctx.executor().schedule( @@ -163,7 +175,7 @@ public class ReadTimeoutHandler extends ChannelStateHandlerAdapter { } private void destroy() { - destroyed = true; + state = 2; if (timeout != null) { timeout.cancel(false); From 32188f83acaaba30a13fd266f47c3e7c9a0c0855 Mon Sep 17 00:00:00 2001 From: Trustin Lee Date: Tue, 19 Jun 2012 09:54:25 +0900 Subject: [PATCH 34/87] Forward-port JDK ZlibEncoder patch (#404) - Rename ZlibEncoder/Decoder to JZlibEncoder/Decoder - Define a new ZlibEncoder/Decoder class - Add JdkZlibEncoder - All JZlib* and JdkZlib* extends ZlibEncoder/Decoder - Add ZlibCodecFactory and use it everywhere --- .../codec/http/HttpContentCompressor.java | 6 +- .../codec/http/HttpContentDecompressor.java | 6 +- .../codec/compression/JZlibDecoder.java | 178 +++++++ .../codec/compression/JZlibEncoder.java | 434 ++++++++++++++++++ .../codec/compression/JdkZlibEncoder.java | 296 ++++++++++++ .../codec/compression/ZlibCodecFactory.java | 96 ++++ .../codec/compression/ZlibDecoder.java | 156 +------ .../codec/compression/ZlibEncoder.java | 385 +--------------- .../handler/codec/compression/ZlibUtil.java | 2 +- .../factorial/FactorialClientInitializer.java | 7 +- .../factorial/FactorialServerInitializer.java | 7 +- .../PortUnificationServerHandler.java | 7 +- 12 files changed, 1031 insertions(+), 549 deletions(-) create mode 100644 codec/src/main/java/io/netty/handler/codec/compression/JZlibDecoder.java create mode 100644 codec/src/main/java/io/netty/handler/codec/compression/JZlibEncoder.java create mode 100644 codec/src/main/java/io/netty/handler/codec/compression/JdkZlibEncoder.java create mode 100644 codec/src/main/java/io/netty/handler/codec/compression/ZlibCodecFactory.java diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentCompressor.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentCompressor.java index 2c8b917640..cf85a2f51e 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentCompressor.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentCompressor.java @@ -16,7 +16,7 @@ package io.netty.handler.codec.http; import io.netty.channel.embedded.EmbeddedByteChannel; -import io.netty.handler.codec.compression.ZlibEncoder; +import io.netty.handler.codec.compression.ZlibCodecFactory; import io.netty.handler.codec.compression.ZlibWrapper; /** @@ -118,8 +118,8 @@ public class HttpContentCompressor extends HttpContentEncoder { return new Result( targetContentEncoding, - new EmbeddedByteChannel( - new ZlibEncoder(wrapper, compressionLevel, windowBits, memLevel))); + new EmbeddedByteChannel(ZlibCodecFactory.newZlibEncoder( + wrapper, compressionLevel, windowBits, memLevel))); } protected ZlibWrapper determineWrapper(String acceptEncoding) { diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentDecompressor.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentDecompressor.java index 8911d85505..b4b6053132 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentDecompressor.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentDecompressor.java @@ -16,7 +16,7 @@ package io.netty.handler.codec.http; import io.netty.channel.embedded.EmbeddedByteChannel; -import io.netty.handler.codec.compression.ZlibDecoder; +import io.netty.handler.codec.compression.ZlibCodecFactory; import io.netty.handler.codec.compression.ZlibWrapper; /** @@ -28,10 +28,10 @@ public class HttpContentDecompressor extends HttpContentDecoder { @Override protected EmbeddedByteChannel newContentDecoder(String contentEncoding) throws Exception { if ("gzip".equalsIgnoreCase(contentEncoding) || "x-gzip".equalsIgnoreCase(contentEncoding)) { - return new EmbeddedByteChannel(new ZlibDecoder(ZlibWrapper.GZIP)); + return new EmbeddedByteChannel(ZlibCodecFactory.newZlibDecoder(ZlibWrapper.GZIP)); } else if ("deflate".equalsIgnoreCase(contentEncoding) || "x-deflate".equalsIgnoreCase(contentEncoding)) { // To be strict, 'deflate' means ZLIB, but some servers were not implemented correctly. - return new EmbeddedByteChannel(new ZlibDecoder(ZlibWrapper.ZLIB_OR_NONE)); + return new EmbeddedByteChannel(ZlibCodecFactory.newZlibDecoder(ZlibWrapper.ZLIB_OR_NONE)); } // 'identity' or unsupported diff --git a/codec/src/main/java/io/netty/handler/codec/compression/JZlibDecoder.java b/codec/src/main/java/io/netty/handler/codec/compression/JZlibDecoder.java new file mode 100644 index 0000000000..ddd12a527a --- /dev/null +++ b/codec/src/main/java/io/netty/handler/codec/compression/JZlibDecoder.java @@ -0,0 +1,178 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.codec.compression; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.util.internal.jzlib.JZlib; +import io.netty.util.internal.jzlib.ZStream; + +public class JZlibDecoder extends ZlibDecoder { + + private final ZStream z = new ZStream(); + private byte[] dictionary; + private volatile boolean finished; + + /** + * Creates a new instance with the default wrapper ({@link ZlibWrapper#ZLIB}). + * + * @throws CompressionException if failed to initialize zlib + */ + public JZlibDecoder() { + this(ZlibWrapper.ZLIB); + } + + /** + * Creates a new instance with the specified wrapper. + * + * @throws CompressionException if failed to initialize zlib + */ + public JZlibDecoder(ZlibWrapper wrapper) { + if (wrapper == null) { + throw new NullPointerException("wrapper"); + } + + int resultCode = z.inflateInit(ZlibUtil.convertWrapperType(wrapper)); + if (resultCode != JZlib.Z_OK) { + ZlibUtil.fail(z, "initialization failure", resultCode); + } + } + + /** + * Creates a new instance with the specified preset dictionary. The wrapper + * is always {@link ZlibWrapper#ZLIB} because it is the only format that + * supports the preset dictionary. + * + * @throws CompressionException if failed to initialize zlib + */ + public JZlibDecoder(byte[] dictionary) { + if (dictionary == null) { + throw new NullPointerException("dictionary"); + } + this.dictionary = dictionary; + + int resultCode; + resultCode = z.inflateInit(JZlib.W_ZLIB); + if (resultCode != JZlib.Z_OK) { + ZlibUtil.fail(z, "initialization failure", resultCode); + } + } + + /** + * Returns {@code true} if and only if the end of the compressed stream + * has been reached. + */ + @Override + public boolean isClosed() { + return finished; + } + + @Override + public void decode( + ChannelHandlerContext ctx, + ByteBuf in, ByteBuf out) throws Exception { + + if (!in.readable()) { + return; + } + + try { + // Configure input. + int inputLength = in.readableBytes(); + boolean inHasArray = in.hasArray(); + z.avail_in = inputLength; + if (inHasArray) { + z.next_in = in.array(); + z.next_in_index = in.arrayOffset() + in.readerIndex(); + } else { + byte[] array = new byte[inputLength]; + in.readBytes(array); + z.next_in = array; + z.next_in_index = 0; + } + int oldNextInIndex = z.next_in_index; + + // Configure output. + int maxOutputLength = inputLength << 1; + boolean outHasArray = out.hasArray(); + if (!outHasArray) { + z.next_out = new byte[maxOutputLength]; + } + + try { + loop: for (;;) { + z.avail_out = maxOutputLength; + if (outHasArray) { + out.ensureWritableBytes(maxOutputLength); + z.next_out = out.array(); + z.next_out_index = out.arrayOffset() + out.writerIndex(); + } else { + z.next_out_index = 0; + } + int oldNextOutIndex = z.next_out_index; + + // Decompress 'in' into 'out' + int resultCode = z.inflate(JZlib.Z_SYNC_FLUSH); + int outputLength = z.next_out_index - oldNextOutIndex; + if (outputLength > 0) { + if (outHasArray) { + out.writerIndex(out.writerIndex() + outputLength); + } else { + out.writeBytes(z.next_out, 0, outputLength); + } + } + + switch (resultCode) { + case JZlib.Z_NEED_DICT: + if (dictionary == null) { + ZlibUtil.fail(z, "decompression failure", resultCode); + } else { + resultCode = z.inflateSetDictionary(dictionary, dictionary.length); + if (resultCode != JZlib.Z_OK) { + ZlibUtil.fail(z, "failed to set the dictionary", resultCode); + } + } + break; + case JZlib.Z_STREAM_END: + finished = true; // Do not decode anymore. + z.inflateEnd(); + break loop; + case JZlib.Z_OK: + break; + case JZlib.Z_BUF_ERROR: + if (z.avail_in <= 0) { + break loop; + } + break; + default: + ZlibUtil.fail(z, "decompression failure", resultCode); + } + } + } finally { + if (inHasArray) { + in.skipBytes(z.next_in_index - oldNextInIndex); + } + } + } finally { + // Deference the external references explicitly to tell the VM that + // the allocated byte arrays are temporary so that the call stack + // can be utilized. + // I'm not sure if the modern VMs do this optimization though. + z.next_in = null; + z.next_out = null; + } + } +} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/JZlibEncoder.java b/codec/src/main/java/io/netty/handler/codec/compression/JZlibEncoder.java new file mode 100644 index 0000000000..a5701630ff --- /dev/null +++ b/codec/src/main/java/io/netty/handler/codec/compression/JZlibEncoder.java @@ -0,0 +1,434 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.codec.compression; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelHandlerContext; +import io.netty.util.internal.jzlib.JZlib; +import io.netty.util.internal.jzlib.ZStream; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + + +/** + * Compresses a {@link ByteBuf} using the deflate algorithm. + * @apiviz.landmark + * @apiviz.has io.netty.handler.codec.compression.ZlibWrapper + */ +public class JZlibEncoder extends ZlibEncoder { + + private static final byte[] EMPTY_ARRAY = new byte[0]; + + private final ZStream z = new ZStream(); + private final AtomicBoolean finished = new AtomicBoolean(); + private volatile ChannelHandlerContext ctx; + + /** + * Creates a new zlib encoder with the default compression level ({@code 6}), + * default window bits ({@code 15}), default memory level ({@code 8}), + * and the default wrapper ({@link ZlibWrapper#ZLIB}). + * + * @throws CompressionException if failed to initialize zlib + */ + public JZlibEncoder() { + this(6); + } + + /** + * Creates a new zlib encoder with the specified {@code compressionLevel}, + * default window bits ({@code 15}), default memory level ({@code 8}), + * and the default wrapper ({@link ZlibWrapper#ZLIB}). + * + * @param compressionLevel + * {@code 1} yields the fastest compression and {@code 9} yields the + * best compression. {@code 0} means no compression. The default + * compression level is {@code 6}. + * + * @throws CompressionException if failed to initialize zlib + */ + public JZlibEncoder(int compressionLevel) { + this(ZlibWrapper.ZLIB, compressionLevel); + } + + /** + * Creates a new zlib encoder with the default compression level ({@code 6}), + * default window bits ({@code 15}), default memory level ({@code 8}), + * and the specified wrapper. + * + * @throws CompressionException if failed to initialize zlib + */ + public JZlibEncoder(ZlibWrapper wrapper) { + this(wrapper, 6); + } + + /** + * Creates a new zlib encoder with the specified {@code compressionLevel}, + * default window bits ({@code 15}), default memory level ({@code 8}), + * and the specified wrapper. + * + * @param compressionLevel + * {@code 1} yields the fastest compression and {@code 9} yields the + * best compression. {@code 0} means no compression. The default + * compression level is {@code 6}. + * + * @throws CompressionException if failed to initialize zlib + */ + public JZlibEncoder(ZlibWrapper wrapper, int compressionLevel) { + this(wrapper, compressionLevel, 15, 8); + } + + /** + * Creates a new zlib encoder with the specified {@code compressionLevel}, + * the specified {@code windowBits}, the specified {@code memLevel}, and + * the specified wrapper. + * + * @param compressionLevel + * {@code 1} yields the fastest compression and {@code 9} yields the + * best compression. {@code 0} means no compression. The default + * compression level is {@code 6}. + * @param windowBits + * The base two logarithm of the size of the history buffer. The + * value should be in the range {@code 9} to {@code 15} inclusive. + * Larger values result in better compression at the expense of + * memory usage. The default value is {@code 15}. + * @param memLevel + * How much memory should be allocated for the internal compression + * state. {@code 1} uses minimum memory and {@code 9} uses maximum + * memory. Larger values result in better and faster compression + * at the expense of memory usage. The default value is {@code 8} + * + * @throws CompressionException if failed to initialize zlib + */ + public JZlibEncoder(ZlibWrapper wrapper, int compressionLevel, int windowBits, int memLevel) { + + if (compressionLevel < 0 || compressionLevel > 9) { + throw new IllegalArgumentException( + "compressionLevel: " + compressionLevel + + " (expected: 0-9)"); + } + if (windowBits < 9 || windowBits > 15) { + throw new IllegalArgumentException( + "windowBits: " + windowBits + " (expected: 9-15)"); + } + if (memLevel < 1 || memLevel > 9) { + throw new IllegalArgumentException( + "memLevel: " + memLevel + " (expected: 1-9)"); + } + if (wrapper == null) { + throw new NullPointerException("wrapper"); + } + if (wrapper == ZlibWrapper.ZLIB_OR_NONE) { + throw new IllegalArgumentException( + "wrapper '" + ZlibWrapper.ZLIB_OR_NONE + "' is not " + + "allowed for compression."); + } + + synchronized (z) { + int resultCode = z.deflateInit( + compressionLevel, windowBits, memLevel, + ZlibUtil.convertWrapperType(wrapper)); + if (resultCode != JZlib.Z_OK) { + ZlibUtil.fail(z, "initialization failure", resultCode); + } + } + } + + /** + * Creates a new zlib encoder with the default compression level ({@code 6}), + * default window bits ({@code 15}), default memory level ({@code 8}), + * and the specified preset dictionary. The wrapper is always + * {@link ZlibWrapper#ZLIB} because it is the only format that supports + * the preset dictionary. + * + * @param dictionary the preset dictionary + * + * @throws CompressionException if failed to initialize zlib + */ + public JZlibEncoder(byte[] dictionary) { + this(6, dictionary); + } + + /** + * Creates a new zlib encoder with the specified {@code compressionLevel}, + * default window bits ({@code 15}), default memory level ({@code 8}), + * and the specified preset dictionary. The wrapper is always + * {@link ZlibWrapper#ZLIB} because it is the only format that supports + * the preset dictionary. + * + * @param compressionLevel + * {@code 1} yields the fastest compression and {@code 9} yields the + * best compression. {@code 0} means no compression. The default + * compression level is {@code 6}. + * @param dictionary the preset dictionary + * + * @throws CompressionException if failed to initialize zlib + */ + public JZlibEncoder(int compressionLevel, byte[] dictionary) { + this(compressionLevel, 15, 8, dictionary); + } + + /** + * Creates a new zlib encoder with the specified {@code compressionLevel}, + * the specified {@code windowBits}, the specified {@code memLevel}, + * and the specified preset dictionary. The wrapper is always + * {@link ZlibWrapper#ZLIB} because it is the only format that supports + * the preset dictionary. + * + * @param compressionLevel + * {@code 1} yields the fastest compression and {@code 9} yields the + * best compression. {@code 0} means no compression. The default + * compression level is {@code 6}. + * @param windowBits + * The base two logarithm of the size of the history buffer. The + * value should be in the range {@code 9} to {@code 15} inclusive. + * Larger values result in better compression at the expense of + * memory usage. The default value is {@code 15}. + * @param memLevel + * How much memory should be allocated for the internal compression + * state. {@code 1} uses minimum memory and {@code 9} uses maximum + * memory. Larger values result in better and faster compression + * at the expense of memory usage. The default value is {@code 8} + * @param dictionary the preset dictionary + * + * @throws CompressionException if failed to initialize zlib + */ + public JZlibEncoder(int compressionLevel, int windowBits, int memLevel, byte[] dictionary) { + if (compressionLevel < 0 || compressionLevel > 9) { + throw new IllegalArgumentException("compressionLevel: " + compressionLevel + " (expected: 0-9)"); + } + if (windowBits < 9 || windowBits > 15) { + throw new IllegalArgumentException( + "windowBits: " + windowBits + " (expected: 9-15)"); + } + if (memLevel < 1 || memLevel > 9) { + throw new IllegalArgumentException( + "memLevel: " + memLevel + " (expected: 1-9)"); + } + if (dictionary == null) { + throw new NullPointerException("dictionary"); + } + + synchronized (z) { + int resultCode; + resultCode = z.deflateInit( + compressionLevel, windowBits, memLevel, + JZlib.W_ZLIB); // Default: ZLIB format + if (resultCode != JZlib.Z_OK) { + ZlibUtil.fail(z, "initialization failure", resultCode); + } else { + resultCode = z.deflateSetDictionary(dictionary, dictionary.length); + if (resultCode != JZlib.Z_OK) { + ZlibUtil.fail(z, "failed to set the dictionary", resultCode); + } + } + } + } + + @Override + public ChannelFuture close() { + return close(ctx().channel().newFuture()); + } + + @Override + public ChannelFuture close(ChannelFuture future) { + return finishEncode(ctx(), future); + } + + private ChannelHandlerContext ctx() { + ChannelHandlerContext ctx = this.ctx; + if (ctx == null) { + throw new IllegalStateException("not added to a pipeline"); + } + return ctx; + } + + @Override + public boolean isClosed() { + return finished.get(); + } + + @Override + public void encode(ChannelHandlerContext ctx, + ByteBuf in, ByteBuf out) throws Exception { + if (finished.get()) { + return; + } + + synchronized (z) { + try { + // Configure input. + int inputLength = in.readableBytes(); + boolean inHasArray = in.hasArray(); + z.avail_in = inputLength; + if (inHasArray) { + z.next_in = in.array(); + z.next_in_index = in.arrayOffset() + in.readerIndex(); + } else { + byte[] array = new byte[inputLength]; + in.readBytes(array); + z.next_in = array; + z.next_in_index = 0; + } + int oldNextInIndex = z.next_in_index; + + // Configure output. + int maxOutputLength = (int) Math.ceil(inputLength * 1.001) + 12; + boolean outHasArray = out.hasArray(); + z.avail_out = maxOutputLength; + if (outHasArray) { + out.ensureWritableBytes(maxOutputLength); + z.next_out = out.array(); + z.next_out_index = out.arrayOffset() + out.writerIndex(); + } else { + z.next_out = new byte[maxOutputLength]; + z.next_out_index = 0; + } + int oldNextOutIndex = z.next_out_index; + + // Note that Z_PARTIAL_FLUSH has been deprecated. + int resultCode; + try { + resultCode = z.deflate(JZlib.Z_SYNC_FLUSH); + } finally { + if (inHasArray) { + in.skipBytes(z.next_in_index - oldNextInIndex); + } + } + + if (resultCode != JZlib.Z_OK) { + ZlibUtil.fail(z, "compression failure", resultCode); + } + + int outputLength = z.next_out_index - oldNextOutIndex; + if (outputLength > 0) { + if (outHasArray) { + out.writerIndex(out.writerIndex() + outputLength); + } else { + out.writeBytes(z.next_out, 0, outputLength); + } + } + } finally { + // Deference the external references explicitly to tell the VM that + // the allocated byte arrays are temporary so that the call stack + // can be utilized. + // I'm not sure if the modern VMs do this optimization though. + z.next_in = null; + z.next_out = null; + } + } + } + + @Override + public void disconnect( + final ChannelHandlerContext ctx, + final ChannelFuture future) throws Exception { + ChannelFuture f = finishEncode(ctx, ctx.newFuture()); + f.addListener(new ChannelFutureListener() { + @Override + public void operationComplete(ChannelFuture f) throws Exception { + ctx.disconnect(future); + } + }); + + if (!f.isDone()) { + // Ensure the channel is closed even if the write operation completes in time. + ctx.executor().schedule(new Runnable() { + @Override + public void run() { + ctx.disconnect(future); + } + }, 10, TimeUnit.SECONDS); // FIXME: Magic number + } + } + + @Override + public void close( + final ChannelHandlerContext ctx, + final ChannelFuture future) throws Exception { + ChannelFuture f = finishEncode(ctx, ctx.newFuture()); + f.addListener(new ChannelFutureListener() { + @Override + public void operationComplete(ChannelFuture f) throws Exception { + ctx.close(future); + } + }); + + if (!f.isDone()) { + // Ensure the channel is closed even if the write operation completes in time. + ctx.executor().schedule(new Runnable() { + @Override + public void run() { + ctx.close(future); + } + }, 10, TimeUnit.SECONDS); // FIXME: Magic number + } + } + + private ChannelFuture finishEncode(ChannelHandlerContext ctx, ChannelFuture future) { + if (!finished.compareAndSet(false, true)) { + future.setSuccess(); + return future; + } + + ByteBuf footer; + synchronized (z) { + try { + // Configure input. + z.next_in = EMPTY_ARRAY; + z.next_in_index = 0; + z.avail_in = 0; + + // Configure output. + byte[] out = new byte[32]; // room for ADLER32 + ZLIB / CRC32 + GZIP header + z.next_out = out; + z.next_out_index = 0; + z.avail_out = out.length; + + // Write the ADLER32 checksum (stream footer). + int resultCode = z.deflate(JZlib.Z_FINISH); + if (resultCode != JZlib.Z_OK && resultCode != JZlib.Z_STREAM_END) { + future.setFailure(ZlibUtil.exception(z, "compression failure", resultCode)); + return future; + } else if (z.next_out_index != 0) { + footer = Unpooled.wrappedBuffer(out, 0, z.next_out_index); + } else { + footer = Unpooled.EMPTY_BUFFER; + } + } finally { + z.deflateEnd(); + + // Deference the external references explicitly to tell the VM that + // the allocated byte arrays are temporary so that the call stack + // can be utilized. + // I'm not sure if the modern VMs do this optimization though. + z.next_in = null; + z.next_out = null; + } + } + + ctx.write(footer, future); + return future; + } + + @Override + public void beforeAdd(ChannelHandlerContext ctx) throws Exception { + this.ctx = ctx; + } +} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/JdkZlibEncoder.java b/codec/src/main/java/io/netty/handler/codec/compression/JdkZlibEncoder.java new file mode 100644 index 0000000000..ba4810df6d --- /dev/null +++ b/codec/src/main/java/io/netty/handler/codec/compression/JdkZlibEncoder.java @@ -0,0 +1,296 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.codec.compression; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelHandlerContext; + +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.zip.CRC32; +import java.util.zip.Deflater; + + +/** + * Compresses a {@link ByteBuf} using the deflate algorithm. + * @apiviz.landmark + * @apiviz.has org.jboss.netty.handler.codec.compression.ZlibWrapper + */ +public class JdkZlibEncoder extends ZlibEncoder { + + private final byte[] encodeBuf = new byte[8192]; + private final Deflater deflater; + private final AtomicBoolean finished = new AtomicBoolean(); + private volatile ChannelHandlerContext ctx; + + /* + * GZIP support + */ + private final boolean gzip; + private final CRC32 crc = new CRC32(); + private static final byte[] gzipHeader = {0x1f, (byte) 0x8b, Deflater.DEFLATED, 0, 0, 0, 0, 0, 0, 0}; + private boolean writeHeader = true; + + /** + * Creates a new zlib encoder with the default compression level ({@code 6}) + * and the default wrapper ({@link ZlibWrapper#ZLIB}). + * + * @throws CompressionException if failed to initialize zlib + */ + public JdkZlibEncoder() { + this(6); + } + + /** + * Creates a new zlib encoder with the specified {@code compressionLevel} + * and the default wrapper ({@link ZlibWrapper#ZLIB}). + * + * @param compressionLevel + * {@code 1} yields the fastest compression and {@code 9} yields the + * best compression. {@code 0} means no compression. The default + * compression level is {@code 6}. + * + * @throws CompressionException if failed to initialize zlib + */ + public JdkZlibEncoder(int compressionLevel) { + this(ZlibWrapper.ZLIB, compressionLevel); + } + + /** + * Creates a new zlib encoder with the default compression level ({@code 6}) + * and the specified wrapper. + * + * @throws CompressionException if failed to initialize zlib + */ + public JdkZlibEncoder(ZlibWrapper wrapper) { + this(wrapper, 6); + } + + /** + * Creates a new zlib encoder with the specified {@code compressionLevel} + * and the specified wrapper. + * + * @param compressionLevel + * {@code 1} yields the fastest compression and {@code 9} yields the + * best compression. {@code 0} means no compression. The default + * compression level is {@code 6}. + * + * @throws CompressionException if failed to initialize zlib + */ + public JdkZlibEncoder(ZlibWrapper wrapper, int compressionLevel) { + if (compressionLevel < 0 || compressionLevel > 9) { + throw new IllegalArgumentException( + "compressionLevel: " + compressionLevel + " (expected: 0-9)"); + } + if (wrapper == null) { + throw new NullPointerException("wrapper"); + } + if (wrapper == ZlibWrapper.ZLIB_OR_NONE) { + throw new IllegalArgumentException( + "wrapper '" + ZlibWrapper.ZLIB_OR_NONE + "' is not " + + "allowed for compression."); + } + + gzip = wrapper == ZlibWrapper.GZIP; + deflater = new Deflater(compressionLevel, wrapper != ZlibWrapper.ZLIB); + } + + /** + * Creates a new zlib encoder with the default compression level ({@code 6}) + * and the specified preset dictionary. The wrapper is always + * {@link ZlibWrapper#ZLIB} because it is the only format that supports + * the preset dictionary. + * + * @param dictionary the preset dictionary + * + * @throws CompressionException if failed to initialize zlib + */ + public JdkZlibEncoder(byte[] dictionary) { + this(6, dictionary); + } + + /** + * Creates a new zlib encoder with the specified {@code compressionLevel} + * and the specified preset dictionary. The wrapper is always + * {@link ZlibWrapper#ZLIB} because it is the only format that supports + * the preset dictionary. + * + * @param compressionLevel + * {@code 1} yields the fastest compression and {@code 9} yields the + * best compression. {@code 0} means no compression. The default + * compression level is {@code 6}. + * @param dictionary the preset dictionary + * + * @throws CompressionException if failed to initialize zlib + */ + public JdkZlibEncoder(int compressionLevel, byte[] dictionary) { + if (compressionLevel < 0 || compressionLevel > 9) { + throw new IllegalArgumentException( + "compressionLevel: " + compressionLevel + " (expected: 0-9)"); + } + if (dictionary == null) { + throw new NullPointerException("dictionary"); + } + + gzip = false; + deflater = new Deflater(compressionLevel); + deflater.setDictionary(dictionary); + } + + @Override + public ChannelFuture close() { + return close(ctx().newFuture()); + } + + @Override + public ChannelFuture close(ChannelFuture future) { + return finishEncode(ctx(), future); + } + + private ChannelHandlerContext ctx() { + ChannelHandlerContext ctx = this.ctx; + if (ctx == null) { + throw new IllegalStateException("not added to a pipeline"); + } + return ctx; + } + + @Override + public boolean isClosed() { + return finished.get(); + } + + @Override + public void encode(ChannelHandlerContext ctx, ByteBuf in, ByteBuf out) throws Exception { + if (finished.get()) { + out.writeBytes(in); + in.discardReadBytes(); + return; + } + + ByteBuf uncompressed = in; + byte[] inAry = new byte[uncompressed.readableBytes()]; + uncompressed.readBytes(inAry); + + int sizeEstimate = (int) Math.ceil(inAry.length * 1.001) + 12; + out.ensureWritableBytes(sizeEstimate); + + synchronized (deflater) { + if (gzip) { + crc.update(inAry); + if (writeHeader) { + out.writeBytes(gzipHeader); + writeHeader = false; + } + } + + deflater.setInput(inAry); + while (!deflater.needsInput()) { + int numBytes = deflater.deflate(encodeBuf, 0, encodeBuf.length, Deflater.SYNC_FLUSH); + out.writeBytes(encodeBuf, 0, numBytes); + } + } + } + + @Override + public void disconnect(final ChannelHandlerContext ctx, final ChannelFuture future) throws Exception { + ChannelFuture f = finishEncode(ctx, ctx.newFuture()); + f.addListener(new ChannelFutureListener() { + @Override + public void operationComplete(ChannelFuture f) throws Exception { + ctx.disconnect(future); + } + }); + + if (!f.isDone()) { + // Ensure the channel is closed even if the write operation completes in time. + ctx.executor().schedule(new Runnable() { + @Override + public void run() { + ctx.disconnect(future); + } + }, 10, TimeUnit.SECONDS); // FIXME: Magic number + } + } + + @Override + public void close(final ChannelHandlerContext ctx, final ChannelFuture future) throws Exception { + ChannelFuture f = finishEncode(ctx, ctx.newFuture()); + f.addListener(new ChannelFutureListener() { + @Override + public void operationComplete(ChannelFuture f) throws Exception { + ctx.close(future); + } + }); + + if (!f.isDone()) { + // Ensure the channel is closed even if the write operation completes in time. + ctx.executor().schedule(new Runnable() { + @Override + public void run() { + ctx.close(future); + } + }, 10, TimeUnit.SECONDS); // FIXME: Magic number + } + } + + private ChannelFuture finishEncode(final ChannelHandlerContext ctx, ChannelFuture future) { + if (!finished.compareAndSet(false, true)) { + future.setSuccess(); + return future; + } + + ByteBuf footer = Unpooled.EMPTY_BUFFER; + synchronized (deflater) { + int numBytes = 0; + deflater.finish(); + if (!deflater.finished()) { + numBytes = deflater.deflate(encodeBuf, 0, encodeBuf.length); + } + int footerSize = gzip ? numBytes + 8 : numBytes; + if (footerSize > 0) { + footer = Unpooled.buffer(footerSize); + footer.writeBytes(encodeBuf, 0, numBytes); + if (gzip) { + int crcValue = (int) crc.getValue(); + int uncBytes = deflater.getTotalIn(); + footer.writeByte(crcValue); + footer.writeByte(crcValue >>> 8); + footer.writeByte(crcValue >>> 16); + footer.writeByte(crcValue >>> 24); + footer.writeByte(uncBytes); + footer.writeByte(uncBytes >>> 8); + footer.writeByte(uncBytes >>> 16); + footer.writeByte(uncBytes >>> 24); + } + } + deflater.end(); + } + + ctx.nextOutboundByteBuffer().writeBytes(footer); + ctx.flush(future); + + return future; + } + + @Override + public void beforeAdd(ChannelHandlerContext ctx) throws Exception { + this.ctx = ctx; + } +} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/ZlibCodecFactory.java b/codec/src/main/java/io/netty/handler/codec/compression/ZlibCodecFactory.java new file mode 100644 index 0000000000..5d8609ce0a --- /dev/null +++ b/codec/src/main/java/io/netty/handler/codec/compression/ZlibCodecFactory.java @@ -0,0 +1,96 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.codec.compression; + +import io.netty.util.internal.DetectionUtil; + +/** + * Creates a new {@link ZlibEncoder} and a new {@link ZlibDecoder}. + */ +public final class ZlibCodecFactory { + + public static ZlibEncoder newZlibEncoder(int compressionLevel) { + if (DetectionUtil.javaVersion() < 7) { + return new JZlibEncoder(compressionLevel); + } else { + return new JdkZlibEncoder(compressionLevel); + } + } + + public static ZlibEncoder newZlibEncoder(ZlibWrapper wrapper) { + if (DetectionUtil.javaVersion() < 7) { + return new JZlibEncoder(wrapper); + } else { + return new JdkZlibEncoder(wrapper); + } + } + + public static ZlibEncoder newZlibEncoder(ZlibWrapper wrapper, int compressionLevel) { + if (DetectionUtil.javaVersion() < 7) { + return new JZlibEncoder(wrapper, compressionLevel); + } else { + return new JdkZlibEncoder(wrapper, compressionLevel); + } + } + + public static ZlibEncoder newZlibEncoder(ZlibWrapper wrapper, int compressionLevel, int windowBits, int memLevel) { + if (DetectionUtil.javaVersion() < 7) { + return new JZlibEncoder(wrapper, compressionLevel, windowBits, memLevel); + } else { + return new JdkZlibEncoder(wrapper, compressionLevel); + } + } + + public static ZlibEncoder newZlibEncoder(byte[] dictionary) { + if (DetectionUtil.javaVersion() < 7) { + return new JZlibEncoder(dictionary); + } else { + return new JdkZlibEncoder(dictionary); + } + } + + public static ZlibEncoder newZlibEncoder(int compressionLevel, byte[] dictionary) { + if (DetectionUtil.javaVersion() < 7) { + return new JZlibEncoder(compressionLevel, dictionary); + } else { + return new JdkZlibEncoder(compressionLevel, dictionary); + } + } + + public static ZlibEncoder newZlibEncoder(int compressionLevel, int windowBits, int memLevel, byte[] dictionary) { + if (DetectionUtil.javaVersion() < 7) { + return new JZlibEncoder(compressionLevel, windowBits, memLevel, dictionary); + } else { + return new JdkZlibEncoder(compressionLevel, dictionary); + } + } + + public static ZlibDecoder newZlibDecoder() { + return new JZlibDecoder(); + } + + public static ZlibDecoder newZlibDecoder(ZlibWrapper wrapper) { + return new JZlibDecoder(wrapper); + } + + public static ZlibDecoder newZlibDecoder(byte[] dictionary) { + return new JZlibDecoder(dictionary); + } + + private ZlibCodecFactory() { + // Unused + } +} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/ZlibDecoder.java b/codec/src/main/java/io/netty/handler/codec/compression/ZlibDecoder.java index 3fe5a08bd8..a3a68e17e3 100644 --- a/codec/src/main/java/io/netty/handler/codec/compression/ZlibDecoder.java +++ b/codec/src/main/java/io/netty/handler/codec/compression/ZlibDecoder.java @@ -16,169 +16,19 @@ package io.netty.handler.codec.compression; import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.ByteToByteDecoder; -import io.netty.util.internal.jzlib.JZlib; -import io.netty.util.internal.jzlib.ZStream; - /** * Decompresses a {@link ByteBuf} using the deflate algorithm. + * * @apiviz.landmark * @apiviz.has io.netty.handler.codec.compression.ZlibWrapper */ -public class ZlibDecoder extends ByteToByteDecoder { - - private final ZStream z = new ZStream(); - private byte[] dictionary; - private volatile boolean finished; - - /** - * Creates a new instance with the default wrapper ({@link ZlibWrapper#ZLIB}). - * - * @throws CompressionException if failed to initialize zlib - */ - public ZlibDecoder() { - this(ZlibWrapper.ZLIB); - } - - /** - * Creates a new instance with the specified wrapper. - * - * @throws CompressionException if failed to initialize zlib - */ - public ZlibDecoder(ZlibWrapper wrapper) { - if (wrapper == null) { - throw new NullPointerException("wrapper"); - } - - int resultCode = z.inflateInit(ZlibUtil.convertWrapperType(wrapper)); - if (resultCode != JZlib.Z_OK) { - ZlibUtil.fail(z, "initialization failure", resultCode); - } - } - - /** - * Creates a new instance with the specified preset dictionary. The wrapper - * is always {@link ZlibWrapper#ZLIB} because it is the only format that - * supports the preset dictionary. - * - * @throws CompressionException if failed to initialize zlib - */ - public ZlibDecoder(byte[] dictionary) { - if (dictionary == null) { - throw new NullPointerException("dictionary"); - } - this.dictionary = dictionary; - - int resultCode; - resultCode = z.inflateInit(JZlib.W_ZLIB); - if (resultCode != JZlib.Z_OK) { - ZlibUtil.fail(z, "initialization failure", resultCode); - } - } +public abstract class ZlibDecoder extends ByteToByteDecoder { /** * Returns {@code true} if and only if the end of the compressed stream * has been reached. */ - public boolean isClosed() { - return finished; - } - - @Override - public void decode( - ChannelHandlerContext ctx, - ByteBuf in, ByteBuf out) throws Exception { - - if (!in.readable()) { - return; - } - - try { - // Configure input. - int inputLength = in.readableBytes(); - boolean inHasArray = in.hasArray(); - z.avail_in = inputLength; - if (inHasArray) { - z.next_in = in.array(); - z.next_in_index = in.arrayOffset() + in.readerIndex(); - } else { - byte[] array = new byte[inputLength]; - in.readBytes(array); - z.next_in = array; - z.next_in_index = 0; - } - int oldNextInIndex = z.next_in_index; - - // Configure output. - int maxOutputLength = inputLength << 1; - boolean outHasArray = out.hasArray(); - if (!outHasArray) { - z.next_out = new byte[maxOutputLength]; - } - - try { - loop: for (;;) { - z.avail_out = maxOutputLength; - if (outHasArray) { - out.ensureWritableBytes(maxOutputLength); - z.next_out = out.array(); - z.next_out_index = out.arrayOffset() + out.writerIndex(); - } else { - z.next_out_index = 0; - } - int oldNextOutIndex = z.next_out_index; - - // Decompress 'in' into 'out' - int resultCode = z.inflate(JZlib.Z_SYNC_FLUSH); - int outputLength = z.next_out_index - oldNextOutIndex; - if (outputLength > 0) { - if (outHasArray) { - out.writerIndex(out.writerIndex() + outputLength); - } else { - out.writeBytes(z.next_out, 0, outputLength); - } - } - - switch (resultCode) { - case JZlib.Z_NEED_DICT: - if (dictionary == null) { - ZlibUtil.fail(z, "decompression failure", resultCode); - } else { - resultCode = z.inflateSetDictionary(dictionary, dictionary.length); - if (resultCode != JZlib.Z_OK) { - ZlibUtil.fail(z, "failed to set the dictionary", resultCode); - } - } - break; - case JZlib.Z_STREAM_END: - finished = true; // Do not decode anymore. - z.inflateEnd(); - break loop; - case JZlib.Z_OK: - break; - case JZlib.Z_BUF_ERROR: - if (z.avail_in <= 0) { - break loop; - } - break; - default: - ZlibUtil.fail(z, "decompression failure", resultCode); - } - } - } finally { - if (inHasArray) { - in.skipBytes(z.next_in_index - oldNextInIndex); - } - } - } finally { - // Deference the external references explicitly to tell the VM that - // the allocated byte arrays are temporary so that the call stack - // can be utilized. - // I'm not sure if the modern VMs do this optimization though. - z.next_in = null; - z.next_out = null; - } - } + public abstract boolean isClosed(); } diff --git a/codec/src/main/java/io/netty/handler/codec/compression/ZlibEncoder.java b/codec/src/main/java/io/netty/handler/codec/compression/ZlibEncoder.java index b5e559d811..e81a0a0413 100644 --- a/codec/src/main/java/io/netty/handler/codec/compression/ZlibEncoder.java +++ b/codec/src/main/java/io/netty/handler/codec/compression/ZlibEncoder.java @@ -16,394 +16,25 @@ package io.netty.handler.codec.compression; import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelFutureListener; -import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.ByteToByteEncoder; -import io.netty.util.internal.jzlib.JZlib; -import io.netty.util.internal.jzlib.ZStream; - -import java.util.concurrent.atomic.AtomicBoolean; - /** - * Compresses a {@link ByteBuf} using the deflate algorithm. + * Decompresses a {@link ByteBuf} using the deflate algorithm. + * * @apiviz.landmark * @apiviz.has io.netty.handler.codec.compression.ZlibWrapper */ -public class ZlibEncoder extends ByteToByteEncoder { - - private static final byte[] EMPTY_ARRAY = new byte[0]; - - private final ZStream z = new ZStream(); - private final AtomicBoolean finished = new AtomicBoolean(); - private volatile ChannelHandlerContext ctx; +public abstract class ZlibEncoder extends ByteToByteEncoder { /** - * Creates a new zlib encoder with the default compression level ({@code 6}), - * default window bits ({@code 15}), default memory level ({@code 8}), - * and the default wrapper ({@link ZlibWrapper#ZLIB}). - * - * @throws CompressionException if failed to initialize zlib + * Returns {@code true} if and only if the end of the compressed stream + * has been reached. */ - public ZlibEncoder() { - this(6); - } + public abstract boolean isClosed(); - /** - * Creates a new zlib encoder with the specified {@code compressionLevel}, - * default window bits ({@code 15}), default memory level ({@code 8}), - * and the default wrapper ({@link ZlibWrapper#ZLIB}). - * - * @param compressionLevel - * {@code 1} yields the fastest compression and {@code 9} yields the - * best compression. {@code 0} means no compression. The default - * compression level is {@code 6}. - * - * @throws CompressionException if failed to initialize zlib - */ - public ZlibEncoder(int compressionLevel) { - this(ZlibWrapper.ZLIB, compressionLevel); - } + public abstract ChannelFuture close(); - /** - * Creates a new zlib encoder with the default compression level ({@code 6}), - * default window bits ({@code 15}), default memory level ({@code 8}), - * and the specified wrapper. - * - * @throws CompressionException if failed to initialize zlib - */ - public ZlibEncoder(ZlibWrapper wrapper) { - this(wrapper, 6); - } + public abstract ChannelFuture close(ChannelFuture future); - /** - * Creates a new zlib encoder with the specified {@code compressionLevel}, - * default window bits ({@code 15}), default memory level ({@code 8}), - * and the specified wrapper. - * - * @param compressionLevel - * {@code 1} yields the fastest compression and {@code 9} yields the - * best compression. {@code 0} means no compression. The default - * compression level is {@code 6}. - * - * @throws CompressionException if failed to initialize zlib - */ - public ZlibEncoder(ZlibWrapper wrapper, int compressionLevel) { - this(wrapper, compressionLevel, 15, 8); - } - - /** - * Creates a new zlib encoder with the specified {@code compressionLevel}, - * the specified {@code windowBits}, the specified {@code memLevel}, and - * the specified wrapper. - * - * @param compressionLevel - * {@code 1} yields the fastest compression and {@code 9} yields the - * best compression. {@code 0} means no compression. The default - * compression level is {@code 6}. - * @param windowBits - * The base two logarithm of the size of the history buffer. The - * value should be in the range {@code 9} to {@code 15} inclusive. - * Larger values result in better compression at the expense of - * memory usage. The default value is {@code 15}. - * @param memLevel - * How much memory should be allocated for the internal compression - * state. {@code 1} uses minimum memory and {@code 9} uses maximum - * memory. Larger values result in better and faster compression - * at the expense of memory usage. The default value is {@code 8} - * - * @throws CompressionException if failed to initialize zlib - */ - public ZlibEncoder(ZlibWrapper wrapper, int compressionLevel, int windowBits, int memLevel) { - - if (compressionLevel < 0 || compressionLevel > 9) { - throw new IllegalArgumentException( - "compressionLevel: " + compressionLevel + - " (expected: 0-9)"); - } - if (windowBits < 9 || windowBits > 15) { - throw new IllegalArgumentException( - "windowBits: " + windowBits + " (expected: 9-15)"); - } - if (memLevel < 1 || memLevel > 9) { - throw new IllegalArgumentException( - "memLevel: " + memLevel + " (expected: 1-9)"); - } - if (wrapper == null) { - throw new NullPointerException("wrapper"); - } - if (wrapper == ZlibWrapper.ZLIB_OR_NONE) { - throw new IllegalArgumentException( - "wrapper '" + ZlibWrapper.ZLIB_OR_NONE + "' is not " + - "allowed for compression."); - } - - synchronized (z) { - int resultCode = z.deflateInit( - compressionLevel, windowBits, memLevel, - ZlibUtil.convertWrapperType(wrapper)); - if (resultCode != JZlib.Z_OK) { - ZlibUtil.fail(z, "initialization failure", resultCode); - } - } - } - - /** - * Creates a new zlib encoder with the default compression level ({@code 6}), - * default window bits ({@code 15}), default memory level ({@code 8}), - * and the specified preset dictionary. The wrapper is always - * {@link ZlibWrapper#ZLIB} because it is the only format that supports - * the preset dictionary. - * - * @param dictionary the preset dictionary - * - * @throws CompressionException if failed to initialize zlib - */ - public ZlibEncoder(byte[] dictionary) { - this(6, dictionary); - } - - /** - * Creates a new zlib encoder with the specified {@code compressionLevel}, - * default window bits ({@code 15}), default memory level ({@code 8}), - * and the specified preset dictionary. The wrapper is always - * {@link ZlibWrapper#ZLIB} because it is the only format that supports - * the preset dictionary. - * - * @param compressionLevel - * {@code 1} yields the fastest compression and {@code 9} yields the - * best compression. {@code 0} means no compression. The default - * compression level is {@code 6}. - * @param dictionary the preset dictionary - * - * @throws CompressionException if failed to initialize zlib - */ - public ZlibEncoder(int compressionLevel, byte[] dictionary) { - this(compressionLevel, 15, 8, dictionary); - } - - /** - * Creates a new zlib encoder with the specified {@code compressionLevel}, - * the specified {@code windowBits}, the specified {@code memLevel}, - * and the specified preset dictionary. The wrapper is always - * {@link ZlibWrapper#ZLIB} because it is the only format that supports - * the preset dictionary. - * - * @param compressionLevel - * {@code 1} yields the fastest compression and {@code 9} yields the - * best compression. {@code 0} means no compression. The default - * compression level is {@code 6}. - * @param windowBits - * The base two logarithm of the size of the history buffer. The - * value should be in the range {@code 9} to {@code 15} inclusive. - * Larger values result in better compression at the expense of - * memory usage. The default value is {@code 15}. - * @param memLevel - * How much memory should be allocated for the internal compression - * state. {@code 1} uses minimum memory and {@code 9} uses maximum - * memory. Larger values result in better and faster compression - * at the expense of memory usage. The default value is {@code 8} - * @param dictionary the preset dictionary - * - * @throws CompressionException if failed to initialize zlib - */ - public ZlibEncoder(int compressionLevel, int windowBits, int memLevel, byte[] dictionary) { - if (compressionLevel < 0 || compressionLevel > 9) { - throw new IllegalArgumentException("compressionLevel: " + compressionLevel + " (expected: 0-9)"); - } - if (windowBits < 9 || windowBits > 15) { - throw new IllegalArgumentException( - "windowBits: " + windowBits + " (expected: 9-15)"); - } - if (memLevel < 1 || memLevel > 9) { - throw new IllegalArgumentException( - "memLevel: " + memLevel + " (expected: 1-9)"); - } - if (dictionary == null) { - throw new NullPointerException("dictionary"); - } - - synchronized (z) { - int resultCode; - resultCode = z.deflateInit( - compressionLevel, windowBits, memLevel, - JZlib.W_ZLIB); // Default: ZLIB format - if (resultCode != JZlib.Z_OK) { - ZlibUtil.fail(z, "initialization failure", resultCode); - } else { - resultCode = z.deflateSetDictionary(dictionary, dictionary.length); - if (resultCode != JZlib.Z_OK) { - ZlibUtil.fail(z, "failed to set the dictionary", resultCode); - } - } - } - } - - public ChannelFuture close() { - return close(ctx().channel().newFuture()); - } - - public ChannelFuture close(ChannelFuture future) { - return finishEncode(ctx(), future); - } - - private ChannelHandlerContext ctx() { - ChannelHandlerContext ctx = this.ctx; - if (ctx == null) { - throw new IllegalStateException("not added to a pipeline"); - } - return ctx; - } - - public boolean isClosed() { - return finished.get(); - } - - @Override - public void encode(ChannelHandlerContext ctx, - ByteBuf in, ByteBuf out) throws Exception { - if (finished.get()) { - return; - } - - synchronized (z) { - try { - // Configure input. - int inputLength = in.readableBytes(); - boolean inHasArray = in.hasArray(); - z.avail_in = inputLength; - if (inHasArray) { - z.next_in = in.array(); - z.next_in_index = in.arrayOffset() + in.readerIndex(); - } else { - byte[] array = new byte[inputLength]; - in.readBytes(array); - z.next_in = array; - z.next_in_index = 0; - } - int oldNextInIndex = z.next_in_index; - - // Configure output. - int maxOutputLength = (int) Math.ceil(inputLength * 1.001) + 12; - boolean outHasArray = out.hasArray(); - z.avail_out = maxOutputLength; - if (outHasArray) { - out.ensureWritableBytes(maxOutputLength); - z.next_out = out.array(); - z.next_out_index = out.arrayOffset() + out.writerIndex(); - } else { - z.next_out = new byte[maxOutputLength]; - z.next_out_index = 0; - } - int oldNextOutIndex = z.next_out_index; - - // Note that Z_PARTIAL_FLUSH has been deprecated. - int resultCode; - try { - resultCode = z.deflate(JZlib.Z_SYNC_FLUSH); - } finally { - if (inHasArray) { - in.skipBytes(z.next_in_index - oldNextInIndex); - } - } - - if (resultCode != JZlib.Z_OK) { - ZlibUtil.fail(z, "compression failure", resultCode); - } - - int outputLength = z.next_out_index - oldNextOutIndex; - if (outputLength > 0) { - if (outHasArray) { - out.writerIndex(out.writerIndex() + outputLength); - } else { - out.writeBytes(z.next_out, 0, outputLength); - } - } - } finally { - // Deference the external references explicitly to tell the VM that - // the allocated byte arrays are temporary so that the call stack - // can be utilized. - // I'm not sure if the modern VMs do this optimization though. - z.next_in = null; - z.next_out = null; - } - } - } - - @Override - public void disconnect( - final ChannelHandlerContext ctx, - final ChannelFuture future) throws Exception { - finishEncode(ctx, ctx.newFuture()).addListener(new ChannelFutureListener() { - @Override - public void operationComplete(ChannelFuture f) throws Exception { - ctx.disconnect(future); - } - }); - } - - @Override - public void close( - final ChannelHandlerContext ctx, - final ChannelFuture future) throws Exception { - finishEncode(ctx, ctx.newFuture()).addListener(new ChannelFutureListener() { - @Override - public void operationComplete(ChannelFuture f) throws Exception { - ctx.close(future); - } - }); - } - - private ChannelFuture finishEncode(ChannelHandlerContext ctx, ChannelFuture future) { - if (!finished.compareAndSet(false, true)) { - future.setSuccess(); - return future; - } - - ByteBuf footer; - synchronized (z) { - try { - // Configure input. - z.next_in = EMPTY_ARRAY; - z.next_in_index = 0; - z.avail_in = 0; - - // Configure output. - byte[] out = new byte[32]; // room for ADLER32 + ZLIB / CRC32 + GZIP header - z.next_out = out; - z.next_out_index = 0; - z.avail_out = out.length; - - // Write the ADLER32 checksum (stream footer). - int resultCode = z.deflate(JZlib.Z_FINISH); - if (resultCode != JZlib.Z_OK && resultCode != JZlib.Z_STREAM_END) { - future.setFailure(ZlibUtil.exception(z, "compression failure", resultCode)); - return future; - } else if (z.next_out_index != 0) { - footer = Unpooled.wrappedBuffer(out, 0, z.next_out_index); - } else { - footer = Unpooled.EMPTY_BUFFER; - } - } finally { - z.deflateEnd(); - - // Deference the external references explicitly to tell the VM that - // the allocated byte arrays are temporary so that the call stack - // can be utilized. - // I'm not sure if the modern VMs do this optimization though. - z.next_in = null; - z.next_out = null; - } - } - - ctx.write(footer, future); - return future; - } - - @Override - public void beforeAdd(ChannelHandlerContext ctx) throws Exception { - this.ctx = ctx; - } } diff --git a/codec/src/main/java/io/netty/handler/codec/compression/ZlibUtil.java b/codec/src/main/java/io/netty/handler/codec/compression/ZlibUtil.java index fd3520dd77..0eaebd356c 100644 --- a/codec/src/main/java/io/netty/handler/codec/compression/ZlibUtil.java +++ b/codec/src/main/java/io/netty/handler/codec/compression/ZlibUtil.java @@ -19,7 +19,7 @@ import io.netty.util.internal.jzlib.JZlib; import io.netty.util.internal.jzlib.ZStream; /** - * Utility methods used by {@link ZlibEncoder} and {@link ZlibDecoder}. + * Utility methods used by {@link JZlibEncoder} and {@link JZlibDecoder}. */ final class ZlibUtil { diff --git a/example/src/main/java/io/netty/example/factorial/FactorialClientInitializer.java b/example/src/main/java/io/netty/example/factorial/FactorialClientInitializer.java index 47e242e921..959c1d6ffc 100644 --- a/example/src/main/java/io/netty/example/factorial/FactorialClientInitializer.java +++ b/example/src/main/java/io/netty/example/factorial/FactorialClientInitializer.java @@ -18,8 +18,7 @@ package io.netty.example.factorial; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.socket.SocketChannel; -import io.netty.handler.codec.compression.ZlibDecoder; -import io.netty.handler.codec.compression.ZlibEncoder; +import io.netty.handler.codec.compression.ZlibCodecFactory; import io.netty.handler.codec.compression.ZlibWrapper; /** @@ -38,8 +37,8 @@ public class FactorialClientInitializer extends ChannelInitializer Date: Tue, 19 Jun 2012 10:39:30 +0900 Subject: [PATCH 35/87] Add ChannelMetadata and remove unnecessary disconnect() impls - Add Channel.metadata() and remove Channel.bufferType() - DefaultPipeline automatically redirects disconnect() request to close() if the channel has no disconnect operation - Remove unnecessary disconnect() implementations --- .../codec/spdy/SpdySessionHandler.java | 6 --- .../codec/compression/JZlibEncoder.java | 23 --------- .../codec/compression/JdkZlibEncoder.java | 21 -------- .../netty/channel/AbstractServerChannel.java | 7 +-- .../main/java/io/netty/channel/Channel.java | 3 +- .../io/netty/channel/ChannelMetadata.java | 51 +++++++++++++++++++ .../netty/channel/DefaultChannelPipeline.java | 12 +++-- .../channel/embedded/EmbeddedByteChannel.java | 7 ++- .../embedded/EmbeddedMessageChannel.java | 7 ++- .../io/netty/channel/local/LocalChannel.java | 7 ++- .../socket/nio/AbstractNioByteChannel.java | 6 --- .../socket/nio/AbstractNioMessageChannel.java | 6 --- .../socket/nio/NioDatagramChannel.java | 9 ++++ .../socket/nio/NioServerSocketChannel.java | 9 ++++ .../channel/socket/nio/NioSocketChannel.java | 9 ++++ .../socket/oio/AbstractOioByteChannel.java | 6 --- .../socket/oio/AbstractOioMessageChannel.java | 6 --- .../socket/oio/OioDatagramChannel.java | 9 ++++ .../socket/oio/OioServerSocketChannel.java | 9 ++++ .../channel/socket/oio/OioSocketChannel.java | 9 ++++ 20 files changed, 134 insertions(+), 88 deletions(-) create mode 100644 transport/src/main/java/io/netty/channel/ChannelMetadata.java diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdySessionHandler.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdySessionHandler.java index 513a3789ea..145c987601 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdySessionHandler.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdySessionHandler.java @@ -434,12 +434,6 @@ public class SpdySessionHandler super.close(ctx, future); } - @Override - public void disconnect(ChannelHandlerContext ctx, ChannelFuture future) throws Exception { - sendGoAwayFrame(ctx); - super.close(ctx, future); - } - @Override public void flush(ChannelHandlerContext ctx, ChannelFuture future) throws Exception { MessageBuf in = ctx.outboundMessageBuffer(); diff --git a/codec/src/main/java/io/netty/handler/codec/compression/JZlibEncoder.java b/codec/src/main/java/io/netty/handler/codec/compression/JZlibEncoder.java index a5701630ff..39b2d8bf09 100644 --- a/codec/src/main/java/io/netty/handler/codec/compression/JZlibEncoder.java +++ b/codec/src/main/java/io/netty/handler/codec/compression/JZlibEncoder.java @@ -335,29 +335,6 @@ public class JZlibEncoder extends ZlibEncoder { } } - @Override - public void disconnect( - final ChannelHandlerContext ctx, - final ChannelFuture future) throws Exception { - ChannelFuture f = finishEncode(ctx, ctx.newFuture()); - f.addListener(new ChannelFutureListener() { - @Override - public void operationComplete(ChannelFuture f) throws Exception { - ctx.disconnect(future); - } - }); - - if (!f.isDone()) { - // Ensure the channel is closed even if the write operation completes in time. - ctx.executor().schedule(new Runnable() { - @Override - public void run() { - ctx.disconnect(future); - } - }, 10, TimeUnit.SECONDS); // FIXME: Magic number - } - } - @Override public void close( final ChannelHandlerContext ctx, diff --git a/codec/src/main/java/io/netty/handler/codec/compression/JdkZlibEncoder.java b/codec/src/main/java/io/netty/handler/codec/compression/JdkZlibEncoder.java index ba4810df6d..e78ebbcc25 100644 --- a/codec/src/main/java/io/netty/handler/codec/compression/JdkZlibEncoder.java +++ b/codec/src/main/java/io/netty/handler/codec/compression/JdkZlibEncoder.java @@ -208,27 +208,6 @@ public class JdkZlibEncoder extends ZlibEncoder { } } - @Override - public void disconnect(final ChannelHandlerContext ctx, final ChannelFuture future) throws Exception { - ChannelFuture f = finishEncode(ctx, ctx.newFuture()); - f.addListener(new ChannelFutureListener() { - @Override - public void operationComplete(ChannelFuture f) throws Exception { - ctx.disconnect(future); - } - }); - - if (!f.isDone()) { - // Ensure the channel is closed even if the write operation completes in time. - ctx.executor().schedule(new Runnable() { - @Override - public void run() { - ctx.disconnect(future); - } - }, 10, TimeUnit.SECONDS); // FIXME: Magic number - } - } - @Override public void close(final ChannelHandlerContext ctx, final ChannelFuture future) throws Exception { ChannelFuture f = finishEncode(ctx, ctx.newFuture()); diff --git a/transport/src/main/java/io/netty/channel/AbstractServerChannel.java b/transport/src/main/java/io/netty/channel/AbstractServerChannel.java index 0c42167eeb..f528215d33 100755 --- a/transport/src/main/java/io/netty/channel/AbstractServerChannel.java +++ b/transport/src/main/java/io/netty/channel/AbstractServerChannel.java @@ -20,7 +20,6 @@ import io.netty.buffer.ChannelBufType; import io.netty.buffer.MessageBuf; import java.net.SocketAddress; -import java.util.Queue; /** * A skeletal server-side {@link Channel} implementation. A server-side @@ -34,6 +33,8 @@ import java.util.Queue; */ public abstract class AbstractServerChannel extends AbstractChannel implements ServerChannel { + private static final ChannelMetadata METADATA = new ChannelMetadata(ChannelBufType.MESSAGE, false); + /** * Creates a new instance. */ @@ -52,8 +53,8 @@ public abstract class AbstractServerChannel extends AbstractChannel implements S } @Override - public ChannelBufType bufferType() { - return ChannelBufType.MESSAGE; + public ChannelMetadata metadata() { + return METADATA; } @Override diff --git a/transport/src/main/java/io/netty/channel/Channel.java b/transport/src/main/java/io/netty/channel/Channel.java index 0ffc44d49d..b6ff470331 100755 --- a/transport/src/main/java/io/netty/channel/Channel.java +++ b/transport/src/main/java/io/netty/channel/Channel.java @@ -16,7 +16,6 @@ package io.netty.channel; import io.netty.buffer.ByteBuf; -import io.netty.buffer.ChannelBufType; import io.netty.buffer.MessageBuf; import io.netty.channel.socket.DatagramChannel; import io.netty.channel.socket.ServerSocketChannel; @@ -139,7 +138,7 @@ public interface Channel extends AttributeMap, ChannelOutboundInvoker, ChannelFu boolean isRegistered(); boolean isActive(); - ChannelBufType bufferType(); + ChannelMetadata metadata(); ByteBuf outboundByteBuffer(); MessageBuf outboundMessageBuffer(); diff --git a/transport/src/main/java/io/netty/channel/ChannelMetadata.java b/transport/src/main/java/io/netty/channel/ChannelMetadata.java new file mode 100644 index 0000000000..e005fed4ca --- /dev/null +++ b/transport/src/main/java/io/netty/channel/ChannelMetadata.java @@ -0,0 +1,51 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.channel; + +import io.netty.buffer.ChannelBufType; + +import java.net.SocketAddress; + +/** + * Represents the properties of a {@link Channel} implementation. + */ +public final class ChannelMetadata { + + private final ChannelBufType bufferType; + private final boolean hasDisconnect; + + public ChannelMetadata(ChannelBufType bufferType, boolean hasDisconnect) { + if (bufferType == null) { + throw new NullPointerException("bufferType"); + } + + this.bufferType = bufferType; + this.hasDisconnect = hasDisconnect; + } + + public ChannelBufType bufferType() { + return bufferType; + } + + /** + * Returns {@code true} if and only if the channel has the {@code disconnect()} operation + * that allows a user to disconnect and then call {@link Channel#connect(SocketAddress)} again, + * such as UDP/IP. + */ + public boolean hasDisconnect() { + return hasDisconnect; + } +} diff --git a/transport/src/main/java/io/netty/channel/DefaultChannelPipeline.java b/transport/src/main/java/io/netty/channel/DefaultChannelPipeline.java index fa3cd3c433..f4a5460099 100755 --- a/transport/src/main/java/io/netty/channel/DefaultChannelPipeline.java +++ b/transport/src/main/java/io/netty/channel/DefaultChannelPipeline.java @@ -887,7 +887,7 @@ public class DefaultChannelPipeline implements ChannelPipeline { @Override public MessageBuf inboundMessageBuffer() { - if (channel.bufferType() != ChannelBufType.MESSAGE) { + if (channel.metadata().bufferType() != ChannelBufType.MESSAGE) { throw new NoSuchBufferException( "The first inbound buffer of this channel must be a message buffer."); } @@ -896,7 +896,7 @@ public class DefaultChannelPipeline implements ChannelPipeline { @Override public ByteBuf inboundByteBuffer() { - if (channel.bufferType() != ChannelBufType.BYTE) { + if (channel.metadata().bufferType() != ChannelBufType.BYTE) { throw new NoSuchBufferException( "The first inbound buffer of this channel must be a byte buffer."); } @@ -1150,6 +1150,12 @@ public class DefaultChannelPipeline implements ChannelPipeline { } ChannelFuture disconnect(final DefaultChannelHandlerContext ctx, final ChannelFuture future) { + // Translate disconnect to close if the channel has no notion of disconnect-reconnect. + // So far, UDP/IP is the only transport that has such behavior. + if (!ctx.channel().metadata().hasDisconnect()) { + return close(ctx, future); + } + validateFuture(future); EventExecutor executor = ctx.executor(); if (executor.inEventLoop()) { @@ -1435,7 +1441,7 @@ public class DefaultChannelPipeline implements ChannelPipeline { private final class HeadHandler implements ChannelOutboundHandler { @Override public ChannelBuf newOutboundBuffer(ChannelHandlerContext ctx) throws Exception { - switch (channel.bufferType()) { + switch (channel.metadata().bufferType()) { case BYTE: return Unpooled.dynamicBuffer(); case MESSAGE: diff --git a/transport/src/main/java/io/netty/channel/embedded/EmbeddedByteChannel.java b/transport/src/main/java/io/netty/channel/embedded/EmbeddedByteChannel.java index 07e8ac5313..c9e37fa4d4 100755 --- a/transport/src/main/java/io/netty/channel/embedded/EmbeddedByteChannel.java +++ b/transport/src/main/java/io/netty/channel/embedded/EmbeddedByteChannel.java @@ -19,16 +19,19 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.ChannelBufType; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelMetadata; public class EmbeddedByteChannel extends AbstractEmbeddedChannel { + private static final ChannelMetadata METADATA = new ChannelMetadata(ChannelBufType.BYTE, false); + public EmbeddedByteChannel(ChannelHandler... handlers) { super(Unpooled.dynamicBuffer(), handlers); } @Override - public ChannelBufType bufferType() { - return ChannelBufType.BYTE; + public ChannelMetadata metadata() { + return METADATA; } public ByteBuf inboundBuffer() { diff --git a/transport/src/main/java/io/netty/channel/embedded/EmbeddedMessageChannel.java b/transport/src/main/java/io/netty/channel/embedded/EmbeddedMessageChannel.java index 9662fb1eb0..86cf059624 100755 --- a/transport/src/main/java/io/netty/channel/embedded/EmbeddedMessageChannel.java +++ b/transport/src/main/java/io/netty/channel/embedded/EmbeddedMessageChannel.java @@ -19,16 +19,19 @@ import io.netty.buffer.ChannelBufType; import io.netty.buffer.MessageBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandler; +import io.netty.channel.ChannelMetadata; public class EmbeddedMessageChannel extends AbstractEmbeddedChannel { + private static final ChannelMetadata METADATA = new ChannelMetadata(ChannelBufType.MESSAGE, false); + public EmbeddedMessageChannel(ChannelHandler... handlers) { super(Unpooled.messageBuffer(), handlers); } @Override - public ChannelBufType bufferType() { - return ChannelBufType.MESSAGE; + public ChannelMetadata metadata() { + return METADATA; } public MessageBuf inboundBuffer() { diff --git a/transport/src/main/java/io/netty/channel/local/LocalChannel.java b/transport/src/main/java/io/netty/channel/local/LocalChannel.java index 174b545191..20aa9609a7 100755 --- a/transport/src/main/java/io/netty/channel/local/LocalChannel.java +++ b/transport/src/main/java/io/netty/channel/local/LocalChannel.java @@ -22,6 +22,7 @@ import io.netty.channel.Channel; import io.netty.channel.ChannelConfig; import io.netty.channel.ChannelException; import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelMetadata; import io.netty.channel.DefaultChannelConfig; import io.netty.channel.EventLoop; import io.netty.channel.SingleThreadEventExecutor; @@ -38,6 +39,8 @@ import java.nio.channels.NotYetConnectedException; */ public class LocalChannel extends AbstractChannel { + private static final ChannelMetadata METADATA = new ChannelMetadata(ChannelBufType.MESSAGE, false); + private final ChannelConfig config = new DefaultChannelConfig(); private final Runnable shutdownHook = new Runnable() { @Override @@ -68,8 +71,8 @@ public class LocalChannel extends AbstractChannel { } @Override - public ChannelBufType bufferType() { - return ChannelBufType.MESSAGE; + public ChannelMetadata metadata() { + return METADATA; } @Override diff --git a/transport/src/main/java/io/netty/channel/socket/nio/AbstractNioByteChannel.java b/transport/src/main/java/io/netty/channel/socket/nio/AbstractNioByteChannel.java index fded47226c..418dc8278b 100755 --- a/transport/src/main/java/io/netty/channel/socket/nio/AbstractNioByteChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/nio/AbstractNioByteChannel.java @@ -16,7 +16,6 @@ package io.netty.channel.socket.nio; import io.netty.buffer.ByteBuf; -import io.netty.buffer.ChannelBufType; import io.netty.channel.Channel; import io.netty.channel.ChannelPipeline; @@ -31,11 +30,6 @@ abstract class AbstractNioByteChannel extends AbstractNioChannel { super(parent, id, ch, SelectionKey.OP_READ); } - @Override - public ChannelBufType bufferType() { - return ChannelBufType.BYTE; - } - @Override protected Unsafe newUnsafe() { return new NioByteUnsafe(); diff --git a/transport/src/main/java/io/netty/channel/socket/nio/AbstractNioMessageChannel.java b/transport/src/main/java/io/netty/channel/socket/nio/AbstractNioMessageChannel.java index b79d10a4a2..aabb90e4fd 100755 --- a/transport/src/main/java/io/netty/channel/socket/nio/AbstractNioMessageChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/nio/AbstractNioMessageChannel.java @@ -15,7 +15,6 @@ */ package io.netty.channel.socket.nio; -import io.netty.buffer.ChannelBufType; import io.netty.buffer.MessageBuf; import io.netty.channel.Channel; import io.netty.channel.ChannelPipeline; @@ -30,11 +29,6 @@ abstract class AbstractNioMessageChannel extends AbstractNioChannel { super(parent, id, ch, defaultInterestOps); } - @Override - public ChannelBufType bufferType() { - return ChannelBufType.MESSAGE; - } - @Override protected Unsafe newUnsafe() { return new NioMessageUnsafe(); diff --git a/transport/src/main/java/io/netty/channel/socket/nio/NioDatagramChannel.java b/transport/src/main/java/io/netty/channel/socket/nio/NioDatagramChannel.java index 968673b59f..483b8b2fa9 100755 --- a/transport/src/main/java/io/netty/channel/socket/nio/NioDatagramChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/nio/NioDatagramChannel.java @@ -16,10 +16,12 @@ package io.netty.channel.socket.nio; import io.netty.buffer.ByteBuf; +import io.netty.buffer.ChannelBufType; import io.netty.buffer.MessageBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelException; import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelMetadata; import io.netty.channel.socket.DatagramChannelConfig; import io.netty.channel.socket.DatagramPacket; import io.netty.channel.socket.InternetProtocolFamily; @@ -47,6 +49,8 @@ import java.util.Map; public final class NioDatagramChannel extends AbstractNioMessageChannel implements io.netty.channel.socket.DatagramChannel { + private static final ChannelMetadata METADATA = new ChannelMetadata(ChannelBufType.MESSAGE, true); + private final DatagramChannelConfig config; private final Map> memberships = new HashMap>(); @@ -92,6 +96,11 @@ public final class NioDatagramChannel config = new NioDatagramChannelConfig(socket); } + @Override + public ChannelMetadata metadata() { + return METADATA; + } + @Override public DatagramChannelConfig config() { return config; diff --git a/transport/src/main/java/io/netty/channel/socket/nio/NioServerSocketChannel.java b/transport/src/main/java/io/netty/channel/socket/nio/NioServerSocketChannel.java index 582393bebf..6a19e55e8c 100755 --- a/transport/src/main/java/io/netty/channel/socket/nio/NioServerSocketChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/nio/NioServerSocketChannel.java @@ -15,8 +15,10 @@ */ package io.netty.channel.socket.nio; +import io.netty.buffer.ChannelBufType; import io.netty.buffer.MessageBuf; import io.netty.channel.ChannelException; +import io.netty.channel.ChannelMetadata; import io.netty.channel.socket.DefaultServerSocketChannelConfig; import io.netty.channel.socket.ServerSocketChannelConfig; @@ -30,6 +32,8 @@ import java.nio.channels.SocketChannel; public class NioServerSocketChannel extends AbstractNioMessageChannel implements io.netty.channel.socket.ServerSocketChannel { + private static final ChannelMetadata METADATA = new ChannelMetadata(ChannelBufType.MESSAGE, false); + private static ServerSocketChannel newSocket() { try { return ServerSocketChannel.open(); @@ -46,6 +50,11 @@ public class NioServerSocketChannel extends AbstractNioMessageChannel config = new DefaultServerSocketChannelConfig(javaChannel().socket()); } + @Override + public ChannelMetadata metadata() { + return METADATA; + } + @Override public ServerSocketChannelConfig config() { return config; diff --git a/transport/src/main/java/io/netty/channel/socket/nio/NioSocketChannel.java b/transport/src/main/java/io/netty/channel/socket/nio/NioSocketChannel.java index 89a3acdb2a..e7a270d73f 100755 --- a/transport/src/main/java/io/netty/channel/socket/nio/NioSocketChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/nio/NioSocketChannel.java @@ -16,8 +16,10 @@ package io.netty.channel.socket.nio; import io.netty.buffer.ByteBuf; +import io.netty.buffer.ChannelBufType; import io.netty.channel.Channel; import io.netty.channel.ChannelException; +import io.netty.channel.ChannelMetadata; import io.netty.channel.socket.DefaultSocketChannelConfig; import io.netty.channel.socket.SocketChannelConfig; import io.netty.logging.InternalLogger; @@ -30,6 +32,8 @@ import java.nio.channels.SocketChannel; public class NioSocketChannel extends AbstractNioByteChannel implements io.netty.channel.socket.SocketChannel { + private static final ChannelMetadata METADATA = new ChannelMetadata(ChannelBufType.BYTE, false); + private static final InternalLogger logger = InternalLoggerFactory.getInstance(NioSocketChannel.class); private final SocketChannelConfig config; @@ -71,6 +75,11 @@ public class NioSocketChannel extends AbstractNioByteChannel implements io.netty config = new DefaultSocketChannelConfig(socket.socket()); } + @Override + public ChannelMetadata metadata() { + return METADATA; + } + @Override public SocketChannelConfig config() { return config; diff --git a/transport/src/main/java/io/netty/channel/socket/oio/AbstractOioByteChannel.java b/transport/src/main/java/io/netty/channel/socket/oio/AbstractOioByteChannel.java index 12697f74aa..161f9c7456 100755 --- a/transport/src/main/java/io/netty/channel/socket/oio/AbstractOioByteChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/oio/AbstractOioByteChannel.java @@ -16,7 +16,6 @@ package io.netty.channel.socket.oio; import io.netty.buffer.ByteBuf; -import io.netty.buffer.ChannelBufType; import io.netty.channel.Channel; import io.netty.channel.ChannelPipeline; @@ -28,11 +27,6 @@ abstract class AbstractOioByteChannel extends AbstractOioChannel { super(parent, id); } - @Override - public ChannelBufType bufferType() { - return ChannelBufType.BYTE; - } - @Override protected Unsafe newUnsafe() { return new OioByteUnsafe(); diff --git a/transport/src/main/java/io/netty/channel/socket/oio/AbstractOioMessageChannel.java b/transport/src/main/java/io/netty/channel/socket/oio/AbstractOioMessageChannel.java index fb0d6141bc..5915991a37 100755 --- a/transport/src/main/java/io/netty/channel/socket/oio/AbstractOioMessageChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/oio/AbstractOioMessageChannel.java @@ -15,7 +15,6 @@ */ package io.netty.channel.socket.oio; -import io.netty.buffer.ChannelBufType; import io.netty.buffer.MessageBuf; import io.netty.channel.Channel; import io.netty.channel.ChannelPipeline; @@ -28,11 +27,6 @@ abstract class AbstractOioMessageChannel extends AbstractOioChannel { super(parent, id); } - @Override - public ChannelBufType bufferType() { - return ChannelBufType.MESSAGE; - } - @Override protected Unsafe newUnsafe() { return new OioMessageUnsafe(); diff --git a/transport/src/main/java/io/netty/channel/socket/oio/OioDatagramChannel.java b/transport/src/main/java/io/netty/channel/socket/oio/OioDatagramChannel.java index 2c8335ec94..47da1e5462 100755 --- a/transport/src/main/java/io/netty/channel/socket/oio/OioDatagramChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/oio/OioDatagramChannel.java @@ -16,10 +16,12 @@ package io.netty.channel.socket.oio; import io.netty.buffer.ByteBuf; +import io.netty.buffer.ChannelBufType; import io.netty.buffer.MessageBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelException; import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelMetadata; import io.netty.channel.socket.DatagramChannel; import io.netty.channel.socket.DatagramChannelConfig; import io.netty.channel.socket.DatagramPacket; @@ -42,6 +44,8 @@ public class OioDatagramChannel extends AbstractOioMessageChannel private static final InternalLogger logger = InternalLoggerFactory.getInstance(OioDatagramChannel.class); + private static final ChannelMetadata METADATA = new ChannelMetadata(ChannelBufType.MESSAGE, true); + private static final byte[] EMPTY_DATA = new byte[0]; private final MulticastSocket socket; @@ -85,6 +89,11 @@ public class OioDatagramChannel extends AbstractOioMessageChannel config = new DefaultDatagramChannelConfig(socket); } + @Override + public ChannelMetadata metadata() { + return METADATA; + } + @Override public DatagramChannelConfig config() { return config; diff --git a/transport/src/main/java/io/netty/channel/socket/oio/OioServerSocketChannel.java b/transport/src/main/java/io/netty/channel/socket/oio/OioServerSocketChannel.java index 43dd539948..740d58dc90 100755 --- a/transport/src/main/java/io/netty/channel/socket/oio/OioServerSocketChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/oio/OioServerSocketChannel.java @@ -15,8 +15,10 @@ */ package io.netty.channel.socket.oio; +import io.netty.buffer.ChannelBufType; import io.netty.buffer.MessageBuf; import io.netty.channel.ChannelException; +import io.netty.channel.ChannelMetadata; import io.netty.channel.socket.DefaultServerSocketChannelConfig; import io.netty.channel.socket.ServerSocketChannel; import io.netty.channel.socket.ServerSocketChannelConfig; @@ -38,6 +40,8 @@ public class OioServerSocketChannel extends AbstractOioMessageChannel private static final InternalLogger logger = InternalLoggerFactory.getInstance(OioServerSocketChannel.class); + private static final ChannelMetadata METADATA = new ChannelMetadata(ChannelBufType.MESSAGE, false); + private static ServerSocket newServerSocket() { try { return new ServerSocket(); @@ -88,6 +92,11 @@ public class OioServerSocketChannel extends AbstractOioMessageChannel config = new DefaultServerSocketChannelConfig(socket); } + @Override + public ChannelMetadata metadata() { + return METADATA; + } + @Override public ServerSocketChannelConfig config() { return config; diff --git a/transport/src/main/java/io/netty/channel/socket/oio/OioSocketChannel.java b/transport/src/main/java/io/netty/channel/socket/oio/OioSocketChannel.java index 390553666d..b7b45cd55e 100755 --- a/transport/src/main/java/io/netty/channel/socket/oio/OioSocketChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/oio/OioSocketChannel.java @@ -16,8 +16,10 @@ package io.netty.channel.socket.oio; import io.netty.buffer.ByteBuf; +import io.netty.buffer.ChannelBufType; import io.netty.channel.Channel; import io.netty.channel.ChannelException; +import io.netty.channel.ChannelMetadata; import io.netty.channel.socket.DefaultSocketChannelConfig; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.SocketChannelConfig; @@ -38,6 +40,8 @@ public class OioSocketChannel extends AbstractOioByteChannel private static final InternalLogger logger = InternalLoggerFactory.getInstance(OioSocketChannel.class); + private static final ChannelMetadata METADATA = new ChannelMetadata(ChannelBufType.BYTE, false); + private final Socket socket; private final SocketChannelConfig config; private InputStream is; @@ -77,6 +81,11 @@ public class OioSocketChannel extends AbstractOioByteChannel } } + @Override + public ChannelMetadata metadata() { + return METADATA; + } + @Override public SocketChannelConfig config() { return config; From 81eaea77bda1dc9e677283f0bd3d30d2d41e242a Mon Sep 17 00:00:00 2001 From: Trustin Lee Date: Tue, 19 Jun 2012 10:43:38 +0900 Subject: [PATCH 36/87] Add DatagramChannel.isConnected() .. because there is no way for a user with isActive to know if DatagramChannel is connected or not --- .../main/java/io/netty/channel/socket/DatagramChannel.java | 2 ++ .../java/io/netty/channel/socket/nio/NioDatagramChannel.java | 5 +++++ .../java/io/netty/channel/socket/oio/OioDatagramChannel.java | 5 +++++ 3 files changed, 12 insertions(+) diff --git a/transport/src/main/java/io/netty/channel/socket/DatagramChannel.java b/transport/src/main/java/io/netty/channel/socket/DatagramChannel.java index d430a73524..7901d238d3 100755 --- a/transport/src/main/java/io/netty/channel/socket/DatagramChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/DatagramChannel.java @@ -35,6 +35,8 @@ public interface DatagramChannel extends Channel { @Override InetSocketAddress remoteAddress(); + boolean isConnected(); + /** * Joins a multicast group. */ diff --git a/transport/src/main/java/io/netty/channel/socket/nio/NioDatagramChannel.java b/transport/src/main/java/io/netty/channel/socket/nio/NioDatagramChannel.java index 483b8b2fa9..19170c9093 100755 --- a/transport/src/main/java/io/netty/channel/socket/nio/NioDatagramChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/nio/NioDatagramChannel.java @@ -112,6 +112,11 @@ public final class NioDatagramChannel return ch.isOpen() && ch.socket().isBound(); } + @Override + public boolean isConnected() { + return javaChannel().isConnected(); + } + @Override protected DatagramChannel javaChannel() { return (DatagramChannel) super.javaChannel(); diff --git a/transport/src/main/java/io/netty/channel/socket/oio/OioDatagramChannel.java b/transport/src/main/java/io/netty/channel/socket/oio/OioDatagramChannel.java index 47da1e5462..9e53c57bfe 100755 --- a/transport/src/main/java/io/netty/channel/socket/oio/OioDatagramChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/oio/OioDatagramChannel.java @@ -109,6 +109,11 @@ public class OioDatagramChannel extends AbstractOioMessageChannel return isOpen() && socket.isBound(); } + @Override + public boolean isConnected() { + return socket.isConnected(); + } + @Override protected SocketAddress localAddress0() { return socket.getLocalSocketAddress(); From 1d0d4fcd78735371d3540f004fba9e97b5d5a66e Mon Sep 17 00:00:00 2001 From: Trustin Lee Date: Tue, 19 Jun 2012 11:35:43 +0900 Subject: [PATCH 37/87] Fix documentation errors --- .../java/io/netty/handler/codec/compression/ZlibDecoder.java | 1 - .../java/io/netty/handler/codec/compression/ZlibEncoder.java | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/codec/src/main/java/io/netty/handler/codec/compression/ZlibDecoder.java b/codec/src/main/java/io/netty/handler/codec/compression/ZlibDecoder.java index a3a68e17e3..a44f9b95a9 100644 --- a/codec/src/main/java/io/netty/handler/codec/compression/ZlibDecoder.java +++ b/codec/src/main/java/io/netty/handler/codec/compression/ZlibDecoder.java @@ -22,7 +22,6 @@ import io.netty.handler.codec.ByteToByteDecoder; * Decompresses a {@link ByteBuf} using the deflate algorithm. * * @apiviz.landmark - * @apiviz.has io.netty.handler.codec.compression.ZlibWrapper */ public abstract class ZlibDecoder extends ByteToByteDecoder { diff --git a/codec/src/main/java/io/netty/handler/codec/compression/ZlibEncoder.java b/codec/src/main/java/io/netty/handler/codec/compression/ZlibEncoder.java index e81a0a0413..b52919fede 100644 --- a/codec/src/main/java/io/netty/handler/codec/compression/ZlibEncoder.java +++ b/codec/src/main/java/io/netty/handler/codec/compression/ZlibEncoder.java @@ -20,10 +20,9 @@ import io.netty.channel.ChannelFuture; import io.netty.handler.codec.ByteToByteEncoder; /** - * Decompresses a {@link ByteBuf} using the deflate algorithm. + * Compresses a {@link ByteBuf} using the deflate algorithm. * * @apiviz.landmark - * @apiviz.has io.netty.handler.codec.compression.ZlibWrapper */ public abstract class ZlibEncoder extends ByteToByteEncoder { From 1ef371b6255d672575f595fabd435130bcede273 Mon Sep 17 00:00:00 2001 From: Trustin Lee Date: Sun, 24 Jun 2012 12:59:44 +0900 Subject: [PATCH 38/87] Fix #405: CookieEncoder should refuse to encode more than one cookie .. if on server mode --- .../io/netty/handler/codec/http/CookieDecoder.java | 11 +++++------ .../io/netty/handler/codec/http/CookieEncoder.java | 7 ++++++- .../example/http/snoop/HttpSnoopServerHandler.java | 9 ++++++++- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/CookieDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/CookieDecoder.java index 960b609358..0565f2693d 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/CookieDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/CookieDecoder.java @@ -41,12 +41,11 @@ import java.util.regex.Pattern; */ public class CookieDecoder { - private static final Pattern PATTERN = - Pattern.compile( - // See: https://github.com/netty/netty/pull/96 - //"(?:\\s|[;,])*\\$*([^;=]+)(?:=(?:[\"']((?:\\\\.|[^\"])*)[\"']|([^;,]*)))?(\\s*(?:[;,]+\\s*|$))" - "(?:\\s|[;,])*\\$*([^;=]+)(?:=(?:[\"']((?:\\\\.|[^\"])*)[\"']|([^;]*)))?(\\s*(?:[;,]+\\s*|$))" - ); + private static final Pattern PATTERN = Pattern.compile( + // See: https://github.com/netty/netty/pull/96 + //"(?:\\s|[;,])*\\$*([^;=]+)(?:=(?:[\"']((?:\\\\.|[^\"])*)[\"']|([^;,]*)))?(\\s*(?:[;,]+\\s*|$))" + "(?:\\s|[;,])*\\$*([^;=]+)(?:=(?:[\"']((?:\\\\.|[^\"])*)[\"']|([^;]*)))?(\\s*(?:[;,]+\\s*|$))" + ); private static final String COMMA = ","; diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/CookieEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/CookieEncoder.java index 4ad856dc0e..a1acd5a4a6 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/CookieEncoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/CookieEncoder.java @@ -68,7 +68,7 @@ public class CookieEncoder { * this encoder. */ public void addCookie(String name, String value) { - cookies.add(new DefaultCookie(name, value)); + addCookie(new DefaultCookie(name, value)); } /** @@ -98,6 +98,11 @@ public class CookieEncoder { } private String encodeServerSide() { + if (cookies.size() > 1) { + throw new IllegalStateException( + "encode() can encode only one cookie on server mode: " + cookies.size() + " cookies added"); + } + StringBuilder sb = new StringBuilder(); for (Cookie cookie: cookies) { diff --git a/example/src/main/java/io/netty/example/http/snoop/HttpSnoopServerHandler.java b/example/src/main/java/io/netty/example/http/snoop/HttpSnoopServerHandler.java index c0e201ed46..cb08c3e0bc 100644 --- a/example/src/main/java/io/netty/example/http/snoop/HttpSnoopServerHandler.java +++ b/example/src/main/java/io/netty/example/http/snoop/HttpSnoopServerHandler.java @@ -144,9 +144,16 @@ public class HttpSnoopServerHandler extends ChannelInboundMessageHandlerAdapter< CookieEncoder cookieEncoder = new CookieEncoder(true); for (Cookie cookie : cookies) { cookieEncoder.addCookie(cookie); + response.addHeader(SET_COOKIE, cookieEncoder.encode()); } - response.addHeader(SET_COOKIE, cookieEncoder.encode()); } + } else { + // Browser sent no cookie. Add some. + CookieEncoder cookieEncoder = new CookieEncoder(true); + cookieEncoder.addCookie("key1", "value1"); + response.addHeader(SET_COOKIE, cookieEncoder.encode()); + cookieEncoder.addCookie("key2", "value2"); + response.addHeader(SET_COOKIE, cookieEncoder.encode()); } // Write the response. From 79425895e2a990fea23dd1fe104660c8c11a5e08 Mon Sep 17 00:00:00 2001 From: Trustin Lee Date: Sun, 24 Jun 2012 13:42:05 +0900 Subject: [PATCH 39/87] Fix test failures --- .../handler/codec/http/CookieEncoderTest.java | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/CookieEncoderTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/CookieEncoderTest.java index 3847e25f06..5887d1d979 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/CookieEncoderTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/CookieEncoderTest.java @@ -93,11 +93,24 @@ public class CookieEncoderTest { } @Test - public void testEncodingMultipleCookies() { - String c1 = "myCookie=myValue;Max-Age=50;Path=\"/apathsomewhere\";Domain=.adomainsomewhere;Secure;Comment=\"this is a Comment\";Version=1;CommentURL=\"http://aurl.com\";Port=\"80,8080\";Discard;"; - String c2 = "myCookie2=myValue2;Path=\"/anotherpathsomewhere\";Domain=.anotherdomainsomewhere;Comment=\"this is another Comment\";Version=1;CommentURL=\"http://anotherurl.com\";"; - String c3 = "myCookie3=myValue3;Version=1"; + public void testEncodingMultipleServerCookies() { CookieEncoder encoder = new CookieEncoder(true); + encoder.addCookie("a", "b"); + encoder.addCookie("b", "c"); + try { + encoder.encode(); + fail(); + } catch (IllegalStateException e) { + // Expected + } + } + + @Test + public void testEncodingMultipleClientCookies() { + String c1 = "$Version=1;myCookie=myValue;$Path=\"/apathsomewhere\";$Domain=.adomainsomewhere;$Port=\"80,8080\";"; + String c2 = "$Version=1;myCookie2=myValue2;$Path=\"/anotherpathsomewhere\";$Domain=.anotherdomainsomewhere;"; + String c3 = "$Version=1;myCookie3=myValue3"; + CookieEncoder encoder = new CookieEncoder(false); Cookie cookie = new DefaultCookie("myCookie", "myValue"); cookie.setVersion(1); cookie.setComment("this is a Comment"); From 7596ad8d584ea29456eeba01cd8e70870b298113 Mon Sep 17 00:00:00 2001 From: Trustin Lee Date: Sun, 24 Jun 2012 14:17:13 +0900 Subject: [PATCH 40/87] Fix #397: Allow all cookie names that conform to the RFC - Lenient flag is not needed anymore --- .../handler/codec/http/CookieDecoder.java | 23 ------------------- .../handler/codec/http/DefaultCookie.java | 22 ++---------------- .../handler/codec/http/CookieDecoderTest.java | 19 +++++++++++++++ 3 files changed, 21 insertions(+), 43 deletions(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/CookieDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/CookieDecoder.java index 0565f2693d..85a9b412ef 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/CookieDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/CookieDecoder.java @@ -49,24 +49,6 @@ public class CookieDecoder { private static final String COMMA = ","; - private final boolean lenient; - - /** - * Creates a new decoder with strict parsing. - */ - public CookieDecoder() { - this(false); - } - - /** - * Creates a new decoder. - * - * @param lenient ignores cookies with the name 'HTTPOnly' instead of throwing an exception - */ - public CookieDecoder(boolean lenient) { - this.lenient = lenient; - } - /** * Decodes the specified HTTP header value into {@link Cookie}s. * @@ -105,11 +87,6 @@ public class CookieDecoder { Set cookies = new TreeSet(); for (; i < names.size(); i ++) { String name = names.get(i); - // Not all user agents understand the HttpOnly attribute - if (lenient && CookieHeaderNames.HTTPONLY.equalsIgnoreCase(name)) { - continue; - } - String value = values.get(i); if (value == null) { value = ""; diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/DefaultCookie.java b/codec-http/src/main/java/io/netty/handler/codec/http/DefaultCookie.java index 9970d21339..81a9b93ebf 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/DefaultCookie.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/DefaultCookie.java @@ -15,8 +15,6 @@ */ package io.netty.handler.codec.http; -import io.netty.util.internal.CaseIgnoringComparator; - import java.util.Collections; import java.util.Set; import java.util.TreeSet; @@ -28,22 +26,6 @@ import java.util.TreeSet; */ public class DefaultCookie implements Cookie { - private static final Set RESERVED_NAMES = new TreeSet(CaseIgnoringComparator.INSTANCE); - - static { - RESERVED_NAMES.add("Domain"); - RESERVED_NAMES.add("Path"); - RESERVED_NAMES.add("Comment"); - RESERVED_NAMES.add("CommentURL"); - RESERVED_NAMES.add("Discard"); - RESERVED_NAMES.add("Port"); - RESERVED_NAMES.add("Max-Age"); - RESERVED_NAMES.add("Expires"); - RESERVED_NAMES.add("Version"); - RESERVED_NAMES.add("Secure"); - RESERVED_NAMES.add("HTTPOnly"); - } - private final String name; private String value; private String domain; @@ -87,8 +69,8 @@ public class DefaultCookie implements Cookie { } } - if (RESERVED_NAMES.contains(name)) { - throw new IllegalArgumentException("reserved name: " + name); + if (name.charAt(0) == '$') { + throw new IllegalArgumentException("name starting with '$' not allowed: " + name); } this.name = name; diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/CookieDecoderTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/CookieDecoderTest.java index 64484fadca..94e4ac047c 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/CookieDecoderTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/CookieDecoderTest.java @@ -371,4 +371,23 @@ public class CookieDecoderTest { Cookie c = cookies.iterator().next(); assertEquals("timeZoneName=(GMT+04:00) Moscow, St. Petersburg, Volgograd&promocode=®ion=BE", c.getValue()); } + + @Test + public void testDecodingWeirdNames1() { + String src = "path=; expires=Mon, 01-Jan-1990 00:00:00 GMT; path=/; domain=.www.google.com"; + Set cookies = new CookieDecoder().decode(src); + Cookie c = cookies.iterator().next(); + assertEquals("path", c.getName()); + assertEquals("", c.getValue()); + assertEquals("/", c.getPath()); + } + + @Test + public void testDecodingWeirdNames2() { + String src = "HTTPOnly="; + Set cookies = new CookieDecoder().decode(src); + Cookie c = cookies.iterator().next(); + assertEquals("HTTPOnly", c.getName()); + assertEquals("", c.getValue()); + } } From 217f8ce1fd26d7c125612ad6aa4f53819b403d12 Mon Sep 17 00:00:00 2001 From: Trustin Lee Date: Sun, 24 Jun 2012 18:07:47 +0900 Subject: [PATCH 41/87] Fix #218: CookieDecoder.decode() throws StackOverflowError - Rewrote key-value decoder not using a regular expression --- .../handler/codec/http/CookieDecoder.java | 164 ++++++++++++------ .../handler/codec/http/CookieDecoderTest.java | 63 ++++++- 2 files changed, 174 insertions(+), 53 deletions(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/CookieDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/CookieDecoder.java index 85a9b412ef..1c1f4c4c47 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/CookieDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/CookieDecoder.java @@ -21,8 +21,6 @@ import java.util.Collections; import java.util.List; import java.util.Set; import java.util.TreeSet; -import java.util.regex.Matcher; -import java.util.regex.Pattern; /** * Decodes an HTTP header value into {@link Cookie}s. This decoder can decode @@ -41,12 +39,6 @@ import java.util.regex.Pattern; */ public class CookieDecoder { - private static final Pattern PATTERN = Pattern.compile( - // See: https://github.com/netty/netty/pull/96 - //"(?:\\s|[;,])*\\$*([^;=]+)(?:=(?:[\"']((?:\\\\.|[^\"])*)[\"']|([^;,]*)))?(\\s*(?:[;,]+\\s*|$))" - "(?:\\s|[;,])*\\$*([^;=]+)(?:=(?:[\"']((?:\\\\.|[^\"])*)[\"']|([^;]*)))?(\\s*(?:[;,]+\\s*|$))" - ); - private static final String COMMA = ","; /** @@ -175,58 +167,126 @@ public class CookieDecoder { } private static void extractKeyValuePairs( - String header, List names, List values) { - Matcher m = PATTERN.matcher(header); - int pos = 0; - String name = null; - String value = null; - String separator = null; - while (m.find(pos)) { - pos = m.end(); + final String header, final List names, final List values) { - // Extract name and value pair from the match. - String newName = m.group(1); - String newValue = m.group(3); - if (newValue == null) { - newValue = decodeValue(m.group(2)); - } - String newSeparator = m.group(4); + final int headerLen = header.length(); + loop: for (int i = 0;;) { - if (name == null) { - name = newName; - value = newValue == null? "" : newValue; - separator = newSeparator; - continue; + // Skip spaces and separators. + for (;;) { + if (i == headerLen) { + break loop; + } + switch (header.charAt(i)) { + case '\t': case '\n': case 0x0b: case '\f': case '\r': + case ' ': case ',': case ';': + i ++; + continue; + } + break; } - if (newValue == null && - !CookieHeaderNames.DISCARD.equalsIgnoreCase(newName) && - !CookieHeaderNames.SECURE.equalsIgnoreCase(newName) && - !CookieHeaderNames.HTTPONLY.equalsIgnoreCase(newName)) { - value = value + separator + newName; - separator = newSeparator; - continue; + // Skip '$'. + for (;;) { + if (i == headerLen) { + break loop; + } + if (header.charAt(i) == '$') { + i ++; + continue; + } + break; + } + + String name; + String value; + + if (i == headerLen) { + name = null; + value = null; + } else { + int newNameStart = i; + keyValLoop: for (;;) { + switch (header.charAt(i)) { + case ';': + // NAME; (no value till ';') + name = header.substring(newNameStart, i); + value = null; + break keyValLoop; + case '=': + // NAME=VALUE + name = header.substring(newNameStart, i); + i ++; + if (i == headerLen) { + // NAME= (empty value, i.e. nothing after '=') + value = ""; + break keyValLoop; + } + + int newValueStart = i; + char c = header.charAt(i); + if (c == '"' || c == '\'') { + // NAME="VALUE" or NAME='VALUE' + StringBuilder newValueBuf = new StringBuilder(header.length() - i); + final char q = c; + boolean hadBackslash = false; + i ++; + for (;;) { + if (i == headerLen) { + value = newValueBuf.toString(); + break keyValLoop; + } + if (hadBackslash) { + hadBackslash = false; + c = header.charAt(i ++); + switch (c) { + case '\\': case '"': case '\'': + // Escape last backslash. + newValueBuf.setCharAt(newValueBuf.length() - 1, c); + break; + default: + // Do not escape last backslash. + newValueBuf.append(c); + } + } else { + c = header.charAt(i ++); + if (c == q) { + value = newValueBuf.toString(); + break keyValLoop; + } + newValueBuf.append(c); + if (c == '\\') { + hadBackslash = true; + } + } + } + } else { + // NAME=VALUE; + int semiPos = header.indexOf(';', i); + if (semiPos > 0) { + value = header.substring(newValueStart, semiPos); + i = semiPos; + } else { + value = header.substring(newValueStart); + i = headerLen; + } + } + break keyValLoop; + default: + i ++; + } + + if (i == headerLen) { + // NAME (no value till the end of string) + name = header.substring(newNameStart); + value = null; + break; + } + } } names.add(name); values.add(value); - - name = newName; - value = newValue; - separator = newSeparator; } - - // The last entry - if (name != null) { - names.add(name); - values.add(value); - } - } - - private static String decodeValue(String value) { - if (value == null) { - return value; - } - return value.replace("\\\"", "\"").replace("\\\\", "\\"); } } diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/CookieDecoderTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/CookieDecoderTest.java index 94e4ac047c..11982f1199 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/CookieDecoderTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/CookieDecoderTest.java @@ -271,7 +271,9 @@ public class CookieDecoderTest { "d=\"1\\\"2\\\"3\"," + "e=\"\\\"\\\"\"," + "f=\"1\\\"\\\"2\"," + - "g=\"\\\\\""; + "g=\"\\\\\"," + + "h=\"';,\\x\""; + Set cookies = new CookieDecoder().decode(source); Iterator it = cookies.iterator(); @@ -305,6 +307,10 @@ public class CookieDecoderTest { assertEquals("g", c.getName()); assertEquals("\\", c.getValue()); + c = it.next(); + assertEquals("h", c.getName()); + assertEquals("';,\\x", c.getValue()); + assertFalse(it.hasNext()); } @@ -390,4 +396,59 @@ public class CookieDecoderTest { assertEquals("HTTPOnly", c.getName()); assertEquals("", c.getValue()); } + + @Test + public void testDecodingLongValue() { + String longValue = + "b!!!$Q!!$ha!!!!!!" + + "%=J^wI!!3iD!!!!$=HbQW!!3iF!!!!#=J^wI!!3iH!!!!%=J^wI!!3iM!!!!%=J^wI!!3iS!!!!" + + "#=J^wI!!3iU!!!!%=J^wI!!3iZ!!!!#=J^wI!!3i]!!!!%=J^wI!!3ig!!!!%=J^wI!!3ij!!!!" + + "%=J^wI!!3ik!!!!#=J^wI!!3il!!!!$=HbQW!!3in!!!!%=J^wI!!3ip!!!!$=HbQW!!3iq!!!!" + + "$=HbQW!!3it!!!!%=J^wI!!3ix!!!!#=J^wI!!3j!!!!!$=HbQW!!3j%!!!!$=HbQW!!3j'!!!!" + + "%=J^wI!!3j(!!!!%=J^wI!!9mJ!!!!'=KqtH!!=SE!!M!!!!" + + "'=KqtH!!s1X!!!!$=MMyc!!s1_!!!!#=MN#O!!ypn!!!!'=KqtH!!ypr!!!!'=KqtH!#%h!!!!!" + + "%=KqtH!#%o!!!!!'=KqtH!#)H6!!!!!!'=KqtH!#]9R!!!!$=H/Lt!#]I6!!!!#=KqtH!#]Z#!!!!%=KqtH!#^*N!!!!" + + "#=KqtH!#^:m!!!!#=KqtH!#_*_!!!!%=J^wI!#`-7!!!!#=KqtH!#`T>!!!!'=KqtH!#`T?!!!!" + + "'=KqtH!#`TA!!!!'=KqtH!#`TB!!!!'=KqtH!#`TG!!!!'=KqtH!#`TP!!!!#=KqtH!#`U,!!!!" + + "'=KqtH!#`U/!!!!'=KqtH!#`U0!!!!#=KqtH!#`U9!!!!'=KqtH!#aEQ!!!!%=KqtH!#b<)!!!!" + + "'=KqtH!#c9-!!!!%=KqtH!#dxC!!!!%=KqtH!#dxE!!!!%=KqtH!#ev$!!!!'=KqtH!#fBi!!!!" + + "#=KqtH!#fBj!!!!'=KqtH!#fG)!!!!'=KqtH!#fG+!!!!'=KqtH!#g*B!!!!'=KqtH!$>hD!!!!+=J^x0!$?lW!!!!'=KqtH!$?ll!!!!'=KqtH!$?lm!!!!" + + "%=KqtH!$?mi!!!!'=KqtH!$?mx!!!!'=KqtH!$D7]!!!!#=J_#p!$D@T!!!!#=J_#p!$V cookies = new CookieDecoder().decode("bh=\"" + longValue + "\";"); + assertEquals(1, cookies.size()); + Cookie c = cookies.iterator().next(); + assertEquals("bh", c.getName()); + assertEquals(longValue, c.getValue()); + } } From c77af321427537be8d7526782ec9232c1a17ea3a Mon Sep 17 00:00:00 2001 From: Trustin Lee Date: Sun, 24 Jun 2012 22:14:17 +0900 Subject: [PATCH 42/87] Add CompositeByteBuf.numComponents() --- buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java index 636600b393..9329de9023 100644 --- a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java @@ -165,6 +165,10 @@ public class CompositeByteBuf extends AbstractByteBuf { return indices[components.length]; } + public int numComponents() { + return components.length; + } + @Override public byte getByte(int index) { int componentId = componentId(index); From 0c55c85d06ccd2f0bd38a5376417b5c6b8c09aef Mon Sep 17 00:00:00 2001 From: Trustin Lee Date: Tue, 26 Jun 2012 05:26:54 +0900 Subject: [PATCH 43/87] Make CookieEncoder and CookieDecoder stateless - Also: CookieEncoder is split into ServerCookieEncoder and ClientCookieEncoder --- .../codec/http/ClientCookieEncoder.java | 122 ++++++++ .../handler/codec/http/CookieDecoder.java | 11 +- .../handler/codec/http/CookieEncoder.java | 269 ------------------ .../handler/codec/http/CookieEncoderUtil.java | 79 +++++ .../netty/handler/codec/http/HttpRequest.java | 8 +- .../handler/codec/http/HttpResponse.java | 8 +- .../codec/http/ServerCookieEncoder.java | 155 ++++++++++ .../handler/codec/http/CookieDecoderTest.java | 36 +-- .../handler/codec/http/CookieEncoderTest.java | 44 +-- .../example/http/snoop/HttpSnoopClient.java | 12 +- .../http/snoop/HttpSnoopServerHandler.java | 18 +- 11 files changed, 411 insertions(+), 351 deletions(-) create mode 100644 codec-http/src/main/java/io/netty/handler/codec/http/ClientCookieEncoder.java delete mode 100644 codec-http/src/main/java/io/netty/handler/codec/http/CookieEncoder.java create mode 100644 codec-http/src/main/java/io/netty/handler/codec/http/CookieEncoderUtil.java create mode 100644 codec-http/src/main/java/io/netty/handler/codec/http/ServerCookieEncoder.java diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/ClientCookieEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/ClientCookieEncoder.java new file mode 100644 index 0000000000..4dafb2a9c3 --- /dev/null +++ b/codec-http/src/main/java/io/netty/handler/codec/http/ClientCookieEncoder.java @@ -0,0 +1,122 @@ +package io.netty.handler.codec.http; + +import static io.netty.handler.codec.http.CookieEncoderUtil.*; + +import java.util.Collection; + +/** + * Encodes client-side {@link Cookie}s into an HTTP header value. This encoder can encode + * the HTTP cookie version 0, 1, and 2. + *
+ * // Example
+ * {@link HttpRequest} req = ...;
+ * res.setHeader("Cookie", {@link ClientCookieEncoder}.encode("JSESSIONID", "1234"));
+ * 
+ * + * @see CookieDecoder + * + * @apiviz.stereotype utility + * @apiviz.has io.netty.handler.codec.http.Cookie oneway - - encodes + */ +public final class ClientCookieEncoder { + + /** + * Encodes the specified cookie into an HTTP header value. + */ + public static String encode(String name, String value) { + return encode(new DefaultCookie(name, value)); + } + + public static String encode(Cookie cookie) { + if (cookie == null) { + throw new NullPointerException("cookie"); + } + + StringBuilder buf = new StringBuilder(); + encode(buf, cookie); + return stripTrailingSeparator(buf); + } + + public static String encode(Cookie... cookies) { + if (cookies == null) { + throw new NullPointerException("cookies"); + } + + StringBuilder buf = new StringBuilder(); + for (Cookie c: cookies) { + if (c == null) { + break; + } + + encode(buf, c); + } + return stripTrailingSeparator(buf); + } + + public static String encode(Collection cookies) { + if (cookies == null) { + throw new NullPointerException("cookies"); + } + + StringBuilder buf = new StringBuilder(); + for (Cookie c: cookies) { + if (c == null) { + break; + } + + encode(buf, c); + } + return stripTrailingSeparator(buf); + } + + public static String encode(Iterable cookies) { + if (cookies == null) { + throw new NullPointerException("cookies"); + } + + StringBuilder buf = new StringBuilder(); + for (Cookie c: cookies) { + if (c == null) { + break; + } + + encode(buf, c); + } + return stripTrailingSeparator(buf); + } + + private static void encode(StringBuilder buf, Cookie c) { + if (c.getVersion() >= 1) { + add(buf, '$' + CookieHeaderNames.VERSION, 1); + } + + add(buf, c.getName(), c.getValue()); + + if (c.getPath() != null) { + add(buf, '$' + CookieHeaderNames.PATH, c.getPath()); + } + + if (c.getDomain() != null) { + add(buf, '$' + CookieHeaderNames.DOMAIN, c.getDomain()); + } + + if (c.getVersion() >= 1) { + if (!c.getPorts().isEmpty()) { + buf.append('$'); + buf.append(CookieHeaderNames.PORT); + buf.append((char) HttpConstants.EQUALS); + buf.append((char) HttpConstants.DOUBLE_QUOTE); + for (int port: c.getPorts()) { + buf.append(port); + buf.append((char) HttpConstants.COMMA); + } + buf.setCharAt(buf.length() - 1, (char) HttpConstants.DOUBLE_QUOTE); + buf.append((char) HttpConstants.SEMICOLON); + } + } + } + + private ClientCookieEncoder() { + // Unused + } +} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/CookieDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/CookieDecoder.java index 1c1f4c4c47..2592c010ea 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/CookieDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/CookieDecoder.java @@ -32,12 +32,13 @@ import java.util.TreeSet; * Set<{@link Cookie}> cookies = new {@link CookieDecoder}().decode(value); * * - * @see CookieEncoder + * @see ClientCookieEncoder + * @see ServerCookieEncoder * * @apiviz.stereotype utility * @apiviz.has io.netty.handler.codec.http.Cookie oneway - - decodes */ -public class CookieDecoder { +public final class CookieDecoder { private static final String COMMA = ","; @@ -46,7 +47,7 @@ public class CookieDecoder { * * @return the decoded {@link Cookie}s */ - public Set decode(String header) { + public static Set decode(String header) { List names = new ArrayList(8); List values = new ArrayList(8); extractKeyValuePairs(header, names, values); @@ -289,4 +290,8 @@ public class CookieDecoder { values.add(value); } } + + private CookieDecoder() { + // Unused + } } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/CookieEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/CookieEncoder.java deleted file mode 100644 index a1acd5a4a6..0000000000 --- a/codec-http/src/main/java/io/netty/handler/codec/http/CookieEncoder.java +++ /dev/null @@ -1,269 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec.http; - -import java.util.Date; -import java.util.Set; -import java.util.TreeSet; - -/** - * Encodes {@link Cookie}s into an HTTP header value. This encoder can encode - * the HTTP cookie version 0, 1, and 2. - *

- * This encoder is stateful. It maintains an internal data structure that - * holds the {@link Cookie}s added by the {@link #addCookie(String, String)} - * method. Once {@link #encode()} is called, all added {@link Cookie}s are - * encoded into an HTTP header value and all {@link Cookie}s in the internal - * data structure are removed so that the encoder can start over. - *

- * // Client-side example
- * {@link HttpRequest} req = ...;
- * {@link CookieEncoder} encoder = new {@link CookieEncoder}(false);
- * encoder.addCookie("JSESSIONID", "1234");
- * res.setHeader("Cookie", encoder.encode());
- *
- * // Server-side example
- * {@link HttpResponse} res = ...;
- * {@link CookieEncoder} encoder = new {@link CookieEncoder}(true);
- * encoder.addCookie("JSESSIONID", "1234");
- * res.setHeader("Set-Cookie", encoder.encode());
- * 
- * - * @see CookieDecoder - * - * @apiviz.stereotype utility - * @apiviz.has io.netty.handler.codec.http.Cookie oneway - - encodes - */ -public class CookieEncoder { - - private final Set cookies = new TreeSet(); - private final boolean server; - - /** - * Creates a new encoder. - * - * @param server {@code true} if and only if this encoder is supposed to - * encode server-side cookies. {@code false} if and only if - * this encoder is supposed to encode client-side cookies. - */ - public CookieEncoder(boolean server) { - this.server = server; - } - - /** - * Adds a new {@link Cookie} created with the specified name and value to - * this encoder. - */ - public void addCookie(String name, String value) { - addCookie(new DefaultCookie(name, value)); - } - - /** - * Adds the specified {@link Cookie} to this encoder. - */ - public void addCookie(Cookie cookie) { - cookies.add(cookie); - } - - /** - * Encodes the {@link Cookie}s which were added by {@link #addCookie(Cookie)} - * so far into an HTTP header value. If no {@link Cookie}s were added, - * an empty string is returned. - * - * Be aware that calling this method will clear the {@link Cookie}s you added to - * this encoder. - */ - public String encode() { - String answer; - if (server) { - answer = encodeServerSide(); - } else { - answer = encodeClientSide(); - } - cookies.clear(); - return answer; - } - - private String encodeServerSide() { - if (cookies.size() > 1) { - throw new IllegalStateException( - "encode() can encode only one cookie on server mode: " + cookies.size() + " cookies added"); - } - - StringBuilder sb = new StringBuilder(); - - for (Cookie cookie: cookies) { - add(sb, cookie.getName(), cookie.getValue()); - - if (cookie.getMaxAge() >= 0) { - if (cookie.getVersion() == 0) { - addUnquoted(sb, CookieHeaderNames.EXPIRES, - new HttpHeaderDateFormat().format( - new Date(System.currentTimeMillis() + - cookie.getMaxAge() * 1000L))); - } else { - add(sb, CookieHeaderNames.MAX_AGE, cookie.getMaxAge()); - } - } - - if (cookie.getPath() != null) { - if (cookie.getVersion() > 0) { - add(sb, CookieHeaderNames.PATH, cookie.getPath()); - } else { - addUnquoted(sb, CookieHeaderNames.PATH, cookie.getPath()); - } - } - - if (cookie.getDomain() != null) { - if (cookie.getVersion() > 0) { - add(sb, CookieHeaderNames.DOMAIN, cookie.getDomain()); - } else { - addUnquoted(sb, CookieHeaderNames.DOMAIN, cookie.getDomain()); - } - } - if (cookie.isSecure()) { - sb.append(CookieHeaderNames.SECURE); - sb.append((char) HttpConstants.SEMICOLON); - } - if (cookie.isHttpOnly()) { - sb.append(CookieHeaderNames.HTTPONLY); - sb.append((char) HttpConstants.SEMICOLON); - } - if (cookie.getVersion() >= 1) { - if (cookie.getComment() != null) { - add(sb, CookieHeaderNames.COMMENT, cookie.getComment()); - } - - add(sb, CookieHeaderNames.VERSION, 1); - - if (cookie.getCommentUrl() != null) { - addQuoted(sb, CookieHeaderNames.COMMENTURL, cookie.getCommentUrl()); - } - - if (!cookie.getPorts().isEmpty()) { - sb.append(CookieHeaderNames.PORT); - sb.append((char) HttpConstants.EQUALS); - sb.append((char) HttpConstants.DOUBLE_QUOTE); - for (int port: cookie.getPorts()) { - sb.append(port); - sb.append((char) HttpConstants.COMMA); - } - sb.setCharAt(sb.length() - 1, (char) HttpConstants.DOUBLE_QUOTE); - sb.append((char) HttpConstants.SEMICOLON); - } - if (cookie.isDiscard()) { - sb.append(CookieHeaderNames.DISCARD); - sb.append((char) HttpConstants.SEMICOLON); - } - } - } - - if (sb.length() > 0) { - sb.setLength(sb.length() - 1); - } - - return sb.toString(); - } - - private String encodeClientSide() { - StringBuilder sb = new StringBuilder(); - - for (Cookie cookie: cookies) { - if (cookie.getVersion() >= 1) { - add(sb, '$' + CookieHeaderNames.VERSION, 1); - } - - add(sb, cookie.getName(), cookie.getValue()); - - if (cookie.getPath() != null) { - add(sb, '$' + CookieHeaderNames.PATH, cookie.getPath()); - } - - if (cookie.getDomain() != null) { - add(sb, '$' + CookieHeaderNames.DOMAIN, cookie.getDomain()); - } - - if (cookie.getVersion() >= 1) { - if (!cookie.getPorts().isEmpty()) { - sb.append('$'); - sb.append(CookieHeaderNames.PORT); - sb.append((char) HttpConstants.EQUALS); - sb.append((char) HttpConstants.DOUBLE_QUOTE); - for (int port: cookie.getPorts()) { - sb.append(port); - sb.append((char) HttpConstants.COMMA); - } - sb.setCharAt(sb.length() - 1, (char) HttpConstants.DOUBLE_QUOTE); - sb.append((char) HttpConstants.SEMICOLON); - } - } - } - - if (sb.length() > 0) { - sb.setLength(sb.length() - 1); - } - - return sb.toString(); - } - - private static void add(StringBuilder sb, String name, String val) { - if (val == null) { - addQuoted(sb, name, ""); - return; - } - - for (int i = 0; i < val.length(); i ++) { - char c = val.charAt(i); - switch (c) { - case '\t': case ' ': case '"': case '(': case ')': case ',': - case '/': case ':': case ';': case '<': case '=': case '>': - case '?': case '@': case '[': case '\\': case ']': - case '{': case '}': - addQuoted(sb, name, val); - return; - } - } - - addUnquoted(sb, name, val); - } - - private static void addUnquoted(StringBuilder sb, String name, String val) { - sb.append(name); - sb.append((char) HttpConstants.EQUALS); - sb.append(val); - sb.append((char) HttpConstants.SEMICOLON); - } - - private static void addQuoted(StringBuilder sb, String name, String val) { - if (val == null) { - val = ""; - } - - sb.append(name); - sb.append((char) HttpConstants.EQUALS); - sb.append((char) HttpConstants.DOUBLE_QUOTE); - sb.append(val.replace("\\", "\\\\").replace("\"", "\\\"")); - sb.append((char) HttpConstants.DOUBLE_QUOTE); - sb.append((char) HttpConstants.SEMICOLON); - } - - private static void add(StringBuilder sb, String name, long val) { - sb.append(name); - sb.append((char) HttpConstants.EQUALS); - sb.append(val); - sb.append((char) HttpConstants.SEMICOLON); - } -} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/CookieEncoderUtil.java b/codec-http/src/main/java/io/netty/handler/codec/http/CookieEncoderUtil.java new file mode 100644 index 0000000000..8891dee841 --- /dev/null +++ b/codec-http/src/main/java/io/netty/handler/codec/http/CookieEncoderUtil.java @@ -0,0 +1,79 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.codec.http; + + +final class CookieEncoderUtil { + + static String stripTrailingSeparator(StringBuilder buf) { + if (buf.length() > 0) { + buf.setLength(buf.length() - 1); + } + return buf.toString(); + } + + static void add(StringBuilder sb, String name, String val) { + if (val == null) { + addQuoted(sb, name, ""); + return; + } + + for (int i = 0; i < val.length(); i ++) { + char c = val.charAt(i); + switch (c) { + case '\t': case ' ': case '"': case '(': case ')': case ',': + case '/': case ':': case ';': case '<': case '=': case '>': + case '?': case '@': case '[': case '\\': case ']': + case '{': case '}': + addQuoted(sb, name, val); + return; + } + } + + addUnquoted(sb, name, val); + } + + static void addUnquoted(StringBuilder sb, String name, String val) { + sb.append(name); + sb.append((char) HttpConstants.EQUALS); + sb.append(val); + sb.append((char) HttpConstants.SEMICOLON); + } + + static void addQuoted(StringBuilder sb, String name, String val) { + if (val == null) { + val = ""; + } + + sb.append(name); + sb.append((char) HttpConstants.EQUALS); + sb.append((char) HttpConstants.DOUBLE_QUOTE); + sb.append(val.replace("\\", "\\\\").replace("\"", "\\\"")); + sb.append((char) HttpConstants.DOUBLE_QUOTE); + sb.append((char) HttpConstants.SEMICOLON); + } + + static void add(StringBuilder sb, String name, long val) { + sb.append(name); + sb.append((char) HttpConstants.EQUALS); + sb.append(val); + sb.append((char) HttpConstants.SEMICOLON); + } + + private CookieEncoderUtil() { + // Unused + } +} diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpRequest.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpRequest.java index c3f25c0e82..a6cfe6a1b3 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpRequest.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpRequest.java @@ -23,10 +23,12 @@ package io.netty.handler.codec.http; *

* Unlike the Servlet API, a query string is constructed and decomposed by * {@link QueryStringEncoder} and {@link QueryStringDecoder}. {@link Cookie} - * support is also provided separately via {@link CookieEncoder} and - * {@link CookieDecoder}. + * support is also provided separately via {@link CookieDecoder}, {@link ClientCookieEncoder}, + * and {@link @ServerCookieEncoder}. + * * @see HttpResponse - * @see CookieEncoder + * @see ClientCookieEncoder + * @see ServerCookieEncoder * @see CookieDecoder */ public interface HttpRequest extends HttpMessage { diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpResponse.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpResponse.java index 8f664d83d0..0eb4023ea4 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpResponse.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpResponse.java @@ -21,11 +21,13 @@ package io.netty.handler.codec.http; * *

Accessing Cookie

*

- * Unlike the Servlet API, {@link Cookie} support is provided separately via - * {@link CookieEncoder} and {@link CookieDecoder}. + * Unlike the Servlet API, {@link Cookie} support is provided separately via {@link CookieDecoder}, + * {@link ClientCookieEncoder}, and {@link ServerCookieEncoder}. + * * @see HttpRequest - * @see CookieEncoder * @see CookieDecoder + * @see ClientCookieEncoder + * @see ServerCookieEncoder */ public interface HttpResponse extends HttpMessage { diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/ServerCookieEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/ServerCookieEncoder.java new file mode 100644 index 0000000000..6caa7f1e31 --- /dev/null +++ b/codec-http/src/main/java/io/netty/handler/codec/http/ServerCookieEncoder.java @@ -0,0 +1,155 @@ +package io.netty.handler.codec.http; + +import static io.netty.handler.codec.http.CookieEncoderUtil.*; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.List; + +/** + * Encodes server-side {@link Cookie}s into HTTP header values. This encoder can encode + * the HTTP cookie version 0, 1, and 2. + *

+ * // Example
+ * {@link HttpRequest} req = ...;
+ * res.setHeader("Set-Cookie", {@link ServerCookieEncoder}.encode("JSESSIONID", "1234"));
+ * 
+ * + * @see CookieDecoder + * + * @apiviz.stereotype utility + * @apiviz.has io.netty.handler.codec.http.Cookie oneway - - encodes + */ +public final class ServerCookieEncoder { + + /** + * Encodes the specified cookie into an HTTP header value. + */ + public static String encode(String name, String value) { + return encode(new DefaultCookie(name, value)); + } + + public static String encode(Cookie cookie) { + if (cookie == null) { + throw new NullPointerException("cookie"); + } + + StringBuilder buf = new StringBuilder(); + + add(buf, cookie.getName(), cookie.getValue()); + + if (cookie.getMaxAge() >= 0) { + if (cookie.getVersion() == 0) { + addUnquoted(buf, CookieHeaderNames.EXPIRES, + new HttpHeaderDateFormat().format( + new Date(System.currentTimeMillis() + + cookie.getMaxAge() * 1000L))); + } else { + add(buf, CookieHeaderNames.MAX_AGE, cookie.getMaxAge()); + } + } + + if (cookie.getPath() != null) { + if (cookie.getVersion() > 0) { + add(buf, CookieHeaderNames.PATH, cookie.getPath()); + } else { + addUnquoted(buf, CookieHeaderNames.PATH, cookie.getPath()); + } + } + + if (cookie.getDomain() != null) { + if (cookie.getVersion() > 0) { + add(buf, CookieHeaderNames.DOMAIN, cookie.getDomain()); + } else { + addUnquoted(buf, CookieHeaderNames.DOMAIN, cookie.getDomain()); + } + } + if (cookie.isSecure()) { + buf.append(CookieHeaderNames.SECURE); + buf.append((char) HttpConstants.SEMICOLON); + } + if (cookie.isHttpOnly()) { + buf.append(CookieHeaderNames.HTTPONLY); + buf.append((char) HttpConstants.SEMICOLON); + } + if (cookie.getVersion() >= 1) { + if (cookie.getComment() != null) { + add(buf, CookieHeaderNames.COMMENT, cookie.getComment()); + } + + add(buf, CookieHeaderNames.VERSION, 1); + + if (cookie.getCommentUrl() != null) { + addQuoted(buf, CookieHeaderNames.COMMENTURL, cookie.getCommentUrl()); + } + + if (!cookie.getPorts().isEmpty()) { + buf.append(CookieHeaderNames.PORT); + buf.append((char) HttpConstants.EQUALS); + buf.append((char) HttpConstants.DOUBLE_QUOTE); + for (int port: cookie.getPorts()) { + buf.append(port); + buf.append((char) HttpConstants.COMMA); + } + buf.setCharAt(buf.length() - 1, (char) HttpConstants.DOUBLE_QUOTE); + buf.append((char) HttpConstants.SEMICOLON); + } + if (cookie.isDiscard()) { + buf.append(CookieHeaderNames.DISCARD); + buf.append((char) HttpConstants.SEMICOLON); + } + } + + return stripTrailingSeparator(buf); + } + + public static List encode(Cookie... cookies) { + if (cookies == null) { + throw new NullPointerException("cookies"); + } + + List encoded = new ArrayList(cookies.length); + for (Cookie c: cookies) { + if (c == null) { + break; + } + encoded.add(encode(c)); + } + return encoded; + } + + public static List encode(Collection cookies) { + if (cookies == null) { + throw new NullPointerException("cookies"); + } + + List encoded = new ArrayList(cookies.size()); + for (Cookie c: cookies) { + if (c == null) { + break; + } + encoded.add(encode(c)); + } + return encoded; + } + + public static List encode(Iterable cookies) { + if (cookies == null) { + throw new NullPointerException("cookies"); + } + + List encoded = new ArrayList(); + for (Cookie c: cookies) { + if (c == null) { + break; + } + encoded.add(encode(c)); + } + return encoded; + } + + private ServerCookieEncoder() { + // Unused + } +} diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/CookieDecoderTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/CookieDecoderTest.java index 11982f1199..2055e4994b 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/CookieDecoderTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/CookieDecoderTest.java @@ -31,8 +31,7 @@ public class CookieDecoderTest { String cookieString = "myCookie=myValue;expires=XXX;path=/apathsomewhere;domain=.adomainsomewhere;secure;"; cookieString = cookieString.replace("XXX", new HttpHeaderDateFormat().format(new Date(System.currentTimeMillis() + 50000))); - CookieDecoder cookieDecoder = new CookieDecoder(); - Set cookies = cookieDecoder.decode(cookieString); + Set cookies = CookieDecoder.decode(cookieString); assertEquals(1, cookies.size()); Cookie cookie = cookies.iterator().next(); assertNotNull(cookie); @@ -62,8 +61,7 @@ public class CookieDecoderTest { @Test public void testDecodingSingleCookieV0ExtraParamsIgnored() { String cookieString = "myCookie=myValue;max-age=50;path=/apathsomewhere;domain=.adomainsomewhere;secure;comment=this is a comment;version=0;commentURL=http://aurl.com;port=\"80,8080\";discard;"; - CookieDecoder cookieDecoder = new CookieDecoder(); - Set cookies = cookieDecoder.decode(cookieString); + Set cookies = CookieDecoder.decode(cookieString); assertEquals(1, cookies.size()); Cookie cookie = cookies.iterator().next(); assertNotNull(cookie); @@ -81,8 +79,7 @@ public class CookieDecoderTest { @Test public void testDecodingSingleCookieV1() { String cookieString = "myCookie=myValue;max-age=50;path=/apathsomewhere;domain=.adomainsomewhere;secure;comment=this is a comment;version=1;"; - CookieDecoder cookieDecoder = new CookieDecoder(); - Set cookies = cookieDecoder.decode(cookieString); + Set cookies = CookieDecoder.decode(cookieString); assertEquals(1, cookies.size()); Cookie cookie = cookies.iterator().next(); assertEquals("myValue", cookie.getValue()); @@ -101,8 +98,7 @@ public class CookieDecoderTest { @Test public void testDecodingSingleCookieV1ExtraParamsIgnored() { String cookieString = "myCookie=myValue;max-age=50;path=/apathsomewhere;domain=.adomainsomewhere;secure;comment=this is a comment;version=1;commentURL=http://aurl.com;port='80,8080';discard;"; - CookieDecoder cookieDecoder = new CookieDecoder(); - Set cookies = cookieDecoder.decode(cookieString); + Set cookies = CookieDecoder.decode(cookieString); assertEquals(1, cookies.size()); Cookie cookie = cookies.iterator().next(); assertNotNull(cookie); @@ -120,8 +116,7 @@ public class CookieDecoderTest { @Test public void testDecodingSingleCookieV2() { String cookieString = "myCookie=myValue;max-age=50;path=/apathsomewhere;domain=.adomainsomewhere;secure;comment=this is a comment;version=2;commentURL=http://aurl.com;port=\"80,8080\";discard;"; - CookieDecoder cookieDecoder = new CookieDecoder(); - Set cookies = cookieDecoder.decode(cookieString); + Set cookies = CookieDecoder.decode(cookieString); assertEquals(1, cookies.size()); Cookie cookie = cookies.iterator().next(); assertNotNull(cookie); @@ -145,9 +140,8 @@ public class CookieDecoderTest { String c1 = "myCookie=myValue;max-age=50;path=/apathsomewhere;domain=.adomainsomewhere;secure;comment=this is a comment;version=2;commentURL=\"http://aurl.com\";port='80,8080';discard;"; String c2 = "myCookie2=myValue2;max-age=0;path=/anotherpathsomewhere;domain=.anotherdomainsomewhere;comment=this is another comment;version=2;commentURL=http://anotherurl.com;"; String c3 = "myCookie3=myValue3;max-age=0;version=2;"; - CookieDecoder decoder = new CookieDecoder(); - Set cookies = decoder.decode(c1 + c2 + c3); + Set cookies = CookieDecoder.decode(c1 + c2 + c3); assertEquals(3, cookies.size()); Iterator it = cookies.iterator(); Cookie cookie = it.next(); @@ -196,7 +190,7 @@ public class CookieDecoderTest { "Part_Number=\"Riding_Rocket_0023\"; $Path=\"/acme/ammo\"; " + "Part_Number=\"Rocket_Launcher_0001\"; $Path=\"/acme\""; - Set cookies = new CookieDecoder().decode(source); + Set cookies = CookieDecoder.decode(source); Iterator it = cookies.iterator(); Cookie c; @@ -231,7 +225,7 @@ public class CookieDecoderTest { "$Version=\"1\"; session_id=\"1234\", " + "$Version=\"1\"; session_id=\"1111\"; $Domain=\".cracker.edu\""; - Set cookies = new CookieDecoder().decode(source); + Set cookies = CookieDecoder.decode(source); Iterator it = cookies.iterator(); Cookie c; @@ -275,7 +269,7 @@ public class CookieDecoderTest { "h=\"';,\\x\""; - Set cookies = new CookieDecoder().decode(source); + Set cookies = CookieDecoder.decode(source); Iterator it = cookies.iterator(); Cookie c; @@ -322,7 +316,7 @@ public class CookieDecoderTest { "__utma=48461872.1094088325.1258140131.1258140131.1258140131.1; " + "__utmb=48461872.13.10.1258140131; __utmc=48461872; " + "__utmz=48461872.1258140131.1.1.utmcsr=overstock.com|utmccn=(referral)|utmcmd=referral|utmcct=/Home-Garden/Furniture/Clearance,/clearance,/32/dept.html"; - Set cookies = new CookieDecoder().decode(source); + Set cookies = CookieDecoder.decode(source); Iterator it = cookies.iterator(); Cookie c; @@ -361,7 +355,7 @@ public class CookieDecoderTest { String source = "Format=EU; expires=Fri, 31-Dec-9999 23:59:59 GMT; path=/"; - Set cookies = new CookieDecoder().decode(source); + Set cookies = CookieDecoder.decode(source); Cookie c = cookies.iterator().next(); assertTrue(Math.abs(expectedMaxAge - c.getMaxAge()) < 2); @@ -372,7 +366,7 @@ public class CookieDecoderTest { String source = "UserCookie=timeZoneName=(GMT+04:00) Moscow, St. Petersburg, Volgograd&promocode=®ion=BE;" + " expires=Sat, 01-Dec-2012 10:53:31 GMT; path=/"; - Set cookies = new CookieDecoder().decode(source); + Set cookies = CookieDecoder.decode(source); Cookie c = cookies.iterator().next(); assertEquals("timeZoneName=(GMT+04:00) Moscow, St. Petersburg, Volgograd&promocode=®ion=BE", c.getValue()); @@ -381,7 +375,7 @@ public class CookieDecoderTest { @Test public void testDecodingWeirdNames1() { String src = "path=; expires=Mon, 01-Jan-1990 00:00:00 GMT; path=/; domain=.www.google.com"; - Set cookies = new CookieDecoder().decode(src); + Set cookies = CookieDecoder.decode(src); Cookie c = cookies.iterator().next(); assertEquals("path", c.getName()); assertEquals("", c.getValue()); @@ -391,7 +385,7 @@ public class CookieDecoderTest { @Test public void testDecodingWeirdNames2() { String src = "HTTPOnly="; - Set cookies = new CookieDecoder().decode(src); + Set cookies = CookieDecoder.decode(src); Cookie c = cookies.iterator().next(); assertEquals("HTTPOnly", c.getName()); assertEquals("", c.getValue()); @@ -445,7 +439,7 @@ public class CookieDecoderTest { "%=KqtH!$?mi!!!!'=KqtH!$?mx!!!!'=KqtH!$D7]!!!!#=J_#p!$D@T!!!!#=J_#p!$V cookies = new CookieDecoder().decode("bh=\"" + longValue + "\";"); + Set cookies = CookieDecoder.decode("bh=\"" + longValue + "\";"); assertEquals(1, cookies.size()); Cookie c = cookies.iterator().next(); assertEquals("bh", c.getName()); diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/CookieEncoderTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/CookieEncoderTest.java index 5887d1d979..ff701c1235 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/CookieEncoderTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/CookieEncoderTest.java @@ -19,6 +19,7 @@ import static org.junit.Assert.*; import java.text.DateFormat; import java.util.Date; +import java.util.List; import org.junit.Test; @@ -28,8 +29,6 @@ public class CookieEncoderTest { String result = "myCookie=myValue;Expires=XXX;Path=/apathsomewhere;Domain=.adomainsomewhere;Secure"; DateFormat df = new HttpHeaderDateFormat(); Cookie cookie = new DefaultCookie("myCookie", "myValue"); - CookieEncoder encoder = new CookieEncoder(true); - encoder.addCookie(cookie); cookie.setComment("this is a Comment"); cookie.setCommentUrl("http://aurl.com"); cookie.setDomain(".adomainsomewhere"); @@ -39,7 +38,7 @@ public class CookieEncoderTest { cookie.setPorts(80, 8080); cookie.setSecure(true); - String encodedCookie = encoder.encode(); + String encodedCookie = ServerCookieEncoder.encode(cookie); long currentTime = System.currentTimeMillis(); boolean fail = true; @@ -61,15 +60,13 @@ public class CookieEncoderTest { public void testEncodingSingleCookieV1() { String result = "myCookie=myValue;Max-Age=50;Path=\"/apathsomewhere\";Domain=.adomainsomewhere;Secure;Comment=\"this is a Comment\";Version=1"; Cookie cookie = new DefaultCookie("myCookie", "myValue"); - CookieEncoder encoder = new CookieEncoder(true); - encoder.addCookie(cookie); cookie.setVersion(1); cookie.setComment("this is a Comment"); cookie.setDomain(".adomainsomewhere"); cookie.setMaxAge(50); cookie.setPath("/apathsomewhere"); cookie.setSecure(true); - String encodedCookie = encoder.encode(); + String encodedCookie = ServerCookieEncoder.encode(cookie); assertEquals(result, encodedCookie); } @@ -77,8 +74,6 @@ public class CookieEncoderTest { public void testEncodingSingleCookieV2() { String result = "myCookie=myValue;Max-Age=50;Path=\"/apathsomewhere\";Domain=.adomainsomewhere;Secure;Comment=\"this is a Comment\";Version=1;CommentURL=\"http://aurl.com\";Port=\"80,8080\";Discard"; Cookie cookie = new DefaultCookie("myCookie", "myValue"); - CookieEncoder encoder = new CookieEncoder(true); - encoder.addCookie(cookie); cookie.setVersion(1); cookie.setComment("this is a Comment"); cookie.setCommentUrl("http://aurl.com"); @@ -88,29 +83,15 @@ public class CookieEncoderTest { cookie.setPath("/apathsomewhere"); cookie.setPorts(80, 8080); cookie.setSecure(true); - String encodedCookie = encoder.encode(); + String encodedCookie = ServerCookieEncoder.encode(cookie); assertEquals(result, encodedCookie); } - @Test - public void testEncodingMultipleServerCookies() { - CookieEncoder encoder = new CookieEncoder(true); - encoder.addCookie("a", "b"); - encoder.addCookie("b", "c"); - try { - encoder.encode(); - fail(); - } catch (IllegalStateException e) { - // Expected - } - } - @Test public void testEncodingMultipleClientCookies() { String c1 = "$Version=1;myCookie=myValue;$Path=\"/apathsomewhere\";$Domain=.adomainsomewhere;$Port=\"80,8080\";"; String c2 = "$Version=1;myCookie2=myValue2;$Path=\"/anotherpathsomewhere\";$Domain=.anotherdomainsomewhere;"; String c3 = "$Version=1;myCookie3=myValue3"; - CookieEncoder encoder = new CookieEncoder(false); Cookie cookie = new DefaultCookie("myCookie", "myValue"); cookie.setVersion(1); cookie.setComment("this is a Comment"); @@ -121,7 +102,6 @@ public class CookieEncoderTest { cookie.setPath("/apathsomewhere"); cookie.setPorts(80, 8080); cookie.setSecure(true); - encoder.addCookie(cookie); Cookie cookie2 = new DefaultCookie("myCookie2", "myValue2"); cookie2.setVersion(1); cookie2.setComment("this is another Comment"); @@ -130,23 +110,17 @@ public class CookieEncoderTest { cookie2.setDiscard(false); cookie2.setPath("/anotherpathsomewhere"); cookie2.setSecure(false); - encoder.addCookie(cookie2); Cookie cookie3 = new DefaultCookie("myCookie3", "myValue3"); cookie3.setVersion(1); - encoder.addCookie(cookie3); - String encodedCookie = encoder.encode(); + String encodedCookie = ClientCookieEncoder.encode(cookie, cookie2, cookie3); assertEquals(c1 + c2 + c3, encodedCookie); } @Test public void testEncodingWithNoCookies() { - CookieEncoder encoderForServer = new CookieEncoder(true); - String encodedCookie1 = encoderForServer.encode(); - CookieEncoder encoderForClient = new CookieEncoder(false); - String encodedCookie2 = encoderForClient.encode(); - assertNotNull(encodedCookie1); - assertNotNull(encodedCookie2); - + String encodedCookie1 = ClientCookieEncoder.encode(); + List encodedCookie2 = ServerCookieEncoder.encode(); + assertNotNull(encodedCookie1); + assertNotNull(encodedCookie2); } - } diff --git a/example/src/main/java/io/netty/example/http/snoop/HttpSnoopClient.java b/example/src/main/java/io/netty/example/http/snoop/HttpSnoopClient.java index 0ce85e67fe..89918eeb7a 100644 --- a/example/src/main/java/io/netty/example/http/snoop/HttpSnoopClient.java +++ b/example/src/main/java/io/netty/example/http/snoop/HttpSnoopClient.java @@ -19,7 +19,8 @@ import io.netty.bootstrap.Bootstrap; import io.netty.channel.Channel; import io.netty.channel.socket.nio.NioEventLoop; import io.netty.channel.socket.nio.NioSocketChannel; -import io.netty.handler.codec.http.CookieEncoder; +import io.netty.handler.codec.http.ClientCookieEncoder; +import io.netty.handler.codec.http.DefaultCookie; import io.netty.handler.codec.http.DefaultHttpRequest; import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpMethod; @@ -79,10 +80,11 @@ public class HttpSnoopClient { request.setHeader(HttpHeaders.Names.ACCEPT_ENCODING, HttpHeaders.Values.GZIP); // Set some example cookies. - CookieEncoder httpCookieEncoder = new CookieEncoder(false); - httpCookieEncoder.addCookie("my-cookie", "foo"); - httpCookieEncoder.addCookie("another-cookie", "bar"); - request.setHeader(HttpHeaders.Names.COOKIE, httpCookieEncoder.encode()); + request.setHeader( + HttpHeaders.Names.COOKIE, + ClientCookieEncoder.encode( + new DefaultCookie("my-cookie", "foo"), + new DefaultCookie("another-cookie", "bar"))); // Send the HTTP request. ch.write(request); diff --git a/example/src/main/java/io/netty/example/http/snoop/HttpSnoopServerHandler.java b/example/src/main/java/io/netty/example/http/snoop/HttpSnoopServerHandler.java index cb08c3e0bc..2d99d18f95 100644 --- a/example/src/main/java/io/netty/example/http/snoop/HttpSnoopServerHandler.java +++ b/example/src/main/java/io/netty/example/http/snoop/HttpSnoopServerHandler.java @@ -27,7 +27,6 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundMessageHandlerAdapter; import io.netty.handler.codec.http.Cookie; import io.netty.handler.codec.http.CookieDecoder; -import io.netty.handler.codec.http.CookieEncoder; import io.netty.handler.codec.http.DefaultHttpResponse; import io.netty.handler.codec.http.HttpChunk; import io.netty.handler.codec.http.HttpChunkTrailer; @@ -35,6 +34,7 @@ import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpResponse; import io.netty.handler.codec.http.QueryStringDecoder; +import io.netty.handler.codec.http.ServerCookieEncoder; import io.netty.util.CharsetUtil; import java.util.List; @@ -137,23 +137,17 @@ public class HttpSnoopServerHandler extends ChannelInboundMessageHandlerAdapter< // Encode the cookie. String cookieString = request.getHeader(COOKIE); if (cookieString != null) { - CookieDecoder cookieDecoder = new CookieDecoder(); - Set cookies = cookieDecoder.decode(cookieString); + Set cookies = CookieDecoder.decode(cookieString); if (!cookies.isEmpty()) { // Reset the cookies if necessary. - CookieEncoder cookieEncoder = new CookieEncoder(true); - for (Cookie cookie : cookies) { - cookieEncoder.addCookie(cookie); - response.addHeader(SET_COOKIE, cookieEncoder.encode()); + for (Cookie cookie: cookies) { + response.addHeader(SET_COOKIE, ServerCookieEncoder.encode(cookie)); } } } else { // Browser sent no cookie. Add some. - CookieEncoder cookieEncoder = new CookieEncoder(true); - cookieEncoder.addCookie("key1", "value1"); - response.addHeader(SET_COOKIE, cookieEncoder.encode()); - cookieEncoder.addCookie("key2", "value2"); - response.addHeader(SET_COOKIE, cookieEncoder.encode()); + response.addHeader(SET_COOKIE, ServerCookieEncoder.encode("key1", "value1")); + response.addHeader(SET_COOKIE, ServerCookieEncoder.encode("key2", "value2")); } // Write the response. From 88e83462b03aaa7ad05c502bb80fa7154a4a048b Mon Sep 17 00:00:00 2001 From: Trustin Lee Date: Tue, 26 Jun 2012 05:30:07 +0900 Subject: [PATCH 44/87] Remove a method of no use --- .../codec/http/ClientCookieEncoder.java | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/ClientCookieEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/ClientCookieEncoder.java index 4dafb2a9c3..006a172fb9 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/ClientCookieEncoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/ClientCookieEncoder.java @@ -2,8 +2,6 @@ package io.netty.handler.codec.http; import static io.netty.handler.codec.http.CookieEncoderUtil.*; -import java.util.Collection; - /** * Encodes client-side {@link Cookie}s into an HTTP header value. This encoder can encode * the HTTP cookie version 0, 1, and 2. @@ -53,22 +51,6 @@ public final class ClientCookieEncoder { return stripTrailingSeparator(buf); } - public static String encode(Collection cookies) { - if (cookies == null) { - throw new NullPointerException("cookies"); - } - - StringBuilder buf = new StringBuilder(); - for (Cookie c: cookies) { - if (c == null) { - break; - } - - encode(buf, c); - } - return stripTrailingSeparator(buf); - } - public static String encode(Iterable cookies) { if (cookies == null) { throw new NullPointerException("cookies"); From de20883517db78015d643b3621ddfcb5c94b8bc6 Mon Sep 17 00:00:00 2001 From: Trustin Lee Date: Tue, 26 Jun 2012 17:33:05 +0900 Subject: [PATCH 45/87] Add missing license headers --- .../handler/codec/http/ClientCookieEncoder.java | 15 +++++++++++++++ .../handler/codec/http/ServerCookieEncoder.java | 15 +++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/ClientCookieEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/ClientCookieEncoder.java index 006a172fb9..ae71b7deea 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/ClientCookieEncoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/ClientCookieEncoder.java @@ -1,3 +1,18 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ package io.netty.handler.codec.http; import static io.netty.handler.codec.http.CookieEncoderUtil.*; diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/ServerCookieEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/ServerCookieEncoder.java index 6caa7f1e31..2923267157 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/ServerCookieEncoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/ServerCookieEncoder.java @@ -1,3 +1,18 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ package io.netty.handler.codec.http; import static io.netty.handler.codec.http.CookieEncoderUtil.*; From 9cc9f4e1ec44fd737c73c0d3b9bc29274dce64cc Mon Sep 17 00:00:00 2001 From: Trustin Lee Date: Wed, 27 Jun 2012 12:41:37 +0900 Subject: [PATCH 46/87] Add more cookie decoding test case --- .../netty/handler/codec/http/CookieDecoderTest.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/CookieDecoderTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/CookieDecoderTest.java index 2055e4994b..9351c9816b 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/CookieDecoderTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/CookieDecoderTest.java @@ -391,6 +391,19 @@ public class CookieDecoderTest { assertEquals("", c.getValue()); } + @Test + public void testDecodingValuesWithCommasAndEquals() { + String src = "A=v=1&lg=en-US,it-IT,it&intl=it&np=1;T=z=E"; + Set cookies = CookieDecoder.decode(src); + Iterator i = cookies.iterator(); + Cookie c = i.next(); + assertEquals("A", c.getName()); + assertEquals("v=1&lg=en-US,it-IT,it&intl=it&np=1", c.getValue()); + c = i.next(); + assertEquals("T", c.getName()); + assertEquals("z=E", c.getValue()); + } + @Test public void testDecodingLongValue() { String longValue = From 2653facd3babfbf35173a04ce87db24bef172e71 Mon Sep 17 00:00:00 2001 From: Cruz Julian Bishop Date: Wed, 27 Jun 2012 17:41:49 +1000 Subject: [PATCH 47/87] Adds javadoc to NetworkConstants Also renames some internal variables to be more understandable No API changes! :) --- .../java/io/netty/util/NetworkConstants.java | 69 +++++++++++++++---- 1 file changed, 54 insertions(+), 15 deletions(-) diff --git a/common/src/main/java/io/netty/util/NetworkConstants.java b/common/src/main/java/io/netty/util/NetworkConstants.java index 509d20a069..213e8b92f3 100644 --- a/common/src/main/java/io/netty/util/NetworkConstants.java +++ b/common/src/main/java/io/netty/util/NetworkConstants.java @@ -24,62 +24,101 @@ import java.net.SocketException; import java.net.UnknownHostException; import java.util.Enumeration; +/** + * A class that holds a number of network-related constants. + */ public final class NetworkConstants { + /** + * The {@link InetAddress} representing the host machine + * + * We cache this because some machines take almost forever to return from + * {@link InetAddress}.getLocalHost(). This may be due to incorrect + * configuration of the hosts and DNS client configuration files. + */ public static final InetAddress LOCALHOST; + + /** + * The loopback {@link NetworkInterface} on the current machine + */ public static final NetworkInterface LOOPBACK_IF; + /** + * The logger being used by this class + */ private static final InternalLogger logger = InternalLoggerFactory.getInstance(NetworkConstants.class); static { - // We cache this because some machine takes almost forever to return - // from InetAddress.getLocalHost(). I think it's due to the incorrect - // /etc/hosts or /etc/resolve.conf. + + //Start the process of discovering localhost InetAddress localhost = null; + try { + //Let's start by getting localhost automatically localhost = InetAddress.getLocalHost(); } catch (UnknownHostException e) { + //No? That's okay. try { + //Try to force an IPv4 localhost address localhost = InetAddress.getByAddress(new byte[] { 127, 0, 0, 1 }); } catch (UnknownHostException e1) { + //No? Okay. You must be using IPv6 try { + //Try to force an IPv6 localhost address localhost = InetAddress.getByAddress( new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }); } catch (UnknownHostException e2) { - logger.error("Failed to resolve localhost", e2); + //No? Okay. + logger.error("Failed to resolve localhost - Incorrect network configuration?", e2); } } } + //Set the localhost constant LOCALHOST = localhost; - NetworkInterface loopbackIf; + //Prepare to get the local NetworkInterface + NetworkInterface loopbackInterface; + try { - loopbackIf = NetworkInterface.getByInetAddress(LOCALHOST); + //Automatically get the loopback interface + loopbackInterface = NetworkInterface.getByInetAddress(LOCALHOST); } catch (SocketException e) { - loopbackIf = null; + //No? Alright. There is a backup! + loopbackInterface = null; } - // If null is returned, iterate over all the available network interfaces. - if (loopbackIf == null) { + //Check to see if a network interface was not found + if (loopbackInterface == null) { try { - for (Enumeration e = NetworkInterface.getNetworkInterfaces(); - e.hasMoreElements();) { - NetworkInterface nif = e.nextElement(); - if (nif.isLoopback()) { - loopbackIf = nif; + //Start iterating over all network interfaces + for (Enumeration interfaces = NetworkInterface.getNetworkInterfaces(); + interfaces.hasMoreElements();) { + //Get the "next" interface + NetworkInterface networkInterface = interfaces.nextElement(); + + //Check to see if the interface is a loopback interface + if (networkInterface.isLoopback()) { + //Phew! The loopback interface was found. + loopbackInterface = networkInterface; + //No need to keep iterating break; } } } catch (SocketException e) { + //Nope. Can't do anything else, sorry! logger.error("Failed to enumerate network interfaces", e); } } - LOOPBACK_IF = loopbackIf; + //Set the loopback interface constant + LOOPBACK_IF = loopbackInterface; } + /** + * A constructor to stop this class being constructed. + */ private NetworkConstants() { // Unused } From 8b5ab52a5b003dfd2f0bdb9d68f7c88aefb423ce Mon Sep 17 00:00:00 2001 From: Cruz Julian Bishop Date: Thu, 28 Jun 2012 08:55:29 +1000 Subject: [PATCH 48/87] Update dependencies to the latest stable versions --- pom.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pom.xml b/pom.xml index 20ef2a7f56..428d3a35c7 100644 --- a/pom.xml +++ b/pom.xml @@ -216,7 +216,7 @@ org.slf4j slf4j-simple - 1.6.4 + 1.6.6 test @@ -226,7 +226,7 @@ maven-enforcer-plugin - 1.0.1 + 1.1 enforce-tools @@ -266,7 +266,7 @@ be used even when compiling with java 1.7+ --> org.codehaus.mojo animal-sniffer-maven-plugin - 1.7 + 1.8 org.codehaus.mojo.signature @@ -353,7 +353,7 @@ maven-release-plugin - 2.3.1 + 2.3.2 release,full From 343d6748394bbc4729c253eeec41bd0ab1ff87ae Mon Sep 17 00:00:00 2001 From: Cruz Julian Bishop Date: Thu, 28 Jun 2012 10:24:18 +1000 Subject: [PATCH 49/87] Adds a method to get the buffer for a specific index in CompositeByteBuf This fixes #414 --- .../java/io/netty/buffer/CompositeByteBuf.java | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java index 9329de9023..f9ecc34aa6 100644 --- a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java @@ -574,6 +574,24 @@ public class CompositeByteBuf extends AbstractByteBuf { dst.writerIndex(dst.capacity()); } + + public ByteBuf getBufferFor(int index) throws IOException { + if (index < 0 || index > capacity()) { + throw new IndexOutOfBoundsException("Invalid index: " + index + + " - Bytes needed: " + (index) + ", maximum is " + + capacity()); + } + + List components = decompose(index, 1); + switch (components.size()) { + case 0: + return Unpooled.EMPTY_BUFFER; + case 1: + return components.get(0); + default: + throw new IOException("Index " + index + " is part of " + components.size() + " buffers!"); + } + } @Override public ByteBuf slice(int index, int length) { From 0d8ed47c3e66379f92025f9048bdcd2960c0ef96 Mon Sep 17 00:00:00 2001 From: Cruz Julian Bishop Date: Thu, 28 Jun 2012 10:29:11 +1000 Subject: [PATCH 50/87] Simplify the process of getting the component --- .../java/io/netty/buffer/CompositeByteBuf.java | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java index f9ecc34aa6..a703a8ad79 100644 --- a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java @@ -582,15 +582,11 @@ public class CompositeByteBuf extends AbstractByteBuf { + capacity()); } - List components = decompose(index, 1); - switch (components.size()) { - case 0: - return Unpooled.EMPTY_BUFFER; - case 1: - return components.get(0); - default: - throw new IOException("Index " + index + " is part of " + components.size() + " buffers!"); - } + int componentId = componentId(index); + + //Return the component byte buffer + return components[componentId].duplicate(); + } @Override From 580c6069fa37c0ad5ee45f04e7be83b2ee70ccbf Mon Sep 17 00:00:00 2001 From: Cruz Julian Bishop Date: Thu, 28 Jun 2012 10:31:49 +1000 Subject: [PATCH 51/87] Actually throw the correct Exception type. Whoops! This should be done, now. --- buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java index a703a8ad79..67529302b3 100644 --- a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java @@ -575,7 +575,7 @@ public class CompositeByteBuf extends AbstractByteBuf { dst.writerIndex(dst.capacity()); } - public ByteBuf getBufferFor(int index) throws IOException { + public ByteBuf getBufferFor(int index) throws IndexOutOfBoundsException { if (index < 0 || index > capacity()) { throw new IndexOutOfBoundsException("Invalid index: " + index + " - Bytes needed: " + (index) + ", maximum is " From f1c375109eaef4a99e786097a3beef6610a5ab0a Mon Sep 17 00:00:00 2001 From: Cruz Julian Bishop Date: Thu, 28 Jun 2012 16:37:41 +1000 Subject: [PATCH 52/87] Add documentation to NonReentrantLock --- .../netty/util/internal/NonReentrantLock.java | 62 ++++++++++++++++++- 1 file changed, 61 insertions(+), 1 deletion(-) diff --git a/common/src/main/java/io/netty/util/internal/NonReentrantLock.java b/common/src/main/java/io/netty/util/internal/NonReentrantLock.java index e774796706..32f32fa5b9 100644 --- a/common/src/main/java/io/netty/util/internal/NonReentrantLock.java +++ b/common/src/main/java/io/netty/util/internal/NonReentrantLock.java @@ -20,48 +20,97 @@ import java.util.concurrent.locks.AbstractQueuedSynchronizer; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; +/** + * A custom implementation of a lock that does not allow reentry + */ public final class NonReentrantLock extends AbstractQueuedSynchronizer - implements Lock { + implements Lock { + /** + * The serial version unique ID + */ private static final long serialVersionUID = -833780837233068610L; + /** + * The {@link Thread} that owns this {@link NonReentrantLock} + */ private Thread owner; + /** + * Locks this {@link NonReentrantLock} + */ @Override public void lock() { acquire(1); } + /** + * Locks this {@link NonReentrantLock}, but allow interruption + * + * @throws InterruptedException The lock was interrupted + */ @Override public void lockInterruptibly() throws InterruptedException { acquireInterruptibly(1); } + /** + * Try to lock this {@link NonReentrantLock} + * + * @return True if locking was successful, otherwise false + */ @Override public boolean tryLock() { return tryAcquire(1); } + /** + * Tries to lock this {@link NonReentrantLock} over a period of time + * + * @param time The maximum number of time units to attempt to get a lock for. + * @param unit The {@link TimeUnit} associated with the time parameter + * @return True if the lock was successful, otherwise false + * @throws InterruptedException The locking attempt was interrupted + */ @Override public boolean tryLock(long time, TimeUnit unit) throws InterruptedException { return tryAcquireNanos(1, unit.toNanos(time)); } + /** + * Unlocks this {@link NonReentrantLock} + */ @Override public void unlock() { release(1); } + /** + * Checks to see if this {@link NonReentrantLock} is held by the current {@link Thread} + * + * @return True if held by the current thread, otherwise false + */ public boolean isHeldByCurrentThread() { return isHeldExclusively(); } + /** + * Creates a new {@link Condition} + * + * @return The condition object + */ @Override public Condition newCondition() { return new ConditionObject(); } + /** + * Try to acquire a lock + * + * @param acquires A number that is sent by acquiring methods + * @return True if a lock is acquired, otherwise false + */ @Override protected boolean tryAcquire(int acquires) { if (compareAndSetState(0, 1)) { @@ -71,6 +120,12 @@ public final class NonReentrantLock extends AbstractQueuedSynchronizer return false; } + /** + * Tries to release the lock + * + * @param releases A number that is passed by the release methods + * @return True if a release is granted, otherwise false + */ @Override protected boolean tryRelease(int releases) { if (Thread.currentThread() != owner) { @@ -81,6 +136,11 @@ public final class NonReentrantLock extends AbstractQueuedSynchronizer return true; } + /** + * Checks to see if this {@link NonReentrantLock} is held exclusively by the current {@link Thread} + * + * @return True if held exclusively, otherwise false + */ @Override protected boolean isHeldExclusively() { return getState() != 0 && owner == Thread.currentThread(); From a92ed57b182a0e19f0de13a59f3a572034f8759f Mon Sep 17 00:00:00 2001 From: Cruz Julian Bishop Date: Thu, 28 Jun 2012 19:00:43 +1000 Subject: [PATCH 53/87] Add documentation and changes to ComposityByteBuf.getBufferFor(index) Thanks to @kimchy and @fredericBregier This is part of #414 --- .../io/netty/buffer/CompositeByteBuf.java | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java index 67529302b3..1977e9d574 100644 --- a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java @@ -575,17 +575,33 @@ public class CompositeByteBuf extends AbstractByteBuf { dst.writerIndex(dst.capacity()); } + /** + * Gets the {@link ByteBuf} used at the specified index. + *

+ * Please note that since a {@link CompositeByteBuf} is made up of + * multiple {@link ByteBuf}s, this does not return the full buffer. + * Instead, it only returns a portion of the composite buffer where the + * index is located + *

+ * + *

+ * This is a method meant for use by experts - Please be careful + * when using it. + *

+ * + * @param index The index to use + * @return The {@link ByteBuf} used at the indes. + * @throws IndexOutOfBoundsException + */ public ByteBuf getBufferFor(int index) throws IndexOutOfBoundsException { if (index < 0 || index > capacity()) { throw new IndexOutOfBoundsException("Invalid index: " + index + " - Bytes needed: " + (index) + ", maximum is " + capacity()); } - - int componentId = componentId(index); //Return the component byte buffer - return components[componentId].duplicate(); + return components[componentId(index)]; } From d2f1c85f24c5114e9ee8a7914fb9dbe667ec1dea Mon Sep 17 00:00:00 2001 From: Cruz Julian Bishop Date: Thu, 28 Jun 2012 19:33:54 +1000 Subject: [PATCH 54/87] Documentation and checkstyle fixes from @fredericBregier This is part of #414 --- .../io/netty/buffer/CompositeByteBuf.java | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java index 1977e9d574..7a7990aa35 100644 --- a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java @@ -574,35 +574,33 @@ public class CompositeByteBuf extends AbstractByteBuf { dst.writerIndex(dst.capacity()); } - + /** - * Gets the {@link ByteBuf} used at the specified index. + * Gets the {@link ByteBuf} portion of this {@link CompositeByteBuf} that + * contains the specified {@code index}. This is an expert method! + * *

* Please note that since a {@link CompositeByteBuf} is made up of * multiple {@link ByteBuf}s, this does not return the full buffer. * Instead, it only returns a portion of the composite buffer where the * index is located *

- * - *

- * This is a method meant for use by experts - Please be careful - * when using it. - *

- * - * @param index The index to use - * @return The {@link ByteBuf} used at the indes. - * @throws IndexOutOfBoundsException + * + * @param index The {@code index} to search for and include in the returned {@link ByteBuf} + * @return The {@link ByteBuf} that contains the specified {@code index} + * @throws IndexOutOfBoundsException when the specified {@code index} is less than + * zero, or larger than {@code capacity()} */ public ByteBuf getBufferFor(int index) throws IndexOutOfBoundsException { if (index < 0 || index > capacity()) { throw new IndexOutOfBoundsException("Invalid index: " + index - + " - Bytes needed: " + (index) + ", maximum is " + + " - Bytes needed: " + index + ", maximum is " + capacity()); } //Return the component byte buffer return components[componentId(index)]; - + } @Override From 0a413af35d7ad111bb7714da5e285a483806c80f Mon Sep 17 00:00:00 2001 From: Cruz Julian Bishop Date: Thu, 28 Jun 2012 19:34:52 +1000 Subject: [PATCH 55/87] Comply with line width a bit more --- buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java index 7a7990aa35..18710d9819 100644 --- a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java @@ -588,8 +588,8 @@ public class CompositeByteBuf extends AbstractByteBuf { * * @param index The {@code index} to search for and include in the returned {@link ByteBuf} * @return The {@link ByteBuf} that contains the specified {@code index} - * @throws IndexOutOfBoundsException when the specified {@code index} is less than - * zero, or larger than {@code capacity()} + * @throws IndexOutOfBoundsException when the specified {@code index} is + * less than zero, or larger than {@code capacity()} */ public ByteBuf getBufferFor(int index) throws IndexOutOfBoundsException { if (index < 0 || index > capacity()) { From a280928731d4dd2c778f76ac50dcbdf50570c713 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Thu, 28 Jun 2012 13:36:21 +0200 Subject: [PATCH 56/87] Fix SpdyHttpHeaders.setScheme setting the wrong header. See #417 --- .../main/java/io/netty/handler/codec/spdy/SpdyHttpHeaders.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpHeaders.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpHeaders.java index e7f3d724ba..9b887d5b20 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpHeaders.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpHeaders.java @@ -165,6 +165,6 @@ public final class SpdyHttpHeaders { * Sets the {@code "X-SPDY-Scheme"} header. */ public static void setScheme(HttpMessage message, String scheme) { - message.setHeader(Names.URL, scheme); + message.setHeader(Names.SCHEME, scheme); } } From 052f8be504344eee0b462fc3c3ecb0228a24d217 Mon Sep 17 00:00:00 2001 From: Cruz Julian Bishop Date: Fri, 29 Jun 2012 13:31:02 +1000 Subject: [PATCH 57/87] Provide a basic test for getBufferFor() --- .../AbstractCompositeChannelBufferTest.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/buffer/src/test/java/io/netty/buffer/AbstractCompositeChannelBufferTest.java b/buffer/src/test/java/io/netty/buffer/AbstractCompositeChannelBufferTest.java index 2d68c5cd1d..97bc1f8b8b 100644 --- a/buffer/src/test/java/io/netty/buffer/AbstractCompositeChannelBufferTest.java +++ b/buffer/src/test/java/io/netty/buffer/AbstractCompositeChannelBufferTest.java @@ -16,6 +16,7 @@ package io.netty.buffer; import static io.netty.buffer.Unpooled.*; +import java.io.IOException; import static org.junit.Assert.*; import java.nio.ByteBuffer; @@ -89,6 +90,25 @@ public abstract class AbstractCompositeChannelBufferTest extends protected boolean discardReadBytesDoesNotMoveWritableBytes() { return false; } + + /** + * Tests the "getBufferFor" method + */ + @Test + public void testGetBufferFor() throws IOException { + CompositeByteBuf buf = (CompositeByteBuf) Unpooled.wrappedBuffer(new byte[] { 1, 2, 3, 4, 5 }, new byte[] {4, 5, 6, 7, 8, 9, 26}); + + //Ensure that a random place will be fine + assertEquals(buf.getBufferFor(2).capacity(), 5); + + //Loop through each byte + + byte index = 0; + + while (index < buf.capacity()) { + assertNotNull(buf.getBufferFor(index++)); + } + } @Test public void testDiscardReadBytes3() { From 73a76bfe6394979ed2e96be30948eb980cedfdd8 Mon Sep 17 00:00:00 2001 From: Cruz Julian Bishop Date: Fri, 29 Jun 2012 13:35:14 +1000 Subject: [PATCH 58/87] Little bit more testing --- .../io/netty/buffer/AbstractCompositeChannelBufferTest.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/buffer/src/test/java/io/netty/buffer/AbstractCompositeChannelBufferTest.java b/buffer/src/test/java/io/netty/buffer/AbstractCompositeChannelBufferTest.java index 97bc1f8b8b..0c69083dc2 100644 --- a/buffer/src/test/java/io/netty/buffer/AbstractCompositeChannelBufferTest.java +++ b/buffer/src/test/java/io/netty/buffer/AbstractCompositeChannelBufferTest.java @@ -106,7 +106,9 @@ public abstract class AbstractCompositeChannelBufferTest extends byte index = 0; while (index < buf.capacity()) { - assertNotNull(buf.getBufferFor(index++)); + ByteBuf _buf = buf.getBufferFor(index++); + assertNotNull(_buf); + assertTrue(_buf.capacity() > 0); } } From 0f6c3137f6b44ca2ca8a1017b6e9ede93c1744a5 Mon Sep 17 00:00:00 2001 From: Cruz Julian Bishop Date: Fri, 29 Jun 2012 13:37:41 +1000 Subject: [PATCH 59/87] Two tentative last asserts in the test --- .../io/netty/buffer/AbstractCompositeChannelBufferTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/buffer/src/test/java/io/netty/buffer/AbstractCompositeChannelBufferTest.java b/buffer/src/test/java/io/netty/buffer/AbstractCompositeChannelBufferTest.java index 0c69083dc2..86fa2a5009 100644 --- a/buffer/src/test/java/io/netty/buffer/AbstractCompositeChannelBufferTest.java +++ b/buffer/src/test/java/io/netty/buffer/AbstractCompositeChannelBufferTest.java @@ -109,6 +109,8 @@ public abstract class AbstractCompositeChannelBufferTest extends ByteBuf _buf = buf.getBufferFor(index++); assertNotNull(_buf); assertTrue(_buf.capacity() > 0); + assertNotNull(_buf.getByte(0)); + assertNotNull(_buf.getByte(_buf.readableBytes() - 1)); } } From dc300f2c812b2231b527c761f4c73d5155137516 Mon Sep 17 00:00:00 2001 From: Cruz Julian Bishop Date: Fri, 29 Jun 2012 13:38:53 +1000 Subject: [PATCH 60/87] Fix a bug where a potential overflow occurs --- buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java index 18710d9819..bf59be816d 100644 --- a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java @@ -592,7 +592,7 @@ public class CompositeByteBuf extends AbstractByteBuf { * less than zero, or larger than {@code capacity()} */ public ByteBuf getBufferFor(int index) throws IndexOutOfBoundsException { - if (index < 0 || index > capacity()) { + if (index < 0 || index >= capacity()) { throw new IndexOutOfBoundsException("Invalid index: " + index + " - Bytes needed: " + index + ", maximum is " + capacity()); From 7e35cc1ebb4c89f2f5d1a0f1397ed0ddbd0b1625 Mon Sep 17 00:00:00 2001 From: Cruz Julian Bishop Date: Fri, 29 Jun 2012 15:03:41 +1000 Subject: [PATCH 61/87] Fixes a bit of javadoc that was broken by a failed merge --- .../codec/http/websocketx/WebSocketServerHandshaker.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker.java index c16f95f95c..dbc2aea64d 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker.java @@ -92,11 +92,9 @@ public abstract class WebSocketServerHandshaker { } /** -<<<<<<< HEAD - * Returns the max length for any frame's payload. -======= - * Returns the max length for any frame's payload ->>>>>>> abd10d9... Fixed bug where subprotocol not sent by client + * Gets the maximum length for any frame's payload. + * + * @return The maximum length for a frame's payload */ public int getMaxFramePayloadLength() { return maxFramePayloadLength; From eaa99efd3018a013b5f8044d6ab80ab8c9ebd6ef Mon Sep 17 00:00:00 2001 From: Cruz Julian Bishop Date: Fri, 29 Jun 2012 15:12:59 +1000 Subject: [PATCH 62/87] Redid documentation for WebSocketUtil --- .../codec/http/websocketx/WebSocketUtil.java | 77 ++++++++++--------- 1 file changed, 39 insertions(+), 38 deletions(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketUtil.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketUtil.java index ab163c90d1..089cd005c4 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketUtil.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketUtil.java @@ -19,90 +19,91 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.handler.codec.base64.Base64; import io.netty.util.CharsetUtil; - import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; /** - * TODO Document me. + * A utility class mainly for use by web sockets */ final class WebSocketUtil { /** - * Performs an MD5 hash + * Performs a MD5 hash on the specified data * - * @param bytes - * Data to hash - * @return Hashed data + * @param data The data to hash + * @return The hashed data */ - static byte[] md5(byte[] bytes) { + static byte[] md5(byte[] data) { try { + //Try to get a MessageDigest that uses MD5 MessageDigest md = MessageDigest.getInstance("MD5"); - return md.digest(bytes); + //Hash the data + return md.digest(data); } catch (NoSuchAlgorithmException e) { - throw new InternalError("MD5 not supported on this platform"); + //This shouldn't happen! How old is the computer? + throw new InternalError("MD5 not supported on this platform - Outdated?"); } } /** - * Performs an SHA-1 hash + * Performs a SHA-1 hash on the specified data * - * @param bytes - * Data to hash - * @return Hashed data + * @param data The data to hash + * @return The hashed data */ - static byte[] sha1(byte[] bytes) { + static byte[] sha1(byte[] data) { try { + //Attempt to get a MessageDigest that uses SHA1 MessageDigest md = MessageDigest.getInstance("SHA1"); - return md.digest(bytes); + //Hash the data + return md.digest(data); } catch (NoSuchAlgorithmException e) { - throw new InternalError("SHA-1 not supported on this platform"); + //Alright, you might have an old system. + throw new InternalError("SHA-1 is not supported on this platform - Outdated?"); } } /** - * Base 64 encoding + * Performs base64 encoding on the specified data * - * @param bytes - * Bytes to encode - * @return encoded string + * @param data The data to encode + * @return An encoded string containing the data */ - static String base64(byte[] bytes) { - ByteBuf hashed = Unpooled.wrappedBuffer(bytes); - return Base64.encode(hashed).toString(CharsetUtil.UTF_8); + static String base64(byte[] data) { + ByteBuf encodedData = Unpooled.wrappedBuffer(data); + return Base64.encode(encodedData).toString(CharsetUtil.UTF_8); } /** - * Creates some random bytes + * Creates an arbitrary number of random bytes * - * @param size - * Number of random bytes to create - * @return random bytes + * @param size the number of random bytes to create + * @return An array of random bytes */ static byte[] randomBytes(int size) { byte[] bytes = new byte[size]; - for (int i = 0; i < size; i++) { - bytes[i] = (byte) randomNumber(0, 255); + for (int index = 0; index < size; index++) { + bytes[index] = (byte) randomNumber(0, 255); } return bytes; } /** - * Generates a random number + * Generates a pseudo-random number * - * @param min - * Minimum value - * @param max - * Maximum value - * @return Random number + * @param minimum The minimum allowable value + * @param maximum The maximum allowable value + * @return A pseudo-random number */ - static int randomNumber(int min, int max) { - return (int) (Math.random() * max + min); + static int randomNumber(int minimum, int maximum) { + return (int) (Math.random() * maximum + minimum); } - + /** + * A private constructor to ensure that instances of this class cannot be made + */ private WebSocketUtil() { // Unused } From 98c61e4128ce7dfe2f51b3ea33406a579fc1a9f1 Mon Sep 17 00:00:00 2001 From: Cruz Julian Bishop Date: Fri, 29 Jun 2012 16:33:14 +1000 Subject: [PATCH 63/87] Made the documentation in HttpMessage a bit easier to understand --- .../netty/handler/codec/http/HttpMessage.java | 127 ++++++++++++------ 1 file changed, 87 insertions(+), 40 deletions(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpMessage.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpMessage.java index 36a33bb967..6e0a29a8d4 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpMessage.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpMessage.java @@ -25,8 +25,10 @@ import java.util.Map; import java.util.Set; /** - * An HTTP message which provides common properties for {@link HttpRequest} and - * {@link HttpResponse}. + * An interface that defines a HTTP message, providing common properties for + * {@link HttpRequest} and {@link HttpResponse}. + * @see HttpResponse + * @see HttpRequest * @see HttpHeaders * * @apiviz.landmark @@ -35,86 +37,111 @@ import java.util.Set; public interface HttpMessage { /** - * Returns the header value with the specified header name. If there are - * more than one header value for the specified header name, the first - * value is returned. + * Gets the value of a header with the specified name. If there are + * more than one values for the specified name, the first value is returned. * - * @return the header value or {@code null} if there is no such header + * @param name The name of the header to search + * @return The first header value or {@code null} if there is no such header */ String getHeader(String name); /** - * Returns the header values with the specified header name. + * Returns the values of headers with the specified name * - * @return the {@link List} of header values. An empty list if there is no - * such header. + * @param name The name of the headers to search + * @return A {@link List} of header values which will be empty if no values + * are found */ List getHeaders(String name); /** - * Returns the all header names and values that this message contains. + * Returns the all headers that this message contains. * - * @return the {@link List} of the header name-value pairs. An empty list - * if there is no header in this message. + * @return A {@link List} of the header name-value entries, which will be + * empty if no pairs are found */ List> getHeaders(); /** - * Returns {@code true} if and only if there is a header with the specified - * header name. + * Checks to see if there is a header with the specified name + * + * @param name The name of the header to search for + * @return True if at least one header is found */ boolean containsHeader(String name); /** - * Returns the {@link Set} of all header names that this message contains. + * Gets a {@link Set} of all header names that this message contains + * + * @return A {@link Set} of all header names */ Set getHeaderNames(); /** - * Returns the protocol version of this message. + * Gets the protocol version of this {@link HttpMessage} + * + * @returns The protocol version */ HttpVersion getProtocolVersion(); /** - * Sets the protocol version of this message. + * Sets the protocol version of this {@link HttpMessage} + * + * @param version The version to set */ void setProtocolVersion(HttpVersion version); /** - * Returns the content of this message. If there is no content or - * {@link #isChunked()} returns {@code true}, an - * {@link Unpooled#EMPTY_BUFFER} is returned. + * Gets the content of this {@link HttpMessage}. + * + * If there is no content or {@link #isChunked()} returns {@code true}, + * an {@link Unpooled#EMPTY_BUFFER} is returned. + * + * @return A {@link ByteBuf} containing this {@link HttpMessage}'s content */ ByteBuf getContent(); /** - * Sets the content of this message. If {@code null} is specified, - * the content of this message will be set to {@link Unpooled#EMPTY_BUFFER}. + * Sets the content of this {@link HttpMessage}. + * + * If {@code null} is specified, the content of this message + * will be set to {@link Unpooled#EMPTY_BUFFER} + * + * @param content The {@link ByteBuf} containing the content to use */ void setContent(ByteBuf content); /** * Adds a new header with the specified name and value. - * If the specified value is not a {@link String}, it is converted into a - * {@link String} by {@link Object#toString()}, except for {@link Date} - * and {@link Calendar} which are formatted to the date format defined in - * RFC2616. + * + * If the specified value is not a {@link String}, it is converted + * into a {@link String} by {@link Object#toString()}, except in the cases + * of {@link Date} and {@link Calendar}, which are formatted to the date + * format defined in RFC2616. + * + * @param name The name of the header being added + * @param value The value of the header being added */ void addHeader(String name, Object value); /** - * Sets a new header with the specified name and value. If there is an - * existing header with the same name, the existing header is removed. + * Sets a header with the specified name and value. + * + * If there is an existing header with the same name, it is removed. * If the specified value is not a {@link String}, it is converted into a * {@link String} by {@link Object#toString()}, except for {@link Date} - * and {@link Calendar} which are formatted to the date format defined in + * and {@link Calendar}, which are formatted to the date format defined in * RFC2616. + * + * @param name The name of the header being set + * @param value The value of the header being set */ void setHeader(String name, Object value); /** - * Sets a new header with the specified name and values. If there is an - * existing header with the same name, the existing header is removed. + * Sets a header with the specified name and values. + * + * If there is an existing header with the same name, it is removed. * This method can be represented approximately as the following code: *
      * m.removeHeader(name);
@@ -125,42 +152,62 @@ public interface HttpMessage {
      *     m.addHeader(name, v);
      * }
      * 
+ * + * @param name The name of the headers being set + * @param values The values of the headers being set */ void setHeader(String name, Iterable values); /** * Removes the header with the specified name. + * + * @param name The name of the header to remove */ void removeHeader(String name); /** - * Removes all headers from this message. + * Removes all headers from this {@link HttpMessage}. */ void clearHeaders(); /** - * Returns {@code true} if and only if this message does not have any - * content but the {@link HttpChunk}s, which is generated by - * {@link HttpMessageDecoder} consecutively, contain the actual content. + * Checks to see if this {@link HttpMessage} is broken into multiple "chunks" + * + * If this returns true, it means that this {@link HttpMessage} + * actually has no content - The {@link HttpChunk}s (which are generated + * by the {@link HttpMessageDecoder} consecutively) contain the actual content. *

* Please note that this method will keep returning {@code true} if the * {@code "Transfer-Encoding"} of this message is {@code "chunked"}, even if * you attempt to override this property by calling {@link #setChunked(boolean)} * with {@code false}. + *

+ * + * @return True if this message is chunked, otherwise false */ boolean isChunked(); /** - * Sets if this message does not have any content but the - * {@link HttpChunk}s, which is generated by {@link HttpMessageDecoder} - * consecutively, contain the actual content. + * Sets the boolean defining if this {@link HttpMessage} is chunked. + * *

- * If this method is called with {@code true}, the content of this message - * becomes {@link Unpooled#EMPTY_BUFFER}. + * If this is set to true, it means that this initial {@link HttpMessage} + * does not contain any content - The content is contained by multiple + * {@link HttpChunk}s, which are generated by the {@link HttpMessageDecoder} + * consecutively. + * + * Because of this, the content of this {@link HttpMessage} becomes + * {@link Unpooled#EMPTY_BUFFER} + *

+ * *

* Even if this method is called with {@code false}, {@link #isChunked()} * will keep returning {@code true} if the {@code "Transfer-Encoding"} of * this message is {@code "chunked"}. + *

+ * + * @param chunked True if this message is to be delivered in chunks, + * otherwise false. */ void setChunked(boolean chunked); } From d975ab365c0e33d59e0ed052e6e196ba20e3b973 Mon Sep 17 00:00:00 2001 From: Cruz Julian Bishop Date: Fri, 29 Jun 2012 16:37:40 +1000 Subject: [PATCH 64/87] Make HttpRequest's documentation easier to read --- .../io/netty/handler/codec/http/HttpRequest.java | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpRequest.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpRequest.java index a6cfe6a1b3..6a4db17da4 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpRequest.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpRequest.java @@ -34,22 +34,30 @@ package io.netty.handler.codec.http; public interface HttpRequest extends HttpMessage { /** - * Returns the method of this request. + * Gets the {@link HttpMethod} of this {@link HttpRequest}. + * + * @return The {@link HttpMethod} of this {@link HttpRequest} */ HttpMethod getMethod(); /** - * Sets the method of this request. + * Sets the {@link HttpMethod} of this {@link HttpRequest}. + * + * @param The {@link HttpMethod} to set */ void setMethod(HttpMethod method); /** - * Returns the URI (or path) of this request. + * Gets the requested URI (or alternatively, path) + * + * @return The URI being requested */ String getUri(); /** - * Sets the URI (or path) of this request. + * Sets the URI (or alternatively, path) being requested. + * + * @param uri The URI being requested */ void setUri(String uri); } From c8d13e03e0bdf7706c4f5d3a9231094db9c93a74 Mon Sep 17 00:00:00 2001 From: Cruz Julian Bishop Date: Fri, 29 Jun 2012 16:39:39 +1000 Subject: [PATCH 65/87] Make HttpResponse's javadoc a bit easier to read --- .../java/io/netty/handler/codec/http/HttpResponse.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpResponse.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpResponse.java index 0eb4023ea4..a56b1821fb 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpResponse.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpResponse.java @@ -19,7 +19,7 @@ package io.netty.handler.codec.http; /** * An HTTP response. * - *

Accessing Cookie

+ *

Accessing Cookies

*

* Unlike the Servlet API, {@link Cookie} support is provided separately via {@link CookieDecoder}, * {@link ClientCookieEncoder}, and {@link ServerCookieEncoder}. @@ -32,12 +32,16 @@ package io.netty.handler.codec.http; public interface HttpResponse extends HttpMessage { /** - * Returns the status of this response. + * Gets the status of this {@link HttpResponse}. + * + * @return The {@link HttpResponseStatus} of this {@link HttpResponse} */ HttpResponseStatus getStatus(); /** - * Sets the status of this response. + * Sets the status of this {@link HttpResponse} + * + * @param status The {@link HttpResponseStatus} to use */ void setStatus(HttpResponseStatus status); } From e08c4ea3e05e76a5a5815d04c171a5b4b7002502 Mon Sep 17 00:00:00 2001 From: Cruz Julian Bishop Date: Fri, 29 Jun 2012 17:03:47 +1000 Subject: [PATCH 66/87] Documentation and slight internal refactoring of HttpCodecUtil --- .../handler/codec/http/HttpCodecUtil.java | 104 ++++++++++++------ 1 file changed, 70 insertions(+), 34 deletions(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpCodecUtil.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpCodecUtil.java index 9e99491469..ac40046505 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpCodecUtil.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpCodecUtil.java @@ -17,57 +17,84 @@ package io.netty.handler.codec.http; import java.util.List; +/** + * A utility class mainly for use with HTTP codec classes + */ final class HttpCodecUtil { - static void validateHeaderName(String name) { - if (name == null) { - throw new NullPointerException("name"); + /** + * Validates the name of a header + * + * @param headerName The header name being validated + */ + static void validateHeaderName(String headerName) { + //Check to see if the name is null + if (headerName == null) { + throw new NullPointerException("Header names cannot be null"); } - for (int i = 0; i < name.length(); i ++) { - char c = name.charAt(i); - if (c > 127) { + //Go through each of the characters in the name + for (int index = 0; index < headerName.length(); index ++) { + //Actually get the character + char character = headerName.charAt(index); + + //Check to see if the character is not an ASCII character + if (character > 127) { throw new IllegalArgumentException( - "name contains non-ascii character: " + name); + "Header name cannot contain non-ASCII characters: " + headerName); } - // Check prohibited characters. - switch (c) { + //Check for prohibited characters. + switch (character) { case '\t': case '\n': case 0x0b: case '\f': case '\r': case ' ': case ',': case ':': case ';': case '=': throw new IllegalArgumentException( - "name contains one of the following prohibited characters: " + - "=,;: \\t\\r\\n\\v\\f: " + name); + "Header name cannot contain the following prohibited characters: " + + "=,;: \\t\\r\\n\\v\\f: " + headerName); } } } - static void validateHeaderValue(String value) { - if (value == null) { - throw new NullPointerException("value"); + /** + * Validates the specified header value + * + * @param value The value being validated + */ + static void validateHeaderValue(String headerValue) { + //Check to see if the value is null + if (headerValue == null) { + throw new NullPointerException("Header values cannot be null"); } - // 0 - the previous character was neither CR nor LF - // 1 - the previous character was CR - // 2 - the previous character was LF + /* + * Set up the state of the validation + * + * States are as follows: + * + * 0: Previous character was neither CR nor LF + * 1: The previous character was CR + * 2: The previous character was LF + */ int state = 0; - for (int i = 0; i < value.length(); i ++) { - char c = value.charAt(i); + //Start looping through each of the character - // Check the absolutely prohibited characters. - switch (c) { + for (int index = 0; index < headerValue.length(); index ++) { + char character = headerValue.charAt(index); + + //Check the absolutely prohibited characters. + switch (character) { case 0x0b: // Vertical tab throw new IllegalArgumentException( - "value contains a prohibited character '\\v': " + value); + "Header value contains a prohibited character '\\v': " + headerValue); case '\f': throw new IllegalArgumentException( - "value contains a prohibited character '\\f': " + value); + "Header value contains a prohibited character '\\f': " + headerValue); } // Check the CRLF (HT | SP) pattern switch (state) { case 0: - switch (c) { + switch (character) { case '\r': state = 1; break; @@ -77,47 +104,56 @@ final class HttpCodecUtil { } break; case 1: - switch (c) { + switch (character) { case '\n': state = 2; break; default: throw new IllegalArgumentException( - "Only '\\n' is allowed after '\\r': " + value); + "Only '\\n' is allowed after '\\r': " + headerValue); } break; case 2: - switch (c) { + switch (character) { case '\t': case ' ': state = 0; break; default: throw new IllegalArgumentException( - "Only ' ' and '\\t' are allowed after '\\n': " + value); + "Only ' ' and '\\t' are allowed after '\\n': " + headerValue); } } } if (state != 0) { throw new IllegalArgumentException( - "value must not end with '\\r' or '\\n':" + value); + "Header value must not end with '\\r' or '\\n':" + headerValue); } } - static boolean isTransferEncodingChunked(HttpMessage m) { - List chunked = m.getHeaders(HttpHeaders.Names.TRANSFER_ENCODING); - if (chunked.isEmpty()) { + /** + * Checks to see if the transfer encoding in a specified {@link HttpMessage} is chunked + * + * @param message The message to check + * @return True if transfer encoding is chunked, otherwise false + */ + static boolean isTransferEncodingChunked(HttpMessage message) { + List transferEncodingHeaders = message.getHeaders(HttpHeaders.Names.TRANSFER_ENCODING); + if (transferEncodingHeaders.isEmpty()) { return false; } - for (String v: chunked) { - if (v.equalsIgnoreCase(HttpHeaders.Values.CHUNKED)) { + for (String value: transferEncodingHeaders) { + if (value.equalsIgnoreCase(HttpHeaders.Values.CHUNKED)) { return true; } } return false; } + /** + * A constructor to ensure that instances of this class are never made + */ private HttpCodecUtil() { } } From 4379a86c4c3ad53e460d40b78727d480601842b3 Mon Sep 17 00:00:00 2001 From: Cruz Julian Bishop Date: Fri, 29 Jun 2012 18:59:12 +1000 Subject: [PATCH 67/87] Documentation redone for Cookie --- .../io/netty/handler/codec/http/Cookie.java | 121 +++++++++++++----- 1 file changed, 89 insertions(+), 32 deletions(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/Cookie.java b/codec-http/src/main/java/io/netty/handler/codec/http/Cookie.java index 21bf4e3f6d..bddd257748 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/Cookie.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/Cookie.java @@ -18,134 +18,191 @@ package io.netty.handler.codec.http; import java.util.Set; /** - * An HTTP Cookie. + * An interface defining an + * HTTP cookie. */ public interface Cookie extends Comparable { /** - * Returns the name of this cookie. + * Gets the name of this {@link Cookie}. + * + * @return The name of this {@link Cookie} */ String getName(); /** - * Returns the value of this cookie. + * Gets the value of this {@link Cookie}. + * + * @return The value of this {@link Cookie} */ String getValue(); /** - * Sets the value of this cookie. + * Sets the value of this {@link Cookie}. + * + * @param value The value to set */ void setValue(String value); /** - * Returns the domain of this cookie. + * Gets the domain of this {@link Cookie}. + * + * @return The domain of this {@link Cookie} */ String getDomain(); /** - * Sets the domain of this cookie. + * Sets the domain of this {@link Cookie}. + * + * @param domain The domain to use */ void setDomain(String domain); /** - * Returns the path of this cookie. + * Gets the path of this {@link Cookie}. + * + * @return The {@link Cookie}'s path */ String getPath(); /** - * Sets the path of this cookie. + * Sets the path of this {@link Cookie}. + * + * @param path The path to use for this {@link Cookie} */ void setPath(String path); /** - * Returns the comment of this cookie. + * Gets the comment of this {@link Cookie}. + * + * @return The comment of this {@link Cookie} */ String getComment(); /** - * Sets the comment of this cookie. + * Sets the comment of this {@link Cookie}. + * + * @param comment The comment to use */ void setComment(String comment); /** - * Returns the max age of this cookie in seconds. + * Gets the maximum age of this {@link Cookie} in seconds. + * + * @return The maximum age of this {@link Cookie} */ long getMaxAge(); /** - * Sets the max age of this cookie in seconds. If {@code 0} is specified, - * this cookie will be removed by browser because it will be expired - * immediately. If {@code -1} is specified, this cookie will be removed - * when a user terminates browser. + * Sets the maximum age of this {@link Cookie} in seconds. + * If an age of {@code 0} is specified, this {@link Cookie} will be + * automatically removed by browser because it will expire immediately. + * If {@code -1} is specified, this {@link Cookie} will be removed when the + * browser is closed. + * + * @param maxAge The maximum age of this {@link Cookie} in seconds */ void setMaxAge(long maxAge); /** - * Returns the version of this cookie. + * Gets the version of this {@link Cookie}. + * + * @return The version of this {@link Cookie} */ int getVersion(); /** - * Sets the version of this cookie. + * Sets the version of this {@link Cookie}. + * + * @param version The new version to use */ void setVersion(int version); /** - * Returns the secure flag of this cookie. + * Checks to see if this {@link Cookie} is secure + * + * @return True if this {@link Cookie} is secure, otherwise false */ boolean isSecure(); /** - * Sets the secure flag of this cookie. + * Sets the security status of this {@link Cookie} + * + * @param secure True if this {@link Cookie} is to be secure, otherwise false */ void setSecure(boolean secure); /** - * Returns if this cookie cannot be accessed through client side script. - * This flag works only if the browser supports it. For more information, - * see here. + * Checks to see if this {@link Cookie} can only be accessed via HTTP. + * If this returns true, the {@link Cookie} cannot be accessed through + * client side script - But only if the browser supports it. + * For more information, please look here + * + * @return True if this {@link Cookie} is HTTP-only or false if it isn't */ boolean isHttpOnly(); /** - * Sets if this cookie cannot be accessed through client side script. - * This flag works only if the browser supports it. For more information, - * see here. + * Determines if this {@link Cookie} is HTTP only. + * If set to true, this {@link Cookie} cannot be accessed by a client + * side script. However, this works only if the browser supports it. + * For for information, please look + * here. + * + * @param httpOnly True if the {@link Cookie} is HTTP only, otherwise false. */ void setHttpOnly(boolean httpOnly); /** - * Returns the comment URL of this cookie. + * Gets the comment URL of this {@link Cookie}. + * + * @return The comment URL of this {@link Cookie} */ String getCommentUrl(); /** - * Sets the comment URL of this cookie. + * Sets the comment URL of this {@link Cookie}. + * + * @param commentUrl The comment URL to use */ void setCommentUrl(String commentUrl); /** - * Returns the discard flag of this cookie. + * Checks to see if this {@link Cookie} is to be discarded by the browser + * at the end of the current session. + * + * @return True if this {@link Cookie} is to be discarded, otherwise false */ boolean isDiscard(); /** - * Sets the discard flag of this cookie. + * Sets the discard flag of this {@link Cookie}. + * If set to true, this {@link Cookie} will be discarded by the browser + * at the end of the current session + * + * @param discard True if the {@link Cookie} is to be discarded */ void setDiscard(boolean discard); /** - * Returns the ports of this cookie. + * Returns the ports that this {@link Cookie} can be accessed on. + * + * @return The {@link Set} of ports that this {@link Cookie} can use */ Set getPorts(); /** - * Sets the ports of this cookie. + * Sets the ports that this {@link Cookie} can be accessed on. + * + * @param ports The ports that this {@link Cookie} can be accessed on */ void setPorts(int... ports); /** - * Sets the ports of this cookie. + * Sets the ports that this {@link Cookie} can be accessed on. + * + * @param ports The {@link Iterable} collection of ports that this + * {@link Cookie} can be accessed on. */ void setPorts(Iterable ports); } From 20062de0d968dcee45f29e7b2bb94089fff85b3a Mon Sep 17 00:00:00 2001 From: Cruz Julian Bishop Date: Fri, 29 Jun 2012 19:25:31 +1000 Subject: [PATCH 68/87] Changed "Gets the" to "Returns the" - Requested by @trustin --- .../java/io/netty/handler/codec/http/Cookie.java | 16 ++++++++-------- .../io/netty/handler/codec/http/HttpMessage.java | 6 +++--- .../io/netty/handler/codec/http/HttpRequest.java | 4 ++-- .../netty/handler/codec/http/HttpResponse.java | 2 +- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/Cookie.java b/codec-http/src/main/java/io/netty/handler/codec/http/Cookie.java index bddd257748..4cd1109c72 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/Cookie.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/Cookie.java @@ -24,14 +24,14 @@ import java.util.Set; public interface Cookie extends Comparable { /** - * Gets the name of this {@link Cookie}. + * Returns the name of this {@link Cookie}. * * @return The name of this {@link Cookie} */ String getName(); /** - * Gets the value of this {@link Cookie}. + * Returns the value of this {@link Cookie}. * * @return The value of this {@link Cookie} */ @@ -45,7 +45,7 @@ public interface Cookie extends Comparable { void setValue(String value); /** - * Gets the domain of this {@link Cookie}. + * Returns the domain of this {@link Cookie}. * * @return The domain of this {@link Cookie} */ @@ -59,7 +59,7 @@ public interface Cookie extends Comparable { void setDomain(String domain); /** - * Gets the path of this {@link Cookie}. + * Returns the path of this {@link Cookie}. * * @return The {@link Cookie}'s path */ @@ -73,7 +73,7 @@ public interface Cookie extends Comparable { void setPath(String path); /** - * Gets the comment of this {@link Cookie}. + * Returns the comment of this {@link Cookie}. * * @return The comment of this {@link Cookie} */ @@ -87,7 +87,7 @@ public interface Cookie extends Comparable { void setComment(String comment); /** - * Gets the maximum age of this {@link Cookie} in seconds. + * Returns the maximum age of this {@link Cookie} in seconds. * * @return The maximum age of this {@link Cookie} */ @@ -105,7 +105,7 @@ public interface Cookie extends Comparable { void setMaxAge(long maxAge); /** - * Gets the version of this {@link Cookie}. + * Returns the version of this {@link Cookie}. * * @return The version of this {@link Cookie} */ @@ -154,7 +154,7 @@ public interface Cookie extends Comparable { void setHttpOnly(boolean httpOnly); /** - * Gets the comment URL of this {@link Cookie}. + * Returns the comment URL of this {@link Cookie}. * * @return The comment URL of this {@link Cookie} */ diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpMessage.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpMessage.java index 6e0a29a8d4..bad45c6890 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpMessage.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpMessage.java @@ -37,7 +37,7 @@ import java.util.Set; public interface HttpMessage { /** - * Gets the value of a header with the specified name. If there are + * Returns the value of a header with the specified name. If there are * more than one values for the specified name, the first value is returned. * * @param name The name of the header to search @@ -78,7 +78,7 @@ public interface HttpMessage { Set getHeaderNames(); /** - * Gets the protocol version of this {@link HttpMessage} + * Returns the protocol version of this {@link HttpMessage} * * @returns The protocol version */ @@ -92,7 +92,7 @@ public interface HttpMessage { void setProtocolVersion(HttpVersion version); /** - * Gets the content of this {@link HttpMessage}. + * Returns the content of this {@link HttpMessage}. * * If there is no content or {@link #isChunked()} returns {@code true}, * an {@link Unpooled#EMPTY_BUFFER} is returned. diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpRequest.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpRequest.java index 6a4db17da4..c82c3ae5e4 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpRequest.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpRequest.java @@ -34,7 +34,7 @@ package io.netty.handler.codec.http; public interface HttpRequest extends HttpMessage { /** - * Gets the {@link HttpMethod} of this {@link HttpRequest}. + * Returns the {@link HttpMethod} of this {@link HttpRequest}. * * @return The {@link HttpMethod} of this {@link HttpRequest} */ @@ -48,7 +48,7 @@ public interface HttpRequest extends HttpMessage { void setMethod(HttpMethod method); /** - * Gets the requested URI (or alternatively, path) + * Returns the requested URI (or alternatively, path) * * @return The URI being requested */ diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpResponse.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpResponse.java index a56b1821fb..d57a697207 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpResponse.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpResponse.java @@ -32,7 +32,7 @@ package io.netty.handler.codec.http; public interface HttpResponse extends HttpMessage { /** - * Gets the status of this {@link HttpResponse}. + * Returns the status of this {@link HttpResponse}. * * @return The {@link HttpResponseStatus} of this {@link HttpResponse} */ From 0ae4a64db2d79c56748482b6b56b751d8c4e22c8 Mon Sep 17 00:00:00 2001 From: Cruz Julian Bishop Date: Fri, 29 Jun 2012 19:27:41 +1000 Subject: [PATCH 69/87] Assuming that @trustin will want this changed :) --- buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java index bf59be816d..ca86eb3cd0 100644 --- a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java @@ -576,7 +576,7 @@ public class CompositeByteBuf extends AbstractByteBuf { } /** - * Gets the {@link ByteBuf} portion of this {@link CompositeByteBuf} that + * Returns the {@link ByteBuf} portion of this {@link CompositeByteBuf} that * contains the specified {@code index}. This is an expert method! * *

From ce90550f64c58a60f6367983dd36c9e046ea9bf3 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 29 Jun 2012 12:28:08 +0200 Subject: [PATCH 70/87] Rename method and make it more clear thats an expert method. See #414 #415 --- buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java | 5 +++-- .../io/netty/buffer/AbstractCompositeChannelBufferTest.java | 4 ++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java index ca86eb3cd0..cca309890d 100644 --- a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java @@ -577,7 +577,7 @@ public class CompositeByteBuf extends AbstractByteBuf { /** * Returns the {@link ByteBuf} portion of this {@link CompositeByteBuf} that - * contains the specified {@code index}. This is an expert method! + * contains the specified {@code index}. This is an expert method! * *

* Please note that since a {@link CompositeByteBuf} is made up of @@ -585,13 +585,14 @@ public class CompositeByteBuf extends AbstractByteBuf { * Instead, it only returns a portion of the composite buffer where the * index is located *

+ * * * @param index The {@code index} to search for and include in the returned {@link ByteBuf} * @return The {@link ByteBuf} that contains the specified {@code index} * @throws IndexOutOfBoundsException when the specified {@code index} is * less than zero, or larger than {@code capacity()} */ - public ByteBuf getBufferFor(int index) throws IndexOutOfBoundsException { + public ByteBuf getBuffer(int index) throws IndexOutOfBoundsException { if (index < 0 || index >= capacity()) { throw new IndexOutOfBoundsException("Invalid index: " + index + " - Bytes needed: " + index + ", maximum is " diff --git a/buffer/src/test/java/io/netty/buffer/AbstractCompositeChannelBufferTest.java b/buffer/src/test/java/io/netty/buffer/AbstractCompositeChannelBufferTest.java index 86fa2a5009..e1229b57c4 100644 --- a/buffer/src/test/java/io/netty/buffer/AbstractCompositeChannelBufferTest.java +++ b/buffer/src/test/java/io/netty/buffer/AbstractCompositeChannelBufferTest.java @@ -99,14 +99,14 @@ public abstract class AbstractCompositeChannelBufferTest extends CompositeByteBuf buf = (CompositeByteBuf) Unpooled.wrappedBuffer(new byte[] { 1, 2, 3, 4, 5 }, new byte[] {4, 5, 6, 7, 8, 9, 26}); //Ensure that a random place will be fine - assertEquals(buf.getBufferFor(2).capacity(), 5); + assertEquals(buf.getBuffer(2).capacity(), 5); //Loop through each byte byte index = 0; while (index < buf.capacity()) { - ByteBuf _buf = buf.getBufferFor(index++); + ByteBuf _buf = buf.getBuffer(index++); assertNotNull(_buf); assertTrue(_buf.capacity() > 0); assertNotNull(_buf.getByte(0)); From efce2624ddeb88a77ec81d85d279347dce2e8c2e Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 29 Jun 2012 12:49:58 +0200 Subject: [PATCH 71/87] Fix checkstyle --- buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java index cca309890d..19e8fc4912 100644 --- a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java @@ -585,7 +585,7 @@ public class CompositeByteBuf extends AbstractByteBuf { * Instead, it only returns a portion of the composite buffer where the * index is located *

- * + * * * @param index The {@code index} to search for and include in the returned {@link ByteBuf} * @return The {@link ByteBuf} that contains the specified {@code index} From 8224c95e059182292380d32f86e1c12961219111 Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Fri, 29 Jun 2012 13:26:13 +0200 Subject: [PATCH 72/87] Port enhancement to reduce memory copy if possible. See #412 --- .../codec/http/HttpMessageDecoder.java | 27 ++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpMessageDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpMessageDecoder.java index f12a3f2647..409ed2e227 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpMessageDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpMessageDecoder.java @@ -444,19 +444,40 @@ public abstract class HttpMessageDecoder extends ReplayingDecoder= len) { + int index = internal.readerIndex(); + ByteBuf buf = internal.slice(index, len); + + // update the readerindex so an the next read its on the correct position + buffer.readerIndex(index + len); + return buf; + } else { + return buffer.readBytes(len); + } + } + private State readHeaders(ByteBuf buffer) throws TooLongFrameException { headerSize = 0; final HttpMessage message = this.message; From 02d5267a7066160cc9c540e5a36cf4697db63a4d Mon Sep 17 00:00:00 2001 From: Cruz Julian Bishop Date: Fri, 29 Jun 2012 21:59:48 +1000 Subject: [PATCH 73/87] Change Timeout.cancel() to return a boolean value, true on a successful cancel As requested in the javadoc for HashedWheelTimer --- .../src/main/java/io/netty/util/HashedWheelTimer.java | 6 +++--- common/src/main/java/io/netty/util/Timeout.java | 10 ++++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/common/src/main/java/io/netty/util/HashedWheelTimer.java b/common/src/main/java/io/netty/util/HashedWheelTimer.java index ca31f06ebc..a55a1498a8 100644 --- a/common/src/main/java/io/netty/util/HashedWheelTimer.java +++ b/common/src/main/java/io/netty/util/HashedWheelTimer.java @@ -501,13 +501,13 @@ public class HashedWheelTimer implements Timer { } @Override - public void cancel() { + public boolean cancel() { if (!state.compareAndSet(ST_INIT, ST_CANCELLED)) { - // TODO return false - return; + return false; } wheel[stopIndex].remove(this); + return true; } @Override diff --git a/common/src/main/java/io/netty/util/Timeout.java b/common/src/main/java/io/netty/util/Timeout.java index 139553f1bc..30a7da3ae1 100644 --- a/common/src/main/java/io/netty/util/Timeout.java +++ b/common/src/main/java/io/netty/util/Timeout.java @@ -44,9 +44,11 @@ public interface Timeout { boolean isCancelled(); /** - * Cancels the {@link TimerTask} associated with this handle. It the - * task has been executed or cancelled already, it will return with no - * side effect. + * Attempts to cancel the {@link TimerTask} associated with this handle. + * If the task has been executed or cancelled already, it will return with + * no side effect. + * + * @return True if the cancellation completed successfully, otherwise false */ - void cancel(); + boolean cancel(); } From 4324c618833138c3a673c6366e62e0c36b4ad78f Mon Sep 17 00:00:00 2001 From: Cruz Julian Bishop Date: Sat, 30 Jun 2012 20:30:56 +1000 Subject: [PATCH 74/87] Fixes javadoc from #414 / #415 (@trustin) --- buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java index 19e8fc4912..9d1e86109a 100644 --- a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java @@ -587,10 +587,10 @@ public class CompositeByteBuf extends AbstractByteBuf { *

* * - * @param index The {@code index} to search for and include in the returned {@link ByteBuf} - * @return The {@link ByteBuf} that contains the specified {@code index} + * @param index the {@code index} to search for and include in the returned {@link ByteBuf} + * @return the {@link ByteBuf} that contains the specified {@code index} * @throws IndexOutOfBoundsException when the specified {@code index} is - * less than zero, or larger than {@code capacity()} + * less than zero, or greater than {@code capacity()} */ public ByteBuf getBuffer(int index) throws IndexOutOfBoundsException { if (index < 0 || index >= capacity()) { @@ -601,7 +601,6 @@ public class CompositeByteBuf extends AbstractByteBuf { //Return the component byte buffer return components[componentId(index)]; - } @Override From 2a2394c132510dc5248e34e82301411613211dcf Mon Sep 17 00:00:00 2001 From: norman Date: Tue, 3 Jul 2012 10:18:57 +0200 Subject: [PATCH 75/87] Add getters for the specified timeout values. See #418 --- .../handler/timeout/IdleStateHandler.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/handler/src/main/java/io/netty/handler/timeout/IdleStateHandler.java b/handler/src/main/java/io/netty/handler/timeout/IdleStateHandler.java index 0a4d177d0b..f87e091420 100644 --- a/handler/src/main/java/io/netty/handler/timeout/IdleStateHandler.java +++ b/handler/src/main/java/io/netty/handler/timeout/IdleStateHandler.java @@ -200,6 +200,30 @@ public class IdleStateHandler extends ChannelHandlerAdapter { } } + /** + * Return the readerIdleTime that was given when instance this class in milliseconds. + * + */ + public long getReaderIdleTimeInMillis() { + return readerIdleTimeMillis; + } + + /** + * Return the writerIdleTime that was given when instance this class in milliseconds. + * + */ + public long getWriterIdleTimeInMillis() { + return writerIdleTimeMillis; + } + + /** + * Return the allIdleTime that was given when instance this class in milliseconds. + * + */ + public long getAllIdleTimeInMillis() { + return allIdleTimeMillis; + } + @Override public void beforeAdd(ChannelHandlerContext ctx) throws Exception { if (ctx.channel().isActive() & ctx.channel().isRegistered()) { From 166d8d9436c5b399939cf1170eba73d0eaba26ba Mon Sep 17 00:00:00 2001 From: norman Date: Tue, 3 Jul 2012 10:37:05 +0200 Subject: [PATCH 76/87] Minimize byte copies by using a CompositeByteBuf to concat the chunks. See #413 --- .../codec/http/HttpChunkAggregator.java | 67 ++++++++- .../codec/http/HttpChunkAggregatorTest.java | 140 ++++++++++++++++++ 2 files changed, 205 insertions(+), 2 deletions(-) create mode 100644 codec-http/src/test/java/io/netty/handler/codec/http/HttpChunkAggregatorTest.java diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpChunkAggregator.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpChunkAggregator.java index e65bb5d95e..44f218506f 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpChunkAggregator.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpChunkAggregator.java @@ -17,6 +17,7 @@ package io.netty.handler.codec.http; import static io.netty.handler.codec.http.HttpHeaders.*; import io.netty.buffer.ByteBuf; +import io.netty.buffer.CompositeByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; @@ -47,13 +48,16 @@ import java.util.Map.Entry; * @apiviz.has io.netty.handler.codec.http.HttpChunk oneway - - filters out */ public class HttpChunkAggregator extends MessageToMessageDecoder { - + public static final int DEFAULT_MAX_COMPOSITEBUFFER_COMPONENTS = 1024; private static final ByteBuf CONTINUE = Unpooled.copiedBuffer( "HTTP/1.1 100 Continue\r\n\r\n", CharsetUtil.US_ASCII); private final int maxContentLength; private HttpMessage currentMessage; + private int maxCumulationBufferComponents = DEFAULT_MAX_COMPOSITEBUFFER_COMPONENTS; + private ChannelHandlerContext ctx; + /** * Creates a new instance. * @@ -71,6 +75,38 @@ public class HttpChunkAggregator extends MessageToMessageDecoder= 2)"); + } + + if (ctx == null) { + this.maxCumulationBufferComponents = maxCumulationBufferComponents; + } else { + throw new IllegalStateException( + "decoder properties cannot be changed once the decoder is added to a pipeline."); + } + } + @Override public boolean isDecodable(Object msg) throws Exception { return msg instanceof HttpMessage || msg instanceof HttpChunk; @@ -131,7 +167,9 @@ public class HttpChunkAggregator extends MessageToMessageDecoder= maxCumulationBufferComponents) { + currentMessage.setContent(Unpooled.wrappedBuffer(composite.copy(), input)); + } else { + List decomposed = composite.decompose(0, composite.readableBytes()); + ByteBuf[] buffers = decomposed.toArray(new ByteBuf[decomposed.size() + 1]); + buffers[buffers.length - 1] = input; + + currentMessage.setContent(Unpooled.wrappedBuffer(buffers)); + } + } else { + currentMessage.setContent(Unpooled.wrappedBuffer(cumulation, input)); + } + + } + + public void beforeAdd(ChannelHandlerContext ctx) throws Exception { + this.ctx = ctx; + } + } diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/HttpChunkAggregatorTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/HttpChunkAggregatorTest.java new file mode 100644 index 0000000000..5662fbf3dc --- /dev/null +++ b/codec-http/src/test/java/io/netty/handler/codec/http/HttpChunkAggregatorTest.java @@ -0,0 +1,140 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.codec.http; + +import static org.junit.Assert.*; + +import java.util.List; + +import org.easymock.EasyMock; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.buffer.CompositeByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.embedded.EmbeddedMessageChannel; +import io.netty.handler.codec.TooLongFrameException; + +import io.netty.util.CharsetUtil; +import org.junit.Test; + +public class HttpChunkAggregatorTest { + + @Test + public void testAggregate() { + HttpChunkAggregator aggr = new HttpChunkAggregator(1024 * 1024); + EmbeddedMessageChannel embedder = new EmbeddedMessageChannel(aggr); + + HttpMessage message = new DefaultHttpMessage(HttpVersion.HTTP_1_1); + HttpHeaders.setHeader(message, "X-Test", true); + message.setChunked(true); + HttpChunk chunk1 = new DefaultHttpChunk(Unpooled.copiedBuffer("test", CharsetUtil.US_ASCII)); + HttpChunk chunk2 = new DefaultHttpChunk(Unpooled.copiedBuffer("test2", CharsetUtil.US_ASCII)); + HttpChunk chunk3 = new DefaultHttpChunk(Unpooled.EMPTY_BUFFER); + assertFalse(embedder.writeInbound(message)); + assertFalse(embedder.writeInbound(chunk1)); + assertFalse(embedder.writeInbound(chunk2)); + + // this should trigger a messageReceived event so return true + assertTrue(embedder.writeInbound(chunk3)); + assertTrue(embedder.finish()); + HttpMessage aggratedMessage = (HttpMessage) embedder.readInbound(); + assertNotNull(aggratedMessage); + + assertEquals(chunk1.getContent().readableBytes() + chunk2.getContent().readableBytes(), HttpHeaders.getContentLength(aggratedMessage)); + assertEquals(aggratedMessage.getHeader("X-Test"), Boolean.TRUE.toString()); + checkContentBuffer(aggratedMessage); + assertNull(embedder.readInbound()); + + } + + private void checkContentBuffer(HttpMessage aggregatedMessage) { + CompositeByteBuf buffer = (CompositeByteBuf) aggregatedMessage.getContent(); + assertEquals(2, buffer.numComponents()); + List buffers = buffer.decompose(0, buffer.capacity()); + assertEquals(2, buffers.size()); + for (ByteBuf buf: buffers) { + // This should be false as we decompose the buffer before to not have deep hierarchy + assertFalse(buf instanceof CompositeByteBuf); + } + } + + @Test + public void testAggregateWithTrailer() { + HttpChunkAggregator aggr = new HttpChunkAggregator(1024 * 1024); + EmbeddedMessageChannel embedder = new EmbeddedMessageChannel(aggr); + HttpMessage message = new DefaultHttpMessage(HttpVersion.HTTP_1_1); + HttpHeaders.setHeader(message, "X-Test", true); + message.setChunked(true); + HttpChunk chunk1 = new DefaultHttpChunk(Unpooled.copiedBuffer("test", CharsetUtil.US_ASCII)); + HttpChunk chunk2 = new DefaultHttpChunk(Unpooled.copiedBuffer("test2", CharsetUtil.US_ASCII)); + HttpChunkTrailer trailer = new DefaultHttpChunkTrailer(); + trailer.setHeader("X-Trailer", true); + + assertFalse(embedder.writeInbound(message)); + assertFalse(embedder.writeInbound(chunk1)); + assertFalse(embedder.writeInbound(chunk2)); + + // this should trigger a messageReceived event so return true + assertTrue(embedder.writeInbound(trailer)); + assertTrue(embedder.finish()); + HttpMessage aggratedMessage = (HttpMessage) embedder.readInbound(); + assertNotNull(aggratedMessage); + + assertEquals(chunk1.getContent().readableBytes() + chunk2.getContent().readableBytes(), HttpHeaders.getContentLength(aggratedMessage)); + assertEquals(aggratedMessage.getHeader("X-Test"), Boolean.TRUE.toString()); + assertEquals(aggratedMessage.getHeader("X-Trailer"), Boolean.TRUE.toString()); + checkContentBuffer(aggratedMessage); + + assertNull(embedder.readInbound()); + + } + + + @Test(expected = TooLongFrameException.class) + public void testTooLongFrameException() { + HttpChunkAggregator aggr = new HttpChunkAggregator(4); + EmbeddedMessageChannel embedder = new EmbeddedMessageChannel(aggr); + HttpMessage message = new DefaultHttpMessage(HttpVersion.HTTP_1_1); + message.setChunked(true); + HttpChunk chunk1 = new DefaultHttpChunk(Unpooled.copiedBuffer("test", CharsetUtil.US_ASCII)); + HttpChunk chunk2 = new DefaultHttpChunk(Unpooled.copiedBuffer("test2", CharsetUtil.US_ASCII)); + assertFalse(embedder.writeInbound(message)); + assertFalse(embedder.writeInbound(chunk1)); + embedder.writeInbound(chunk2); + fail(); + + } + + @Test(expected = IllegalArgumentException.class) + public void testInvalidConstructorUsage() { + new HttpChunkAggregator(0); + } + + @Test(expected = IllegalArgumentException.class) + public void testInvalidMaxCumulationBufferComponents() { + HttpChunkAggregator aggr= new HttpChunkAggregator(Integer.MAX_VALUE); + aggr.setMaxCumulationBufferComponents(1); + } + + @Test(expected = IllegalStateException.class) + public void testSetMaxCumulationBufferComponentsAfterInit() throws Exception { + HttpChunkAggregator aggr = new HttpChunkAggregator(Integer.MAX_VALUE); + ChannelHandlerContext ctx = EasyMock.createMock(ChannelHandlerContext.class); + EasyMock.replay(ctx); + aggr.beforeAdd(ctx); + aggr.setMaxCumulationBufferComponents(10); + } +} From 8595d85e4a450ac514d4d7e019b7793372fb66e8 Mon Sep 17 00:00:00 2001 From: norman Date: Wed, 4 Jul 2012 15:14:05 +0200 Subject: [PATCH 77/87] Port fix for #433 --- .../codec/http/HttpMessageDecoder.java | 22 ++++++ .../codec/http/HttpServerCodecTest.java | 73 +++++++++++++++++++ 2 files changed, 95 insertions(+) create mode 100644 codec-http/src/test/java/io/netty/handler/codec/http/HttpServerCodecTest.java diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpMessageDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpMessageDecoder.java index 409ed2e227..7b3049e0f0 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpMessageDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpMessageDecoder.java @@ -272,6 +272,17 @@ public abstract class HttpMessageDecoder extends ReplayingDecoder maxChunkSize) { toRead = maxChunkSize; @@ -325,6 +336,17 @@ public abstract class HttpMessageDecoder extends ReplayingDecoder maxChunkSize) { toRead = maxChunkSize; diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/HttpServerCodecTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/HttpServerCodecTest.java new file mode 100644 index 0000000000..2b627d431a --- /dev/null +++ b/codec-http/src/test/java/io/netty/handler/codec/http/HttpServerCodecTest.java @@ -0,0 +1,73 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.codec.http; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.embedded.EmbeddedByteChannel; +import io.netty.util.CharsetUtil; + +import org.junit.Assert; +import org.junit.Test; + +public class HttpServerCodecTest { + + /** + * Testcase for https://github.com/netty/netty/issues/433 + */ + @Test + public void testUnfinishedChunkedHttpRequestIsLastFlag() throws Exception { + + int maxChunkSize = 2000; + HttpServerCodec httpServerCodec = new HttpServerCodec(1000, 1000, maxChunkSize); + EmbeddedByteChannel decoderEmbedder = new EmbeddedByteChannel(httpServerCodec); + + int totalContentLength = maxChunkSize * 5; + decoderEmbedder.writeInbound(Unpooled.copiedBuffer("PUT /test HTTP/1.1\r\n" + + "Content-Length: " + totalContentLength + "\r\n" + + "\r\n", CharsetUtil.UTF_8)); + + int offeredContentLength = (int) (maxChunkSize * 2.5); + decoderEmbedder.writeInbound(prepareDataChunk(offeredContentLength)); + decoderEmbedder.finish(); + + HttpMessage httpMessage = (HttpMessage) decoderEmbedder.readInbound(); + Assert.assertTrue(httpMessage.isChunked()); + + + boolean empty = true; + int totalBytesPolled = 0; + for (;;) { + HttpChunk httpChunk = (HttpChunk) decoderEmbedder.readInbound(); + if (httpChunk == null) { + break; + } + empty = false; + totalBytesPolled += httpChunk.getContent().readableBytes(); + Assert.assertFalse(httpChunk.isLast()); + } + Assert.assertFalse(empty); + Assert.assertEquals(offeredContentLength, totalBytesPolled); + } + + private ByteBuf prepareDataChunk(int size) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < size; ++i) { + sb.append("a"); + } + return Unpooled.copiedBuffer(sb.toString(), CharsetUtil.UTF_8); + } +} From ae2906de1ed504a4170a2194a9a7ce51a8e2b0d7 Mon Sep 17 00:00:00 2001 From: norman Date: Wed, 4 Jul 2012 15:20:47 +0200 Subject: [PATCH 78/87] Correctly format cookies. This fix some bug which lead to expiring of cookies to not work. See #426 --- .../handler/codec/http/ClientCookieEncoder.java | 1 + .../netty/handler/codec/http/CookieEncoderUtil.java | 5 ++++- .../handler/codec/http/ServerCookieEncoder.java | 4 ++++ .../netty/handler/codec/http/CookieEncoderTest.java | 12 ++++++------ 4 files changed, 15 insertions(+), 7 deletions(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/ClientCookieEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/ClientCookieEncoder.java index ae71b7deea..b21cf174e7 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/ClientCookieEncoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/ClientCookieEncoder.java @@ -109,6 +109,7 @@ public final class ClientCookieEncoder { } buf.setCharAt(buf.length() - 1, (char) HttpConstants.DOUBLE_QUOTE); buf.append((char) HttpConstants.SEMICOLON); + buf.append((char) HttpConstants.SP); } } } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/CookieEncoderUtil.java b/codec-http/src/main/java/io/netty/handler/codec/http/CookieEncoderUtil.java index 8891dee841..66e24a9d34 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/CookieEncoderUtil.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/CookieEncoderUtil.java @@ -20,7 +20,7 @@ final class CookieEncoderUtil { static String stripTrailingSeparator(StringBuilder buf) { if (buf.length() > 0) { - buf.setLength(buf.length() - 1); + buf.setLength(buf.length() - 2); } return buf.toString(); } @@ -51,6 +51,7 @@ final class CookieEncoderUtil { sb.append((char) HttpConstants.EQUALS); sb.append(val); sb.append((char) HttpConstants.SEMICOLON); + sb.append((char) HttpConstants.SP); } static void addQuoted(StringBuilder sb, String name, String val) { @@ -64,6 +65,7 @@ final class CookieEncoderUtil { sb.append(val.replace("\\", "\\\\").replace("\"", "\\\"")); sb.append((char) HttpConstants.DOUBLE_QUOTE); sb.append((char) HttpConstants.SEMICOLON); + sb.append((char) HttpConstants.SP); } static void add(StringBuilder sb, String name, long val) { @@ -71,6 +73,7 @@ final class CookieEncoderUtil { sb.append((char) HttpConstants.EQUALS); sb.append(val); sb.append((char) HttpConstants.SEMICOLON); + sb.append((char) HttpConstants.SP); } private CookieEncoderUtil() { diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/ServerCookieEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/ServerCookieEncoder.java index 2923267157..4c917f52db 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/ServerCookieEncoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/ServerCookieEncoder.java @@ -83,10 +83,12 @@ public final class ServerCookieEncoder { if (cookie.isSecure()) { buf.append(CookieHeaderNames.SECURE); buf.append((char) HttpConstants.SEMICOLON); + buf.append((char) HttpConstants.SP); } if (cookie.isHttpOnly()) { buf.append(CookieHeaderNames.HTTPONLY); buf.append((char) HttpConstants.SEMICOLON); + buf.append((char) HttpConstants.SP); } if (cookie.getVersion() >= 1) { if (cookie.getComment() != null) { @@ -109,10 +111,12 @@ public final class ServerCookieEncoder { } buf.setCharAt(buf.length() - 1, (char) HttpConstants.DOUBLE_QUOTE); buf.append((char) HttpConstants.SEMICOLON); + buf.append((char) HttpConstants.SP); } if (cookie.isDiscard()) { buf.append(CookieHeaderNames.DISCARD); buf.append((char) HttpConstants.SEMICOLON); + buf.append((char) HttpConstants.SP); } } diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/CookieEncoderTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/CookieEncoderTest.java index ff701c1235..7c04c541d1 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/CookieEncoderTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/CookieEncoderTest.java @@ -26,7 +26,7 @@ import org.junit.Test; public class CookieEncoderTest { @Test public void testEncodingSingleCookieV0() { - String result = "myCookie=myValue;Expires=XXX;Path=/apathsomewhere;Domain=.adomainsomewhere;Secure"; + String result = "myCookie=myValue; Expires=XXX; Path=/apathsomewhere; Domain=.adomainsomewhere; Secure"; DateFormat df = new HttpHeaderDateFormat(); Cookie cookie = new DefaultCookie("myCookie", "myValue"); cookie.setComment("this is a Comment"); @@ -58,7 +58,7 @@ public class CookieEncoderTest { @Test public void testEncodingSingleCookieV1() { - String result = "myCookie=myValue;Max-Age=50;Path=\"/apathsomewhere\";Domain=.adomainsomewhere;Secure;Comment=\"this is a Comment\";Version=1"; + String result = "myCookie=myValue; Max-Age=50; Path=\"/apathsomewhere\"; Domain=.adomainsomewhere; Secure; Comment=\"this is a Comment\"; Version=1"; Cookie cookie = new DefaultCookie("myCookie", "myValue"); cookie.setVersion(1); cookie.setComment("this is a Comment"); @@ -72,7 +72,7 @@ public class CookieEncoderTest { @Test public void testEncodingSingleCookieV2() { - String result = "myCookie=myValue;Max-Age=50;Path=\"/apathsomewhere\";Domain=.adomainsomewhere;Secure;Comment=\"this is a Comment\";Version=1;CommentURL=\"http://aurl.com\";Port=\"80,8080\";Discard"; + String result = "myCookie=myValue; Max-Age=50; Path=\"/apathsomewhere\"; Domain=.adomainsomewhere; Secure; Comment=\"this is a Comment\"; Version=1; CommentURL=\"http://aurl.com\"; Port=\"80,8080\"; Discard"; Cookie cookie = new DefaultCookie("myCookie", "myValue"); cookie.setVersion(1); cookie.setComment("this is a Comment"); @@ -89,9 +89,9 @@ public class CookieEncoderTest { @Test public void testEncodingMultipleClientCookies() { - String c1 = "$Version=1;myCookie=myValue;$Path=\"/apathsomewhere\";$Domain=.adomainsomewhere;$Port=\"80,8080\";"; - String c2 = "$Version=1;myCookie2=myValue2;$Path=\"/anotherpathsomewhere\";$Domain=.anotherdomainsomewhere;"; - String c3 = "$Version=1;myCookie3=myValue3"; + String c1 = "$Version=1; myCookie=myValue; $Path=\"/apathsomewhere\"; $Domain=.adomainsomewhere; $Port=\"80,8080\"; "; + String c2 = "$Version=1; myCookie2=myValue2; $Path=\"/anotherpathsomewhere\"; $Domain=.anotherdomainsomewhere; "; + String c3 = "$Version=1; myCookie3=myValue3"; Cookie cookie = new DefaultCookie("myCookie", "myValue"); cookie.setVersion(1); cookie.setComment("this is a Comment"); From d6f1a82c310d8b10bdf7b3daa38132c0e2224c7a Mon Sep 17 00:00:00 2001 From: norman Date: Wed, 4 Jul 2012 15:27:10 +0200 Subject: [PATCH 79/87] Fix checkstyle --- .../java/io/netty/handler/codec/http/HttpMessageDecoder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpMessageDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpMessageDecoder.java index 7b3049e0f0..389110f532 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpMessageDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpMessageDecoder.java @@ -272,7 +272,7 @@ public abstract class HttpMessageDecoder extends ReplayingDecoder Date: Thu, 5 Jul 2012 07:53:29 +0200 Subject: [PATCH 80/87] Throw a special SSLException if a non SSL/TLS record was detected. See #437 --- .../handler/ssl/NotSslRecordException.java | 35 +++++++++ .../java/io/netty/handler/ssl/SslHandler.java | 77 +++++++++++++++++++ 2 files changed, 112 insertions(+) create mode 100644 handler/src/main/java/io/netty/handler/ssl/NotSslRecordException.java diff --git a/handler/src/main/java/io/netty/handler/ssl/NotSslRecordException.java b/handler/src/main/java/io/netty/handler/ssl/NotSslRecordException.java new file mode 100644 index 0000000000..6ba98f766d --- /dev/null +++ b/handler/src/main/java/io/netty/handler/ssl/NotSslRecordException.java @@ -0,0 +1,35 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.ssl; + +import javax.net.ssl.SSLException; + +/** + * Special {@link SSLException} which will get thrown if a packet is + * received that not looks like a TLS/SSL record. A user can check for + * this {@link NotSslRecordException} and so detect if one peer tries to + * use secure and the other plain connection. + * + * + */ +public class NotSslRecordException extends SSLException { + + private static final long serialVersionUID = -4316784434770656841L; + + public NotSslRecordException(String reason) { + super(reason); + } +} diff --git a/handler/src/main/java/io/netty/handler/ssl/SslHandler.java b/handler/src/main/java/io/netty/handler/ssl/SslHandler.java index 0323985dd3..dd74f7f0e9 100644 --- a/handler/src/main/java/io/netty/handler/ssl/SslHandler.java +++ b/handler/src/main/java/io/netty/handler/ssl/SslHandler.java @@ -16,6 +16,7 @@ package io.netty.handler.ssl; import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; @@ -463,6 +464,74 @@ public class SslHandler @Override public void inboundBufferUpdated(final ChannelHandlerContext ctx) throws Exception { final ByteBuf in = ctx.inboundByteBuffer(); + + if (in.readableBytes() < 5) { + return; + } + + // SSLv3 or TLS - Check ContentType + boolean tls; + switch (in.getUnsignedByte(in.readerIndex())) { + case 20: // change_cipher_spec + case 21: // alert + case 22: // handshake + case 23: // application_data + tls = true; + break; + default: + // SSLv2 or bad data + tls = false; + } + + int packetLength = -1; + if (tls) { + // SSLv3 or TLS - Check ProtocolVersion + int majorVersion = in.getUnsignedByte(in.readerIndex() + 1); + if (majorVersion == 3) { + // SSLv3 or TLS + packetLength = (getShort(in, in.readerIndex() + 3) & 0xFFFF) + 5; + if (packetLength <= 5) { + // Neither SSLv3 or TLSv1 (i.e. SSLv2 or bad data) + tls = false; + } + } else { + // Neither SSLv3 or TLSv1 (i.e. SSLv2 or bad data) + tls = false; + } + } + + if (!tls) { + // SSLv2 or bad data - Check the version + boolean sslv2 = true; + int headerLength = (in.getUnsignedByte( + in.readerIndex()) & 0x80) != 0 ? 2 : 3; + int majorVersion = in.getUnsignedByte( + in.readerIndex() + headerLength + 1); + if (majorVersion == 2 || majorVersion == 3) { + // SSLv2 + if (headerLength == 2) { + packetLength = (getShort(in, in.readerIndex()) & 0x7FFF) + 2; + } else { + packetLength = (getShort(in, in.readerIndex()) & 0x3FFF) + 3; + } + if (packetLength <= headerLength) { + sslv2 = false; + } + } else { + sslv2 = false; + } + + if (!sslv2) { + // Bad data - discard the buffer and raise an exception. + NotSslRecordException e = new NotSslRecordException( + "not an SSL/TLS record: " + ByteBufUtil.hexDump(in)); + in.skipBytes(in.readableBytes()); + throw e; + } + } + + assert packetLength > 0; + final ByteBuf out = ctx.nextInboundByteBuffer(); out.discardReadBytes(); @@ -521,6 +590,14 @@ public class SslHandler } } + /** + * Reads a big-endian short integer from the buffer. Please note that we do not use + * {@link ByteBuf#getShort(int)} because it might be a little-endian buffer. + */ + private static short getShort(ByteBuf buf, int offset) { + return (short) (buf.getByte(offset) << 8 | buf.getByte(offset + 1) & 0xFF); + } + private static SSLEngineResult unwrap(SSLEngine engine, ByteBuf in, ByteBuf out) throws SSLException { ByteBuffer in0 = in.nioBuffer(); for (;;) { From 3f1478b653dfa0f815a4f5c9b77dd3bad81832fe Mon Sep 17 00:00:00 2001 From: norman Date: Thu, 5 Jul 2012 09:18:59 +0200 Subject: [PATCH 81/87] Optimize SslHandler's detection of supressable exceptions, so it will not break on different OS's or jdk impls. See #79 --- .../java/io/netty/handler/ssl/SslHandler.java | 106 ++++++++++++++---- 1 file changed, 85 insertions(+), 21 deletions(-) diff --git a/handler/src/main/java/io/netty/handler/ssl/SslHandler.java b/handler/src/main/java/io/netty/handler/ssl/SslHandler.java index dd74f7f0e9..8138d4cdf6 100644 --- a/handler/src/main/java/io/netty/handler/ssl/SslHandler.java +++ b/handler/src/main/java/io/netty/handler/ssl/SslHandler.java @@ -29,10 +29,15 @@ import io.netty.channel.ChannelPipeline; import io.netty.channel.DefaultChannelFuture; import io.netty.logging.InternalLogger; import io.netty.logging.InternalLoggerFactory; +import io.netty.util.internal.DetectionUtil; import java.io.IOException; +import java.net.DatagramSocket; +import java.net.Socket; import java.nio.ByteBuffer; import java.nio.channels.ClosedChannelException; +import java.nio.channels.DatagramChannel; +import java.nio.channels.SocketChannel; import java.util.ArrayDeque; import java.util.Queue; import java.util.concurrent.Executor; @@ -149,9 +154,8 @@ public class SslHandler private static final InternalLogger logger = InternalLoggerFactory.getInstance(SslHandler.class); - private static final Pattern IGNORABLE_ERROR_MESSAGE = Pattern.compile( - "^.*(?:connection.*reset|connection.*closed|broken.*pipe).*$", - Pattern.CASE_INSENSITIVE); + private static final Pattern IGNORABLE_CLASS_IN_STACK = Pattern.compile( + "^.*(Socket|DatagramChannel|SctpChannel).*$"); private volatile ChannelHandlerContext ctx; private final SSLEngine engine; @@ -438,29 +442,89 @@ public class SslHandler @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - if (cause instanceof IOException && engine.isOutboundDone()) { - String message = String.valueOf(cause.getMessage()).toLowerCase(); - if (IGNORABLE_ERROR_MESSAGE.matcher(message).matches()) { - // It is safe to ignore the 'connection reset by peer' or - // 'broken pipe' error after sending closure_notify. - if (logger.isDebugEnabled()) { - logger.debug( - "Swallowing a 'connection reset by peer / " + - "broken pipe' error occurred while writing " + - "'closure_notify'", cause); - } - - // Close the connection explicitly just in case the transport - // did not close the connection automatically. - if (ctx.channel().isActive()) { - ctx.close(); - } - return; + if (ignoreException(cause)) { + // It is safe to ignore the 'connection reset by peer' or + // 'broken pipe' error after sending closure_notify. + if (logger.isDebugEnabled()) { + logger.debug( + "Swallowing a 'connection reset by peer / " + + "broken pipe' error occurred while writing " + + "'closure_notify'", cause); } + + // Close the connection explicitly just in case the transport + // did not close the connection automatically. + if (ctx.channel().isActive()) { + ctx.close(); + } + return; + } super.exceptionCaught(ctx, cause); } + /** + * Checks if the given {@link Throwable} can be ignore and just "swallowed" + * + * When an ssl connection is closed a close_notify message is sent. + * After that the peer also sends close_notify however, it's not mandatory to receive + * the close_notify. The party who sent the initial close_notify can close the connection immediately + * then the peer will get connection reset error. + * + */ + private boolean ignoreException(Throwable t) { + if (!(t instanceof SSLException) && t instanceof IOException && engine.isOutboundDone()) { + + // Inspect the StackTraceElements to see if it was a connection reset / broken pipe or not + StackTraceElement[] elements = t.getStackTrace(); + for (StackTraceElement element: elements) { + String classname = element.getClassName(); + String methodname = element.getMethodName(); + + // skip all classes that belong to the io.netty package + if (classname.startsWith("io.netty.")) { + continue; + } + + // check if the method name is read if not skip it + if (!methodname.equals("read")) { + continue; + } + + // This will also match against SocketInputStream which is used by openjdk 7 and maybe + // also others + if (IGNORABLE_CLASS_IN_STACK.matcher(classname).matches()) { + return true; + } + + try { + // No match by now.. Try to load the class via classloader and inspect it. + // This is mainly done as other JDK implementations may differ in name of + // the impl. + Class clazz = getClass().getClassLoader().loadClass(classname); + + if (SocketChannel.class.isAssignableFrom(clazz) + || DatagramChannel.class.isAssignableFrom(clazz) + || Socket.class.isAssignableFrom(clazz) + || DatagramSocket.class.isAssignableFrom(clazz)) { + return true; + } + + // also match against SctpChannel via String matching as it may not present. + if (DetectionUtil.javaVersion() >= 7 + && "com.sun.nio.sctp.SctpChannel".equals(clazz.getSuperclass().getName())) { + return true; + } + } catch (ClassNotFoundException e) { + // This should not happen just ignore + } + + } + } + + return false; + } + @Override public void inboundBufferUpdated(final ChannelHandlerContext ctx) throws Exception { final ByteBuf in = ctx.inboundByteBuffer(); From e1f39f1d85be02fe5e4c7c2801730eebb02e314f Mon Sep 17 00:00:00 2001 From: norman Date: Thu, 5 Jul 2012 09:37:26 +0200 Subject: [PATCH 82/87] Optimize SslHandler's detection of supressable exceptions, so it will not break on different OS's or jdk impls. See #79 --- .../java/io/netty/handler/ssl/SslHandler.java | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/handler/src/main/java/io/netty/handler/ssl/SslHandler.java b/handler/src/main/java/io/netty/handler/ssl/SslHandler.java index 8138d4cdf6..b0ded28cc6 100644 --- a/handler/src/main/java/io/netty/handler/ssl/SslHandler.java +++ b/handler/src/main/java/io/netty/handler/ssl/SslHandler.java @@ -156,6 +156,9 @@ public class SslHandler private static final Pattern IGNORABLE_CLASS_IN_STACK = Pattern.compile( "^.*(Socket|DatagramChannel|SctpChannel).*$"); + private static final Pattern IGNORABLE_ERROR_MESSAGE = Pattern.compile( + "^.*(?:connection.*reset|connection.*closed|broken.*pipe).*$", + Pattern.CASE_INSENSITIVE); private volatile ChannelHandlerContext ctx; private final SSLEngine engine; @@ -474,6 +477,14 @@ public class SslHandler */ private boolean ignoreException(Throwable t) { if (!(t instanceof SSLException) && t instanceof IOException && engine.isOutboundDone()) { + String message = String.valueOf(t.getMessage()).toLowerCase(); + + // first try to match connection reset / broke peer based on the regex. This is the fastest way + // but may fail on different jdk impls or OS's + if (IGNORABLE_ERROR_MESSAGE.matcher(message).matches()) { + return true; + } + // Inspect the StackTraceElements to see if it was a connection reset / broken pipe or not StackTraceElement[] elements = t.getStackTrace(); @@ -504,9 +515,7 @@ public class SslHandler Class clazz = getClass().getClassLoader().loadClass(classname); if (SocketChannel.class.isAssignableFrom(clazz) - || DatagramChannel.class.isAssignableFrom(clazz) - || Socket.class.isAssignableFrom(clazz) - || DatagramSocket.class.isAssignableFrom(clazz)) { + || DatagramChannel.class.isAssignableFrom(clazz)) { return true; } From e5972a74539d918dd1364bfb95dcb9d02c25a74d Mon Sep 17 00:00:00 2001 From: Trustin Lee Date: Sat, 7 Jul 2012 13:30:28 +0900 Subject: [PATCH 83/87] Use String.CASE_INSENSITIVE_ORDER instead of custom Comparator --- .../netty/handler/codec/http/HttpHeaders.java | 6 +-- .../util/internal/CaseIgnoringComparator.java | 39 ------------------- 2 files changed, 2 insertions(+), 43 deletions(-) delete mode 100644 common/src/main/java/io/netty/util/internal/CaseIgnoringComparator.java diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaders.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaders.java index 86a411f524..a29d87199a 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaders.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpHeaders.java @@ -15,8 +15,6 @@ */ package io.netty.handler.codec.http; -import io.netty.util.internal.CaseIgnoringComparator; - import java.text.ParseException; import java.util.Calendar; import java.util.Date; @@ -1134,8 +1132,8 @@ public class HttpHeaders { } Set getHeaderNames() { - Set names = - new TreeSet(CaseIgnoringComparator.INSTANCE); + + Set names = new TreeSet(String.CASE_INSENSITIVE_ORDER); Entry e = head.after; while (e != head) { diff --git a/common/src/main/java/io/netty/util/internal/CaseIgnoringComparator.java b/common/src/main/java/io/netty/util/internal/CaseIgnoringComparator.java deleted file mode 100644 index 5c9471d086..0000000000 --- a/common/src/main/java/io/netty/util/internal/CaseIgnoringComparator.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.util.internal; - -import java.io.Serializable; -import java.util.Comparator; - -public final class CaseIgnoringComparator implements Comparator, Serializable { - - private static final long serialVersionUID = 4582133183775373862L; - - public static final CaseIgnoringComparator INSTANCE = new CaseIgnoringComparator(); - - private CaseIgnoringComparator() { - } - - @Override - public int compare(String o1, String o2) { - return o1.compareToIgnoreCase(o2); - } - - @SuppressWarnings("static-method") - private Object readResolve() { - return INSTANCE; - } -} From b86e2730cc43be9cf957df2dd95ed8a43570d8e9 Mon Sep 17 00:00:00 2001 From: Trustin Lee Date: Sat, 7 Jul 2012 13:41:49 +0900 Subject: [PATCH 84/87] Remove unused classes in util.internal --- .../util/internal/DeadLockProofWorker.java | 53 ------- .../io/netty/util/internal/ExecutorUtil.java | 119 -------------- .../netty/util/internal/NonReentrantLock.java | 148 ------------------ .../io/netty/util/internal/StringUtil.java | 74 --------- .../util/internal/SystemPropertyUtil.java | 2 +- .../util/internal/ThreadLocalBoolean.java | 34 ---- .../util/internal/UnterminatableExecutor.java | 38 ----- .../netty/util/internal/StringUtilTest.java | 82 ---------- .../group/DefaultChannelGroupFuture.java | 13 -- 9 files changed, 1 insertion(+), 562 deletions(-) delete mode 100644 common/src/main/java/io/netty/util/internal/DeadLockProofWorker.java delete mode 100644 common/src/main/java/io/netty/util/internal/ExecutorUtil.java delete mode 100644 common/src/main/java/io/netty/util/internal/NonReentrantLock.java delete mode 100644 common/src/main/java/io/netty/util/internal/ThreadLocalBoolean.java delete mode 100644 common/src/main/java/io/netty/util/internal/UnterminatableExecutor.java delete mode 100644 common/src/test/java/io/netty/util/internal/StringUtilTest.java diff --git a/common/src/main/java/io/netty/util/internal/DeadLockProofWorker.java b/common/src/main/java/io/netty/util/internal/DeadLockProofWorker.java deleted file mode 100644 index 14406d6584..0000000000 --- a/common/src/main/java/io/netty/util/internal/DeadLockProofWorker.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.util.internal; - -import java.util.concurrent.Executor; - -/** - */ -public final class DeadLockProofWorker { - - /** - * An internal use only thread-local variable that tells the - * {@link Executor} that this worker acquired a worker thread from. - */ - public static final ThreadLocal PARENT = new ThreadLocal(); - - public static void start(final Executor parent, final Runnable runnable) { - if (parent == null) { - throw new NullPointerException("parent"); - } - if (runnable == null) { - throw new NullPointerException("runnable"); - } - - parent.execute(new Runnable() { - @Override - public void run() { - PARENT.set(parent); - try { - runnable.run(); - } finally { - PARENT.remove(); - } - } - }); - } - - private DeadLockProofWorker() { - } -} diff --git a/common/src/main/java/io/netty/util/internal/ExecutorUtil.java b/common/src/main/java/io/netty/util/internal/ExecutorUtil.java deleted file mode 100644 index a6892718af..0000000000 --- a/common/src/main/java/io/netty/util/internal/ExecutorUtil.java +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.util.internal; - -import java.util.concurrent.Executor; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.TimeUnit; - -/** - * Shuts down a list of {@link Executor}s. {@link #terminate(Executor...)} will - * shut down all specified {@link ExecutorService}s immediately and wait for - * their termination. An {@link Executor} which is not an {@link ExecutorService} - * will be ignored silently. - */ -public final class ExecutorUtil { - - /** - * Returns {@code true} if and only if the specified {@code executor} - * is an {@link ExecutorService} and is shut down. Please note that this - * method returns {@code false} if the specified {@code executor} is not an - * {@link ExecutorService}. - */ - public static boolean isShutdown(Executor executor) { - if (executor instanceof ExecutorService) { - if (((ExecutorService) executor).isShutdown()) { - return true; - } - } - return false; - } - - /** - * Shuts down the specified executors. - */ - public static void terminate(Executor... executors) { - // Check nulls. - if (executors == null) { - throw new NullPointerException("executors"); - } - - Executor[] executorsCopy = new Executor[executors.length]; - for (int i = 0; i < executors.length; i ++) { - if (executors[i] == null) { - throw new NullPointerException("executors[" + i + "]"); - } - executorsCopy[i] = executors[i]; - } - - // Check dead lock. - final Executor currentParent = DeadLockProofWorker.PARENT.get(); - if (currentParent != null) { - for (Executor e: executorsCopy) { - if (e == currentParent) { - throw new IllegalStateException( - "An Executor cannot be shut down from the thread " + - "acquired from itself. Please make sure you are " + - "not calling releaseExternalResources() from an " + - "I/O worker thread."); - } - } - } - - // Shut down all executors. - boolean interrupted = false; - for (Executor e: executorsCopy) { - if (!(e instanceof ExecutorService)) { - continue; - } - - ExecutorService es = (ExecutorService) e; - for (;;) { - try { - es.shutdownNow(); - } catch (SecurityException ex) { - // Running in a restricted environment - fall back. - try { - es.shutdown(); - } catch (SecurityException ex2) { - // Running in a more restricted environment. - // Can't shut down this executor - skip to the next. - break; - } catch (NullPointerException ex2) { - // Some JDK throws NPE here, but shouldn't. - } - } catch (NullPointerException ex) { - // Some JDK throws NPE here, but shouldn't. - } - - try { - if (es.awaitTermination(100, TimeUnit.MILLISECONDS)) { - break; - } - } catch (InterruptedException ex) { - interrupted = true; - } - } - } - - if (interrupted) { - Thread.currentThread().interrupt(); - } - } - - private ExecutorUtil() { - } -} diff --git a/common/src/main/java/io/netty/util/internal/NonReentrantLock.java b/common/src/main/java/io/netty/util/internal/NonReentrantLock.java deleted file mode 100644 index 32f32fa5b9..0000000000 --- a/common/src/main/java/io/netty/util/internal/NonReentrantLock.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.util.internal; - -import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.AbstractQueuedSynchronizer; -import java.util.concurrent.locks.Condition; -import java.util.concurrent.locks.Lock; - -/** - * A custom implementation of a lock that does not allow reentry - */ -public final class NonReentrantLock extends AbstractQueuedSynchronizer - implements Lock { - - /** - * The serial version unique ID - */ - private static final long serialVersionUID = -833780837233068610L; - - /** - * The {@link Thread} that owns this {@link NonReentrantLock} - */ - private Thread owner; - - /** - * Locks this {@link NonReentrantLock} - */ - @Override - public void lock() { - acquire(1); - } - - /** - * Locks this {@link NonReentrantLock}, but allow interruption - * - * @throws InterruptedException The lock was interrupted - */ - @Override - public void lockInterruptibly() throws InterruptedException { - acquireInterruptibly(1); - } - - /** - * Try to lock this {@link NonReentrantLock} - * - * @return True if locking was successful, otherwise false - */ - @Override - public boolean tryLock() { - return tryAcquire(1); - } - - /** - * Tries to lock this {@link NonReentrantLock} over a period of time - * - * @param time The maximum number of time units to attempt to get a lock for. - * @param unit The {@link TimeUnit} associated with the time parameter - * @return True if the lock was successful, otherwise false - * @throws InterruptedException The locking attempt was interrupted - */ - @Override - public boolean tryLock(long time, TimeUnit unit) - throws InterruptedException { - return tryAcquireNanos(1, unit.toNanos(time)); - } - - /** - * Unlocks this {@link NonReentrantLock} - */ - @Override - public void unlock() { - release(1); - } - - /** - * Checks to see if this {@link NonReentrantLock} is held by the current {@link Thread} - * - * @return True if held by the current thread, otherwise false - */ - public boolean isHeldByCurrentThread() { - return isHeldExclusively(); - } - - /** - * Creates a new {@link Condition} - * - * @return The condition object - */ - @Override - public Condition newCondition() { - return new ConditionObject(); - } - - /** - * Try to acquire a lock - * - * @param acquires A number that is sent by acquiring methods - * @return True if a lock is acquired, otherwise false - */ - @Override - protected boolean tryAcquire(int acquires) { - if (compareAndSetState(0, 1)) { - owner = Thread.currentThread(); - return true; - } - return false; - } - - /** - * Tries to release the lock - * - * @param releases A number that is passed by the release methods - * @return True if a release is granted, otherwise false - */ - @Override - protected boolean tryRelease(int releases) { - if (Thread.currentThread() != owner) { - throw new IllegalMonitorStateException(); - } - owner = null; - setState(0); - return true; - } - - /** - * Checks to see if this {@link NonReentrantLock} is held exclusively by the current {@link Thread} - * - * @return True if held exclusively, otherwise false - */ - @Override - protected boolean isHeldExclusively() { - return getState() != 0 && owner == Thread.currentThread(); - } -} diff --git a/common/src/main/java/io/netty/util/internal/StringUtil.java b/common/src/main/java/io/netty/util/internal/StringUtil.java index a82e7af4d9..ecdd72b8e2 100644 --- a/common/src/main/java/io/netty/util/internal/StringUtil.java +++ b/common/src/main/java/io/netty/util/internal/StringUtil.java @@ -38,80 +38,6 @@ public final class StringUtil { } NEWLINE = newLine; - } - /** - * Strip an Object of it's ISO control characters. - * - * @param value - * The Object that should be stripped. This objects toString method will - * called and the result passed to {@link #stripControlCharacters(String)}. - * @return {@code String} - * A new String instance with its hexadecimal control characters replaced - * by a space. Or the unmodified String if it does not contain any ISO - * control characters. - */ - public static String stripControlCharacters(Object value) { - if (value == null) { - return null; - } - - return stripControlCharacters(value.toString()); - } - - /** - * Strip a String of it's ISO control characters. - * - * @param value - * The String that should be stripped. - * @return {@code String} - * A new String instance with its hexadecimal control characters replaced - * by a space. Or the unmodified String if it does not contain any ISO - * control characters. - */ - public static String stripControlCharacters(String value) { - if (value == null) { - return null; - } - - boolean hasControlChars = false; - for (int i = value.length() - 1; i >= 0; i --) { - if (Character.isISOControl(value.charAt(i))) { - hasControlChars = true; - break; - } - } - - if (!hasControlChars) { - return value; - } - - StringBuilder buf = new StringBuilder(value.length()); - int i = 0; - - // Skip initial control characters (i.e. left trim) - for (; i < value.length(); i ++) { - if (!Character.isISOControl(value.charAt(i))) { - break; - } - } - - // Copy non control characters and substitute control characters with - // a space. The last control characters are trimmed. - boolean suppressingControlChars = false; - for (; i < value.length(); i ++) { - if (Character.isISOControl(value.charAt(i))) { - suppressingControlChars = true; - continue; - } else { - if (suppressingControlChars) { - suppressingControlChars = false; - buf.append(' '); - } - buf.append(value.charAt(i)); - } - } - - return buf.toString(); } } diff --git a/common/src/main/java/io/netty/util/internal/SystemPropertyUtil.java b/common/src/main/java/io/netty/util/internal/SystemPropertyUtil.java index 9bef749292..977e28918e 100644 --- a/common/src/main/java/io/netty/util/internal/SystemPropertyUtil.java +++ b/common/src/main/java/io/netty/util/internal/SystemPropertyUtil.java @@ -20,7 +20,7 @@ import java.util.regex.Pattern; /** * Accesses the system property swallowing a {@link SecurityException}. */ -public final class SystemPropertyUtil { +final class SystemPropertyUtil { /** * Returns the value of the Java system property with the specified diff --git a/common/src/main/java/io/netty/util/internal/ThreadLocalBoolean.java b/common/src/main/java/io/netty/util/internal/ThreadLocalBoolean.java deleted file mode 100644 index c262c7104c..0000000000 --- a/common/src/main/java/io/netty/util/internal/ThreadLocalBoolean.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.util.internal; - -public class ThreadLocalBoolean extends ThreadLocal { - - private final boolean defaultValue; - - public ThreadLocalBoolean() { - this(false); - } - - public ThreadLocalBoolean(boolean defaultValue) { - this.defaultValue = defaultValue; - } - - @Override - protected Boolean initialValue() { - return defaultValue? Boolean.TRUE : Boolean.FALSE; - } -} diff --git a/common/src/main/java/io/netty/util/internal/UnterminatableExecutor.java b/common/src/main/java/io/netty/util/internal/UnterminatableExecutor.java deleted file mode 100644 index e8b111d949..0000000000 --- a/common/src/main/java/io/netty/util/internal/UnterminatableExecutor.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.util.internal; - -import java.util.concurrent.Executor; - -/** - * Disables shutdown of an {@link Executor} by wrapping the {@link Executor}. - */ -public class UnterminatableExecutor implements Executor { - - private final Executor executor; - - public UnterminatableExecutor(Executor executor) { - if (executor == null) { - throw new NullPointerException("executor"); - } - this.executor = executor; - } - - @Override - public void execute(Runnable command) { - executor.execute(command); - } -} diff --git a/common/src/test/java/io/netty/util/internal/StringUtilTest.java b/common/src/test/java/io/netty/util/internal/StringUtilTest.java deleted file mode 100644 index b77ca465db..0000000000 --- a/common/src/test/java/io/netty/util/internal/StringUtilTest.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.util.internal; - -import static org.junit.Assert.*; - -import org.junit.Test; - -/** - * Unit test for {@link StringUtil}. - */ -public class StringUtilTest { - - @Test - public void stripControlCharactersObjectNull() { - assertNull(StringUtil.stripControlCharacters(null)); - } - - @Test - public void stripControlCharactersNull() { - assertNull(StringUtil.stripControlCharacters((String) null)); - } - - @Test - public void stripControlCharactersRightTrim() { - final char controlCode = 0x0000; - final Object object = "abbb" + controlCode; - assertEquals(5, ((String) object).length()); - - final String stripped = StringUtil.stripControlCharacters(object); - assertFalse(object.equals(stripped)); - assertEquals(4, stripped.length()); - } - - @Test - public void stripControlCharactersLeftTrim() { - final char controlCode = 0x0000; - final String string = controlCode + "abbb"; - assertEquals(5, string.length()); - - final String stripped = StringUtil.stripControlCharacters(string); - assertFalse(string.equals(stripped)); - assertEquals(4, stripped.length()); - } - - @Test - public void stripControlCharacters() { - for (char i = 0x0000; i <= 0x001F; i ++) { - assertStripped(i); - } - for (char i = 0x007F; i <= 0x009F; i ++) { - assertStripped(i); - } - } - - private static void assertStripped(final char controlCode) { - final Object object = "aaa" + controlCode + "bbb"; - final String stripped = StringUtil.stripControlCharacters(object); - assertEquals("aaa bbb", stripped); - } - - @Test - public void stripNonControlCharacter() { - final char controlCode = 0x002F; - final String string = controlCode + "abbb"; - final String stripped = StringUtil.stripControlCharacters(string); - assertEquals("The string should be unchanged", string, stripped); - } -} diff --git a/transport/src/main/java/io/netty/channel/group/DefaultChannelGroupFuture.java b/transport/src/main/java/io/netty/channel/group/DefaultChannelGroupFuture.java index abc534700e..8f0b1ab409 100755 --- a/transport/src/main/java/io/netty/channel/group/DefaultChannelGroupFuture.java +++ b/transport/src/main/java/io/netty/channel/group/DefaultChannelGroupFuture.java @@ -21,7 +21,6 @@ import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.logging.InternalLogger; import io.netty.logging.InternalLoggerFactory; -import io.netty.util.internal.DeadLockProofWorker; import java.util.ArrayList; import java.util.Collection; @@ -216,7 +215,6 @@ public class DefaultChannelGroupFuture implements ChannelGroupFuture { synchronized (this) { while (!done) { - checkDeadLock(); waiters++; try { wait(); @@ -244,7 +242,6 @@ public class DefaultChannelGroupFuture implements ChannelGroupFuture { boolean interrupted = false; synchronized (this) { while (!done) { - checkDeadLock(); waiters++; try { wait(); @@ -298,7 +295,6 @@ public class DefaultChannelGroupFuture implements ChannelGroupFuture { return done; } - checkDeadLock(); waiters++; try { for (;;) { @@ -332,15 +328,6 @@ public class DefaultChannelGroupFuture implements ChannelGroupFuture { } } - private static void checkDeadLock() { - if (DeadLockProofWorker.PARENT.get() != null) { - throw new IllegalStateException( - "await*() in I/O thread causes a dead lock or " + - "sudden performance drop. Use addListener() instead or " + - "call await*() from a different thread."); - } - } - void setDone() { synchronized (this) { // Allow only once. From 86d59a2e91fc29e24eae0054e5bddd020e15f787 Mon Sep 17 00:00:00 2001 From: Trustin Lee Date: Sat, 7 Jul 2012 13:42:36 +0900 Subject: [PATCH 85/87] No more mind-boggling ExternalResourceReleasable --- .../util/ExternalResourceReleasable.java | 31 ------------- .../io/netty/util/ExternalResourceUtil.java | 45 ------------------- 2 files changed, 76 deletions(-) delete mode 100644 common/src/main/java/io/netty/util/ExternalResourceReleasable.java delete mode 100644 common/src/main/java/io/netty/util/ExternalResourceUtil.java diff --git a/common/src/main/java/io/netty/util/ExternalResourceReleasable.java b/common/src/main/java/io/netty/util/ExternalResourceReleasable.java deleted file mode 100644 index 5e61834cec..0000000000 --- a/common/src/main/java/io/netty/util/ExternalResourceReleasable.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.util; - -/** - * A common interface for a class which depends on external resources that - * need explicit release or shutdown. - * @apiviz.landmark - */ -public interface ExternalResourceReleasable { - - /** - * Releases the external resources that this object depends on. You should - * not call this method if the external resources (e.g. thread pool) are - * in use by other objects. - */ - void releaseExternalResources(); -} diff --git a/common/src/main/java/io/netty/util/ExternalResourceUtil.java b/common/src/main/java/io/netty/util/ExternalResourceUtil.java deleted file mode 100644 index 59dae4d665..0000000000 --- a/common/src/main/java/io/netty/util/ExternalResourceUtil.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.util; - -/** - * A utility class that provides the convenient shutdown of - * {@link ExternalResourceReleasable}s. - */ -public final class ExternalResourceUtil { - - /** - * Releases the specified {@link ExternalResourceReleasable}s. - */ - public static void release(ExternalResourceReleasable... releasables) { - ExternalResourceReleasable[] releasablesCopy = - new ExternalResourceReleasable[releasables.length]; - - for (int i = 0; i < releasables.length; i ++) { - if (releasables[i] == null) { - throw new NullPointerException("releasables[" + i + "]"); - } - releasablesCopy[i] = releasables[i]; - } - - for (ExternalResourceReleasable e: releasablesCopy) { - e.releaseExternalResources(); - } - } - - private ExternalResourceUtil() { - } -} From e40c4ea6018fda2e95adfded6792fb8c136a949e Mon Sep 17 00:00:00 2001 From: Trustin Lee Date: Sat, 7 Jul 2012 13:52:54 +0900 Subject: [PATCH 86/87] Remove unused classes / Make HashedWheelTimer not use ReusableIterator --- .../netty/util/EstimatableObjectWrapper.java | 30 - .../java/io/netty/util/HashedWheelTimer.java | 19 +- .../ConcurrentIdentityWeakKeyHashMap.java | 1501 ----------------- .../netty/util/internal/ReusableIterator.java | 22 - 4 files changed, 3 insertions(+), 1569 deletions(-) delete mode 100644 common/src/main/java/io/netty/util/EstimatableObjectWrapper.java delete mode 100644 common/src/main/java/io/netty/util/internal/ConcurrentIdentityWeakKeyHashMap.java delete mode 100644 common/src/main/java/io/netty/util/internal/ReusableIterator.java diff --git a/common/src/main/java/io/netty/util/EstimatableObjectWrapper.java b/common/src/main/java/io/netty/util/EstimatableObjectWrapper.java deleted file mode 100644 index b143b5ee13..0000000000 --- a/common/src/main/java/io/netty/util/EstimatableObjectWrapper.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.util; - -/** - * Represents an object which contains another object that needs to be taken - * into account by {@link ObjectSizeEstimator} for more accurate object size - * estimation. - */ -public interface EstimatableObjectWrapper { - - /** - * Returns the underlying object that needs to be taken into account - * by {@link ObjectSizeEstimator} for more accurate object size estimation. - */ - Object unwrap(); -} diff --git a/common/src/main/java/io/netty/util/HashedWheelTimer.java b/common/src/main/java/io/netty/util/HashedWheelTimer.java index a55a1498a8..4271cfa968 100644 --- a/common/src/main/java/io/netty/util/HashedWheelTimer.java +++ b/common/src/main/java/io/netty/util/HashedWheelTimer.java @@ -18,12 +18,12 @@ package io.netty.util; import io.netty.logging.InternalLogger; import io.netty.logging.InternalLoggerFactory; import io.netty.util.internal.DetectionUtil; -import io.netty.util.internal.ReusableIterator; import io.netty.util.internal.SharedResourceMisuseDetector; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -91,7 +91,6 @@ public class HashedWheelTimer implements Timer { private final long roundDuration; final long tickDuration; final Set[] wheel; - final ReusableIterator[] iterators; final int mask; final ReadWriteLock lock = new ReentrantReadWriteLock(); volatile int wheelCursor; @@ -186,7 +185,6 @@ public class HashedWheelTimer implements Timer { // Normalize ticksPerWheel to power of two and initialize the wheel. wheel = createWheel(ticksPerWheel); - iterators = createIterators(wheel); mask = wheel.length - 1; // Convert tickDuration to milliseconds. @@ -228,15 +226,6 @@ public class HashedWheelTimer implements Timer { return wheel; } - @SuppressWarnings("unchecked") - private static ReusableIterator[] createIterators(Set[] wheel) { - ReusableIterator[] iterators = new ReusableIterator[wheel.length]; - for (int i = 0; i < wheel.length; i ++) { - iterators[i] = (ReusableIterator) wheel[i].iterator(); - } - return iterators; - } - private static int normalizeTicksPerWheel(int ticksPerWheel) { int normalizedTicksPerWheel = 1; while (normalizedTicksPerWheel < ticksPerWheel) { @@ -385,8 +374,7 @@ public class HashedWheelTimer implements Timer { lock.writeLock().lock(); try { int newWheelCursor = wheelCursor = wheelCursor + 1 & mask; - ReusableIterator i = iterators[newWheelCursor]; - fetchExpiredTimeouts(expiredTimeouts, i, deadline); + fetchExpiredTimeouts(expiredTimeouts, wheel[newWheelCursor].iterator(), deadline); } finally { lock.writeLock().unlock(); } @@ -394,10 +382,9 @@ public class HashedWheelTimer implements Timer { private void fetchExpiredTimeouts( List expiredTimeouts, - ReusableIterator i, long deadline) { + Iterator i, long deadline) { List slipped = null; - i.rewind(); while (i.hasNext()) { HashedWheelTimeout timeout = i.next(); if (timeout.remainingRounds <= 0) { diff --git a/common/src/main/java/io/netty/util/internal/ConcurrentIdentityWeakKeyHashMap.java b/common/src/main/java/io/netty/util/internal/ConcurrentIdentityWeakKeyHashMap.java deleted file mode 100644 index 2c8f075259..0000000000 --- a/common/src/main/java/io/netty/util/internal/ConcurrentIdentityWeakKeyHashMap.java +++ /dev/null @@ -1,1501 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -/* - * Written by Doug Lea with assistance from members of JCP JSR-166 - * Expert Group and released to the public domain, as explained at - * http://creativecommons.org/licenses/publicdomain - */ -package io.netty.util.internal; - -import java.lang.ref.Reference; -import java.lang.ref.ReferenceQueue; -import java.lang.ref.WeakReference; -import java.util.AbstractCollection; -import java.util.AbstractMap; -import java.util.AbstractSet; -import java.util.Collection; -import java.util.ConcurrentModificationException; -import java.util.Enumeration; -import java.util.Hashtable; -import java.util.Iterator; -import java.util.Map; -import java.util.NoSuchElementException; -import java.util.Set; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.locks.ReentrantLock; - - -/** - * An alternative weak-key identity-comparing {@link ConcurrentMap} which is - * similar to {@link java.util.concurrent.ConcurrentHashMap}. - * @param the type of keys maintained by this map - * @param the type of mapped values - */ -public final class ConcurrentIdentityWeakKeyHashMap extends AbstractMap implements ConcurrentMap { - - /* - * The basic strategy is to subdivide the table among Segments, - * each of which itself is a concurrently readable hash table. - */ - - /** - * The default initial capacity for this table, used when not otherwise - * specified in a constructor. - */ - static final int DEFAULT_INITIAL_CAPACITY = 16; - - /** - * The default load factor for this table, used when not otherwise specified - * in a constructor. - */ - static final float DEFAULT_LOAD_FACTOR = 0.75f; - - /** - * The default concurrency level for this table, used when not otherwise - * specified in a constructor. - */ - static final int DEFAULT_CONCURRENCY_LEVEL = 16; - - /** - * The maximum capacity, used if a higher value is implicitly specified by - * either of the constructors with arguments. MUST be a power of two - * <= 1<<30 to ensure that entries are indexable using integers. - */ - static final int MAXIMUM_CAPACITY = 1 << 30; - - /** - * The maximum number of segments to allow; used to bound constructor - * arguments. - */ - static final int MAX_SEGMENTS = 1 << 16; // slightly conservative - - /** - * Number of unsynchronized retries in size and containsValue methods before - * resorting to locking. This is used to avoid unbounded retries if tables - * undergo continuous modification which would make it impossible to obtain - * an accurate result. - */ - static final int RETRIES_BEFORE_LOCK = 2; - - /* ---------------- Fields -------------- */ - - /** - * Mask value for indexing into segments. The upper bits of a key's hash - * code are used to choose the segment. - */ - final int segmentMask; - - /** - * Shift value for indexing within segments. - */ - final int segmentShift; - - /** - * The segments, each of which is a specialized hash table - */ - final Segment[] segments; - - Set keySet; - Set> entrySet; - Collection values; - - /* ---------------- Small Utilities -------------- */ - - /** - * Applies a supplemental hash function to a given hashCode, which defends - * against poor quality hash functions. This is critical because - * ConcurrentReferenceHashMap uses power-of-two length hash tables, that - * otherwise encounter collisions for hashCodes that do not differ in lower - * or upper bits. - */ - private static int hash(int h) { - // Spread bits to regularize both segment and index locations, - // using variant of single-word Wang/Jenkins hash. - h += h << 15 ^ 0xffffcd7d; - h ^= h >>> 10; - h += h << 3; - h ^= h >>> 6; - h += (h << 2) + (h << 14); - return h ^ h >>> 16; - } - - /** - * Returns the segment that should be used for key with given hash. - * - * @param hash the hash code for the key - * @return the segment - */ - Segment segmentFor(int hash) { - return segments[hash >>> segmentShift & segmentMask]; - } - - private static int hashOf(Object key) { - return hash(System.identityHashCode(key)); - } - - /* ---------------- Inner Classes -------------- */ - - /** - * A weak-key reference which stores the key hash needed for reclamation. - */ - static final class WeakKeyReference extends WeakReference { - - final int hash; - - WeakKeyReference(K key, int hash, ReferenceQueue refQueue) { - super(key, refQueue); - this.hash = hash; - } - - public int keyHash() { - return hash; - } - - public Object keyRef() { - return this; - } - } - - /** - * ConcurrentReferenceHashMap list entry. Note that this is never exported - * out as a user-visible Map.Entry. - * - * Because the value field is volatile, not final, it is legal wrt - * the Java Memory Model for an unsynchronized reader to see null - * instead of initial value when read via a data race. Although a - * reordering leading to this is not likely to ever actually - * occur, the Segment.readValueUnderLock method is used as a - * backup in case a null (pre-initialized) value is ever seen in - * an unsynchronized access method. - */ - static final class HashEntry { - final Object keyRef; - final int hash; - volatile Object valueRef; - final HashEntry next; - - HashEntry( - K key, int hash, HashEntry next, V value, - ReferenceQueue refQueue) { - this.hash = hash; - this.next = next; - this.keyRef = new WeakKeyReference(key, hash, refQueue); - this.valueRef = value; - } - - @SuppressWarnings("unchecked") - K key() { - return ((WeakReference) keyRef).get(); - } - - V value() { - return dereferenceValue(valueRef); - } - - @SuppressWarnings("unchecked") - V dereferenceValue(Object value) { - if (value instanceof WeakKeyReference) { - return ((Reference) value).get(); - } - - return (V) value; - } - - void setValue(V value) { - this.valueRef = value; - } - - @SuppressWarnings("unchecked") - static HashEntry[] newArray(int i) { - return new HashEntry[i]; - } - } - - /** - * Segments are specialized versions of hash tables. This subclasses from - * ReentrantLock opportunistically, just to simplify some locking and avoid - * separate construction. - */ - static final class Segment extends ReentrantLock { - /* - * Segments maintain a table of entry lists that are ALWAYS kept in a - * consistent state, so can be read without locking. Next fields of - * nodes are immutable (final). All list additions are performed at the - * front of each bin. This makes it easy to check changes, and also fast - * to traverse. When nodes would otherwise be changed, new nodes are - * created to replace them. This works well for hash tables since the - * bin lists tend to be short. (The average length is less than two for - * the default load factor threshold.) - * - * Read operations can thus proceed without locking, but rely on - * selected uses of volatiles to ensure that completed write operations - * performed by other threads are noticed. For most purposes, the - * "count" field, tracking the number of elements, serves as that - * volatile variable ensuring visibility. This is convenient because - * this field needs to be read in many read operations anyway: - * - * - All (unsynchronized) read operations must first read the - * "count" field, and should not look at table entries if - * it is 0. - * - * - All (synchronized) write operations should write to - * the "count" field after structurally changing any bin. - * The operations must not take any action that could even - * momentarily cause a concurrent read operation to see - * inconsistent data. This is made easier by the nature of - * the read operations in Map. For example, no operation - * can reveal that the table has grown but the threshold - * has not yet been updated, so there are no atomicity - * requirements for this with respect to reads. - * - * As a guide, all critical volatile reads and writes to the count field - * are marked in code comments. - */ - - private static final long serialVersionUID = 5571906852696599096L; - - /** - * The number of elements in this segment's region. - */ - transient volatile int count; - - /** - * Number of updates that alter the size of the table. This is used - * during bulk-read methods to make sure they see a consistent snapshot: - * If modCounts change during a traversal of segments computing size or - * checking containsValue, then we might have an inconsistent view of - * state so (usually) must retry. - */ - int modCount; - - /** - * The table is rehashed when its size exceeds this threshold. - * (The value of this field is always (capacity * loadFactor).) - */ - int threshold; - - /** - * The per-segment table. - */ - transient volatile HashEntry[] table; - - /** - * The load factor for the hash table. Even though this value is same - * for all segments, it is replicated to avoid needing links to outer - * object. - */ - final float loadFactor; - - /** - * The collected weak-key reference queue for this segment. This should - * be (re)initialized whenever table is assigned, - */ - transient volatile ReferenceQueue refQueue; - - Segment(int initialCapacity, float lf) { - loadFactor = lf; - setTable(HashEntry.newArray(initialCapacity)); - } - - @SuppressWarnings("unchecked") - static Segment[] newArray(int i) { - return new Segment[i]; - } - - private static boolean keyEq(Object src, Object dest) { - return src == dest; - } - - /** - * Sets table to new HashEntry array. Call only while holding lock or in - * constructor. - */ - void setTable(HashEntry[] newTable) { - threshold = (int) (newTable.length * loadFactor); - table = newTable; - refQueue = new ReferenceQueue(); - } - - /** - * Returns properly casted first entry of bin for given hash. - */ - HashEntry getFirst(int hash) { - HashEntry[] tab = table; - return tab[hash & tab.length - 1]; - } - - HashEntry newHashEntry( - K key, int hash, HashEntry next, V value) { - return new HashEntry( - key, hash, next, value, refQueue); - } - - /** - * Reads value field of an entry under lock. Called if value field ever - * appears to be null. This is possible only if a compiler happens to - * reorder a HashEntry initialization with its table assignment, which - * is legal under memory model but is not known to ever occur. - */ - V readValueUnderLock(HashEntry e) { - lock(); - try { - removeStale(); - return e.value(); - } finally { - unlock(); - } - } - - /* Specialized implementations of map methods */ - - V get(Object key, int hash) { - if (count != 0) { // read-volatile - HashEntry e = getFirst(hash); - while (e != null) { - if (e.hash == hash && keyEq(key, e.key())) { - Object opaque = e.valueRef; - if (opaque != null) { - return e.dereferenceValue(opaque); - } - - return readValueUnderLock(e); // recheck - } - e = e.next; - } - } - return null; - } - - boolean containsKey(Object key, int hash) { - if (count != 0) { // read-volatile - HashEntry e = getFirst(hash); - while (e != null) { - if (e.hash == hash && keyEq(key, e.key())) { - return true; - } - e = e.next; - } - } - return false; - } - - boolean containsValue(Object value) { - if (count != 0) { // read-volatile - HashEntry[] tab = table; - int len = tab.length; - for (int i = 0; i < len; i ++) { - for (HashEntry e = tab[i]; e != null; e = e.next) { - Object opaque = e.valueRef; - V v; - - if (opaque == null) { - v = readValueUnderLock(e); // recheck - } else { - v = e.dereferenceValue(opaque); - } - - if (value.equals(v)) { - return true; - } - } - } - } - return false; - } - - boolean replace(K key, int hash, V oldValue, V newValue) { - lock(); - try { - removeStale(); - HashEntry e = getFirst(hash); - while (e != null && (e.hash != hash || !keyEq(key, e.key()))) { - e = e.next; - } - - boolean replaced = false; - if (e != null && oldValue.equals(e.value())) { - replaced = true; - e.setValue(newValue); - } - return replaced; - } finally { - unlock(); - } - } - - V replace(K key, int hash, V newValue) { - lock(); - try { - removeStale(); - HashEntry e = getFirst(hash); - while (e != null && (e.hash != hash || !keyEq(key, e.key()))) { - e = e.next; - } - - V oldValue = null; - if (e != null) { - oldValue = e.value(); - e.setValue(newValue); - } - return oldValue; - } finally { - unlock(); - } - } - - V put(K key, int hash, V value, boolean onlyIfAbsent) { - lock(); - try { - removeStale(); - int c = count; - if (c ++ > threshold) { // ensure capacity - int reduced = rehash(); - if (reduced > 0) { - count = (c -= reduced) - 1; // write-volatile - } - } - - HashEntry[] tab = table; - int index = hash & tab.length - 1; - HashEntry first = tab[index]; - HashEntry e = first; - while (e != null && (e.hash != hash || !keyEq(key, e.key()))) { - e = e.next; - } - - V oldValue; - if (e != null) { - oldValue = e.value(); - if (!onlyIfAbsent) { - e.setValue(value); - } - } else { - oldValue = null; - ++ modCount; - tab[index] = newHashEntry(key, hash, first, value); - count = c; // write-volatile - } - return oldValue; - } finally { - unlock(); - } - } - - int rehash() { - HashEntry[] oldTable = table; - int oldCapacity = oldTable.length; - if (oldCapacity >= MAXIMUM_CAPACITY) { - return 0; - } - - /* - * Reclassify nodes in each list to new Map. Because we are using - * power-of-two expansion, the elements from each bin must either - * stay at same index, or move with a power of two offset. We - * eliminate unnecessary node creation by catching cases where old - * nodes can be reused because their next fields won't change. - * Statistically, at the default threshold, only about one-sixth of - * them need cloning when a table doubles. The nodes they replace - * will be garbage collectable as soon as they are no longer - * referenced by any reader thread that may be in the midst of - * traversing table right now. - */ - - HashEntry[] newTable = HashEntry.newArray(oldCapacity << 1); - threshold = (int) (newTable.length * loadFactor); - int sizeMask = newTable.length - 1; - int reduce = 0; - for (int i = 0; i < oldCapacity; i ++) { - // We need to guarantee that any existing reads of old Map can - // proceed. So we cannot yet null out each bin. - HashEntry e = oldTable[i]; - - if (e != null) { - HashEntry next = e.next; - int idx = e.hash & sizeMask; - - // Single node on list - if (next == null) { - newTable[idx] = e; - } else { - // Reuse trailing consecutive sequence at same slot - HashEntry lastRun = e; - int lastIdx = idx; - for (HashEntry last = next; last != null; last = last.next) { - int k = last.hash & sizeMask; - if (k != lastIdx) { - lastIdx = k; - lastRun = last; - } - } - newTable[lastIdx] = lastRun; - // Clone all remaining nodes - for (HashEntry p = e; p != lastRun; p = p.next) { - // Skip GC'd weak references - K key = p.key(); - if (key == null) { - reduce ++; - continue; - } - int k = p.hash & sizeMask; - HashEntry n = newTable[k]; - newTable[k] = newHashEntry(key, p.hash, n, p.value()); - } - } - } - } - table = newTable; - return reduce; - } - - /** - * Remove; match on key only if value null, else match both. - */ - V remove(Object key, int hash, Object value, boolean refRemove) { - lock(); - try { - if (!refRemove) { - removeStale(); - } - int c = count - 1; - HashEntry[] tab = table; - int index = hash & tab.length - 1; - HashEntry first = tab[index]; - HashEntry e = first; - // a reference remove operation compares the Reference instance - while (e != null && key != e.keyRef && - (refRemove || hash != e.hash || !keyEq(key, e.key()))) { - e = e.next; - } - - V oldValue = null; - if (e != null) { - V v = e.value(); - if (value == null || value.equals(v)) { - oldValue = v; - // All entries following removed node can stay in list, - // but all preceding ones need to be cloned. - ++ modCount; - HashEntry newFirst = e.next; - for (HashEntry p = first; p != e; p = p.next) { - K pKey = p.key(); - if (pKey == null) { // Skip GC'd keys - c --; - continue; - } - - newFirst = newHashEntry( - pKey, p.hash, newFirst, p.value()); - } - tab[index] = newFirst; - count = c; // write-volatile - } - } - return oldValue; - } finally { - unlock(); - } - } - - @SuppressWarnings("rawtypes") - void removeStale() { - WeakKeyReference ref; - while ((ref = (WeakKeyReference) refQueue.poll()) != null) { - remove(ref.keyRef(), ref.keyHash(), null, true); - } - } - - void clear() { - if (count != 0) { - lock(); - try { - HashEntry[] tab = table; - for (int i = 0; i < tab.length; i ++) { - tab[i] = null; - } - ++ modCount; - // replace the reference queue to avoid unnecessary stale - // cleanups - refQueue = new ReferenceQueue(); - count = 0; // write-volatile - } finally { - unlock(); - } - } - } - } - - /* ---------------- Public operations -------------- */ - - /** - * Creates a new, empty map with the specified initial capacity, load factor - * and concurrency level. - * - * @param initialCapacity the initial capacity. The implementation performs - * internal sizing to accommodate this many elements. - * @param loadFactor the load factor threshold, used to control resizing. - * Resizing may be performed when the average number of - * elements per bin exceeds this threshold. - * @param concurrencyLevel the estimated number of concurrently updating - * threads. The implementation performs internal - * sizing to try to accommodate this many threads. - * @throws IllegalArgumentException if the initial capacity is negative or - * the load factor or concurrencyLevel are - * nonpositive. - */ - public ConcurrentIdentityWeakKeyHashMap( - int initialCapacity, float loadFactor, int concurrencyLevel) { - if (!(loadFactor > 0) || initialCapacity < 0 || concurrencyLevel <= 0) { - throw new IllegalArgumentException(); - } - - if (concurrencyLevel > MAX_SEGMENTS) { - concurrencyLevel = MAX_SEGMENTS; - } - - // Find power-of-two sizes best matching arguments - int sshift = 0; - int ssize = 1; - while (ssize < concurrencyLevel) { - ++ sshift; - ssize <<= 1; - } - segmentShift = 32 - sshift; - segmentMask = ssize - 1; - this.segments = Segment.newArray(ssize); - - if (initialCapacity > MAXIMUM_CAPACITY) { - initialCapacity = MAXIMUM_CAPACITY; - } - int c = initialCapacity / ssize; - if (c * ssize < initialCapacity) { - ++ c; - } - int cap = 1; - while (cap < c) { - cap <<= 1; - } - - for (int i = 0; i < this.segments.length; ++ i) { - this.segments[i] = new Segment(cap, loadFactor); - } - } - - /** - * Creates a new, empty map with the specified initial capacity and load - * factor and with the default reference types (weak keys, strong values), - * and concurrencyLevel (16). - * - * @param initialCapacity The implementation performs internal sizing to - * accommodate this many elements. - * @param loadFactor the load factor threshold, used to control resizing. - * Resizing may be performed when the average number of - * elements per bin exceeds this threshold. - * @throws IllegalArgumentException if the initial capacity of elements is - * negative or the load factor is - * nonpositive - */ - public ConcurrentIdentityWeakKeyHashMap(int initialCapacity, float loadFactor) { - this(initialCapacity, loadFactor, DEFAULT_CONCURRENCY_LEVEL); - } - - /** - * Creates a new, empty map with the specified initial capacity, and with - * default reference types (weak keys, strong values), load factor (0.75) - * and concurrencyLevel (16). - * - * @param initialCapacity the initial capacity. The implementation performs - * internal sizing to accommodate this many elements. - * @throws IllegalArgumentException if the initial capacity of elements is - * negative. - */ - public ConcurrentIdentityWeakKeyHashMap(int initialCapacity) { - this(initialCapacity, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL); - } - - /** - * Creates a new, empty map with a default initial capacity (16), reference - * types (weak keys, strong values), default load factor (0.75) and - * concurrencyLevel (16). - */ - public ConcurrentIdentityWeakKeyHashMap() { - this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL); - } - - /** - * Creates a new map with the same mappings as the given map. The map is - * created with a capacity of 1.5 times the number of mappings in the given - * map or 16 (whichever is greater), and a default load factor (0.75) and - * concurrencyLevel (16). - * - * @param m the map - */ - public ConcurrentIdentityWeakKeyHashMap(Map m) { - this(Math.max((int) (m.size() / DEFAULT_LOAD_FACTOR) + 1, - DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR, - DEFAULT_CONCURRENCY_LEVEL); - putAll(m); - } - - /** - * Returns true if this map contains no key-value mappings. - * - * @return true if this map contains no key-value mappings - */ - @Override - public boolean isEmpty() { - final Segment[] segments = this.segments; - /* - * We keep track of per-segment modCounts to avoid ABA problems in which - * an element in one segment was added and in another removed during - * traversal, in which case the table was never actually empty at any - * point. Note the similar use of modCounts in the size() and - * containsValue() methods, which are the only other methods also - * susceptible to ABA problems. - */ - int[] mc = new int[segments.length]; - int mcsum = 0; - for (int i = 0; i < segments.length; ++ i) { - if (segments[i].count != 0) { - return false; - } else { - mcsum += mc[i] = segments[i].modCount; - } - } - // If mcsum happens to be zero, then we know we got a snapshot before - // any modifications at all were made. This is probably common enough - // to bother tracking. - if (mcsum != 0) { - for (int i = 0; i < segments.length; ++ i) { - if (segments[i].count != 0 || mc[i] != segments[i].modCount) { - return false; - } - } - } - return true; - } - - /** - * Returns the number of key-value mappings in this map. If the map contains - * more than Integer.MAX_VALUE elements, returns - * Integer.MAX_VALUE. - * - * @return the number of key-value mappings in this map - */ - @Override - public int size() { - final Segment[] segments = this.segments; - long sum = 0; - long check = 0; - int[] mc = new int[segments.length]; - // Try a few times to get accurate count. On failure due to continuous - // async changes in table, resort to locking. - for (int k = 0; k < RETRIES_BEFORE_LOCK; ++ k) { - check = 0; - sum = 0; - int mcsum = 0; - for (int i = 0; i < segments.length; ++ i) { - sum += segments[i].count; - mcsum += mc[i] = segments[i].modCount; - } - if (mcsum != 0) { - for (int i = 0; i < segments.length; ++ i) { - check += segments[i].count; - if (mc[i] != segments[i].modCount) { - check = -1; // force retry - break; - } - } - } - if (check == sum) { - break; - } - } - if (check != sum) { // Resort to locking all segments - sum = 0; - for (int i = 0; i < segments.length; ++ i) { - segments[i].lock(); - } - for (int i = 0; i < segments.length; ++ i) { - sum += segments[i].count; - } - for (int i = 0; i < segments.length; ++ i) { - segments[i].unlock(); - } - } - if (sum > Integer.MAX_VALUE) { - return Integer.MAX_VALUE; - } else { - return (int) sum; - } - } - - /** - * Returns the value to which the specified key is mapped, or {@code null} - * if this map contains no mapping for the key. - * - *

More formally, if this map contains a mapping from a key {@code k} to - * a value {@code v} such that {@code key.equals(k)}, then this method - * returns {@code v}; otherwise it returns {@code null}. (There can be at - * most one such mapping.) - * - * @throws NullPointerException if the specified key is null - */ - @Override - public V get(Object key) { - int hash = hashOf(key); - return segmentFor(hash).get(key, hash); - } - - /** - * Tests if the specified object is a key in this table. - * - * @param key possible key - * @return true if and only if the specified object is a key in - * this table, as determined by the equals method; - * false otherwise. - * @throws NullPointerException if the specified key is null - */ - @Override - public boolean containsKey(Object key) { - int hash = hashOf(key); - return segmentFor(hash).containsKey(key, hash); - } - - /** - * Returns true if this map maps one or more keys to the specified - * value. Note: This method requires a full internal traversal of the hash - * table, and so is much slower than method containsKey. - * - * @param value value whose presence in this map is to be tested - * @return true if this map maps one or more keys to the specified - * value - * @throws NullPointerException if the specified value is null - */ - - @Override - public boolean containsValue(Object value) { - if (value == null) { - throw new NullPointerException(); - } - - // See explanation of modCount use above - - final Segment[] segments = this.segments; - int[] mc = new int[segments.length]; - - // Try a few times without locking - for (int k = 0; k < RETRIES_BEFORE_LOCK; ++ k) { - int mcsum = 0; - for (int i = 0; i < segments.length; ++ i) { - mcsum += mc[i] = segments[i].modCount; - if (segments[i].containsValue(value)) { - return true; - } - } - boolean cleanSweep = true; - if (mcsum != 0) { - for (int i = 0; i < segments.length; ++ i) { - if (mc[i] != segments[i].modCount) { - cleanSweep = false; - break; - } - } - } - if (cleanSweep) { - return false; - } - } - // Resort to locking all segments - for (int i = 0; i < segments.length; ++ i) { - segments[i].lock(); - } - boolean found = false; - try { - for (int i = 0; i < segments.length; ++ i) { - if (segments[i].containsValue(value)) { - found = true; - break; - } - } - } finally { - for (int i = 0; i < segments.length; ++ i) { - segments[i].unlock(); - } - } - return found; - } - - /** - * Legacy method testing if some key maps into the specified value in this - * table. This method is identical in functionality to - * {@link #containsValue}, and exists solely to ensure full compatibility - * with class {@link Hashtable}, which supported this method prior to - * introduction of the Java Collections framework. - * - * @param value a value to search for - * @return true if and only if some key maps to the value - * argument in this table as determined by the equals - * method; false otherwise - * @throws NullPointerException if the specified value is null - */ - public boolean contains(Object value) { - return containsValue(value); - } - - /** - * Maps the specified key to the specified value in this table. Neither the - * key nor the value can be null. - * - *

The value can be retrieved by calling the get method with a - * key that is equal to the original key. - * - * @param key key with which the specified value is to be associated - * @param value value to be associated with the specified key - * @return the previous value associated with key, or null - * if there was no mapping for key - * @throws NullPointerException if the specified key or value is null - */ - @Override - public V put(K key, V value) { - if (value == null) { - throw new NullPointerException(); - } - int hash = hashOf(key); - return segmentFor(hash).put(key, hash, value, false); - } - - /** - * {@inheritDoc} - * - * @return the previous value associated with the specified key, or - * null if there was no mapping for the key - * @throws NullPointerException if the specified key or value is null - */ - @Override - public V putIfAbsent(K key, V value) { - if (value == null) { - throw new NullPointerException(); - } - int hash = hashOf(key); - return segmentFor(hash).put(key, hash, value, true); - } - - /** - * Copies all of the mappings from the specified map to this one. These - * mappings replace any mappings that this map had for any of the keys - * currently in the specified map. - * - * @param m mappings to be stored in this map - */ - @Override - public void putAll(Map m) { - for (Map.Entry e: m.entrySet()) { - put(e.getKey(), e.getValue()); - } - } - - /** - * Removes the key (and its corresponding value) from this map. This method - * does nothing if the key is not in the map. - * - * @param key the key that needs to be removed - * @return the previous value associated with key, or null - * if there was no mapping for key - * @throws NullPointerException if the specified key is null - */ - @Override - public V remove(Object key) { - int hash = hashOf(key); - return segmentFor(hash).remove(key, hash, null, false); - } - - /** - * {@inheritDoc} - * - * @throws NullPointerException if the specified key is null - */ - @Override - public boolean remove(Object key, Object value) { - int hash = hashOf(key); - if (value == null) { - return false; - } - return segmentFor(hash).remove(key, hash, value, false) != null; - } - - /** - * {@inheritDoc} - * - * @throws NullPointerException if any of the arguments are null - */ - @Override - public boolean replace(K key, V oldValue, V newValue) { - if (oldValue == null || newValue == null) { - throw new NullPointerException(); - } - int hash = hashOf(key); - return segmentFor(hash).replace(key, hash, oldValue, newValue); - } - - /** - * {@inheritDoc} - * - * @return the previous value associated with the specified key, or - * null if there was no mapping for the key - * @throws NullPointerException if the specified key or value is null - */ - @Override - public V replace(K key, V value) { - if (value == null) { - throw new NullPointerException(); - } - int hash = hashOf(key); - return segmentFor(hash).replace(key, hash, value); - } - - /** - * Removes all of the mappings from this map. - */ - @Override - public void clear() { - for (int i = 0; i < segments.length; ++ i) { - segments[i].clear(); - } - } - - /** - * Removes any stale entries whose keys have been finalized. Use of this - * method is normally not necessary since stale entries are automatically - * removed lazily, when blocking operations are required. However, there are - * some cases where this operation should be performed eagerly, such as - * cleaning up old references to a ClassLoader in a multi-classloader - * environment. - * - * Note: this method will acquire locks, one at a time, across all segments - * of this table, so if it is to be used, it should be used sparingly. - */ - public void purgeStaleEntries() { - for (int i = 0; i < segments.length; ++ i) { - segments[i].removeStale(); - } - } - - /** - * Returns a {@link Set} view of the keys contained in this map. The set is - * backed by the map, so changes to the map are reflected in the set, and - * vice-versa. The set supports element removal, which removes the - * corresponding mapping from this map, via the Iterator.remove, - * Set.remove, removeAll, retainAll, and - * clear operations. It does not support the add or - * addAll operations. - * - *

The view's iterator is a "weakly consistent" iterator that - * will never throw {@link ConcurrentModificationException}, and guarantees - * to traverse elements as they existed upon construction of the iterator, - * and may (but is not guaranteed to) reflect any modifications subsequent - * to construction. - */ - @Override - public Set keySet() { - Set ks = keySet; - return ks != null? ks : (keySet = new KeySet()); - } - - /** - * Returns a {@link Collection} view of the values contained in this map. - * The collection is backed by the map, so changes to the map are reflected - * in the collection, and vice-versa. The collection supports element - * removal, which removes the corresponding mapping from this map, via the - * Iterator.remove, Collection.remove, removeAll, - * retainAll, and clear operations. It does not support - * the add or addAll operations. - * - *

The view's iterator is a "weakly consistent" iterator that - * will never throw {@link ConcurrentModificationException}, and guarantees - * to traverse elements as they existed upon construction of the iterator, - * and may (but is not guaranteed to) reflect any modifications subsequent - * to construction. - */ - @Override - public Collection values() { - Collection vs = values; - return vs != null? vs : (values = new Values()); - } - - /** - * Returns a {@link Set} view of the mappings contained in this map. - * The set is backed by the map, so changes to the map are reflected in the - * set, and vice-versa. The set supports element removal, which removes the - * corresponding mapping from the map, via the Iterator.remove, - * Set.remove, removeAll, retainAll, and - * clear operations. It does not support the add or - * addAll operations. - * - *

The view's iterator is a "weakly consistent" iterator that - * will never throw {@link ConcurrentModificationException}, and guarantees - * to traverse elements as they existed upon construction of the iterator, - * and may (but is not guaranteed to) reflect any modifications subsequent - * to construction. - */ - @Override - public Set> entrySet() { - Set> es = entrySet; - return es != null? es : (entrySet = new EntrySet()); - } - - /** - * Returns an enumeration of the keys in this table. - * - * @return an enumeration of the keys in this table - * @see #keySet() - */ - public Enumeration keys() { - return new KeyIterator(); - } - - /** - * Returns an enumeration of the values in this table. - * - * @return an enumeration of the values in this table - * @see #values() - */ - public Enumeration elements() { - return new ValueIterator(); - } - - /* ---------------- Iterator Support -------------- */ - - abstract class HashIterator { - int nextSegmentIndex; - int nextTableIndex; - HashEntry[] currentTable; - HashEntry nextEntry; - HashEntry lastReturned; - K currentKey; // Strong reference to weak key (prevents gc) - - HashIterator() { - nextSegmentIndex = segments.length - 1; - nextTableIndex = -1; - advance(); - } - - public void rewind() { - nextSegmentIndex = segments.length - 1; - nextTableIndex = -1; - currentTable = null; - nextEntry = null; - lastReturned = null; - currentKey = null; - advance(); - } - - public boolean hasMoreElements() { - return hasNext(); - } - - final void advance() { - if (nextEntry != null && (nextEntry = nextEntry.next) != null) { - return; - } - - while (nextTableIndex >= 0) { - if ((nextEntry = currentTable[nextTableIndex --]) != null) { - return; - } - } - - while (nextSegmentIndex >= 0) { - Segment seg = segments[nextSegmentIndex --]; - if (seg.count != 0) { - currentTable = seg.table; - for (int j = currentTable.length - 1; j >= 0; -- j) { - if ((nextEntry = currentTable[j]) != null) { - nextTableIndex = j - 1; - return; - } - } - } - } - } - - public boolean hasNext() { - while (nextEntry != null) { - if (nextEntry.key() != null) { - return true; - } - advance(); - } - - return false; - } - - HashEntry nextEntry() { - do { - if (nextEntry == null) { - throw new NoSuchElementException(); - } - - lastReturned = nextEntry; - currentKey = lastReturned.key(); - advance(); - } while (currentKey == null); // Skip GC'd keys - - return lastReturned; - } - - public void remove() { - if (lastReturned == null) { - throw new IllegalStateException(); - } - ConcurrentIdentityWeakKeyHashMap.this.remove(currentKey); - lastReturned = null; - } - } - - final class KeyIterator - extends HashIterator implements ReusableIterator, Enumeration { - - @Override - public K next() { - return super.nextEntry().key(); - } - - @Override - public K nextElement() { - return super.nextEntry().key(); - } - } - - final class ValueIterator - extends HashIterator implements ReusableIterator, Enumeration { - - @Override - public V next() { - return super.nextEntry().value(); - } - - @Override - public V nextElement() { - return super.nextEntry().value(); - } - } - - /* - * This class is needed for JDK5 compatibility. - */ - static class SimpleEntry implements Entry { - - private final K key; - - private V value; - - public SimpleEntry(K key, V value) { - this.key = key; - this.value = value; - - } - - public SimpleEntry(Entry entry) { - this.key = entry.getKey(); - this.value = entry.getValue(); - - } - - @Override - public K getKey() { - return key; - } - - @Override - public V getValue() { - return value; - } - - @Override - public V setValue(V value) { - V oldValue = this.value; - this.value = value; - return oldValue; - } - - @Override - public boolean equals(Object o) { - if (!(o instanceof Map.Entry)) { - return false; - } - @SuppressWarnings("rawtypes") - Map.Entry e = (Map.Entry) o; - return eq(key, e.getKey()) && eq(value, e.getValue()); - } - - @Override - public int hashCode() { - return (key == null? 0 : key.hashCode()) ^ (value == null? 0 : value.hashCode()); - } - - @Override - public String toString() { - return key + "=" + value; - } - - private static boolean eq(Object o1, Object o2) { - return o1 == null? o2 == null : o1.equals(o2); - } - } - - /** - * Custom Entry class used by EntryIterator.next(), that relays setValue - * changes to the underlying map. - */ - final class WriteThroughEntry extends SimpleEntry { - - WriteThroughEntry(K k, V v) { - super(k, v); - } - - /** - * Set our entry's value and write through to the map. The value to - * return is somewhat arbitrary here. Since a WriteThroughEntry does not - * necessarily track asynchronous changes, the most recent "previous" - * value could be different from what we return (or could even have been - * removed in which case the put will re-establish). We do not and can - * not guarantee more. - */ - @Override - public V setValue(V value) { - - if (value == null) { - throw new NullPointerException(); - } - V v = super.setValue(value); - ConcurrentIdentityWeakKeyHashMap.this.put(getKey(), value); - return v; - } - - } - - final class EntryIterator extends HashIterator implements - ReusableIterator> { - @Override - public Map.Entry next() { - HashEntry e = super.nextEntry(); - return new WriteThroughEntry(e.key(), e.value()); - } - } - - final class KeySet extends AbstractSet { - @Override - public Iterator iterator() { - - return new KeyIterator(); - } - - @Override - public int size() { - return ConcurrentIdentityWeakKeyHashMap.this.size(); - } - - @Override - public boolean isEmpty() { - return ConcurrentIdentityWeakKeyHashMap.this.isEmpty(); - } - - @Override - public boolean contains(Object o) { - return ConcurrentIdentityWeakKeyHashMap.this.containsKey(o); - } - - @Override - public boolean remove(Object o) { - return ConcurrentIdentityWeakKeyHashMap.this.remove(o) != null; - - } - - @Override - public void clear() { - ConcurrentIdentityWeakKeyHashMap.this.clear(); - } - } - - final class Values extends AbstractCollection { - @Override - public Iterator iterator() { - return new ValueIterator(); - } - - @Override - public int size() { - return ConcurrentIdentityWeakKeyHashMap.this.size(); - } - - @Override - public boolean isEmpty() { - return ConcurrentIdentityWeakKeyHashMap.this.isEmpty(); - } - - @Override - public boolean contains(Object o) { - return ConcurrentIdentityWeakKeyHashMap.this.containsValue(o); - } - - @Override - public void clear() { - ConcurrentIdentityWeakKeyHashMap.this.clear(); - } - } - - final class EntrySet extends AbstractSet> { - @Override - public Iterator> iterator() { - return new EntryIterator(); - } - - @Override - public boolean contains(Object o) { - if (!(o instanceof Map.Entry)) { - return false; - } - Map.Entry e = (Map.Entry) o; - V v = ConcurrentIdentityWeakKeyHashMap.this.get(e.getKey()); - return v != null && v.equals(e.getValue()); - } - - @Override - public boolean remove(Object o) { - if (!(o instanceof Map.Entry)) { - return false; - } - Map.Entry e = (Map.Entry) o; - return ConcurrentIdentityWeakKeyHashMap.this.remove(e.getKey(), e.getValue()); - } - - @Override - public int size() { - return ConcurrentIdentityWeakKeyHashMap.this.size(); - } - - @Override - public boolean isEmpty() { - return ConcurrentIdentityWeakKeyHashMap.this.isEmpty(); - } - - @Override - public void clear() { - ConcurrentIdentityWeakKeyHashMap.this.clear(); - } - } -} diff --git a/common/src/main/java/io/netty/util/internal/ReusableIterator.java b/common/src/main/java/io/netty/util/internal/ReusableIterator.java deleted file mode 100644 index ad36b8fd6e..0000000000 --- a/common/src/main/java/io/netty/util/internal/ReusableIterator.java +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.util.internal; - -import java.util.Iterator; - -public interface ReusableIterator extends Iterator { - void rewind(); -} From 9a90b947db97e30f4af319e6ec2326c597e08e1f Mon Sep 17 00:00:00 2001 From: Trustin Lee Date: Sat, 7 Jul 2012 14:22:12 +0900 Subject: [PATCH 87/87] Fix a build problem with all-in-one module - mvn test was failing --- all/pom.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/all/pom.xml b/all/pom.xml index a1d12565aa..76cb252ff1 100644 --- a/all/pom.xml +++ b/all/pom.xml @@ -109,7 +109,7 @@ unpack-sources - generate-resources + prepare-package unpack-dependencies @@ -122,7 +122,7 @@ unpack-jars - generate-resources + prepare-package unpack-dependencies @@ -141,7 +141,7 @@ add-source - generate-sources + prepare-package add-source