diff --git a/buffer/src/main/java/io/netty/buffer/ChannelBuffers.java b/buffer/src/main/java/io/netty/buffer/ChannelBuffers.java index 5f540b0a8f..299114539f 100644 --- a/buffer/src/main/java/io/netty/buffer/ChannelBuffers.java +++ b/buffer/src/main/java/io/netty/buffer/ChannelBuffers.java @@ -821,6 +821,81 @@ public final class ChannelBuffers { return new ReadOnlyChannelBuffer(buffer); } + /** + * Create a {@link ChannelBuffer} that holds all the given values as int's + * + */ + public static ChannelBuffer wrapInt(int... values) { + if (values == null || values.length == 0) { + return EMPTY_BUFFER; + } + ChannelBuffer buffer = buffer(values.length * 4); + for (int v: values) { + buffer.writeInt(v); + } + return buffer; + } + + /** + * Create a {@link ChannelBuffer} that holds all the given values as short's + * + */ + public static ChannelBuffer wrapShort(int... values) { + if (values == null || values.length == 0) { + return EMPTY_BUFFER; + } + ChannelBuffer buffer = buffer(values.length * 2); + for (int v: values) { + buffer.writeShort(v); + } + return buffer; + } + + /** + * Create a {@link ChannelBuffer} that holds all the given values as medium's + * + */ + public static ChannelBuffer wrapMedium(int... values) { + if (values == null || values.length == 0) { + return EMPTY_BUFFER; + } + ChannelBuffer buffer = buffer(values.length * 3); + for (int v: values) { + buffer.writeMedium(v); + } + return buffer; + } + + /** + * Create a {@link ChannelBuffer} that holds all the given values as long's + * + */ + public static ChannelBuffer wrapLong(long... values) { + if (values == null || values.length == 0) { + return EMPTY_BUFFER; + } + ChannelBuffer buffer = buffer(values.length * 8); + for (long v: values) { + buffer.writeLong(v); + } + return buffer; + } + + /** + * Create a {@link ChannelBuffer} that holds all the given values as boolean's + * + */ + public static ChannelBuffer wrapBoolean(boolean... values) { + if (values == null || values.length == 0) { + return EMPTY_BUFFER; + } + ChannelBuffer buffer = buffer(values.length); + for (boolean v: values) { + buffer.writeBoolean(v); + } + return buffer; + } + /** * Returns a hex dump * of the specified buffer's readable bytes. diff --git a/buffer/src/test/java/io/netty/buffer/ChannelBuffersTest.java b/buffer/src/test/java/io/netty/buffer/ChannelBuffersTest.java index b0b4280c66..0d0511ff56 100644 --- a/buffer/src/test/java/io/netty/buffer/ChannelBuffersTest.java +++ b/buffer/src/test/java/io/netty/buffer/ChannelBuffersTest.java @@ -425,4 +425,70 @@ public class ChannelBuffersTest { // Expected } } + + @Test + public void testWrapInt() { + ChannelBuffer buffer = ChannelBuffers.wrapInt(1,4); + assertEquals(8, buffer.capacity()); + assertEquals(1, buffer.readInt()); + assertEquals(4, buffer.readInt()); + assertFalse(buffer.readable()); + + assertEquals(0, ChannelBuffers.wrapInt(null).capacity()); + assertEquals(0, ChannelBuffers.wrapInt(new int[0]).capacity()); + + } + + @Test + public void testWrapShort() { + ChannelBuffer buffer = ChannelBuffers.wrapShort(1,4); + assertEquals(4, buffer.capacity()); + assertEquals(1, buffer.readShort()); + assertEquals(4, buffer.readShort()); + assertFalse(buffer.readable()); + + assertEquals(0, ChannelBuffers.wrapShort(null).capacity()); + assertEquals(0, ChannelBuffers.wrapShort(new int[0]).capacity()); + + } + + @Test + public void testWrapMedium() { + ChannelBuffer buffer = ChannelBuffers.wrapMedium(1,4); + assertEquals(6, buffer.capacity()); + assertEquals(1, buffer.readMedium()); + assertEquals(4, buffer.readMedium()); + assertFalse(buffer.readable()); + + assertEquals(0, ChannelBuffers.wrapMedium(null).capacity()); + assertEquals(0, ChannelBuffers.wrapMedium(new int[0]).capacity()); + + } + + + @Test + public void testWrapLong() { + ChannelBuffer buffer = ChannelBuffers.wrapLong(1,4); + assertEquals(16, buffer.capacity()); + assertEquals(1, buffer.readLong()); + assertEquals(4, buffer.readLong()); + assertFalse(buffer.readable()); + + assertEquals(0, ChannelBuffers.wrapLong(null).capacity()); + assertEquals(0, ChannelBuffers.wrapLong(new long[0]).capacity()); + + } + + @Test + public void testWrapBoolean() { + ChannelBuffer buffer = ChannelBuffers.wrapBoolean(true, false); + assertEquals(2, buffer.capacity()); + assertEquals(true, buffer.readBoolean()); + assertEquals(false, buffer.readBoolean()); + assertFalse(buffer.readable()); + + assertEquals(0, ChannelBuffers.wrapBoolean(null).capacity()); + assertEquals(0, ChannelBuffers.wrapBoolean(new boolean[0]).capacity()); + + } } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker.java index 18ad9ec051..246db85bda 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker.java @@ -16,17 +16,11 @@ package io.netty.handler.codec.http.websocketx; import java.net.URI; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; import java.util.Map; -import io.netty.buffer.ChannelBuffer; -import io.netty.buffer.ChannelBuffers; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; -import io.netty.handler.codec.base64.Base64; import io.netty.handler.codec.http.HttpResponse; -import io.netty.util.CharsetUtil; /** * Base class for web socket client handshake implementations @@ -111,7 +105,7 @@ public abstract class WebSocketClientHandshaker { /** * Begins the opening handshake - * + * * @param channel * Channel */ @@ -119,7 +113,7 @@ public abstract class WebSocketClientHandshaker { /** * Validates and finishes the opening handshake initiated by {@link #handshake}}. - * + * * @param channel * Channel * @param response 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 5cbe843429..e22561bc88 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 @@ -15,18 +15,12 @@ */ package io.netty.handler.codec.http.websocketx; -import java.security.MessageDigest; -import java.security.NoSuchAlgorithmException; import java.util.LinkedHashSet; import java.util.Set; -import io.netty.buffer.ChannelBuffer; -import io.netty.buffer.ChannelBuffers; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; -import io.netty.handler.codec.base64.Base64; import io.netty.handler.codec.http.HttpRequest; -import io.netty.util.CharsetUtil; /** * Base class for server side web socket opening and closing handshakes @@ -41,7 +35,7 @@ public abstract class WebSocketServerHandshaker { /** * Constructor specifying the destination web socket location - * + * * @param version * the protocol version * @param webSocketUrl @@ -92,7 +86,7 @@ public abstract class WebSocketServerHandshaker { /** * Performs the opening handshake - * + * * @param channel * Channel * @param req @@ -102,7 +96,7 @@ public abstract class WebSocketServerHandshaker { /** * Performs the closing handshake - * + * * @param channel * Channel * @param frame @@ -112,7 +106,7 @@ public abstract class WebSocketServerHandshaker { /** * Selects the first matching supported sub protocol - * + * * @param requestedSubprotocols * CSV of protocols to be supported. e.g. "chat, superchat" * @return First matching supported sub protocol. Null if not found. diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdyDataFrame.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdyDataFrame.java index 8d2bfa2070..fc1c203a6c 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdyDataFrame.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdyDataFrame.java @@ -13,21 +13,6 @@ * License for the specific language governing permissions and limitations * under the License. */ -/* - * Copyright 2012 Twitter, Inc. - * - * Licensed 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.spdy; import io.netty.buffer.ChannelBuffer; diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdyGoAwayFrame.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdyGoAwayFrame.java index 386eb7f750..1185e2f929 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdyGoAwayFrame.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdyGoAwayFrame.java @@ -13,21 +13,6 @@ * License for the specific language governing permissions and limitations * under the License. */ -/* - * Copyright 2012 Twitter, Inc. - * - * Licensed 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.spdy; import io.netty.util.internal.StringUtil; diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdyHeaderBlock.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdyHeaderBlock.java index 4049ef94a6..4d4fd20e82 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdyHeaderBlock.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdyHeaderBlock.java @@ -13,21 +13,6 @@ * License for the specific language governing permissions and limitations * under the License. */ -/* - * Copyright 2012 Twitter, Inc. - * - * Licensed 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.spdy; import java.util.List; diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdyHeadersFrame.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdyHeadersFrame.java index f0e8bc9030..c779f9baf2 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdyHeadersFrame.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdyHeadersFrame.java @@ -13,21 +13,6 @@ * License for the specific language governing permissions and limitations * under the License. */ -/* - * Copyright 2012 Twitter, Inc. - * - * Licensed 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.spdy; import io.netty.util.internal.StringUtil; diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdyNoOpFrame.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdyNoOpFrame.java index a976baf304..e3c3e02d3a 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdyNoOpFrame.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdyNoOpFrame.java @@ -13,21 +13,6 @@ * License for the specific language governing permissions and limitations * under the License. */ -/* - * Copyright 2012 Twitter, Inc. - * - * Licensed 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.spdy; /** diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdyPingFrame.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdyPingFrame.java index ae0119dd68..33c6ffb6ce 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdyPingFrame.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdyPingFrame.java @@ -13,21 +13,6 @@ * License for the specific language governing permissions and limitations * under the License. */ -/* - * Copyright 2012 Twitter, Inc. - * - * Licensed 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.spdy; import io.netty.util.internal.StringUtil; diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdyRstStreamFrame.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdyRstStreamFrame.java index 3273eed153..f49c8595b6 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdyRstStreamFrame.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdyRstStreamFrame.java @@ -13,21 +13,6 @@ * License for the specific language governing permissions and limitations * under the License. */ -/* - * Copyright 2012 Twitter, Inc. - * - * Licensed 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.spdy; import io.netty.util.internal.StringUtil; diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdySettingsFrame.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdySettingsFrame.java index 1cb20818e4..0149e508a9 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdySettingsFrame.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdySettingsFrame.java @@ -13,21 +13,6 @@ * License for the specific language governing permissions and limitations * under the License. */ -/* - * Copyright 2012 Twitter, Inc. - * - * Licensed 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.spdy; import java.util.Map; diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdySynReplyFrame.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdySynReplyFrame.java index dba75a7f37..5b033a1617 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdySynReplyFrame.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdySynReplyFrame.java @@ -13,21 +13,6 @@ * License for the specific language governing permissions and limitations * under the License. */ -/* - * Copyright 2012 Twitter, Inc. - * - * Licensed 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.spdy; import io.netty.util.internal.StringUtil; diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdySynStreamFrame.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdySynStreamFrame.java index 95d1d9fe1a..548d8a7e72 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdySynStreamFrame.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/DefaultSpdySynStreamFrame.java @@ -13,21 +13,6 @@ * License for the specific language governing permissions and limitations * under the License. */ -/* - * Copyright 2012 Twitter, Inc. - * - * Licensed 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.spdy; import io.netty.util.internal.StringUtil; diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyCodecUtil.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyCodecUtil.java index 38adfcbd1e..bafe9a660a 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyCodecUtil.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyCodecUtil.java @@ -13,21 +13,6 @@ * License for the specific language governing permissions and limitations * under the License. */ -/* - * Copyright 2012 Twitter, Inc. - * - * Licensed 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.spdy; import io.netty.buffer.ChannelBuffer; diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyDataFrame.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyDataFrame.java index 9754154786..27e4123e79 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyDataFrame.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyDataFrame.java @@ -13,21 +13,6 @@ * License for the specific language governing permissions and limitations * under the License. */ -/* - * Copyright 2012 Twitter, Inc. - * - * Licensed 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.spdy; import io.netty.buffer.ChannelBuffer; diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyFrameCodec.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyFrameCodec.java index 5d47322ae5..8fe63d66c2 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyFrameCodec.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyFrameCodec.java @@ -13,21 +13,6 @@ * License for the specific language governing permissions and limitations * under the License. */ -/* - * Copyright 2012 Twitter, Inc. - * - * Licensed 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.spdy; import io.netty.channel.ChannelDownstreamHandler; diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyFrameDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyFrameDecoder.java index 6758e395a9..5d0c5140e9 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyFrameDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyFrameDecoder.java @@ -13,28 +13,12 @@ * License for the specific language governing permissions and limitations * under the License. */ -/* - * Copyright 2012 Twitter, Inc. - * - * Licensed 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.spdy; import io.netty.buffer.ChannelBuffer; +import io.netty.buffer.ChannelBuffers; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.compression.ZlibDecoder; -import io.netty.handler.codec.embedder.DecoderEmbedder; import io.netty.handler.codec.frame.FrameDecoder; import static io.netty.handler.codec.spdy.SpdyCodecUtil.*; @@ -48,8 +32,8 @@ public class SpdyFrameDecoder extends FrameDecoder { private final int maxFrameSize; private final int maxHeaderSize; - private final DecoderEmbedder headerBlockDecompressor = - new DecoderEmbedder(new ZlibDecoder(SPDY_DICT)); + private final SpdyHeaderBlockDecompressor headerBlockDecompressor = + SpdyHeaderBlockDecompressor.newInstance(); /** * Creates a new instance with the default {@code maxChunkSize (8192)}, @@ -129,7 +113,9 @@ public class SpdyFrameDecoder extends FrameDecoder { int type = getUnsignedShort(buffer, typeOffset); buffer.skipBytes(SPDY_HEADER_SIZE); - return decodeControlFrame(type, flags, buffer.readBytes(dataLength)); + int readerIndex = buffer.readerIndex(); + buffer.skipBytes(dataLength); + return decodeControlFrame(type, flags, buffer.slice(readerIndex, dataLength)); } else { // Decode data frame common header int streamID = getUnsignedInt(buffer, frameOffset); @@ -181,7 +167,7 @@ public class SpdyFrameDecoder extends FrameDecoder { spdySynStreamFrame.setLast(last); spdySynStreamFrame.setUnidirectional(unid); - decodeHeaderBlock(spdySynStreamFrame, decompress(data)); + decodeHeaderBlock(spdySynStreamFrame, data); return spdySynStreamFrame; @@ -199,7 +185,7 @@ public class SpdyFrameDecoder extends FrameDecoder { last = (flags & SPDY_FLAG_FIN) != 0; spdySynReplyFrame.setLast(last); - decodeHeaderBlock(spdySynReplyFrame, decompress(data)); + decodeHeaderBlock(spdySynReplyFrame, data); return spdySynReplyFrame; @@ -299,7 +285,7 @@ public class SpdyFrameDecoder extends FrameDecoder { SpdyHeadersFrame spdyHeadersFrame = new DefaultSpdyHeadersFrame(streamID); - decodeHeaderBlock(spdyHeadersFrame, decompress(data)); + decodeHeaderBlock(spdyHeadersFrame, data); return spdyHeadersFrame; @@ -311,31 +297,38 @@ public class SpdyFrameDecoder extends FrameDecoder { } } - private ChannelBuffer decompress(ChannelBuffer compressed) throws Exception { - if ((compressed.readableBytes() == 2) && - (compressed.getShort(compressed.readerIndex()) == 0)) { - return compressed; + private boolean ensureBytes(ChannelBuffer decompressed, int bytes) throws Exception { + if (decompressed.readableBytes() >= bytes) { + return true; } - headerBlockDecompressor.offer(compressed); - return headerBlockDecompressor.poll(); + decompressed.discardReadBytes(); + headerBlockDecompressor.decode(decompressed); + return decompressed.readableBytes() >= bytes; } private void decodeHeaderBlock(SpdyHeaderBlock headerFrame, ChannelBuffer headerBlock) throws Exception { - if (headerBlock.readableBytes() < 2) { + if ((headerBlock.readableBytes() == 2) && + (headerBlock.getShort(headerBlock.readerIndex()) == 0)) { + return; + } + + headerBlockDecompressor.setInput(headerBlock); + ChannelBuffer decompressed = ChannelBuffers.dynamicBuffer(8192); + headerBlockDecompressor.decode(decompressed); + + if (decompressed.readableBytes() < 2) { throw new SpdyProtocolException( "Received invalid header block"); } int headerSize = 0; - int numEntries = getUnsignedShort(headerBlock, headerBlock.readerIndex()); - headerBlock.skipBytes(2); + int numEntries = decompressed.readUnsignedShort(); for (int i = 0; i < numEntries; i ++) { - if (headerBlock.readableBytes() < 2) { + if (!ensureBytes(decompressed, 2)) { throw new SpdyProtocolException( "Received invalid header block"); } - int nameLength = getUnsignedShort(headerBlock, headerBlock.readerIndex()); - headerBlock.skipBytes(2); + int nameLength = decompressed.readUnsignedShort(); if (nameLength == 0) { headerFrame.setInvalid(); return; @@ -345,23 +338,22 @@ public class SpdyFrameDecoder extends FrameDecoder { throw new SpdyProtocolException( "Header block exceeds " + maxHeaderSize); } - if (headerBlock.readableBytes() < nameLength) { + if (!ensureBytes(decompressed, nameLength)) { throw new SpdyProtocolException( "Received invalid header block"); } byte[] nameBytes = new byte[nameLength]; - headerBlock.readBytes(nameBytes); + decompressed.readBytes(nameBytes); String name = new String(nameBytes, "UTF-8"); if (headerFrame.containsHeader(name)) { throw new SpdyProtocolException( "Received duplicate header name: " + name); } - if (headerBlock.readableBytes() < 2) { + if (!ensureBytes(decompressed, 2)) { throw new SpdyProtocolException( "Received invalid header block"); } - int valueLength = getUnsignedShort(headerBlock, headerBlock.readerIndex()); - headerBlock.skipBytes(2); + int valueLength = decompressed.readUnsignedShort(); if (valueLength == 0) { headerFrame.setInvalid(); return; @@ -371,15 +363,15 @@ public class SpdyFrameDecoder extends FrameDecoder { throw new SpdyProtocolException( "Header block exceeds " + maxHeaderSize); } - if (headerBlock.readableBytes() < valueLength) { + if (!ensureBytes(decompressed, valueLength)) { throw new SpdyProtocolException( "Received invalid header block"); } byte[] valueBytes = new byte[valueLength]; - headerBlock.readBytes(valueBytes); + decompressed.readBytes(valueBytes); int index = 0; int offset = 0; - while (index < valueBytes.length) { + while (index < valueLength) { while (index < valueBytes.length && valueBytes[index] != (byte) 0) { index ++; } diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyFrameEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyFrameEncoder.java index ea9f2e469b..878dacf405 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyFrameEncoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyFrameEncoder.java @@ -13,42 +13,28 @@ * License for the specific language governing permissions and limitations * under the License. */ -/* - * Copyright 2012 Twitter, Inc. - * - * Licensed 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.spdy; +import static io.netty.handler.codec.spdy.SpdyCodecUtil.*; + import java.nio.ByteOrder; import java.util.Set; import io.netty.buffer.ChannelBuffer; import io.netty.buffer.ChannelBuffers; import io.netty.channel.Channel; +import io.netty.channel.ChannelEvent; import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.compression.ZlibEncoder; -import io.netty.handler.codec.embedder.EncoderEmbedder; +import io.netty.channel.ChannelStateEvent; import io.netty.handler.codec.oneone.OneToOneEncoder; -import static io.netty.handler.codec.spdy.SpdyCodecUtil.*; - /** * Encodes a SPDY Data or Control Frame into a {@link ChannelBuffer}. */ public class SpdyFrameEncoder extends OneToOneEncoder { - private final EncoderEmbedder headerBlockCompressor; + private volatile boolean finished; + private final SpdyHeaderBlockCompressor headerBlockCompressor; /** * Creates a new instance with the default {@code compressionLevel (6)}, @@ -63,8 +49,27 @@ public class SpdyFrameEncoder extends OneToOneEncoder { */ public SpdyFrameEncoder(int compressionLevel, int windowBits, int memLevel) { super(); - headerBlockCompressor = new EncoderEmbedder( - new ZlibEncoder(compressionLevel, windowBits, memLevel, SPDY_DICT)); + headerBlockCompressor = SpdyHeaderBlockCompressor.newInstance(compressionLevel, windowBits, memLevel); + } + + @Override + public void handleDownstream( + ChannelHandlerContext ctx, ChannelEvent evt) throws Exception { + if (evt instanceof ChannelStateEvent) { + ChannelStateEvent e = (ChannelStateEvent) evt; + switch (e.getState()) { + case OPEN: + case CONNECTED: + case BOUND: + if (Boolean.FALSE.equals(e.getValue()) || e.getValue() == null) { + synchronized (headerBlockCompressor) { + finished = true; + headerBlockCompressor.end(); + } + } + } + } + super.handleDownstream(ctx, evt); } @Override @@ -278,7 +283,13 @@ public class SpdyFrameEncoder extends OneToOneEncoder { if (uncompressed.readableBytes() == 0) { return ChannelBuffers.EMPTY_BUFFER; } - headerBlockCompressor.offer(uncompressed); - return headerBlockCompressor.poll(); + ChannelBuffer compressed = ChannelBuffers.dynamicBuffer(); + synchronized (headerBlockCompressor) { + if (!finished) { + headerBlockCompressor.setInput(uncompressed); + headerBlockCompressor.encode(compressed); + } + } + return compressed; } } diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyGoAwayFrame.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyGoAwayFrame.java index c4c7fb201d..890e464406 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyGoAwayFrame.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyGoAwayFrame.java @@ -13,21 +13,6 @@ * License for the specific language governing permissions and limitations * under the License. */ -/* - * Copyright 2012 Twitter, Inc. - * - * Licensed 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.spdy; /** diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHeaderBlock.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHeaderBlock.java index a8d0486fed..5590ceaaf8 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHeaderBlock.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHeaderBlock.java @@ -13,21 +13,6 @@ * License for the specific language governing permissions and limitations * under the License. */ -/* - * Copyright 2012 Twitter, Inc. - * - * Licensed 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.spdy; import java.util.List; diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHeaderBlockCompressor.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHeaderBlockCompressor.java new file mode 100644 index 0000000000..d52e7e17d9 --- /dev/null +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHeaderBlockCompressor.java @@ -0,0 +1,54 @@ +/* + * 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.spdy; + +import java.util.zip.Deflater; + +import io.netty.buffer.ChannelBuffer; + +abstract class SpdyHeaderBlockCompressor { + + private static final boolean USE_ZLIB; + + static { + boolean java7 = false; + try { + Deflater.class.getDeclaredMethod( + "deflate", + new Class[] { byte[].class, int.class, int.class, int.class }); + java7 = true; + } catch (Exception e) { + // Ignore + } + + USE_ZLIB = java7; + } + + static SpdyHeaderBlockCompressor newInstance( + int compressionLevel, int windowBits, int memLevel) { + + if (USE_ZLIB) { + return new SpdyHeaderBlockZlibCompressor(compressionLevel); + } else { + return new SpdyHeaderBlockJZlibCompressor( + compressionLevel, windowBits, memLevel); + } + } + + abstract void setInput(ChannelBuffer decompressed); + abstract void encode(ChannelBuffer compressed); + abstract void end(); +} diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHeaderBlockDecompressor.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHeaderBlockDecompressor.java new file mode 100644 index 0000000000..119ef46454 --- /dev/null +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHeaderBlockDecompressor.java @@ -0,0 +1,28 @@ +/* + * 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.spdy; + +import io.netty.buffer.ChannelBuffer; + +abstract class SpdyHeaderBlockDecompressor { + + static SpdyHeaderBlockDecompressor newInstance() { + return new SpdyHeaderBlockJZlibDecompressor(); + } + + abstract void setInput(ChannelBuffer compressed); + abstract void decode(ChannelBuffer decompressed); +} diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHeaderBlockJZlibCompressor.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHeaderBlockJZlibCompressor.java new file mode 100644 index 0000000000..dc6e7db1ff --- /dev/null +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHeaderBlockJZlibCompressor.java @@ -0,0 +1,98 @@ +/* + * 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.spdy; + +import static io.netty.handler.codec.spdy.SpdyCodecUtil.*; + +import io.netty.buffer.ChannelBuffer; +import io.netty.handler.codec.compression.CompressionException; +import io.netty.util.internal.jzlib.JZlib; +import io.netty.util.internal.jzlib.ZStream; + +class SpdyHeaderBlockJZlibCompressor extends SpdyHeaderBlockCompressor { + + private final ZStream z = new ZStream(); + + public SpdyHeaderBlockJZlibCompressor(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)"); + } + + int resultCode = z.deflateInit( + compressionLevel, windowBits, memLevel, JZlib.W_ZLIB); + if (resultCode != JZlib.Z_OK) { + throw new CompressionException( + "failed to initialize an SPDY header block deflater: " + resultCode); + } else { + resultCode = z.deflateSetDictionary(SPDY_DICT, SPDY_DICT.length); + if (resultCode != JZlib.Z_OK) { + throw new CompressionException( + "failed to set the SPDY dictionary: " + resultCode); + } + } + } + + @Override + public void setInput(ChannelBuffer decompressed) { + byte[] in = new byte[decompressed.readableBytes()]; + decompressed.readBytes(in); + z.next_in = in; + z.next_in_index = 0; + z.avail_in = in.length; + } + + @Override + public void encode(ChannelBuffer compressed) { + try { + byte[] out = new byte[(int) Math.ceil(z.next_in.length * 1.001) + 12]; + z.next_out = out; + z.next_out_index = 0; + z.avail_out = out.length; + + int resultCode = z.deflate(JZlib.Z_SYNC_FLUSH); + if (resultCode != JZlib.Z_OK) { + throw new CompressionException("compression failure: " + resultCode); + } + + if (z.next_out_index != 0) { + compressed.writeBytes(out, 0, z.next_out_index); + } + } 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 end() { + z.deflateEnd(); + z.next_in = null; + z.next_out = null; + } +} diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHeaderBlockJZlibDecompressor.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHeaderBlockJZlibDecompressor.java new file mode 100644 index 0000000000..21593aba40 --- /dev/null +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHeaderBlockJZlibDecompressor.java @@ -0,0 +1,67 @@ +/* + * 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.spdy; + +import io.netty.buffer.ChannelBuffer; +import io.netty.handler.codec.compression.CompressionException; +import io.netty.util.internal.jzlib.JZlib; +import io.netty.util.internal.jzlib.ZStream; + +import static io.netty.handler.codec.spdy.SpdyCodecUtil.*; + +class SpdyHeaderBlockJZlibDecompressor extends SpdyHeaderBlockDecompressor { + + private final byte[] out = new byte[8192]; + private final ZStream z = new ZStream(); + + public SpdyHeaderBlockJZlibDecompressor() { + int resultCode; + resultCode = z.inflateInit(JZlib.W_ZLIB); + if (resultCode != JZlib.Z_OK) { + throw new CompressionException("ZStream initialization failure"); + } + z.next_out = out; + } + + @Override + public void setInput(ChannelBuffer compressed) { + byte[] in = new byte[compressed.readableBytes()]; + compressed.readBytes(in); + z.next_in = in; + z.next_in_index = 0; + z.avail_in = in.length; + } + + @Override + public void decode(ChannelBuffer decompressed) { + z.next_out_index = 0; + z.avail_out = out.length; + + int resultCode = z.inflate(JZlib.Z_SYNC_FLUSH); + + if (resultCode == JZlib.Z_NEED_DICT) { + resultCode = z.inflateSetDictionary(SPDY_DICT, SPDY_DICT.length); + if (resultCode != JZlib.Z_OK) { + throw new CompressionException("ZStream dictionary failure"); + } + z.inflate(JZlib.Z_SYNC_FLUSH); + } + + if (z.next_out_index > 0) { + decompressed.writeBytes(out, 0, z.next_out_index); + } + } +} diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHeaderBlockZlibCompressor.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHeaderBlockZlibCompressor.java new file mode 100644 index 0000000000..d00434b120 --- /dev/null +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHeaderBlockZlibCompressor.java @@ -0,0 +1,57 @@ +/* + * 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.spdy; + +import java.util.zip.Deflater; + +import io.netty.buffer.ChannelBuffer; + +import static io.netty.handler.codec.spdy.SpdyCodecUtil.*; + +class SpdyHeaderBlockZlibCompressor extends SpdyHeaderBlockCompressor { + + private final byte[] out = new byte[8192]; + private final Deflater compressor; + + public SpdyHeaderBlockZlibCompressor(int compressionLevel) { + if (compressionLevel < 0 || compressionLevel > 9) { + throw new IllegalArgumentException( + "compressionLevel: " + compressionLevel + " (expected: 0-9)"); + } + compressor = new Deflater(compressionLevel); + compressor.setDictionary(SPDY_DICT); + } + + @Override + public void setInput(ChannelBuffer decompressed) { + byte[] in = new byte[decompressed.readableBytes()]; + decompressed.readBytes(in); + compressor.setInput(in); + } + + @Override + public void encode(ChannelBuffer compressed) { + while (!compressor.needsInput()) { + int numBytes = compressor.deflate(out, 0, out.length, Deflater.SYNC_FLUSH); + compressed.writeBytes(out, 0, numBytes); + } + } + + @Override + public void end() { + compressor.end(); + } +} diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHeaders.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHeaders.java index ec8adea0df..6802520a87 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHeaders.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHeaders.java @@ -13,21 +13,6 @@ * License for the specific language governing permissions and limitations * under the License. */ -/* - * Copyright 2012 Twitter, Inc. - * - * Licensed 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.spdy; import java.util.LinkedList; diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHeadersFrame.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHeadersFrame.java index ecfbf6017c..4d1dac9d11 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHeadersFrame.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHeadersFrame.java @@ -13,21 +13,6 @@ * License for the specific language governing permissions and limitations * under the License. */ -/* - * Copyright 2012 Twitter, Inc. - * - * Licensed 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.spdy; /** diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpCodec.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpCodec.java index 75bfcdceb4..1e56429002 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpCodec.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpCodec.java @@ -13,21 +13,6 @@ * License for the specific language governing permissions and limitations * under the License. */ -/* - * Copyright 2012 Twitter, Inc. - * - * Licensed 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.spdy; import io.netty.channel.ChannelDownstreamHandler; diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpDecoder.java index fdd1f49e4d..a16ab9fccd 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpDecoder.java @@ -13,21 +13,6 @@ * License for the specific language governing permissions and limitations * under the License. */ -/* - * Copyright 2012 Twitter, Inc. - * - * Licensed 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.spdy; import java.util.HashMap; diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpEncoder.java index 30ce7a6352..f235482990 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpEncoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpEncoder.java @@ -13,21 +13,6 @@ * License for the specific language governing permissions and limitations * under the License. */ -/* - * Copyright 2012 Twitter, Inc. - * - * Licensed 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.spdy; import java.util.List; 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 6b13986af4..c1c45e4e94 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 @@ -13,21 +13,6 @@ * License for the specific language governing permissions and limitations * under the License. */ -/* - * Copyright 2012 Twitter, Inc. - * - * Licensed 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.spdy; import io.netty.handler.codec.http.HttpHeaders; diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyNoOpFrame.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyNoOpFrame.java index 1652344fd7..8b955ecd27 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyNoOpFrame.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyNoOpFrame.java @@ -13,21 +13,6 @@ * License for the specific language governing permissions and limitations * under the License. */ -/* - * Copyright 2012 Twitter, Inc. - * - * Licensed 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.spdy; /** diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyPingFrame.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyPingFrame.java index 48b7bc65ec..0dbfdb978b 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyPingFrame.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyPingFrame.java @@ -13,21 +13,6 @@ * License for the specific language governing permissions and limitations * under the License. */ -/* - * Copyright 2012 Twitter, Inc. - * - * Licensed 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.spdy; /** diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyProtocolException.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyProtocolException.java index 508354fb1d..f373bed840 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyProtocolException.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyProtocolException.java @@ -13,21 +13,6 @@ * License for the specific language governing permissions and limitations * under the License. */ -/* - * Copyright 2012 Twitter, Inc. - * - * Licensed 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.spdy; /** diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyRstStreamFrame.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyRstStreamFrame.java index 0cfc8b6945..6b7eb71964 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyRstStreamFrame.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyRstStreamFrame.java @@ -13,21 +13,6 @@ * License for the specific language governing permissions and limitations * under the License. */ -/* - * Copyright 2012 Twitter, Inc. - * - * Licensed 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.spdy; /** diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdySession.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdySession.java index 7645b65b78..4c68061ca0 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdySession.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdySession.java @@ -13,21 +13,6 @@ * License for the specific language governing permissions and limitations * under the License. */ -/* - * Copyright 2012 Twitter, Inc. - * - * Licensed 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.spdy; import java.util.Map; 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 82e54afe6a..c2a77220d9 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 @@ -13,21 +13,6 @@ * License for the specific language governing permissions and limitations * under the License. */ -/* - * Copyright 2012 Twitter, Inc. - * - * Licensed 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.spdy; import java.net.SocketAddress; diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdySettingsFrame.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdySettingsFrame.java index 39751cfcb9..986db39542 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdySettingsFrame.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdySettingsFrame.java @@ -13,21 +13,6 @@ * License for the specific language governing permissions and limitations * under the License. */ -/* - * Copyright 2012 Twitter, Inc. - * - * Licensed 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.spdy; import java.util.Set; diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyStreamStatus.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyStreamStatus.java index 51272629d8..57689f0098 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyStreamStatus.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyStreamStatus.java @@ -13,21 +13,6 @@ * License for the specific language governing permissions and limitations * under the License. */ -/* - * Copyright 2012 Twitter, Inc. - * - * Licensed 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.spdy; /** diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdySynReplyFrame.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdySynReplyFrame.java index e24bc844d7..a30c1565d3 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdySynReplyFrame.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdySynReplyFrame.java @@ -13,21 +13,6 @@ * License for the specific language governing permissions and limitations * under the License. */ -/* - * Copyright 2012 Twitter, Inc. - * - * Licensed 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.spdy; /** diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdySynStreamFrame.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdySynStreamFrame.java index b8aa7de181..b69bcf3b16 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdySynStreamFrame.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdySynStreamFrame.java @@ -13,21 +13,6 @@ * License for the specific language governing permissions and limitations * under the License. */ -/* - * Copyright 2012 Twitter, Inc. - * - * Licensed 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.spdy; /** diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/package-info.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/package-info.java index 3b97176307..a43006d6bc 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/package-info.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/package-info.java @@ -13,22 +13,6 @@ * License for the specific language governing permissions and limitations * under the License. */ -/* - * Copyright 2012 Twitter, Inc. - * - * Licensed 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. - */ - /** * Encoder, decoder, session handler and their related message types for the SPDY protocol. * diff --git a/codec-http/src/test/java/io/netty/handler/codec/spdy/AbstractSocketSpdyEchoTest.java b/codec-http/src/test/java/io/netty/handler/codec/spdy/AbstractSocketSpdyEchoTest.java index b2881b7b2b..03caefc4f6 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/spdy/AbstractSocketSpdyEchoTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/spdy/AbstractSocketSpdyEchoTest.java @@ -13,21 +13,6 @@ * License for the specific language governing permissions and limitations * under the License. */ -/* - * Copyright 2012 Twitter, Inc. - * - * Licensed 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.spdy; import static org.junit.Assert.*; diff --git a/codec-http/src/test/java/io/netty/handler/codec/spdy/NioNioSocketSpdyEchoTest.java b/codec-http/src/test/java/io/netty/handler/codec/spdy/NioNioSocketSpdyEchoTest.java index db4b2c194e..2ed9995ac9 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/spdy/NioNioSocketSpdyEchoTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/spdy/NioNioSocketSpdyEchoTest.java @@ -13,21 +13,6 @@ * License for the specific language governing permissions and limitations * under the License. */ -/* - * Copyright 2012 Twitter, Inc. - * - * Licensed 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.spdy; import java.util.concurrent.Executor; diff --git a/codec-http/src/test/java/io/netty/handler/codec/spdy/NioOioSocketSpdyEchoTest.java b/codec-http/src/test/java/io/netty/handler/codec/spdy/NioOioSocketSpdyEchoTest.java index c9fa404320..65e2c9c5d3 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/spdy/NioOioSocketSpdyEchoTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/spdy/NioOioSocketSpdyEchoTest.java @@ -13,21 +13,6 @@ * License for the specific language governing permissions and limitations * under the License. */ -/* - * Copyright 2012 Twitter, Inc. - * - * Licensed 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.spdy; import java.util.concurrent.Executor; diff --git a/codec-http/src/test/java/io/netty/handler/codec/spdy/OioNioSocketSpdyEchoTest.java b/codec-http/src/test/java/io/netty/handler/codec/spdy/OioNioSocketSpdyEchoTest.java index eea3409d42..ba628aa325 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/spdy/OioNioSocketSpdyEchoTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/spdy/OioNioSocketSpdyEchoTest.java @@ -13,21 +13,6 @@ * License for the specific language governing permissions and limitations * under the License. */ -/* - * Copyright 2012 Twitter, Inc. - * - * Licensed 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.spdy; import java.util.concurrent.Executor; diff --git a/codec-http/src/test/java/io/netty/handler/codec/spdy/OioOioSocketSpdyEchoTest.java b/codec-http/src/test/java/io/netty/handler/codec/spdy/OioOioSocketSpdyEchoTest.java index 423d6f0ffa..3e89046407 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/spdy/OioOioSocketSpdyEchoTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/spdy/OioOioSocketSpdyEchoTest.java @@ -13,21 +13,6 @@ * License for the specific language governing permissions and limitations * under the License. */ -/* - * Copyright 2012 Twitter, Inc. - * - * Licensed 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.spdy; import java.util.concurrent.Executor; diff --git a/codec-http/src/test/java/io/netty/handler/codec/spdy/SpdySessionHandlerTest.java b/codec-http/src/test/java/io/netty/handler/codec/spdy/SpdySessionHandlerTest.java index 84875e9fcc..83bfdb6f7b 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/spdy/SpdySessionHandlerTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/spdy/SpdySessionHandlerTest.java @@ -13,21 +13,6 @@ * License for the specific language governing permissions and limitations * under the License. */ -/* - * Copyright 2012 Twitter, Inc. - * - * Licensed 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.spdy; import java.util.List; diff --git a/codec/src/main/java/io/netty/handler/codec/embedder/AbstractCodecEmbedder.java b/codec/src/main/java/io/netty/handler/codec/embedder/AbstractCodecEmbedder.java index 4e85a1d01c..2521682ca7 100644 --- a/codec/src/main/java/io/netty/handler/codec/embedder/AbstractCodecEmbedder.java +++ b/codec/src/main/java/io/netty/handler/codec/embedder/AbstractCodecEmbedder.java @@ -22,9 +22,11 @@ import java.util.ConcurrentModificationException; import java.util.LinkedList; import java.util.Queue; +import io.netty.channel.Channels; import io.netty.buffer.ChannelBufferFactory; import io.netty.channel.Channel; import io.netty.channel.ChannelEvent; +import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPipeline; @@ -224,6 +226,16 @@ abstract class AbstractCodecEmbedder implements CodecEmbedder { throw new CodecEmbedderException(actualCause); } + + @Override + public ChannelFuture execute(ChannelPipeline pipeline, Runnable task) { + try { + task.run(); + return Channels.succeededFuture(pipeline.getChannel()); + } catch (Throwable t) { + return Channels.failedFuture(pipeline.getChannel(), t); + } + } } private static final class EmbeddedChannelPipeline extends DefaultChannelPipeline { diff --git a/codec/src/main/java/io/netty/handler/codec/frame/FixedLengthFrameDecoder.java b/codec/src/main/java/io/netty/handler/codec/frame/FixedLengthFrameDecoder.java index 21537efced..57863f3208 100644 --- a/codec/src/main/java/io/netty/handler/codec/frame/FixedLengthFrameDecoder.java +++ b/codec/src/main/java/io/netty/handler/codec/frame/FixedLengthFrameDecoder.java @@ -16,6 +16,7 @@ package io.netty.handler.codec.frame; import io.netty.buffer.ChannelBuffer; +import io.netty.buffer.ChannelBufferFactory; import io.netty.buffer.ChannelBuffers; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; @@ -74,11 +75,13 @@ public class FixedLengthFrameDecoder extends FrameDecoder { } @Override - protected ChannelBuffer createCumulationDynamicBuffer(ChannelHandlerContext ctx) { + protected ChannelBuffer newCumulationBuffer(ChannelHandlerContext ctx, int minimumCapacity) { + ChannelBufferFactory factory = ctx.getChannel().getConfig().getBufferFactory(); if (allocateFullBuffer) { - return ChannelBuffers.dynamicBuffer(frameLength, ctx.getChannel().getConfig().getBufferFactory()); + return ChannelBuffers.dynamicBuffer( + factory.getDefaultOrder(), frameLength, ctx.getChannel().getConfig().getBufferFactory()); } - return super.createCumulationDynamicBuffer(ctx); + return super.newCumulationBuffer(ctx, minimumCapacity); } } diff --git a/codec/src/main/java/io/netty/handler/codec/frame/FrameDecoder.java b/codec/src/main/java/io/netty/handler/codec/frame/FrameDecoder.java index 73c797191b..67c23316e5 100644 --- a/codec/src/main/java/io/netty/handler/codec/frame/FrameDecoder.java +++ b/codec/src/main/java/io/netty/handler/codec/frame/FrameDecoder.java @@ -18,6 +18,7 @@ package io.netty.handler.codec.frame; import java.net.SocketAddress; import io.netty.buffer.ChannelBuffer; +import io.netty.buffer.ChannelBufferFactory; import io.netty.buffer.ChannelBuffers; import io.netty.channel.Channel; import io.netty.channel.ChannelHandler; @@ -206,23 +207,20 @@ public abstract class FrameDecoder extends SimpleChannelUpstreamHandler { callDecode(ctx, e.getChannel(), input, e.getRemoteAddress()); if (input.readable()) { // seems like there is something readable left in the input buffer. So create the cumulation buffer and copy the input into it - ChannelBuffer cumulation = cumulation(ctx); - cumulation.writeBytes(input); + (this.cumulation = newCumulationBuffer(ctx, input.readableBytes())).writeBytes(input); } } else { - ChannelBuffer cumulation = cumulation(ctx); - if (cumulation.readable()) { + ChannelBuffer cumulation = this.cumulation; + assert cumulation.readable(); + if (cumulation.writableBytes() < input.readableBytes()) { cumulation.discardReadBytes(); - cumulation.writeBytes(input); - callDecode(ctx, e.getChannel(), cumulation, e.getRemoteAddress()); - } else { - callDecode(ctx, e.getChannel(), input, e.getRemoteAddress()); - if (input.readable()) { - cumulation.writeBytes(input); - } + } + cumulation.writeBytes(input); + callDecode(ctx, e.getChannel(), cumulation, e.getRemoteAddress()); + if (!cumulation.readable()) { + this.cumulation = null; } } - } @Override @@ -303,10 +301,6 @@ public abstract class FrameDecoder extends SimpleChannelUpstreamHandler { unfoldAndFireMessageReceived(context, remoteAddress, frame); } - - if (!cumulation.readable()) { - this.cumulation = null; - } } private void unfoldAndFireMessageReceived(ChannelHandlerContext context, SocketAddress remoteAddress, Object result) { @@ -333,10 +327,10 @@ public abstract class FrameDecoder extends SimpleChannelUpstreamHandler { ChannelBuffer cumulation = this.cumulation; if (cumulation == null) { return; - } else { - this.cumulation = null; } + this.cumulation = null; + if (cumulation.readable()) { // Make sure all frames are read before notifying a closed channel. callDecode(ctx, ctx.getChannel(), cumulation, null); @@ -355,28 +349,18 @@ public abstract class FrameDecoder extends SimpleChannelUpstreamHandler { } /** - * Get the currently used {@link ChannelBuffer} for cumulation or create one in a lazy fashion if none exist yet - * - * @param ctx the {@link ChannelHandlerContext} for this handler - * @return buffer the {@link ChannelBuffer} which is used for cumulation - */ - private ChannelBuffer cumulation(ChannelHandlerContext ctx) { - ChannelBuffer c = cumulation; - if (c == null) { - c = createCumulationDynamicBuffer(ctx); - cumulation = c; - } - return c; - } - - /** - * Create a new {@link ChannelBuffer} which is used for the cumulation. Be aware that this MUST be a dynamic buffer. Sub-classes may override this to provide a - * dynamic {@link ChannelBuffer} which has some prelocated size that better fit their need. - * + * Create a new {@link ChannelBuffer} which is used for the cumulation. + * Be aware that this MUST be a dynamic buffer. Sub-classes may override + * this to provide a dynamic {@link ChannelBuffer} which has some + * pre-allocated size that better fit their need. + * * @param ctx {@link ChannelHandlerContext} for this handler * @return buffer the {@link ChannelBuffer} which is used for cumulation */ - protected ChannelBuffer createCumulationDynamicBuffer(ChannelHandlerContext ctx) { - return ChannelBuffers.dynamicBuffer(ctx.getChannel().getConfig().getBufferFactory()); + protected ChannelBuffer newCumulationBuffer( + ChannelHandlerContext ctx, int minimumCapacity) { + ChannelBufferFactory factory = ctx.getChannel().getConfig().getBufferFactory(); + return ChannelBuffers.dynamicBuffer( + factory.getDefaultOrder(), Math.max(minimumCapacity, 256), factory); } } diff --git a/codec/src/main/java/io/netty/handler/codec/replay/ReplayingDecoder.java b/codec/src/main/java/io/netty/handler/codec/replay/ReplayingDecoder.java index 058f046625..a2c6b557a5 100644 --- a/codec/src/main/java/io/netty/handler/codec/replay/ReplayingDecoder.java +++ b/codec/src/main/java/io/netty/handler/codec/replay/ReplayingDecoder.java @@ -289,7 +289,6 @@ public abstract class ReplayingDecoder> private ChannelBuffer cumulation; - private boolean needsCleanup; private final boolean unfold; private ReplayingDecoderBuffer replayable; private T state; @@ -430,11 +429,58 @@ public abstract class ReplayingDecoder> return; } - ChannelBuffer cumulation = cumulation(ctx); - needsCleanup = true; - cumulation.discardReadBytes(); - cumulation.writeBytes(input); - callDecode(ctx, e.getChannel(), cumulation, e.getRemoteAddress()); + if (cumulation == null) { + // the cumulation buffer is not created yet so just pass the input + // to callDecode(...) method + this.cumulation = input; + replayable = new ReplayingDecoderBuffer(input); + + int oldReaderIndex = input.readerIndex(); + int inputSize = input.readableBytes(); + callDecode( + ctx, e.getChannel(), + input, replayable, + e.getRemoteAddress()); + + if (input.readable()) { + // seems like there is something readable left in the input buffer + // or decoder wants a replay - create the cumulation buffer and + // copy the input into it + ChannelBuffer cumulation; + if (checkpoint > 0) { + int bytesToPreserve = inputSize - (checkpoint - oldReaderIndex); + cumulation = this.cumulation = + newCumulationBuffer(ctx, bytesToPreserve); + cumulation.writeBytes(input, checkpoint, bytesToPreserve); + } else if (checkpoint == 0) { + cumulation = this.cumulation = + newCumulationBuffer(ctx, inputSize); + cumulation.writeBytes(input, oldReaderIndex, inputSize); + cumulation.readerIndex(input.readerIndex()); + + } else { + cumulation = this.cumulation = + newCumulationBuffer(ctx, input.readableBytes()); + cumulation.writeBytes(input); + } + replayable = new ReplayingDecoderBuffer(cumulation); + } else { + this.cumulation = null; + replayable = ReplayingDecoderBuffer.EMPTY_BUFFER; + } + } else { + ChannelBuffer cumulation = this.cumulation; + assert cumulation.readable(); + if (cumulation.writableBytes() < input.readableBytes()) { + cumulation.discardReadBytes(); + } + cumulation.writeBytes(input); + callDecode(ctx, e.getChannel(), cumulation, replayable, e.getRemoteAddress()); + if (!cumulation.readable()) { + this.cumulation = null; + replayable = ReplayingDecoderBuffer.EMPTY_BUFFER; + } + } } @Override @@ -455,15 +501,15 @@ public abstract class ReplayingDecoder> ctx.sendUpstream(e); } - private void callDecode(ChannelHandlerContext context, Channel channel, ChannelBuffer cumulation, SocketAddress remoteAddress) throws Exception { - while (cumulation.readable()) { - int oldReaderIndex = checkpoint = cumulation.readerIndex(); + private void callDecode(ChannelHandlerContext context, Channel channel, ChannelBuffer input, ChannelBuffer replayableInput, SocketAddress remoteAddress) throws Exception { + while (input.readable()) { + int oldReaderIndex = checkpoint = input.readerIndex(); Object result = null; T oldState = state; try { - result = decode(context, channel, replayable, state); + result = decode(context, channel, replayableInput, state); if (result == null) { - if (oldReaderIndex == cumulation.readerIndex() && oldState == state) { + if (oldReaderIndex == input.readerIndex() && oldState == state) { throw new IllegalStateException( "null cannot be returned if no data is consumed and state didn't change."); } else { @@ -476,7 +522,7 @@ public abstract class ReplayingDecoder> // Return to the checkpoint (or oldPosition) and retry. int checkpoint = this.checkpoint; if (checkpoint >= 0) { - cumulation.readerIndex(checkpoint); + input.readerIndex(checkpoint); } else { // Called by cleanup() - no need to maintain the readerIndex // anymore because the buffer has been released already. @@ -489,7 +535,7 @@ public abstract class ReplayingDecoder> break; } - if (oldReaderIndex == cumulation.readerIndex() && oldState == state) { + if (oldReaderIndex == input.readerIndex() && oldState == state) { throw new IllegalStateException( "decode() method must consume at least one byte " + "if it returned a decoded message (caused by: " + @@ -498,11 +544,6 @@ public abstract class ReplayingDecoder> // A successful decode unfoldAndFireMessageReceived(context, result, remoteAddress); - - if (!cumulation.readable()) { - this.cumulation = null; - replayable = ReplayingDecoderBuffer.EMPTY_BUFFER; - } } } @@ -528,19 +569,17 @@ public abstract class ReplayingDecoder> private void cleanup(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { try { - if (!needsCleanup) { + ChannelBuffer cumulation = this.cumulation; + if (cumulation == null) { return; - } else { - needsCleanup = false; } - ChannelBuffer cumulation = this.cumulation; this.cumulation = null; replayable.terminate(); if (cumulation != null && cumulation.readable()) { // Make sure all data was read before notifying a closed channel. - callDecode(ctx, e.getChannel(), cumulation, null); + callDecode(ctx, e.getChannel(), cumulation, replayable, null); } // Call decodeLast() finally. Please note that decodeLast() is @@ -558,19 +597,19 @@ public abstract class ReplayingDecoder> } } - private ChannelBuffer cumulation(ChannelHandlerContext ctx) { - ChannelBuffer buf = this.cumulation; - if (buf == null) { - - if (cumulation == null) { - ChannelBufferFactory factory = ctx.getChannel().getConfig().getBufferFactory(); - buf = new UnsafeDynamicChannelBuffer(factory); - cumulation = buf; - replayable = new ReplayingDecoderBuffer(buf); - } else { - buf = cumulation; - } - } - return buf; + /** + * Create a new {@link ChannelBuffer} which is used for the cumulation. + * Be aware that this MUST be a dynamic buffer. Sub-classes may override + * this to provide a dynamic {@link ChannelBuffer} which has some + * pre-allocated size that better fit their need. + * + * @param ctx {@link ChannelHandlerContext} for this handler + * @return buffer the {@link ChannelBuffer} which is used for cumulation + */ + protected ChannelBuffer newCumulationBuffer( + ChannelHandlerContext ctx, int minimumCapacity) { + ChannelBufferFactory factory = ctx.getChannel().getConfig().getBufferFactory(); + return ChannelBuffers.dynamicBuffer( + factory.getDefaultOrder(), Math.max(minimumCapacity, 256), factory); } } diff --git a/common/src/main/java/io/netty/util/internal/LegacyLinkedTransferQueue.java b/common/src/main/java/io/netty/util/internal/LegacyLinkedTransferQueue.java index f0168ea637..45ab82dd2b 100644 --- a/common/src/main/java/io/netty/util/internal/LegacyLinkedTransferQueue.java +++ b/common/src/main/java/io/netty/util/internal/LegacyLinkedTransferQueue.java @@ -612,6 +612,7 @@ public class LegacyLinkedTransferQueue extends AbstractQueue @SuppressWarnings("unchecked") static E cast(Object item) { // assert item == null || item.getClass() != Node.class; + // Explicit cast, see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6302954 return (E) item; } @@ -625,6 +626,7 @@ public class LegacyLinkedTransferQueue extends AbstractQueue * @return an item if matched, else e * @throws NullPointerException if haveData mode but e is null */ + @SuppressWarnings("unchecked") private E xfer(E e, boolean haveData, int how, long nanos) { if (haveData && e == null) { throw new NullPointerException(); @@ -653,7 +655,8 @@ public class LegacyLinkedTransferQueue extends AbstractQueue } } LockSupport.unpark(p.waiter); - return LegacyLinkedTransferQueue.cast(item); + // Explicit cast, see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6302954 + return (E) LegacyLinkedTransferQueue.cast(item); } } Node n = p.next; @@ -726,6 +729,7 @@ public class LegacyLinkedTransferQueue extends AbstractQueue * @param nanos timeout in nanosecs, used only if timed is true * @return matched item, or e if unmatched on interrupt or timeout */ + @SuppressWarnings("unchecked") private E awaitMatch(Node s, Node pred, E e, boolean timed, long nanos) { long lastTime = timed ? System.nanoTime() : 0L; Thread w = Thread.currentThread(); @@ -737,7 +741,8 @@ public class LegacyLinkedTransferQueue extends AbstractQueue if (item != e) { // matched // assert item != s; s.forgetContents(); // avoid garbage - return LegacyLinkedTransferQueue.cast(item); + // Explicit cast, see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6302954 + return (E) LegacyLinkedTransferQueue.cast(item); } if ((w.isInterrupted() || timed && nanos <= 0) && s.casItem(e, s)) { // cancel @@ -820,12 +825,14 @@ public class LegacyLinkedTransferQueue extends AbstractQueue * Returns the item in the first unmatched node with isData; or * null if none. Used by peek. */ + @SuppressWarnings("unchecked") private E firstDataItem() { for (Node p = head; p != null; p = succ(p)) { Object item = p.item; if (p.isData) { if (item != null && item != p) { - return LegacyLinkedTransferQueue.cast(item); + // Explicit cast, see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6302954 + return (E) LegacyLinkedTransferQueue.cast(item); } } else if (item == null) { @@ -870,6 +877,7 @@ public class LegacyLinkedTransferQueue extends AbstractQueue /** * Moves to next node after prev, or first node if prev null. */ + @SuppressWarnings("unchecked") private void advance(Node prev) { lastPred = lastRet; lastRet = prev; @@ -878,7 +886,8 @@ public class LegacyLinkedTransferQueue extends AbstractQueue Object item = p.item; if (p.isData) { if (item != null && item != p) { - nextItem = LegacyLinkedTransferQueue.cast(item); + // Explicit cast, see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6302954 + nextItem = (E) LegacyLinkedTransferQueue.cast(item); nextNode = p; return; } diff --git a/common/src/main/java/io/netty/util/internal/LinkedTransferQueue.java b/common/src/main/java/io/netty/util/internal/LinkedTransferQueue.java index b43634016e..7242eaf084 100644 --- a/common/src/main/java/io/netty/util/internal/LinkedTransferQueue.java +++ b/common/src/main/java/io/netty/util/internal/LinkedTransferQueue.java @@ -81,6 +81,7 @@ import java.util.concurrent.locks.LockSupport; * @param the type of elements held in this collection */ +@SuppressWarnings("restriction") public class LinkedTransferQueue extends AbstractQueue implements BlockingQueue, java.io.Serializable { private static final long serialVersionUID = -3223113410248163686L; @@ -529,8 +530,6 @@ public class LinkedTransferQueue extends AbstractQueue return false; } - private static final long serialVersionUID = -3375979862319811754L; - // Unsafe mechanics private static final sun.misc.Unsafe UNSAFE; private static final long itemOffset; diff --git a/common/src/main/java/io/netty/util/internal/jzlib/Adler32.java b/common/src/main/java/io/netty/util/internal/jzlib/Adler32.java index 8e5f00c834..234d25404d 100644 --- a/common/src/main/java/io/netty/util/internal/jzlib/Adler32.java +++ b/common/src/main/java/io/netty/util/internal/jzlib/Adler32.java @@ -112,4 +112,8 @@ final class Adler32 { } return s2 << 16 | s1; } + + private Adler32() { + // Utility Class + } } diff --git a/common/src/main/java/io/netty/util/internal/jzlib/CRC32.java b/common/src/main/java/io/netty/util/internal/jzlib/CRC32.java index c6b17f0876..6af6a37545 100644 --- a/common/src/main/java/io/netty/util/internal/jzlib/CRC32.java +++ b/common/src/main/java/io/netty/util/internal/jzlib/CRC32.java @@ -92,4 +92,8 @@ final class CRC32 { crc32 ^= 0xffffffff; return crc32; } + + private CRC32() { + // Utility Class + } } diff --git a/common/src/main/java/io/netty/util/internal/jzlib/Deflate.java b/common/src/main/java/io/netty/util/internal/jzlib/Deflate.java index 4d45145b86..ca714f06d3 100644 --- a/common/src/main/java/io/netty/util/internal/jzlib/Deflate.java +++ b/common/src/main/java/io/netty/util/internal/jzlib/Deflate.java @@ -96,7 +96,7 @@ final class Deflate { "data error", // Z_DATA_ERROR (-3) "insufficient memory", // Z_MEM_ERROR (-4) "buffer error", // Z_BUF_ERROR (-5) - "incompatible version",// Z_VERSION_ERROR (-6) + "incompatible version", // Z_VERSION_ERROR (-6) "" }; // block not completed, need more input or more output @@ -342,7 +342,7 @@ final class Deflate { // Scan a literal or distance tree to determine the frequencies of the codes // in the bit length tree. - private void scan_tree(short[] tree,// the tree to be scanned + private void scan_tree(short[] tree, // the tree to be scanned int max_code // and its largest code of non zero frequency ) { int n; // iterates over all tree elements @@ -437,7 +437,7 @@ final class Deflate { // Send a literal or distance tree in compressed form, using the codes in // bl_tree. - private void send_tree(short[] tree,// the tree to be sent + private void send_tree(short[] tree, // the tree to be sent int max_code // and its largest code of non zero frequency ) { int n; // iterates over all tree elements @@ -514,7 +514,7 @@ final class Deflate { private void send_code(int c, short[] tree) { int c2 = c * 2; - send_bits((tree[c2] & 0xffff), (tree[c2 + 1] & 0xffff)); + send_bits(tree[c2] & 0xffff, tree[c2 + 1] & 0xffff); } private void send_bits(int value, int length) { @@ -804,7 +804,7 @@ final class Deflate { int stored_len, // length of input block boolean eof // true if this is the last block for a file ) { - int opt_lenb, static_lenb;// opt_len and static_len in bytes + int opt_lenb, static_lenb; // opt_len and static_len in bytes int max_blindex = 0; // index of last bit length code of non zero freq // Build the Huffman trees unless a stored block is forced diff --git a/common/src/main/java/io/netty/util/internal/jzlib/InfBlocks.java b/common/src/main/java/io/netty/util/internal/jzlib/InfBlocks.java index b387ffbb1c..e51d5ee454 100644 --- a/common/src/main/java/io/netty/util/internal/jzlib/InfBlocks.java +++ b/common/src/main/java/io/netty/util/internal/jzlib/InfBlocks.java @@ -61,7 +61,7 @@ final class InfBlocks { private static final int TYPE = 0; // get type bits (3, including end bit) private static final int LENS = 1; // get lengths for stored - private static final int STORED = 2;// processing stored block + private static final int STORED = 2; // processing stored block private static final int TABLE = 3; // get table lengths private static final int BTREE = 4; // get bit lengths tree for a dynamic block private static final int DTREE = 5; // get length, distance trees for a dynamic block @@ -124,16 +124,15 @@ final class InfBlocks { int m; // bytes to end of window or read pointer // copy input/output information to locals (UPDATE macro restores) - { - p = z.next_in_index; - n = z.avail_in; - b = bitb; - k = bitk; - } - { - q = write; - m = q < read? read - q - 1 : end - q; - } + + p = z.next_in_index; + n = z.avail_in; + b = bitb; + k = bitk; + + q = write; + m = q < read? read - q - 1 : end - q; + // process input based on current state while (true) { @@ -161,20 +160,17 @@ final class InfBlocks { switch (t >>> 1) { case 0: // stored - { + b >>>= 3; k -= 3; - } + t = k & 7; // go to byte boundary - { - b >>>= t; - k -= t; - } + b >>>= t; + k -= t; mode = LENS; // get length of stored block break; case 1: // fixed - { int[] bl = new int[1]; int[] bd = new int[1]; int[][] tl = new int[1][]; @@ -182,30 +178,24 @@ final class InfBlocks { InfTree.inflate_trees_fixed(bl, bd, tl, td); codes.init(bl[0], bd[0], tl[0], 0, td[0], 0); - } - { - b >>>= 3; - k -= 3; - } + b >>>= 3; + k -= 3; mode = CODES; break; case 2: // dynamic - { b >>>= 3; k -= 3; - } mode = TABLE; break; case 3: // illegal - { b >>>= 3; k -= 3; - } + mode = BAD; z.msg = "invalid block type"; r = JZlib.Z_DATA_ERROR; @@ -352,10 +342,9 @@ final class InfBlocks { } } - { - b >>>= 14; - k -= 14; - } + + b >>>= 14; + k -= 14; index = 0; mode = BTREE; @@ -380,10 +369,9 @@ final class InfBlocks { blens[border[index ++]] = b & 7; - { - b >>>= 3; - k -= 3; - } + b >>>= 3; + k -= 3; + } while (index < 19) { @@ -505,36 +493,36 @@ final class InfBlocks { } tb[0] = -1; - { - int[] bl = new int[1]; - int[] bd = new int[1]; - int[] tl = new int[1]; - int[] td = new int[1]; - bl[0] = 9; // must be <= 9 for lookahead assumptions - bd[0] = 6; // must be <= 9 for lookahead assumptions - t = table; - t = inftree.inflate_trees_dynamic(257 + (t & 0x1f), - 1 + (t >> 5 & 0x1f), blens, bl, bd, tl, td, hufts, - z); + int[] bl = new int[1]; + int[] bd = new int[1]; + int[] tl = new int[1]; + int[] td = new int[1]; + bl[0] = 9; // must be <= 9 for lookahead assumptions + bd[0] = 6; // must be <= 9 for lookahead assumptions - if (t != JZlib.Z_OK) { - if (t == JZlib.Z_DATA_ERROR) { - blens = null; - mode = BAD; - } - r = t; + t = table; + t = inftree.inflate_trees_dynamic(257 + (t & 0x1f), + 1 + (t >> 5 & 0x1f), blens, bl, bd, tl, td, hufts, + z); - bitb = b; - bitk = k; - z.avail_in = n; - z.total_in += p - z.next_in_index; - z.next_in_index = p; - write = q; - return inflate_flush(z, r); + if (t != JZlib.Z_OK) { + if (t == JZlib.Z_DATA_ERROR) { + blens = null; + mode = BAD; } - codes.init(bl[0], bd[0], hufts, tl[0], hufts, td[0]); + r = t; + + bitb = b; + bitk = k; + z.avail_in = n; + z.total_in += p - z.next_in_index; + z.next_in_index = p; + write = q; + return inflate_flush(z, r); } + codes.init(bl[0], bd[0], hufts, tl[0], hufts, td[0]); + mode = CODES; case CODES: bitb = b; diff --git a/common/src/main/java/io/netty/util/internal/jzlib/InfCodes.java b/common/src/main/java/io/netty/util/internal/jzlib/InfCodes.java index ef979b57a3..6e6ea3ab2a 100644 --- a/common/src/main/java/io/netty/util/internal/jzlib/InfCodes.java +++ b/common/src/main/java/io/netty/util/internal/jzlib/InfCodes.java @@ -71,7 +71,7 @@ final class InfCodes { // mode dependent information private int len; private int[] tree; // pointer into tree - private int tree_index = 0; + private int tree_index; private int need; // bits needed private int lit; // if EXT or COPY, where and how much diff --git a/common/src/main/java/io/netty/util/internal/jzlib/InfTree.java b/common/src/main/java/io/netty/util/internal/jzlib/InfTree.java index 313c1789f6..67012e3dea 100644 --- a/common/src/main/java/io/netty/util/internal/jzlib/InfTree.java +++ b/common/src/main/java/io/netty/util/internal/jzlib/InfTree.java @@ -180,8 +180,8 @@ final class InfTree { int[] e, // list of extra bits for non-simple codes int[] t, // result: starting table int[] m, // maximum lookup bits, returns actual - int[] hp,// space for trees - int[] hn,// hufts used in space + int[] hp, // space for trees + int[] hn, // hufts used in space int[] v // working area: values in order of bit length ) { // Given a list of code lengths and a maximum table size, make a set of @@ -437,7 +437,7 @@ final class InfTree { static int inflate_trees_fixed(int[] bl, //literal desired/actual bit depth int[] bd, //distance desired/actual bit depth - int[][] tl,//literal/length tree result + int[][] tl, //literal/length tree result int[][] td //distance tree result ) { bl[0] = fixed_bl; diff --git a/common/src/main/java/io/netty/util/internal/jzlib/Inflate.java b/common/src/main/java/io/netty/util/internal/jzlib/Inflate.java index c496533203..c18dc1592f 100644 --- a/common/src/main/java/io/netty/util/internal/jzlib/Inflate.java +++ b/common/src/main/java/io/netty/util/internal/jzlib/Inflate.java @@ -76,7 +76,7 @@ final class Inflate { private static final int GZIP_FNAME = 21; private static final int GZIP_FCOMMENT = 22; private static final int GZIP_FHCRC = 23; - private static final int GZIP_CRC32= 24; + private static final int GZIP_CRC32 = 24; private static final int GZIP_ISIZE = 25; private int mode; // current inflate mode @@ -305,7 +305,7 @@ final class Inflate { break; } else if (z.istate.wrapperType == WrapperType.ZLIB) { z.istate.mode = CHECK4; - } else if (z.istate.wrapperType == WrapperType.GZIP){ + } else if (z.istate.wrapperType == WrapperType.GZIP) { gzipCRC32 = 0; gzipISize = 0; gzipBytesToRead = 4; diff --git a/common/src/main/java/io/netty/util/internal/jzlib/JZlib.java b/common/src/main/java/io/netty/util/internal/jzlib/JZlib.java index b97f96a878..5d74519f5b 100644 --- a/common/src/main/java/io/netty/util/internal/jzlib/JZlib.java +++ b/common/src/main/java/io/netty/util/internal/jzlib/JZlib.java @@ -105,4 +105,8 @@ public final class JZlib { enum WrapperType { NONE, ZLIB, GZIP, ZLIB_OR_NONE } + + private JZlib() { + // Utility class + } } diff --git a/codec/src/main/java/io/netty/handler/codec/replay/UnsafeDynamicChannelBuffer.java b/common/src/main/java/io/netty/util/internal/jzlib/package-info.java similarity index 57% rename from codec/src/main/java/io/netty/handler/codec/replay/UnsafeDynamicChannelBuffer.java rename to common/src/main/java/io/netty/util/internal/jzlib/package-info.java index 57b5be4fc7..758bc4e1fe 100644 --- a/codec/src/main/java/io/netty/handler/codec/replay/UnsafeDynamicChannelBuffer.java +++ b/common/src/main/java/io/netty/util/internal/jzlib/package-info.java @@ -13,19 +13,9 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.handler.codec.replay; -import io.netty.buffer.ChannelBufferFactory; -import io.netty.buffer.DynamicChannelBuffer; - -class UnsafeDynamicChannelBuffer extends DynamicChannelBuffer { - - UnsafeDynamicChannelBuffer(ChannelBufferFactory factory) { - super(factory.getDefaultOrder(), 256, factory); - } - - @Override - protected void checkReadableBytes(int minReaderRemaining) { - // Do not check here - ReplayingDecoderBuffer will check. - } -} +/** + * Internal-use-only utilities which is not allowed to be used + * outside Netty. + */ +package io.netty.util.internal.jzlib; diff --git a/example/src/main/java/io/netty/example/http/snoop/HttpSnoopServerPipelineFactory.java b/example/src/main/java/io/netty/example/http/snoop/HttpSnoopServerPipelineFactory.java index e8241e96ce..c7a8bacc5b 100644 --- a/example/src/main/java/io/netty/example/http/snoop/HttpSnoopServerPipelineFactory.java +++ b/example/src/main/java/io/netty/example/http/snoop/HttpSnoopServerPipelineFactory.java @@ -22,15 +22,12 @@ import io.netty.channel.ChannelPipelineFactory; import io.netty.handler.codec.http.HttpContentCompressor; import io.netty.handler.codec.http.HttpRequestDecoder; import io.netty.handler.codec.http.HttpResponseEncoder; -import io.netty.handler.logging.LoggingHandler; -import io.netty.logging.InternalLogLevel; public class HttpSnoopServerPipelineFactory implements ChannelPipelineFactory { @Override public ChannelPipeline getPipeline() throws Exception { // Create a default pipeline implementation. ChannelPipeline pipeline = pipeline(); - pipeline.addLast("log", new LoggingHandler(InternalLogLevel.INFO)); // Uncomment the following line if you want HTTPS //SSLEngine engine = SecureChatSslContextFactory.getServerContext().createSSLEngine(); diff --git a/example/src/main/java/io/netty/example/http/websocketx/autobahn/AutobahnServer.java b/example/src/main/java/io/netty/example/http/websocketx/autobahn/AutobahnServer.java index 2f2f5bab4e..c4a012ca59 100644 --- a/example/src/main/java/io/netty/example/http/websocketx/autobahn/AutobahnServer.java +++ b/example/src/main/java/io/netty/example/http/websocketx/autobahn/AutobahnServer.java @@ -17,9 +17,6 @@ package io.netty.example.http.websocketx.autobahn; import java.net.InetSocketAddress; import java.util.concurrent.Executors; -import java.util.logging.ConsoleHandler; -import java.util.logging.Level; -import java.util.logging.Logger; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.socket.nio.NioServerSocketChannelFactory; diff --git a/example/src/main/java/io/netty/example/http/websocketx/server/WebSocketServer.java b/example/src/main/java/io/netty/example/http/websocketx/server/WebSocketServer.java index b2899692dd..d0918c4ec1 100644 --- a/example/src/main/java/io/netty/example/http/websocketx/server/WebSocketServer.java +++ b/example/src/main/java/io/netty/example/http/websocketx/server/WebSocketServer.java @@ -17,23 +17,20 @@ package io.netty.example.http.websocketx.server; import java.net.InetSocketAddress; import java.util.concurrent.Executors; -import java.util.logging.ConsoleHandler; -import java.util.logging.Level; -import java.util.logging.Logger; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.socket.nio.NioServerSocketChannelFactory; /** * A HTTP server which serves Web Socket requests at: - * + * * http://localhost:8080/websocket - * + * * Open your browser at http://localhost:8080/, then the demo page will be loaded and a Web Socket connection will be * made automatically. - * + * * This server illustrates support for the different web socket specification versions and will work with: - * + * *
    *
  • Safari 5+ (draft-ietf-hybi-thewebsocketprotocol-00) *
  • Chrome 6-13 (draft-ietf-hybi-thewebsocketprotocol-00) diff --git a/example/src/main/java/io/netty/example/http/websocketx/sslserver/WebSocketSslServer.java b/example/src/main/java/io/netty/example/http/websocketx/sslserver/WebSocketSslServer.java index def9595f90..e84c3dd93c 100644 --- a/example/src/main/java/io/netty/example/http/websocketx/sslserver/WebSocketSslServer.java +++ b/example/src/main/java/io/netty/example/http/websocketx/sslserver/WebSocketSslServer.java @@ -17,23 +17,20 @@ package io.netty.example.http.websocketx.sslserver; import java.net.InetSocketAddress; import java.util.concurrent.Executors; -import java.util.logging.ConsoleHandler; -import java.util.logging.Level; -import java.util.logging.Logger; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.socket.nio.NioServerSocketChannelFactory; /** * A HTTP server which serves Web Socket requests at: - * + * * https://localhost:8081/websocket - * + * * Open your browser at https://localhost:8081/, then the demo page will be loaded and a Web Socket connection will be * made automatically. - * + * * This server illustrates support for the different web socket specification versions and will work with: - * + * *
      *
    • Safari 5+ (draft-ietf-hybi-thewebsocketprotocol-00) *
    • Chrome 6-13 (draft-ietf-hybi-thewebsocketprotocol-00) diff --git a/example/src/main/java/io/netty/example/sctp/SctpClient.java b/example/src/main/java/io/netty/example/sctp/SctpClient.java index 3357060b13..3378f65f3a 100644 --- a/example/src/main/java/io/netty/example/sctp/SctpClient.java +++ b/example/src/main/java/io/netty/example/sctp/SctpClient.java @@ -87,7 +87,6 @@ public class SctpClient { // Parse options. final String host = args[0]; final int port = Integer.parseInt(args[1]); - final int firstMessageSize; new SctpClient(host, port).run(); } diff --git a/example/src/main/java/io/netty/example/securechat/SecureChatClientPipelineFactory.java b/example/src/main/java/io/netty/example/securechat/SecureChatClientPipelineFactory.java index 7aceadb0df..170e774767 100644 --- a/example/src/main/java/io/netty/example/securechat/SecureChatClientPipelineFactory.java +++ b/example/src/main/java/io/netty/example/securechat/SecureChatClientPipelineFactory.java @@ -45,7 +45,7 @@ public class SecureChatClientPipelineFactory implements SSLEngine engine = SecureChatSslContextFactory.getClientContext().createSSLEngine(); - //engine.setUseClientMode(true); + engine.setUseClientMode(true); pipeline.addLast("ssl", new SslHandler(engine)); diff --git a/handler/src/main/java/io/netty/handler/execution/ExecutionHandler.java b/handler/src/main/java/io/netty/handler/execution/ExecutionHandler.java index de2f435666..391a59b797 100644 --- a/handler/src/main/java/io/netty/handler/execution/ExecutionHandler.java +++ b/handler/src/main/java/io/netty/handler/execution/ExecutionHandler.java @@ -110,25 +110,40 @@ public class ExecutionHandler implements ChannelUpstreamHandler, ChannelDownstre private final Executor executor; private final boolean handleDownstream; + private final boolean handleUpstream; + + /** + * Creates a new instance with the specified {@link Executor} which only handles upstream events. + * Specify an {@link OrderedMemoryAwareThreadPoolExecutor} if unsure. + */ + public ExecutionHandler(Executor executor) { + this(executor, false, true); + } + + /** + * Use {@link #ExecutionHandler(Executor, boolean, boolean)} + * + * {@link Deprecated} + */ + @Deprecated + public ExecutionHandler(Executor executor, boolean handleDownstream) { + this(executor, handleDownstream, true); + } /** * Creates a new instance with the specified {@link Executor}. * Specify an {@link OrderedMemoryAwareThreadPoolExecutor} if unsure. */ - public ExecutionHandler(Executor executor) { - this(executor, false); - } - - /** - * Creates a new instance with the specified {@link Executor}. - * Specify an {@link OrderedMemoryAwareThreadPoolExecutor} if unsure. - */ - public ExecutionHandler(Executor executor, boolean handleDownstream) { + public ExecutionHandler(Executor executor, boolean handleDownstream, boolean handleUpstream) { if (executor == null) { throw new NullPointerException("executor"); } + if (!handleDownstream && !handleUpstream) { + throw new IllegalArgumentException("You must handle at least handle one event type"); + } this.executor = executor; this.handleDownstream = handleDownstream; + this.handleUpstream = handleUpstream; } /** @@ -154,7 +169,11 @@ public class ExecutionHandler implements ChannelUpstreamHandler, ChannelDownstre @Override public void handleUpstream( ChannelHandlerContext context, ChannelEvent e) throws Exception { - executor.execute(new ChannelUpstreamEventRunnable(context, e)); + if (handleUpstream) { + executor.execute(new ChannelUpstreamEventRunnable(context, e)); + } else { + context.sendUpstream(e); + } } @Override diff --git a/handler/src/main/java/io/netty/handler/execution/MemoryAwareThreadPoolExecutor.java b/handler/src/main/java/io/netty/handler/execution/MemoryAwareThreadPoolExecutor.java index 7d6589b10d..e78200ff99 100644 --- a/handler/src/main/java/io/netty/handler/execution/MemoryAwareThreadPoolExecutor.java +++ b/handler/src/main/java/io/netty/handler/execution/MemoryAwareThreadPoolExecutor.java @@ -419,10 +419,18 @@ public class MemoryAwareThreadPoolExecutor extends ThreadPoolExecutor { //System.out.println("READABLE"); ChannelHandlerContext ctx = eventTask.getContext(); if (ctx.getHandler() instanceof ExecutionHandler) { - // readSuspended = false; - ctx.setAttachment(null); + // check if the attachment was set as this means that we suspend the channel from reads. This only works when + // this pool is used with ExecutionHandler but I guess thats good enough for us. + // + // See #215 + if (ctx.getAttachment() != null) { + // readSuspended = false; + ctx.setAttachment(null); + channel.setReadable(true); + } + } else { + channel.setReadable(true); } - channel.setReadable(true); } } } diff --git a/handler/src/main/java/io/netty/handler/execution/OrderedDownstreamThreadPoolExecutor.java b/handler/src/main/java/io/netty/handler/execution/OrderedDownstreamThreadPoolExecutor.java new file mode 100644 index 0000000000..2bd5ed6252 --- /dev/null +++ b/handler/src/main/java/io/netty/handler/execution/OrderedDownstreamThreadPoolExecutor.java @@ -0,0 +1,165 @@ +/* + * Copyright 2011 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.execution; + +import io.netty.channel.Channel; +import io.netty.channel.ChannelEvent; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelFutureListener; + +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; + +/** + * {@link Executor} which should be used for downstream {@link ChannelEvent}'s. This implementation will take care of preserve the order of the events in a {@link Channel}. + * If you don't need to preserve the order just use one of the {@link Executor} implementations provided by the static methods of {@link Executors}. + *
      + *
      + * + * For more informations about how the order is preserved see {@link OrderedMemoryAwareThreadPoolExecutor} + * + */ +public final class OrderedDownstreamThreadPoolExecutor extends OrderedMemoryAwareThreadPoolExecutor { + + /** + * Creates a new instance. + * + * @param corePoolSize the maximum number of active threads + */ + public OrderedDownstreamThreadPoolExecutor(int corePoolSize) { + super(corePoolSize, 0L, 0L); + } + + /** + * Creates a new instance. + * + * @param corePoolSize the maximum number of active threads + * @param keepAliveTime the amount of time for an inactive thread to shut itself down + * @param unit the {@link TimeUnit} of {@code keepAliveTime} + */ + public OrderedDownstreamThreadPoolExecutor( + int corePoolSize, long keepAliveTime, TimeUnit unit) { + super(corePoolSize, 0L, 0L, keepAliveTime, unit); + } + + /** + * Creates a new instance. + * + * @param corePoolSize the maximum number of active threads + * @param keepAliveTime the amount of time for an inactive thread to shut itself down + * @param unit the {@link TimeUnit} of {@code keepAliveTime} + * @param threadFactory the {@link ThreadFactory} of this pool + */ + public OrderedDownstreamThreadPoolExecutor( + int corePoolSize, long keepAliveTime, TimeUnit unit, ThreadFactory threadFactory) { + super(corePoolSize, 0L, 0L, + keepAliveTime, unit, threadFactory); + } + + /** + * Return null + */ + @Override + public ObjectSizeEstimator getObjectSizeEstimator() { + return null; + } + + /** + * Throws {@link UnsupportedOperationException} as there is not support for limit the memory size in this implementation + */ + @Override + public void setObjectSizeEstimator(ObjectSizeEstimator objectSizeEstimator) { + throw new UnsupportedOperationException("Not supported by this implementation"); + } + + /** + * Returns 0L + */ + @Override + public long getMaxChannelMemorySize() { + return 0L; + } + + /** + * Throws {@link UnsupportedOperationException} as there is not support for limit the memory size in this implementation + */ + @Override + public void setMaxChannelMemorySize(long maxChannelMemorySize) { + throw new UnsupportedOperationException("Not supported by this implementation"); + } + + /** + * Returns 0L + */ + @Override + public long getMaxTotalMemorySize() { + return 0L; + } + + /** + * Throws {@link UnsupportedOperationException} as there is not support for limit the memory size in this implementation + */ + @Override + public void setMaxTotalMemorySize(long maxTotalMemorySize) { + throw new UnsupportedOperationException("Not supported by this implementation"); + } + + /** + * Return false as we not need to cound the memory in this implementation + */ + @Override + protected boolean shouldCount(Runnable task) { + return false; + } + + @Override + public void execute(Runnable command) { + + // check if the Runnable was of an unsupported type + if (command instanceof ChannelUpstreamEventRunnable) { + throw new RejectedExecutionException("command must be enclosed with an downstream event."); + } + doExecute(command); + } + + @Override + protected Executor getChildExecutor(ChannelEvent e) { + final Object key = getChildExecutorKey(e); + Executor executor = childExecutors.get(key); + if (executor == null) { + executor = new ChildExecutor(); + Executor oldExecutor = childExecutors.putIfAbsent(key, executor); + if (oldExecutor != null) { + executor = oldExecutor; + } else { + + // register a listener so that the ChildExecutor will get removed once the channel was closed + e.getChannel().getCloseFuture().addListener(new ChannelFutureListener() { + + public void operationComplete(ChannelFuture future) throws Exception { + removeChildExecutor(key); + } + }); + } + } + + return executor; + } + +} diff --git a/handler/src/main/java/io/netty/handler/execution/OrderedMemoryAwareThreadPoolExecutor.java b/handler/src/main/java/io/netty/handler/execution/OrderedMemoryAwareThreadPoolExecutor.java index 06699125d2..5434ac5e59 100644 --- a/handler/src/main/java/io/netty/handler/execution/OrderedMemoryAwareThreadPoolExecutor.java +++ b/handler/src/main/java/io/netty/handler/execution/OrderedMemoryAwareThreadPoolExecutor.java @@ -136,7 +136,7 @@ public class OrderedMemoryAwareThreadPoolExecutor extends // TODO Make OMATPE focus on the case where Channel is the key. // Add a new less-efficient TPE that allows custom key. - private final ConcurrentMap childExecutors = newChildExecutorMap(); + protected final ConcurrentMap childExecutors = newChildExecutorMap(); /** * Creates a new instance. @@ -242,7 +242,7 @@ public class OrderedMemoryAwareThreadPoolExecutor extends } } - private Executor getChildExecutor(ChannelEvent e) { + protected Executor getChildExecutor(ChannelEvent e) { Object key = getChildExecutorKey(e); Executor executor = childExecutors.get(key); if (executor == null) { @@ -278,7 +278,7 @@ public class OrderedMemoryAwareThreadPoolExecutor extends afterExecute(r, t); } - private final class ChildExecutor implements Executor, Runnable { + protected final class ChildExecutor implements Executor, Runnable { private final Queue tasks = QueueFactory.createQueue(Runnable.class); private final AtomicBoolean isRunning = new AtomicBoolean(); 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 0b37456911..d14f8205c3 100644 --- a/handler/src/main/java/io/netty/handler/ssl/SslHandler.java +++ b/handler/src/main/java/io/netty/handler/ssl/SslHandler.java @@ -574,15 +574,15 @@ public class SslHandler extends FrameDecoder if (tls) { // SSLv3 or TLS - Check ProtocolVersion int majorVersion = buffer.getUnsignedByte(buffer.readerIndex() + 1); - if (majorVersion >= 3 && majorVersion < 10) { + if (majorVersion == 3) { // SSLv3 or TLS packetLength = (getShort(buffer, buffer.readerIndex() + 3) & 0xFFFF) + 5; if (packetLength <= 5) { - // Neither SSLv2 or TLSv1 (i.e. SSLv2 or bad data) + // Neither SSLv3 or TLSv1 (i.e. SSLv2 or bad data) tls = false; } } else { - // Neither SSLv2 or TLSv1 (i.e. SSLv2 or bad data) + // Neither SSLv3 or TLSv1 (i.e. SSLv2 or bad data) tls = false; } } @@ -594,7 +594,7 @@ public class SslHandler extends FrameDecoder buffer.readerIndex()) & 0x80) != 0 ? 2 : 3; int majorVersion = buffer.getUnsignedByte( buffer.readerIndex() + headerLength + 1); - if (majorVersion >= 2 && majorVersion < 10) { + if (majorVersion == 2 || majorVersion == 3) { // SSLv2 if (headerLength == 2) { packetLength = (getShort(buffer, buffer.readerIndex()) & 0x7FFF) + 2; @@ -972,7 +972,7 @@ public class SslHandler extends FrameDecoder outAppBuf.flip(); if (outAppBuf.hasRemaining()) { - ChannelBuffer frame = ChannelBuffers.buffer(outAppBuf.remaining()); + ChannelBuffer frame = ctx.getChannel().getConfig().getBufferFactory().getBuffer(outAppBuf.remaining()); frame.writeBytes(outAppBuf.array(), 0, frame.capacity()); return frame; } else { diff --git a/handler/src/main/java/io/netty/handler/stream/ChunkedWriteHandler.java b/handler/src/main/java/io/netty/handler/stream/ChunkedWriteHandler.java index c5387a33e9..2e70697e0e 100644 --- a/handler/src/main/java/io/netty/handler/stream/ChunkedWriteHandler.java +++ b/handler/src/main/java/io/netty/handler/stream/ChunkedWriteHandler.java @@ -90,7 +90,7 @@ public class ChunkedWriteHandler implements ChannelUpstreamHandler, ChannelDowns } try { - flush(ctx); + flush(ctx, false); } catch (Exception e) { if (logger.isWarnEnabled()) { logger.warn("Unexpected exception while sending chunks.", e); @@ -112,10 +112,10 @@ public class ChunkedWriteHandler implements ChannelUpstreamHandler, ChannelDowns final Channel channel = ctx.getChannel(); if (channel.isWritable()) { this.ctx = ctx; - flush(ctx); + flush(ctx, false); } else if (!channel.isConnected()) { this.ctx = ctx; - discard(ctx); + discard(ctx, false); } } @@ -127,12 +127,12 @@ public class ChunkedWriteHandler implements ChannelUpstreamHandler, ChannelDowns switch (cse.getState()) { case INTEREST_OPS: // Continue writing when the channel becomes writable. - flush(ctx); + flush(ctx, true); break; case OPEN: if (!Boolean.TRUE.equals(cse.getValue())) { // Fail all pending writes - discard(ctx); + discard(ctx, true); } break; } @@ -140,7 +140,7 @@ public class ChunkedWriteHandler implements ChannelUpstreamHandler, ChannelDowns ctx.sendUpstream(e); } - private void discard(ChannelHandlerContext ctx) { + private void discard(ChannelHandlerContext ctx, boolean fireNow) { ClosedChannelException cause = null; boolean fireExceptionCaught = false; @@ -169,20 +169,22 @@ public class ChunkedWriteHandler implements ChannelUpstreamHandler, ChannelDowns } currentEvent.getFuture().setFailure(cause); fireExceptionCaught = true; - - currentEvent = null; } if (fireExceptionCaught) { - Channels.fireExceptionCaught(ctx.getChannel(), cause); + if (fireNow) { + fireExceptionCaught(ctx, cause); + } else { + fireExceptionCaughtLater(ctx, cause); + } } } - private synchronized void flush(ChannelHandlerContext ctx) throws Exception { + private synchronized void flush(ChannelHandlerContext ctx, boolean fireNow) throws Exception { final Channel channel = ctx.getChannel(); if (!channel.isConnected()) { - discard(ctx); + discard(ctx, fireNow); } while (channel.isWritable()) { @@ -220,7 +222,11 @@ public class ChunkedWriteHandler implements ChannelUpstreamHandler, ChannelDowns this.currentEvent = null; currentEvent.getFuture().setFailure(t); - fireExceptionCaught(ctx, t); + if (fireNow) { + fireExceptionCaught(ctx, t); + } else { + fireExceptionCaughtLater(ctx, t); + } closeInput(chunks); break; @@ -262,7 +268,7 @@ public class ChunkedWriteHandler implements ChannelUpstreamHandler, ChannelDowns } if (!channel.isConnected()) { - discard(ctx); + discard(ctx, fireNow); break; } } diff --git a/handler/src/main/java/io/netty/handler/timeout/WriteTimeoutHandler.java b/handler/src/main/java/io/netty/handler/timeout/WriteTimeoutHandler.java index b5af55d670..bb6adde8fc 100644 --- a/handler/src/main/java/io/netty/handler/timeout/WriteTimeoutHandler.java +++ b/handler/src/main/java/io/netty/handler/timeout/WriteTimeoutHandler.java @@ -154,7 +154,7 @@ public class WriteTimeoutHandler extends SimpleChannelDownstreamHandler } protected void writeTimedOut(ChannelHandlerContext ctx) throws Exception { - Channels.fireExceptionCaught(ctx, EXCEPTION); + Channels.fireExceptionCaughtLater(ctx, EXCEPTION); } private final class WriteTimeoutTask implements TimerTask { diff --git a/lib/apiviz-1.3.1.GA.jar b/lib/apiviz-1.3.1.GA.jar deleted file mode 100644 index 1e1edb2c2d..0000000000 Binary files a/lib/apiviz-1.3.1.GA.jar and /dev/null differ diff --git a/pom.xml b/pom.xml index 6ac479d717..f69676e699 100644 --- a/pom.xml +++ b/pom.xml @@ -212,12 +212,12 @@ - - - [1.6.0,) + + + [1.7.0,) - [3.0.3,) + [3.0.2,) @@ -237,6 +237,32 @@ true + + + org.codehaus.mojo + animal-sniffer-maven-plugin + 1.7 + + + org.codehaus.mojo.signature + java16 + 1.0 + + + sun.misc.Unsafe + java.util.zip.Deflater + + + + + process-classes + + check + + + + maven-checkstyle-plugin 2.8 @@ -260,10 +286,22 @@ ${project.groupId} netty-build - 6 + 7 + + maven-surefire-plugin + 2.12 + + once + + **/Abstract* + **/TestUtil* + + random + + + + + javax.servlet + servlet-api + true + diff --git a/transport-http/src/main/java/io/netty/channel/socket/http/HttpTunnelingServlet.java b/transport-http/src/main/java/io/netty/channel/socket/http/HttpTunnelingServlet.java new file mode 100644 index 0000000000..2bf840bc48 --- /dev/null +++ b/transport-http/src/main/java/io/netty/channel/socket/http/HttpTunnelingServlet.java @@ -0,0 +1,248 @@ +/* + * Copyright 2011 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.http; + +import java.io.EOFException; +import java.io.IOException; +import java.io.PushbackInputStream; +import java.net.SocketAddress; + +import javax.servlet.ServletConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import io.netty.buffer.ChannelBuffer; +import io.netty.buffer.ChannelBuffers; +import io.netty.channel.Channel; +import io.netty.channel.ChannelFactory; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.Channels; +import io.netty.channel.ExceptionEvent; +import io.netty.channel.MessageEvent; +import io.netty.channel.SimpleChannelUpstreamHandler; +import io.netty.channel.local.DefaultLocalClientChannelFactory; +import io.netty.channel.local.LocalAddress; +import io.netty.handler.codec.http.HttpHeaders; +import io.netty.logging.InternalLogger; +import io.netty.logging.InternalLoggerFactory; + +/** + * An {@link HttpServlet} that proxies an incoming data to the actual server + * and vice versa. Please refer to the + * package summary for + * the detailed usage. + * @apiviz.landmark + */ +public class HttpTunnelingServlet extends HttpServlet { + + private static final long serialVersionUID = 4259910275899756070L; + + private static final String ENDPOINT = "endpoint"; + + static final InternalLogger logger = InternalLoggerFactory.getInstance(HttpTunnelingServlet.class); + + private volatile SocketAddress remoteAddress; + private volatile ChannelFactory channelFactory; + + @Override + public void init() throws ServletException { + ServletConfig config = getServletConfig(); + String endpoint = config.getInitParameter(ENDPOINT); + if (endpoint == null) { + throw new ServletException("init-param '" + ENDPOINT + "' must be specified."); + } + + try { + remoteAddress = parseEndpoint(endpoint.trim()); + } catch (ServletException e) { + throw e; + } catch (Exception e) { + throw new ServletException("Failed to parse an endpoint.", e); + } + + try { + channelFactory = createChannelFactory(remoteAddress); + } catch (ServletException e) { + throw e; + } catch (Exception e) { + throw new ServletException("Failed to create a channel factory.", e); + } + + // Stuff for testing purpose + //ServerBootstrap b = new ServerBootstrap(new DefaultLocalServerChannelFactory()); + //b.getPipeline().addLast("logger", new LoggingHandler(getClass(), InternalLogLevel.INFO, true)); + //b.getPipeline().addLast("handler", new EchoHandler()); + //b.bind(remoteAddress); + } + + protected SocketAddress parseEndpoint(String endpoint) throws Exception { + if (endpoint.startsWith("local:")) { + return new LocalAddress(endpoint.substring(6).trim()); + } else { + throw new ServletException( + "Invalid or unknown endpoint: " + endpoint); + } + } + + protected ChannelFactory createChannelFactory(SocketAddress remoteAddress) throws Exception { + if (remoteAddress instanceof LocalAddress) { + return new DefaultLocalClientChannelFactory(); + } else { + throw new ServletException( + "Unsupported remote address type: " + + remoteAddress.getClass().getName()); + } + } + + @Override + public void destroy() { + try { + destroyChannelFactory(channelFactory); + } catch (Exception e) { + if (logger.isWarnEnabled()) { + logger.warn("Failed to destroy a channel factory.", e); + } + } + } + + protected void destroyChannelFactory(ChannelFactory factory) throws Exception { + factory.releaseExternalResources(); + } + + @Override + protected void service(HttpServletRequest req, HttpServletResponse res) + throws ServletException, IOException { + if (!"POST".equalsIgnoreCase(req.getMethod())) { + if (logger.isWarnEnabled()) { + logger.warn("Unallowed method: " + req.getMethod()); + } + res.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED); + return; + } + + final ChannelPipeline pipeline = Channels.pipeline(); + final ServletOutputStream out = res.getOutputStream(); + final OutboundConnectionHandler handler = new OutboundConnectionHandler(out); + pipeline.addLast("handler", handler); + + Channel channel = channelFactory.newChannel(pipeline); + ChannelFuture future = channel.connect(remoteAddress).awaitUninterruptibly(); + if (!future.isSuccess()) { + Throwable cause = future.getCause(); + if (logger.isWarnEnabled()) { + logger.warn("Endpoint unavailable: " + cause.getMessage(), cause); + } + res.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE); + return; + } + + ChannelFuture lastWriteFuture = null; + try { + res.setStatus(HttpServletResponse.SC_OK); + res.setHeader(HttpHeaders.Names.CONTENT_TYPE, "application/octet-stream"); + res.setHeader(HttpHeaders.Names.CONTENT_TRANSFER_ENCODING, HttpHeaders.Values.BINARY); + + // Initiate chunked encoding by flushing the headers. + out.flush(); + + PushbackInputStream in = + new PushbackInputStream(req.getInputStream()); + while (channel.isConnected()) { + ChannelBuffer buffer; + try { + buffer = read(in); + } catch (EOFException e) { + break; + } + if (buffer == null) { + break; + } + lastWriteFuture = channel.write(buffer); + } + } finally { + if (lastWriteFuture == null) { + channel.close(); + } else { + lastWriteFuture.addListener(ChannelFutureListener.CLOSE); + } + } + } + + private static ChannelBuffer read(PushbackInputStream in) throws IOException { + byte[] buf; + int readBytes; + + int bytesToRead = in.available(); + if (bytesToRead > 0) { + buf = new byte[bytesToRead]; + readBytes = in.read(buf); + } else if (bytesToRead == 0) { + int b = in.read(); + if (b < 0 || in.available() < 0) { + return null; + } + in.unread(b); + bytesToRead = in.available(); + buf = new byte[bytesToRead]; + readBytes = in.read(buf); + } else { + return null; + } + + assert readBytes > 0; + + ChannelBuffer buffer; + if (readBytes == buf.length) { + buffer = ChannelBuffers.wrappedBuffer(buf); + } else { + // A rare case, but it sometimes happen. + buffer = ChannelBuffers.wrappedBuffer(buf, 0, readBytes); + } + return buffer; + } + + private static final class OutboundConnectionHandler extends SimpleChannelUpstreamHandler { + + private final ServletOutputStream out; + + public OutboundConnectionHandler(ServletOutputStream out) { + this.out = out; + } + + @Override + public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception { + ChannelBuffer buffer = (ChannelBuffer) e.getMessage(); + synchronized (this) { + buffer.readBytes(out, buffer.readableBytes()); + out.flush(); + } + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception { + if (logger.isWarnEnabled()) { + logger.warn("Unexpected exception while HTTP tunneling", e.getCause()); + } + e.getChannel().close(); + } + } +} diff --git a/transport-sctp/src/main/java/com/sun/nio/sctp/SctpChannel.java b/transport-sctp/src/main/java/com/sun/nio/sctp/SctpChannel.java index 01ad77c0e3..4d0c906d0e 100644 --- a/transport-sctp/src/main/java/com/sun/nio/sctp/SctpChannel.java +++ b/transport-sctp/src/main/java/com/sun/nio/sctp/SctpChannel.java @@ -52,4 +52,6 @@ public abstract class SctpChannel extends AbstractSelectableChannel { public abstract MessageInfo receive(ByteBuffer dst, T attachment, NotificationHandler handler) throws IOException; public abstract int send(ByteBuffer src, MessageInfo messageInfo) throws IOException; + + public abstract Set> supportedOptions(); } diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/AbstractSctpChannelSink.java b/transport-sctp/src/main/java/io/netty/channel/sctp/AbstractSctpChannelSink.java new file mode 100644 index 0000000000..f01321c2ce --- /dev/null +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/AbstractSctpChannelSink.java @@ -0,0 +1,38 @@ +/* + * Copyright 2011 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.sctp; + +import io.netty.channel.AbstractChannelSink; +import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelPipeline; + +public abstract class AbstractSctpChannelSink extends AbstractChannelSink { + + @Override + public ChannelFuture execute(ChannelPipeline pipeline, final Runnable task) { + Channel ch = pipeline.getChannel(); + if (ch instanceof SctpChannelImpl) { + SctpChannelImpl channel = (SctpChannelImpl) ch; + return channel.worker.executeInIoThread(channel, task); + + } else { + return super.execute(pipeline, task); + } + + } +} diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/NioSctpChannelConfig.java b/transport-sctp/src/main/java/io/netty/channel/sctp/NioSctpChannelConfig.java index 69f8fe791d..c2344793b1 100644 --- a/transport-sctp/src/main/java/io/netty/channel/sctp/NioSctpChannelConfig.java +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/NioSctpChannelConfig.java @@ -17,7 +17,6 @@ package io.netty.channel.sctp; import io.netty.channel.ReceiveBufferSizePredictor; import io.netty.channel.ReceiveBufferSizePredictorFactory; -import io.netty.channel.socket.SocketChannelConfig; /** * A {@link io.netty.channel.sctp.SctpChannelConfig} for a NIO SCTP/IP {@link io.netty.channel.sctp.SctpChannel}. diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/SctpChannel.java b/transport-sctp/src/main/java/io/netty/channel/sctp/SctpChannel.java index 0c9231588a..fd0d70c15b 100644 --- a/transport-sctp/src/main/java/io/netty/channel/sctp/SctpChannel.java +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/SctpChannel.java @@ -18,9 +18,6 @@ package io.netty.channel.sctp; import com.sun.nio.sctp.Association; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; -import io.netty.channel.socket.SocketChannel; -import io.netty.channel.socket.SocketChannelConfig; -import io.netty.channel.socket.nio.NioSocketChannelConfig; import java.net.InetAddress; import java.net.InetSocketAddress; diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/SctpChannelImpl.java b/transport-sctp/src/main/java/io/netty/channel/sctp/SctpChannelImpl.java index e688963c6c..9bfd51e3a7 100644 --- a/transport-sctp/src/main/java/io/netty/channel/sctp/SctpChannelImpl.java +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/SctpChannelImpl.java @@ -257,8 +257,6 @@ class SctpChannelImpl extends AbstractChannel implements SctpChannel { private final class WriteRequestQueue extends AbstractWriteRequestQueue { - private static final long serialVersionUID = -246694024103520626L; - private final ThreadLocalBoolean notifying = new ThreadLocalBoolean(); WriteRequestQueue() { diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/SctpClientPipelineSink.java b/transport-sctp/src/main/java/io/netty/channel/sctp/SctpClientPipelineSink.java index 363407f7d5..1e664a0120 100644 --- a/transport-sctp/src/main/java/io/netty/channel/sctp/SctpClientPipelineSink.java +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/SctpClientPipelineSink.java @@ -32,7 +32,6 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; -import io.netty.channel.AbstractChannelSink; import io.netty.channel.ChannelEvent; import io.netty.channel.ChannelException; import io.netty.channel.ChannelFuture; @@ -48,7 +47,7 @@ import io.netty.util.internal.QueueFactory; /** */ -class SctpClientPipelineSink extends AbstractChannelSink { +class SctpClientPipelineSink extends AbstractSctpChannelSink { static final InternalLogger logger = InternalLoggerFactory.getInstance(SctpClientPipelineSink.class); @@ -262,7 +261,7 @@ class SctpClientPipelineSink extends AbstractChannelSink { wakenUp.set(false); try { - int selectedKeyCount = selector.select(500); + int selectedKeyCount = selector.select(10); // 'wakenUp.compareAndSet(false, true)' is always evaluated // before calling 'selector.wakeup()' to reduce the wake-up @@ -302,9 +301,9 @@ class SctpClientPipelineSink extends AbstractChannelSink { processSelectedKeys(selector.selectedKeys()); } - // Handle connection timeout every 0.5 seconds approximately. + // Handle connection timeout every 10 milliseconds approximately. long currentTimeNanos = System.nanoTime(); - if (currentTimeNanos - lastConnectTimeoutCheckTimeNanos >= 500 * 1000000L) { + if (currentTimeNanos - lastConnectTimeoutCheckTimeNanos >= 10 * 1000000L) { lastConnectTimeoutCheckTimeNanos = currentTimeNanos; processConnectTimeout(selector.keys(), currentTimeNanos); } diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/SctpServerPipelineSink.java b/transport-sctp/src/main/java/io/netty/channel/sctp/SctpServerPipelineSink.java index 3a0f86bb16..c4001af990 100644 --- a/transport-sctp/src/main/java/io/netty/channel/sctp/SctpServerPipelineSink.java +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/SctpServerPipelineSink.java @@ -31,7 +31,6 @@ import java.util.concurrent.atomic.AtomicInteger; import com.sun.nio.sctp.SctpChannel; -import io.netty.channel.AbstractChannelSink; import io.netty.channel.Channel; import io.netty.channel.ChannelEvent; import io.netty.channel.ChannelFuture; @@ -45,7 +44,7 @@ import io.netty.util.internal.DeadLockProofWorker; /** */ -class SctpServerPipelineSink extends AbstractChannelSink { +class SctpServerPipelineSink extends AbstractSctpChannelSink { static final InternalLogger logger = InternalLoggerFactory.getInstance(SctpServerPipelineSink.class); diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/SctpWorker.java b/transport-sctp/src/main/java/io/netty/channel/sctp/SctpWorker.java index 5dc6f5820a..1d0127d047 100644 --- a/transport-sctp/src/main/java/io/netty/channel/sctp/SctpWorker.java +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/SctpWorker.java @@ -31,6 +31,7 @@ import java.util.Queue; import java.util.Set; import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; +import java.util.concurrent.RejectedExecutionException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -45,6 +46,8 @@ import io.netty.channel.ChannelFuture; import io.netty.channel.MessageEvent; import io.netty.channel.ReceiveBufferSizePredictor; import io.netty.channel.sctp.SctpSendBufferPool.SendBuffer; +import io.netty.channel.socket.ChannelRunnableWrapper; +import io.netty.channel.socket.Worker; import io.netty.logging.InternalLogger; import io.netty.logging.InternalLoggerFactory; import io.netty.util.internal.DeadLockProofWorker; @@ -53,7 +56,7 @@ import io.netty.util.internal.QueueFactory; /** */ @SuppressWarnings("unchecked") -class SctpWorker implements Runnable { +class SctpWorker implements Worker { private static final InternalLogger logger = InternalLoggerFactory.getInstance(SctpWorker.class); @@ -64,13 +67,15 @@ class SctpWorker implements Runnable { private final Executor executor; private boolean started; - private volatile Thread thread; + volatile Thread thread; volatile Selector selector; private final AtomicBoolean wakenUp = new AtomicBoolean(); private final ReadWriteLock selectorGuard = new ReentrantReadWriteLock(); private final Object startStopLock = new Object(); private final Queue registerTaskQueue = QueueFactory.createQueue(Runnable.class); private final Queue writeTaskQueue = QueueFactory.createQueue(Runnable.class); + private final Queue eventQueue = QueueFactory.createQueue(Runnable.class); + private volatile int cancelledKeys; // should use AtomicInteger but we just need approximation private final SctpReceiveBufferPool recvBufferPool = new SctpReceiveBufferPool(); @@ -188,6 +193,7 @@ class SctpWorker implements Runnable { cancelledKeys = 0; processRegisterTaskQueue(); + processEventQueue(); processWriteTaskQueue(); processSelectedKeys(selector.selectedKeys()); @@ -240,7 +246,35 @@ class SctpWorker implements Runnable { } } } + + @Override + public ChannelFuture executeInIoThread(Channel channel, Runnable task) { + if (channel instanceof SctpChannelImpl && isIoThread((SctpChannelImpl) channel)) { + try { + task.run(); + return succeededFuture(channel); + } catch (Throwable t) { + return failedFuture(channel, t); + } + } else { + ChannelRunnableWrapper channelRunnable = new ChannelRunnableWrapper(channel, task); + boolean added = eventQueue.offer(channelRunnable); + + if (added) { + // wake up the selector to speed things + selector.wakeup(); + } else { + channelRunnable.setFailure(new RejectedExecutionException("Unable to queue task " + task)); + } + return channelRunnable; + } + } + + static boolean isIoThread(SctpChannelImpl channel) { + return Thread.currentThread() == channel.worker.thread; + } + private void processRegisterTaskQueue() throws IOException { for (; ;) { final Runnable task = registerTaskQueue.poll(); @@ -264,7 +298,18 @@ class SctpWorker implements Runnable { cleanUpCancelledKeys(); } } - + + private void processEventQueue() throws IOException { + for (;;) { + final Runnable task = eventQueue.poll(); + if (task == null) { + break; + } + task.run(); + cleanUpCancelledKeys(); + } + } + private void processSelectedKeys(final Set selectedKeys) throws IOException { for (Iterator i = selectedKeys.iterator(); i.hasNext();) { SelectionKey k = i.next(); diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/handler/SimpleSctpChannelHandler.java b/transport-sctp/src/main/java/io/netty/channel/sctp/handler/SimpleSctpChannelHandler.java index 1a14464999..910fd0093b 100644 --- a/transport-sctp/src/main/java/io/netty/channel/sctp/handler/SimpleSctpChannelHandler.java +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/handler/SimpleSctpChannelHandler.java @@ -19,8 +19,6 @@ import io.netty.channel.ChannelEvent; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelHandler; import io.netty.channel.sctp.SctpNotificationEvent; -import io.netty.logging.InternalLogger; -import io.netty.logging.InternalLoggerFactory; /** * SCTP Channel Handler (upstream + downstream) with SCTP notification handling @@ -28,10 +26,6 @@ import io.netty.logging.InternalLoggerFactory; public class SimpleSctpChannelHandler extends SimpleChannelHandler { - private static final InternalLogger logger = - InternalLoggerFactory.getInstance(SimpleSctpUpstreamHandler.class.getName()); - - @Override public void handleUpstream(ChannelHandlerContext ctx, ChannelEvent event) throws Exception { if (!(event instanceof SctpNotificationEvent)) { diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/handler/SimpleSctpUpstreamHandler.java b/transport-sctp/src/main/java/io/netty/channel/sctp/handler/SimpleSctpUpstreamHandler.java index cf69c86301..37c3696de4 100644 --- a/transport-sctp/src/main/java/io/netty/channel/sctp/handler/SimpleSctpUpstreamHandler.java +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/handler/SimpleSctpUpstreamHandler.java @@ -19,17 +19,12 @@ import io.netty.channel.ChannelEvent; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelUpstreamHandler; import io.netty.channel.sctp.SctpNotificationEvent; -import io.netty.logging.InternalLogger; -import io.netty.logging.InternalLoggerFactory; /** * SCTP Upstream Channel Handler with SCTP notification handling */ public class SimpleSctpUpstreamHandler extends SimpleChannelUpstreamHandler { - private static final InternalLogger logger = - InternalLoggerFactory.getInstance(SimpleSctpUpstreamHandler.class.getName()); - - + @Override public void handleUpstream(ChannelHandlerContext ctx, ChannelEvent event) throws Exception { if (!(event instanceof SctpNotificationEvent)) { diff --git a/transport-sctp/src/test/java/io/netty/testsuite/transport/AbstractSocketClientBootstrapTest.java b/transport-sctp/src/test/java/io/netty/testsuite/transport/AbstractSocketClientBootstrapTest.java index e8ca6d7fd7..509a35398b 100644 --- a/transport-sctp/src/test/java/io/netty/testsuite/transport/AbstractSocketClientBootstrapTest.java +++ b/transport-sctp/src/test/java/io/netty/testsuite/transport/AbstractSocketClientBootstrapTest.java @@ -15,7 +15,6 @@ */ package io.netty.testsuite.transport; -import com.sun.nio.sctp.SctpChannel; import com.sun.nio.sctp.SctpServerChannel; import io.netty.bootstrap.ClientBootstrap; import io.netty.channel.ChannelFactory; @@ -40,7 +39,6 @@ import java.util.concurrent.Executor; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; diff --git a/transport-sctp/src/test/java/io/netty/testsuite/transport/sctp/SctpMultiStreamingEchoTest.java b/transport-sctp/src/test/java/io/netty/testsuite/transport/sctp/SctpMultiStreamingEchoTest.java index 9df1837864..7f8b104db8 100644 --- a/transport-sctp/src/test/java/io/netty/testsuite/transport/sctp/SctpMultiStreamingEchoTest.java +++ b/transport-sctp/src/test/java/io/netty/testsuite/transport/sctp/SctpMultiStreamingEchoTest.java @@ -23,8 +23,6 @@ import io.netty.channel.*; import io.netty.channel.sctp.SctpClientSocketChannelFactory; import io.netty.channel.sctp.SctpFrame; import io.netty.channel.sctp.SctpServerSocketChannelFactory; -import io.netty.channel.sctp.codec.SctpFrameDecoder; -import io.netty.channel.sctp.codec.SctpFrameEncoder; import io.netty.testsuite.util.SctpSocketAddresses; import io.netty.util.internal.ExecutorUtil; import org.junit.AfterClass; diff --git a/transport-sctp/src/test/java/io/netty/testsuite/transport/sctp/SctpServerBootstrapTest.java b/transport-sctp/src/test/java/io/netty/testsuite/transport/sctp/SctpServerBootstrapTest.java index bcb680c1f7..a20c6ae0fe 100644 --- a/transport-sctp/src/test/java/io/netty/testsuite/transport/sctp/SctpServerBootstrapTest.java +++ b/transport-sctp/src/test/java/io/netty/testsuite/transport/sctp/SctpServerBootstrapTest.java @@ -16,7 +16,6 @@ package io.netty.testsuite.transport.sctp; import io.netty.channel.ChannelFactory; -import io.netty.channel.sctp.SctpClientSocketChannelFactory; import io.netty.channel.sctp.SctpServerSocketChannelFactory; import io.netty.testsuite.transport.AbstractSocketServerBootstrapTest; diff --git a/transport/src/main/java/io/netty/channel/AbstractChannelSink.java b/transport/src/main/java/io/netty/channel/AbstractChannelSink.java index a1c27839ff..1210a8b94e 100644 --- a/transport/src/main/java/io/netty/channel/AbstractChannelSink.java +++ b/transport/src/main/java/io/netty/channel/AbstractChannelSink.java @@ -43,7 +43,28 @@ public abstract class AbstractChannelSink implements ChannelSink { if (actualCause == null) { actualCause = cause; } + if (isFireExceptionCaughtLater(event, actualCause)) { + fireExceptionCaughtLater(event.getChannel(), actualCause); + } else { + fireExceptionCaught(event.getChannel(), actualCause); + } + } + + protected boolean isFireExceptionCaughtLater(ChannelEvent event, Throwable actualCause) { + return false; + } - fireExceptionCaught(event.getChannel(), actualCause); + /** + * This implementation just directly call {@link Runnable#run()}. Sub-classes should override this if they can handle it + * in a better way + */ + @Override + public ChannelFuture execute(ChannelPipeline pipeline, Runnable task) { + try { + task.run(); + return Channels.succeededFuture(pipeline.getChannel()); + } catch (Throwable t) { + return Channels.failedFuture(pipeline.getChannel(), t); + } } } diff --git a/transport/src/main/java/io/netty/channel/ChannelDownstreamHandler.java b/transport/src/main/java/io/netty/channel/ChannelDownstreamHandler.java index 6f652c944d..3cfc13157c 100644 --- a/transport/src/main/java/io/netty/channel/ChannelDownstreamHandler.java +++ b/transport/src/main/java/io/netty/channel/ChannelDownstreamHandler.java @@ -56,6 +56,10 @@ package io.netty.channel; *

      * You will also find various helper methods in {@link Channels} to be useful * to generate and send an artificial or manipulated event. + *

      + * Caution: + *

      + * Use the *Later(..) methods of the {@link Channels} class if you want to send an upstream event from a {@link ChannelDownstreamHandler} otherwise you may run into threading issues. * *

      State management

      * diff --git a/transport/src/main/java/io/netty/channel/ChannelPipeline.java b/transport/src/main/java/io/netty/channel/ChannelPipeline.java index cbdeeab097..f69df63fbe 100644 --- a/transport/src/main/java/io/netty/channel/ChannelPipeline.java +++ b/transport/src/main/java/io/netty/channel/ChannelPipeline.java @@ -432,7 +432,6 @@ public interface ChannelPipeline { */ ChannelHandlerContext getContext(Class handlerType); - /** * Sends the specified {@link ChannelEvent} to the first * {@link ChannelUpstreamHandler} in this pipeline. @@ -451,6 +450,12 @@ public interface ChannelPipeline { */ void sendDownstream(ChannelEvent e); + /** + * Schedules the specified task to be executed in the I/O thread associated + * with this pipeline's {@link Channel}. + */ + ChannelFuture execute(Runnable task); + /** * Returns the {@link Channel} that this pipeline is attached to. * diff --git a/transport/src/main/java/io/netty/channel/ChannelSink.java b/transport/src/main/java/io/netty/channel/ChannelSink.java index 86d042e56f..77187f62b6 100644 --- a/transport/src/main/java/io/netty/channel/ChannelSink.java +++ b/transport/src/main/java/io/netty/channel/ChannelSink.java @@ -37,4 +37,10 @@ public interface ChannelSink { * one of its {@link ChannelHandler}s process a {@link ChannelEvent}. */ void exceptionCaught(ChannelPipeline pipeline, ChannelEvent e, ChannelPipelineException cause) throws Exception; + + /** + * Execute the given {@link Runnable} later in the io-thread. + * Some implementation may not support this and just execute it directly. + */ + ChannelFuture execute(ChannelPipeline pipeline, Runnable task); } diff --git a/transport/src/main/java/io/netty/channel/Channels.java b/transport/src/main/java/io/netty/channel/Channels.java index e5852d2ced..1ea98c0827 100644 --- a/transport/src/main/java/io/netty/channel/Channels.java +++ b/transport/src/main/java/io/netty/channel/Channels.java @@ -298,6 +298,22 @@ public final class Channels { ctx.getChannel(), message, remoteAddress)); } + /** + * Sends a {@code "writeComplete"} event to the first + * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of + * the specified {@link Channel} in the next io-thread. + */ + public static ChannelFuture fireWriteCompleteLater(final Channel channel, final long amount) { + return channel.getPipeline().execute(new Runnable() { + @Override + public void run() { + fireWriteComplete(channel, amount); + } + }); + + } + + /** * Sends a {@code "writeComplete"} event to the first * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of @@ -321,6 +337,25 @@ public final class Channels { public static void fireWriteComplete(ChannelHandlerContext ctx, long amount) { ctx.sendUpstream(new DefaultWriteCompletionEvent(ctx.getChannel(), amount)); } + + + + /** + * Sends a {@code "channelInterestChanged"} event to the first + * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of + * the specified {@link Channel} once the io-thread runs again. + */ + public static ChannelFuture fireChannelInterestChangedLater(final Channel channel) { + return channel.getPipeline().execute(new Runnable() { + + @Override + public void run() { + fireChannelInterestChanged(channel); + + } + }); + } + /** * Sends a {@code "channelInterestChanged"} event to the first * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of @@ -346,6 +381,21 @@ public final class Channels { ctx.getChannel(), ChannelState.INTEREST_OPS, Channel.OP_READ)); } + /** + * Sends a {@code "channelDisconnected"} event to the first + * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of + * the specified {@link Channel} once the io-thread runs again. + */ + public static ChannelFuture fireChannelDisconnectedLater(final Channel channel) { + return channel.getPipeline().execute(new Runnable() { + + @Override + public void run() { + fireChannelDisconnected(channel); + } + }); + } + /** * Sends a {@code "channelDisconnected"} event to the first * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of @@ -368,6 +418,23 @@ public final class Channels { ctx.getChannel(), ChannelState.CONNECTED, null)); } + + + /** + * Sends a {@code "channelUnbound"} event to the first + * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of + * the specified {@link Channel} once the io-thread runs again. + */ + public static ChannelFuture fireChannelUnboundLater(final Channel channel) { + return channel.getPipeline().execute(new Runnable() { + + @Override + public void run() { + fireChannelUnbound(channel); + } + }); + } + /** * Sends a {@code "channelUnbound"} event to the first * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of @@ -390,6 +457,24 @@ public final class Channels { ctx.getChannel(), ChannelState.BOUND, null)); } + + + /** + * Sends a {@code "channelClosed"} event to the first + * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of + * the specified {@link Channel} once the io-thread runs again. + */ + public static ChannelFuture fireChannelClosedLater(final Channel channel) { + return channel.getPipeline().execute(new Runnable() { + + @Override + public void run() { + fireChannelClosed(channel); + } + }); + + } + /** * Sends a {@code "channelClosed"} event to the first * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of @@ -418,6 +503,39 @@ public final class Channels { ctx.getChannel(), ChannelState.OPEN, Boolean.FALSE)); } + + + /** + * Sends a {@code "exceptionCaught"} event to the first + * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of + * the specified {@link Channel} once the io-thread runs again. + */ + public static ChannelFuture fireExceptionCaughtLater(final Channel channel, final Throwable cause) { + return channel.getPipeline().execute(new Runnable() { + + @Override + public void run() { + fireExceptionCaught(channel, cause); + } + }); + } + + /** + * Sends a {@code "exceptionCaught"} event to the + * {@link ChannelUpstreamHandler} which is placed in the closest upstream + * from the handler associated with the specified + * {@link ChannelHandlerContext} once the io-thread runs again. + */ + public static ChannelFuture fireExceptionCaughtLater(final ChannelHandlerContext ctx, final Throwable cause) { + return ctx.getPipeline().execute(new Runnable() { + + @Override + public void run() { + fireExceptionCaught(ctx, cause); + } + }); + } + /** * Sends a {@code "exceptionCaught"} event to the first * {@link ChannelUpstreamHandler} in the {@link ChannelPipeline} of @@ -444,6 +562,7 @@ public final class Channels { new DefaultChildChannelStateEvent(channel, childChannel)); } + /** * Sends a {@code "bind"} request to the last * {@link ChannelDownstreamHandler} in the {@link ChannelPipeline} of diff --git a/transport/src/main/java/io/netty/channel/DefaultChannelPipeline.java b/transport/src/main/java/io/netty/channel/DefaultChannelPipeline.java index 50487ae36e..df7e72ecb8 100644 --- a/transport/src/main/java/io/netty/channel/DefaultChannelPipeline.java +++ b/transport/src/main/java/io/netty/channel/DefaultChannelPipeline.java @@ -21,6 +21,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; +import java.util.concurrent.RejectedExecutionException; import io.netty.logging.InternalLogger; import io.netty.logging.InternalLoggerFactory; @@ -649,6 +650,11 @@ public class DefaultChannelPipeline implements ChannelPipeline { return realCtx; } + @Override + public ChannelFuture execute(Runnable task) { + return getSink().execute(this, task); + } + protected void notifyHandlerException(ChannelEvent e, Throwable t) { if (e instanceof ExceptionEvent) { if (logger.isWarnEnabled()) { @@ -832,5 +838,14 @@ public class DefaultChannelPipeline implements ChannelPipeline { ChannelEvent e, ChannelPipelineException cause) throws Exception { throw cause; } + + @Override + public ChannelFuture execute(ChannelPipeline pipeline, Runnable task) { + if (logger.isWarnEnabled()) { + logger.warn("Not attached yet; rejecting: " + task); + } + return Channels.failedFuture(pipeline.getChannel(), new RejectedExecutionException("Not attached yet")); + } + } } diff --git a/transport/src/main/java/io/netty/channel/SimpleChannelDownstreamHandler.java b/transport/src/main/java/io/netty/channel/SimpleChannelDownstreamHandler.java index 2108c07734..f98de0bf90 100644 --- a/transport/src/main/java/io/netty/channel/SimpleChannelDownstreamHandler.java +++ b/transport/src/main/java/io/netty/channel/SimpleChannelDownstreamHandler.java @@ -49,6 +49,12 @@ import java.net.SocketAddress; * super.handleDownstream(ctx, e); * } * } + * + *

      + * Caution: + *

      + * Use the *Later(..) methods of the {@link Channels} class if you want to send an upstream event from a {@link ChannelDownstreamHandler} otherwise you may run into threading issues. + * */ public class SimpleChannelDownstreamHandler implements ChannelDownstreamHandler { diff --git a/transport/src/main/java/io/netty/channel/socket/ChannelRunnableWrapper.java b/transport/src/main/java/io/netty/channel/socket/ChannelRunnableWrapper.java new file mode 100644 index 0000000000..fcf0e3b00f --- /dev/null +++ b/transport/src/main/java/io/netty/channel/socket/ChannelRunnableWrapper.java @@ -0,0 +1,58 @@ +/* + * Copyright 2011 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; + +import io.netty.channel.Channel; +import io.netty.channel.DefaultChannelFuture; + +public class ChannelRunnableWrapper extends DefaultChannelFuture implements Runnable { + + private final Runnable task; + private boolean started; + + public ChannelRunnableWrapper(Channel channel, Runnable task) { + super(channel, true); + this.task = task; + } + + @Override + public void run() { + synchronized (this) { + if (!isCancelled()) { + started = true; + } else { + return; + } + } + try { + task.run(); + setSuccess(); + } catch (Throwable t) { + setFailure(t); + } + } + + @Override + public synchronized boolean cancel() { + if (started) { + return false; + } + return super.cancel(); + } + + + +} diff --git a/transport/src/main/java/io/netty/channel/socket/Worker.java b/transport/src/main/java/io/netty/channel/socket/Worker.java new file mode 100644 index 0000000000..271897881a --- /dev/null +++ b/transport/src/main/java/io/netty/channel/socket/Worker.java @@ -0,0 +1,34 @@ +/* + * Copyright 2011 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; + +import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; + +/** + * A {@link Worker} is responsible to dispatch IO operations + * + */ +public interface Worker extends Runnable { + + /** + * Execute the given {@link Runnable} in the IO-Thread. This may be now or later once the IO-Thread do some other work. + * + * @param task the {@link Runnable} to execute + */ + ChannelFuture executeInIoThread(Channel channel, Runnable task); +} 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 new file mode 100644 index 0000000000..27fe0a8e44 --- /dev/null +++ b/transport/src/main/java/io/netty/channel/socket/nio/AbstractNioChannel.java @@ -0,0 +1,397 @@ +/* + * Copyright 2011 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.nio; + +import static io.netty.channel.Channels.fireChannelInterestChanged; +import io.netty.buffer.ChannelBuffer; +import io.netty.channel.AbstractChannel; +import io.netty.channel.Channel; +import io.netty.channel.ChannelFactory; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.ChannelSink; +import io.netty.channel.MessageEvent; +import io.netty.channel.socket.nio.SocketSendBufferPool.SendBuffer; +import io.netty.util.internal.QueueFactory; +import io.netty.util.internal.ThreadLocalBoolean; + +import java.net.InetSocketAddress; +import java.net.SocketAddress; +import java.nio.channels.SelectableChannel; +import java.nio.channels.WritableByteChannel; +import java.util.Collection; +import java.util.Iterator; +import java.util.Queue; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +abstract class AbstractNioChannel extends AbstractChannel { + + /** + * The {@link AbstractNioWorker}. + */ + final AbstractNioWorker worker; + + /** + * Monitor object to synchronize access to InterestedOps. + */ + final Object interestOpsLock = new Object(); + + /** + * Monitor object for synchronizing access to the {@link WriteRequestQueue}. + */ + final Object writeLock = new Object(); + + /** + * WriteTask that performs write operations. + */ + final Runnable writeTask = new WriteTask(); + + /** + * Indicates if there is a {@link WriteTask} in the task queue. + */ + final AtomicBoolean writeTaskInTaskQueue = new AtomicBoolean(); + + /** + * Queue of write {@link MessageEvent}s. + */ + final Queue writeBufferQueue = new WriteRequestQueue(); + + /** + * Keeps track of the number of bytes that the {@link WriteRequestQueue} currently + * contains. + */ + final AtomicInteger writeBufferSize = new AtomicInteger(); + + /** + * Keeps track of the highWaterMark. + */ + final AtomicInteger highWaterMarkCounter = new AtomicInteger(); + + /** + * The current write {@link MessageEvent} + */ + MessageEvent currentWriteEvent; + SendBuffer currentWriteBuffer; + + /** + * Boolean that indicates that write operation is in progress. + */ + boolean inWriteNowLoop; + boolean writeSuspended; + + + private volatile InetSocketAddress localAddress; + volatile InetSocketAddress remoteAddress; + + final C channel; + + protected AbstractNioChannel(Integer id, Channel parent, ChannelFactory factory, ChannelPipeline pipeline, ChannelSink sink, AbstractNioWorker worker, C ch) { + super(id, parent, factory, pipeline, sink); + this.worker = worker; + this.channel = ch; + } + + protected AbstractNioChannel( + Channel parent, ChannelFactory factory, + ChannelPipeline pipeline, ChannelSink sink, AbstractNioWorker worker, C ch) { + super(parent, factory, pipeline, sink); + this.worker = worker; + this.channel = ch; + } + + @Override + public InetSocketAddress getLocalAddress() { + InetSocketAddress localAddress = this.localAddress; + if (localAddress == null) { + try { + this.localAddress = localAddress = + (InetSocketAddress) getLocalSocketAddress(); + } catch (Throwable t) { + // Sometimes fails on a closed socket in Windows. + return null; + } + } + return localAddress; + } + + @Override + public InetSocketAddress getRemoteAddress() { + InetSocketAddress remoteAddress = this.remoteAddress; + if (remoteAddress == null) { + try { + this.remoteAddress = remoteAddress = + (InetSocketAddress) getRemoteSocketAddress(); + } catch (Throwable t) { + // Sometimes fails on a closed socket in Windows. + return null; + } + } + return remoteAddress; + } + + @Override + public abstract NioChannelConfig getConfig(); + + int getRawInterestOps() { + return super.getInterestOps(); + } + + void setRawInterestOpsNow(int interestOps) { + super.setInterestOpsNow(interestOps); + } + + @Override + public ChannelFuture write(Object message, SocketAddress remoteAddress) { + if (remoteAddress == null || remoteAddress.equals(getRemoteAddress())) { + return super.write(message, null); + } else { + return getUnsupportedOperationFuture(); + } + } + + @Override + public int getInterestOps() { + if (!isOpen()) { + return Channel.OP_WRITE; + } + + int interestOps = getRawInterestOps(); + int writeBufferSize = this.writeBufferSize.get(); + if (writeBufferSize != 0) { + if (highWaterMarkCounter.get() > 0) { + int lowWaterMark = getConfig().getWriteBufferLowWaterMark(); + if (writeBufferSize >= lowWaterMark) { + interestOps |= Channel.OP_WRITE; + } else { + interestOps &= ~Channel.OP_WRITE; + } + } else { + int highWaterMark = getConfig().getWriteBufferHighWaterMark(); + if (writeBufferSize >= highWaterMark) { + interestOps |= Channel.OP_WRITE; + } else { + interestOps &= ~Channel.OP_WRITE; + } + } + } else { + interestOps &= ~Channel.OP_WRITE; + } + + return interestOps; + } + + @Override + protected boolean setClosed() { + return super.setClosed(); + } + + abstract InetSocketAddress getLocalSocketAddress() throws Exception; + + abstract InetSocketAddress getRemoteSocketAddress() throws Exception; + + private final class WriteRequestQueue implements BlockingQueue { + private final ThreadLocalBoolean notifying = new ThreadLocalBoolean(); + + private final BlockingQueue queue; + + public WriteRequestQueue() { + this.queue = QueueFactory.createQueue(MessageEvent.class); + } + + @Override + public MessageEvent remove() { + return queue.remove(); + } + + @Override + public MessageEvent element() { + return queue.element(); + } + + @Override + public MessageEvent peek() { + return queue.peek(); + } + + @Override + public int size() { + return queue.size(); + } + + @Override + public boolean isEmpty() { + return queue.isEmpty(); + } + + @Override + public Iterator iterator() { + return queue.iterator(); + } + + @Override + public Object[] toArray() { + return queue.toArray(); + } + + @Override + public T[] toArray(T[] a) { + return queue.toArray(a); + } + + @Override + public boolean containsAll(Collection c) { + return queue.containsAll(c); + } + + @Override + public boolean addAll(Collection c) { + return queue.addAll(c); + } + + @Override + public boolean removeAll(Collection c) { + return queue.removeAll(c); + } + + @Override + public boolean retainAll(Collection c) { + return queue.retainAll(c); + } + + @Override + public void clear() { + queue.clear(); + } + + @Override + public boolean add(MessageEvent e) { + return queue.add(e); + } + + @Override + public void put(MessageEvent e) throws InterruptedException { + queue.put(e); + } + + @Override + public boolean offer(MessageEvent e, long timeout, TimeUnit unit) throws InterruptedException { + return queue.offer(e, timeout, unit); + } + + @Override + public MessageEvent take() throws InterruptedException { + return queue.take(); + } + + @Override + public MessageEvent poll(long timeout, TimeUnit unit) throws InterruptedException { + return queue.poll(timeout, unit); + } + + @Override + public int remainingCapacity() { + return queue.remainingCapacity(); + } + + @Override + public boolean remove(Object o) { + return queue.remove(o); + } + + @Override + public boolean contains(Object o) { + return queue.contains(o); + } + + @Override + public int drainTo(Collection c) { + return queue.drainTo(c); + } + + @Override + public int drainTo(Collection c, int maxElements) { + return queue.drainTo(c, maxElements); + } + + @Override + public boolean offer(MessageEvent e) { + boolean success = queue.offer(e); + assert success; + + int messageSize = getMessageSize(e); + int newWriteBufferSize = writeBufferSize.addAndGet(messageSize); + int highWaterMark = getConfig().getWriteBufferHighWaterMark(); + + if (newWriteBufferSize >= highWaterMark) { + if (newWriteBufferSize - messageSize < highWaterMark) { + highWaterMarkCounter.incrementAndGet(); + if (!notifying.get()) { + notifying.set(Boolean.TRUE); + fireChannelInterestChanged(AbstractNioChannel.this); + notifying.set(Boolean.FALSE); + } + } + } + return true; + } + + @Override + public MessageEvent poll() { + MessageEvent e = queue.poll(); + if (e != null) { + int messageSize = getMessageSize(e); + int newWriteBufferSize = writeBufferSize.addAndGet(-messageSize); + int lowWaterMark = getConfig().getWriteBufferLowWaterMark(); + + if (newWriteBufferSize == 0 || newWriteBufferSize < lowWaterMark) { + if (newWriteBufferSize + messageSize >= lowWaterMark) { + highWaterMarkCounter.decrementAndGet(); + if (isConnected() && !notifying.get()) { + notifying.set(Boolean.TRUE); + fireChannelInterestChanged(AbstractNioChannel.this); + notifying.set(Boolean.FALSE); + } + } + } + } + return e; + } + + private int getMessageSize(MessageEvent e) { + Object m = e.getMessage(); + if (m instanceof ChannelBuffer) { + return ((ChannelBuffer) m).readableBytes(); + } + return 0; + } + } + + private final class WriteTask implements Runnable { + + WriteTask() { + } + + @Override + public void run() { + writeTaskInTaskQueue.set(false); + worker.writeFromTaskLoop(AbstractNioChannel.this); + } + } + +} diff --git a/transport/src/main/java/io/netty/channel/socket/nio/AbstractNioChannelSink.java b/transport/src/main/java/io/netty/channel/socket/nio/AbstractNioChannelSink.java new file mode 100644 index 0000000000..1a29f5ef19 --- /dev/null +++ b/transport/src/main/java/io/netty/channel/socket/nio/AbstractNioChannelSink.java @@ -0,0 +1,50 @@ +/* + * Copyright 2011 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.nio; + +import io.netty.channel.AbstractChannelSink; +import io.netty.channel.Channel; +import io.netty.channel.ChannelEvent; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelPipeline; + +public abstract class AbstractNioChannelSink extends AbstractChannelSink { + + @Override + public ChannelFuture execute(ChannelPipeline pipeline, final Runnable task) { + Channel ch = pipeline.getChannel(); + if (ch instanceof AbstractNioChannel) { + AbstractNioChannel channel = (AbstractNioChannel) ch; + + return channel.worker.executeInIoThread(ch, task); + } + return super.execute(pipeline, task); + + + } + + @Override + protected boolean isFireExceptionCaughtLater(ChannelEvent event, Throwable actualCause) { + Channel channel = event.getChannel(); + boolean fireLater = false; + if (channel instanceof AbstractNioChannel) { + fireLater = !AbstractNioWorker.isIoThread((AbstractNioChannel) channel); + } + return fireLater; + } + +} diff --git a/transport/src/main/java/io/netty/channel/socket/nio/AbstractNioWorker.java b/transport/src/main/java/io/netty/channel/socket/nio/AbstractNioWorker.java new file mode 100644 index 0000000000..43aa1bca84 --- /dev/null +++ b/transport/src/main/java/io/netty/channel/socket/nio/AbstractNioWorker.java @@ -0,0 +1,785 @@ +/* + * Copyright 2011 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.nio; + +import static io.netty.channel.Channels.*; + +import io.netty.channel.Channel; +import io.netty.channel.ChannelException; +import io.netty.channel.ChannelFuture; +import io.netty.channel.MessageEvent; +import io.netty.channel.socket.ChannelRunnableWrapper; +import io.netty.channel.socket.Worker; +import io.netty.channel.socket.nio.SocketSendBufferPool.SendBuffer; +import io.netty.logging.InternalLogger; +import io.netty.logging.InternalLoggerFactory; +import io.netty.util.internal.DeadLockProofWorker; +import io.netty.util.internal.QueueFactory; + +import java.io.IOException; +import java.nio.channels.AsynchronousCloseException; +import java.nio.channels.CancelledKeyException; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.NotYetConnectedException; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.WritableByteChannel; +import java.util.Iterator; +import java.util.Queue; +import java.util.Set; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.RejectedExecutionException; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +abstract class AbstractNioWorker implements Worker { + /** + * Internal Netty logger. + */ + private static final InternalLogger logger = InternalLoggerFactory + .getInstance(AbstractNioWorker.class); + + private static final int CONSTRAINT_LEVEL = NioProviderMetadata.CONSTRAINT_LEVEL; + + static final int CLEANUP_INTERVAL = 256; // XXX Hard-coded value, but won't need customization. + + + /** + * Executor used to execute {@link Runnable}s such as registration task. + */ + private final Executor executor; + + /** + * Boolean to indicate if this worker has been started. + */ + private boolean started; + + /** + * If this worker has been started thread will be a reference to the thread + * used when starting. i.e. the current thread when the run method is executed. + */ + protected volatile Thread thread; + + /** + * The NIO {@link Selector}. + */ + volatile Selector selector; + + /** + * Boolean that controls determines if a blocked Selector.select should + * break out of its selection process. In our case we use a timeone for + * the select method and the select method will block for that time unless + * waken up. + */ + protected final AtomicBoolean wakenUp = new AtomicBoolean(); + + /** + * Lock for this workers Selector. + */ + private final ReadWriteLock selectorGuard = new ReentrantReadWriteLock(); + + /** + * Monitor object used to synchronize selector open/close. + */ + private final Object startStopLock = new Object(); + + /** + * Queue of channel registration tasks. + */ + private final Queue registerTaskQueue = QueueFactory.createQueue(Runnable.class); + + /** + * Queue of WriteTasks + */ + protected final Queue writeTaskQueue = QueueFactory.createQueue(Runnable.class); + + private final Queue eventQueue = QueueFactory.createQueue(Runnable.class); + + + private volatile int cancelledKeys; // should use AtomicInteger but we just need approximation + + private final SocketSendBufferPool sendBufferPool = new SocketSendBufferPool(); + + AbstractNioWorker(Executor executor) { + this.executor = executor; + } + + void register(AbstractNioChannel channel, ChannelFuture future) { + + Runnable registerTask = createRegisterTask(channel, future); + Selector selector; + + synchronized (startStopLock) { + if (!started) { + // Open a selector if this worker didn't start yet. + try { + this.selector = selector = Selector.open(); + } catch (Throwable t) { + throw new ChannelException("Failed to create a selector.", t); + } + + // Start the worker thread with the new Selector. + boolean success = false; + try { + DeadLockProofWorker.start(executor, this); + success = true; + } finally { + if (!success) { + // Release the Selector if the execution fails. + try { + selector.close(); + } catch (Throwable t) { + logger.warn("Failed to close a selector.", t); + } + this.selector = selector = null; + // The method will return to the caller at this point. + } + } + } else { + // Use the existing selector if this worker has been started. + selector = this.selector; + } + + assert selector != null && selector.isOpen(); + + started = true; + boolean offered = registerTaskQueue.offer(registerTask); + assert offered; + } + + if (wakenUp.compareAndSet(false, true)) { + selector.wakeup(); + } + } + + + @Override + public void run() { + thread = Thread.currentThread(); + + boolean shutdown = false; + Selector selector = this.selector; + for (;;) { + wakenUp.set(false); + + if (CONSTRAINT_LEVEL != 0) { + selectorGuard.writeLock().lock(); + // This empty synchronization block prevents the selector + // from acquiring its lock. + selectorGuard.writeLock().unlock(); + } + + try { + SelectorUtil.select(selector); + + // 'wakenUp.compareAndSet(false, true)' is always evaluated + // before calling 'selector.wakeup()' to reduce the wake-up + // overhead. (Selector.wakeup() is an expensive operation.) + // + // However, there is a race condition in this approach. + // The race condition is triggered when 'wakenUp' is set to + // true too early. + // + // 'wakenUp' is set to true too early if: + // 1) Selector is waken up between 'wakenUp.set(false)' and + // 'selector.select(...)'. (BAD) + // 2) Selector is waken up between 'selector.select(...)' and + // 'if (wakenUp.get()) { ... }'. (OK) + // + // In the first case, 'wakenUp' is set to true and the + // following 'selector.select(...)' will wake up immediately. + // Until 'wakenUp' is set to false again in the next round, + // 'wakenUp.compareAndSet(false, true)' will fail, and therefore + // any attempt to wake up the Selector will fail, too, causing + // the following 'selector.select(...)' call to block + // unnecessarily. + // + // To fix this problem, we wake up the selector again if wakenUp + // is true immediately after selector.select(...). + // It is inefficient in that it wakes up the selector for both + // the first case (BAD - wake-up required) and the second case + // (OK - no wake-up required). + + if (wakenUp.get()) { + selector.wakeup(); + } + + cancelledKeys = 0; + processRegisterTaskQueue(); + processEventQueue(); + processWriteTaskQueue(); + processSelectedKeys(selector.selectedKeys()); + + // Exit the loop when there's nothing to handle. + // The shutdown flag is used to delay the shutdown of this + // loop to avoid excessive Selector creation when + // connections are registered in a one-by-one manner instead of + // concurrent manner. + if (selector.keys().isEmpty()) { + if (shutdown || + executor instanceof ExecutorService && ((ExecutorService) executor).isShutdown()) { + + synchronized (startStopLock) { + if (registerTaskQueue.isEmpty() && selector.keys().isEmpty()) { + started = false; + try { + selector.close(); + } catch (IOException e) { + logger.warn( + "Failed to close a selector.", e); + } finally { + this.selector = null; + } + break; + } else { + shutdown = false; + } + } + } else { + // Give one more second. + shutdown = true; + } + } else { + shutdown = false; + } + } catch (Throwable t) { + logger.warn( + "Unexpected exception in the selector loop.", t); + + // Prevent possible consecutive immediate failures that lead to + // excessive CPU consumption. + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + // Ignore. + } + } + } + } + + @Override + public ChannelFuture executeInIoThread(Channel channel, Runnable task) { + if (channel instanceof AbstractNioChannel && isIoThread((AbstractNioChannel) channel)) { + try { + task.run(); + return succeededFuture(channel); + } catch (Throwable t) { + return failedFuture(channel, t); + } + } else { + ChannelRunnableWrapper channelRunnable = new ChannelRunnableWrapper(channel, task); + boolean added = eventQueue.offer(channelRunnable); + + if (added) { + // wake up the selector to speed things + Selector selector = this.selector; + if (selector != null) { + selector.wakeup(); + } + } else { + channelRunnable.setFailure(new RejectedExecutionException("Unable to queue task " + task)); + } + return channelRunnable; + } + + + } + + private void processRegisterTaskQueue() throws IOException { + for (;;) { + final Runnable task = registerTaskQueue.poll(); + if (task == null) { + break; + } + + task.run(); + cleanUpCancelledKeys(); + } + } + + private void processWriteTaskQueue() throws IOException { + for (;;) { + final Runnable task = writeTaskQueue.poll(); + if (task == null) { + break; + } + + task.run(); + cleanUpCancelledKeys(); + } + } + + private void processEventQueue() throws IOException { + for (;;) { + final Runnable task = eventQueue.poll(); + if (task == null) { + break; + } + task.run(); + cleanUpCancelledKeys(); + } + } + + private void processSelectedKeys(Set selectedKeys) throws IOException { + for (Iterator i = selectedKeys.iterator(); i.hasNext();) { + SelectionKey k = i.next(); + i.remove(); + try { + int readyOps = k.readyOps(); + if ((readyOps & SelectionKey.OP_READ) != 0 || readyOps == 0) { + if (!read(k)) { + // Connection already closed - no need to handle write. + continue; + } + } + if ((readyOps & SelectionKey.OP_WRITE) != 0) { + writeFromSelectorLoop(k); + } + } catch (CancelledKeyException e) { + close(k); + } + + if (cleanUpCancelledKeys()) { + break; // break the loop to avoid ConcurrentModificationException + } + } + } + + private boolean cleanUpCancelledKeys() throws IOException { + if (cancelledKeys >= CLEANUP_INTERVAL) { + cancelledKeys = 0; + selector.selectNow(); + return true; + } + return false; + } + + + + private void close(SelectionKey k) { + AbstractNioChannel ch = (AbstractNioChannel) k.attachment(); + close(ch, succeededFuture(ch)); + } + + void writeFromUserCode(final AbstractNioChannel channel) { + if (!channel.isConnected()) { + cleanUpWriteBuffer(channel); + return; + } + + if (scheduleWriteIfNecessary(channel)) { + return; + } + + // From here, we are sure Thread.currentThread() == workerThread. + + if (channel.writeSuspended) { + return; + } + + if (channel.inWriteNowLoop) { + return; + } + + write0(channel); + } + + void writeFromTaskLoop(AbstractNioChannel ch) { + if (!ch.writeSuspended) { + write0(ch); + } + } + + void writeFromSelectorLoop(final SelectionKey k) { + AbstractNioChannel ch = (AbstractNioChannel) k.attachment(); + ch.writeSuspended = false; + write0(ch); + } + + protected abstract boolean scheduleWriteIfNecessary(final AbstractNioChannel channel); + + + private void write0(AbstractNioChannel channel) { + boolean open = true; + boolean addOpWrite = false; + boolean removeOpWrite = false; + boolean iothread = isIoThread(channel); + + long writtenBytes = 0; + + final SocketSendBufferPool sendBufferPool = this.sendBufferPool; + final WritableByteChannel ch = channel.channel; + final Queue writeBuffer = channel.writeBufferQueue; + final int writeSpinCount = channel.getConfig().getWriteSpinCount(); + synchronized (channel.writeLock) { + channel.inWriteNowLoop = true; + for (;;) { + MessageEvent evt = channel.currentWriteEvent; + SendBuffer buf; + if (evt == null) { + if ((channel.currentWriteEvent = evt = writeBuffer.poll()) == null) { + removeOpWrite = true; + channel.writeSuspended = false; + break; + } + + channel.currentWriteBuffer = buf = sendBufferPool.acquire(evt.getMessage()); + } else { + buf = channel.currentWriteBuffer; + } + + ChannelFuture future = evt.getFuture(); + try { + long localWrittenBytes = 0; + for (int i = writeSpinCount; i > 0; i --) { + localWrittenBytes = buf.transferTo(ch); + if (localWrittenBytes != 0) { + writtenBytes += localWrittenBytes; + break; + } + if (buf.finished()) { + break; + } + } + + if (buf.finished()) { + // Successful write - proceed to the next message. + buf.release(); + channel.currentWriteEvent = null; + channel.currentWriteBuffer = null; + evt = null; + buf = null; + future.setSuccess(); + } else { + // Not written fully - perhaps the kernel buffer is full. + addOpWrite = true; + channel.writeSuspended = true; + + if (localWrittenBytes > 0) { + // Notify progress listeners if necessary. + future.setProgress( + localWrittenBytes, + buf.writtenBytes(), buf.totalBytes()); + } + break; + } + } catch (AsynchronousCloseException e) { + // Doesn't need a user attention - ignore. + } catch (Throwable t) { + if (buf != null) { + buf.release(); + } + channel.currentWriteEvent = null; + channel.currentWriteBuffer = null; + buf = null; + evt = null; + future.setFailure(t); + if (iothread) { + fireExceptionCaught(channel, t); + } else { + fireExceptionCaughtLater(channel, t); + } + if (t instanceof IOException) { + open = false; + close(channel, succeededFuture(channel)); + } + } + } + channel.inWriteNowLoop = false; + + // Initially, the following block was executed after releasing + // the writeLock, but there was a race condition, and it has to be + // executed before releasing the writeLock: + // + // https://issues.jboss.org/browse/NETTY-410 + // + if (open) { + if (addOpWrite) { + setOpWrite(channel); + } else if (removeOpWrite) { + clearOpWrite(channel); + } + } + } + if (iothread) { + fireWriteComplete(channel, writtenBytes); + } else { + fireWriteCompleteLater(channel, writtenBytes); + } + } + + static boolean isIoThread(AbstractNioChannel channel) { + return Thread.currentThread() == channel.worker.thread; + } + + private void setOpWrite(AbstractNioChannel channel) { + Selector selector = this.selector; + SelectionKey key = channel.channel.keyFor(selector); + if (key == null) { + return; + } + if (!key.isValid()) { + close(key); + return; + } + + // interestOps can change at any time and at any thread. + // Acquire a lock to avoid possible race condition. + synchronized (channel.interestOpsLock) { + int interestOps = channel.getRawInterestOps(); + if ((interestOps & SelectionKey.OP_WRITE) == 0) { + interestOps |= SelectionKey.OP_WRITE; + key.interestOps(interestOps); + channel.setRawInterestOpsNow(interestOps); + } + } + } + + private void clearOpWrite(AbstractNioChannel channel) { + Selector selector = this.selector; + SelectionKey key = channel.channel.keyFor(selector); + if (key == null) { + return; + } + if (!key.isValid()) { + close(key); + return; + } + + // interestOps can change at any time and at any thread. + // Acquire a lock to avoid possible race condition. + synchronized (channel.interestOpsLock) { + int interestOps = channel.getRawInterestOps(); + if ((interestOps & SelectionKey.OP_WRITE) != 0) { + interestOps &= ~SelectionKey.OP_WRITE; + key.interestOps(interestOps); + channel.setRawInterestOpsNow(interestOps); + } + } + } + + + void close(AbstractNioChannel channel, ChannelFuture future) { + boolean connected = channel.isConnected(); + boolean bound = channel.isBound(); + boolean iothread = isIoThread(channel); + + try { + channel.channel.close(); + cancelledKeys ++; + + if (channel.setClosed()) { + future.setSuccess(); + if (connected) { + if (iothread) { + fireChannelDisconnected(channel); + } else { + fireChannelDisconnectedLater(channel); + } + } + if (bound) { + if (iothread) { + fireChannelUnbound(channel); + } else { + fireChannelUnboundLater(channel); + } + } + + cleanUpWriteBuffer(channel); + if (iothread) { + fireChannelClosed(channel); + } else { + fireChannelClosedLater(channel); + } + } else { + future.setSuccess(); + } + } catch (Throwable t) { + future.setFailure(t); + if (iothread) { + fireExceptionCaught(channel, t); + } else { + fireExceptionCaughtLater(channel, t); + } + } + } + + private void cleanUpWriteBuffer(AbstractNioChannel channel) { + Exception cause = null; + boolean fireExceptionCaught = false; + + // Clean up the stale messages in the write buffer. + synchronized (channel.writeLock) { + MessageEvent evt = channel.currentWriteEvent; + if (evt != null) { + // Create the exception only once to avoid the excessive overhead + // caused by fillStackTrace. + if (channel.isOpen()) { + cause = new NotYetConnectedException(); + } else { + cause = new ClosedChannelException(); + } + + ChannelFuture future = evt.getFuture(); + channel.currentWriteBuffer.release(); + channel.currentWriteBuffer = null; + channel.currentWriteEvent = null; + evt = null; + future.setFailure(cause); + fireExceptionCaught = true; + } + + Queue writeBuffer = channel.writeBufferQueue; + if (!writeBuffer.isEmpty()) { + // Create the exception only once to avoid the excessive overhead + // caused by fillStackTrace. + if (cause == null) { + if (channel.isOpen()) { + cause = new NotYetConnectedException(); + } else { + cause = new ClosedChannelException(); + } + } + + for (;;) { + evt = writeBuffer.poll(); + if (evt == null) { + break; + } + evt.getFuture().setFailure(cause); + fireExceptionCaught = true; + } + } + } + + if (fireExceptionCaught) { + if (isIoThread(channel)) { + fireExceptionCaught(channel, cause); + } else { + fireExceptionCaughtLater(channel, cause); + } + } + } + + void setInterestOps(AbstractNioChannel channel, ChannelFuture future, int interestOps) { + boolean changed = false; + boolean iothread = isIoThread(channel); + try { + // interestOps can change at any time and at any thread. + // Acquire a lock to avoid possible race condition. + synchronized (channel.interestOpsLock) { + Selector selector = this.selector; + SelectionKey key = channel.channel.keyFor(selector); + + if (key == null || selector == null) { + // Not registered to the worker yet. + // Set the rawInterestOps immediately; RegisterTask will pick it up. + channel.setRawInterestOpsNow(interestOps); + return; + } + + // Override OP_WRITE flag - a user cannot change this flag. + interestOps &= ~Channel.OP_WRITE; + interestOps |= channel.getRawInterestOps() & Channel.OP_WRITE; + + switch (CONSTRAINT_LEVEL) { + case 0: + if (channel.getRawInterestOps() != interestOps) { + key.interestOps(interestOps); + if (Thread.currentThread() != thread && + wakenUp.compareAndSet(false, true)) { + selector.wakeup(); + } + changed = true; + } + break; + case 1: + case 2: + if (channel.getRawInterestOps() != interestOps) { + if (Thread.currentThread() == thread) { + key.interestOps(interestOps); + changed = true; + } else { + selectorGuard.readLock().lock(); + try { + if (wakenUp.compareAndSet(false, true)) { + selector.wakeup(); + } + key.interestOps(interestOps); + changed = true; + } finally { + selectorGuard.readLock().unlock(); + } + } + } + break; + default: + throw new Error(); + } + + if (changed) { + channel.setRawInterestOpsNow(interestOps); + } + } + + future.setSuccess(); + if (changed) { + if (iothread) { + fireChannelInterestChanged(channel); + } else { + fireChannelInterestChangedLater(channel); + } + } + } catch (CancelledKeyException e) { + // setInterestOps() was called on a closed channel. + ClosedChannelException cce = new ClosedChannelException(); + future.setFailure(cce); + if (iothread) { + fireExceptionCaught(channel, cce); + } else { + fireExceptionCaughtLater(channel, cce); + } + } catch (Throwable t) { + future.setFailure(t); + if (iothread) { + fireExceptionCaught(channel, t); + } else { + fireExceptionCaughtLater(channel, t); + } + } + } + + /** + * Read is called when a Selector has been notified that the underlying channel + * was something to be read. The channel would previously have registered its interest + * in read operations. + * + * @param k The selection key which contains the Selector registration information. + */ + protected abstract boolean read(SelectionKey k); + + /** + * Create a new {@link Runnable} which will register the {@link AbstractNioWorker} with the {@link Channel} + * + * @param channel + * @param future + * @return task + */ + protected abstract Runnable createRegisterTask(AbstractNioChannel channel, ChannelFuture future); + +} diff --git a/transport/src/main/java/io/netty/channel/socket/nio/AbstractWriteRequestQueue.java b/transport/src/main/java/io/netty/channel/socket/nio/AbstractWriteRequestQueue.java deleted file mode 100644 index b9a848ead1..0000000000 --- a/transport/src/main/java/io/netty/channel/socket/nio/AbstractWriteRequestQueue.java +++ /dev/null @@ -1,149 +0,0 @@ -/* - * Copyright 2011 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.nio; - -import java.util.Collection; -import java.util.Iterator; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.TimeUnit; - -import io.netty.channel.MessageEvent; -import io.netty.util.internal.QueueFactory; - -abstract class AbstractWriteRequestQueue implements BlockingQueue { - - protected final BlockingQueue queue; - - public AbstractWriteRequestQueue() { - this.queue = QueueFactory.createQueue(MessageEvent.class); - } - - @Override - public MessageEvent remove() { - return queue.remove(); - } - - @Override - public MessageEvent element() { - return queue.element(); - } - - @Override - public MessageEvent peek() { - return queue.peek(); - } - - @Override - public int size() { - return queue.size(); - } - - @Override - public boolean isEmpty() { - return queue.isEmpty(); - } - - @Override - public Iterator iterator() { - return queue.iterator(); - } - - @Override - public Object[] toArray() { - return queue.toArray(); - } - - @Override - public T[] toArray(T[] a) { - return queue.toArray(a); - } - - @Override - public boolean containsAll(Collection c) { - return queue.containsAll(c); - } - - @Override - public boolean addAll(Collection c) { - return queue.addAll(c); - } - - @Override - public boolean removeAll(Collection c) { - return queue.removeAll(c); - } - - @Override - public boolean retainAll(Collection c) { - return queue.retainAll(c); - } - - @Override - public void clear() { - queue.clear(); - } - - @Override - public boolean add(MessageEvent e) { - return queue.add(e); - } - - @Override - public void put(MessageEvent e) throws InterruptedException { - queue.put(e); - } - - @Override - public boolean offer(MessageEvent e, long timeout, TimeUnit unit) throws InterruptedException { - return queue.offer(e, timeout, unit); - } - - @Override - public MessageEvent take() throws InterruptedException { - return queue.take(); - } - - @Override - public MessageEvent poll(long timeout, TimeUnit unit) throws InterruptedException { - return queue.poll(timeout, unit); - } - - @Override - public int remainingCapacity() { - return queue.remainingCapacity(); - } - - @Override - public boolean remove(Object o) { - return queue.remove(o); - } - - @Override - public boolean contains(Object o) { - return queue.contains(o); - } - - @Override - public int drainTo(Collection c) { - return queue.drainTo(c); - } - - @Override - public int drainTo(Collection c, int maxElements) { - return queue.drainTo(c, maxElements); - } - -} diff --git a/transport/src/main/java/io/netty/channel/socket/nio/NioClientSocketPipelineSink.java b/transport/src/main/java/io/netty/channel/socket/nio/NioClientSocketPipelineSink.java index 32ffb4d5ce..512d17da00 100644 --- a/transport/src/main/java/io/netty/channel/socket/nio/NioClientSocketPipelineSink.java +++ b/transport/src/main/java/io/netty/channel/socket/nio/NioClientSocketPipelineSink.java @@ -31,7 +31,6 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; -import io.netty.channel.AbstractChannelSink; import io.netty.channel.ChannelEvent; import io.netty.channel.ChannelException; import io.netty.channel.ChannelFuture; @@ -45,7 +44,7 @@ import io.netty.logging.InternalLoggerFactory; import io.netty.util.internal.DeadLockProofWorker; import io.netty.util.internal.QueueFactory; -class NioClientSocketPipelineSink extends AbstractChannelSink { +class NioClientSocketPipelineSink extends AbstractNioChannelSink { static final InternalLogger logger = InternalLoggerFactory.getInstance(NioClientSocketPipelineSink.class); @@ -113,7 +112,7 @@ class NioClientSocketPipelineSink extends AbstractChannelSink { } else if (e instanceof MessageEvent) { MessageEvent event = (MessageEvent) e; NioSocketChannel channel = (NioSocketChannel) event.getChannel(); - boolean offered = channel.writeBuffer.offer(event); + boolean offered = channel.writeBufferQueue.offer(event); assert offered; channel.worker.writeFromUserCode(channel); } @@ -123,7 +122,7 @@ class NioClientSocketPipelineSink extends AbstractChannelSink { NioClientSocketChannel channel, ChannelFuture future, SocketAddress localAddress) { try { - channel.socket.socket().bind(localAddress); + channel.channel.socket().bind(localAddress); channel.boundManually = true; channel.setBound(); future.setSuccess(); @@ -138,7 +137,7 @@ class NioClientSocketPipelineSink extends AbstractChannelSink { final NioClientSocketChannel channel, final ChannelFuture cf, SocketAddress remoteAddress) { try { - if (channel.socket.connect(remoteAddress)) { + if (channel.channel.connect(remoteAddress)) { channel.worker.register(channel, cf); } else { channel.getCloseFuture().addListener(new ChannelFutureListener() { @@ -242,7 +241,7 @@ class NioClientSocketPipelineSink extends AbstractChannelSink { wakenUp.set(false); try { - int selectedKeyCount = selector.select(500); + int selectedKeyCount = selector.select(10); // 'wakenUp.compareAndSet(false, true)' is always evaluated // before calling 'selector.wakeup()' to reduce the wake-up @@ -282,9 +281,9 @@ class NioClientSocketPipelineSink extends AbstractChannelSink { processSelectedKeys(selector.selectedKeys()); } - // Handle connection timeout every 0.5 seconds approximately. + // Handle connection timeout every 10 milliseconds approximately. long currentTimeNanos = System.nanoTime(); - if (currentTimeNanos - lastConnectTimeoutCheckTimeNanos >= 500 * 1000000L) { + if (currentTimeNanos - lastConnectTimeoutCheckTimeNanos >= 10 * 1000000L) { lastConnectTimeoutCheckTimeNanos = currentTimeNanos; processConnectTimeout(selector.keys(), currentTimeNanos); } @@ -400,7 +399,7 @@ class NioClientSocketPipelineSink extends AbstractChannelSink { private void connect(SelectionKey k) { NioClientSocketChannel ch = (NioClientSocketChannel) k.attachment(); try { - if (ch.socket.finishConnect()) { + if (ch.channel.finishConnect()) { k.cancel(); ch.worker.register(ch, ch.connectFuture); } @@ -430,7 +429,7 @@ class NioClientSocketPipelineSink extends AbstractChannelSink { @Override public void run() { try { - channel.socket.register( + channel.channel.register( boss.selector, SelectionKey.OP_CONNECT, channel); } catch (ClosedChannelException e) { channel.worker.close(channel, succeededFuture(channel)); 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 89d54527cc..c8dcc77813 100644 --- a/transport/src/main/java/io/netty/channel/socket/nio/NioDatagramChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/nio/NioDatagramChannel.java @@ -15,36 +15,23 @@ */ package io.netty.channel.socket.nio; -import static io.netty.channel.Channels.*; +import static io.netty.channel.Channels.fireChannelOpen; +import io.netty.channel.ChannelException; +import io.netty.channel.ChannelFactory; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.ChannelSink; +import io.netty.channel.socket.DatagramChannelConfig; import java.io.IOException; import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.NetworkInterface; -import java.net.SocketAddress; import java.nio.channels.DatagramChannel; -import java.util.Queue; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; - -import io.netty.buffer.ChannelBuffer; -import io.netty.channel.AbstractChannel; -import io.netty.channel.Channel; -import io.netty.channel.ChannelException; -import io.netty.channel.ChannelFactory; -import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.ChannelSink; -import io.netty.channel.MessageEvent; -import io.netty.channel.socket.DatagramChannelConfig; -import io.netty.channel.socket.nio.SocketSendBufferPool.SendBuffer; -import io.netty.util.internal.LegacyLinkedTransferQueue; -import io.netty.util.internal.ThreadLocalBoolean; /** * Provides an NIO based {@link io.netty.channel.socket.DatagramChannel}. */ -final class NioDatagramChannel extends AbstractChannel +final class NioDatagramChannel extends AbstractNioChannel implements io.netty.channel.socket.DatagramChannel { /** @@ -52,67 +39,7 @@ final class NioDatagramChannel extends AbstractChannel */ private final NioDatagramChannelConfig config; - /** - * The {@link NioDatagramWorker} for this NioDatagramChannnel. - */ - final NioDatagramWorker worker; - - /** - * The {@link DatagramChannel} that this channel uses. - */ - private final java.nio.channels.DatagramChannel datagramChannel; - - /** - * Monitor object to synchronize access to InterestedOps. - */ - final Object interestOpsLock = new Object(); - - /** - * Monitor object for synchronizing access to the {@link WriteRequestQueue}. - */ - final Object writeLock = new Object(); - - /** - * WriteTask that performs write operations. - */ - final Runnable writeTask = new WriteTask(); - - /** - * Indicates if there is a {@link WriteTask} in the task queue. - */ - final AtomicBoolean writeTaskInTaskQueue = new AtomicBoolean(); - - /** - * Queue of write {@link MessageEvent}s. - */ - final Queue writeBufferQueue = new WriteRequestQueue(); - - /** - * Keeps track of the number of bytes that the {@link WriteRequestQueue} currently - * contains. - */ - final AtomicInteger writeBufferSize = new AtomicInteger(); - - /** - * Keeps track of the highWaterMark. - */ - final AtomicInteger highWaterMarkCounter = new AtomicInteger(); - - /** - * The current write {@link MessageEvent} - */ - MessageEvent currentWriteEvent; - SendBuffer currentWriteBuffer; - - /** - * Boolean that indicates that write operation is in progress. - */ - boolean inWriteNowLoop; - boolean writeSuspended; - - private volatile InetSocketAddress localAddress; - volatile InetSocketAddress remoteAddress; - + static NioDatagramChannel create(ChannelFactory factory, ChannelPipeline pipeline, ChannelSink sink, NioDatagramWorker worker) { NioDatagramChannel instance = @@ -124,13 +51,11 @@ final class NioDatagramChannel extends AbstractChannel private NioDatagramChannel(final ChannelFactory factory, final ChannelPipeline pipeline, final ChannelSink sink, final NioDatagramWorker worker) { - super(null, factory, pipeline, sink); - this.worker = worker; - datagramChannel = openNonBlockingChannel(); - config = new DefaultNioDatagramChannelConfig(datagramChannel.socket()); + super(null, factory, pipeline, sink, worker, openNonBlockingChannel()); + config = new DefaultNioDatagramChannelConfig(channel.socket()); } - private DatagramChannel openNonBlockingChannel() { + private static DatagramChannel openNonBlockingChannel() { try { final DatagramChannel channel = DatagramChannel.open(); channel.configureBlocking(false); @@ -140,44 +65,15 @@ final class NioDatagramChannel extends AbstractChannel } } - @Override - public InetSocketAddress getLocalAddress() { - InetSocketAddress localAddress = this.localAddress; - if (localAddress == null) { - try { - this.localAddress = localAddress = - (InetSocketAddress) datagramChannel.socket().getLocalSocketAddress(); - } catch (Throwable t) { - // Sometimes fails on a closed socket in Windows. - return null; - } - } - return localAddress; - } - - @Override - public InetSocketAddress getRemoteAddress() { - InetSocketAddress remoteAddress = this.remoteAddress; - if (remoteAddress == null) { - try { - this.remoteAddress = remoteAddress = - (InetSocketAddress) datagramChannel.socket().getRemoteSocketAddress(); - } catch (Throwable t) { - // Sometimes fails on a closed socket in Windows. - return null; - } - } - return remoteAddress; - } @Override public boolean isBound() { - return isOpen() && datagramChannel.socket().isBound(); + return isOpen() && channel.socket().isBound(); } @Override public boolean isConnected() { - return datagramChannel.isConnected(); + return channel.isConnected(); } @Override @@ -191,140 +87,7 @@ final class NioDatagramChannel extends AbstractChannel } DatagramChannel getDatagramChannel() { - return datagramChannel; - } - - @Override - public int getInterestOps() { - if (!isOpen()) { - return Channel.OP_WRITE; - } - - int interestOps = getRawInterestOps(); - int writeBufferSize = this.writeBufferSize.get(); - if (writeBufferSize != 0) { - if (highWaterMarkCounter.get() > 0) { - int lowWaterMark = getConfig().getWriteBufferLowWaterMark(); - if (writeBufferSize >= lowWaterMark) { - interestOps |= Channel.OP_WRITE; - } else { - interestOps &= ~Channel.OP_WRITE; - } - } else { - int highWaterMark = getConfig().getWriteBufferHighWaterMark(); - if (writeBufferSize >= highWaterMark) { - interestOps |= Channel.OP_WRITE; - } else { - interestOps &= ~Channel.OP_WRITE; - } - } - } else { - interestOps &= ~Channel.OP_WRITE; - } - - return interestOps; - } - - int getRawInterestOps() { - return super.getInterestOps(); - } - - void setRawInterestOpsNow(int interestOps) { - super.setInterestOpsNow(interestOps); - } - - @Override - public ChannelFuture write(Object message, SocketAddress remoteAddress) { - if (remoteAddress == null || remoteAddress.equals(getRemoteAddress())) { - return super.write(message, null); - } else { - return super.write(message, remoteAddress); - } - } - - /** - * {@link WriteRequestQueue} is an extension of {@link AbstractWriteRequestQueue} - * that adds support for highWaterMark checking of the write buffer size. - */ - private final class WriteRequestQueue extends - AbstractWriteRequestQueue { - - private final ThreadLocalBoolean notifying = new ThreadLocalBoolean(); - - - /** - * This method first delegates to {@link LegacyLinkedTransferQueue#offer(Object)} and - * adds support for keeping track of the size of the this write buffer. - */ - @Override - public boolean offer(MessageEvent e) { - boolean success = queue.offer(e); - assert success; - - int messageSize = getMessageSize(e); - int newWriteBufferSize = writeBufferSize.addAndGet(messageSize); - int highWaterMark = getConfig().getWriteBufferHighWaterMark(); - - if (newWriteBufferSize >= highWaterMark) { - if (newWriteBufferSize - messageSize < highWaterMark) { - highWaterMarkCounter.incrementAndGet(); - if (!notifying.get()) { - notifying.set(Boolean.TRUE); - fireChannelInterestChanged(NioDatagramChannel.this); - notifying.set(Boolean.FALSE); - } - } - } - return true; - } - - /** - * This method first delegates to {@link LegacyLinkedTransferQueue#poll()} and - * adds support for keeping track of the size of the this writebuffers queue. - */ - @Override - public MessageEvent poll() { - MessageEvent e = queue.poll(); - if (e != null) { - int messageSize = getMessageSize(e); - int newWriteBufferSize = writeBufferSize.addAndGet(-messageSize); - int lowWaterMark = getConfig().getWriteBufferLowWaterMark(); - - if (newWriteBufferSize == 0 || newWriteBufferSize < lowWaterMark) { - if (newWriteBufferSize + messageSize >= lowWaterMark) { - highWaterMarkCounter.decrementAndGet(); - if (isBound() && !notifying.get()) { - notifying.set(Boolean.TRUE); - fireChannelInterestChanged(NioDatagramChannel.this); - notifying.set(Boolean.FALSE); - } - } - } - } - return e; - } - - private int getMessageSize(MessageEvent e) { - Object m = e.getMessage(); - if (m instanceof ChannelBuffer) { - return ((ChannelBuffer) m).readableBytes(); - } - return 0; - } - } - - /** - * WriteTask is a simple runnable performs writes by delegating the {@link NioDatagramWorker}. - */ - private final class WriteTask implements Runnable { - WriteTask() { - } - - @Override - public void run() { - writeTaskInTaskQueue.set(false); - worker.writeFromTaskLoop(NioDatagramChannel.this); - } + return channel; } @Override @@ -348,4 +111,14 @@ final class NioDatagramChannel extends AbstractChannel NetworkInterface networkInterface) { throw new UnsupportedOperationException(); } + + @Override + InetSocketAddress getLocalSocketAddress() throws Exception { + return (InetSocketAddress) channel.socket().getLocalSocketAddress(); + } + + @Override + InetSocketAddress getRemoteSocketAddress() throws Exception { + return (InetSocketAddress) channel.socket().getRemoteSocketAddress(); + } } diff --git a/transport/src/main/java/io/netty/channel/socket/nio/NioDatagramPipelineSink.java b/transport/src/main/java/io/netty/channel/socket/nio/NioDatagramPipelineSink.java index 60b6943725..401d6dbf8b 100644 --- a/transport/src/main/java/io/netty/channel/socket/nio/NioDatagramPipelineSink.java +++ b/transport/src/main/java/io/netty/channel/socket/nio/NioDatagramPipelineSink.java @@ -22,7 +22,6 @@ import java.net.SocketAddress; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicInteger; -import io.netty.channel.AbstractChannelSink; import io.netty.channel.ChannelEvent; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; @@ -35,7 +34,7 @@ import io.netty.channel.MessageEvent; * Receives downstream events from a {@link ChannelPipeline}. It contains * an array of I/O workers. */ -class NioDatagramPipelineSink extends AbstractChannelSink { +class NioDatagramPipelineSink extends AbstractNioChannelSink { private final NioDatagramWorker[] workers; private final AtomicInteger workerIndex = new AtomicInteger(); diff --git a/transport/src/main/java/io/netty/channel/socket/nio/NioDatagramWorker.java b/transport/src/main/java/io/netty/channel/socket/nio/NioDatagramWorker.java index 1e8583e856..6191df2e08 100644 --- a/transport/src/main/java/io/netty/channel/socket/nio/NioDatagramWorker.java +++ b/transport/src/main/java/io/netty/channel/socket/nio/NioDatagramWorker.java @@ -15,102 +15,30 @@ */ package io.netty.channel.socket.nio; -import static io.netty.channel.Channels.*; - -import java.io.IOException; -import java.net.SocketAddress; -import java.nio.ByteBuffer; -import java.nio.channels.AsynchronousCloseException; -import java.nio.channels.CancelledKeyException; -import java.nio.channels.ClosedChannelException; -import java.nio.channels.DatagramChannel; -import java.nio.channels.NotYetBoundException; -import java.nio.channels.SelectionKey; -import java.nio.channels.Selector; -import java.util.Iterator; -import java.util.Queue; -import java.util.Set; -import java.util.concurrent.Executor; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.locks.ReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; - +import static io.netty.channel.Channels.fireChannelDisconnected; +import static io.netty.channel.Channels.fireChannelDisconnectedLater; +import static io.netty.channel.Channels.fireExceptionCaught; +import static io.netty.channel.Channels.fireExceptionCaughtLater; +import static io.netty.channel.Channels.fireMessageReceived; +import static io.netty.channel.Channels.succeededFuture; import io.netty.buffer.ChannelBufferFactory; -import io.netty.channel.Channel; import io.netty.channel.ChannelException; import io.netty.channel.ChannelFuture; -import io.netty.channel.MessageEvent; import io.netty.channel.ReceiveBufferSizePredictor; -import io.netty.channel.socket.nio.SocketSendBufferPool.SendBuffer; -import io.netty.logging.InternalLogger; -import io.netty.logging.InternalLoggerFactory; -import io.netty.util.internal.QueueFactory; + +import java.net.SocketAddress; +import java.nio.ByteBuffer; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.DatagramChannel; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.util.concurrent.Executor; /** * A class responsible for registering channels with {@link Selector}. * It also implements the {@link Selector} loop. */ -class NioDatagramWorker implements Runnable { - /** - * Internal Netty logger. - */ - private static final InternalLogger logger = InternalLoggerFactory - .getInstance(NioDatagramWorker.class); - - /** - * Executor used to execute {@link Runnable}s such as - * {@link ChannelRegistionTask}. - */ - private final Executor executor; - - /** - * Boolean to indicate if this worker has been started. - */ - private boolean started; - - /** - * If this worker has been started thread will be a reference to the thread - * used when starting. i.e. the current thread when the run method is executed. - */ - private volatile Thread thread; - - /** - * The NIO {@link Selector}. - */ - volatile Selector selector; - - /** - * Boolean that controls determines if a blocked Selector.select should - * break out of its selection process. In our case we use a timeone for - * the select method and the select method will block for that time unless - * waken up. - */ - private final AtomicBoolean wakenUp = new AtomicBoolean(); - - /** - * Lock for this workers Selector. - */ - private final ReadWriteLock selectorGuard = new ReentrantReadWriteLock(); - - /** - * Monitor object used to synchronize selector open/close. - */ - private final Object startStopLock = new Object(); - - /** - * Queue of {@link ChannelRegistionTask}s - */ - private final Queue registerTaskQueue = QueueFactory.createQueue(Runnable.class); - - /** - * Queue of WriteTasks - */ - private final Queue writeTaskQueue = QueueFactory.createQueue(Runnable.class); - - private volatile int cancelledKeys; // should use AtomicInteger but we just need approximation - - private final SocketSendBufferPool sendBufferPool = new SocketSendBufferPool(); +class NioDatagramWorker extends AbstractNioWorker { /** * Sole constructor. @@ -119,248 +47,11 @@ class NioDatagramWorker implements Runnable { * such as {@link ChannelRegistionTask} */ NioDatagramWorker(final Executor executor) { - this.executor = executor; + super(executor); } - /** - * Registers the passed-in channel with a selector. - * - * @param channel the channel to register - * @param future the {@link ChannelFuture} that has to be notified on - * completion - */ - void register(final NioDatagramChannel channel, final ChannelFuture future) { - final Runnable channelRegTask = new ChannelRegistionTask(channel, - future); - Selector selector; - - synchronized (startStopLock) { - if (!started) { - // Open a selector if this worker didn't start yet. - try { - this.selector = selector = Selector.open(); - } catch (final Throwable t) { - throw new ChannelException("Failed to create a selector.", - t); - } - - boolean success = false; - try { - // Start the main selector loop. See run() for details. - executor.execute(this); - success = true; - } finally { - if (!success) { - try { - // Release the Selector if the execution fails. - selector.close(); - } catch (final Throwable t) { - if (logger.isWarnEnabled()) { - logger.warn("Failed to close a selector.", t); - } - } - this.selector = selector = null; - // The method will return to the caller at this point. - } - } - } else { - // Use the existing selector if this worker has been started. - selector = this.selector; - } - assert selector != null && selector.isOpen(); - - started = true; - - // "Add" the registration task to the register task queue. - boolean offered = registerTaskQueue.offer(channelRegTask); - assert offered; - } - - if (wakenUp.compareAndSet(false, true)) { - selector.wakeup(); - } - } - - /** - * Selector loop. - */ @Override - public void run() { - // Store a ref to the current thread. - thread = Thread.currentThread(); - - final Selector selector = this.selector; - boolean shutdown = false; - - for (;;) { - wakenUp.set(false); - - if (NioProviderMetadata.CONSTRAINT_LEVEL != 0) { - selectorGuard.writeLock().lock(); - // This empty synchronization block prevents the selector from acquiring its lock. - selectorGuard.writeLock().unlock(); - } - - try { - SelectorUtil.select(selector); - - // 'wakenUp.compareAndSet(false, true)' is always evaluated - // before calling 'selector.wakeup()' to reduce the wake-up - // overhead. (Selector.wakeup() is an expensive operation.) - // - // However, there is a race condition in this approach. - // The race condition is triggered when 'wakenUp' is set to - // true too early. - // - // 'wakenUp' is set to true too early if: - // 1) Selector is waken up between 'wakenUp.set(false)' and - // 'selector.select(...)'. (BAD) - // 2) Selector is waken up between 'selector.select(...)' and - // 'if (wakenUp.get()) { ... }'. (OK) - // - // In the first case, 'wakenUp' is set to true and the - // following 'selector.select(...)' will wake up immediately. - // Until 'wakenUp' is set to false again in the next round, - // 'wakenUp.compareAndSet(false, true)' will fail, and therefore - // any attempt to wake up the Selector will fail, too, causing - // the following 'selector.select(...)' call to block - // unnecessarily. - // - // To fix this problem, we wake up the selector again if wakenUp - // is true immediately after selector.select(...). - // It is inefficient in that it wakes up the selector for both - // the first case (BAD - wake-up required) and the second case - // (OK - no wake-up required). - - if (wakenUp.get()) { - selector.wakeup(); - } - - cancelledKeys = 0; - processRegisterTaskQueue(); - processWriteTaskQueue(); - processSelectedKeys(selector.selectedKeys()); - - // Exit the loop when there's nothing to handle (the registered - // key set is empty. - // The shutdown flag is used to delay the shutdown of this - // loop to avoid excessive Selector creation when - // connections are registered in a one-by-one manner instead of - // concurrent manner. - if (selector.keys().isEmpty()) { - if (shutdown || executor instanceof ExecutorService && - ((ExecutorService) executor).isShutdown()) { - synchronized (startStopLock) { - if (registerTaskQueue.isEmpty() && - selector.keys().isEmpty()) { - started = false; - try { - selector.close(); - } catch (IOException e) { - logger.warn("Failed to close a selector.", - e); - } finally { - this.selector = null; - } - break; - } else { - shutdown = false; - } - } - } else { - // Give one more second. - shutdown = true; - } - } else { - shutdown = false; - } - } catch (Throwable t) { - logger.warn("Unexpected exception in the selector loop.", t); - - // Prevent possible consecutive immediate failures that lead to - // excessive CPU consumption. - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - // Ignore. - } - } - } - } - - /** - * Will go through all the {@link ChannelRegistionTask}s in the - * task queue and run them (registering them). - */ - private void processRegisterTaskQueue() throws IOException { - for (;;) { - final Runnable task = registerTaskQueue.poll(); - if (task == null) { - break; - } - - task.run(); - cleanUpCancelledKeys(); - } - } - - /** - * Will go through all the WriteTasks and run them. - */ - private void processWriteTaskQueue() throws IOException { - for (;;) { - final Runnable task = writeTaskQueue.poll(); - if (task == null) { - break; - } - - task.run(); - cleanUpCancelledKeys(); - } - } - - private void processSelectedKeys(final Set selectedKeys) throws IOException { - for (Iterator i = selectedKeys.iterator(); i.hasNext();) { - SelectionKey k = i.next(); - i.remove(); - try { - int readyOps = k.readyOps(); - if ((readyOps & SelectionKey.OP_READ) != 0 || readyOps == 0) { - if (!read(k)) { - // Connection already closed - no need to handle write. - continue; - } - } - if ((readyOps & SelectionKey.OP_WRITE) != 0) { - writeFromSelectorLoop(k); - } - } catch (CancelledKeyException e) { - close(k); - } - - if (cleanUpCancelledKeys()) { - break; // Break the loop to avoid ConcurrentModificationException - } - } - } - - private boolean cleanUpCancelledKeys() throws IOException { - if (cancelledKeys >= NioWorker.CLEANUP_INTERVAL) { - cancelledKeys = 0; - selector.selectNow(); - return true; - } - return false; - } - - /** - * Read is called when a Selector has been notified that the underlying channel - * was something to be read. The channel would previously have registered its interest - * in read operations. - * - * @param key The selection key which contains the Selector registration information. - */ - private boolean read(final SelectionKey key) { + protected boolean read(final SelectionKey key) { final NioDatagramChannel channel = (NioDatagramChannel) key.attachment(); ReceiveBufferSizePredictor predictor = channel.getConfig().getReceiveBufferSizePredictor(); @@ -410,53 +101,10 @@ class NioDatagramWorker implements Runnable { return true; } + - private void close(SelectionKey k) { - final NioDatagramChannel ch = (NioDatagramChannel) k.attachment(); - close(ch, succeededFuture(ch)); - } - - void writeFromUserCode(final NioDatagramChannel channel) { - /* - * Note that we are not checking if the channel is connected. Connected - * has a different meaning in UDP and means that the channels socket is - * configured to only send and receive from a given remote peer. - */ - if (!channel.isBound()) { - cleanUpWriteBuffer(channel); - return; - } - - if (scheduleWriteIfNecessary(channel)) { - return; - } - - // From here, we are sure Thread.currentThread() == workerThread. - - if (channel.writeSuspended) { - return; - } - - if (channel.inWriteNowLoop) { - return; - } - - write0(channel); - } - - void writeFromTaskLoop(final NioDatagramChannel ch) { - if (!ch.writeSuspended) { - write0(ch); - } - } - - void writeFromSelectorLoop(final SelectionKey k) { - NioDatagramChannel ch = (NioDatagramChannel) k.attachment(); - ch.writeSuspended = false; - write0(ch); - } - - private boolean scheduleWriteIfNecessary(final NioDatagramChannel channel) { + @Override + protected boolean scheduleWriteIfNecessary(final AbstractNioChannel channel) { final Thread workerThread = thread; if (workerThread == null || Thread.currentThread() != workerThread) { if (channel.writeTaskInTaskQueue.compareAndSet(false, true)) { @@ -477,335 +125,36 @@ class NioDatagramWorker implements Runnable { return false; } - private void write0(final NioDatagramChannel channel) { - - boolean addOpWrite = false; - boolean removeOpWrite = false; - - long writtenBytes = 0; - - final SocketSendBufferPool sendBufferPool = this.sendBufferPool; - final DatagramChannel ch = channel.getDatagramChannel(); - final Queue writeBuffer = channel.writeBufferQueue; - final int writeSpinCount = channel.getConfig().getWriteSpinCount(); - synchronized (channel.writeLock) { - // inform the channel that write is in-progress - channel.inWriteNowLoop = true; - - // loop forever... - for (;;) { - MessageEvent evt = channel.currentWriteEvent; - SendBuffer buf; - if (evt == null) { - if ((channel.currentWriteEvent = evt = writeBuffer.poll()) == null) { - removeOpWrite = true; - channel.writeSuspended = false; - break; - } - - channel.currentWriteBuffer = buf = sendBufferPool.acquire(evt.getMessage()); - } else { - buf = channel.currentWriteBuffer; - } - - try { - long localWrittenBytes = 0; - SocketAddress raddr = evt.getRemoteAddress(); - if (raddr == null) { - for (int i = writeSpinCount; i > 0; i --) { - localWrittenBytes = buf.transferTo(ch); - if (localWrittenBytes != 0) { - writtenBytes += localWrittenBytes; - break; - } - if (buf.finished()) { - break; - } - } - } else { - for (int i = writeSpinCount; i > 0; i --) { - localWrittenBytes = buf.transferTo(ch, raddr); - if (localWrittenBytes != 0) { - writtenBytes += localWrittenBytes; - break; - } - if (buf.finished()) { - break; - } - } - } - - if (localWrittenBytes > 0 || buf.finished()) { - // Successful write - proceed to the next message. - buf.release(); - ChannelFuture future = evt.getFuture(); - channel.currentWriteEvent = null; - channel.currentWriteBuffer = null; - evt = null; - buf = null; - future.setSuccess(); - } else { - // Not written at all - perhaps the kernel buffer is full. - addOpWrite = true; - channel.writeSuspended = true; - break; - } - } catch (final AsynchronousCloseException e) { - // Doesn't need a user attention - ignore. - } catch (final Throwable t) { - buf.release(); - ChannelFuture future = evt.getFuture(); - channel.currentWriteEvent = null; - channel.currentWriteBuffer = null; - buf = null; - evt = null; - future.setFailure(t); - fireExceptionCaught(channel, t); - } - } - channel.inWriteNowLoop = false; - - // Initially, the following block was executed after releasing - // the writeLock, but there was a race condition, and it has to be - // executed before releasing the writeLock: - // - // https://issues.jboss.org/browse/NETTY-410 - // - if (addOpWrite) { - setOpWrite(channel); - } else if (removeOpWrite) { - clearOpWrite(channel); - } - } - - fireWriteComplete(channel, writtenBytes); - } - - private void setOpWrite(final NioDatagramChannel channel) { - Selector selector = this.selector; - SelectionKey key = channel.getDatagramChannel().keyFor(selector); - if (key == null) { - return; - } - if (!key.isValid()) { - close(key); - return; - } - - // interestOps can change at any time and at any thread. - // Acquire a lock to avoid possible race condition. - synchronized (channel.interestOpsLock) { - int interestOps = channel.getRawInterestOps(); - if ((interestOps & SelectionKey.OP_WRITE) == 0) { - interestOps |= SelectionKey.OP_WRITE; - key.interestOps(interestOps); - channel.setRawInterestOpsNow(interestOps); - } - } - } - - private void clearOpWrite(NioDatagramChannel channel) { - Selector selector = this.selector; - SelectionKey key = channel.getDatagramChannel().keyFor(selector); - if (key == null) { - return; - } - if (!key.isValid()) { - close(key); - return; - } - - // interestOps can change at any time and at any thread. - // Acquire a lock to avoid possible race condition. - synchronized (channel.interestOpsLock) { - int interestOps = channel.getRawInterestOps(); - if ((interestOps & SelectionKey.OP_WRITE) != 0) { - interestOps &= ~SelectionKey.OP_WRITE; - key.interestOps(interestOps); - channel.setRawInterestOpsNow(interestOps); - } - } - } static void disconnect(NioDatagramChannel channel, ChannelFuture future) { boolean connected = channel.isConnected(); + boolean iothread = isIoThread(channel); try { channel.getDatagramChannel().disconnect(); future.setSuccess(); if (connected) { - fireChannelDisconnected(channel); - } - } catch (Throwable t) { - future.setFailure(t); - fireExceptionCaught(channel, t); - } - } - - void close(final NioDatagramChannel channel, - final ChannelFuture future) { - boolean connected = channel.isConnected(); - boolean bound = channel.isBound(); - try { - channel.getDatagramChannel().close(); - cancelledKeys ++; - - if (channel.setClosed()) { - future.setSuccess(); - if (connected) { + if (iothread) { fireChannelDisconnected(channel); + } else { + fireChannelDisconnectedLater(channel); } - if (bound) { - fireChannelUnbound(channel); - } - - cleanUpWriteBuffer(channel); - fireChannelClosed(channel); - } else { - future.setSuccess(); } } catch (Throwable t) { future.setFailure(t); - fireExceptionCaught(channel, t); + if (iothread) { + fireExceptionCaught(channel, t); + } else { + fireExceptionCaughtLater(channel, t); + } } } - private void cleanUpWriteBuffer(final NioDatagramChannel channel) { - Exception cause = null; - boolean fireExceptionCaught = false; - // Clean up the stale messages in the write buffer. - synchronized (channel.writeLock) { - MessageEvent evt = channel.currentWriteEvent; - if (evt != null) { - // Create the exception only once to avoid the excessive overhead - // caused by fillStackTrace. - if (channel.isOpen()) { - cause = new NotYetBoundException(); - } else { - cause = new ClosedChannelException(); - } - - ChannelFuture future = evt.getFuture(); - channel.currentWriteBuffer.release(); - channel.currentWriteBuffer = null; - channel.currentWriteEvent = null; - evt = null; - future.setFailure(cause); - fireExceptionCaught = true; - } - - Queue writeBuffer = channel.writeBufferQueue; - if (!writeBuffer.isEmpty()) { - // Create the exception only once to avoid the excessive overhead - // caused by fillStackTrace. - if (cause == null) { - if (channel.isOpen()) { - cause = new NotYetBoundException(); - } else { - cause = new ClosedChannelException(); - } - } - - for (;;) { - evt = writeBuffer.poll(); - if (evt == null) { - break; - } - evt.getFuture().setFailure(cause); - fireExceptionCaught = true; - } - } - } - - if (fireExceptionCaught) { - fireExceptionCaught(channel, cause); - } + @Override + protected Runnable createRegisterTask(AbstractNioChannel channel, ChannelFuture future) { + return new ChannelRegistionTask((NioDatagramChannel) channel, future); } - - void setInterestOps(final NioDatagramChannel channel, - ChannelFuture future, int interestOps) { - - boolean changed = false; - try { - // interestOps can change at any time and by any thread. - // Acquire a lock to avoid possible race condition. - synchronized (channel.interestOpsLock) { - final Selector selector = this.selector; - final SelectionKey key = channel.getDatagramChannel().keyFor(selector); - - if (key == null || selector == null) { - // Not registered to the worker yet. - // Set the rawInterestOps immediately; RegisterTask will pick it up. - channel.setRawInterestOpsNow(interestOps); - return; - } - - // Override OP_WRITE flag - a user cannot change this flag. - interestOps &= ~Channel.OP_WRITE; - interestOps |= channel.getRawInterestOps() & Channel.OP_WRITE; - - switch (NioProviderMetadata.CONSTRAINT_LEVEL) { - case 0: - if (channel.getRawInterestOps() != interestOps) { - // Set the interesteOps on the SelectionKey - key.interestOps(interestOps); - // If the worker thread (the one that that might possibly be blocked - // in a select() call) is not the thread executing this method wakeup - // the select() operation. - if (Thread.currentThread() != thread && - wakenUp.compareAndSet(false, true)) { - selector.wakeup(); - } - changed = true; - } - break; - case 1: - case 2: - if (channel.getRawInterestOps() != interestOps) { - if (Thread.currentThread() == thread) { - // Going to set the interestOps from the same thread. - // Set the interesteOps on the SelectionKey - key.interestOps(interestOps); - changed = true; - } else { - // Going to set the interestOps from a different thread - // and some old provides will need synchronization. - selectorGuard.readLock().lock(); - try { - if (wakenUp.compareAndSet(false, true)) { - selector.wakeup(); - } - key.interestOps(interestOps); - changed = true; - } finally { - selectorGuard.readLock().unlock(); - } - } - } - break; - default: - throw new Error(); - } - if (changed) { - channel.setRawInterestOpsNow(interestOps); - } - } - - future.setSuccess(); - if (changed) { - fireChannelInterestChanged(channel); - } - } catch (final CancelledKeyException e) { - // setInterestOps() was called on a closed channel. - ClosedChannelException cce = new ClosedChannelException(); - future.setFailure(cce); - fireExceptionCaught(channel, cce); - } catch (final Throwable t) { - future.setFailure(t); - fireExceptionCaught(channel, t); - } - } - + /** * RegisterTask is a task responsible for registering a channel with a * selector. @@ -854,4 +203,5 @@ class NioDatagramWorker implements Runnable { } } } + } diff --git a/transport/src/main/java/io/netty/channel/socket/nio/NioServerSocketPipelineSink.java b/transport/src/main/java/io/netty/channel/socket/nio/NioServerSocketPipelineSink.java index ce25a36f91..965c585827 100644 --- a/transport/src/main/java/io/netty/channel/socket/nio/NioServerSocketPipelineSink.java +++ b/transport/src/main/java/io/netty/channel/socket/nio/NioServerSocketPipelineSink.java @@ -29,7 +29,6 @@ import java.nio.channels.SocketChannel; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicInteger; -import io.netty.channel.AbstractChannelSink; import io.netty.channel.Channel; import io.netty.channel.ChannelEvent; import io.netty.channel.ChannelFuture; @@ -41,7 +40,7 @@ import io.netty.logging.InternalLogger; import io.netty.logging.InternalLoggerFactory; import io.netty.util.internal.DeadLockProofWorker; -class NioServerSocketPipelineSink extends AbstractChannelSink { +class NioServerSocketPipelineSink extends AbstractNioChannelSink { static final InternalLogger logger = InternalLoggerFactory.getInstance(NioServerSocketPipelineSink.class); @@ -122,7 +121,7 @@ class NioServerSocketPipelineSink extends AbstractChannelSink { } else if (e instanceof MessageEvent) { MessageEvent event = (MessageEvent) e; NioSocketChannel channel = (NioSocketChannel) event.getChannel(); - boolean offered = channel.writeBuffer.offer(event); + boolean offered = channel.writeBufferQueue.offer(event); assert offered; channel.worker.writeFromUserCode(channel); } 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 e73d133d2b..37f4e669ff 100644 --- a/transport/src/main/java/io/netty/channel/socket/nio/NioSocketChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/nio/NioSocketChannel.java @@ -15,27 +15,15 @@ */ package io.netty.channel.socket.nio; -import static io.netty.channel.Channels.*; - -import java.net.InetSocketAddress; -import java.net.SocketAddress; -import java.nio.channels.SocketChannel; -import java.util.Queue; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; - -import io.netty.buffer.ChannelBuffer; -import io.netty.channel.AbstractChannel; import io.netty.channel.Channel; import io.netty.channel.ChannelFactory; -import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelSink; -import io.netty.channel.MessageEvent; -import io.netty.channel.socket.nio.SocketSendBufferPool.SendBuffer; -import io.netty.util.internal.ThreadLocalBoolean; -class NioSocketChannel extends AbstractChannel +import java.net.InetSocketAddress; +import java.nio.channels.SocketChannel; + +class NioSocketChannel extends AbstractNioChannel implements io.netty.channel.socket.SocketChannel { private static final int ST_OPEN = 0; @@ -44,35 +32,14 @@ class NioSocketChannel extends AbstractChannel private static final int ST_CLOSED = -1; volatile int state = ST_OPEN; - final SocketChannel socket; - final NioWorker worker; private final NioSocketChannelConfig config; - private volatile InetSocketAddress localAddress; - private volatile InetSocketAddress remoteAddress; - - final Object interestOpsLock = new Object(); - final Object writeLock = new Object(); - - final Runnable writeTask = new WriteTask(); - final AtomicBoolean writeTaskInTaskQueue = new AtomicBoolean(); - - final Queue writeBuffer = new WriteRequestQueue(); - final AtomicInteger writeBufferSize = new AtomicInteger(); - final AtomicInteger highWaterMarkCounter = new AtomicInteger(); - boolean inWriteNowLoop; - boolean writeSuspended; - - MessageEvent currentWriteEvent; - SendBuffer currentWriteBuffer; public NioSocketChannel( Channel parent, ChannelFactory factory, ChannelPipeline pipeline, ChannelSink sink, SocketChannel socket, NioWorker worker) { - super(parent, factory, pipeline, sink); + super(parent, factory, pipeline, sink, worker, socket); - this.socket = socket; - this.worker = worker; config = new DefaultNioSocketChannelConfig(socket.socket()); } @@ -81,36 +48,6 @@ class NioSocketChannel extends AbstractChannel return config; } - @Override - public InetSocketAddress getLocalAddress() { - InetSocketAddress localAddress = this.localAddress; - if (localAddress == null) { - try { - this.localAddress = localAddress = - (InetSocketAddress) socket.socket().getLocalSocketAddress(); - } catch (Throwable t) { - // Sometimes fails on a closed socket in Windows. - return null; - } - } - return localAddress; - } - - @Override - public InetSocketAddress getRemoteAddress() { - InetSocketAddress remoteAddress = this.remoteAddress; - if (remoteAddress == null) { - try { - this.remoteAddress = remoteAddress = - (InetSocketAddress) socket.socket().getRemoteSocketAddress(); - } catch (Throwable t) { - // Sometimes fails on a closed socket in Windows. - return null; - } - } - return remoteAddress; - } - @Override public boolean isOpen() { return state >= ST_OPEN; @@ -143,123 +80,14 @@ class NioSocketChannel extends AbstractChannel return super.setClosed(); } + @Override - public int getInterestOps() { - if (!isOpen()) { - return Channel.OP_WRITE; - } - - int interestOps = getRawInterestOps(); - int writeBufferSize = this.writeBufferSize.get(); - if (writeBufferSize != 0) { - if (highWaterMarkCounter.get() > 0) { - int lowWaterMark = getConfig().getWriteBufferLowWaterMark(); - if (writeBufferSize >= lowWaterMark) { - interestOps |= Channel.OP_WRITE; - } else { - interestOps &= ~Channel.OP_WRITE; - } - } else { - int highWaterMark = getConfig().getWriteBufferHighWaterMark(); - if (writeBufferSize >= highWaterMark) { - interestOps |= Channel.OP_WRITE; - } else { - interestOps &= ~Channel.OP_WRITE; - } - } - } else { - interestOps &= ~Channel.OP_WRITE; - } - - return interestOps; - } - - int getRawInterestOps() { - return super.getInterestOps(); - } - - void setRawInterestOpsNow(int interestOps) { - super.setInterestOpsNow(interestOps); + InetSocketAddress getLocalSocketAddress() throws Exception { + return (InetSocketAddress) channel.socket().getLocalSocketAddress(); } @Override - public ChannelFuture write(Object message, SocketAddress remoteAddress) { - if (remoteAddress == null || remoteAddress.equals(getRemoteAddress())) { - return super.write(message, null); - } else { - return getUnsupportedOperationFuture(); - } - } - - private final class WriteRequestQueue extends AbstractWriteRequestQueue { - - private final ThreadLocalBoolean notifying = new ThreadLocalBoolean(); - - WriteRequestQueue() { - } - - @Override - public boolean offer(MessageEvent e) { - boolean success = queue.offer(e); - assert success; - - int messageSize = getMessageSize(e); - int newWriteBufferSize = writeBufferSize.addAndGet(messageSize); - int highWaterMark = getConfig().getWriteBufferHighWaterMark(); - - if (newWriteBufferSize >= highWaterMark) { - if (newWriteBufferSize - messageSize < highWaterMark) { - highWaterMarkCounter.incrementAndGet(); - if (!notifying.get()) { - notifying.set(Boolean.TRUE); - fireChannelInterestChanged(NioSocketChannel.this); - notifying.set(Boolean.FALSE); - } - } - } - return true; - } - - @Override - public MessageEvent poll() { - MessageEvent e = queue.poll(); - if (e != null) { - int messageSize = getMessageSize(e); - int newWriteBufferSize = writeBufferSize.addAndGet(-messageSize); - int lowWaterMark = getConfig().getWriteBufferLowWaterMark(); - - if (newWriteBufferSize == 0 || newWriteBufferSize < lowWaterMark) { - if (newWriteBufferSize + messageSize >= lowWaterMark) { - highWaterMarkCounter.decrementAndGet(); - if (isConnected() && !notifying.get()) { - notifying.set(Boolean.TRUE); - fireChannelInterestChanged(NioSocketChannel.this); - notifying.set(Boolean.FALSE); - } - } - } - } - return e; - } - - private int getMessageSize(MessageEvent e) { - Object m = e.getMessage(); - if (m instanceof ChannelBuffer) { - return ((ChannelBuffer) m).readableBytes(); - } - return 0; - } - } - - private final class WriteTask implements Runnable { - - WriteTask() { - } - - @Override - public void run() { - writeTaskInTaskQueue.set(false); - worker.writeFromTaskLoop(NioSocketChannel.this); - } + InetSocketAddress getRemoteSocketAddress() throws Exception { + return (InetSocketAddress) channel.socket().getRemoteSocketAddress(); } } diff --git a/transport/src/main/java/io/netty/channel/socket/nio/NioWorker.java b/transport/src/main/java/io/netty/channel/socket/nio/NioWorker.java index 35f07a512d..f9fb541cdd 100644 --- a/transport/src/main/java/io/netty/channel/socket/nio/NioWorker.java +++ b/transport/src/main/java/io/netty/channel/socket/nio/NioWorker.java @@ -15,286 +15,36 @@ */ package io.netty.channel.socket.nio; -import static io.netty.channel.Channels.*; +import static io.netty.channel.Channels.fireChannelBound; +import static io.netty.channel.Channels.fireChannelConnected; +import static io.netty.channel.Channels.fireExceptionCaught; +import static io.netty.channel.Channels.fireMessageReceived; +import static io.netty.channel.Channels.succeededFuture; +import io.netty.buffer.ChannelBuffer; +import io.netty.buffer.ChannelBufferFactory; +import io.netty.channel.ChannelException; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ReceiveBufferSizePredictor; import java.io.IOException; import java.net.SocketAddress; import java.nio.ByteBuffer; -import java.nio.channels.AsynchronousCloseException; -import java.nio.channels.CancelledKeyException; import java.nio.channels.ClosedChannelException; -import java.nio.channels.NotYetConnectedException; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.SocketChannel; -import java.util.Iterator; -import java.util.Queue; -import java.util.Set; import java.util.concurrent.Executor; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.locks.ReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock; -import io.netty.buffer.ChannelBuffer; -import io.netty.buffer.ChannelBufferFactory; -import io.netty.channel.Channel; -import io.netty.channel.ChannelException; -import io.netty.channel.ChannelFuture; -import io.netty.channel.MessageEvent; -import io.netty.channel.ReceiveBufferSizePredictor; -import io.netty.channel.socket.nio.SocketSendBufferPool.SendBuffer; -import io.netty.logging.InternalLogger; -import io.netty.logging.InternalLoggerFactory; -import io.netty.util.internal.DeadLockProofWorker; -import io.netty.util.internal.QueueFactory; - -class NioWorker implements Runnable { - - private static final InternalLogger logger = - InternalLoggerFactory.getInstance(NioWorker.class); - - private static final int CONSTRAINT_LEVEL = NioProviderMetadata.CONSTRAINT_LEVEL; - - static final int CLEANUP_INTERVAL = 256; // XXX Hard-coded value, but won't need customization. - - private final Executor executor; - private boolean started; - private volatile Thread thread; - volatile Selector selector; - private final AtomicBoolean wakenUp = new AtomicBoolean(); - private final ReadWriteLock selectorGuard = new ReentrantReadWriteLock(); - private final Object startStopLock = new Object(); - private final Queue registerTaskQueue = QueueFactory.createQueue(Runnable.class); - private final Queue writeTaskQueue = QueueFactory.createQueue(Runnable.class); - private volatile int cancelledKeys; // should use AtomicInteger but we just need approximation +class NioWorker extends AbstractNioWorker { private final SocketReceiveBufferPool recvBufferPool = new SocketReceiveBufferPool(); - private final SocketSendBufferPool sendBufferPool = new SocketSendBufferPool(); NioWorker(Executor executor) { - this.executor = executor; - } - - void register(NioSocketChannel channel, ChannelFuture future) { - - boolean server = !(channel instanceof NioClientSocketChannel); - Runnable registerTask = new RegisterTask(channel, future, server); - Selector selector; - - synchronized (startStopLock) { - if (!started) { - // Open a selector if this worker didn't start yet. - try { - this.selector = selector = Selector.open(); - } catch (Throwable t) { - throw new ChannelException( - "Failed to create a selector.", t); - } - - // Start the worker thread with the new Selector. - boolean success = false; - try { - DeadLockProofWorker.start(executor, this); - success = true; - } finally { - if (!success) { - // Release the Selector if the execution fails. - try { - selector.close(); - } catch (Throwable t) { - if (logger.isWarnEnabled()) { - logger.warn("Failed to close a selector.", t); - } - } - this.selector = selector = null; - // The method will return to the caller at this point. - } - } - } else { - // Use the existing selector if this worker has been started. - selector = this.selector; - } - - assert selector != null && selector.isOpen(); - - started = true; - boolean offered = registerTaskQueue.offer(registerTask); - assert offered; - } - - if (wakenUp.compareAndSet(false, true)) { - selector.wakeup(); - } + super(executor); } @Override - public void run() { - thread = Thread.currentThread(); - - boolean shutdown = false; - Selector selector = this.selector; - for (;;) { - wakenUp.set(false); - - if (CONSTRAINT_LEVEL != 0) { - selectorGuard.writeLock().lock(); - // This empty synchronization block prevents the selector - // from acquiring its lock. - selectorGuard.writeLock().unlock(); - } - - try { - SelectorUtil.select(selector); - - // 'wakenUp.compareAndSet(false, true)' is always evaluated - // before calling 'selector.wakeup()' to reduce the wake-up - // overhead. (Selector.wakeup() is an expensive operation.) - // - // However, there is a race condition in this approach. - // The race condition is triggered when 'wakenUp' is set to - // true too early. - // - // 'wakenUp' is set to true too early if: - // 1) Selector is waken up between 'wakenUp.set(false)' and - // 'selector.select(...)'. (BAD) - // 2) Selector is waken up between 'selector.select(...)' and - // 'if (wakenUp.get()) { ... }'. (OK) - // - // In the first case, 'wakenUp' is set to true and the - // following 'selector.select(...)' will wake up immediately. - // Until 'wakenUp' is set to false again in the next round, - // 'wakenUp.compareAndSet(false, true)' will fail, and therefore - // any attempt to wake up the Selector will fail, too, causing - // the following 'selector.select(...)' call to block - // unnecessarily. - // - // To fix this problem, we wake up the selector again if wakenUp - // is true immediately after selector.select(...). - // It is inefficient in that it wakes up the selector for both - // the first case (BAD - wake-up required) and the second case - // (OK - no wake-up required). - - if (wakenUp.get()) { - selector.wakeup(); - } - - cancelledKeys = 0; - processRegisterTaskQueue(); - processWriteTaskQueue(); - processSelectedKeys(selector.selectedKeys()); - - // Exit the loop when there's nothing to handle. - // The shutdown flag is used to delay the shutdown of this - // loop to avoid excessive Selector creation when - // connections are registered in a one-by-one manner instead of - // concurrent manner. - if (selector.keys().isEmpty()) { - if (shutdown || - executor instanceof ExecutorService && ((ExecutorService) executor).isShutdown()) { - - synchronized (startStopLock) { - if (registerTaskQueue.isEmpty() && selector.keys().isEmpty()) { - started = false; - try { - selector.close(); - } catch (IOException e) { - if (logger.isWarnEnabled()) { - logger.warn( - "Failed to close a selector.", e); - } - } finally { - this.selector = null; - } - break; - } else { - shutdown = false; - } - } - } else { - // Give one more second. - shutdown = true; - } - } else { - shutdown = false; - } - } catch (Throwable t) { - if (logger.isWarnEnabled()) { - logger.warn( - "Unexpected exception in the selector loop.", t); - } - - - // Prevent possible consecutive immediate failures that lead to - // excessive CPU consumption. - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - // Ignore. - } - } - } - } - - private void processRegisterTaskQueue() throws IOException { - for (;;) { - final Runnable task = registerTaskQueue.poll(); - if (task == null) { - break; - } - - task.run(); - cleanUpCancelledKeys(); - } - } - - private void processWriteTaskQueue() throws IOException { - for (;;) { - final Runnable task = writeTaskQueue.poll(); - if (task == null) { - break; - } - - task.run(); - cleanUpCancelledKeys(); - } - } - - private void processSelectedKeys(Set selectedKeys) throws IOException { - for (Iterator i = selectedKeys.iterator(); i.hasNext();) { - SelectionKey k = i.next(); - i.remove(); - try { - int readyOps = k.readyOps(); - if ((readyOps & SelectionKey.OP_READ) != 0 || readyOps == 0) { - if (!read(k)) { - // Connection already closed - no need to handle write. - continue; - } - } - if ((readyOps & SelectionKey.OP_WRITE) != 0) { - writeFromSelectorLoop(k); - } - } catch (CancelledKeyException e) { - close(k); - } - - if (cleanUpCancelledKeys()) { - break; // break the loop to avoid ConcurrentModificationException - } - } - } - - private boolean cleanUpCancelledKeys() throws IOException { - if (cancelledKeys >= CLEANUP_INTERVAL) { - cancelledKeys = 0; - selector.selectNow(); - return true; - } - return false; - } - - private boolean read(SelectionKey k) { + protected boolean read(SelectionKey k) { final SocketChannel ch = (SocketChannel) k.channel(); final NioSocketChannel channel = (NioSocketChannel) k.attachment(); @@ -350,47 +100,9 @@ class NioWorker implements Runnable { return true; } - private void close(SelectionKey k) { - NioSocketChannel ch = (NioSocketChannel) k.attachment(); - close(ch, succeededFuture(ch)); - } - void writeFromUserCode(final NioSocketChannel channel) { - if (!channel.isConnected()) { - cleanUpWriteBuffer(channel); - return; - } - - if (scheduleWriteIfNecessary(channel)) { - return; - } - - // From here, we are sure Thread.currentThread() == workerThread. - - if (channel.writeSuspended) { - return; - } - - if (channel.inWriteNowLoop) { - return; - } - - write0(channel); - } - - void writeFromTaskLoop(final NioSocketChannel ch) { - if (!ch.writeSuspended) { - write0(ch); - } - } - - void writeFromSelectorLoop(final SelectionKey k) { - NioSocketChannel ch = (NioSocketChannel) k.attachment(); - ch.writeSuspended = false; - write0(ch); - } - - private boolean scheduleWriteIfNecessary(final NioSocketChannel channel) { + @Override + protected boolean scheduleWriteIfNecessary(final AbstractNioChannel channel) { final Thread currentThread = Thread.currentThread(); final Thread workerThread = thread; if (currentThread != workerThread) { @@ -424,310 +136,13 @@ class NioWorker implements Runnable { return false; } - - private void write0(NioSocketChannel channel) { - boolean open = true; - boolean addOpWrite = false; - boolean removeOpWrite = false; - - long writtenBytes = 0; - - final SocketSendBufferPool sendBufferPool = this.sendBufferPool; - final SocketChannel ch = channel.socket; - final Queue writeBuffer = channel.writeBuffer; - final int writeSpinCount = channel.getConfig().getWriteSpinCount(); - synchronized (channel.writeLock) { - channel.inWriteNowLoop = true; - for (;;) { - MessageEvent evt = channel.currentWriteEvent; - SendBuffer buf; - if (evt == null) { - if ((channel.currentWriteEvent = evt = writeBuffer.poll()) == null) { - removeOpWrite = true; - channel.writeSuspended = false; - break; - } - - channel.currentWriteBuffer = buf = sendBufferPool.acquire(evt.getMessage()); - } else { - buf = channel.currentWriteBuffer; - } - - ChannelFuture future = evt.getFuture(); - try { - long localWrittenBytes = 0; - for (int i = writeSpinCount; i > 0; i --) { - localWrittenBytes = buf.transferTo(ch); - if (localWrittenBytes != 0) { - writtenBytes += localWrittenBytes; - break; - } - if (buf.finished()) { - break; - } - } - - if (buf.finished()) { - // Successful write - proceed to the next message. - buf.release(); - channel.currentWriteEvent = null; - channel.currentWriteBuffer = null; - evt = null; - buf = null; - future.setSuccess(); - } else { - // Not written fully - perhaps the kernel buffer is full. - addOpWrite = true; - channel.writeSuspended = true; - - if (localWrittenBytes > 0) { - // Notify progress listeners if necessary. - future.setProgress( - localWrittenBytes, - buf.writtenBytes(), buf.totalBytes()); - } - break; - } - } catch (AsynchronousCloseException e) { - // Doesn't need a user attention - ignore. - } catch (Throwable t) { - if (buf != null) { - buf.release(); - } - channel.currentWriteEvent = null; - channel.currentWriteBuffer = null; - buf = null; - evt = null; - future.setFailure(t); - fireExceptionCaught(channel, t); - if (t instanceof IOException) { - open = false; - close(channel, succeededFuture(channel)); - } - } - } - channel.inWriteNowLoop = false; - - // Initially, the following block was executed after releasing - // the writeLock, but there was a race condition, and it has to be - // executed before releasing the writeLock: - // - // https://issues.jboss.org/browse/NETTY-410 - // - if (open) { - if (addOpWrite) { - setOpWrite(channel); - } else if (removeOpWrite) { - clearOpWrite(channel); - } - } - } - - fireWriteComplete(channel, writtenBytes); + + @Override + protected Runnable createRegisterTask(AbstractNioChannel channel, ChannelFuture future) { + boolean server = !(channel instanceof NioClientSocketChannel); + return new RegisterTask((NioSocketChannel) channel, future, server); } - - private void setOpWrite(NioSocketChannel channel) { - Selector selector = this.selector; - SelectionKey key = channel.socket.keyFor(selector); - if (key == null) { - return; - } - if (!key.isValid()) { - close(key); - return; - } - - // interestOps can change at any time and at any thread. - // Acquire a lock to avoid possible race condition. - synchronized (channel.interestOpsLock) { - int interestOps = channel.getRawInterestOps(); - if ((interestOps & SelectionKey.OP_WRITE) == 0) { - interestOps |= SelectionKey.OP_WRITE; - key.interestOps(interestOps); - channel.setRawInterestOpsNow(interestOps); - } - } - } - - private void clearOpWrite(NioSocketChannel channel) { - Selector selector = this.selector; - SelectionKey key = channel.socket.keyFor(selector); - if (key == null) { - return; - } - if (!key.isValid()) { - close(key); - return; - } - - // interestOps can change at any time and at any thread. - // Acquire a lock to avoid possible race condition. - synchronized (channel.interestOpsLock) { - int interestOps = channel.getRawInterestOps(); - if ((interestOps & SelectionKey.OP_WRITE) != 0) { - interestOps &= ~SelectionKey.OP_WRITE; - key.interestOps(interestOps); - channel.setRawInterestOpsNow(interestOps); - } - } - } - - void close(NioSocketChannel channel, ChannelFuture future) { - boolean connected = channel.isConnected(); - boolean bound = channel.isBound(); - try { - channel.socket.close(); - cancelledKeys ++; - - if (channel.setClosed()) { - future.setSuccess(); - if (connected) { - fireChannelDisconnected(channel); - } - if (bound) { - fireChannelUnbound(channel); - } - - cleanUpWriteBuffer(channel); - fireChannelClosed(channel); - } else { - future.setSuccess(); - } - } catch (Throwable t) { - future.setFailure(t); - fireExceptionCaught(channel, t); - } - } - - private void cleanUpWriteBuffer(NioSocketChannel channel) { - Exception cause = null; - boolean fireExceptionCaught = false; - - // Clean up the stale messages in the write buffer. - synchronized (channel.writeLock) { - MessageEvent evt = channel.currentWriteEvent; - if (evt != null) { - // Create the exception only once to avoid the excessive overhead - // caused by fillStackTrace. - if (channel.isOpen()) { - cause = new NotYetConnectedException(); - } else { - cause = new ClosedChannelException(); - } - - ChannelFuture future = evt.getFuture(); - channel.currentWriteBuffer.release(); - channel.currentWriteBuffer = null; - channel.currentWriteEvent = null; - evt = null; - future.setFailure(cause); - fireExceptionCaught = true; - } - - Queue writeBuffer = channel.writeBuffer; - if (!writeBuffer.isEmpty()) { - // Create the exception only once to avoid the excessive overhead - // caused by fillStackTrace. - if (cause == null) { - if (channel.isOpen()) { - cause = new NotYetConnectedException(); - } else { - cause = new ClosedChannelException(); - } - } - - for (;;) { - evt = writeBuffer.poll(); - if (evt == null) { - break; - } - evt.getFuture().setFailure(cause); - fireExceptionCaught = true; - } - } - } - - if (fireExceptionCaught) { - fireExceptionCaught(channel, cause); - } - } - - void setInterestOps( - NioSocketChannel channel, ChannelFuture future, int interestOps) { - boolean changed = false; - try { - // interestOps can change at any time and at any thread. - // Acquire a lock to avoid possible race condition. - synchronized (channel.interestOpsLock) { - Selector selector = this.selector; - SelectionKey key = channel.socket.keyFor(selector); - - if (key == null || selector == null) { - // Not registered to the worker yet. - // Set the rawInterestOps immediately; RegisterTask will pick it up. - channel.setRawInterestOpsNow(interestOps); - return; - } - - // Override OP_WRITE flag - a user cannot change this flag. - interestOps &= ~Channel.OP_WRITE; - interestOps |= channel.getRawInterestOps() & Channel.OP_WRITE; - - switch (CONSTRAINT_LEVEL) { - case 0: - if (channel.getRawInterestOps() != interestOps) { - key.interestOps(interestOps); - if (Thread.currentThread() != thread && - wakenUp.compareAndSet(false, true)) { - selector.wakeup(); - } - changed = true; - } - break; - case 1: - case 2: - if (channel.getRawInterestOps() != interestOps) { - if (Thread.currentThread() == thread) { - key.interestOps(interestOps); - changed = true; - } else { - selectorGuard.readLock().lock(); - try { - if (wakenUp.compareAndSet(false, true)) { - selector.wakeup(); - } - key.interestOps(interestOps); - changed = true; - } finally { - selectorGuard.readLock().unlock(); - } - } - } - break; - default: - throw new Error(); - } - - if (changed) { - channel.setRawInterestOpsNow(interestOps); - } - } - - future.setSuccess(); - if (changed) { - fireChannelInterestChanged(channel); - } - } catch (CancelledKeyException e) { - // setInterestOps() was called on a closed channel. - ClosedChannelException cce = new ClosedChannelException(); - future.setFailure(cce); - fireExceptionCaught(channel, cce); - } catch (Throwable t) { - future.setFailure(t); - fireExceptionCaught(channel, t); - } - } - + private final class RegisterTask implements Runnable { private final NioSocketChannel channel; private final ChannelFuture future; @@ -745,6 +160,7 @@ class NioWorker implements Runnable { public void run() { SocketAddress localAddress = channel.getLocalAddress(); SocketAddress remoteAddress = channel.getRemoteAddress(); + if (localAddress == null || remoteAddress == null) { if (future != null) { future.setFailure(new ClosedChannelException()); @@ -755,11 +171,11 @@ class NioWorker implements Runnable { try { if (server) { - channel.socket.configureBlocking(false); + channel.channel.configureBlocking(false); } synchronized (channel.interestOpsLock) { - channel.socket.register( + channel.channel.register( selector, channel.getRawInterestOps(), channel); } if (future != null) { @@ -783,4 +199,5 @@ class NioWorker implements Runnable { fireChannelConnected(channel, remoteAddress); } } + } 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 index 51c8bbaa8c..511f5dcf26 100644 --- a/transport/src/main/java/io/netty/channel/socket/nio/SelectorUtil.java +++ b/transport/src/main/java/io/netty/channel/socket/nio/SelectorUtil.java @@ -28,9 +28,28 @@ final class SelectorUtil { static final int DEFAULT_IO_THREADS = Runtime.getRuntime().availableProcessors() * 2; + // Workaround for JDK NIO bug. + // + // See: + // - http://bugs.sun.com/view_bug.do?bug_id=6427854 + // - https://github.com/netty/netty/issues/203 + static { + String key = "sun.nio.ch.bugLevel"; + try { + String buglevel = System.getProperty(key); + if (buglevel == null) { + System.setProperty(key, ""); + } + } catch (SecurityException e) { + if (logger.isDebugEnabled()) { + logger.debug("Unable to get/set System Property '" + key + "'", e); + } + } + } + static void select(Selector selector) throws IOException { try { - selector.select(500); + selector.select(10); } catch (CancelledKeyException e) { if (logger.isDebugEnabled()) { logger.debug( 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 new file mode 100644 index 0000000000..2c7009050a --- /dev/null +++ b/transport/src/main/java/io/netty/channel/socket/oio/AbstractOioChannel.java @@ -0,0 +1,118 @@ +/* + * Copyright 2011 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.oio; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.SocketAddress; + +import io.netty.channel.AbstractChannel; +import io.netty.channel.Channel; +import io.netty.channel.ChannelFactory; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.ChannelSink; +import io.netty.channel.socket.Worker; + +abstract class AbstractOioChannel extends AbstractChannel { + private volatile InetSocketAddress localAddress; + volatile InetSocketAddress remoteAddress; + volatile Thread workerThread; + volatile Worker worker; + + final Object interestOpsLock = new Object(); + + AbstractOioChannel( + Channel parent, + ChannelFactory factory, + ChannelPipeline pipeline, + ChannelSink sink) { + super(parent, factory, pipeline, sink); + } + + @Override + protected boolean setClosed() { + return super.setClosed(); + } + + @Override + protected void setInterestOpsNow(int interestOps) { + super.setInterestOpsNow(interestOps); + } + + @Override + public ChannelFuture write(Object message, SocketAddress remoteAddress) { + if (remoteAddress == null || remoteAddress.equals(getRemoteAddress())) { + return super.write(message, null); + } else { + return super.write(message, remoteAddress); + } + } + + @Override + public boolean isBound() { + return isOpen() && isSocketBound(); + } + + @Override + public boolean isConnected() { + return isOpen() && isSocketConnected(); + } + + + @Override + public InetSocketAddress getLocalAddress() { + InetSocketAddress localAddress = this.localAddress; + if (localAddress == null) { + try { + this.localAddress = localAddress = + (InetSocketAddress) getLocalSocketAddress(); + } catch (Throwable t) { + // Sometimes fails on a closed socket in Windows. + return null; + } + } + return localAddress; + } + + @Override + public InetSocketAddress getRemoteAddress() { + InetSocketAddress remoteAddress = this.remoteAddress; + if (remoteAddress == null) { + try { + this.remoteAddress = remoteAddress = + (InetSocketAddress) getRemoteSocketAddress(); + } catch (Throwable t) { + // Sometimes fails on a closed socket in Windows. + return null; + } + } + return remoteAddress; + } + + abstract boolean isSocketBound(); + + abstract boolean isSocketConnected(); + + abstract boolean isSocketClosed(); + + abstract InetSocketAddress getLocalSocketAddress() throws Exception; + + abstract InetSocketAddress getRemoteSocketAddress() throws Exception; + + abstract void closeSocket() throws IOException; + +} diff --git a/transport/src/main/java/io/netty/channel/socket/oio/AbstractOioChannelSink.java b/transport/src/main/java/io/netty/channel/socket/oio/AbstractOioChannelSink.java new file mode 100644 index 0000000000..d57c198534 --- /dev/null +++ b/transport/src/main/java/io/netty/channel/socket/oio/AbstractOioChannelSink.java @@ -0,0 +1,54 @@ +/* + * Copyright 2011 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.oio; + +import io.netty.channel.AbstractChannelSink; +import io.netty.channel.Channel; +import io.netty.channel.ChannelEvent; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.socket.Worker; + +public abstract class AbstractOioChannelSink extends AbstractChannelSink { + + @Override + public ChannelFuture execute(final ChannelPipeline pipeline, final Runnable task) { + Channel ch = pipeline.getChannel(); + if (ch instanceof AbstractOioChannel) { + AbstractOioChannel channel = (AbstractOioChannel) ch; + Worker worker = channel.worker; + if (worker != null) { + return channel.worker.executeInIoThread(ch, task); + } + } + + return super.execute(pipeline, task); + + + } + + @Override + protected boolean isFireExceptionCaughtLater(ChannelEvent event, Throwable actualCause) { + Channel channel = event.getChannel(); + boolean fireLater = false; + if (channel instanceof AbstractOioChannel) { + fireLater = !AbstractOioWorker.isIoThread((AbstractOioChannel) channel); + } + return fireLater; + } + +} diff --git a/transport/src/main/java/io/netty/channel/socket/oio/AbstractOioWorker.java b/transport/src/main/java/io/netty/channel/socket/oio/AbstractOioWorker.java new file mode 100644 index 0000000000..930abbce59 --- /dev/null +++ b/transport/src/main/java/io/netty/channel/socket/oio/AbstractOioWorker.java @@ -0,0 +1,232 @@ +/* + * Copyright 2011 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.oio; + +import static io.netty.channel.Channels.*; +import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; +import io.netty.channel.Channels; +import io.netty.channel.socket.ChannelRunnableWrapper; +import io.netty.channel.socket.Worker; +import io.netty.util.internal.QueueFactory; + +import java.io.IOException; +import java.util.Queue; +import java.util.concurrent.RejectedExecutionException; + +/** + * Abstract base class for Oio-Worker implementations + * + * @param {@link AbstractOioChannel} + */ +abstract class AbstractOioWorker implements Worker { + + private final Queue eventQueue = QueueFactory.createQueue(ChannelRunnableWrapper.class); + + protected final C channel; + + public AbstractOioWorker(C channel) { + this.channel = channel; + channel.worker = this; + } + + @Override + public void run() { + channel.workerThread = Thread.currentThread(); + + while (channel.isOpen()) { + synchronized (channel.interestOpsLock) { + while (!channel.isReadable()) { + try { + // notify() is not called at all. + // close() and setInterestOps() calls Thread.interrupt() + channel.interestOpsLock.wait(); + } catch (InterruptedException e) { + if (!channel.isOpen()) { + break; + } + } + } + } + + try { + boolean cont = process(); + + processEventQueue(); + + if (!cont) { + break; + } + } catch (Throwable t) { + if (!channel.isSocketClosed()) { + fireExceptionCaught(channel, t); + } + break; + } + } + + // Setting the workerThread to null will prevent any channel + // operations from interrupting this thread from now on. + channel.workerThread = null; + + // Clean up. + close(channel, succeededFuture(channel), true); + } + + static boolean isIoThread(AbstractOioChannel channel) { + return Thread.currentThread() == channel.workerThread; + } + + @Override + public ChannelFuture executeInIoThread(Channel channel, Runnable task) { + if (channel instanceof AbstractOioChannel && isIoThread((AbstractOioChannel) channel)) { + try { + task.run(); + return succeededFuture(channel); + } catch (Throwable t) { + return failedFuture(channel, t); + } + } else { + ChannelRunnableWrapper channelRunnable = new ChannelRunnableWrapper(channel, task); + boolean added = eventQueue.offer(channelRunnable); + + if (added) { + // as we set the SO_TIMEOUT to 1 second this task will get picked up in 1 second at latest + + } else { + channelRunnable.setFailure(new RejectedExecutionException("Unable to queue task " + task)); + } + return channelRunnable; + } + } + + private void processEventQueue() throws IOException { + for (;;) { + final ChannelRunnableWrapper task = eventQueue.poll(); + if (task == null) { + break; + } + task.run(); + } + } + + + /** + * Process the incoming messages and also is responsible for call {@link Channels#fireMessageReceived(Channel, Object)} once a message + * was processed without errors. + * + * @return continue returns true as long as this worker should continue to try processing incoming messages + * @throws IOException + */ + abstract boolean process() throws IOException; + + static void setInterestOps( + AbstractOioChannel channel, ChannelFuture future, int interestOps) { + boolean iothread = isIoThread(channel); + + // Override OP_WRITE flag - a user cannot change this flag. + interestOps &= ~Channel.OP_WRITE; + interestOps |= channel.getInterestOps() & Channel.OP_WRITE; + + boolean changed = false; + try { + if (channel.getInterestOps() != interestOps) { + if ((interestOps & Channel.OP_READ) != 0) { + channel.setInterestOpsNow(Channel.OP_READ); + } else { + channel.setInterestOpsNow(Channel.OP_NONE); + } + changed = true; + } + + future.setSuccess(); + if (changed) { + synchronized (channel.interestOpsLock) { + channel.setInterestOpsNow(interestOps); + + // Notify the worker so it stops or continues reading. + Thread currentThread = Thread.currentThread(); + Thread workerThread = channel.workerThread; + if (workerThread != null && currentThread != workerThread) { + workerThread.interrupt(); + } + } + if (iothread) { + fireChannelInterestChanged(channel); + } else { + fireChannelInterestChangedLater(channel); + } + } + } catch (Throwable t) { + future.setFailure(t); + if (iothread) { + fireExceptionCaught(channel, t); + } else { + fireExceptionCaughtLater(channel, t); + } + } + } + + static void close(AbstractOioChannel channel, ChannelFuture future) { + close(channel, future, isIoThread(channel)); + } + + private static void close(AbstractOioChannel channel, ChannelFuture future, boolean iothread) { + boolean connected = channel.isConnected(); + boolean bound = channel.isBound(); + + try { + channel.closeSocket(); + if (channel.setClosed()) { + future.setSuccess(); + if (connected) { + // Notify the worker so it stops reading. + Thread currentThread = Thread.currentThread(); + Thread workerThread = channel.workerThread; + if (workerThread != null && currentThread != workerThread) { + workerThread.interrupt(); + } + if (iothread) { + fireChannelDisconnected(channel); + } else { + fireChannelDisconnectedLater(channel); + } + } + if (bound) { + if (iothread) { + fireChannelUnbound(channel); + } else { + fireChannelUnboundLater(channel); + } + } + if (iothread) { + fireChannelClosed(channel); + } else { + fireChannelClosedLater(channel); + } + } else { + future.setSuccess(); + } + } catch (Throwable t) { + future.setFailure(t); + if (iothread) { + fireExceptionCaught(channel, t); + } else { + fireExceptionCaughtLater(channel, t); + } + } + } +} diff --git a/transport/src/main/java/io/netty/channel/socket/oio/OioClientSocketPipelineSink.java b/transport/src/main/java/io/netty/channel/socket/oio/OioClientSocketPipelineSink.java index e5cf415a02..e607d3282e 100644 --- a/transport/src/main/java/io/netty/channel/socket/oio/OioClientSocketPipelineSink.java +++ b/transport/src/main/java/io/netty/channel/socket/oio/OioClientSocketPipelineSink.java @@ -21,7 +21,6 @@ import java.io.PushbackInputStream; import java.net.SocketAddress; import java.util.concurrent.Executor; -import io.netty.channel.AbstractChannelSink; import io.netty.channel.ChannelEvent; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; @@ -31,7 +30,7 @@ import io.netty.channel.ChannelStateEvent; import io.netty.channel.MessageEvent; import io.netty.util.internal.DeadLockProofWorker; -class OioClientSocketPipelineSink extends AbstractChannelSink { +class OioClientSocketPipelineSink extends AbstractOioChannelSink { private final Executor workerExecutor; 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 841fd1b316..a73a24c503 100644 --- a/transport/src/main/java/io/netty/channel/socket/oio/OioDatagramChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/oio/OioDatagramChannel.java @@ -22,28 +22,22 @@ import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.MulticastSocket; import java.net.NetworkInterface; -import java.net.SocketAddress; import java.net.SocketException; -import io.netty.channel.AbstractChannel; import io.netty.channel.ChannelException; import io.netty.channel.ChannelFactory; -import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelSink; import io.netty.channel.socket.DatagramChannel; import io.netty.channel.socket.DatagramChannelConfig; import io.netty.channel.socket.DefaultDatagramChannelConfig; -final class OioDatagramChannel extends AbstractChannel +final class OioDatagramChannel extends AbstractOioChannel implements DatagramChannel { final MulticastSocket socket; - final Object interestOpsLock = new Object(); private final DatagramChannelConfig config; - volatile Thread workerThread; - private volatile InetSocketAddress localAddress; - volatile InetSocketAddress remoteAddress; + static OioDatagramChannel create(ChannelFactory factory, ChannelPipeline pipeline, ChannelSink sink) { @@ -81,65 +75,6 @@ final class OioDatagramChannel extends AbstractChannel return config; } - @Override - public InetSocketAddress getLocalAddress() { - InetSocketAddress localAddress = this.localAddress; - if (localAddress == null) { - try { - this.localAddress = localAddress = - (InetSocketAddress) socket.getLocalSocketAddress(); - } catch (Throwable t) { - // Sometimes fails on a closed socket in Windows. - return null; - } - } - return localAddress; - } - - @Override - public InetSocketAddress getRemoteAddress() { - InetSocketAddress remoteAddress = this.remoteAddress; - if (remoteAddress == null) { - try { - this.remoteAddress = remoteAddress = - (InetSocketAddress) socket.getRemoteSocketAddress(); - } catch (Throwable t) { - // Sometimes fails on a closed socket in Windows. - return null; - } - } - return remoteAddress; - } - - @Override - public boolean isBound() { - return isOpen() && socket.isBound(); - } - - @Override - public boolean isConnected() { - return isOpen() && socket.isConnected(); - } - - @Override - protected boolean setClosed() { - return super.setClosed(); - } - - @Override - protected void setInterestOpsNow(int interestOps) { - super.setInterestOpsNow(interestOps); - } - - @Override - public ChannelFuture write(Object message, SocketAddress remoteAddress) { - if (remoteAddress == null || remoteAddress.equals(getRemoteAddress())) { - return super.write(message, null); - } else { - return super.write(message, remoteAddress); - } - } - @Override public void joinGroup(InetAddress multicastAddress) { ensureBound(); @@ -187,4 +122,36 @@ final class OioDatagramChannel extends AbstractChannel throw new ChannelException(e); } } + + @Override + boolean isSocketBound() { + return socket.isBound(); + } + + @Override + boolean isSocketConnected() { + return socket.isConnected(); + } + + @Override + InetSocketAddress getLocalSocketAddress() throws Exception { + return (InetSocketAddress) socket.getLocalSocketAddress(); + } + + @Override + InetSocketAddress getRemoteSocketAddress() throws Exception { + return (InetSocketAddress) socket.getRemoteSocketAddress(); + } + + @Override + void closeSocket() throws IOException { + socket.close(); + } + + @Override + boolean isSocketClosed() { + return socket.isClosed(); + } + + } diff --git a/transport/src/main/java/io/netty/channel/socket/oio/OioDatagramPipelineSink.java b/transport/src/main/java/io/netty/channel/socket/oio/OioDatagramPipelineSink.java index 3cf3e6baf6..2b198080b5 100644 --- a/transport/src/main/java/io/netty/channel/socket/oio/OioDatagramPipelineSink.java +++ b/transport/src/main/java/io/netty/channel/socket/oio/OioDatagramPipelineSink.java @@ -20,7 +20,6 @@ import static io.netty.channel.Channels.*; import java.net.SocketAddress; import java.util.concurrent.Executor; -import io.netty.channel.AbstractChannelSink; import io.netty.channel.ChannelEvent; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; @@ -30,7 +29,7 @@ import io.netty.channel.ChannelStateEvent; import io.netty.channel.MessageEvent; import io.netty.util.internal.DeadLockProofWorker; -class OioDatagramPipelineSink extends AbstractChannelSink { +class OioDatagramPipelineSink extends AbstractOioChannelSink { private final Executor workerExecutor; diff --git a/transport/src/main/java/io/netty/channel/socket/oio/OioDatagramWorker.java b/transport/src/main/java/io/netty/channel/socket/oio/OioDatagramWorker.java index ab6ffbd277..2581425616 100644 --- a/transport/src/main/java/io/netty/channel/socket/oio/OioDatagramWorker.java +++ b/transport/src/main/java/io/netty/channel/socket/oio/OioDatagramWorker.java @@ -17,80 +17,54 @@ package io.netty.channel.socket.oio; import static io.netty.channel.Channels.*; +import java.io.IOException; import java.io.InterruptedIOException; import java.net.DatagramPacket; -import java.net.MulticastSocket; import java.net.SocketAddress; import java.nio.ByteBuffer; import io.netty.buffer.ChannelBuffer; -import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ReceiveBufferSizePredictor; -class OioDatagramWorker implements Runnable { - - private final OioDatagramChannel channel; +class OioDatagramWorker extends AbstractOioWorker { OioDatagramWorker(OioDatagramChannel channel) { - this.channel = channel; + super(channel); } + + @Override - public void run() { - channel.workerThread = Thread.currentThread(); - final MulticastSocket socket = channel.socket; + boolean process() throws IOException { - while (channel.isOpen()) { - synchronized (channel.interestOpsLock) { - while (!channel.isReadable()) { - try { - // notify() is not called at all. - // close() and setInterestOps() calls Thread.interrupt() - channel.interestOpsLock.wait(); - } catch (InterruptedException e) { - if (!channel.isOpen()) { - break; - } - } - } - } + ReceiveBufferSizePredictor predictor = + channel.getConfig().getReceiveBufferSizePredictor(); - ReceiveBufferSizePredictor predictor = - channel.getConfig().getReceiveBufferSizePredictor(); + byte[] buf = new byte[predictor.nextReceiveBufferSize()]; + DatagramPacket packet = new DatagramPacket(buf, buf.length); + try { + channel.socket.receive(packet); + } catch (InterruptedIOException e) { + // Can happen on interruption. + // Keep receiving unless the channel is closed. + return true; + } - byte[] buf = new byte[predictor.nextReceiveBufferSize()]; - DatagramPacket packet = new DatagramPacket(buf, buf.length); - try { - socket.receive(packet); - } catch (InterruptedIOException e) { - // Can happen on interruption. - // Keep receiving unless the channel is closed. - continue; - } catch (Throwable t) { - if (!channel.socket.isClosed()) { - fireExceptionCaught(channel, t); - } - break; - } - - fireMessageReceived( - channel, - channel.getConfig().getBufferFactory().getBuffer(buf, 0, packet.getLength()), - packet.getSocketAddress()); - } - - // Setting the workerThread to null will prevent any channel - // operations from interrupting this thread from now on. - channel.workerThread = null; - - // Clean up. - close(channel, succeededFuture(channel)); + fireMessageReceived( + channel, + channel.getConfig().getBufferFactory().getBuffer(buf, 0, packet.getLength()), + packet.getSocketAddress()); + return true; } + + static void write( OioDatagramChannel channel, ChannelFuture future, Object message, SocketAddress remoteAddress) { + boolean iothread = isIoThread(channel); + try { ChannelBuffer buf = (ChannelBuffer) message; int offset = buf.readerIndex(); @@ -112,94 +86,46 @@ class OioDatagramWorker implements Runnable { packet.setSocketAddress(remoteAddress); } channel.socket.send(packet); - fireWriteComplete(channel, length); + if (iothread) { + fireWriteComplete(channel, length); + } else { + fireWriteCompleteLater(channel, length); + } future.setSuccess(); } catch (Throwable t) { future.setFailure(t); - fireExceptionCaught(channel, t); - } - } - - static void setInterestOps( - OioDatagramChannel channel, ChannelFuture future, int interestOps) { - - // Override OP_WRITE flag - a user cannot change this flag. - interestOps &= ~Channel.OP_WRITE; - interestOps |= channel.getInterestOps() & Channel.OP_WRITE; - - boolean changed = false; - try { - if (channel.getInterestOps() != interestOps) { - if ((interestOps & Channel.OP_READ) != 0) { - channel.setInterestOpsNow(Channel.OP_READ); - } else { - channel.setInterestOpsNow(Channel.OP_NONE); - } - changed = true; - } - - future.setSuccess(); - if (changed) { - synchronized (channel.interestOpsLock) { - channel.setInterestOpsNow(interestOps); - - // Notify the worker so it stops or continues reading. - Thread currentThread = Thread.currentThread(); - Thread workerThread = channel.workerThread; - if (workerThread != null && currentThread != workerThread) { - workerThread.interrupt(); - } - } - - fireChannelInterestChanged(channel); - } - } catch (Throwable t) { - future.setFailure(t); - fireExceptionCaught(channel, t); + if (iothread) { + fireExceptionCaught(channel, t); + } else { + fireExceptionCaughtLater(channel, t); + } } } + static void disconnect(OioDatagramChannel channel, ChannelFuture future) { boolean connected = channel.isConnected(); + boolean iothread = isIoThread(channel); + try { channel.socket.disconnect(); future.setSuccess(); if (connected) { // Notify. - fireChannelDisconnected(channel); + if (iothread) { + fireChannelDisconnected(channel); + } else { + fireChannelDisconnectedLater(channel); + } } } catch (Throwable t) { future.setFailure(t); - fireExceptionCaught(channel, t); + if (iothread) { + fireExceptionCaught(channel, t); + } else { + fireExceptionCaughtLater(channel, t); + } } } - static void close(OioDatagramChannel channel, ChannelFuture future) { - boolean connected = channel.isConnected(); - boolean bound = channel.isBound(); - try { - channel.socket.close(); - if (channel.setClosed()) { - future.setSuccess(); - if (connected) { - // Notify the worker so it stops reading. - Thread currentThread = Thread.currentThread(); - Thread workerThread = channel.workerThread; - if (workerThread != null && currentThread != workerThread) { - workerThread.interrupt(); - } - fireChannelDisconnected(channel); - } - if (bound) { - fireChannelUnbound(channel); - } - fireChannelClosed(channel); - } else { - future.setSuccess(); - } - } catch (Throwable t) { - future.setFailure(t); - fireExceptionCaught(channel, t); - } - } } diff --git a/transport/src/main/java/io/netty/channel/socket/oio/OioServerSocketPipelineSink.java b/transport/src/main/java/io/netty/channel/socket/oio/OioServerSocketPipelineSink.java index e8c4c3278f..5daad24afc 100644 --- a/transport/src/main/java/io/netty/channel/socket/oio/OioServerSocketPipelineSink.java +++ b/transport/src/main/java/io/netty/channel/socket/oio/OioServerSocketPipelineSink.java @@ -24,7 +24,6 @@ import java.net.SocketTimeoutException; import java.util.concurrent.Executor; import java.util.concurrent.atomic.AtomicInteger; -import io.netty.channel.AbstractChannelSink; import io.netty.channel.Channel; import io.netty.channel.ChannelEvent; import io.netty.channel.ChannelFuture; @@ -36,7 +35,7 @@ import io.netty.logging.InternalLogger; import io.netty.logging.InternalLoggerFactory; import io.netty.util.internal.DeadLockProofWorker; -class OioServerSocketPipelineSink extends AbstractChannelSink { +class OioServerSocketPipelineSink extends AbstractOioChannelSink { static final InternalLogger logger = InternalLoggerFactory.getInstance(OioServerSocketPipelineSink.class); 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 bd1ccdb588..2b3303e519 100644 --- a/transport/src/main/java/io/netty/channel/socket/oio/OioSocketChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/oio/OioSocketChannel.java @@ -15,31 +15,25 @@ */ package io.netty.channel.socket.oio; +import java.io.IOException; import java.io.OutputStream; import java.io.PushbackInputStream; import java.net.InetSocketAddress; import java.net.Socket; -import java.net.SocketAddress; -import io.netty.channel.AbstractChannel; import io.netty.channel.Channel; import io.netty.channel.ChannelFactory; -import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelSink; import io.netty.channel.socket.DefaultSocketChannelConfig; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.SocketChannelConfig; -abstract class OioSocketChannel extends AbstractChannel +abstract class OioSocketChannel extends AbstractOioChannel implements SocketChannel { final Socket socket; - final Object interestOpsLock = new Object(); private final SocketChannelConfig config; - volatile Thread workerThread; - private volatile InetSocketAddress localAddress; - private volatile InetSocketAddress remoteAddress; OioSocketChannel( Channel parent, @@ -59,65 +53,36 @@ abstract class OioSocketChannel extends AbstractChannel return config; } - @Override - public InetSocketAddress getLocalAddress() { - InetSocketAddress localAddress = this.localAddress; - if (localAddress == null) { - try { - this.localAddress = localAddress = - (InetSocketAddress) socket.getLocalSocketAddress(); - } catch (Throwable t) { - // Sometimes fails on a closed socket in Windows. - return null; - } - } - return localAddress; - } - - @Override - public InetSocketAddress getRemoteAddress() { - InetSocketAddress remoteAddress = this.remoteAddress; - if (remoteAddress == null) { - try { - this.remoteAddress = remoteAddress = - (InetSocketAddress) socket.getRemoteSocketAddress(); - } catch (Throwable t) { - // Sometimes fails on a closed socket in Windows. - return null; - } - } - return remoteAddress; - } - - @Override - public boolean isBound() { - return isOpen() && socket.isBound(); - } - - @Override - public boolean isConnected() { - return isOpen() && socket.isConnected(); - } - - @Override - protected boolean setClosed() { - return super.setClosed(); - } - - @Override - protected void setInterestOpsNow(int interestOps) { - super.setInterestOpsNow(interestOps); - } - abstract PushbackInputStream getInputStream(); abstract OutputStream getOutputStream(); @Override - public ChannelFuture write(Object message, SocketAddress remoteAddress) { - if (remoteAddress == null || remoteAddress.equals(getRemoteAddress())) { - return super.write(message, null); - } else { - return getUnsupportedOperationFuture(); - } + boolean isSocketBound() { + return socket.isBound(); + } + + @Override + boolean isSocketConnected() { + return socket.isConnected(); + } + + @Override + InetSocketAddress getLocalSocketAddress() throws Exception { + return (InetSocketAddress) socket.getLocalSocketAddress(); + } + + @Override + InetSocketAddress getRemoteSocketAddress() throws Exception { + return (InetSocketAddress) socket.getRemoteSocketAddress(); + } + + @Override + void closeSocket() throws IOException { + socket.close(); + } + + @Override + boolean isSocketClosed() { + return socket.isClosed(); } } diff --git a/transport/src/main/java/io/netty/channel/socket/oio/OioWorker.java b/transport/src/main/java/io/netty/channel/socket/oio/OioWorker.java index 9afe62ba62..5d4eb6aa3b 100644 --- a/transport/src/main/java/io/netty/channel/socket/oio/OioWorker.java +++ b/transport/src/main/java/io/netty/channel/socket/oio/OioWorker.java @@ -17,6 +17,7 @@ package io.netty.channel.socket.oio; import static io.netty.channel.Channels.*; +import java.io.IOException; import java.io.OutputStream; import java.io.PushbackInputStream; import java.net.SocketException; @@ -26,90 +27,54 @@ import java.nio.channels.WritableByteChannel; import java.util.regex.Pattern; import io.netty.buffer.ChannelBuffer; -import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.FileRegion; -class OioWorker implements Runnable { +class OioWorker extends AbstractOioWorker { private static final Pattern SOCKET_CLOSED_MESSAGE = Pattern.compile( "^.*(?:Socket.*closed).*$", Pattern.CASE_INSENSITIVE); - private final OioSocketChannel channel; - OioWorker(OioSocketChannel channel) { - this.channel = channel; + super(channel); } @Override - public void run() { - channel.workerThread = Thread.currentThread(); - final PushbackInputStream in = channel.getInputStream(); - boolean fireOpen = channel instanceof OioAcceptedSocketChannel; - - while (channel.isOpen()) { - if (fireOpen) { - fireOpen = false; - fireChannelConnected(channel, channel.getRemoteAddress()); + boolean process() throws IOException { + byte[] buf; + int readBytes; + PushbackInputStream in = channel.getInputStream(); + int bytesToRead = in.available(); + if (bytesToRead > 0) { + buf = new byte[bytesToRead]; + readBytes = in.read(buf); + } else { + int b = in.read(); + if (b < 0) { + return false; } - synchronized (channel.interestOpsLock) { - while (!channel.isReadable()) { - try { - // notify() is not called at all. - // close() and setInterestOps() calls Thread.interrupt() - channel.interestOpsLock.wait(); - } catch (InterruptedException e) { - if (!channel.isOpen()) { - break; - } - } - } - } - - byte[] buf; - int readBytes; - try { - int bytesToRead = in.available(); - if (bytesToRead > 0) { - buf = new byte[bytesToRead]; - readBytes = in.read(buf); - } else { - int b = in.read(); - if (b < 0) { - break; - } - in.unread(b); - continue; - } - } catch (Throwable t) { - if (!channel.socket.isClosed()) { - fireExceptionCaught(channel, t); - } - break; - } - - fireMessageReceived( - channel, - channel.getConfig().getBufferFactory().getBuffer(buf, 0, readBytes)); + in.unread(b); + return true; } - - // Setting the workerThread to null will prevent any channel - // operations from interrupting this thread from now on. - channel.workerThread = null; - - // Clean up. - close(channel, succeededFuture(channel)); + fireMessageReceived(channel, channel.getConfig().getBufferFactory().getBuffer(buf, 0, readBytes)); + + return true; } static void write( OioSocketChannel channel, ChannelFuture future, Object message) { + boolean iothread = isIoThread(channel); OutputStream out = channel.getOutputStream(); if (out == null) { Exception e = new ClosedChannelException(); future.setFailure(e); - fireExceptionCaught(channel, e); + if (iothread) { + fireExceptionCaught(channel, e); + } else { + fireExceptionCaughtLater(channel, e); + } return; } @@ -146,7 +111,11 @@ class OioWorker implements Runnable { } } - fireWriteComplete(channel, length); + if (iothread) { + fireWriteComplete(channel, length); + } else { + fireWriteCompleteLater(channel, length); + } future.setSuccess(); } catch (Throwable t) { @@ -158,75 +127,13 @@ class OioWorker implements Runnable { t = new ClosedChannelException(); } future.setFailure(t); - fireExceptionCaught(channel, t); - } - } - - static void setInterestOps( - OioSocketChannel channel, ChannelFuture future, int interestOps) { - - // Override OP_WRITE flag - a user cannot change this flag. - interestOps &= ~Channel.OP_WRITE; - interestOps |= channel.getInterestOps() & Channel.OP_WRITE; - - boolean changed = false; - try { - if (channel.getInterestOps() != interestOps) { - if ((interestOps & Channel.OP_READ) != 0) { - channel.setInterestOpsNow(Channel.OP_READ); - } else { - channel.setInterestOpsNow(Channel.OP_NONE); - } - changed = true; - } - - future.setSuccess(); - if (changed) { - synchronized (channel.interestOpsLock) { - channel.setInterestOpsNow(interestOps); - - // Notify the worker so it stops or continues reading. - Thread currentThread = Thread.currentThread(); - Thread workerThread = channel.workerThread; - if (workerThread != null && currentThread != workerThread) { - workerThread.interrupt(); - } - } - - fireChannelInterestChanged(channel); - } - } catch (Throwable t) { - future.setFailure(t); - fireExceptionCaught(channel, t); - } - } - - static void close(OioSocketChannel channel, ChannelFuture future) { - boolean connected = channel.isConnected(); - boolean bound = channel.isBound(); - try { - channel.socket.close(); - if (channel.setClosed()) { - future.setSuccess(); - if (connected) { - // Notify the worker so it stops reading. - Thread currentThread = Thread.currentThread(); - Thread workerThread = channel.workerThread; - if (workerThread != null && currentThread != workerThread) { - workerThread.interrupt(); - } - fireChannelDisconnected(channel); - } - if (bound) { - fireChannelUnbound(channel); - } - fireChannelClosed(channel); + if (iothread) { + fireExceptionCaught(channel, t); } else { - future.setSuccess(); + fireExceptionCaughtLater(channel, t); } - } catch (Throwable t) { - future.setFailure(t); - fireExceptionCaught(channel, t); } } + + }