diff --git a/all/pom.xml b/all/pom.xml index 9df6cdb3da..7f4fce2356 100644 --- a/all/pom.xml +++ b/all/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.0.0.Final-SNAPSHOT + 4.0.0.CR4-SNAPSHOT netty-all diff --git a/buffer/pom.xml b/buffer/pom.xml index 310ebb5649..93e1e56f39 100644 --- a/buffer/pom.xml +++ b/buffer/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.0.0.Final-SNAPSHOT + 4.0.0.CR4-SNAPSHOT netty-buffer diff --git a/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java b/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java index 0828ab1e0b..21351f8a13 100644 --- a/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/AbstractByteBuf.java @@ -50,11 +50,6 @@ public abstract class AbstractByteBuf implements ByteBuf { this.maxCapacity = maxCapacity; } - @Override - public BufType type() { - return BufType.BYTE; - } - @Override public int maxCapacity() { return maxCapacity; @@ -971,17 +966,17 @@ public abstract class AbstractByteBuf implements ByteBuf { nioBuffer.flip(); } - return BufUtil.decodeString(nioBuffer, charset); + return ByteBufUtil.decodeString(nioBuffer, charset); } @Override public int indexOf(int fromIndex, int toIndex, byte value) { - return BufUtil.indexOf(this, fromIndex, toIndex, value); + return ByteBufUtil.indexOf(this, fromIndex, toIndex, value); } @Override public int indexOf(int fromIndex, int toIndex, ByteBufIndexFinder indexFinder) { - return BufUtil.indexOf(this, fromIndex, toIndex, indexFinder); + return ByteBufUtil.indexOf(this, fromIndex, toIndex, indexFinder); } @Override @@ -1027,7 +1022,7 @@ public abstract class AbstractByteBuf implements ByteBuf { @Override public int hashCode() { - return BufUtil.hashCode(this); + return ByteBufUtil.hashCode(this); } @Override @@ -1036,14 +1031,14 @@ public abstract class AbstractByteBuf implements ByteBuf { return true; } if (o instanceof ByteBuf) { - return BufUtil.equals(this, (ByteBuf) o); + return ByteBufUtil.equals(this, (ByteBuf) o); } return false; } @Override public int compareTo(ByteBuf that) { - return BufUtil.compare(this, that); + return ByteBufUtil.compare(this, that); } @Override diff --git a/buffer/src/main/java/io/netty/buffer/AbstractDerivedByteBuf.java b/buffer/src/main/java/io/netty/buffer/AbstractDerivedByteBuf.java index 4ef5961f55..13f906f6a6 100644 --- a/buffer/src/main/java/io/netty/buffer/AbstractDerivedByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/AbstractDerivedByteBuf.java @@ -52,14 +52,4 @@ public abstract class AbstractDerivedByteBuf extends AbstractByteBuf { public final boolean release(int decrement) { return unwrap().release(decrement); } - - @Override - public final ByteBuf suspendIntermediaryDeallocations() { - throw new UnsupportedOperationException("derived"); - } - - @Override - public final ByteBuf resumeIntermediaryDeallocations() { - throw new UnsupportedOperationException("derived"); - } } diff --git a/buffer/src/main/java/io/netty/buffer/AbstractMessageBuf.java b/buffer/src/main/java/io/netty/buffer/AbstractMessageBuf.java deleted file mode 100644 index ec9067239c..0000000000 --- a/buffer/src/main/java/io/netty/buffer/AbstractMessageBuf.java +++ /dev/null @@ -1,258 +0,0 @@ -/* - * Copyright 2013 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.buffer; - -import io.netty.util.internal.PlatformDependent; - -import java.util.AbstractQueue; -import java.util.Collection; -import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; - -/** - * Abstract base class for {@link MessageBuf} implementations. - * @param - */ -public abstract class AbstractMessageBuf extends AbstractQueue implements MessageBuf { - - @SuppressWarnings("rawtypes") - private static final AtomicIntegerFieldUpdater refCntUpdater = - AtomicIntegerFieldUpdater.newUpdater(AbstractMessageBuf.class, "refCnt"); - - private static final long REFCNT_FIELD_OFFSET; - - static { - long refCntFieldOffset = -1; - try { - if (PlatformDependent.hasUnsafe()) { - refCntFieldOffset = PlatformDependent.objectFieldOffset( - AbstractMessageBuf.class.getDeclaredField("refCnt")); - } - } catch (Throwable t) { - // Ignored - } - - REFCNT_FIELD_OFFSET = refCntFieldOffset; - } - - private final int maxCapacity; - - @SuppressWarnings("FieldMayBeFinal") - private volatile int refCnt = 1; - - protected AbstractMessageBuf(int maxCapacity) { - if (maxCapacity < 0) { - throw new IllegalArgumentException("maxCapacity: " + maxCapacity + " (expected: >= 0)"); - } - this.maxCapacity = maxCapacity; - } - - @Override - public final BufType type() { - return BufType.MESSAGE; - } - - @Override - public final int refCnt() { - if (REFCNT_FIELD_OFFSET >= 0) { - // Try to do non-volatile read for performance. - return PlatformDependent.getInt(this, REFCNT_FIELD_OFFSET); - } else { - return refCnt; - } - } - - @Override - public MessageBuf retain() { - for (;;) { - int refCnt = this.refCnt; - if (refCnt == 0) { - throw new IllegalBufferAccessException(); - } - if (refCnt == Integer.MAX_VALUE) { - throw new IllegalBufferAccessException("refCnt overflow"); - } - if (refCntUpdater.compareAndSet(this, refCnt, refCnt + 1)) { - break; - } - } - return this; - } - - @Override - public MessageBuf retain(int increment) { - if (increment <= 0) { - throw new IllegalArgumentException("increment: " + increment + " (expected: > 0)"); - } - - for (;;) { - int refCnt = this.refCnt; - if (refCnt == 0) { - throw new IllegalBufferAccessException(); - } - if (refCnt > Integer.MAX_VALUE - increment) { - throw new IllegalBufferAccessException("refCnt overflow"); - } - if (refCntUpdater.compareAndSet(this, refCnt, refCnt + increment)) { - break; - } - } - return this; - } - - @Override - public final boolean release() { - for (;;) { - int refCnt = this.refCnt; - if (refCnt == 0) { - throw new IllegalBufferAccessException(); - } - - if (refCntUpdater.compareAndSet(this, refCnt, refCnt - 1)) { - if (refCnt == 1) { - deallocate(); - return true; - } - return false; - } - } - } - - @Override - public final boolean release(int decrement) { - if (decrement <= 0) { - throw new IllegalArgumentException("decrement: " + decrement + " (expected: > 0)"); - } - - for (;;) { - int refCnt = this.refCnt; - if (refCnt < decrement) { - throw new IllegalBufferAccessException(); - } - - if (refCntUpdater.compareAndSet(this, refCnt, refCnt - decrement)) { - if (refCnt == decrement) { - deallocate(); - return true; - } - return false; - } - } - } - - protected abstract void deallocate(); - - @Override - public final int maxCapacity() { - return maxCapacity; - } - - @Override - public final boolean isReadable() { - return !isEmpty(); - } - - @Override - public final boolean isReadable(int size) { - if (size < 0) { - throw new IllegalArgumentException("size: " + size + " (expected: >= 0)"); - } - return size() >= size; - } - - @Override - public final boolean isWritable() { - return size() < maxCapacity; - } - - @Override - public final boolean isWritable(int size) { - if (size < 0) { - throw new IllegalArgumentException("size: " + size + " (expected: >= 0)"); - } - return size() <= maxCapacity - size; - } - - protected final void ensureAccessible() { - if (refCnt <= 0) { - throw new IllegalBufferAccessException(); - } - } - - @Override - public final boolean add(T t) { - return super.add(t); - } - - @Override - public final T remove() { - return super.remove(); - } - - @Override - public final T element() { - return super.element(); - } - - @Override - public int drainTo(Collection c) { - ensureAccessible(); - int cnt = 0; - for (;;) { - T o = poll(); - if (o == null) { - break; - } - c.add(o); - cnt ++; - } - return cnt; - } - - @Override - public int drainTo(Collection c, int maxElements) { - ensureAccessible(); - int cnt = 0; - while (cnt < maxElements) { - T o = poll(); - if (o == null) { - break; - } - c.add(o); - cnt ++; - } - return cnt; - } - - @Override - public String toString() { - if (refCnt <= 0) { - return getClass().getSimpleName() + "(freed)"; - } - - StringBuilder buf = new StringBuilder(); - buf.append(getClass().getSimpleName()); - buf.append("(size: "); - buf.append(size()); - if (maxCapacity != Integer.MAX_VALUE) { - buf.append('/'); - buf.append(maxCapacity); - } - buf.append(')'); - - return buf.toString(); - } -} diff --git a/buffer/src/main/java/io/netty/buffer/Buf.java b/buffer/src/main/java/io/netty/buffer/Buf.java deleted file mode 100644 index 5cf36606e7..0000000000 --- a/buffer/src/main/java/io/netty/buffer/Buf.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.buffer; - -/** - * A buffer to operate on - */ -public interface Buf extends ReferenceCounted { - /** - * The BufType which will be handled by the Buf implementation - */ - BufType type(); - - /** - * Returns the maximum allowed capacity of this buffer. - */ - int maxCapacity(); - - /** - * Returns {@code true} if and only if this buffer contains at least one readable element. - */ - boolean isReadable(); - - /** - * Returns {@code true} if and only if this buffer contains equal to or more than the specified number of elements. - */ - boolean isReadable(int size); - - /** - * Returns {@code true} if and only if this buffer has enough room to allow writing one element. - */ - boolean isWritable(); - - /** - * Returns {@code true} if and only if this buffer has enough room to allow writing the specified number of - * elements. - */ - boolean isWritable(int size); - - @Override - Buf retain(); - - @Override - Buf retain(int increment); -} diff --git a/buffer/src/main/java/io/netty/buffer/ByteBuf.java b/buffer/src/main/java/io/netty/buffer/ByteBuf.java index ce0c68ea71..b201d26328 100644 --- a/buffer/src/main/java/io/netty/buffer/ByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/ByteBuf.java @@ -227,7 +227,7 @@ import java.nio.charset.UnsupportedCharsetException; * Please refer to {@link ByteBufInputStream} and * {@link ByteBufOutputStream}. */ -public interface ByteBuf extends Buf, Comparable { +public interface ByteBuf extends ReferenceCounted, Comparable { /** * Returns the number of bytes (octets) this buffer can contain. @@ -248,7 +248,6 @@ public interface ByteBuf extends Buf, Comparable { * {@link #ensureWritable(int)}, those methods will raise an * {@link IllegalArgumentException}. */ - @Override int maxCapacity(); /** @@ -391,9 +390,13 @@ public interface ByteBuf extends Buf, Comparable { * if and only if {@code (this.writerIndex - this.readerIndex)} is greater * than {@code 0}. */ - @Override boolean isReadable(); + /** + * Returns {@code true} if and only if this buffer contains equal to or more than the specified number of elements. + */ + boolean isReadable(int size); + /** * @deprecated Use {@link #isReadable()} or {@link #isReadable(int)} instead. */ @@ -405,9 +408,14 @@ public interface ByteBuf extends Buf, Comparable { * if and only if {@code (this.capacity - this.writerIndex)} is greater * than {@code 0}. */ - @Override boolean isWritable(); + /** + * Returns {@code true} if and only if this buffer has enough room to allow writing the specified number of + * elements. + */ + boolean isWritable(int size); + /** * @deprecated Use {@link #isWritable()} or {@link #isWritable(int)} instead. */ @@ -1869,23 +1877,6 @@ public interface ByteBuf extends Buf, Comparable { */ String toString(int index, int length, Charset charset); - /** - * Suspends the intermediary deallocation of the internal memory block of this buffer until asked via - * {@link #resumeIntermediaryDeallocations()}. An intermediary deallocation is usually made when the capacity of - * a buffer changes. - * - * @throws UnsupportedOperationException if this buffer is derived - */ - ByteBuf suspendIntermediaryDeallocations(); - - /** - * Resumes the intermediary deallocation of the internal memory block of this buffer, suspended by - * {@link #suspendIntermediaryDeallocations()}. - * - * @throws UnsupportedOperationException if this buffer is derived - */ - ByteBuf resumeIntermediaryDeallocations(); - /** * Returns a hash code which was calculated from the content of this * buffer. If there's a byte array which is diff --git a/buffer/src/main/java/io/netty/buffer/BufUtil.java b/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java similarity index 94% rename from buffer/src/main/java/io/netty/buffer/BufUtil.java rename to buffer/src/main/java/io/netty/buffer/ByteBufUtil.java index b651316387..323b26ed25 100644 --- a/buffer/src/main/java/io/netty/buffer/BufUtil.java +++ b/buffer/src/main/java/io/netty/buffer/ByteBufUtil.java @@ -25,13 +25,11 @@ import java.nio.charset.Charset; import java.nio.charset.CharsetDecoder; import java.nio.charset.CharsetEncoder; import java.nio.charset.CoderResult; -import java.util.Iterator; /** - * A collection of utility methods that is related with handling {@link ByteBuf}, {@link MessageBuf}, and their - * elements. + * A collection of utility methods that is related with handling {@link ByteBuf}. */ -public final class BufUtil { +public final class ByteBufUtil { private static final char[] HEXDUMP_TABLE = new char[256 * 4]; @@ -418,29 +416,5 @@ public final class BufUtil { return dst.flip().toString(); } - /** - * Return the content of the given {@link MessageBuf} as string representation. - */ - public static String contentToString(MessageBuf buf) { - if (buf.isEmpty()) { - return "[]"; - } - Iterator it = buf.iterator(); - StringBuilder sb = new StringBuilder(); - sb.append('['); - while (it.hasNext()) { - Object msg = it.next(); - if (msg == buf) { - sb.append('(' + buf.getClass().getSimpleName() + ')'); - } else { - sb.append(msg); - } - if (it.hasNext()) { - sb.append(", "); - } - } - return sb.append(']').toString(); - } - - private BufUtil() { } + private ByteBufUtil() { } } diff --git a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java index 483390bd33..f39a50608f 100644 --- a/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/CompositeByteBuf.java @@ -387,12 +387,6 @@ public interface CompositeByteBuf extends ByteBuf, Iterable { @Override CompositeByteBuf writeZero(int length); - @Override - CompositeByteBuf suspendIntermediaryDeallocations(); - - @Override - CompositeByteBuf resumeIntermediaryDeallocations(); - @Override CompositeByteBuf retain(int increment); diff --git a/buffer/src/main/java/io/netty/buffer/DefaultCompositeByteBuf.java b/buffer/src/main/java/io/netty/buffer/DefaultCompositeByteBuf.java index 49dc661d29..9dfd3ddf0e 100644 --- a/buffer/src/main/java/io/netty/buffer/DefaultCompositeByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/DefaultCompositeByteBuf.java @@ -26,14 +26,12 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.channels.GatheringByteChannel; import java.nio.channels.ScatteringByteChannel; -import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.ListIterator; -import java.util.Queue; /** @@ -50,7 +48,6 @@ public class DefaultCompositeByteBuf extends AbstractReferenceCountedByteBuf imp private final int maxNumComponents; private boolean freed; - private Queue suspendedDeallocations; public DefaultCompositeByteBuf(ByteBufAllocator alloc, boolean direct, int maxNumComponents) { super(Integer.MAX_VALUE); @@ -1202,11 +1199,7 @@ public class DefaultCompositeByteBuf extends AbstractReferenceCountedByteBuf imp void freeIfNecessary() { // Unwrap so that we can free slices, too. - if (suspendedDeallocations == null) { - buf.release(); // We should not get a NPE here. If so, it must be a bug. - } else { - suspendedDeallocations.add(buf); - } + buf.release(); // We should not get a NPE here. If so, it must be a bug. } } @@ -1457,7 +1450,6 @@ public class DefaultCompositeByteBuf extends AbstractReferenceCountedByteBuf imp } freed = true; - resumeIntermediaryDeallocations(); for (Component c: components) { c.freeIfNecessary(); } @@ -1465,30 +1457,6 @@ public class DefaultCompositeByteBuf extends AbstractReferenceCountedByteBuf imp leak.close(); } - @Override - public CompositeByteBuf suspendIntermediaryDeallocations() { - ensureAccessible(); - if (suspendedDeallocations == null) { - suspendedDeallocations = new ArrayDeque(2); - } - return this; - } - - @Override - public CompositeByteBuf resumeIntermediaryDeallocations() { - if (suspendedDeallocations == null) { - return this; - } - - Queue suspendedDeallocations = this.suspendedDeallocations; - this.suspendedDeallocations = null; - - for (ByteBuf buf: suspendedDeallocations) { - buf.release(); - } - return this; - } - @Override public ByteBuf unwrap() { return null; diff --git a/buffer/src/main/java/io/netty/buffer/DefaultMessageBuf.java b/buffer/src/main/java/io/netty/buffer/DefaultMessageBuf.java deleted file mode 100644 index 300b3d46b1..0000000000 --- a/buffer/src/main/java/io/netty/buffer/DefaultMessageBuf.java +++ /dev/null @@ -1,337 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -/* - * Written by Josh Bloch of Google Inc. and released to the public domain, - * as explained at http://creativecommons.org/publicdomain/zero/1.0/. - */ -package io.netty.buffer; - -import java.lang.reflect.Array; -import java.util.ConcurrentModificationException; -import java.util.Iterator; -import java.util.NoSuchElementException; - -/** - * Default {@link MessageBuf} implementation. - * - * You should use {@link Unpooled#messageBuffer()} to create an instance - * - */ -public class DefaultMessageBuf extends AbstractMessageBuf { - - private static final int MIN_INITIAL_CAPACITY = 8; - private static final Object[] PLACEHOLDER = new Object[2]; - - private T[] elements; - private int head; - private int tail; - - protected DefaultMessageBuf() { - this(MIN_INITIAL_CAPACITY << 1); - } - - protected DefaultMessageBuf(int initialCapacity) { - this(initialCapacity, Integer.MAX_VALUE); - } - - protected DefaultMessageBuf(int initialCapacity, int maxCapacity) { - super(maxCapacity); - - if (initialCapacity < 0) { - throw new IllegalArgumentException("initialCapacity: " + initialCapacity + " (expected: >= 0)"); - } - if (maxCapacity < initialCapacity) { - throw new IllegalArgumentException( - "maxCapacity: " + maxCapacity + " (expected: >= initialCapacity(" + initialCapacity + ')'); - } - - // Find the best power of two to hold elements. - // Tests "<=" because arrays aren't kept full. - if (initialCapacity >= MIN_INITIAL_CAPACITY) { - initialCapacity |= initialCapacity >>> 1; - initialCapacity |= initialCapacity >>> 2; - initialCapacity |= initialCapacity >>> 4; - initialCapacity |= initialCapacity >>> 8; - initialCapacity |= initialCapacity >>> 16; - initialCapacity ++; - - if (initialCapacity < 0) { // Too many elements, must back off - initialCapacity >>>= 1; // Good luck allocating 2 ^ 30 elements - } - } else { - initialCapacity = MIN_INITIAL_CAPACITY; - } - - elements = cast(new Object[initialCapacity]); - } - - @Override - protected void deallocate() { - head = 0; - tail = 0; - elements = cast(PLACEHOLDER); - } - - @Override - public boolean offer(T e) { - if (e == null) { - throw new NullPointerException(); - } - - ensureAccessible(); - if (!isWritable()) { - return false; - } - - elements[tail] = e; - if ((tail = tail + 1 & elements.length - 1) == head) { - doubleCapacity(); - } - - return true; - } - - private void doubleCapacity() { - assert head == tail; - - int p = head; - int n = elements.length; - int r = n - p; // number of elements to the right of p - int newCapacity = n << 1; - if (newCapacity < 0) { - throw new IllegalStateException("Sorry, deque too big"); - } - Object[] a = new Object[newCapacity]; - System.arraycopy(elements, p, a, 0, r); - System.arraycopy(elements, 0, a, r, p); - elements = cast(a); - head = 0; - tail = n; - } - - @Override - public T poll() { - ensureAccessible(); - int h = head; - T result = elements[h]; // Element is null if deque empty - if (result == null) { - return null; - } - elements[h] = null; // Must null out slot - head = h + 1 & elements.length - 1; - return result; - } - - @Override - public T peek() { - ensureAccessible(); - return elements[head]; // elements[head] is null if deque empty - } - - @Override - public boolean remove(Object o) { - if (o == null) { - return false; - } - - ensureAccessible(); - int mask = elements.length - 1; - int i = head; - T x; - while ((x = elements[i]) != null) { - if (o.equals(x)) { - delete(i); - return true; - } - i = i + 1 & mask; - } - return false; - } - - private boolean delete(int i) { - assert elements[tail] == null; - assert head == tail ? elements[head] == null - : elements[head] != null && elements[tail - 1 & elements.length - 1] != null; - assert elements[head - 1 & elements.length - 1] == null; - - final T[] elements = this.elements; - final int mask = elements.length - 1; - final int h = head; - final int t = tail; - final int front = i - h & mask; - final int back = t - i & mask; - - // Invariant: head <= i < tail mod circularity - if (front >= (t - h & mask)) { - throw new ConcurrentModificationException(); - } - - // Optimize for least element motion - if (front < back) { - if (h <= i) { - System.arraycopy(elements, h, elements, h + 1, front); - } else { // Wrap around - System.arraycopy(elements, 0, elements, 1, i); - elements[0] = elements[mask]; - System.arraycopy(elements, h, elements, h + 1, mask - h); - } - elements[h] = null; - head = h + 1 & mask; - return false; - } else { - if (i < t) { // Copy the null tail as well - System.arraycopy(elements, i + 1, elements, i, back); - tail = t - 1; - } else { // Wrap around - System.arraycopy(elements, i + 1, elements, i, mask - i); - elements[mask] = elements[0]; - System.arraycopy(elements, 1, elements, 0, t); - tail = t - 1 & mask; - } - return true; - } - } - - @Override - public int size() { - return tail - head & elements.length - 1; - } - - @Override - public boolean isEmpty() { - return head == tail; - } - - @Override - public Iterator iterator() { - ensureAccessible(); - return new Itr(); - } - - @Override - public boolean contains(Object o) { - if (o == null) { - return false; - } - - ensureAccessible(); - final int mask = elements.length - 1; - int i = head; - Object e; - while ((e = elements[i]) != null) { - if (o.equals(e)) { - return true; - } - i = i + 1 & mask; - } - - return false; - } - - @Override - public void clear() { - ensureAccessible(); - int head = this.head; - int tail = this.tail; - if (head != tail) { - this.head = this.tail = 0; - final int mask = elements.length - 1; - int i = head; - do { - elements[i] = null; - i = i + 1 & mask; - } while (i != tail); - } - } - - @Override - public Object[] toArray() { - ensureAccessible(); - return copyElements(new Object[size()]); - } - - @Override - public T[] toArray(T[] a) { - ensureAccessible(); - int size = size(); - if (a.length < size) { - a = cast(Array.newInstance(a.getClass().getComponentType(), size)); - } - copyElements(a); - if (a.length > size) { - a[size] = null; - } - return a; - } - - private U[] copyElements(U[] a) { - if (head < tail) { - System.arraycopy(elements, head, cast(a), 0, size()); - } else if (head > tail) { - int headPortionLen = elements.length - head; - System.arraycopy(elements, head, cast(a), 0, headPortionLen); - System.arraycopy(elements, 0, cast(a), headPortionLen, tail); - } - return a; - } - - @SuppressWarnings({ "unchecked", "SuspiciousArrayCast" }) - private static T[] cast(Object a) { - return (T[]) a; - } - - private class Itr implements Iterator { - private int cursor = head; - private int fence = tail; - private int lastRet = -1; - - @Override - public boolean hasNext() { - ensureAccessible(); - return cursor != fence; - } - - @Override - public T next() { - ensureAccessible(); - if (cursor == fence) { - throw new NoSuchElementException(); - } - T result = elements[cursor]; - // This check doesn't catch all possible comodifications, - // but does catch the ones that corrupt traversal - if (tail != fence || result == null) { - throw new ConcurrentModificationException(); - } - lastRet = cursor; - cursor = cursor + 1 & elements.length - 1; - return result; - } - - @Override - public void remove() { - ensureAccessible(); - if (lastRet < 0) { - throw new IllegalStateException(); - } - if (delete(lastRet)) { // if left-shifted, undo increment in next() - cursor = cursor - 1 & elements.length - 1; - fence = tail; - } - lastRet = -1; - } - } -} diff --git a/buffer/src/main/java/io/netty/buffer/EmptyByteBuf.java b/buffer/src/main/java/io/netty/buffer/EmptyByteBuf.java index 07745ec127..dcf152508d 100644 --- a/buffer/src/main/java/io/netty/buffer/EmptyByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/EmptyByteBuf.java @@ -800,16 +800,6 @@ public final class EmptyByteBuf implements ByteBuf { return toString(charset); } - @Override - public ByteBuf suspendIntermediaryDeallocations() { - return this; - } - - @Override - public ByteBuf resumeIntermediaryDeallocations() { - return this; - } - @Override public int hashCode() { return 0; @@ -830,11 +820,6 @@ public final class EmptyByteBuf implements ByteBuf { return str; } - @Override - public BufType type() { - return BufType.BYTE; - } - @Override public boolean isReadable(int size) { checkLength(size); diff --git a/buffer/src/main/java/io/netty/buffer/FilteredMessageBuf.java b/buffer/src/main/java/io/netty/buffer/FilteredMessageBuf.java deleted file mode 100644 index 496814db08..0000000000 --- a/buffer/src/main/java/io/netty/buffer/FilteredMessageBuf.java +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Copyright 2013 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.buffer; - -import java.util.Collection; -import java.util.Iterator; - -public abstract class FilteredMessageBuf implements MessageBuf { - - protected final MessageBuf buf; - - @SuppressWarnings("unchecked") - protected FilteredMessageBuf(MessageBuf buf) { - if (buf == null) { - throw new NullPointerException("buf"); - } - this.buf = (MessageBuf) buf; - } - - protected abstract Object filter(Object msg); - - @Override - public int drainTo(Collection c) { - return buf.drainTo(c); - } - - @Override - public int drainTo(Collection c, int maxElements) { - return buf.drainTo(c, maxElements); - } - - @Override - public BufType type() { - return buf.type(); - } - - @Override - public int maxCapacity() { - return buf.maxCapacity(); - } - - @Override - public boolean isReadable() { - return buf.isReadable(); - } - - @Override - public boolean isReadable(int size) { - return buf.isReadable(size); - } - - @Override - public boolean isWritable() { - return buf.isWritable(); - } - - @Override - public boolean isWritable(int size) { - return buf.isWritable(size); - } - - @Override - public boolean add(Object e) { - if (e == null) { - throw new NullPointerException("e"); - } - - e = filter(e); - ensureNonNull(e); - - return buf.add(e); - } - - @Override - public boolean offer(Object e) { - if (e == null) { - throw new NullPointerException("e"); - } - - e = filter(e); - ensureNonNull(e); - - return buf.offer(e); - } - - private void ensureNonNull(Object e) { - if (e == null) { - throw new IllegalStateException(getClass().getSimpleName() + ".filter() returned null"); - } - } - - @Override - public Object remove() { - return buf.remove(); - } - - @Override - public Object poll() { - return buf.poll(); - } - - @Override - public Object element() { - return buf.element(); - } - - @Override - public Object peek() { - return buf.peek(); - } - - @Override - public int size() { - return buf.size(); - } - - @Override - public boolean isEmpty() { - return buf.isEmpty(); - } - - @Override - public boolean contains(Object o) { - return buf.contains(o); - } - - @Override - public Iterator iterator() { - return buf.iterator(); - } - - @Override - public Object[] toArray() { - return buf.toArray(); - } - - @Override - public T[] toArray(T[] a) { - return buf.toArray(a); - } - - @Override - public boolean remove(Object o) { - return buf.remove(o); - } - - @Override - public boolean containsAll(Collection c) { - return buf.containsAll(c); - } - - @Override - public boolean addAll(Collection c) { - int i = 0; - boolean added = false; - for (Object e: c) { - if (e == null) { - throw new NullPointerException("c[" + i + ']'); - } - - e = filter(e); - ensureNonNull(e); - added |= buf.add(e); - } - return added; - } - - @Override - public boolean removeAll(Collection c) { - return buf.removeAll(c); - } - - @Override - public boolean retainAll(Collection c) { - return buf.retainAll(c); - } - - @Override - public void clear() { - buf.clear(); - } - - @Override - public int refCnt() { - return buf.refCnt(); - } - - @Override - public MessageBuf retain() { - buf.retain(); - return this; - } - - @Override - public MessageBuf retain(int increment) { - buf.retain(increment); - return this; - } - - @Override - public boolean release() { - return buf.release(); - } - - @Override - public boolean release(int decrement) { - return buf.release(decrement); - } - - @Override - public String toString() { - return getClass().getSimpleName() + '(' + buf + ')'; - } -} - diff --git a/buffer/src/main/java/io/netty/buffer/MessageBuf.java b/buffer/src/main/java/io/netty/buffer/MessageBuf.java deleted file mode 100644 index e5d47df8e7..0000000000 --- a/buffer/src/main/java/io/netty/buffer/MessageBuf.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.buffer; - -import java.util.Collection; -import java.util.Queue; - -/** - * Buf which operates on messages. - * - * @param the type of the messages that are hold by this {@link MessageBuf} - */ -public interface MessageBuf extends Buf, Queue { - - /** - * Drain the content of the {@link MessageBuf} to the given {@link Collection}. - * - * @param c the {@link Collection} to drain the content to - * @return number the number of objects which was transfered - */ - int drainTo(Collection c); - - /** - * Drain the content of the {@link MessageBuf} to the given {@link Collection}. - * - * @param c the {@link Collection} to drain the content to - * @param maxElements the max number of elements to drain - * @return number the number of objects which was transfered - */ - int drainTo(Collection c, int maxElements); - - @Override - MessageBuf retain(int increment); - - @Override - MessageBuf retain(); -} diff --git a/buffer/src/main/java/io/netty/buffer/PooledByteBuf.java b/buffer/src/main/java/io/netty/buffer/PooledByteBuf.java index 1e67c88c44..153c3475c9 100644 --- a/buffer/src/main/java/io/netty/buffer/PooledByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/PooledByteBuf.java @@ -20,8 +20,6 @@ import io.netty.util.ResourceLeak; import java.nio.ByteBuffer; import java.nio.ByteOrder; -import java.util.ArrayDeque; -import java.util.Queue; abstract class PooledByteBuf extends AbstractReferenceCountedByteBuf { @@ -35,7 +33,6 @@ abstract class PooledByteBuf extends AbstractReferenceCountedByteBuf { private int maxLength; private ByteBuffer tmpNioBuf; - private Queue> suspendedDeallocations; protected PooledByteBuf(int maxCapacity) { super(maxCapacity); @@ -108,13 +105,7 @@ abstract class PooledByteBuf extends AbstractReferenceCountedByteBuf { } // Reallocation required. - if (suspendedDeallocations == null) { - chunk.arena.reallocate(this, newCapacity, true); - } else { - Allocation old = new Allocation(chunk, handle); - chunk.arena.reallocate(this, newCapacity, false); - suspendedDeallocations.add(old); - } + chunk.arena.reallocate(this, newCapacity, true); return this; } @@ -143,38 +134,9 @@ abstract class PooledByteBuf extends AbstractReferenceCountedByteBuf { protected abstract ByteBuffer newInternalNioBuffer(T memory); - @Override - public final ByteBuf suspendIntermediaryDeallocations() { - ensureAccessible(); - if (suspendedDeallocations == null) { - suspendedDeallocations = new ArrayDeque>(2); - } - return this; - } - - @Override - public final ByteBuf resumeIntermediaryDeallocations() { - if (suspendedDeallocations == null) { - return this; - } - - Queue> suspendedDeallocations = this.suspendedDeallocations; - this.suspendedDeallocations = null; - - if (suspendedDeallocations.isEmpty()) { - return this; - } - - for (Allocation a: suspendedDeallocations) { - a.chunk.arena.free(a.chunk, a.handle); - } - return this; - } - @Override protected final void deallocate() { if (handle >= 0) { - resumeIntermediaryDeallocations(); final long handle = this.handle; this.handle = -1; memory = null; @@ -186,14 +148,4 @@ abstract class PooledByteBuf extends AbstractReferenceCountedByteBuf { protected final int idx(int index) { return offset + index; } - - private static final class Allocation { - final PoolChunk chunk; - final long handle; - - Allocation(PoolChunk chunk, long handle) { - this.chunk = chunk; - this.handle = handle; - } - } } diff --git a/buffer/src/main/java/io/netty/buffer/QueueBackedMessageBuf.java b/buffer/src/main/java/io/netty/buffer/QueueBackedMessageBuf.java deleted file mode 100644 index 6c193c2a3d..0000000000 --- a/buffer/src/main/java/io/netty/buffer/QueueBackedMessageBuf.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.buffer; - -import java.util.Collection; -import java.util.Iterator; -import java.util.Queue; - -final class QueueBackedMessageBuf extends AbstractMessageBuf { - - private Queue queue; - - QueueBackedMessageBuf(Queue queue) { - super(Integer.MAX_VALUE); - if (queue == null) { - throw new NullPointerException("queue"); - } - this.queue = queue; - } - - @Override - public boolean offer(T e) { - if (e == null) { - throw new NullPointerException("e"); - } - ensureAccessible(); - return isWritable() && queue.offer(e); - } - - @Override - public T poll() { - ensureAccessible(); - return queue.poll(); - } - - @Override - public T peek() { - ensureAccessible(); - return queue.peek(); - } - - @Override - public int size() { - return queue.size(); - } - - @Override - public boolean isEmpty() { - return queue.isEmpty(); - } - - @Override - public boolean contains(Object o) { - ensureAccessible(); - return queue.contains(o); - } - - @Override - public Iterator iterator() { - ensureAccessible(); - return queue.iterator(); - } - - @Override - public Object[] toArray() { - ensureAccessible(); - return queue.toArray(); - } - - @Override - public E[] toArray(E[] a) { - ensureAccessible(); - return queue.toArray(a); - } - - @Override - public boolean remove(Object o) { - ensureAccessible(); - return queue.remove(o); - } - - @Override - public boolean containsAll(Collection c) { - ensureAccessible(); - return queue.containsAll(c); - } - - @Override - public boolean addAll(Collection c) { - ensureAccessible(); - return isWritable(c.size()) && queue.addAll(c); - } - - @Override - public boolean removeAll(Collection c) { - ensureAccessible(); - return queue.removeAll(c); - } - - @Override - public boolean retainAll(Collection c) { - ensureAccessible(); - return queue.retainAll(c); - } - - @Override - public void clear() { - ensureAccessible(); - queue.clear(); - } - - @Override - protected void deallocate() { - for (T e: queue) { - BufUtil.release(e); - } - queue = null; - } -} diff --git a/buffer/src/main/java/io/netty/buffer/ReadOnlyByteBufferBuf.java b/buffer/src/main/java/io/netty/buffer/ReadOnlyByteBufferBuf.java index dd5345d63f..83b72bea46 100644 --- a/buffer/src/main/java/io/netty/buffer/ReadOnlyByteBufferBuf.java +++ b/buffer/src/main/java/io/netty/buffer/ReadOnlyByteBufferBuf.java @@ -316,14 +316,4 @@ class ReadOnlyByteBufferBuf extends AbstractReferenceCountedByteBuf { public long memoryAddress() { throw new UnsupportedOperationException(); } - - @Override - public ByteBuf suspendIntermediaryDeallocations() { - return this; - } - - @Override - public ByteBuf resumeIntermediaryDeallocations() { - return this; - } } diff --git a/buffer/src/main/java/io/netty/buffer/SwappedByteBuf.java b/buffer/src/main/java/io/netty/buffer/SwappedByteBuf.java index a18c4dc1e4..566e0b38b0 100644 --- a/buffer/src/main/java/io/netty/buffer/SwappedByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/SwappedByteBuf.java @@ -67,11 +67,6 @@ public final class SwappedByteBuf implements ByteBuf { return buf.alloc(); } - @Override - public BufType type() { - return BufType.MESSAGE; - } - @Override public int capacity() { return buf.capacity(); @@ -245,7 +240,7 @@ public final class SwappedByteBuf implements ByteBuf { @Override public short getShort(int index) { - return BufUtil.swapShort(buf.getShort(index)); + return ByteBufUtil.swapShort(buf.getShort(index)); } @Override @@ -255,7 +250,7 @@ public final class SwappedByteBuf implements ByteBuf { @Override public int getMedium(int index) { - return BufUtil.swapMedium(buf.getMedium(index)); + return ByteBufUtil.swapMedium(buf.getMedium(index)); } @Override @@ -265,7 +260,7 @@ public final class SwappedByteBuf implements ByteBuf { @Override public int getInt(int index) { - return BufUtil.swapInt(buf.getInt(index)); + return ByteBufUtil.swapInt(buf.getInt(index)); } @Override @@ -275,7 +270,7 @@ public final class SwappedByteBuf implements ByteBuf { @Override public long getLong(int index) { - return BufUtil.swapLong(buf.getLong(index)); + return ByteBufUtil.swapLong(buf.getLong(index)); } @Override @@ -354,25 +349,25 @@ public final class SwappedByteBuf implements ByteBuf { @Override public ByteBuf setShort(int index, int value) { - buf.setShort(index, BufUtil.swapShort((short) value)); + buf.setShort(index, ByteBufUtil.swapShort((short) value)); return this; } @Override public ByteBuf setMedium(int index, int value) { - buf.setMedium(index, BufUtil.swapMedium(value)); + buf.setMedium(index, ByteBufUtil.swapMedium(value)); return this; } @Override public ByteBuf setInt(int index, int value) { - buf.setInt(index, BufUtil.swapInt(value)); + buf.setInt(index, ByteBufUtil.swapInt(value)); return this; } @Override public ByteBuf setLong(int index, long value) { - buf.setLong(index, BufUtil.swapLong(value)); + buf.setLong(index, ByteBufUtil.swapLong(value)); return this; } @@ -463,7 +458,7 @@ public final class SwappedByteBuf implements ByteBuf { @Override public short readShort() { - return BufUtil.swapShort(buf.readShort()); + return ByteBufUtil.swapShort(buf.readShort()); } @Override @@ -473,7 +468,7 @@ public final class SwappedByteBuf implements ByteBuf { @Override public int readMedium() { - return BufUtil.swapMedium(buf.readMedium()); + return ByteBufUtil.swapMedium(buf.readMedium()); } @Override @@ -483,7 +478,7 @@ public final class SwappedByteBuf implements ByteBuf { @Override public int readInt() { - return BufUtil.swapInt(buf.readInt()); + return ByteBufUtil.swapInt(buf.readInt()); } @Override @@ -493,7 +488,7 @@ public final class SwappedByteBuf implements ByteBuf { @Override public long readLong() { - return BufUtil.swapLong(buf.readLong()); + return ByteBufUtil.swapLong(buf.readLong()); } @Override @@ -588,25 +583,25 @@ public final class SwappedByteBuf implements ByteBuf { @Override public ByteBuf writeShort(int value) { - buf.writeShort(BufUtil.swapShort((short) value)); + buf.writeShort(ByteBufUtil.swapShort((short) value)); return this; } @Override public ByteBuf writeMedium(int value) { - buf.writeMedium(BufUtil.swapMedium(value)); + buf.writeMedium(ByteBufUtil.swapMedium(value)); return this; } @Override public ByteBuf writeInt(int value) { - buf.writeInt(BufUtil.swapInt(value)); + buf.writeInt(ByteBufUtil.swapInt(value)); return this; } @Override public ByteBuf writeLong(long value) { - buf.writeLong(BufUtil.swapLong(value)); + buf.writeLong(ByteBufUtil.swapLong(value)); return this; } @@ -813,18 +808,6 @@ public final class SwappedByteBuf implements ByteBuf { return buf.toString(index, length, charset); } - @Override - public ByteBuf suspendIntermediaryDeallocations() { - buf.suspendIntermediaryDeallocations(); - return this; - } - - @Override - public ByteBuf resumeIntermediaryDeallocations() { - buf.resumeIntermediaryDeallocations(); - return this; - } - @Override public int refCnt() { return buf.refCnt(); @@ -863,14 +846,14 @@ public final class SwappedByteBuf implements ByteBuf { return true; } if (obj instanceof ByteBuf) { - return BufUtil.equals(this, (ByteBuf) obj); + return ByteBufUtil.equals(this, (ByteBuf) obj); } return false; } @Override public int compareTo(ByteBuf buffer) { - return BufUtil.compare(this, buffer); + return ByteBufUtil.compare(this, buffer); } @Override diff --git a/buffer/src/main/java/io/netty/buffer/Unpooled.java b/buffer/src/main/java/io/netty/buffer/Unpooled.java index 92ae8d2186..46fce0fc3a 100644 --- a/buffer/src/main/java/io/netty/buffer/Unpooled.java +++ b/buffer/src/main/java/io/netty/buffer/Unpooled.java @@ -23,11 +23,10 @@ import java.nio.CharBuffer; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; -import java.util.Queue; /** - * Creates a new {@link ByteBuf} or a new {@link MessageBuf} by allocating new space or by wrapping + * Creates a new {@link ByteBuf} by allocating new space or by wrapping * or copying existing byte arrays, byte buffers and a string. * *

Use static import

@@ -94,39 +93,6 @@ public final class Unpooled { */ public static final ByteBuf EMPTY_BUFFER = ALLOC.buffer(0, 0); - /** - * Creates a new {@link MessageBuf} with reasonably small initial capacity, which - * expands its capacity boundlessly on demand. - */ - public static MessageBuf messageBuffer() { - return new DefaultMessageBuf(); - } - - /** - * Creates a new {@link MessageBuf} with the specified {@code initialCapacity}. - */ - public static MessageBuf messageBuffer(int initialCapacity) { - return new DefaultMessageBuf(initialCapacity); - } - - /** - * Creates a new {@link MessageBuf} with the specified {@code initialCapacity} and - * {@code maxCapacity}. - */ - public static MessageBuf messageBuffer(int initialCapacity, int maxCapacity) { - return new DefaultMessageBuf(initialCapacity, maxCapacity); - } - - /** - * Creates a new {@link MessageBuf} which wraps the given {@code queue}. - */ - public static MessageBuf wrappedBuffer(Queue queue) { - if (queue instanceof MessageBuf) { - return (MessageBuf) queue; - } - return new QueueBackedMessageBuf(queue); - } - /** * Creates a new big-endian Java heap buffer with reasonably small initial capacity, which * expands its capacity boundlessly on demand. @@ -681,7 +647,7 @@ public final class Unpooled { } private static ByteBuf copiedBuffer(CharBuffer buffer, Charset charset) { - ByteBuffer dst = BufUtil.encodeString(buffer, charset); + ByteBuffer dst = ByteBufUtil.encodeString(buffer, charset); ByteBuf result = wrappedBuffer(dst.array()); result.writerIndex(dst.remaining()); return result; diff --git a/buffer/src/main/java/io/netty/buffer/UnpooledDirectByteBuf.java b/buffer/src/main/java/io/netty/buffer/UnpooledDirectByteBuf.java index b066299297..b9d49ac15a 100644 --- a/buffer/src/main/java/io/netty/buffer/UnpooledDirectByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/UnpooledDirectByteBuf.java @@ -26,8 +26,6 @@ import java.nio.ByteOrder; import java.nio.channels.ClosedChannelException; import java.nio.channels.GatheringByteChannel; import java.nio.channels.ScatteringByteChannel; -import java.util.ArrayDeque; -import java.util.Queue; /** * A NIO {@link ByteBuffer} based buffer. It is recommended to use {@link Unpooled#directBuffer(int)} @@ -43,7 +41,6 @@ public class UnpooledDirectByteBuf extends AbstractReferenceCountedByteBuf { private ByteBuffer tmpNioBuf; private int capacity; private boolean doNotFree; - private Queue suspendedDeallocations; /** * Creates a new direct buffer. @@ -111,11 +108,7 @@ public class UnpooledDirectByteBuf extends AbstractReferenceCountedByteBuf { if (doNotFree) { doNotFree = false; } else { - if (suspendedDeallocations == null) { - PlatformDependent.freeDirectBuffer(oldBuffer); - } else { - suspendedDeallocations.add(oldBuffer); - } + PlatformDependent.freeDirectBuffer(oldBuffer); } } @@ -527,37 +520,12 @@ public class UnpooledDirectByteBuf extends AbstractReferenceCountedByteBuf { this.buffer = null; - resumeIntermediaryDeallocations(); if (!doNotFree) { PlatformDependent.freeDirectBuffer(buffer); } leak.close(); } - @Override - public ByteBuf suspendIntermediaryDeallocations() { - ensureAccessible(); - if (suspendedDeallocations == null) { - suspendedDeallocations = new ArrayDeque(2); - } - return this; - } - - @Override - public ByteBuf resumeIntermediaryDeallocations() { - if (suspendedDeallocations == null) { - return this; - } - - Queue suspendedDeallocations = this.suspendedDeallocations; - this.suspendedDeallocations = null; - - for (ByteBuffer buf: suspendedDeallocations) { - PlatformDependent.freeDirectBuffer(buf); - } - return this; - } - @Override public ByteBuf unwrap() { return null; diff --git a/buffer/src/main/java/io/netty/buffer/UnpooledHeapByteBuf.java b/buffer/src/main/java/io/netty/buffer/UnpooledHeapByteBuf.java index affae8f8aa..ce70ff2f14 100644 --- a/buffer/src/main/java/io/netty/buffer/UnpooledHeapByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/UnpooledHeapByteBuf.java @@ -417,16 +417,6 @@ public class UnpooledHeapByteBuf extends AbstractReferenceCountedByteBuf { array = null; } - @Override - public ByteBuf suspendIntermediaryDeallocations() { - return this; - } - - @Override - public ByteBuf resumeIntermediaryDeallocations() { - return this; - } - @Override public ByteBuf unwrap() { return null; diff --git a/buffer/src/main/java/io/netty/buffer/UnpooledUnsafeDirectByteBuf.java b/buffer/src/main/java/io/netty/buffer/UnpooledUnsafeDirectByteBuf.java index 8875613271..3dbf665c09 100644 --- a/buffer/src/main/java/io/netty/buffer/UnpooledUnsafeDirectByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/UnpooledUnsafeDirectByteBuf.java @@ -26,8 +26,6 @@ import java.nio.ByteOrder; import java.nio.channels.ClosedChannelException; import java.nio.channels.GatheringByteChannel; import java.nio.channels.ScatteringByteChannel; -import java.util.ArrayDeque; -import java.util.Queue; /** * A NIO {@link ByteBuffer} based buffer. It is recommended to use {@link Unpooled#directBuffer(int)} @@ -46,7 +44,6 @@ public class UnpooledUnsafeDirectByteBuf extends AbstractReferenceCountedByteBuf private ByteBuffer tmpNioBuf; private int capacity; private boolean doNotFree; - private Queue suspendedDeallocations; /** * Creates a new direct buffer. @@ -114,11 +111,7 @@ public class UnpooledUnsafeDirectByteBuf extends AbstractReferenceCountedByteBuf if (doNotFree) { doNotFree = false; } else { - if (suspendedDeallocations == null) { - PlatformDependent.freeDirectBuffer(oldBuffer); - } else { - suspendedDeallocations.add(oldBuffer); - } + PlatformDependent.freeDirectBuffer(oldBuffer); } } @@ -459,37 +452,12 @@ public class UnpooledUnsafeDirectByteBuf extends AbstractReferenceCountedByteBuf this.buffer = null; - resumeIntermediaryDeallocations(); if (!doNotFree) { PlatformDependent.freeDirectBuffer(buffer); } leak.close(); } - @Override - public ByteBuf suspendIntermediaryDeallocations() { - ensureAccessible(); - if (suspendedDeallocations == null) { - suspendedDeallocations = new ArrayDeque(2); - } - return this; - } - - @Override - public ByteBuf resumeIntermediaryDeallocations() { - if (suspendedDeallocations == null) { - return this; - } - - Queue suspendedDeallocations = this.suspendedDeallocations; - this.suspendedDeallocations = null; - - for (ByteBuffer buf: suspendedDeallocations) { - PlatformDependent.freeDirectBuffer(buf); - } - return this; - } - @Override public ByteBuf unwrap() { return null; diff --git a/buffer/src/main/java/io/netty/buffer/UnreleasableByteBuf.java b/buffer/src/main/java/io/netty/buffer/UnreleasableByteBuf.java index fb8a226722..c56fc49b53 100644 --- a/buffer/src/main/java/io/netty/buffer/UnreleasableByteBuf.java +++ b/buffer/src/main/java/io/netty/buffer/UnreleasableByteBuf.java @@ -796,18 +796,6 @@ final class UnreleasableByteBuf implements ByteBuf { return buf.toString(index, length, charset); } - @Override - public ByteBuf suspendIntermediaryDeallocations() { - buf.suspendIntermediaryDeallocations(); - return this; - } - - @Override - public ByteBuf resumeIntermediaryDeallocations() { - buf.resumeIntermediaryDeallocations(); - return this; - } - @Override public int hashCode() { return buf.hashCode(); @@ -838,11 +826,6 @@ final class UnreleasableByteBuf implements ByteBuf { return this; } - @Override - public BufType type() { - return buf.type(); - } - @Override public boolean isReadable(int size) { return buf.isReadable(size); diff --git a/buffer/src/test/java/io/netty/buffer/AbstractCompositeByteBufTest.java b/buffer/src/test/java/io/netty/buffer/AbstractCompositeByteBufTest.java index 584f96a6b0..68fd19c4b3 100644 --- a/buffer/src/test/java/io/netty/buffer/AbstractCompositeByteBufTest.java +++ b/buffer/src/test/java/io/netty/buffer/AbstractCompositeByteBufTest.java @@ -142,20 +142,20 @@ public abstract class AbstractCompositeByteBufTest extends AbstractByteBufTest { a.writerIndex(a.writerIndex() + 1); b.writerIndex(b.writerIndex() + 1); assertEquals(a.writerIndex(), b.writerIndex()); - assertTrue(BufUtil.equals(a, b)); + assertTrue(ByteBufUtil.equals(a, b)); // now discard a.discardReadBytes(); b.discardReadBytes(); assertEquals(a.readerIndex(), b.readerIndex()); assertEquals(a.writerIndex(), b.writerIndex()); - assertTrue(BufUtil.equals(a, b)); + assertTrue(ByteBufUtil.equals(a, b)); a.resetReaderIndex(); b.resetReaderIndex(); assertEquals(a.readerIndex(), b.readerIndex()); a.resetWriterIndex(); b.resetWriterIndex(); assertEquals(a.writerIndex(), b.writerIndex()); - assertTrue(BufUtil.equals(a, b)); + assertTrue(ByteBufUtil.equals(a, b)); } @Test @@ -231,7 +231,7 @@ public abstract class AbstractCompositeByteBufTest extends AbstractByteBufTest { b = freeLater(wrappedBuffer( wrappedBuffer(new byte[] { 1 }).order(order), wrappedBuffer(new byte[] { 2 }).order(order))); - assertFalse(BufUtil.equals(a, b)); + assertFalse(ByteBufUtil.equals(a, b)); // Same content, same firstIndex, short length. a = freeLater(wrappedBuffer(new byte[] { 1, 2, 3 }).order(order)); @@ -239,28 +239,28 @@ public abstract class AbstractCompositeByteBufTest extends AbstractByteBufTest { wrappedBuffer(new byte[]{1}).order(order), wrappedBuffer(new byte[]{2}).order(order), wrappedBuffer(new byte[]{3}).order(order))); - assertTrue(BufUtil.equals(a, b)); + assertTrue(ByteBufUtil.equals(a, b)); // Same content, different firstIndex, short length. a = freeLater(wrappedBuffer(new byte[] { 1, 2, 3 }).order(order)); b = freeLater(wrappedBuffer( wrappedBuffer(new byte[] { 0, 1, 2, 3, 4 }, 1, 2).order(order), wrappedBuffer(new byte[] { 0, 1, 2, 3, 4 }, 3, 1).order(order))); - assertTrue(BufUtil.equals(a, b)); + assertTrue(ByteBufUtil.equals(a, b)); // Different content, same firstIndex, short length. a = freeLater(wrappedBuffer(new byte[] { 1, 2, 3 }).order(order)); b = freeLater(wrappedBuffer( wrappedBuffer(new byte[] { 1, 2 }).order(order), wrappedBuffer(new byte[] { 4 }).order(order))); - assertFalse(BufUtil.equals(a, b)); + assertFalse(ByteBufUtil.equals(a, b)); // Different content, different firstIndex, short length. a = freeLater(wrappedBuffer(new byte[] { 1, 2, 3 }).order(order)); b = freeLater(wrappedBuffer( wrappedBuffer(new byte[] { 0, 1, 2, 4, 5 }, 1, 2).order(order), wrappedBuffer(new byte[] { 0, 1, 2, 4, 5 }, 3, 1).order(order))); - assertFalse(BufUtil.equals(a, b)); + assertFalse(ByteBufUtil.equals(a, b)); // Same content, same firstIndex, long length. a = freeLater(wrappedBuffer(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }).order(order)); @@ -268,28 +268,28 @@ public abstract class AbstractCompositeByteBufTest extends AbstractByteBufTest { wrappedBuffer(new byte[] { 1, 2, 3 }).order(order), wrappedBuffer(new byte[] { 4, 5, 6 }).order(order), wrappedBuffer(new byte[] { 7, 8, 9, 10 }).order(order))); - assertTrue(BufUtil.equals(a, b)); + assertTrue(ByteBufUtil.equals(a, b)); // Same content, different firstIndex, long length. a = freeLater(wrappedBuffer(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }).order(order)); b = freeLater(wrappedBuffer( wrappedBuffer(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, 1, 5).order(order), wrappedBuffer(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, 6, 5).order(order))); - assertTrue(BufUtil.equals(a, b)); + assertTrue(ByteBufUtil.equals(a, b)); // Different content, same firstIndex, long length. a = freeLater(wrappedBuffer(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }).order(order)); b = freeLater(wrappedBuffer( wrappedBuffer(new byte[] { 1, 2, 3, 4, 6 }).order(order), wrappedBuffer(new byte[] { 7, 8, 5, 9, 10 }).order(order))); - assertFalse(BufUtil.equals(a, b)); + assertFalse(ByteBufUtil.equals(a, b)); // Different content, different firstIndex, long length. a = freeLater(wrappedBuffer(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }).order(order)); b = freeLater(wrappedBuffer( wrappedBuffer(new byte[] { 0, 1, 2, 3, 4, 6, 7, 8, 5, 9, 10, 11 }, 1, 5).order(order), wrappedBuffer(new byte[] { 0, 1, 2, 3, 4, 6, 7, 8, 5, 9, 10, 11 }, 6, 5).order(order))); - assertFalse(BufUtil.equals(a, b)); + assertFalse(ByteBufUtil.equals(a, b)); } @Test @@ -345,7 +345,7 @@ public abstract class AbstractCompositeByteBufTest extends AbstractByteBufTest { // to enable writeBytes b.writerIndex(b.writerIndex() - 1); b.writeBytes(wrappedBuffer(new byte[] { 2 }).order(order)); - assertFalse(BufUtil.equals(a, b)); + assertFalse(ByteBufUtil.equals(a, b)); // Same content, same firstIndex, short length. a = wrappedBuffer(new byte[] { 1, 2, 3 }).order(order); @@ -354,7 +354,7 @@ public abstract class AbstractCompositeByteBufTest extends AbstractByteBufTest { b.writerIndex(b.writerIndex() - 2); b.writeBytes(wrappedBuffer(new byte[] { 2 }).order(order)); b.writeBytes(wrappedBuffer(new byte[] { 3 }).order(order)); - assertTrue(BufUtil.equals(a, b)); + assertTrue(ByteBufUtil.equals(a, b)); // Same content, different firstIndex, short length. a = wrappedBuffer(new byte[] { 1, 2, 3 }).order(order); @@ -362,7 +362,7 @@ public abstract class AbstractCompositeByteBufTest extends AbstractByteBufTest { // to enable writeBytes b.writerIndex(b.writerIndex() - 1); b.writeBytes(wrappedBuffer(new byte[] { 0, 1, 2, 3, 4 }, 3, 1).order(order)); - assertTrue(BufUtil.equals(a, b)); + assertTrue(ByteBufUtil.equals(a, b)); // Different content, same firstIndex, short length. a = wrappedBuffer(new byte[] { 1, 2, 3 }).order(order); @@ -370,7 +370,7 @@ public abstract class AbstractCompositeByteBufTest extends AbstractByteBufTest { // to enable writeBytes b.writerIndex(b.writerIndex() - 1); b.writeBytes(wrappedBuffer(new byte[] { 4 }).order(order)); - assertFalse(BufUtil.equals(a, b)); + assertFalse(ByteBufUtil.equals(a, b)); // Different content, different firstIndex, short length. a = wrappedBuffer(new byte[] { 1, 2, 3 }).order(order); @@ -378,7 +378,7 @@ public abstract class AbstractCompositeByteBufTest extends AbstractByteBufTest { // to enable writeBytes b.writerIndex(b.writerIndex() - 1); b.writeBytes(wrappedBuffer(new byte[] { 0, 1, 2, 4, 5 }, 3, 1).order(order)); - assertFalse(BufUtil.equals(a, b)); + assertFalse(ByteBufUtil.equals(a, b)); // Same content, same firstIndex, long length. a = wrappedBuffer(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }).order(order); @@ -387,7 +387,7 @@ public abstract class AbstractCompositeByteBufTest extends AbstractByteBufTest { b.writerIndex(b.writerIndex() - 7); b.writeBytes(wrappedBuffer(new byte[] { 4, 5, 6 }).order(order)); b.writeBytes(wrappedBuffer(new byte[] { 7, 8, 9, 10 }).order(order)); - assertTrue(BufUtil.equals(a, b)); + assertTrue(ByteBufUtil.equals(a, b)); // Same content, different firstIndex, long length. a = wrappedBuffer(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }).order(order); @@ -395,7 +395,7 @@ public abstract class AbstractCompositeByteBufTest extends AbstractByteBufTest { // to enable writeBytes b.writerIndex(b.writerIndex() - 5); b.writeBytes(wrappedBuffer(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, 6, 5).order(order)); - assertTrue(BufUtil.equals(a, b)); + assertTrue(ByteBufUtil.equals(a, b)); // Different content, same firstIndex, long length. a = wrappedBuffer(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }).order(order); @@ -403,7 +403,7 @@ public abstract class AbstractCompositeByteBufTest extends AbstractByteBufTest { // to enable writeBytes b.writerIndex(b.writerIndex() - 5); b.writeBytes(wrappedBuffer(new byte[] { 7, 8, 5, 9, 10 }).order(order)); - assertFalse(BufUtil.equals(a, b)); + assertFalse(ByteBufUtil.equals(a, b)); // Different content, different firstIndex, long length. a = wrappedBuffer(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }).order(order); @@ -411,7 +411,7 @@ public abstract class AbstractCompositeByteBufTest extends AbstractByteBufTest { // to enable writeBytes b.writerIndex(b.writerIndex() - 5); b.writeBytes(wrappedBuffer(new byte[] { 0, 1, 2, 3, 4, 6, 7, 8, 5, 9, 10, 11 }, 6, 5).order(order)); - assertFalse(BufUtil.equals(a, b)); + assertFalse(ByteBufUtil.equals(a, b)); } @Test diff --git a/buffer/src/test/java/io/netty/buffer/UnpooledTest.java b/buffer/src/test/java/io/netty/buffer/UnpooledTest.java index 07859fa680..6d164e5bdc 100644 --- a/buffer/src/test/java/io/netty/buffer/UnpooledTest.java +++ b/buffer/src/test/java/io/netty/buffer/UnpooledTest.java @@ -97,7 +97,7 @@ public class UnpooledTest { for (Entry e: map.entrySet()) { assertEquals( e.getValue().intValue(), - BufUtil.hashCode(wrappedBuffer(e.getKey()))); + ByteBufUtil.hashCode(wrappedBuffer(e.getKey()))); } } @@ -108,47 +108,47 @@ public class UnpooledTest { // Different length. a = wrappedBuffer(new byte[] { 1 }); b = wrappedBuffer(new byte[] { 1, 2 }); - assertFalse(BufUtil.equals(a, b)); + assertFalse(ByteBufUtil.equals(a, b)); // Same content, same firstIndex, short length. a = wrappedBuffer(new byte[] { 1, 2, 3 }); b = wrappedBuffer(new byte[] { 1, 2, 3 }); - assertTrue(BufUtil.equals(a, b)); + assertTrue(ByteBufUtil.equals(a, b)); // Same content, different firstIndex, short length. a = wrappedBuffer(new byte[] { 1, 2, 3 }); b = wrappedBuffer(new byte[] { 0, 1, 2, 3, 4 }, 1, 3); - assertTrue(BufUtil.equals(a, b)); + assertTrue(ByteBufUtil.equals(a, b)); // Different content, same firstIndex, short length. a = wrappedBuffer(new byte[] { 1, 2, 3 }); b = wrappedBuffer(new byte[] { 1, 2, 4 }); - assertFalse(BufUtil.equals(a, b)); + assertFalse(ByteBufUtil.equals(a, b)); // Different content, different firstIndex, short length. a = wrappedBuffer(new byte[] { 1, 2, 3 }); b = wrappedBuffer(new byte[] { 0, 1, 2, 4, 5 }, 1, 3); - assertFalse(BufUtil.equals(a, b)); + assertFalse(ByteBufUtil.equals(a, b)); // Same content, same firstIndex, long length. a = wrappedBuffer(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); b = wrappedBuffer(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); - assertTrue(BufUtil.equals(a, b)); + assertTrue(ByteBufUtil.equals(a, b)); // Same content, different firstIndex, long length. a = wrappedBuffer(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); b = wrappedBuffer(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, 1, 10); - assertTrue(BufUtil.equals(a, b)); + assertTrue(ByteBufUtil.equals(a, b)); // Different content, same firstIndex, long length. a = wrappedBuffer(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); b = wrappedBuffer(new byte[] { 1, 2, 3, 4, 6, 7, 8, 5, 9, 10 }); - assertFalse(BufUtil.equals(a, b)); + assertFalse(ByteBufUtil.equals(a, b)); // Different content, different firstIndex, long length. a = wrappedBuffer(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); b = wrappedBuffer(new byte[] { 0, 1, 2, 3, 4, 6, 7, 8, 5, 9, 10, 11 }, 1, 10); - assertFalse(BufUtil.equals(a, b)); + assertFalse(ByteBufUtil.equals(a, b)); } @Test @@ -174,11 +174,11 @@ public class UnpooledTest { for (int i = 0; i < expected.size(); i ++) { for (int j = 0; j < expected.size(); j ++) { if (i == j) { - assertEquals(0, BufUtil.compare(expected.get(i), expected.get(j))); + assertEquals(0, ByteBufUtil.compare(expected.get(i), expected.get(j))); } else if (i < j) { - assertTrue(BufUtil.compare(expected.get(i), expected.get(j)) < 0); + assertTrue(ByteBufUtil.compare(expected.get(i), expected.get(j)) < 0); } else { - assertTrue(BufUtil.compare(expected.get(i), expected.get(j)) > 0); + assertTrue(ByteBufUtil.compare(expected.get(i), expected.get(j)) > 0); } } } @@ -217,12 +217,12 @@ public class UnpooledTest { @Test public void testCompare2() { - assertTrue(BufUtil.compare( + assertTrue(ByteBufUtil.compare( wrappedBuffer(new byte[]{(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF}), wrappedBuffer(new byte[]{(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00})) > 0); - assertTrue(BufUtil.compare( + assertTrue(ByteBufUtil.compare( wrappedBuffer(new byte[]{(byte) 0xFF}), wrappedBuffer(new byte[]{(byte) 0x00})) > 0); @@ -327,14 +327,14 @@ public class UnpooledTest { @Test public void testHexDump() { - assertEquals("", BufUtil.hexDump(EMPTY_BUFFER)); + assertEquals("", ByteBufUtil.hexDump(EMPTY_BUFFER)); - assertEquals("123456", BufUtil.hexDump(wrappedBuffer( + assertEquals("123456", ByteBufUtil.hexDump(wrappedBuffer( new byte[]{ 0x12, 0x34, 0x56 }))); - assertEquals("1234567890abcdef", BufUtil.hexDump(wrappedBuffer( + assertEquals("1234567890abcdef", ByteBufUtil.hexDump(wrappedBuffer( new byte[]{ 0x12, 0x34, 0x56, 0x78, (byte) 0x90, (byte) 0xAB, (byte) 0xCD, (byte) 0xEF @@ -343,8 +343,8 @@ public class UnpooledTest { @Test public void testSwapMedium() { - assertEquals(0x563412, BufUtil.swapMedium(0x123456)); - assertEquals(0x80, BufUtil.swapMedium(0x800000)); + assertEquals(0x563412, ByteBufUtil.swapMedium(0x123456)); + assertEquals(0x80, ByteBufUtil.swapMedium(0x800000)); } @Test diff --git a/codec-http/pom.xml b/codec-http/pom.xml index 979864cb7a..4e187b74c1 100644 --- a/codec-http/pom.xml +++ b/codec-http/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.0.0.Final-SNAPSHOT + 4.0.0.CR4-SNAPSHOT netty-codec-http diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpClientCodec.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpClientCodec.java index a91c54efa9..3c82ebd12b 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpClientCodec.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpClientCodec.java @@ -16,13 +16,10 @@ package io.netty.handler.codec.http; import io.netty.buffer.ByteBuf; -import io.netty.buffer.FilteredMessageBuf; -import io.netty.buffer.MessageBuf; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundByteHandler; -import io.netty.channel.ChannelOutboundMessageHandler; import io.netty.channel.CombinedChannelDuplexHandler; +import io.netty.channel.MessageList; import io.netty.handler.codec.PrematureChannelClosureException; import java.util.ArrayDeque; @@ -44,8 +41,7 @@ import java.util.concurrent.atomic.AtomicLong; * @see HttpServerCodec */ public final class HttpClientCodec - extends CombinedChannelDuplexHandler - implements ChannelInboundByteHandler, ChannelOutboundMessageHandler { + extends CombinedChannelDuplexHandler { /** A queue that is used for correlating a request and a response. */ private final Queue queue = new ArrayDeque(); @@ -66,11 +62,11 @@ public final class HttpClientCodec } public void setSingleDecode(boolean singleDecode) { - decoder().setSingleDecode(singleDecode); + inboundHandler().setSingleDecode(singleDecode); } public boolean isSingleDecode() { - return decoder().isSingleDecode(); + return inboundHandler().isSingleDecode(); } /** @@ -86,29 +82,6 @@ public final class HttpClientCodec this.failOnMissingResponse = failOnMissingResponse; } - private Decoder decoder() { - return (Decoder) stateHandler(); - } - - private Encoder encoder() { - return (Encoder) operationHandler(); - } - - @Override - public ByteBuf newInboundBuffer(ChannelHandlerContext ctx) throws Exception { - return decoder().newInboundBuffer(ctx); - } - - @Override - public void discardInboundReadBytes(ChannelHandlerContext ctx) throws Exception { - decoder().discardInboundReadBytes(ctx); - } - - @Override - public MessageBuf newOutboundBuffer(ChannelHandlerContext ctx) throws Exception { - return encoder().newOutboundBuffer(ctx); - } - private final class Encoder extends HttpRequestEncoder { @Override @@ -137,7 +110,7 @@ public final class HttpClientCodec @Override protected void decode( - ChannelHandlerContext ctx, ByteBuf buffer, MessageBuf out) throws Exception { + ChannelHandlerContext ctx, ByteBuf buffer, MessageList out) throws Exception { if (done) { int readable = actualReadableBytes(); if (readable == 0) { @@ -147,16 +120,14 @@ public final class HttpClientCodec } out.add(buffer.readBytes(readable)); } else { - if (failOnMissingResponse) { - out = new FilteredMessageBuf(out) { - @Override - protected Object filter(Object msg) { - decrement(msg); - return msg; - } - }; - } + int oldSize = out.size(); super.decode(ctx, buffer, out); + if (failOnMissingResponse) { + int size = out.size(); + for (int i = oldSize; i < size; i++) { + decrement(out.get(i)); + } + } } } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentCompressor.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentCompressor.java index f319f8f581..5fcef790d0 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentCompressor.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentCompressor.java @@ -15,7 +15,7 @@ */ package io.netty.handler.codec.http; -import io.netty.channel.embedded.EmbeddedByteChannel; +import io.netty.channel.embedded.EmbeddedChannel; import io.netty.handler.codec.compression.ZlibCodecFactory; import io.netty.handler.codec.compression.ZlibWrapper; import io.netty.util.internal.StringUtil; @@ -119,7 +119,7 @@ public class HttpContentCompressor extends HttpContentEncoder { return new Result( targetContentEncoding, - new EmbeddedByteChannel(ZlibCodecFactory.newZlibEncoder( + new EmbeddedChannel(ZlibCodecFactory.newZlibEncoder( wrapper, compressionLevel, windowBits, memLevel))); } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentDecoder.java index 2d00bf9647..5e5ac73687 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentDecoder.java @@ -15,21 +15,19 @@ */ package io.netty.handler.codec.http; -import io.netty.buffer.BufUtil; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufHolder; -import io.netty.buffer.MessageBuf; +import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.embedded.EmbeddedByteChannel; +import io.netty.channel.MessageList; +import io.netty.channel.embedded.EmbeddedChannel; import io.netty.handler.codec.MessageToMessageDecoder; -import java.util.Collections; - /** * Decodes the content of the received {@link HttpRequest} and {@link HttpContent}. * The original content is replaced with the new content decoded by the - * {@link EmbeddedByteChannel}, which is created by {@link #newContentDecoder(String)}. + * {@link EmbeddedChannel}, which is created by {@link #newContentDecoder(String)}. * Once decoding is finished, the value of the 'Content-Encoding' * header is set to the target content encoding, as returned by {@link #getTargetContentEncoding(String)}. * Also, the 'Content-Length' header is updated to the length of the @@ -47,20 +45,20 @@ import java.util.Collections; */ public abstract class HttpContentDecoder extends MessageToMessageDecoder { - private EmbeddedByteChannel decoder; + private EmbeddedChannel decoder; private HttpMessage message; private boolean decodeStarted; private boolean continueResponse; @Override - protected void decode(ChannelHandlerContext ctx, HttpObject msg, MessageBuf out) throws Exception { + protected void decode(ChannelHandlerContext ctx, HttpObject msg, MessageList out) throws Exception { if (msg instanceof HttpResponse && ((HttpResponse) msg).getStatus().code() == 100) { if (!(msg instanceof LastHttpContent)) { continueResponse = true; } // 100-continue response must be passed through. - out.add(BufUtil.retain(msg)); + out.add(ByteBufUtil.retain(msg)); return; } @@ -69,7 +67,7 @@ public abstract class HttpContentDecoder extends MessageToMessageDecodercontentEncoding. * * @param contentEncoding the value of the {@code "Content-Encoding"} header - * @return a new {@link EmbeddedByteChannel} if the specified encoding is supported. + * @return a new {@link EmbeddedChannel} if the specified encoding is supported. * {@code null} otherwise (alternatively, you can throw an exception * to block unknown encoding). */ - protected abstract EmbeddedByteChannel newContentDecoder(String contentEncoding) throws Exception; + protected abstract EmbeddedChannel newContentDecoder(String contentEncoding) throws Exception; /** * Returns the expected content encoding of the decoded content. diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentDecompressor.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentDecompressor.java index 254a483dae..e328477640 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentDecompressor.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentDecompressor.java @@ -15,7 +15,7 @@ */ package io.netty.handler.codec.http; -import io.netty.channel.embedded.EmbeddedByteChannel; +import io.netty.channel.embedded.EmbeddedChannel; import io.netty.handler.codec.compression.ZlibCodecFactory; import io.netty.handler.codec.compression.ZlibWrapper; @@ -26,13 +26,13 @@ import io.netty.handler.codec.compression.ZlibWrapper; */ public class HttpContentDecompressor extends HttpContentDecoder { @Override - protected EmbeddedByteChannel newContentDecoder(String contentEncoding) throws Exception { + protected EmbeddedChannel newContentDecoder(String contentEncoding) throws Exception { if ("gzip".equalsIgnoreCase(contentEncoding) || "x-gzip".equalsIgnoreCase(contentEncoding)) { - return new EmbeddedByteChannel(ZlibCodecFactory.newZlibDecoder(ZlibWrapper.GZIP)); + return new EmbeddedChannel(ZlibCodecFactory.newZlibDecoder(ZlibWrapper.GZIP)); } if ("deflate".equalsIgnoreCase(contentEncoding) || "x-deflate".equalsIgnoreCase(contentEncoding)) { // To be strict, 'deflate' means ZLIB, but some servers were not implemented correctly. - return new EmbeddedByteChannel(ZlibCodecFactory.newZlibDecoder(ZlibWrapper.ZLIB_OR_NONE)); + return new EmbeddedChannel(ZlibCodecFactory.newZlibDecoder(ZlibWrapper.ZLIB_OR_NONE)); } // 'identity' or unsupported diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentEncoder.java index d6be9c34c8..fb5b46c19a 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentEncoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpContentEncoder.java @@ -15,25 +15,24 @@ */ package io.netty.handler.codec.http; -import io.netty.buffer.BufUtil; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufHolder; -import io.netty.buffer.MessageBuf; +import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.embedded.EmbeddedByteChannel; +import io.netty.channel.MessageList; +import io.netty.channel.embedded.EmbeddedChannel; import io.netty.handler.codec.MessageToMessageCodec; import io.netty.handler.codec.http.HttpHeaders.Names; import io.netty.handler.codec.http.HttpHeaders.Values; import java.util.ArrayDeque; -import java.util.Collections; import java.util.Queue; /** * Encodes the content of the outbound {@link HttpResponse} and {@link HttpContent}. * The original content is replaced with the new content encoded by the - * {@link EmbeddedByteChannel}, which is created by {@link #beginEncode(HttpResponse, String)}. + * {@link EmbeddedChannel}, which is created by {@link #beginEncode(HttpResponse, String)}. * Once encoding is finished, the value of the 'Content-Encoding' header * is set to the target content encoding, as returned by * {@link #beginEncode(HttpResponse, String)}. @@ -62,7 +61,7 @@ public abstract class HttpContentEncoder extends MessageToMessageCodec acceptEncodingQueue = new ArrayDeque(); private String acceptEncoding; - private EmbeddedByteChannel encoder; + private EmbeddedChannel encoder; private State state = State.AWAIT_HEADERS; @Override @@ -71,18 +70,18 @@ public abstract class HttpContentEncoder extends MessageToMessageCodec out) + protected void decode(ChannelHandlerContext ctx, HttpRequest msg, MessageList out) throws Exception { String acceptedEncoding = msg.headers().get(HttpHeaders.Names.ACCEPT_ENCODING); if (acceptedEncoding == null) { acceptedEncoding = HttpHeaders.Values.IDENTITY; } acceptEncodingQueue.add(acceptedEncoding); - out.add(BufUtil.retain(msg)); + out.add(ByteBufUtil.retain(msg)); } @Override - protected void encode(ChannelHandlerContext ctx, HttpObject msg, MessageBuf out) throws Exception { + protected void encode(ChannelHandlerContext ctx, HttpObject msg, MessageList out) throws Exception { final boolean isFull = msg instanceof HttpResponse && msg instanceof LastHttpContent; switch (state) { case AWAIT_HEADERS: { @@ -93,7 +92,7 @@ public abstract class HttpContentEncoder extends MessageToMessageCodec { } @Override - protected void decode(ChannelHandlerContext ctx, HttpObject msg, MessageBuf out) throws Exception { + protected void decode(ChannelHandlerContext ctx, HttpObject msg, MessageList out) throws Exception { FullHttpMessage currentMessage = this.currentMessage; if (msg instanceof HttpMessage) { @@ -129,7 +129,7 @@ public class HttpObjectAggregator extends MessageToMessageDecoder { if (!m.getDecoderResult().isSuccess()) { removeTransferEncodingChunked(m); this.currentMessage = null; - out.add(BufUtil.retain(m)); + out.add(ByteBufUtil.retain(m)); return; } if (msg instanceof HttpRequest) { diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java index c30eabe9ef..f1100fe0e0 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java @@ -16,10 +16,10 @@ package io.netty.handler.codec.http; import io.netty.buffer.ByteBuf; -import io.netty.buffer.MessageBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPipeline; +import io.netty.channel.MessageList; import io.netty.handler.codec.DecoderResult; import io.netty.handler.codec.ReplayingDecoder; import io.netty.handler.codec.TooLongFrameException; @@ -168,7 +168,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder out) throws Exception { + protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, MessageList out) throws Exception { switch (state()) { case SKIP_CONTROL_CHARS: { try { @@ -449,7 +449,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder out) { + private void reset(MessageList out) { if (out != null) { HttpMessage message = this.message; ByteBuf content = this.content; @@ -500,7 +500,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder out) { + private void readFixedLengthContent(ByteBuf buffer, MessageList out) { //we have a content-length so we just read the correct number of bytes long length = HttpHeaders.getContentLength(message, -1); assert length <= Integer.MAX_VALUE; diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectEncoder.java index bdb7cbbf40..edcaf7c036 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectEncoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectEncoder.java @@ -40,7 +40,7 @@ import static io.netty.handler.codec.http.HttpConstants.*; */ public abstract class HttpObjectEncoder extends MessageToByteEncoder { private static final byte[] CRLF = { CR, LF }; - private static final byte[] CRLF_END = { CR, LF, 0 }; + private static final byte[] ZERO_CRLF = { '0', CR, LF }; private static final byte[] HEADER_SEPARATOR = { COLON , SP }; private static final int ST_INIT = 0; private static final int ST_CONTENT_NON_CHUNK = 1; @@ -93,7 +93,7 @@ public abstract class HttpObjectEncoder extends MessageTo } if (chunk instanceof LastHttpContent) { - out.writeBytes(CRLF_END); + out.writeBytes(ZERO_CRLF); encodeTrailingHeaders(out, (LastHttpContent) chunk); out.writeBytes(CRLF); diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/HttpServerCodec.java b/codec-http/src/main/java/io/netty/handler/codec/http/HttpServerCodec.java index 2018362ba4..eabe8a1e8b 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/HttpServerCodec.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/HttpServerCodec.java @@ -15,11 +15,6 @@ */ package io.netty.handler.codec.http; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.MessageBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundByteHandler; -import io.netty.channel.ChannelOutboundMessageHandler; import io.netty.channel.CombinedChannelDuplexHandler; @@ -30,8 +25,7 @@ import io.netty.channel.CombinedChannelDuplexHandler; * @see HttpClientCodec */ public final class HttpServerCodec - extends CombinedChannelDuplexHandler - implements ChannelInboundByteHandler, ChannelOutboundMessageHandler { + extends CombinedChannelDuplexHandler { /** * Creates a new instance with the default decoder options @@ -48,27 +42,4 @@ public final class HttpServerCodec public HttpServerCodec(int maxInitialLineLength, int maxHeaderSize, int maxChunkSize) { super(new HttpRequestDecoder(maxInitialLineLength, maxHeaderSize, maxChunkSize), new HttpResponseEncoder()); } - - private HttpRequestDecoder decoder() { - return (HttpRequestDecoder) stateHandler(); - } - - private HttpResponseEncoder encoder() { - return (HttpResponseEncoder) operationHandler(); - } - - @Override - public ByteBuf newInboundBuffer(ChannelHandlerContext ctx) throws Exception { - return decoder().newInboundBuffer(ctx); - } - - @Override - public void discardInboundReadBytes(ChannelHandlerContext ctx) throws Exception { - decoder().discardInboundReadBytes(ctx); - } - - @Override - public MessageBuf newOutboundBuffer(ChannelHandlerContext ctx) throws Exception { - return encoder().newOutboundBuffer(ctx); - } } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostRequestDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostRequestDecoder.java index 5057ae92e5..fb65bdfffc 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostRequestDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostRequestDecoder.java @@ -15,7 +15,6 @@ */ package io.netty.handler.codec.http.multipart; -import io.netty.buffer.BufUtil; import io.netty.buffer.ByteBuf; import io.netty.handler.codec.DecoderException; import io.netty.handler.codec.http.HttpConstants; diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostRequestEncoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostRequestEncoder.java index 41ede3403e..adf46f5f24 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostRequestEncoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/multipart/HttpPostRequestEncoder.java @@ -16,7 +16,7 @@ package io.netty.handler.codec.http.multipart; import io.netty.buffer.ByteBuf; -import io.netty.buffer.MessageBuf; +import io.netty.channel.MessageList; import io.netty.handler.codec.DecoderResult; import io.netty.handler.codec.http.DefaultFullHttpRequest; import io.netty.handler.codec.http.DefaultHttpContent; @@ -943,7 +943,7 @@ public class HttpPostRequestEncoder implements ChunkedMessageInput * if the encoding is in error */ @Override - public boolean readChunk(MessageBuf buffer) throws ErrorDataEncoderException { + public boolean readChunk(MessageList buffer) throws ErrorDataEncoderException { if (isLastChunkSent) { return false; } else { diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket00FrameDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket00FrameDecoder.java index d1d486f2d2..c5bd9b02d2 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket00FrameDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket00FrameDecoder.java @@ -16,8 +16,8 @@ package io.netty.handler.codec.http.websocketx; import io.netty.buffer.ByteBuf; -import io.netty.buffer.MessageBuf; import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.MessageList; import io.netty.handler.codec.ReplayingDecoder; import io.netty.handler.codec.TooLongFrameException; @@ -50,7 +50,7 @@ public class WebSocket00FrameDecoder extends ReplayingDecoder { } @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in, MessageBuf out) throws Exception { + protected void decode(ChannelHandlerContext ctx, ByteBuf in, MessageList out) throws Exception { // Discard all data received if closing handshake was received before. if (receivedClosingHandshake) { in.skipBytes(actualReadableBytes()); diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket08FrameDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket08FrameDecoder.java index 2e0be188d7..8032d5ec48 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket08FrameDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket08FrameDecoder.java @@ -54,9 +54,10 @@ package io.netty.handler.codec.http.websocketx; import io.netty.buffer.ByteBuf; -import io.netty.buffer.MessageBuf; +import io.netty.buffer.Unpooled; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.MessageList; import io.netty.handler.codec.CorruptedFrameException; import io.netty.handler.codec.ReplayingDecoder; import io.netty.handler.codec.TooLongFrameException; @@ -89,6 +90,7 @@ public class WebSocket08FrameDecoder extends ReplayingDecoder out) throws Exception { + protected void decode(ChannelHandlerContext ctx, ByteBuf in, MessageList out) throws Exception { // Discard all data received if closing handshake was received before. if (receivedClosingHandshake) { @@ -126,248 +128,267 @@ public class WebSocket08FrameDecoder extends ReplayingDecoder> 4; - frameOpcode = b & 0x0F; + // FIN, RSV, OPCODE + byte b = in.readByte(); + frameFinalFlag = (b & 0x80) != 0; + frameRsv = (b & 0x70) >> 4; + frameOpcode = b & 0x0F; - if (logger.isDebugEnabled()) { - logger.debug("Decoding WebSocket Frame opCode={}", frameOpcode); - } + if (logger.isDebugEnabled()) { + logger.debug("Decoding WebSocket Frame opCode={}", frameOpcode); + } - // MASK, PAYLOAD LEN 1 - b = in.readByte(); - boolean frameMasked = (b & 0x80) != 0; - int framePayloadLen1 = b & 0x7F; + // MASK, PAYLOAD LEN 1 + b = in.readByte(); + boolean frameMasked = (b & 0x80) != 0; + int framePayloadLen1 = b & 0x7F; - if (frameRsv != 0 && !allowExtensions) { - protocolViolation(ctx, "RSV != 0 and no extension negotiated, RSV:" + frameRsv); - return; - } - - if (maskedPayload && !frameMasked) { - protocolViolation(ctx, "unmasked client to server frame"); - return; - } - if (frameOpcode > 7) { // control frame (have MSB in opcode set) - - // control frames MUST NOT be fragmented - if (!frameFinalFlag) { - protocolViolation(ctx, "fragmented control frame"); + if (frameRsv != 0 && !allowExtensions) { + protocolViolation(ctx, "RSV != 0 and no extension negotiated, RSV:" + frameRsv); return; } - // control frames MUST have payload 125 octets or less - if (framePayloadLen1 > 125) { - protocolViolation(ctx, "control frame with payload length > 125 octets"); + if (maskedPayload && !frameMasked) { + protocolViolation(ctx, "unmasked client to server frame"); return; } + if (frameOpcode > 7) { // control frame (have MSB in opcode set) - // check for reserved control frame opcodes - if (!(frameOpcode == OPCODE_CLOSE || frameOpcode == OPCODE_PING || frameOpcode == OPCODE_PONG)) { - protocolViolation(ctx, "control frame using reserved opcode " + frameOpcode); - return; - } + // control frames MUST NOT be fragmented + if (!frameFinalFlag) { + protocolViolation(ctx, "fragmented control frame"); + return; + } - // close frame : if there is a body, the first two bytes of the - // body MUST be a 2-byte unsigned integer (in network byte - // order) representing a getStatus code - if (frameOpcode == 8 && framePayloadLen1 == 1) { - protocolViolation(ctx, "received close control frame with payload len 1"); - return; - } - } else { // data frame - // check for reserved data frame opcodes - if (!(frameOpcode == OPCODE_CONT || frameOpcode == OPCODE_TEXT || frameOpcode == OPCODE_BINARY)) { - protocolViolation(ctx, "data frame using reserved opcode " + frameOpcode); - return; - } + // control frames MUST have payload 125 octets or less + if (framePayloadLen1 > 125) { + protocolViolation(ctx, "control frame with payload length > 125 octets"); + return; + } - // check opcode vs message fragmentation state 1/2 - if (fragmentedFramesCount == 0 && frameOpcode == OPCODE_CONT) { - protocolViolation(ctx, "received continuation data frame outside fragmented message"); - return; - } + // check for reserved control frame opcodes + if (!(frameOpcode == OPCODE_CLOSE || frameOpcode == OPCODE_PING + || frameOpcode == OPCODE_PONG)) { + protocolViolation(ctx, "control frame using reserved opcode " + frameOpcode); + return; + } - // check opcode vs message fragmentation state 2/2 - if (fragmentedFramesCount != 0 && frameOpcode != OPCODE_CONT && frameOpcode != OPCODE_PING) { - protocolViolation(ctx, "received non-continuation data frame while inside fragmented message"); - return; - } - } + // close frame : if there is a body, the first two bytes of the + // body MUST be a 2-byte unsigned integer (in network byte + // order) representing a getStatus code + if (frameOpcode == 8 && framePayloadLen1 == 1) { + protocolViolation(ctx, "received close control frame with payload len 1"); + return; + } + } else { // data frame + // check for reserved data frame opcodes + if (!(frameOpcode == OPCODE_CONT || frameOpcode == OPCODE_TEXT + || frameOpcode == OPCODE_BINARY)) { + protocolViolation(ctx, "data frame using reserved opcode " + frameOpcode); + return; + } - // Read frame payload length - if (framePayloadLen1 == 126) { - framePayloadLength = in.readUnsignedShort(); - if (framePayloadLength < 126) { - protocolViolation(ctx, "invalid data frame length (not using minimal length encoding)"); - return; - } - } else if (framePayloadLen1 == 127) { - framePayloadLength = in.readLong(); - // TODO: check if it's bigger than 0x7FFFFFFFFFFFFFFF, Maybe - // just check if it's negative? + // check opcode vs message fragmentation state 1/2 + if (fragmentedFramesCount == 0 && frameOpcode == OPCODE_CONT) { + protocolViolation(ctx, "received continuation data frame outside fragmented message"); + return; + } - if (framePayloadLength < 65536) { - protocolViolation(ctx, "invalid data frame length (not using minimal length encoding)"); - return; - } - } else { - framePayloadLength = framePayloadLen1; - } - - if (framePayloadLength > maxFramePayloadLength) { - protocolViolation(ctx, "Max frame length of " + maxFramePayloadLength + " has been exceeded."); - return; - } - - if (logger.isDebugEnabled()) { - logger.debug("Decoding WebSocket Frame length={}", framePayloadLength); - } - - checkpoint(State.MASKING_KEY); - case MASKING_KEY: - if (maskedPayload) { - maskingKey = in.readBytes(4); - } - checkpoint(State.PAYLOAD); - case PAYLOAD: - // Sometimes, the payload may not be delivered in 1 nice packet - // We need to accumulate the data until we have it all - int rbytes = actualReadableBytes(); - ByteBuf payloadBuffer = null; - - long willHaveReadByteCount = framePayloadBytesRead + rbytes; - // logger.debug("Frame rbytes=" + rbytes + " willHaveReadByteCount=" - // + willHaveReadByteCount + " framePayloadLength=" + - // framePayloadLength); - if (willHaveReadByteCount == framePayloadLength) { - // We have all our content so proceed to process - payloadBuffer = ctx.alloc().buffer(rbytes); - payloadBuffer.writeBytes(in, rbytes); - } else if (willHaveReadByteCount < framePayloadLength) { - - // We don't have all our content so accumulate payload. - // Returning null means we will get called back - if (framePayload == null) { - framePayload = ctx.alloc().buffer(toFrameLength(framePayloadLength)); - } - framePayload.writeBytes(in, rbytes); - framePayloadBytesRead += rbytes; - - // Return null to wait for more bytes to arrive - return; - } else if (willHaveReadByteCount > framePayloadLength) { - // We have more than what we need so read up to the end of frame - // Leave the remainder in the buffer for next frame - if (framePayload == null) { - framePayload = ctx.alloc().buffer(toFrameLength(framePayloadLength)); - } - framePayload.writeBytes(in, toFrameLength(framePayloadLength - framePayloadBytesRead)); - } - - // Now we have all the data, the next checkpoint must be the next - // frame - checkpoint(State.FRAME_START); - - // Take the data that we have in this packet - if (framePayload == null) { - framePayload = payloadBuffer; - } else if (payloadBuffer != null) { - framePayload.writeBytes(payloadBuffer); - } - - // Unmask data if needed - if (maskedPayload) { - unmask(framePayload); - } - - // Processing ping/pong/close frames because they cannot be - // fragmented - if (frameOpcode == OPCODE_PING) { - out.add(new PingWebSocketFrame(frameFinalFlag, frameRsv, framePayload)); - return; - } - if (frameOpcode == OPCODE_PONG) { - out.add(new PongWebSocketFrame(frameFinalFlag, frameRsv, framePayload)); - return; - } - if (frameOpcode == OPCODE_CLOSE) { - checkCloseFrameBody(ctx, framePayload); - receivedClosingHandshake = true; - out.add(new CloseWebSocketFrame(frameFinalFlag, frameRsv, framePayload)); - return; - } - - // Processing for possible fragmented messages for text and binary - // frames - String aggregatedText = null; - if (frameFinalFlag) { - // Final frame of the sequence. Apparently ping frames are - // allowed in the middle of a fragmented message - if (frameOpcode != OPCODE_PING) { - fragmentedFramesCount = 0; - - // Check text for UTF8 correctness - if (frameOpcode == OPCODE_TEXT || fragmentedFramesText != null) { - // Check UTF-8 correctness for this payload - checkUTF8String(ctx, framePayload); - - // This does a second check to make sure UTF-8 - // correctness for entire text message - aggregatedText = fragmentedFramesText.toString(); - - fragmentedFramesText = null; + // check opcode vs message fragmentation state 2/2 + if (fragmentedFramesCount != 0 && frameOpcode != OPCODE_CONT && frameOpcode != OPCODE_PING) { + protocolViolation(ctx, + "received non-continuation data frame while inside fragmented message"); + return; } } - } else { - // Not final frame so we can expect more frames in the - // fragmented sequence - if (fragmentedFramesCount == 0) { - // First text or binary frame for a fragmented set - fragmentedFramesText = null; - if (frameOpcode == OPCODE_TEXT) { - checkUTF8String(ctx, framePayload); + + // Read frame payload length + if (framePayloadLen1 == 126) { + framePayloadLength = in.readUnsignedShort(); + if (framePayloadLength < 126) { + protocolViolation(ctx, "invalid data frame length (not using minimal length encoding)"); + return; + } + } else if (framePayloadLen1 == 127) { + framePayloadLength = in.readLong(); + // TODO: check if it's bigger than 0x7FFFFFFFFFFFFFFF, Maybe + // just check if it's negative? + + if (framePayloadLength < 65536) { + protocolViolation(ctx, "invalid data frame length (not using minimal length encoding)"); + return; } } else { - // Subsequent frames - only check if init frame is text - if (fragmentedFramesText != null) { - checkUTF8String(ctx, framePayload); - } + framePayloadLength = framePayloadLen1; } - // Increment counter - fragmentedFramesCount++; - } + if (framePayloadLength > maxFramePayloadLength) { + protocolViolation(ctx, "Max frame length of " + maxFramePayloadLength + " has been exceeded."); + return; + } - // Return the frame - if (frameOpcode == OPCODE_TEXT) { - out.add(new TextWebSocketFrame(frameFinalFlag, frameRsv, framePayload)); + if (logger.isDebugEnabled()) { + logger.debug("Decoding WebSocket Frame length={}", framePayloadLength); + } + + checkpoint(State.MASKING_KEY); + case MASKING_KEY: + if (maskedPayload) { + maskingKey = in.readBytes(4); + } + checkpoint(State.PAYLOAD); + case PAYLOAD: + // Sometimes, the payload may not be delivered in 1 nice packet + // We need to accumulate the data until we have it all + int rbytes = actualReadableBytes(); + + long willHaveReadByteCount = framePayloadBytesRead + rbytes; + // logger.debug("Frame rbytes=" + rbytes + " willHaveReadByteCount=" + // + willHaveReadByteCount + " framePayloadLength=" + + // framePayloadLength); + if (willHaveReadByteCount == framePayloadLength) { + // We have all our content so proceed to process + payloadBuffer = ctx.alloc().buffer(rbytes); + payloadBuffer.writeBytes(in, rbytes); + } else if (willHaveReadByteCount < framePayloadLength) { + + // We don't have all our content so accumulate payload. + // Returning null means we will get called back + if (framePayload == null) { + framePayload = ctx.alloc().buffer(toFrameLength(framePayloadLength)); + } + framePayload.writeBytes(in, rbytes); + framePayloadBytesRead += rbytes; + + // Return null to wait for more bytes to arrive + return; + } else if (willHaveReadByteCount > framePayloadLength) { + // We have more than what we need so read up to the end of frame + // Leave the remainder in the buffer for next frame + if (framePayload == null) { + framePayload = ctx.alloc().buffer(toFrameLength(framePayloadLength)); + } + framePayload.writeBytes(in, toFrameLength(framePayloadLength - framePayloadBytesRead)); + } + + // Now we have all the data, the next checkpoint must be the next + // frame + checkpoint(State.FRAME_START); + + // Take the data that we have in this packet + if (framePayload == null) { + framePayload = payloadBuffer; + } else if (payloadBuffer != null) { + framePayload.writeBytes(payloadBuffer); + } + + // Unmask data if needed + if (maskedPayload) { + unmask(framePayload); + } + + // Processing ping/pong/close frames because they cannot be + // fragmented + if (frameOpcode == OPCODE_PING) { + out.add(new PingWebSocketFrame(frameFinalFlag, frameRsv, framePayload)); + return; + } + if (frameOpcode == OPCODE_PONG) { + out.add(new PongWebSocketFrame(frameFinalFlag, frameRsv, framePayload)); + return; + } + if (frameOpcode == OPCODE_CLOSE) { + checkCloseFrameBody(ctx, framePayload); + receivedClosingHandshake = true; + out.add(new CloseWebSocketFrame(frameFinalFlag, frameRsv, framePayload)); + return; + } + + // Processing for possible fragmented messages for text and binary + // frames + String aggregatedText = null; + if (frameFinalFlag) { + // Final frame of the sequence. Apparently ping frames are + // allowed in the middle of a fragmented message + if (frameOpcode != OPCODE_PING) { + fragmentedFramesCount = 0; + + // Check text for UTF8 correctness + if (frameOpcode == OPCODE_TEXT || fragmentedFramesText != null) { + // Check UTF-8 correctness for this payload + checkUTF8String(ctx, framePayload); + + // This does a second check to make sure UTF-8 + // correctness for entire text message + aggregatedText = fragmentedFramesText.toString(); + + fragmentedFramesText = null; + } + } + } else { + // Not final frame so we can expect more frames in the + // fragmented sequence + if (fragmentedFramesCount == 0) { + // First text or binary frame for a fragmented set + fragmentedFramesText = null; + if (frameOpcode == OPCODE_TEXT) { + checkUTF8String(ctx, framePayload); + } + } else { + // Subsequent frames - only check if init frame is text + if (fragmentedFramesText != null) { + checkUTF8String(ctx, framePayload); + } + } + + // Increment counter + fragmentedFramesCount++; + } + + // Return the frame + if (frameOpcode == OPCODE_TEXT) { + out.add(new TextWebSocketFrame(frameFinalFlag, frameRsv, framePayload)); + return; + } else if (frameOpcode == OPCODE_BINARY) { + out.add(new BinaryWebSocketFrame(frameFinalFlag, frameRsv, framePayload)); + return; + } else if (frameOpcode == OPCODE_CONT) { + out.add(new ContinuationWebSocketFrame(frameFinalFlag, frameRsv, framePayload, aggregatedText)); + return; + } else { + throw new UnsupportedOperationException("Cannot decode web socket frame with opcode: " + + frameOpcode); + } + case CORRUPT: + // If we don't keep reading Netty will throw an exception saying + // we can't return null if no bytes read and state not changed. + in.readByte(); return; - } else if (frameOpcode == OPCODE_BINARY) { - out.add(new BinaryWebSocketFrame(frameFinalFlag, frameRsv, framePayload)); - return; - } else if (frameOpcode == OPCODE_CONT) { - out.add(new ContinuationWebSocketFrame(frameFinalFlag, frameRsv, framePayload, aggregatedText)); - return; - } else { - throw new UnsupportedOperationException("Cannot decode web socket frame with opcode: " - + frameOpcode); + default: + throw new Error("Shouldn't reach here."); + } + } catch (Exception e) { + if (payloadBuffer != null) { + if (payloadBuffer.refCnt() > 0) { + payloadBuffer.release(); } - case CORRUPT: - // If we don't keep reading Netty will throw an exception saying - // we can't return null if no bytes read and state not changed. - in.readByte(); - return; - default: - throw new Error("Shouldn't reach here."); + payloadBuffer = null; + } + if (framePayload != null) { + if (framePayload.refCnt() > 0) { + framePayload.release(); + } + framePayload = null; + } + throw e; } } @@ -380,7 +401,7 @@ public class WebSocket08FrameDecoder extends ReplayingDecoder newWebSocketEncoder(); + protected abstract ChannelOutboundHandler newWebSocketEncoder(); } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker00.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker00.java index 4fc2211625..b8422ac00c 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker00.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker00.java @@ -17,8 +17,8 @@ package io.netty.handler.codec.http.websocketx; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelInboundByteHandler; -import io.netty.channel.ChannelOutboundMessageHandler; +import io.netty.channel.ChannelInboundHandler; +import io.netty.channel.ChannelOutboundHandler; import io.netty.handler.codec.http.DefaultFullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpResponse; @@ -252,12 +252,12 @@ public class WebSocketClientHandshaker00 extends WebSocketClientHandshaker { } @Override - protected ChannelInboundByteHandler newWebsocketDecoder() { + protected ChannelInboundHandler newWebsocketDecoder() { return new WebSocket00FrameDecoder(maxFramePayloadLength()); } @Override - protected ChannelOutboundMessageHandler newWebSocketEncoder() { + protected ChannelOutboundHandler newWebSocketEncoder() { return new WebSocket00FrameEncoder(); } } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker07.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker07.java index 205ba762fe..b85c654696 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker07.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker07.java @@ -15,8 +15,8 @@ */ package io.netty.handler.codec.http.websocketx; -import io.netty.channel.ChannelInboundByteHandler; -import io.netty.channel.ChannelOutboundMessageHandler; +import io.netty.channel.ChannelInboundHandler; +import io.netty.channel.ChannelOutboundHandler; import io.netty.handler.codec.http.DefaultFullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpResponse; @@ -191,12 +191,12 @@ public class WebSocketClientHandshaker07 extends WebSocketClientHandshaker { } @Override - protected ChannelInboundByteHandler newWebsocketDecoder() { + protected ChannelInboundHandler newWebsocketDecoder() { return new WebSocket07FrameDecoder(false, allowExtensions, maxFramePayloadLength()); } @Override - protected ChannelOutboundMessageHandler newWebSocketEncoder() { + protected ChannelOutboundHandler newWebSocketEncoder() { return new WebSocket07FrameEncoder(true); } } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker08.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker08.java index bb66d369b6..93b2132aa2 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker08.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker08.java @@ -15,8 +15,8 @@ */ package io.netty.handler.codec.http.websocketx; -import io.netty.channel.ChannelInboundByteHandler; -import io.netty.channel.ChannelOutboundMessageHandler; +import io.netty.channel.ChannelInboundHandler; +import io.netty.channel.ChannelOutboundHandler; import io.netty.handler.codec.http.DefaultFullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpResponse; @@ -191,12 +191,12 @@ public class WebSocketClientHandshaker08 extends WebSocketClientHandshaker { } @Override - protected ChannelInboundByteHandler newWebsocketDecoder() { + protected ChannelInboundHandler newWebsocketDecoder() { return new WebSocket08FrameDecoder(false, allowExtensions, maxFramePayloadLength()); } @Override - protected ChannelOutboundMessageHandler newWebSocketEncoder() { + protected ChannelOutboundHandler newWebSocketEncoder() { return new WebSocket08FrameEncoder(true); } } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker13.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker13.java index 9f2c4ca336..b12ac03dde 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker13.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientHandshaker13.java @@ -15,8 +15,8 @@ */ package io.netty.handler.codec.http.websocketx; -import io.netty.channel.ChannelInboundByteHandler; -import io.netty.channel.ChannelOutboundMessageHandler; +import io.netty.channel.ChannelInboundHandler; +import io.netty.channel.ChannelOutboundHandler; import io.netty.handler.codec.http.DefaultFullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpResponse; @@ -191,12 +191,12 @@ public class WebSocketClientHandshaker13 extends WebSocketClientHandshaker { } @Override - protected ChannelInboundByteHandler newWebsocketDecoder() { + protected ChannelInboundHandler newWebsocketDecoder() { return new WebSocket13FrameDecoder(false, allowExtensions, maxFramePayloadLength()); } @Override - protected ChannelOutboundMessageHandler newWebSocketEncoder() { + protected ChannelOutboundHandler newWebSocketEncoder() { return new WebSocket13FrameEncoder(true); } } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientProtocolHandler.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientProtocolHandler.java index a3d72ab96d..69d5af599f 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientProtocolHandler.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientProtocolHandler.java @@ -16,8 +16,9 @@ package io.netty.handler.codec.http.websocketx; import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandler; import io.netty.channel.ChannelPipeline; -import io.netty.channel.ChannelStateHandler; +import io.netty.channel.MessageList; import io.netty.handler.codec.http.HttpHeaders; import java.net.URI; @@ -33,7 +34,7 @@ import java.net.URI; * This implementation will establish the websocket connection once the connection to the remote server was complete. * * To know once a handshake was done you can intercept the - * {@link ChannelStateHandler#userEventTriggered(ChannelHandlerContext, Object)} and check if the event was of type + * {@link ChannelInboundHandler#userEventTriggered(ChannelHandlerContext, Object)} and check if the event was of type * {@link ClientHandshakeStateEvent#HANDSHAKE_ISSUED} or {@link ClientHandshakeStateEvent#HANDSHAKE_COMPLETE}. */ public class WebSocketClientProtocolHandler extends WebSocketProtocolHandler { @@ -128,12 +129,12 @@ public class WebSocketClientProtocolHandler extends WebSocketProtocolHandler { } @Override - public void messageReceived(ChannelHandlerContext ctx, WebSocketFrame frame) throws Exception { + protected void decode(ChannelHandlerContext ctx, WebSocketFrame frame, MessageList out) throws Exception { if (handleCloseFrames && frame instanceof CloseWebSocketFrame) { ctx.close(); return; } - super.messageReceived(ctx, frame); + super.decode(ctx, frame, out); } @Override diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientProtocolHandshakeHandler.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientProtocolHandshakeHandler.java index 6ba95fa736..56c3b62967 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientProtocolHandshakeHandler.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketClientProtocolHandshakeHandler.java @@ -18,10 +18,11 @@ package io.netty.handler.codec.http.websocketx; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.MessageList; import io.netty.handler.codec.http.FullHttpResponse; -class WebSocketClientProtocolHandshakeHandler extends ChannelInboundMessageHandlerAdapter { +class WebSocketClientProtocolHandshakeHandler extends ChannelInboundHandlerAdapter { private final WebSocketClientHandshaker handshaker; public WebSocketClientProtocolHandshakeHandler(WebSocketClientHandshaker handshaker) { @@ -45,9 +46,10 @@ class WebSocketClientProtocolHandshakeHandler extends ChannelInboundMessageHandl } @Override - public void messageReceived(ChannelHandlerContext ctx, FullHttpResponse msg) throws Exception { + public void messageReceived(ChannelHandlerContext ctx, MessageList messages) throws Exception { if (!handshaker.isHandshakeComplete()) { - handshaker.finishHandshake(ctx.channel(), msg); + handshaker.finishHandshake(ctx.channel(), (FullHttpResponse) messages.get(0)); + messages.remove(0); ctx.fireUserEventTriggered( WebSocketClientProtocolHandler.ClientHandshakeStateEvent.HANDSHAKE_COMPLETE); ctx.pipeline().remove(this); diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketFrameAggregator.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketFrameAggregator.java index 0ff0b28ba0..739ff18e2f 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketFrameAggregator.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketFrameAggregator.java @@ -17,8 +17,8 @@ package io.netty.handler.codec.http.websocketx; import io.netty.buffer.ByteBuf; import io.netty.buffer.CompositeByteBuf; -import io.netty.buffer.MessageBuf; import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.MessageList; import io.netty.handler.codec.MessageToMessageDecoder; import io.netty.handler.codec.TooLongFrameException; @@ -47,7 +47,7 @@ public class WebSocketFrameAggregator extends MessageToMessageDecoder out) throws Exception { + protected void decode(ChannelHandlerContext ctx, WebSocketFrame msg, MessageList out) throws Exception { if (currentFrame == null) { tooLongFrameFound = false; if (msg.isFinalFragment()) { diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketProtocolHandler.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketProtocolHandler.java index c5014ba6d7..49ca45ec86 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketProtocolHandler.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketProtocolHandler.java @@ -17,12 +17,12 @@ package io.netty.handler.codec.http.websocketx; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; - -abstract class WebSocketProtocolHandler extends ChannelInboundMessageHandlerAdapter { +import io.netty.channel.MessageList; +import io.netty.handler.codec.MessageToMessageDecoder; +abstract class WebSocketProtocolHandler extends MessageToMessageDecoder { @Override - public void messageReceived(ChannelHandlerContext ctx, WebSocketFrame frame) throws Exception { + protected void decode(ChannelHandlerContext ctx, WebSocketFrame frame, MessageList out) throws Exception { if (frame instanceof PingWebSocketFrame) { frame.content().retain(); ctx.channel().write(new PongWebSocketFrame(frame.content())); @@ -33,8 +33,7 @@ abstract class WebSocketProtocolHandler extends ChannelInboundMessageHandlerAda return; } - frame.retain(); - ctx.nextInboundMessageBuffer().add(frame); + out.add(frame.retain()); } @Override 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 38d00d0389..de8c88fdfe 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 @@ -19,8 +19,8 @@ import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundByteHandler; -import io.netty.channel.ChannelOutboundMessageHandler; +import io.netty.channel.ChannelInboundHandler; +import io.netty.channel.ChannelOutboundHandler; import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPromise; import io.netty.handler.codec.http.FullHttpRequest; @@ -267,11 +267,11 @@ public abstract class WebSocketServerHandshaker { /** * Returns the decoder to use after handshake is complete. */ - protected abstract ChannelInboundByteHandler newWebsocketDecoder(); + protected abstract ChannelInboundHandler newWebsocketDecoder(); /** * Returns the encoder to use after the handshake is complete. */ - protected abstract ChannelOutboundMessageHandler newWebSocketEncoder(); + protected abstract ChannelOutboundHandler newWebSocketEncoder(); } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker00.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker00.java index 89969fde68..73307000e3 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker00.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker00.java @@ -19,8 +19,8 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelInboundByteHandler; -import io.netty.channel.ChannelOutboundMessageHandler; +import io.netty.channel.ChannelInboundHandler; +import io.netty.channel.ChannelOutboundHandler; import io.netty.channel.ChannelPromise; import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.FullHttpRequest; @@ -183,12 +183,12 @@ public class WebSocketServerHandshaker00 extends WebSocketServerHandshaker { } @Override - protected ChannelInboundByteHandler newWebsocketDecoder() { + protected ChannelInboundHandler newWebsocketDecoder() { return new WebSocket00FrameDecoder(maxFramePayloadLength()); } @Override - protected ChannelOutboundMessageHandler newWebSocketEncoder() { + protected ChannelOutboundHandler newWebSocketEncoder() { return new WebSocket00FrameEncoder(); } } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker07.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker07.java index 514dd1a12b..e7b76de3a7 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker07.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker07.java @@ -15,8 +15,8 @@ */ package io.netty.handler.codec.http.websocketx; -import io.netty.channel.ChannelInboundByteHandler; -import io.netty.channel.ChannelOutboundMessageHandler; +import io.netty.channel.ChannelInboundHandler; +import io.netty.channel.ChannelOutboundHandler; import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpResponse; @@ -132,12 +132,12 @@ public class WebSocketServerHandshaker07 extends WebSocketServerHandshaker { } @Override - protected ChannelInboundByteHandler newWebsocketDecoder() { + protected ChannelInboundHandler newWebsocketDecoder() { return new WebSocket07FrameDecoder(true, allowExtensions, maxFramePayloadLength()); } @Override - protected ChannelOutboundMessageHandler newWebSocketEncoder() { + protected ChannelOutboundHandler newWebSocketEncoder() { return new WebSocket07FrameEncoder(false); } } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker08.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker08.java index d465b81f18..98bc9dd800 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker08.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker08.java @@ -15,8 +15,8 @@ */ package io.netty.handler.codec.http.websocketx; -import io.netty.channel.ChannelInboundByteHandler; -import io.netty.channel.ChannelOutboundMessageHandler; +import io.netty.channel.ChannelInboundHandler; +import io.netty.channel.ChannelOutboundHandler; import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpResponse; @@ -131,12 +131,12 @@ public class WebSocketServerHandshaker08 extends WebSocketServerHandshaker { } @Override - protected ChannelInboundByteHandler newWebsocketDecoder() { + protected ChannelInboundHandler newWebsocketDecoder() { return new WebSocket08FrameDecoder(true, allowExtensions, maxFramePayloadLength()); } @Override - protected ChannelOutboundMessageHandler newWebSocketEncoder() { + protected ChannelOutboundHandler newWebSocketEncoder() { return new WebSocket08FrameEncoder(false); } } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker13.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker13.java index 470fb86dd9..611294adde 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker13.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker13.java @@ -15,8 +15,8 @@ */ package io.netty.handler.codec.http.websocketx; -import io.netty.channel.ChannelInboundByteHandler; -import io.netty.channel.ChannelOutboundMessageHandler; +import io.netty.channel.ChannelInboundHandler; +import io.netty.channel.ChannelOutboundHandler; import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpResponse; @@ -130,12 +130,12 @@ public class WebSocketServerHandshaker13 extends WebSocketServerHandshaker { } @Override - protected ChannelInboundByteHandler newWebsocketDecoder() { + protected ChannelInboundHandler newWebsocketDecoder() { return new WebSocket13FrameDecoder(true, allowExtensions, maxFramePayloadLength()); } @Override - protected ChannelOutboundMessageHandler newWebSocketEncoder() { + protected ChannelOutboundHandler newWebSocketEncoder() { return new WebSocket13FrameEncoder(false); } } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerProtocolHandler.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerProtocolHandler.java index aad5f9be07..449444cc37 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerProtocolHandler.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerProtocolHandler.java @@ -19,9 +19,10 @@ import io.netty.buffer.Unpooled; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; -import io.netty.channel.ChannelStateHandler; +import io.netty.channel.ChannelInboundHandler; +import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelPipeline; +import io.netty.channel.MessageList; import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpResponse; @@ -43,7 +44,7 @@ import static io.netty.handler.codec.http.HttpVersion.*; * to the io.netty.example.http.websocketx.server.WebSocketServer example. * * To know once a handshake was done you can intercept the - * {@link ChannelStateHandler#userEventTriggered(ChannelHandlerContext, Object)} and check if the event was of type + * {@link ChannelInboundHandler#userEventTriggered(ChannelHandlerContext, Object)} and check if the event was of type * {@link ServerHandshakeStateEvent#HANDSHAKE_COMPLETE}. */ public class WebSocketServerProtocolHandler extends WebSocketProtocolHandler { @@ -90,14 +91,14 @@ public class WebSocketServerProtocolHandler extends WebSocketProtocolHandler { } @Override - public void messageReceived(ChannelHandlerContext ctx, WebSocketFrame frame) throws Exception { + protected void decode(ChannelHandlerContext ctx, WebSocketFrame frame, MessageList out) throws Exception { if (frame instanceof CloseWebSocketFrame) { WebSocketServerHandshaker handshaker = getHandshaker(ctx); frame.retain(); handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame); return; } - super.messageReceived(ctx, frame); + super.decode(ctx, frame, out); } @Override @@ -120,12 +121,19 @@ public class WebSocketServerProtocolHandler extends WebSocketProtocolHandler { } static ChannelHandler forbiddenHttpRequestResponder() { - return new ChannelInboundMessageHandlerAdapter() { + return new ChannelInboundHandlerAdapter() { @Override - public void messageReceived(ChannelHandlerContext ctx, FullHttpRequest msg) throws Exception { - FullHttpResponse response = - new DefaultFullHttpResponse(HTTP_1_1, HttpResponseStatus.FORBIDDEN); - ctx.channel().write(response); + public void messageReceived(final ChannelHandlerContext ctx, MessageList msgs) throws Exception { + for (int i = 0; i < msgs.size(); i++) { + Object msg = msgs.get(i); + if (msg instanceof FullHttpRequest) { + FullHttpResponse response = + new DefaultFullHttpResponse(HTTP_1_1, HttpResponseStatus.FORBIDDEN); + ctx.channel().write(response); + msgs.remove(i--); + } + } + ctx.fireMessageReceived(msgs); } }; } diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerProtocolHandshakeHandler.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerProtocolHandshakeHandler.java index c30fff92e4..31e4ada4ee 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerProtocolHandshakeHandler.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocketServerProtocolHandshakeHandler.java @@ -18,8 +18,9 @@ package io.netty.handler.codec.http.websocketx; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelPipeline; +import io.netty.channel.MessageList; import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.HttpHeaders; @@ -36,7 +37,7 @@ import static io.netty.handler.codec.http.HttpVersion.*; * Handles the HTTP handshake (the HTTP Upgrade request) for {@link WebSocketServerProtocolHandler}. */ class WebSocketServerProtocolHandshakeHandler - extends ChannelInboundMessageHandlerAdapter { + extends ChannelInboundHandlerAdapter { private final String websocketPath; private final String subprotocols; @@ -50,33 +51,37 @@ class WebSocketServerProtocolHandshakeHandler } @Override - public void messageReceived(final ChannelHandlerContext ctx, FullHttpRequest req) throws Exception { - if (req.getMethod() != GET) { - sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, FORBIDDEN)); - return; - } + public void messageReceived(final ChannelHandlerContext ctx, MessageList msgs) throws Exception { + MessageList requests = msgs.cast(); + for (int i = 0; i < requests.size(); i++) { + FullHttpRequest req = requests.get(i); + if (req.getMethod() != GET) { + sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, FORBIDDEN)); + return; + } - final WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory( - getWebSocketLocation(ctx.pipeline(), req, websocketPath), subprotocols, allowExtensions); - final WebSocketServerHandshaker handshaker = wsFactory.newHandshaker(req); - if (handshaker == null) { - WebSocketServerHandshakerFactory.sendUnsupportedWebSocketVersionResponse(ctx.channel()); - } else { - final ChannelFuture handshakeFuture = handshaker.handshake(ctx.channel(), req); - handshakeFuture.addListener(new ChannelFutureListener() { - @Override - public void operationComplete(ChannelFuture future) throws Exception { - if (!future.isSuccess()) { - ctx.fireExceptionCaught(future.cause()); - } else { - ctx.fireUserEventTriggered( - WebSocketServerProtocolHandler.ServerHandshakeStateEvent.HANDSHAKE_COMPLETE); + final WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory( + getWebSocketLocation(ctx.pipeline(), req, websocketPath), subprotocols, allowExtensions); + final WebSocketServerHandshaker handshaker = wsFactory.newHandshaker(req); + if (handshaker == null) { + WebSocketServerHandshakerFactory.sendUnsupportedWebSocketVersionResponse(ctx.channel()); + } else { + final ChannelFuture handshakeFuture = handshaker.handshake(ctx.channel(), req); + handshakeFuture.addListener(new ChannelFutureListener() { + @Override + public void operationComplete(ChannelFuture future) throws Exception { + if (!future.isSuccess()) { + ctx.fireExceptionCaught(future.cause()); + } else { + ctx.fireUserEventTriggered( + WebSocketServerProtocolHandler.ServerHandshakeStateEvent.HANDSHAKE_COMPLETE); + } } - } - }); - WebSocketServerProtocolHandler.setHandshaker(ctx, handshaker); - ctx.pipeline().replace(this, "WS403Responder", - WebSocketServerProtocolHandler.forbiddenHttpRequestResponder()); + }); + WebSocketServerProtocolHandler.setHandshaker(ctx, handshaker); + ctx.pipeline().replace(this, "WS403Responder", + WebSocketServerProtocolHandler.forbiddenHttpRequestResponder()); + } } } 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 67b73b1fad..d18952f6fb 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 @@ -15,20 +15,12 @@ */ package io.netty.handler.codec.spdy; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.MessageBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundByteHandler; -import io.netty.channel.ChannelOutboundMessageHandler; import io.netty.channel.CombinedChannelDuplexHandler; - /** * A combination of {@link SpdyFrameDecoder} and {@link SpdyFrameEncoder}. */ -public final class SpdyFrameCodec - extends CombinedChannelDuplexHandler - implements ChannelInboundByteHandler, ChannelOutboundMessageHandler { +public final class SpdyFrameCodec extends CombinedChannelDuplexHandler { /** * Creates a new instance with the specified {@code version} and @@ -51,27 +43,4 @@ public final class SpdyFrameCodec new SpdyFrameDecoder(version, maxChunkSize, maxHeaderSize), new SpdyFrameEncoder(version, compressionLevel, windowBits, memLevel)); } - - private SpdyFrameDecoder decoder() { - return (SpdyFrameDecoder) stateHandler(); - } - - private SpdyFrameEncoder encoder() { - return (SpdyFrameEncoder) operationHandler(); - } - - @Override - public ByteBuf newInboundBuffer(ChannelHandlerContext ctx) throws Exception { - return decoder().newInboundBuffer(ctx); - } - - @Override - public void discardInboundReadBytes(ChannelHandlerContext ctx) throws Exception { - decoder().discardInboundReadBytes(ctx); - } - - @Override - public MessageBuf newOutboundBuffer(ChannelHandlerContext ctx) throws Exception { - return encoder().newOutboundBuffer(ctx); - } } 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 6a0de974d1..4147f5ac5d 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 @@ -16,9 +16,9 @@ package io.netty.handler.codec.spdy; import io.netty.buffer.ByteBuf; -import io.netty.buffer.MessageBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.MessageList; import io.netty.handler.codec.ByteToMessageDecoder; import io.netty.handler.codec.TooLongFrameException; @@ -94,7 +94,7 @@ public class SpdyFrameDecoder extends ByteToMessageDecoder { } @Override - public void decodeLast(ChannelHandlerContext ctx, ByteBuf in, MessageBuf out) throws Exception { + public void decodeLast(ChannelHandlerContext ctx, ByteBuf in, MessageList out) throws Exception { try { decode(ctx, in, out); } finally { @@ -103,7 +103,7 @@ public class SpdyFrameDecoder extends ByteToMessageDecoder { } @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, MessageBuf out) throws Exception { + protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, MessageList out) throws Exception { switch(state) { case READ_COMMON_HEADER: state = readCommonHeader(buffer); 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 cfb73bf528..562aef5b1c 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 @@ -15,20 +15,13 @@ */ package io.netty.handler.codec.spdy; -import io.netty.buffer.MessageBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandler; -import io.netty.channel.ChannelOutboundMessageHandler; import io.netty.channel.CombinedChannelDuplexHandler; -import io.netty.handler.codec.http.HttpObject; - /** * A combination of {@link SpdyHttpDecoder} and {@link SpdyHttpEncoder} */ public final class SpdyHttpCodec - extends CombinedChannelDuplexHandler - implements ChannelInboundMessageHandler, ChannelOutboundMessageHandler { + extends CombinedChannelDuplexHandler { /** * Creates a new instance with the specified decoder options. @@ -36,22 +29,4 @@ public final class SpdyHttpCodec public SpdyHttpCodec(int version, int maxContentLength) { super(new SpdyHttpDecoder(version, maxContentLength), new SpdyHttpEncoder(version)); } - - private SpdyHttpDecoder decoder() { - return (SpdyHttpDecoder) stateHandler(); - } - - private SpdyHttpEncoder encoder() { - return (SpdyHttpEncoder) operationHandler(); - } - - @Override - public MessageBuf newInboundBuffer(ChannelHandlerContext ctx) throws Exception { - return decoder().newInboundBuffer(ctx); - } - - @Override - public MessageBuf newOutboundBuffer(ChannelHandlerContext ctx) throws Exception { - return encoder().newOutboundBuffer(ctx); - } } 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 ee0874f16a..e10fc2809c 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 @@ -16,8 +16,8 @@ package io.netty.handler.codec.spdy; import io.netty.buffer.ByteBuf; -import io.netty.buffer.MessageBuf; import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.MessageList; import io.netty.handler.codec.MessageToMessageDecoder; import io.netty.handler.codec.TooLongFrameException; import io.netty.handler.codec.http.DefaultFullHttpRequest; @@ -91,7 +91,7 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder out) + protected void decode(ChannelHandlerContext ctx, SpdyDataOrControlFrame msg, MessageList out) throws Exception { if (msg instanceof SpdySynStreamFrame) { 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 7400a08227..136f7bb996 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 @@ -15,8 +15,8 @@ */ package io.netty.handler.codec.spdy; -import io.netty.buffer.MessageBuf; import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.MessageList; import io.netty.handler.codec.MessageToMessageEncoder; import io.netty.handler.codec.UnsupportedMessageTypeException; import io.netty.handler.codec.http.FullHttpRequest; @@ -139,7 +139,7 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder { } @Override - protected void encode(ChannelHandlerContext ctx, HttpObject msg, MessageBuf out) throws Exception { + protected void encode(ChannelHandlerContext ctx, HttpObject msg, MessageList out) throws Exception { boolean valid = false; diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpResponseStreamIdHandler.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpResponseStreamIdHandler.java index cb18684d67..b30d848465 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpResponseStreamIdHandler.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyHttpResponseStreamIdHandler.java @@ -15,9 +15,9 @@ */ package io.netty.handler.codec.spdy; -import io.netty.buffer.BufUtil; -import io.netty.buffer.MessageBuf; +import io.netty.buffer.ByteBufUtil; import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.MessageList; import io.netty.handler.codec.MessageToMessageCodec; import io.netty.handler.codec.http.HttpMessage; @@ -40,17 +40,17 @@ public class SpdyHttpResponseStreamIdHandler extends } @Override - protected void encode(ChannelHandlerContext ctx, HttpMessage msg, MessageBuf out) throws Exception { + protected void encode(ChannelHandlerContext ctx, HttpMessage msg, MessageList out) throws Exception { Integer id = ids.poll(); if (id != null && id.intValue() != NO_ID && !msg.headers().contains(SpdyHttpHeaders.Names.STREAM_ID)) { SpdyHttpHeaders.setStreamId(msg, id); } - out.add(BufUtil.retain(msg)); + out.add(ByteBufUtil.retain(msg)); } @Override - protected void decode(ChannelHandlerContext ctx, Object msg, MessageBuf out) throws Exception { + protected void decode(ChannelHandlerContext ctx, Object msg, MessageList out) throws Exception { if (msg instanceof HttpMessage) { boolean contains = ((HttpMessage) msg).headers().contains(SpdyHttpHeaders.Names.STREAM_ID); if (!contains) { @@ -62,6 +62,6 @@ public class SpdyHttpResponseStreamIdHandler extends ids.remove(((SpdyRstStreamFrame) msg).getStreamId()); } - out.add(BufUtil.retain(msg)); + out.add(ByteBufUtil.retain(msg)); } } diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyOrHttpChooser.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyOrHttpChooser.java index 9baca054eb..b14fec68ff 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyOrHttpChooser.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyOrHttpChooser.java @@ -15,14 +15,12 @@ */ package io.netty.handler.codec.spdy; -import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelHandlerUtil; -import io.netty.channel.ChannelInboundByteHandler; -import io.netty.channel.ChannelInboundByteHandlerAdapter; -import io.netty.channel.ChannelInboundMessageHandler; +import io.netty.channel.ChannelInboundHandler; +import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelPipeline; +import io.netty.channel.MessageList; import io.netty.handler.codec.http.HttpObjectAggregator; import io.netty.handler.codec.http.HttpRequestDecoder; import io.netty.handler.codec.http.HttpResponseEncoder; @@ -31,11 +29,11 @@ import io.netty.handler.ssl.SslHandler; import javax.net.ssl.SSLEngine; /** - * {@link ChannelInboundByteHandler} which is responsible to setup the {@link ChannelPipeline} either for + * {@link ChannelInboundHandler} which is responsible to setup the {@link ChannelPipeline} either for * HTTP or SPDY. This offers an easy way for users to support both at the same time while not care to * much about the low-level details. */ -public abstract class SpdyOrHttpChooser extends ChannelInboundByteHandlerAdapter { +public abstract class SpdyOrHttpChooser extends ChannelInboundHandlerAdapter { // TODO: Replace with generic NPN handler @@ -63,25 +61,13 @@ public abstract class SpdyOrHttpChooser extends ChannelInboundByteHandlerAdapter protected abstract SelectedProtocol getProtocol(SSLEngine engine); @Override - public ByteBuf newInboundBuffer(ChannelHandlerContext ctx) throws Exception { - return ChannelHandlerUtil.allocate(ctx); - } - - @Override - public void discardInboundReadBytes(ChannelHandlerContext ctx) throws Exception { - // No need to discard anything because this handler will be replaced with something else very quickly. - } - - @Override - public void inboundBufferUpdated(ChannelHandlerContext ctx, ByteBuf in) throws Exception { + public void messageReceived(ChannelHandlerContext ctx, MessageList in) throws Exception { if (initPipeline(ctx)) { - ctx.nextInboundByteBuffer().writeBytes(in); - // When we reached here we can remove this handler as its now clear what protocol we want to use // from this point on. ctx.pipeline().remove(this); - ctx.fireInboundBufferUpdated(); + ctx.fireMessageReceived(in); } } @@ -140,21 +126,21 @@ public abstract class SpdyOrHttpChooser extends ChannelInboundByteHandlerAdapter } /** - * Create the {@link ChannelInboundMessageHandler} that is responsible for handling the http requests + * Create the {@link ChannelInboundHandler} that is responsible for handling the http requests * when the {@link SelectedProtocol} was {@link SelectedProtocol#HTTP_1_0} or * {@link SelectedProtocol#HTTP_1_1} */ - protected abstract ChannelInboundMessageHandler createHttpRequestHandlerForHttp(); + protected abstract ChannelInboundHandler createHttpRequestHandlerForHttp(); /** - * Create the {@link ChannelInboundMessageHandler} that is responsible for handling the http responses + * Create the {@link ChannelInboundHandler} that is responsible for handling the http responses * when the {@link SelectedProtocol} was {@link SelectedProtocol#SPDY_2} or * {@link SelectedProtocol#SPDY_3}. * * Bye default this getMethod will just delecate to {@link #createHttpRequestHandlerForHttp()}, but * sub-classes may override this to change the behaviour. */ - protected ChannelInboundMessageHandler createHttpRequestHandlerForSpdy() { + protected ChannelInboundHandler createHttpRequestHandlerForSpdy() { return createHttpRequestHandlerForHttp(); } } 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 696a1d88b9..fce1fb7b80 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 @@ -15,15 +15,12 @@ */ package io.netty.handler.codec.spdy; -import io.netty.buffer.MessageBuf; -import io.netty.buffer.Unpooled; import io.netty.channel.ChannelDuplexHandler; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandler; -import io.netty.channel.ChannelOutboundMessageHandler; import io.netty.channel.ChannelPromise; +import io.netty.channel.MessageList; import io.netty.util.internal.EmptyArrays; import java.util.concurrent.atomic.AtomicInteger; @@ -32,8 +29,7 @@ import java.util.concurrent.atomic.AtomicInteger; * Manages streams within a SPDY session. */ public class SpdySessionHandler - extends ChannelDuplexHandler - implements ChannelInboundMessageHandler, ChannelOutboundMessageHandler { + extends ChannelDuplexHandler { private static final SpdyProtocolException PROTOCOL_EXCEPTION = new SpdyProtocolException(); private static final SpdyProtocolException STREAM_CLOSED = new SpdyProtocolException("Stream closed"); @@ -85,21 +81,11 @@ public class SpdySessionHandler } @Override - public MessageBuf newInboundBuffer(ChannelHandlerContext ctx) throws Exception { - return Unpooled.messageBuffer(); - } - - @Override - public MessageBuf newOutboundBuffer(ChannelHandlerContext ctx) throws Exception { - return Unpooled.messageBuffer(); - } - - @Override - public void inboundBufferUpdated(ChannelHandlerContext ctx) throws Exception { - MessageBuf in = ctx.inboundMessageBuffer(); + public void messageReceived(ChannelHandlerContext ctx, MessageList in) throws Exception { boolean handled = false; - for (;;) { - Object msg = in.poll(); + MessageList out = MessageList.newInstance(); + for (int i = 0 ; i < in.size(); i++) { + Object msg = in.get(i); if (msg == null) { break; } @@ -108,18 +94,20 @@ public class SpdySessionHandler // Let the next handlers handle the buffered messages before SYN_STREAM message updates the // lastGoodStreamId. if (handled) { - ctx.fireInboundBufferUpdated(); + ctx.fireMessageReceived(out); + out = MessageList.newInstance(); } } - handleInboundMessage(ctx, msg); + handleInboundMessage(ctx, msg, out); handled = true; } - ctx.fireInboundBufferUpdated(); + in.recycle(); + ctx.fireMessageReceived(out); } - private void handleInboundMessage(ChannelHandlerContext ctx, Object msg) throws Exception { + private void handleInboundMessage(ChannelHandlerContext ctx, Object msg, MessageList out) throws Exception { if (msg instanceof SpdyDataFrame) { @@ -152,22 +140,22 @@ public class SpdySessionHandler // Check if we received a data frame for a Stream-ID which is not open if (!spdySession.isActiveStream(streamID)) { if (streamID <= lastGoodStreamId) { - issueStreamError(ctx, streamID, SpdyStreamStatus.PROTOCOL_ERROR); + issueStreamError(ctx, streamID, SpdyStreamStatus.PROTOCOL_ERROR, out); } else if (!sentGoAwayFrame) { - issueStreamError(ctx, streamID, SpdyStreamStatus.INVALID_STREAM); + issueStreamError(ctx, streamID, SpdyStreamStatus.INVALID_STREAM, out); } return; } // Check if we received a data frame for a stream which is half-closed if (spdySession.isRemoteSideClosed(streamID)) { - issueStreamError(ctx, streamID, SpdyStreamStatus.STREAM_ALREADY_CLOSED); + issueStreamError(ctx, streamID, SpdyStreamStatus.STREAM_ALREADY_CLOSED, out); return; } // Check if we received a data frame before receiving a SYN_REPLY if (!isRemoteInitiatedID(streamID) && !spdySession.hasReceivedReply(streamID)) { - issueStreamError(ctx, streamID, SpdyStreamStatus.PROTOCOL_ERROR); + issueStreamError(ctx, streamID, SpdyStreamStatus.PROTOCOL_ERROR, out); return; } @@ -188,7 +176,7 @@ public class SpdySessionHandler // This difference is stored for the session when writing the SETTINGS frame // and is cleared once we send a WINDOW_UPDATE frame. if (newWindowSize < spdySession.getReceiveWindowSizeLowerBound(streamID)) { - issueStreamError(ctx, streamID, SpdyStreamStatus.FLOW_CONTROL_ERROR); + issueStreamError(ctx, streamID, SpdyStreamStatus.FLOW_CONTROL_ERROR, out); return; } @@ -198,8 +186,7 @@ public class SpdySessionHandler while (spdyDataFrame.content().readableBytes() > initialReceiveWindowSize) { SpdyDataFrame partialDataFrame = new DefaultSpdyDataFrame(streamID, spdyDataFrame.content().readSlice(initialReceiveWindowSize).retain()); - ctx.nextOutboundMessageBuffer().add(partialDataFrame); - ctx.flush(); + ctx.write(partialDataFrame); } } @@ -241,7 +228,7 @@ public class SpdySessionHandler if (spdySynStreamFrame.isInvalid() || !isRemoteInitiatedID(streamID) || spdySession.isActiveStream(streamID)) { - issueStreamError(ctx, streamID, SpdyStreamStatus.PROTOCOL_ERROR); + issueStreamError(ctx, streamID, SpdyStreamStatus.PROTOCOL_ERROR, out); return; } @@ -256,7 +243,7 @@ public class SpdySessionHandler boolean remoteSideClosed = spdySynStreamFrame.isLast(); boolean localSideClosed = spdySynStreamFrame.isUnidirectional(); if (!acceptStream(streamID, priority, remoteSideClosed, localSideClosed)) { - issueStreamError(ctx, streamID, SpdyStreamStatus.REFUSED_STREAM); + issueStreamError(ctx, streamID, SpdyStreamStatus.REFUSED_STREAM, out); return; } @@ -276,13 +263,13 @@ public class SpdySessionHandler if (spdySynReplyFrame.isInvalid() || isRemoteInitiatedID(streamID) || spdySession.isRemoteSideClosed(streamID)) { - issueStreamError(ctx, streamID, SpdyStreamStatus.INVALID_STREAM); + issueStreamError(ctx, streamID, SpdyStreamStatus.INVALID_STREAM, out); return; } // Check if we have received multiple frames for the same Stream-ID if (spdySession.hasReceivedReply(streamID)) { - issueStreamError(ctx, streamID, SpdyStreamStatus.STREAM_IN_USE); + issueStreamError(ctx, streamID, SpdyStreamStatus.STREAM_IN_USE, out); return; } @@ -368,12 +355,12 @@ public class SpdySessionHandler // Check if we received a valid HEADERS frame if (spdyHeadersFrame.isInvalid()) { - issueStreamError(ctx, streamID, SpdyStreamStatus.PROTOCOL_ERROR); + issueStreamError(ctx, streamID, SpdyStreamStatus.PROTOCOL_ERROR, out); return; } if (spdySession.isRemoteSideClosed(streamID)) { - issueStreamError(ctx, streamID, SpdyStreamStatus.INVALID_STREAM); + issueStreamError(ctx, streamID, SpdyStreamStatus.INVALID_STREAM, out); return; } @@ -406,15 +393,15 @@ public class SpdySessionHandler // Check for numerical overflow if (spdySession.getSendWindowSize(streamID) > Integer.MAX_VALUE - deltaWindowSize) { - issueStreamError(ctx, streamID, SpdyStreamStatus.FLOW_CONTROL_ERROR); + issueStreamError(ctx, streamID, SpdyStreamStatus.FLOW_CONTROL_ERROR, out); return; } - updateSendWindowSize(ctx, streamID, deltaWindowSize); + updateSendWindowSize(streamID, deltaWindowSize, out); } } - ctx.nextInboundMessageBuffer().add(msg); + out.add(msg); } @Override @@ -432,10 +419,10 @@ public class SpdySessionHandler } @Override - public void flush(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { - MessageBuf in = ctx.outboundMessageBuffer(); - for (;;) { - Object msg = in.poll(); + public void write(ChannelHandlerContext ctx, MessageList msgs, ChannelPromise promise) throws Exception { + MessageList out = MessageList.newInstance(); + for (int i = 0; i < msgs.size(); i++) { + Object msg = msgs.get(i); if (msg == null) { break; } @@ -449,24 +436,26 @@ public class SpdySessionHandler msg instanceof SpdyHeadersFrame || msg instanceof SpdyWindowUpdateFrame) { try { - handleOutboundMessage(ctx, msg); + handleOutboundMessage(ctx, msg, out); } catch (SpdyProtocolException e) { if (e == PROTOCOL_EXCEPTION) { - // on the case of PROTOCOL_EXCEPTION faile the promise directly + // on the case of PROTOCOL_EXCEPTION, fail the promise directly // See #1211 promise.setFailure(PROTOCOL_EXCEPTION); return; } } } else { - ctx.nextOutboundMessageBuffer().add(msg); + out.add(msg); } } - ctx.flush(promise); + + msgs.recycle(); + ctx.write(out, promise); } - private void handleOutboundMessage(ChannelHandlerContext ctx, Object msg) throws Exception { - + private void handleOutboundMessage(ChannelHandlerContext ctx, Object msg, MessageList out) + throws Exception { if (msg instanceof SpdyDataFrame) { SpdyDataFrame spdyDataFrame = (SpdyDataFrame) msg; @@ -656,7 +645,7 @@ public class SpdySessionHandler throw PROTOCOL_EXCEPTION; } - ctx.nextOutboundMessageBuffer().add(msg); + out.add(msg); } /* @@ -671,8 +660,7 @@ public class SpdySessionHandler private void issueSessionError( ChannelHandlerContext ctx, SpdySessionStatus status) { - sendGoAwayFrame(ctx, status); - ctx.flush().addListener(ChannelFutureListener.CLOSE); + sendGoAwayFrame(ctx, status).addListener(ChannelFutureListener.CLOSE); } /* @@ -687,7 +675,7 @@ public class SpdySessionHandler * Note: this is only called by the worker thread */ private void issueStreamError( - ChannelHandlerContext ctx, int streamID, SpdyStreamStatus status) { + ChannelHandlerContext ctx, int streamID, SpdyStreamStatus status, MessageList in) { boolean fireMessageReceived = !spdySession.isRemoteSideClosed(streamID); removeStream(ctx, streamID); @@ -695,8 +683,11 @@ public class SpdySessionHandler SpdyRstStreamFrame spdyRstStreamFrame = new DefaultSpdyRstStreamFrame(streamID, status); ctx.write(spdyRstStreamFrame); if (fireMessageReceived) { - ctx.nextInboundMessageBuffer().add(spdyRstStreamFrame); - ctx.fireInboundBufferUpdated(); + in.add(spdyRstStreamFrame); + ctx.fireMessageReceived(in); + while (!in.isEmpty()) { + in.remove(0); + } } } @@ -793,7 +784,7 @@ public class SpdySessionHandler } } - private void updateSendWindowSize(ChannelHandlerContext ctx, final int streamID, int deltaWindowSize) { + private void updateSendWindowSize(final int streamID, int deltaWindowSize, MessageList out) { synchronized (flowControlLock) { int newWindowSize = spdySession.updateSendWindowSize(streamID, deltaWindowSize); @@ -832,7 +823,7 @@ public class SpdySessionHandler halfCloseStream(streamID, false); } - ctx.nextOutboundMessageBuffer().add(spdyDataFrame); + out.add(spdyDataFrame); } else { // We can send a partial frame spdySession.updateSendWindowSize(streamID, -1 * newWindowSize); @@ -858,7 +849,7 @@ public class SpdySessionHandler // } //}); - ctx.nextOutboundMessageBuffer().add(partialDataFrame); + out.add(partialDataFrame); newWindowSize = 0; } @@ -873,8 +864,7 @@ public class SpdySessionHandler return; } - sendGoAwayFrame(ctx, SpdySessionStatus.OK); - ChannelFuture f = ctx.flush(); + ChannelFuture f = sendGoAwayFrame(ctx, SpdySessionStatus.OK); if (spdySession.noActiveStreams()) { f.addListener(new ClosingChannelFutureListener(ctx, future)); } else { @@ -884,12 +874,14 @@ public class SpdySessionHandler // FIXME: Close the connection forcibly after timeout. } - private synchronized void sendGoAwayFrame( + private synchronized ChannelFuture sendGoAwayFrame( ChannelHandlerContext ctx, SpdySessionStatus status) { if (!sentGoAwayFrame) { sentGoAwayFrame = true; SpdyGoAwayFrame spdyGoAwayFrame = new DefaultSpdyGoAwayFrame(lastGoodStreamId, status); - ctx.nextOutboundMessageBuffer().add(spdyGoAwayFrame); + return ctx.write(spdyGoAwayFrame); + } else { + return ctx.newSucceededFuture(); } } diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/HttpClientCodecTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/HttpClientCodecTest.java index 5ba19f180b..07af776a60 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpClientCodecTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/HttpClientCodecTest.java @@ -16,7 +16,7 @@ package io.netty.handler.codec.http; import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedByteChannel; +import io.netty.channel.embedded.EmbeddedChannel; import io.netty.handler.codec.CodecException; import io.netty.handler.codec.PrematureChannelClosureException; import io.netty.util.CharsetUtil; @@ -37,7 +37,7 @@ public class HttpClientCodecTest { @Test public void testFailsNotOnRequestResponse() { HttpClientCodec codec = new HttpClientCodec(4096, 8192, 8192, true); - EmbeddedByteChannel ch = new EmbeddedByteChannel(codec); + EmbeddedChannel ch = new EmbeddedChannel(codec); ch.writeOutbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "http://localhost/")); ch.writeInbound(Unpooled.copiedBuffer(RESPONSE, CharsetUtil.ISO_8859_1)); @@ -47,7 +47,7 @@ public class HttpClientCodecTest { @Test public void testFailsNotOnRequestResponseChunked() { HttpClientCodec codec = new HttpClientCodec(4096, 8192, 8192, true); - EmbeddedByteChannel ch = new EmbeddedByteChannel(codec); + EmbeddedChannel ch = new EmbeddedChannel(codec); ch.writeOutbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "http://localhost/")); ch.writeInbound(Unpooled.copiedBuffer(CHUNKED_RESPONSE, CharsetUtil.ISO_8859_1)); @@ -57,7 +57,7 @@ public class HttpClientCodecTest { @Test public void testFailsOnMissingResponse() { HttpClientCodec codec = new HttpClientCodec(4096, 8192, 8192, true); - EmbeddedByteChannel ch = new EmbeddedByteChannel(codec); + EmbeddedChannel ch = new EmbeddedChannel(codec); assertTrue(ch.writeOutbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "http://localhost/"))); @@ -74,7 +74,7 @@ public class HttpClientCodecTest { @Test public void testFailsOnIncompleteChunkedResponse() { HttpClientCodec codec = new HttpClientCodec(4096, 8192, 8192, true); - EmbeddedByteChannel ch = new EmbeddedByteChannel(codec); + EmbeddedChannel ch = new EmbeddedChannel(codec); ch.writeOutbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "http://localhost/")); ch.writeInbound(Unpooled.copiedBuffer(INCOMPLETE_CHUNKED_RESPONSE, CharsetUtil.ISO_8859_1)); diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/HttpContentCompressorTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/HttpContentCompressorTest.java index b0875f9494..113b22cb52 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpContentCompressorTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/HttpContentCompressorTest.java @@ -15,7 +15,7 @@ */ package io.netty.handler.codec.http; -import io.netty.channel.embedded.EmbeddedMessageChannel; +import io.netty.channel.embedded.EmbeddedChannel; import io.netty.handler.codec.compression.ZlibWrapper; import io.netty.handler.codec.http.HttpHeaders.Names; import org.junit.Test; @@ -63,7 +63,7 @@ public class HttpContentCompressorTest { @Test public void testEmptyContentCompression() throws Exception { - EmbeddedMessageChannel ch = new EmbeddedMessageChannel(new HttpContentCompressor()); + EmbeddedChannel ch = new EmbeddedChannel(new HttpContentCompressor()); FullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/"); req.headers().set(Names.ACCEPT_ENCODING, "deflate"); ch.writeInbound(req); diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/HttpContentEncoderTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/HttpContentEncoderTest.java index 8e293cb412..dbf450bf25 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpContentEncoderTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/HttpContentEncoderTest.java @@ -19,9 +19,8 @@ package io.netty.handler.codec.http; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.embedded.EmbeddedByteChannel; -import io.netty.channel.embedded.EmbeddedMessageChannel; -import io.netty.handler.codec.ByteToByteEncoder; +import io.netty.channel.embedded.EmbeddedChannel; +import io.netty.handler.codec.MessageToByteEncoder; import io.netty.handler.codec.http.HttpHeaders.Names; import io.netty.handler.codec.http.HttpHeaders.Values; import io.netty.util.CharsetUtil; @@ -35,9 +34,9 @@ public class HttpContentEncoderTest { private static final class TestEncoder extends HttpContentEncoder { @Override protected Result beginEncode(HttpResponse headers, String acceptEncoding) { - return new Result("test", new EmbeddedByteChannel(new ByteToByteEncoder() { + return new Result("test", new EmbeddedChannel(new MessageToByteEncoder() { @Override - protected void encode(ChannelHandlerContext ctx, ByteBuf in, ByteBuf out) { + protected void encode(ChannelHandlerContext ctx, ByteBuf in, ByteBuf out) throws Exception { out.writeBytes(String.valueOf(in.readableBytes()).getBytes(CharsetUtil.US_ASCII)); in.skipBytes(in.readableBytes()); } @@ -47,7 +46,7 @@ public class HttpContentEncoderTest { @Test public void testSplitContent() throws Exception { - EmbeddedMessageChannel ch = new EmbeddedMessageChannel(new TestEncoder()); + EmbeddedChannel ch = new EmbeddedChannel(new TestEncoder()); ch.writeInbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/")); ch.writeOutbound(new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK)); @@ -71,7 +70,7 @@ public class HttpContentEncoderTest { @Test public void testChunkedContent() throws Exception { - EmbeddedMessageChannel ch = new EmbeddedMessageChannel(new TestEncoder()); + EmbeddedChannel ch = new EmbeddedChannel(new TestEncoder()); ch.writeInbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/")); HttpResponse res = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); @@ -98,7 +97,7 @@ public class HttpContentEncoderTest { @Test public void testFullContent() throws Exception { - EmbeddedMessageChannel ch = new EmbeddedMessageChannel(new TestEncoder()); + EmbeddedChannel ch = new EmbeddedChannel(new TestEncoder()); ch.writeInbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/")); FullHttpResponse res = new DefaultFullHttpResponse( @@ -116,12 +115,12 @@ public class HttpContentEncoderTest { } /** - * If the length of the content is unknown, {@link HttpContentEncoder} should not skip encoding even if the - * actual length is turned out to be 0. + * If the length of the content is unknown, {@link HttpContentEncoder} should not skip encoding the content + * even if the actual length is turned out to be 0. */ @Test public void testEmptySplitContent() throws Exception { - EmbeddedMessageChannel ch = new EmbeddedMessageChannel(new TestEncoder()); + EmbeddedChannel ch = new EmbeddedChannel(new TestEncoder()); ch.writeInbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/")); ch.writeOutbound(new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK)); @@ -129,7 +128,7 @@ public class HttpContentEncoderTest { ch.writeOutbound(LastHttpContent.EMPTY_LAST_CONTENT); HttpContent chunk = (HttpContent) ch.readOutbound(); - assertThat(chunk.content().isReadable(), is(false)); + assertThat(chunk.content().toString(CharsetUtil.US_ASCII), is("0")); assertThat(chunk, is(instanceOf(LastHttpContent.class))); assertThat(ch.readOutbound(), is(nullValue())); } @@ -139,7 +138,7 @@ public class HttpContentEncoderTest { */ @Test public void testEmptyFullContent() throws Exception { - EmbeddedMessageChannel ch = new EmbeddedMessageChannel(new TestEncoder()); + EmbeddedChannel ch = new EmbeddedChannel(new TestEncoder()); ch.writeInbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/")); FullHttpResponse res = new DefaultFullHttpResponse( @@ -162,7 +161,7 @@ public class HttpContentEncoderTest { assertThat(ch.readOutbound(), is(nullValue())); } - private static void assertEncodedResponse(EmbeddedMessageChannel ch) { + private static void assertEncodedResponse(EmbeddedChannel ch) { Object o = ch.readOutbound(); assertThat(o, is(instanceOf(HttpResponse.class))); diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/HttpInvalidMessageTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/HttpInvalidMessageTest.java index bae6eeea8a..caa5f50fe7 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpInvalidMessageTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/HttpInvalidMessageTest.java @@ -17,7 +17,7 @@ package io.netty.handler.codec.http; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedByteChannel; +import io.netty.channel.embedded.EmbeddedChannel; import io.netty.handler.codec.DecoderResult; import io.netty.util.CharsetUtil; import org.junit.Test; @@ -32,7 +32,7 @@ public class HttpInvalidMessageTest { @Test public void testRequestWithBadInitialLine() throws Exception { - EmbeddedByteChannel ch = new EmbeddedByteChannel(new HttpRequestDecoder()); + EmbeddedChannel ch = new EmbeddedChannel(new HttpRequestDecoder()); ch.writeInbound(Unpooled.copiedBuffer("GET / HTTP/1.0 with extra\r\n", CharsetUtil.UTF_8)); HttpRequest req = (HttpRequest) ch.readInbound(); DecoderResult dr = req.getDecoderResult(); @@ -43,7 +43,7 @@ public class HttpInvalidMessageTest { @Test public void testRequestWithBadHeader() throws Exception { - EmbeddedByteChannel ch = new EmbeddedByteChannel(new HttpRequestDecoder()); + EmbeddedChannel ch = new EmbeddedChannel(new HttpRequestDecoder()); ch.writeInbound(Unpooled.copiedBuffer("GET /maybe-something HTTP/1.0\r\n", CharsetUtil.UTF_8)); ch.writeInbound(Unpooled.copiedBuffer("Good_Name: Good Value\r\n", CharsetUtil.UTF_8)); ch.writeInbound(Unpooled.copiedBuffer("Bad=Name: Bad Value\r\n", CharsetUtil.UTF_8)); @@ -59,7 +59,7 @@ public class HttpInvalidMessageTest { @Test public void testResponseWithBadInitialLine() throws Exception { - EmbeddedByteChannel ch = new EmbeddedByteChannel(new HttpResponseDecoder()); + EmbeddedChannel ch = new EmbeddedChannel(new HttpResponseDecoder()); ch.writeInbound(Unpooled.copiedBuffer("HTTP/1.0 BAD_CODE Bad Server\r\n", CharsetUtil.UTF_8)); HttpResponse res = (HttpResponse) ch.readInbound(); DecoderResult dr = res.getDecoderResult(); @@ -70,7 +70,7 @@ public class HttpInvalidMessageTest { @Test public void testResponseWithBadHeader() throws Exception { - EmbeddedByteChannel ch = new EmbeddedByteChannel(new HttpResponseDecoder()); + EmbeddedChannel ch = new EmbeddedChannel(new HttpResponseDecoder()); ch.writeInbound(Unpooled.copiedBuffer("HTTP/1.0 200 Maybe OK\r\n", CharsetUtil.UTF_8)); ch.writeInbound(Unpooled.copiedBuffer("Good_Name: Good Value\r\n", CharsetUtil.UTF_8)); ch.writeInbound(Unpooled.copiedBuffer("Bad=Name: Bad Value\r\n", CharsetUtil.UTF_8)); @@ -86,7 +86,7 @@ public class HttpInvalidMessageTest { @Test public void testBadChunk() throws Exception { - EmbeddedByteChannel ch = new EmbeddedByteChannel(new HttpRequestDecoder()); + EmbeddedChannel ch = new EmbeddedChannel(new HttpRequestDecoder()); ch.writeInbound(Unpooled.copiedBuffer("GET / HTTP/1.0\r\n", CharsetUtil.UTF_8)); ch.writeInbound(Unpooled.copiedBuffer("Transfer-Encoding: chunked\r\n\r\n", CharsetUtil.UTF_8)); ch.writeInbound(Unpooled.copiedBuffer("BAD_LENGTH\r\n", CharsetUtil.UTF_8)); @@ -101,7 +101,7 @@ public class HttpInvalidMessageTest { ensureInboundTrafficDiscarded(ch); } - private void ensureInboundTrafficDiscarded(EmbeddedByteChannel ch) { + private void ensureInboundTrafficDiscarded(EmbeddedChannel ch) { // Generate a lot of random traffic to ensure that it's discarded silently. byte[] data = new byte[1048576]; rnd.nextBytes(data); @@ -109,9 +109,10 @@ public class HttpInvalidMessageTest { ByteBuf buf = Unpooled.wrappedBuffer(data); for (int i = 0; i < 4096; i ++) { buf.setIndex(0, data.length); - ch.writeInbound(buf); + ch.writeInbound(buf.retain()); ch.checkException(); assertNull(ch.readInbound()); } + buf.release(); } } diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/HttpObjectAggregatorTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/HttpObjectAggregatorTest.java index e724bc4513..d6f8fb4676 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpObjectAggregatorTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/HttpObjectAggregatorTest.java @@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.CompositeByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.embedded.EmbeddedMessageChannel; +import io.netty.channel.embedded.EmbeddedChannel; import io.netty.handler.codec.TooLongFrameException; import io.netty.util.CharsetUtil; import org.easymock.EasyMock; @@ -34,7 +34,7 @@ public class HttpObjectAggregatorTest { @Test public void testAggregate() { HttpObjectAggregator aggr = new HttpObjectAggregator(1024 * 1024); - EmbeddedMessageChannel embedder = new EmbeddedMessageChannel(aggr); + EmbeddedChannel embedder = new EmbeddedChannel(aggr); HttpRequest message = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "http://localhost"); @@ -73,7 +73,7 @@ public class HttpObjectAggregatorTest { @Test public void testAggregateWithTrailer() { HttpObjectAggregator aggr = new HttpObjectAggregator(1024 * 1024); - EmbeddedMessageChannel embedder = new EmbeddedMessageChannel(aggr); + EmbeddedChannel embedder = new EmbeddedChannel(aggr); HttpRequest message = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "http://localhost"); HttpHeaders.setHeader(message, "X-Test", true); @@ -105,7 +105,7 @@ public class HttpObjectAggregatorTest { @Test public void testTooLongFrameException() { HttpObjectAggregator aggr = new HttpObjectAggregator(4); - EmbeddedMessageChannel embedder = new EmbeddedMessageChannel(aggr); + EmbeddedChannel embedder = new EmbeddedChannel(aggr); HttpRequest message = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "http://localhost"); HttpContent chunk1 = new DefaultHttpContent(Unpooled.copiedBuffer("test", CharsetUtil.US_ASCII)); @@ -159,7 +159,7 @@ public class HttpObjectAggregatorTest { @Test public void testAggregateTransferEncodingChunked() { HttpObjectAggregator aggr = new HttpObjectAggregator(1024 * 1024); - EmbeddedMessageChannel embedder = new EmbeddedMessageChannel(aggr); + EmbeddedChannel embedder = new EmbeddedChannel(aggr); HttpRequest message = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "http://localhost"); diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/HttpServerCodecTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/HttpServerCodecTest.java index 237b30fa56..68771595fc 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/HttpServerCodecTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/HttpServerCodecTest.java @@ -17,7 +17,7 @@ package io.netty.handler.codec.http; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedByteChannel; +import io.netty.channel.embedded.EmbeddedChannel; import io.netty.util.CharsetUtil; import org.junit.Assert; import org.junit.Test; @@ -32,10 +32,11 @@ public class HttpServerCodecTest { int maxChunkSize = 2000; HttpServerCodec httpServerCodec = new HttpServerCodec(1000, 1000, maxChunkSize); - EmbeddedByteChannel decoderEmbedder = new EmbeddedByteChannel(httpServerCodec); + EmbeddedChannel decoderEmbedder = new EmbeddedChannel(httpServerCodec); int totalContentLength = maxChunkSize * 5; - decoderEmbedder.writeInbound(Unpooled.copiedBuffer("PUT /test HTTP/1.1\r\n" + + decoderEmbedder.writeInbound(Unpooled.copiedBuffer( + "PUT /test HTTP/1.1\r\n" + "Content-Length: " + totalContentLength + "\r\n" + "\r\n", CharsetUtil.UTF_8)); diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketFrameAggregatorTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketFrameAggregatorTest.java index a78ca4c102..f360329a60 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketFrameAggregatorTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketFrameAggregatorTest.java @@ -17,7 +17,7 @@ package io.netty.handler.codec.http.websocketx; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedMessageChannel; +import io.netty.channel.embedded.EmbeddedChannel; import io.netty.handler.codec.TooLongFrameException; import io.netty.util.CharsetUtil; import org.junit.Assert; @@ -32,7 +32,7 @@ public class WebSocketFrameAggregatorTest { .writeBytes(content2.duplicate()).writeBytes(content3.duplicate()); @Test public void testAggregationBinary() { - EmbeddedMessageChannel channel = new EmbeddedMessageChannel(new WebSocketFrameAggregator(Integer.MAX_VALUE)); + EmbeddedChannel channel = new EmbeddedChannel(new WebSocketFrameAggregator(Integer.MAX_VALUE)); channel.writeInbound(new BinaryWebSocketFrame(true, 1, content1.copy())); channel.writeInbound(new BinaryWebSocketFrame(false, 0, content1.copy())); channel.writeInbound(new ContinuationWebSocketFrame(false, 0, content2.copy())); @@ -42,7 +42,7 @@ public class WebSocketFrameAggregatorTest { Assert.assertTrue(channel.finish()); - System.out.println(channel.lastInboundMessageBuffer().size()); + System.out.println(channel.lastInboundBuffer().size()); BinaryWebSocketFrame frame = (BinaryWebSocketFrame) channel.readInbound(); Assert.assertTrue(frame.isFinalFragment()); Assert.assertEquals(1, frame.rsv()); @@ -68,7 +68,7 @@ public class WebSocketFrameAggregatorTest { @Test public void testAggregationText() { - EmbeddedMessageChannel channel = new EmbeddedMessageChannel(new WebSocketFrameAggregator(Integer.MAX_VALUE)); + EmbeddedChannel channel = new EmbeddedChannel(new WebSocketFrameAggregator(Integer.MAX_VALUE)); channel.writeInbound(new TextWebSocketFrame(true, 1, content1.copy())); channel.writeInbound(new TextWebSocketFrame(false, 0, content1.copy())); channel.writeInbound(new ContinuationWebSocketFrame(false, 0, content2.copy())); @@ -103,7 +103,7 @@ public class WebSocketFrameAggregatorTest { @Test public void textFrameTooBig() { - EmbeddedMessageChannel channel = new EmbeddedMessageChannel(new WebSocketFrameAggregator(8)); + EmbeddedChannel channel = new EmbeddedChannel(new WebSocketFrameAggregator(8)); channel.writeInbound(new BinaryWebSocketFrame(true, 1, content1.copy())); channel.writeInbound(new BinaryWebSocketFrame(false, 0, content1.copy())); try { diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker00Test.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker00Test.java index 78354b607f..aa91646b92 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker00Test.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker00Test.java @@ -17,7 +17,7 @@ package io.netty.handler.codec.http.websocketx; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedByteChannel; +import io.netty.channel.embedded.EmbeddedChannel; import io.netty.handler.codec.http.DefaultFullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.HttpHeaders.Names; @@ -39,7 +39,7 @@ public class WebSocketServerHandshaker00Test { @Test public void testPerformOpeningHandshake() { - EmbeddedByteChannel ch = new EmbeddedByteChannel( + EmbeddedChannel ch = new EmbeddedChannel( new HttpObjectAggregator(42), new HttpRequestDecoder(), new HttpResponseEncoder()); FullHttpRequest req = new DefaultFullHttpRequest( @@ -56,9 +56,9 @@ public class WebSocketServerHandshaker00Test { new WebSocketServerHandshaker00( "ws://example.com/chat", "chat", Integer.MAX_VALUE).handshake(ch, req); - ByteBuf resBuf = ch.readOutbound(); + ByteBuf resBuf = (ByteBuf) ch.readOutbound(); - EmbeddedByteChannel ch2 = new EmbeddedByteChannel(new HttpResponseDecoder()); + EmbeddedChannel ch2 = new EmbeddedChannel(new HttpResponseDecoder()); ch2.writeInbound(resBuf); HttpResponse res = (HttpResponse) ch2.readInbound(); diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker08Test.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker08Test.java index f49ac6b101..6b6d3c7aca 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker08Test.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker08Test.java @@ -16,7 +16,7 @@ package io.netty.handler.codec.http.websocketx; import io.netty.buffer.ByteBuf; -import io.netty.channel.embedded.EmbeddedByteChannel; +import io.netty.channel.embedded.EmbeddedChannel; import io.netty.handler.codec.http.DefaultFullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.HttpHeaders.Names; @@ -36,7 +36,7 @@ public class WebSocketServerHandshaker08Test { @Test public void testPerformOpeningHandshake() { - EmbeddedByteChannel ch = new EmbeddedByteChannel( + EmbeddedChannel ch = new EmbeddedChannel( new HttpObjectAggregator(42), new HttpRequestDecoder(), new HttpResponseEncoder()); FullHttpRequest req = new DefaultFullHttpRequest(HTTP_1_1, HttpMethod.GET, "/chat"); @@ -51,9 +51,9 @@ public class WebSocketServerHandshaker08Test { new WebSocketServerHandshaker08( "ws://example.com/chat", "chat", false, Integer.MAX_VALUE).handshake(ch, req); - ByteBuf resBuf = ch.readOutbound(); + ByteBuf resBuf = (ByteBuf) ch.readOutbound(); - EmbeddedByteChannel ch2 = new EmbeddedByteChannel(new HttpResponseDecoder()); + EmbeddedChannel ch2 = new EmbeddedChannel(new HttpResponseDecoder()); ch2.writeInbound(resBuf); HttpResponse res = (HttpResponse) ch2.readInbound(); diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker13Test.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker13Test.java index 791de6a604..aacc460a26 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker13Test.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerHandshaker13Test.java @@ -16,7 +16,7 @@ package io.netty.handler.codec.http.websocketx; import io.netty.buffer.ByteBuf; -import io.netty.channel.embedded.EmbeddedByteChannel; +import io.netty.channel.embedded.EmbeddedChannel; import io.netty.handler.codec.http.DefaultFullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.HttpHeaders.Names; @@ -36,7 +36,7 @@ public class WebSocketServerHandshaker13Test { @Test public void testPerformOpeningHandshake() { - EmbeddedByteChannel ch = new EmbeddedByteChannel( + EmbeddedChannel ch = new EmbeddedChannel( new HttpObjectAggregator(42), new HttpRequestDecoder(), new HttpResponseEncoder()); FullHttpRequest req = new DefaultFullHttpRequest(HTTP_1_1, HttpMethod.GET, "/chat"); @@ -51,9 +51,9 @@ public class WebSocketServerHandshaker13Test { new WebSocketServerHandshaker13( "ws://example.com/chat", "chat", false, Integer.MAX_VALUE).handshake(ch, req); - ByteBuf resBuf = ch.readOutbound(); + ByteBuf resBuf = (ByteBuf) ch.readOutbound(); - EmbeddedByteChannel ch2 = new EmbeddedByteChannel(new HttpResponseDecoder()); + EmbeddedChannel ch2 = new EmbeddedChannel(new HttpResponseDecoder()); ch2.writeInbound(resBuf); HttpResponse res = (HttpResponse) ch2.readInbound(); diff --git a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerProtocolHandlerTest.java b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerProtocolHandlerTest.java index 643363fd16..67f635122b 100644 --- a/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerProtocolHandlerTest.java +++ b/codec-http/src/test/java/io/netty/handler/codec/http/websocketx/WebSocketServerProtocolHandlerTest.java @@ -15,25 +15,26 @@ */ package io.netty.handler.codec.http.websocketx; -import io.netty.buffer.MessageBuf; -import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; -import io.netty.channel.ChannelOperationHandlerAdapter; -import io.netty.channel.ChannelOutboundMessageHandler; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.ChannelOutboundHandlerAdapter; import io.netty.channel.ChannelPromise; -import io.netty.channel.embedded.EmbeddedMessageChannel; +import io.netty.channel.MessageList; +import io.netty.channel.embedded.EmbeddedChannel; import io.netty.handler.codec.http.DefaultFullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpRequestDecoder; -import io.netty.handler.codec.http.HttpResponse; import io.netty.handler.codec.http.HttpResponseEncoder; +import org.junit.Before; import org.junit.Test; +import java.util.ArrayDeque; +import java.util.Queue; + import static io.netty.handler.codec.http.HttpHeaders.Values.*; import static io.netty.handler.codec.http.HttpResponseStatus.*; import static io.netty.handler.codec.http.HttpVersion.*; @@ -41,29 +42,36 @@ import static org.junit.Assert.*; public class WebSocketServerProtocolHandlerTest { + private final Queue responses = new ArrayDeque(); + + @Before + public void setUp() { + responses.clear(); + } + @Test public void testHttpUpgradeRequest() throws Exception { - EmbeddedMessageChannel ch = createChannel(new MockOutboundHandler()); + EmbeddedChannel ch = createChannel(new MockOutboundHandler()); ChannelHandlerContext handshakerCtx = ch.pipeline().context(WebSocketServerProtocolHandshakeHandler.class); writeUpgradeRequest(ch); - assertEquals(SWITCHING_PROTOCOLS, ((HttpResponse) ch.outboundMessageBuffer().poll()).getStatus()); + assertEquals(SWITCHING_PROTOCOLS, responses.remove().getStatus()); assertNotNull(WebSocketServerProtocolHandler.getHandshaker(handshakerCtx)); } @Test public void testSubsequentHttpRequestsAfterUpgradeShouldReturn403() throws Exception { - EmbeddedMessageChannel ch = createChannel(new MockOutboundHandler()); + EmbeddedChannel ch = createChannel(); writeUpgradeRequest(ch); - assertEquals(SWITCHING_PROTOCOLS, ((HttpResponse) ch.outboundMessageBuffer().poll()).getStatus()); + assertEquals(SWITCHING_PROTOCOLS, responses.remove().getStatus()); ch.writeInbound(new DefaultFullHttpRequest(HTTP_1_1, HttpMethod.GET, "/test")); - assertEquals(FORBIDDEN, ((HttpResponse) ch.outboundMessageBuffer().poll()).getStatus()); + assertEquals(FORBIDDEN, responses.remove().getStatus()); } @Test public void testHttpUpgradeRequestInvalidUpgradeHeader() { - EmbeddedMessageChannel ch = createChannel(); + EmbeddedChannel ch = createChannel(); FullHttpRequest httpRequestWithEntity = new WebSocketRequestBuilder().httpVersion(HTTP_1_1) .method(HttpMethod.GET) .uri("/test") @@ -74,14 +82,14 @@ public class WebSocketServerProtocolHandlerTest { ch.writeInbound(httpRequestWithEntity); - FullHttpResponse response = getHttpResponse(ch); + FullHttpResponse response = responses.remove(); assertEquals(BAD_REQUEST, response.getStatus()); assertEquals("not a WebSocket handshake request: missing upgrade", getResponseMessage(response)); } @Test public void testHttpUpgradeRequestMissingWSKeyHeader() { - EmbeddedMessageChannel ch = createChannel(); + EmbeddedChannel ch = createChannel(); HttpRequest httpRequest = new WebSocketRequestBuilder().httpVersion(HTTP_1_1) .method(HttpMethod.GET) .uri("/test") @@ -93,7 +101,7 @@ public class WebSocketServerProtocolHandlerTest { ch.writeInbound(httpRequest); - FullHttpResponse response = getHttpResponse(ch); + FullHttpResponse response = responses.remove(); assertEquals(BAD_REQUEST, response.getStatus()); assertEquals("not a WebSocket request: missing key", getResponseMessage(response)); } @@ -101,22 +109,26 @@ public class WebSocketServerProtocolHandlerTest { @Test public void testHandleTextFrame() { CustomTextFrameHandler customTextFrameHandler = new CustomTextFrameHandler(); - EmbeddedMessageChannel ch = createChannel(customTextFrameHandler); + EmbeddedChannel ch = createChannel(customTextFrameHandler); writeUpgradeRequest(ch); - // Removing the HttpRequestDecoder as we are writing a TextWebSocketFrame so decoding is not neccessary. - ch.pipeline().remove(HttpRequestDecoder.class); + + if (ch.pipeline().context(HttpRequestDecoder.class) != null) { + // Removing the HttpRequestDecoder because we are writing a TextWebSocketFrame and thus + // decoding is not neccessary. + ch.pipeline().remove(HttpRequestDecoder.class); + } ch.writeInbound(new TextWebSocketFrame("payload")); assertEquals("processed: payload", customTextFrameHandler.getContent()); } - private static EmbeddedMessageChannel createChannel() { + private EmbeddedChannel createChannel() { return createChannel(null); } - private static EmbeddedMessageChannel createChannel(ChannelHandler handler) { - return new EmbeddedMessageChannel( + private EmbeddedChannel createChannel(ChannelHandler handler) { + return new EmbeddedChannel( new WebSocketServerProtocolHandler("/test", null, false), new HttpRequestDecoder(), new HttpResponseEncoder(), @@ -124,7 +136,7 @@ public class WebSocketServerProtocolHandlerTest { handler); } - private static void writeUpgradeRequest(EmbeddedMessageChannel ch) { + private static void writeUpgradeRequest(EmbeddedChannel ch) { ch.writeInbound(WebSocketRequestBuilder.sucessful()); } @@ -132,31 +144,25 @@ public class WebSocketServerProtocolHandlerTest { return new String(response.content().array()); } - private static FullHttpResponse getHttpResponse(EmbeddedMessageChannel ch) { - MessageBuf outbound = ch.pipeline().context(MockOutboundHandler.class).outboundMessageBuffer(); - return (FullHttpResponse) outbound.poll(); - } - - private static class MockOutboundHandler - extends ChannelOperationHandlerAdapter implements ChannelOutboundMessageHandler { + private class MockOutboundHandler extends ChannelOutboundHandlerAdapter { @Override - public MessageBuf newOutboundBuffer(ChannelHandlerContext ctx) throws Exception { - return Unpooled.messageBuffer(); - } - - @Override - public void flush(ChannelHandlerContext ctx, ChannelPromise future) throws Exception { - //NoOp + public void write(ChannelHandlerContext ctx, MessageList msgs, ChannelPromise promise) + throws Exception { + for (int i = 0; i < msgs.size(); i++) { + responses.add((FullHttpResponse) msgs.get(i)); + } + promise.setSuccess(); } } - private static class CustomTextFrameHandler extends ChannelInboundMessageHandlerAdapter { + private static class CustomTextFrameHandler extends ChannelInboundHandlerAdapter { private String content; @Override - public void messageReceived(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception { - content = "processed: " + msg.text(); + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + assertEquals(1, msgs.size()); + content = "processed: " + ((TextWebSocketFrame) msgs.get(0)).text(); } String getContent() { 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 bd18a2b6a5..7515ceb888 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 @@ -16,8 +16,9 @@ package io.netty.handler.codec.spdy; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; -import io.netty.channel.embedded.EmbeddedMessageChannel; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.MessageList; +import io.netty.channel.embedded.EmbeddedChannel; import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; import org.junit.Test; @@ -99,7 +100,7 @@ public class SpdySessionHandlerTest { } private static void testSpdySessionHandler(int version, boolean server) { - EmbeddedMessageChannel sessionHandler = new EmbeddedMessageChannel( + EmbeddedChannel sessionHandler = new EmbeddedChannel( new SpdySessionHandler(version, server), new EchoHandler(closeSignal, server)); while (sessionHandler.readOutbound() != null) { @@ -278,7 +279,7 @@ public class SpdySessionHandlerTest { // Echo Handler opens 4 half-closed streams on session connection // and then sets the number of concurrent streams to 3 - private static class EchoHandler extends ChannelInboundMessageHandlerAdapter { + private static class EchoHandler extends ChannelInboundHandlerAdapter { private final int closeSignal; private final boolean server; @@ -309,35 +310,38 @@ public class SpdySessionHandlerTest { } @Override - public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { - if (msg instanceof SpdyDataFrame || - msg instanceof SpdyPingFrame || - msg instanceof SpdyHeadersFrame) { + public void messageReceived(ChannelHandlerContext ctx, MessageList messages) throws Exception { + for (int i = 0; i < messages.size(); i++) { + Object msg = messages.get(i); + if (msg instanceof SpdyDataFrame || + msg instanceof SpdyPingFrame || + msg instanceof SpdyHeadersFrame) { - ctx.write(msg); - return; - } - - if (msg instanceof SpdySynStreamFrame) { - - SpdySynStreamFrame spdySynStreamFrame = (SpdySynStreamFrame) msg; - if (!spdySynStreamFrame.isUnidirectional()) { - int streamID = spdySynStreamFrame.getStreamId(); - SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamID); - spdySynReplyFrame.setLast(spdySynStreamFrame.isLast()); - for (Map.Entry entry: spdySynStreamFrame.headers()) { - spdySynReplyFrame.headers().add(entry.getKey(), entry.getValue()); - } - - ctx.write(spdySynReplyFrame); + ctx.write(msg); + return; } - return; - } - if (msg instanceof SpdySettingsFrame) { - SpdySettingsFrame spdySettingsFrame = (SpdySettingsFrame) msg; - if (spdySettingsFrame.isSet(closeSignal)) { - ctx.close(); + if (msg instanceof SpdySynStreamFrame) { + + SpdySynStreamFrame spdySynStreamFrame = (SpdySynStreamFrame) msg; + if (!spdySynStreamFrame.isUnidirectional()) { + int streamID = spdySynStreamFrame.getStreamId(); + SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamID); + spdySynReplyFrame.setLast(spdySynStreamFrame.isLast()); + for (Map.Entry entry: spdySynStreamFrame.headers()) { + spdySynReplyFrame.headers().add(entry.getKey(), entry.getValue()); + } + + ctx.write(spdySynReplyFrame); + } + return; + } + + if (msg instanceof SpdySettingsFrame) { + SpdySettingsFrame spdySettingsFrame = (SpdySettingsFrame) msg; + if (spdySettingsFrame.isSet(closeSignal)) { + ctx.close(); + } } } } diff --git a/codec-socks/pom.xml b/codec-socks/pom.xml index 5e5806e6e2..2771f57ac1 100644 --- a/codec-socks/pom.xml +++ b/codec-socks/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.0.0.Final-SNAPSHOT + 4.0.0.CR4-SNAPSHOT netty-codec-socks diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksAuthRequestDecoder.java b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksAuthRequestDecoder.java index 53101ed862..d298fd4917 100644 --- a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksAuthRequestDecoder.java +++ b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksAuthRequestDecoder.java @@ -16,8 +16,8 @@ package io.netty.handler.codec.socks; import io.netty.buffer.ByteBuf; -import io.netty.buffer.MessageBuf; import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.MessageList; import io.netty.handler.codec.ReplayingDecoder; import io.netty.util.CharsetUtil; @@ -43,7 +43,7 @@ public class SocksAuthRequestDecoder extends ReplayingDecoder out) throws Exception { + protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, MessageList out) throws Exception { switch (state()) { case CHECK_PROTOCOL_VERSION: { version = SocksSubnegotiationVersion.fromByte(byteBuf.readByte()); diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksAuthResponseDecoder.java b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksAuthResponseDecoder.java index 7ba8db8d94..01ed2c2979 100644 --- a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksAuthResponseDecoder.java +++ b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksAuthResponseDecoder.java @@ -16,8 +16,8 @@ package io.netty.handler.codec.socks; import io.netty.buffer.ByteBuf; -import io.netty.buffer.MessageBuf; import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.MessageList; import io.netty.handler.codec.ReplayingDecoder; /** @@ -40,7 +40,7 @@ public class SocksAuthResponseDecoder extends ReplayingDecoder out) + protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, MessageList out) throws Exception { switch (state()) { case CHECK_PROTOCOL_VERSION: { diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksCmdRequestDecoder.java b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksCmdRequestDecoder.java index feadc09982..b85df155e9 100644 --- a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksCmdRequestDecoder.java +++ b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksCmdRequestDecoder.java @@ -16,8 +16,8 @@ package io.netty.handler.codec.socks; import io.netty.buffer.ByteBuf; -import io.netty.buffer.MessageBuf; import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.MessageList; import io.netty.handler.codec.ReplayingDecoder; import io.netty.util.CharsetUtil; @@ -46,7 +46,7 @@ public class SocksCmdRequestDecoder extends ReplayingDecoder out) throws Exception { + protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, MessageList out) throws Exception { switch (state()) { case CHECK_PROTOCOL_VERSION: { version = SocksProtocolVersion.fromByte(byteBuf.readByte()); diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksCmdResponseDecoder.java b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksCmdResponseDecoder.java index 3670a36dcc..203005dd2d 100644 --- a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksCmdResponseDecoder.java +++ b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksCmdResponseDecoder.java @@ -16,8 +16,8 @@ package io.netty.handler.codec.socks; import io.netty.buffer.ByteBuf; -import io.netty.buffer.MessageBuf; import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.MessageList; import io.netty.handler.codec.ReplayingDecoder; import io.netty.util.CharsetUtil; @@ -46,7 +46,7 @@ public class SocksCmdResponseDecoder extends ReplayingDecoder out) throws Exception { + protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, MessageList out) throws Exception { switch (state()) { case CHECK_PROTOCOL_VERSION: { version = SocksProtocolVersion.fromByte(byteBuf.readByte()); diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksInitRequestDecoder.java b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksInitRequestDecoder.java index 30102c077c..16c0b7221d 100644 --- a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksInitRequestDecoder.java +++ b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksInitRequestDecoder.java @@ -16,8 +16,8 @@ package io.netty.handler.codec.socks; import io.netty.buffer.ByteBuf; -import io.netty.buffer.MessageBuf; import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.MessageList; import io.netty.handler.codec.ReplayingDecoder; import java.util.ArrayList; @@ -44,7 +44,7 @@ public class SocksInitRequestDecoder extends ReplayingDecoder out) throws Exception { + protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, MessageList out) throws Exception { switch (state()) { case CHECK_PROTOCOL_VERSION: { version = SocksProtocolVersion.fromByte(byteBuf.readByte()); diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksInitResponseDecoder.java b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksInitResponseDecoder.java index 1978269363..e5c07a2a1c 100644 --- a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksInitResponseDecoder.java +++ b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksInitResponseDecoder.java @@ -16,8 +16,8 @@ package io.netty.handler.codec.socks; import io.netty.buffer.ByteBuf; -import io.netty.buffer.MessageBuf; import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.MessageList; import io.netty.handler.codec.ReplayingDecoder; /** @@ -41,7 +41,7 @@ public class SocksInitResponseDecoder extends ReplayingDecoder out) throws Exception { + protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, MessageList out) throws Exception { switch (state()) { case CHECK_PROTOCOL_VERSION: { version = SocksProtocolVersion.fromByte(byteBuf.readByte()); diff --git a/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksAuthRequestDecoderTest.java b/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksAuthRequestDecoderTest.java index a94bbeec24..ff8692a521 100644 --- a/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksAuthRequestDecoderTest.java +++ b/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksAuthRequestDecoderTest.java @@ -15,7 +15,7 @@ */ package io.netty.handler.codec.socks; -import io.netty.channel.embedded.EmbeddedByteChannel; +import io.netty.channel.embedded.EmbeddedChannel; import org.junit.Test; import static org.junit.Assert.*; @@ -28,7 +28,7 @@ public class SocksAuthRequestDecoderTest { String password = "test"; SocksAuthRequest msg = new SocksAuthRequest(username, password); SocksAuthRequestDecoder decoder = new SocksAuthRequestDecoder(); - EmbeddedByteChannel embedder = new EmbeddedByteChannel(decoder); + EmbeddedChannel embedder = new EmbeddedChannel(decoder); SocksCommonTestUtils.writeMessageIntoEmbedder(embedder, msg); msg = (SocksAuthRequest) embedder.readInbound(); assertEquals(msg.username(), username); diff --git a/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksAuthResponseDecoderTest.java b/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksAuthResponseDecoderTest.java index ad88597a4d..1604f53bb9 100644 --- a/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksAuthResponseDecoderTest.java +++ b/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksAuthResponseDecoderTest.java @@ -15,7 +15,7 @@ */ package io.netty.handler.codec.socks; -import io.netty.channel.embedded.EmbeddedByteChannel; +import io.netty.channel.embedded.EmbeddedChannel; import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; import org.junit.Test; @@ -29,7 +29,7 @@ public class SocksAuthResponseDecoderTest { logger.debug("Testing SocksAuthResponseDecoder with authStatus: " + authStatus); SocksAuthResponse msg = new SocksAuthResponse(authStatus); SocksAuthResponseDecoder decoder = new SocksAuthResponseDecoder(); - EmbeddedByteChannel embedder = new EmbeddedByteChannel(decoder); + EmbeddedChannel embedder = new EmbeddedChannel(decoder); SocksCommonTestUtils.writeMessageIntoEmbedder(embedder, msg); msg = (SocksAuthResponse) embedder.readInbound(); assertSame(msg.authStatus(), authStatus); diff --git a/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksCmdRequestDecoderTest.java b/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksCmdRequestDecoderTest.java index 32de008844..8ae2a726db 100644 --- a/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksCmdRequestDecoderTest.java +++ b/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksCmdRequestDecoderTest.java @@ -15,7 +15,7 @@ */ package io.netty.handler.codec.socks; -import io.netty.channel.embedded.EmbeddedByteChannel; +import io.netty.channel.embedded.EmbeddedChannel; import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; import org.junit.Test; @@ -34,7 +34,7 @@ public class SocksCmdRequestDecoderTest { " port: " + port); SocksCmdRequest msg = new SocksCmdRequest(cmdType, addressType, host, port); SocksCmdRequestDecoder decoder = new SocksCmdRequestDecoder(); - EmbeddedByteChannel embedder = new EmbeddedByteChannel(decoder); + EmbeddedChannel embedder = new EmbeddedChannel(decoder); SocksCommonTestUtils.writeMessageIntoEmbedder(embedder, msg); if (msg.addressType() == SocksAddressType.UNKNOWN) { assertTrue(embedder.readInbound() instanceof UnknownSocksRequest); diff --git a/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksCmdResponseDecoderTest.java b/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksCmdResponseDecoderTest.java index f058780d36..9f2691dc96 100644 --- a/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksCmdResponseDecoderTest.java +++ b/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksCmdResponseDecoderTest.java @@ -15,7 +15,7 @@ */ package io.netty.handler.codec.socks; -import io.netty.channel.embedded.EmbeddedByteChannel; +import io.netty.channel.embedded.EmbeddedChannel; import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; import org.junit.Test; @@ -25,12 +25,12 @@ import static org.junit.Assert.*; public class SocksCmdResponseDecoderTest { private static final InternalLogger logger = InternalLoggerFactory.getInstance(SocksCmdResponseDecoderTest.class); - private static void testSocksCmdResponseDecoderWithDifferentParams(SocksCmdStatus cmdStatus, - SocksAddressType addressType) { + private static void testSocksCmdResponseDecoderWithDifferentParams( + SocksCmdStatus cmdStatus, SocksAddressType addressType) { logger.debug("Testing cmdStatus: " + cmdStatus + " addressType: " + addressType); SocksResponse msg = new SocksCmdResponse(cmdStatus, addressType); SocksCmdResponseDecoder decoder = new SocksCmdResponseDecoder(); - EmbeddedByteChannel embedder = new EmbeddedByteChannel(decoder); + EmbeddedChannel embedder = new EmbeddedChannel(decoder); SocksCommonTestUtils.writeMessageIntoEmbedder(embedder, msg); if (addressType == SocksAddressType.UNKNOWN) { assertTrue(embedder.readInbound() instanceof UnknownSocksResponse); diff --git a/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksCommonTestUtils.java b/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksCommonTestUtils.java index ade7a7e417..f4a54dbad0 100644 --- a/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksCommonTestUtils.java +++ b/codec-socks/src/test/java/io/netty/handler/codec/socks/SocksCommonTestUtils.java @@ -17,7 +17,7 @@ package io.netty.handler.codec.socks; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedByteChannel; +import io.netty.channel.embedded.EmbeddedChannel; final class SocksCommonTestUtils { /** @@ -27,7 +27,7 @@ final class SocksCommonTestUtils { //NOOP } - public static void writeMessageIntoEmbedder(EmbeddedByteChannel embedder, SocksMessage msg) { + public static void writeMessageIntoEmbedder(EmbeddedChannel embedder, SocksMessage msg) { ByteBuf buf = Unpooled.buffer(); msg.encodeAsByteBuf(buf); embedder.writeInbound(buf); diff --git a/codec/pom.xml b/codec/pom.xml index b12012909f..a11717c764 100644 --- a/codec/pom.xml +++ b/codec/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.0.0.Final-SNAPSHOT + 4.0.0.CR4-SNAPSHOT netty-codec diff --git a/codec/src/main/java/io/netty/handler/codec/ByteToByteCodec.java b/codec/src/main/java/io/netty/handler/codec/ByteToByteCodec.java deleted file mode 100644 index 2907e6a00a..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/ByteToByteCodec.java +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelDuplexHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundByteHandler; -import io.netty.channel.ChannelOutboundByteHandler; -import io.netty.channel.ChannelPromise; - -/** - * A Codec for on-the-fly encoding/decoding of bytes. - * - * This can be thought of as a combination of {@link ByteToByteDecoder} and {@link ByteToByteEncoder}. - * - * Here is an example of a {@link ByteToByteCodec} which just square {@link Integer} read from a {@link ByteBuf}. - *
- *     public class SquareCodec extends {@link ByteToByteCodec} {
- *         {@code @Override}
- *         public void decode({@link ChannelHandlerContext} ctx, {@link ByteBuf} in, {@link ByteBuf} out)
- *                 throws {@link Exception} {
- *             if (in.readableBytes() < 4) {
- *                 return;
- *             }
- *             int value = in.readInt();
- *             out.writeInt(value * value);
- *         }
- *
- *         {@code @Overrride}
- *         public void encode({@link ChannelHandlerContext} ctx, {@link ByteBuf} in, {@link ByteBuf} out)
- *                 throws {@link Exception} {
- *             if (in.readableBytes() < 4) {
- *                 return;
- *             }
- *             int value = in.readInt();
- *             out.writeInt(value / value);
- *         }
- *     }
- * 
- */ -public abstract class ByteToByteCodec - extends ChannelDuplexHandler - implements ChannelInboundByteHandler, ChannelOutboundByteHandler { - - private final ByteToByteEncoder encoder = new ByteToByteEncoder() { - @Override - protected void encode( - ChannelHandlerContext ctx, - ByteBuf in, ByteBuf out) throws Exception { - ByteToByteCodec.this.encode(ctx, in, out); - } - }; - - private final ByteToByteDecoder decoder = new ByteToByteDecoder() { - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in, ByteBuf out) throws Exception { - ByteToByteCodec.this.decode(ctx, in, out); - } - - @Override - protected void decodeLast(ChannelHandlerContext ctx, ByteBuf in, ByteBuf out) throws Exception { - ByteToByteCodec.this.decodeLast(ctx, in, out); - } - }; - - @Override - public ByteBuf newInboundBuffer(ChannelHandlerContext ctx) throws Exception { - return decoder.newInboundBuffer(ctx); - } - - @Override - public void inboundBufferUpdated(ChannelHandlerContext ctx) throws Exception { - decoder.inboundBufferUpdated(ctx); - } - - @Override - public ByteBuf newOutboundBuffer(ChannelHandlerContext ctx) throws Exception { - return encoder.newOutboundBuffer(ctx); - } - - @Override - public void flush( - ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { - encoder.flush(ctx, promise); - } - - @Override - public void discardInboundReadBytes(ChannelHandlerContext ctx) throws Exception { - decoder.discardInboundReadBytes(ctx); - } - - @Override - public void discardOutboundReadBytes(ChannelHandlerContext ctx) throws Exception { - encoder.discardOutboundReadBytes(ctx); - } - - /** - * @see {@link ByteToByteEncoder#encode(ChannelHandlerContext, ByteBuf, ByteBuf)} - */ - protected abstract void encode(ChannelHandlerContext ctx, ByteBuf in, ByteBuf out) throws Exception; - - /** - * @see {@link ByteToByteDecoder#decode(ChannelHandlerContext, ByteBuf, ByteBuf)} - */ - protected abstract void decode(ChannelHandlerContext ctx, ByteBuf in, ByteBuf out) throws Exception; - - /** - * @see {@link ByteToByteDecoder#decodeLast(ChannelHandlerContext, ByteBuf, ByteBuf)} - */ - protected void decodeLast(ChannelHandlerContext ctx, ByteBuf in, ByteBuf out) throws Exception { - decode(ctx, in, out); - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/ByteToByteDecoder.java b/codec/src/main/java/io/netty/handler/codec/ByteToByteDecoder.java deleted file mode 100644 index a2ad13ead7..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/ByteToByteDecoder.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundByteHandlerAdapter; - -/** - * {@link ChannelInboundByteHandlerAdapter} which decodes bytes in a stream-like fashion from one {@link ByteBuf} to an - * other. - * - * This kind of decoder is often useful for doing on-the-fly processiing like i.e. compression. - * - * But you can also do other things with it. For example here is an implementation which reads {@link Integer}s from - * the input {@link ByteBuf} and square them before write them to the output {@link ByteBuf}. - * - *
- *     public class SquareDecoder extends {@link ByteToByteDecoder} {
- *         {@code @Override}
- *         public void decode({@link ChannelHandlerContext} ctx, {@link ByteBuf} in, {@link ByteBuf} out)
- *                 throws {@link Exception} {
- *             if (in.readableBytes() < 4) {
- *                 return;
- *             }
- *             int value = in.readInt();
- *             out.writeInt(value * value);
- *         }
- *     }
- * 
- */ -public abstract class ByteToByteDecoder extends ChannelInboundByteHandlerAdapter { - - private volatile boolean singleDecode; - - /** - * If set then only one message is decoded on each {@link #inboundBufferUpdated(ChannelHandlerContext)} call. - * This may be useful if you need to do some protocol upgrade and want to make sure nothing is mixed up. - * - * Default is {@code false} as this has performance impacts. - */ - public void setSingleDecode(boolean singleDecode) { - this.singleDecode = singleDecode; - } - - /** - * If {@code true} then only one message is decoded on each - * {@link #inboundBufferUpdated(ChannelHandlerContext)} call. - * - * Default is {@code false} as this has performance impacts. - */ - public boolean isSingleDecode() { - return singleDecode; - } - - @Override - public void inboundBufferUpdated(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - callDecode(ctx, in, ctx.nextInboundByteBuffer()); - } - - @Override - public void channelInactive(ChannelHandlerContext ctx) throws Exception { - ByteBuf in = ctx.inboundByteBuffer(); - ByteBuf out = ctx.nextInboundByteBuffer(); - if (!in.isReadable()) { - callDecode(ctx, in, out); - } - - int oldOutSize = out.readableBytes(); - try { - decodeLast(ctx, in, out); - } catch (CodecException e) { - throw e; - } catch (Throwable t) { - throw new DecoderException(t); - } finally { - if (out.readableBytes() > oldOutSize) { - ctx.fireInboundBufferUpdated(); - } - ctx.fireChannelInactive(); - } - } - - /** - * Call the {@link #decode(ChannelHandlerContext, ByteBuf, ByteBuf)} method until it is done. - */ - private void callDecode(ChannelHandlerContext ctx, ByteBuf in, ByteBuf out) { - int oldOutSize = out.readableBytes(); - try { - while (in.isReadable()) { - int oldInSize = in.readableBytes(); - decode(ctx, in, out); - if (oldInSize == in.readableBytes() || isSingleDecode()) { - break; - } - } - } catch (CodecException e) { - throw e; - } catch (Throwable t) { - throw new DecoderException(t); - } finally { - if (out.readableBytes() > oldOutSize) { - ctx.fireInboundBufferUpdated(); - } - } - } - - /** - * Decode the from one {@link ByteBuf} to an other. This method will be called till either the input - * {@link ByteBuf} has nothing to read anymore or till nothing was read from the input {@link ByteBuf}. - * - * @param ctx the {@link ChannelHandlerContext} which this {@link ByteToByteDecoder} belongs to - * @param in the {@link ByteBuf} from which to read data - * @param out the {@link ByteBuf} to which the decoded data will be written - * @throws Exception is thrown if an error accour - */ - protected abstract void decode(ChannelHandlerContext ctx, ByteBuf in, ByteBuf out) throws Exception; - - /** - * Is called one last time when the {@link ChannelHandlerContext} goes in-active. Which means the - * {@link #channelInactive(ChannelHandlerContext)} was triggered. - * - * By default this will just call {@link #decode(ChannelHandlerContext, ByteBuf, ByteBuf)} but sub-classes may - * override this for some special cleanup operation. - */ - protected void decodeLast(ChannelHandlerContext ctx, ByteBuf in, ByteBuf out) throws Exception { - decode(ctx, in, out); - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/ByteToByteEncoder.java b/codec/src/main/java/io/netty/handler/codec/ByteToByteEncoder.java deleted file mode 100644 index f1ed0f9212..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/ByteToByteEncoder.java +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.handler.codec; - -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelOutboundByteHandlerAdapter; -import io.netty.channel.ChannelPromise; -import io.netty.channel.FileRegion; -import io.netty.channel.IncompleteFlushException; - -import java.io.EOFException; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.channels.WritableByteChannel; - -/** - * {@link ChannelOutboundByteHandlerAdapter} which encodes bytes in a stream-like fashion from one {@link ByteBuf} to an - * other. - * - * This kind of decoder is often useful for doing on-the-fly processing like i.e. compression. - * - * But you can also do other things with it. For example here is an implementation which reads {@link Integer}s from - * the input {@link ByteBuf} and square them before write them to the output {@link ByteBuf}. - * - *
- *     public class SquareEncoder extends {@link ByteToByteEncoder} {
- *         {@code @Override}
- *         public void encode({@link ChannelHandlerContext} ctx, {@link ByteBuf} in, {@link ByteBuf} out)
- *                 throws {@link Exception} {
- *             if (in.readableBytes() < 4) {
- *                 return;
- *             }
- *             int value = in.readInt();
- *             out.writeInt(value * value);
- *         }
- *     }
- * 
- */ -public abstract class ByteToByteEncoder extends ChannelOutboundByteHandlerAdapter { - - @Override - protected void flush(ChannelHandlerContext ctx, ByteBuf in, ChannelPromise promise) throws Exception { - ByteBuf out = ctx.nextOutboundByteBuffer(); - boolean encoded = false; - - try { - while (in.isReadable()) { - int oldInSize = in.readableBytes(); - encode(ctx, in, out); - encoded = true; - if (oldInSize == in.readableBytes()) { - break; - } - } - } catch (Throwable t) { - if (!(t instanceof CodecException)) { - t = new EncoderException(t); - } - if (encoded) { - t = new IncompleteFlushException("unable to encode all bytes", t); - } - in.discardSomeReadBytes(); - promise.setFailure(t); - return; - } - - ctx.flush(promise); - } - - @Override - public void sendFile(ChannelHandlerContext ctx, FileRegion region, ChannelPromise promise) throws Exception { - long written = 0; - try { - for (;;) { - long localWritten = region.transferTo(new BufferChannel(ctx.outboundByteBuffer()), written); - if (localWritten == -1) { - checkEOF(region, written); - flush(ctx, promise); - break; - } - written += localWritten; - if (written >= region.count()) { - flush(ctx, promise); - break; - } - } - } catch (IOException e) { - promise.setFailure(new EncoderException(e)); - } finally { - region.release(); - } - } - - private static void checkEOF(FileRegion region, long writtenBytes) throws IOException { - if (writtenBytes < region.count()) { - throw new EOFException("Expected to be able to write " - + region.count() + " bytes, but only wrote " - + writtenBytes); - } - } - - private static final class BufferChannel implements WritableByteChannel { - private final ByteBuf buffer; - - BufferChannel(ByteBuf buffer) { - this.buffer = buffer; - } - @Override - public int write(ByteBuffer src) { - int bytes = src.remaining(); - buffer.writeBytes(src); - return bytes; - } - - @Override - public boolean isOpen() { - return buffer.refCnt() > 0; - } - - @Override - public void close() { - // NOOP - } - } - - /** - * Encodes the from one {@link ByteBuf} to an other. This method will be called till either the input - * {@link ByteBuf} has nothing to read anymore or till nothing was read from the input {@link ByteBuf}. - * - * @param ctx the {@link ChannelHandlerContext} which this {@link ByteToByteDecoder} belongs to - * @param in the {@link ByteBuf} from which to read data - * @param out the {@link ByteBuf} to which the decoded data will be written - * @throws Exception is thrown if an error accour - */ - protected abstract void encode(ChannelHandlerContext ctx, ByteBuf in, ByteBuf out) throws Exception; -} diff --git a/codec/src/main/java/io/netty/handler/codec/ByteToMessageCodec.java b/codec/src/main/java/io/netty/handler/codec/ByteToMessageCodec.java index b44331b4b6..7622510c35 100644 --- a/codec/src/main/java/io/netty/handler/codec/ByteToMessageCodec.java +++ b/codec/src/main/java/io/netty/handler/codec/ByteToMessageCodec.java @@ -16,16 +16,13 @@ package io.netty.handler.codec; import io.netty.buffer.ByteBuf; -import io.netty.buffer.MessageBuf; import io.netty.channel.ChannelDuplexHandler; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundByteHandler; -import io.netty.channel.ChannelOutboundMessageHandler; import io.netty.channel.ChannelPromise; +import io.netty.channel.MessageList; import io.netty.util.internal.TypeParameterMatcher; -public abstract class ByteToMessageCodec extends ChannelDuplexHandler - implements ChannelInboundByteHandler, ChannelOutboundMessageHandler { +public abstract class ByteToMessageCodec extends ChannelDuplexHandler { private final TypeParameterMatcher outboundMsgMatcher; private final MessageToByteEncoder encoder = new MessageToByteEncoder() { @@ -42,12 +39,12 @@ public abstract class ByteToMessageCodec extends ChannelDuplexHandler private final ByteToMessageDecoder decoder = new ByteToMessageDecoder() { @Override - public void decode(ChannelHandlerContext ctx, ByteBuf in, MessageBuf out) throws Exception { + public void decode(ChannelHandlerContext ctx, ByteBuf in, MessageList out) throws Exception { ByteToMessageCodec.this.decode(ctx, in, out); } @Override - protected void decodeLast(ChannelHandlerContext ctx, ByteBuf in, MessageBuf out) throws Exception { + protected void decodeLast(ChannelHandlerContext ctx, ByteBuf in, MessageList out) throws Exception { ByteToMessageCodec.this.decodeLast(ctx, in, out); } }; @@ -60,38 +57,23 @@ public abstract class ByteToMessageCodec extends ChannelDuplexHandler outboundMsgMatcher = TypeParameterMatcher.get(outboundMessageType); } - @Override - public ByteBuf newInboundBuffer(ChannelHandlerContext ctx) throws Exception { - return decoder.newInboundBuffer(ctx); - } - - @Override - public void discardInboundReadBytes(ChannelHandlerContext ctx) throws Exception { - decoder.discardInboundReadBytes(ctx); - } - - @Override - public MessageBuf newOutboundBuffer(ChannelHandlerContext ctx) throws Exception { - return encoder.newOutboundBuffer(ctx); - } - - @Override - public void inboundBufferUpdated(ChannelHandlerContext ctx) throws Exception { - decoder.inboundBufferUpdated(ctx); - } - - @Override - public void flush(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { - encoder.flush(ctx, promise); - } - public boolean acceptOutboundMessage(Object msg) throws Exception { return outboundMsgMatcher.match(msg); } + @Override + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + decoder.messageReceived(ctx, msgs); + } + + @Override + public void write(ChannelHandlerContext ctx, MessageList msgs, ChannelPromise promise) throws Exception { + encoder.write(ctx, msgs, promise); + } + protected abstract void encode(ChannelHandlerContext ctx, I msg, ByteBuf out) throws Exception; - protected abstract void decode(ChannelHandlerContext ctx, ByteBuf in, MessageBuf out) throws Exception; - protected void decodeLast(ChannelHandlerContext ctx, ByteBuf in, MessageBuf out) throws Exception { + protected abstract void decode(ChannelHandlerContext ctx, ByteBuf in, MessageList out) throws Exception; + protected void decodeLast(ChannelHandlerContext ctx, ByteBuf in, MessageList out) throws Exception { decode(ctx, in, out); } } diff --git a/codec/src/main/java/io/netty/handler/codec/ByteToMessageDecoder.java b/codec/src/main/java/io/netty/handler/codec/ByteToMessageDecoder.java index 6366340c63..7ed8d7fdb7 100644 --- a/codec/src/main/java/io/netty/handler/codec/ByteToMessageDecoder.java +++ b/codec/src/main/java/io/netty/handler/codec/ByteToMessageDecoder.java @@ -16,14 +16,15 @@ package io.netty.handler.codec; import io.netty.buffer.ByteBuf; -import io.netty.buffer.MessageBuf; +import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundByteHandler; -import io.netty.channel.ChannelInboundByteHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.MessageList; +import io.netty.util.internal.StringUtil; /** - * {@link ChannelInboundByteHandler} which decodes bytes in a stream-like fashion from one {@link ByteBuf} to an other - * Message type. + * {@link ChannelInboundHandlerAdapter} which decodes bytes in a stream-like fashion from one {@link ByteBuf} to an + * other Message type. * * For example here is an implementation which reads all readable bytes from * the input {@link ByteBuf} and create a new {@link ByteBuf}. @@ -31,22 +32,24 @@ import io.netty.channel.ChannelInboundByteHandlerAdapter; *
  *     public class SquareDecoder extends {@link ByteToMessageDecoder} {
  *         {@code @Override}
- *         public void decode({@link ChannelHandlerContext} ctx, {@link ByteBuf} in, {@link MessageBuf} out)
+ *         public void decode({@link ChannelHandlerContext} ctx, {@link ByteBuf} in, {@link MessageList} out)
  *                 throws {@link Exception} {
  *             out.add(in.readBytes(in.readableBytes()));
  *         }
  *     }
  * 
*/ -public abstract class ByteToMessageDecoder - extends ChannelInboundByteHandlerAdapter { +public abstract class ByteToMessageDecoder extends ChannelInboundHandlerAdapter { + protected ByteBuf cumulation; private volatile boolean singleDecode; private boolean decodeWasNull; + private MessageList out; + private boolean removed; /** - * If set then only one message is decoded on each {@link #inboundBufferUpdated(ChannelHandlerContext)} call. - * This may be useful if you need to do some protocol upgrade and want to make sure nothing is mixed up. + * If set then only one message is decoded on each {@link #messageReceived(ChannelHandlerContext, MessageList)} + * call. This may be useful if you need to do some protocol upgrade and want to make sure nothing is mixed up. * * Default is {@code false} as this has performance impacts. */ @@ -56,7 +59,7 @@ public abstract class ByteToMessageDecoder /** * If {@code true} then only one message is decoded on each - * {@link #inboundBufferUpdated(ChannelHandlerContext)} call. + * {@link #messageReceived(ChannelHandlerContext, MessageList)} call. * * Default is {@code false} as this has performance impacts. */ @@ -64,9 +67,119 @@ public abstract class ByteToMessageDecoder return singleDecode; } + /** + * Returns the actual number of readable bytes in the internal cumulative + * buffer of this decoder. You usually do not need to rely on this value + * to write a decoder. Use it only when you must use it at your own risk. + * This method is a shortcut to {@link #internalBuffer() internalBuffer().readableBytes()}. + */ + protected int actualReadableBytes() { + return internalBuffer().readableBytes(); + } + + /** + * Returns the internal cumulative buffer of this decoder. You usually + * do not need to access the internal buffer directly to write a decoder. + * Use it only when you must use it at your own risk. + */ + protected ByteBuf internalBuffer() { + if (cumulation != null) { + return cumulation; + } else { + return Unpooled.EMPTY_BUFFER; + } + } + @Override - public void inboundBufferUpdated(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - callDecode(ctx, in); + public final void handlerRemoved(ChannelHandlerContext ctx) throws Exception { + removed = true; + ByteBuf buf = internalBuffer(); + if (buf.isReadable()) { + if (out == null) { + ctx.fireMessageReceived(buf); + } else { + out.add(buf.copy()); + } + buf.clear(); + } + handlerRemoved0(); + } + + /** + * Gets called after the {@link ByteToMessageDecoder} was removed from the actual context and it doesn't handle + * events anymore. + */ + protected void handlerRemoved0() throws Exception { } + + @Override + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + out = MessageList.newInstance(); + try { + int size = msgs.size(); + for (int i = 0; i < size; i ++) { + Object m = msgs.get(i); + // handler was removed in the loop + if (removed) { + out.add(m); + continue; + } + if (m instanceof ByteBuf) { + ByteBuf data = (ByteBuf) m; + if (cumulation == null) { + cumulation = data; + try { + callDecode(ctx, data, out); + } finally { + if (!data.isReadable()) { + cumulation = null; + data.release(); + } + } + } else { + try { + if (cumulation.writerIndex() > cumulation.maxCapacity() - data.readableBytes()) { + ByteBuf oldCumulation = cumulation; + cumulation = ctx.alloc().buffer(oldCumulation.readableBytes() + data.readableBytes()); + cumulation.writeBytes(oldCumulation); + oldCumulation.release(); + } + cumulation.writeBytes(data); + callDecode(ctx, cumulation, out); + } finally { + if (!cumulation.isReadable()) { + cumulation.release(); + cumulation = null; + } else { + cumulation.discardSomeReadBytes(); + } + data.release(); + } + } + } else { + out.add(m); + } + } + } catch (DecoderException e) { + throw e; + } catch (Throwable t) { + throw new DecoderException(t); + } finally { + // release the cumulation if the handler was removed while messages are processed + if (removed) { + if (cumulation != null) { + cumulation.release(); + cumulation = null; + } + removed = false; + } + MessageList out = this.out; + this.out = null; + if (out.isEmpty()) { + decodeWasNull = true; + } + msgs.recycle(); + ctx.fireMessageReceived(out); + } } @Override @@ -82,35 +195,36 @@ public abstract class ByteToMessageDecoder @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { - OutputMessageBuf out = OutputMessageBuf.get(); + MessageList out = MessageList.newInstance(); try { - ByteBuf in = ctx.inboundByteBuffer(); - if (in.isReadable()) { - callDecode(ctx, in); + if (cumulation != null) { + callDecode(ctx, cumulation, out); + decodeLast(ctx, cumulation, out); + } else { + decodeLast(ctx, Unpooled.EMPTY_BUFFER, out); } - decodeLast(ctx, in, out); - } catch (CodecException e) { + } catch (DecoderException e) { throw e; - } catch (Throwable cause) { - throw new DecoderException(cause); + } catch (Exception e) { + throw new DecoderException(e); } finally { - if (out.drainToNextInbound(ctx)) { - ctx.fireInboundBufferUpdated(); + if (cumulation != null) { + cumulation.release(); + cumulation = null; } + + ctx.fireMessageReceived(out); ctx.fireChannelInactive(); } } - protected void callDecode(ChannelHandlerContext ctx, ByteBuf in) { - boolean wasNull = false; - OutputMessageBuf out = OutputMessageBuf.get(); + protected void callDecode(ChannelHandlerContext ctx, ByteBuf in, MessageList out) { try { while (in.isReadable()) { int outSize = out.size(); int oldInputLength = in.readableBytes(); decode(ctx, in, out); if (outSize == out.size()) { - wasNull = true; if (oldInputLength == in.readableBytes()) { break; } else { @@ -118,10 +232,10 @@ public abstract class ByteToMessageDecoder } } - wasNull = false; if (oldInputLength == in.readableBytes()) { - throw new IllegalStateException( - "decode() did not read anything but decoded a message."); + throw new DecoderException( + StringUtil.simpleClassName(getClass()) + + ".decode() did not read anything but decoded a message."); } if (isSingleDecode()) { @@ -132,15 +246,6 @@ public abstract class ByteToMessageDecoder throw e; } catch (Throwable cause) { throw new DecoderException(cause); - } finally { - if (out.drainToNextInbound(ctx)) { - decodeWasNull = false; - ctx.fireInboundBufferUpdated(); - } else { - if (wasNull) { - decodeWasNull = true; - } - } } } @@ -149,22 +254,22 @@ public abstract class ByteToMessageDecoder * {@link ByteBuf} has nothing to read anymore, till nothing was read from the input {@link ByteBuf} or till * this method returns {@code null}. * - * @param ctx the {@link ChannelHandlerContext} which this {@link ByteToByteDecoder} belongs to + * @param ctx the {@link ChannelHandlerContext} which this {@link ByteToMessageDecoder} belongs to * @param in the {@link ByteBuf} from which to read data - * @param out the {@link MessageBuf} to which decoded messages should be added + * @param out the {@link MessageList} to which decoded messages should be added * @throws Exception is thrown if an error accour */ - protected abstract void decode(ChannelHandlerContext ctx, ByteBuf in, MessageBuf out) throws Exception; + protected abstract void decode(ChannelHandlerContext ctx, ByteBuf in, MessageList out) throws Exception; /** * Is called one last time when the {@link ChannelHandlerContext} goes in-active. Which means the * {@link #channelInactive(ChannelHandlerContext)} was triggered. * - * By default this will just call {@link #decode(ChannelHandlerContext, ByteBuf, MessageBuf)} but sub-classes may + * By default this will just call {@link #decode(ChannelHandlerContext, ByteBuf, MessageList)} but sub-classes may * override this for some special cleanup operation. */ - protected void decodeLast(ChannelHandlerContext ctx, ByteBuf in, MessageBuf out) throws Exception { + protected void decodeLast(ChannelHandlerContext ctx, ByteBuf in, MessageList out) throws Exception { decode(ctx, in, out); } } diff --git a/codec/src/main/java/io/netty/handler/codec/DelimiterBasedFrameDecoder.java b/codec/src/main/java/io/netty/handler/codec/DelimiterBasedFrameDecoder.java index 3d22a7dc83..1a0a02d2fa 100644 --- a/codec/src/main/java/io/netty/handler/codec/DelimiterBasedFrameDecoder.java +++ b/codec/src/main/java/io/netty/handler/codec/DelimiterBasedFrameDecoder.java @@ -16,8 +16,8 @@ package io.netty.handler.codec; import io.netty.buffer.ByteBuf; -import io.netty.buffer.MessageBuf; import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.MessageList; /** * A decoder that splits the received {@link ByteBuf}s by one or more @@ -211,7 +211,7 @@ public class DelimiterBasedFrameDecoder extends ByteToMessageDecoder { } @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in, MessageBuf out) throws Exception { + protected void decode(ChannelHandlerContext ctx, ByteBuf in, MessageList out) throws Exception { Object decoded = decode(ctx, in); if (decoded != null) { out.add(decoded); diff --git a/codec/src/main/java/io/netty/handler/codec/FixedLengthFrameDecoder.java b/codec/src/main/java/io/netty/handler/codec/FixedLengthFrameDecoder.java index 600c19babd..3cc025e711 100644 --- a/codec/src/main/java/io/netty/handler/codec/FixedLengthFrameDecoder.java +++ b/codec/src/main/java/io/netty/handler/codec/FixedLengthFrameDecoder.java @@ -16,9 +16,8 @@ package io.netty.handler.codec; import io.netty.buffer.ByteBuf; -import io.netty.buffer.MessageBuf; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelHandlerUtil; +import io.netty.channel.MessageList; /** * A decoder that splits the received {@link ByteBuf}s by the fixed number @@ -39,51 +38,30 @@ import io.netty.channel.ChannelHandlerUtil; public class FixedLengthFrameDecoder extends ByteToMessageDecoder { private final int frameLength; - private final boolean allocateFullBuffer; - - /** - * Calls {@link #FixedLengthFrameDecoder(int, boolean)} with {@code false} - */ - public FixedLengthFrameDecoder(int frameLength) { - this(frameLength, false); - } /** * Creates a new instance. * - * @param frameLength - * the length of the frame - * @param allocateFullBuffer - * {@code true} if the cumulative {@link ByteBuf} should use the - * {@link #frameLength} as its initial size + * @param frameLength the length of the frame */ - public FixedLengthFrameDecoder(int frameLength, boolean allocateFullBuffer) { + public FixedLengthFrameDecoder(int frameLength) { if (frameLength <= 0) { throw new IllegalArgumentException( "frameLength must be a positive integer: " + frameLength); } this.frameLength = frameLength; - this.allocateFullBuffer = allocateFullBuffer; } @Override - public ByteBuf newInboundBuffer(ChannelHandlerContext ctx) throws Exception { - if (allocateFullBuffer) { - return ChannelHandlerUtil.allocate(ctx, frameLength); - } else { - return super.newInboundBuffer(ctx); - } - } - - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in, MessageBuf out) throws Exception { + protected void decode(ChannelHandlerContext ctx, ByteBuf in, MessageList out) throws Exception { Object decoded = decode(ctx, in); if (decoded != null) { out.add(decoded); } } - protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception { + protected Object decode( + @SuppressWarnings("UnusedParameters") ChannelHandlerContext ctx, ByteBuf in) throws Exception { if (in.readableBytes() < frameLength) { return null; } else { diff --git a/codec/src/main/java/io/netty/handler/codec/LengthFieldBasedFrameDecoder.java b/codec/src/main/java/io/netty/handler/codec/LengthFieldBasedFrameDecoder.java index b9a6e357b2..d554eff759 100644 --- a/codec/src/main/java/io/netty/handler/codec/LengthFieldBasedFrameDecoder.java +++ b/codec/src/main/java/io/netty/handler/codec/LengthFieldBasedFrameDecoder.java @@ -16,9 +16,9 @@ package io.netty.handler.codec; import io.netty.buffer.ByteBuf; -import io.netty.buffer.MessageBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.MessageList; import io.netty.handler.codec.serialization.ObjectDecoder; import java.nio.ByteOrder; @@ -348,7 +348,7 @@ public class LengthFieldBasedFrameDecoder extends ByteToMessageDecoder { } @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in, MessageBuf out) throws Exception { + protected void decode(ChannelHandlerContext ctx, ByteBuf in, MessageList out) throws Exception { Object decoded = decode(ctx, in); if (decoded != null) { out.add(decoded); diff --git a/codec/src/main/java/io/netty/handler/codec/LineBasedFrameDecoder.java b/codec/src/main/java/io/netty/handler/codec/LineBasedFrameDecoder.java index 41153fc25f..414ebb38c0 100644 --- a/codec/src/main/java/io/netty/handler/codec/LineBasedFrameDecoder.java +++ b/codec/src/main/java/io/netty/handler/codec/LineBasedFrameDecoder.java @@ -16,8 +16,8 @@ package io.netty.handler.codec; import io.netty.buffer.ByteBuf; -import io.netty.buffer.MessageBuf; import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.MessageList; /** * A decoder that splits the received {@link ByteBuf}s on line endings. @@ -69,7 +69,7 @@ public class LineBasedFrameDecoder extends ByteToMessageDecoder { } @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in, MessageBuf out) throws Exception { + protected void decode(ChannelHandlerContext ctx, ByteBuf in, MessageList out) throws Exception { Object decoded = decode(ctx, in); if (decoded != null) { out.add(decoded); diff --git a/codec/src/main/java/io/netty/handler/codec/MessageToByteEncoder.java b/codec/src/main/java/io/netty/handler/codec/MessageToByteEncoder.java index 9aec6d9823..9072105de7 100644 --- a/codec/src/main/java/io/netty/handler/codec/MessageToByteEncoder.java +++ b/codec/src/main/java/io/netty/handler/codec/MessageToByteEncoder.java @@ -16,13 +16,16 @@ package io.netty.handler.codec; import io.netty.buffer.ByteBuf; -import io.netty.buffer.MessageBuf; +import io.netty.buffer.ByteBufUtil; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelOutboundMessageHandlerAdapter; +import io.netty.channel.ChannelOutboundHandlerAdapter; +import io.netty.channel.ChannelPromise; +import io.netty.channel.MessageList; +import io.netty.util.internal.TypeParameterMatcher; /** - * {@link ChannelOutboundMessageHandlerAdapter} which encodes message in a stream-like fashion from one message to an + * {@link ChannelOutboundHandlerAdapter} which encodes message in a stream-like fashion from one message to an * {@link ByteBuf}. * * @@ -38,27 +41,73 @@ import io.netty.channel.ChannelOutboundMessageHandlerAdapter; * } * */ -public abstract class MessageToByteEncoder extends ChannelOutboundMessageHandlerAdapter { +public abstract class MessageToByteEncoder extends ChannelOutboundHandlerAdapter { - protected MessageToByteEncoder() { } + private final TypeParameterMatcher matcher; + + protected MessageToByteEncoder() { + matcher = TypeParameterMatcher.find(this, MessageToByteEncoder.class, "I"); + } protected MessageToByteEncoder(Class outboundMessageType) { - super(outboundMessageType); + matcher = TypeParameterMatcher.get(outboundMessageType); + } + + public boolean acceptOutboundMessage(Object msg) throws Exception { + return matcher.match(msg); } @Override - public void flush(ChannelHandlerContext ctx, I msg) throws Exception { + public void write(ChannelHandlerContext ctx, MessageList msgs, ChannelPromise promise) throws Exception { + MessageList out = MessageList.newInstance(); + boolean success = false; try { - encode(ctx, msg, ctx.nextOutboundByteBuffer()); - } catch (CodecException e) { + ByteBuf buf = null; + int size = msgs.size(); + for (int i = 0; i < size; i ++) { + Object m = msgs.get(i); + if (acceptOutboundMessage(m)) { + @SuppressWarnings("unchecked") + I cast = (I) m; + if (buf == null) { + buf = ctx.alloc().buffer(); + } + try { + encode(ctx, cast, buf); + } finally { + ByteBufUtil.release(cast); + } + } else { + if (buf != null && buf.isReadable()) { + out.add(buf); + buf = null; + } + + out.add(m); + } + } + + if (buf != null && buf.isReadable()) { + out.add(buf); + } + + success = true; + } catch (EncoderException e) { throw e; - } catch (Exception e) { - throw new CodecException(e); + } catch (Throwable e) { + throw new EncoderException(e); + } finally { + msgs.recycle(); + if (success) { + ctx.write(out, promise); + } else { + out.releaseAllAndRecycle(); + } } } /** - * Encode a message into a {@link ByteBuf}. This method will be called till the {@link MessageBuf} has + * Encode a message into a {@link ByteBuf}. This method will be called till the {@link MessageList} has * nothing left. * * @param ctx the {@link ChannelHandlerContext} which this {@link MessageToByteEncoder} belongs to diff --git a/codec/src/main/java/io/netty/handler/codec/MessageToMessageCodec.java b/codec/src/main/java/io/netty/handler/codec/MessageToMessageCodec.java index f2bd26a43b..8f22b7b2e7 100644 --- a/codec/src/main/java/io/netty/handler/codec/MessageToMessageCodec.java +++ b/codec/src/main/java/io/netty/handler/codec/MessageToMessageCodec.java @@ -15,12 +15,10 @@ */ package io.netty.handler.codec; -import io.netty.buffer.MessageBuf; import io.netty.channel.ChannelDuplexHandler; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandler; -import io.netty.channel.ChannelOutboundMessageHandler; import io.netty.channel.ChannelPromise; +import io.netty.channel.MessageList; import io.netty.util.internal.TypeParameterMatcher; /** @@ -48,13 +46,10 @@ import io.netty.util.internal.TypeParameterMatcher; * } * */ -public abstract class MessageToMessageCodec - extends ChannelDuplexHandler - implements ChannelInboundMessageHandler, - ChannelOutboundMessageHandler { +public abstract class MessageToMessageCodec extends ChannelDuplexHandler { + + private final MessageToMessageEncoder encoder = new MessageToMessageEncoder() { - private final MessageToMessageEncoder encoder = - new MessageToMessageEncoder() { @Override public boolean acceptOutboundMessage(Object msg) throws Exception { return MessageToMessageCodec.this.acceptOutboundMessage(msg); @@ -62,13 +57,12 @@ public abstract class MessageToMessageCodec @Override @SuppressWarnings("unchecked") - protected void encode(ChannelHandlerContext ctx, Object msg, MessageBuf out) throws Exception { + protected void encode(ChannelHandlerContext ctx, Object msg, MessageList out) throws Exception { MessageToMessageCodec.this.encode(ctx, (OUTBOUND_IN) msg, out); } }; - private final MessageToMessageDecoder decoder = - new MessageToMessageDecoder() { + private final MessageToMessageDecoder decoder = new MessageToMessageDecoder() { @Override public boolean acceptInboundMessage(Object msg) throws Exception { @@ -77,7 +71,7 @@ public abstract class MessageToMessageCodec @Override @SuppressWarnings("unchecked") - protected void decode(ChannelHandlerContext ctx, Object msg, MessageBuf out) throws Exception { + protected void decode(ChannelHandlerContext ctx, Object msg, MessageList out) throws Exception { MessageToMessageCodec.this.decode(ctx, (INBOUND_IN) msg, out); } }; @@ -97,26 +91,13 @@ public abstract class MessageToMessageCodec } @Override - @SuppressWarnings("unchecked") - public MessageBuf newInboundBuffer(ChannelHandlerContext ctx) throws Exception { - return (MessageBuf) decoder.newInboundBuffer(ctx); + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + decoder.messageReceived(ctx, msgs); } @Override - @SuppressWarnings("unchecked") - public MessageBuf newOutboundBuffer(ChannelHandlerContext ctx) throws Exception { - return (MessageBuf) encoder.newOutboundBuffer(ctx); - } - - @Override - public void inboundBufferUpdated( - ChannelHandlerContext ctx) throws Exception { - decoder.inboundBufferUpdated(ctx); - } - - @Override - public void flush(ChannelHandlerContext ctx, ChannelPromise future) throws Exception { - encoder.flush(ctx, future); + public void write(ChannelHandlerContext ctx, MessageList msgs, ChannelPromise promise) throws Exception { + encoder.write(ctx, msgs, promise); } /** @@ -137,6 +118,8 @@ public abstract class MessageToMessageCodec return outboundMsgMatcher.match(msg); } - protected abstract void encode(ChannelHandlerContext ctx, OUTBOUND_IN msg, MessageBuf out) throws Exception; - protected abstract void decode(ChannelHandlerContext ctx, INBOUND_IN msg, MessageBuf out) throws Exception; + protected abstract void encode(ChannelHandlerContext ctx, OUTBOUND_IN msg, MessageList out) + throws Exception; + protected abstract void decode(ChannelHandlerContext ctx, INBOUND_IN msg, MessageList out) + throws Exception; } diff --git a/codec/src/main/java/io/netty/handler/codec/MessageToMessageDecoder.java b/codec/src/main/java/io/netty/handler/codec/MessageToMessageDecoder.java index 91d24eb4da..14b8d8a3ec 100644 --- a/codec/src/main/java/io/netty/handler/codec/MessageToMessageDecoder.java +++ b/codec/src/main/java/io/netty/handler/codec/MessageToMessageDecoder.java @@ -15,13 +15,14 @@ */ package io.netty.handler.codec; -import io.netty.buffer.MessageBuf; +import io.netty.buffer.ByteBufUtil; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandler; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.MessageList; +import io.netty.util.internal.TypeParameterMatcher; /** - * {@link ChannelInboundMessageHandler} which decodes from one message to an other message + * {@link ChannelInboundHandlerAdapter} which decodes from one message to an other message * * For example here is an implementation which decodes a {@link String} to an {@link Integer} which represent * the length of the {@link String}. @@ -32,43 +33,66 @@ import io.netty.channel.ChannelInboundMessageHandlerAdapter; * * {@code @Override} * public void decode({@link ChannelHandlerContext} ctx, {@link String} message, - * {@link MessageBuf} out) throws {@link Exception} { + * {@link MessageList} out) throws {@link Exception} { * out.add(message.length()); * } * } * * */ -public abstract class MessageToMessageDecoder extends ChannelInboundMessageHandlerAdapter { +public abstract class MessageToMessageDecoder extends ChannelInboundHandlerAdapter { - protected MessageToMessageDecoder() { } + private final TypeParameterMatcher matcher; + + protected MessageToMessageDecoder() { + matcher = TypeParameterMatcher.find(this, MessageToMessageDecoder.class, "I"); + } protected MessageToMessageDecoder(Class inboundMessageType) { - super(inboundMessageType); + matcher = TypeParameterMatcher.get(inboundMessageType); + } + + public boolean acceptInboundMessage(Object msg) throws Exception { + return matcher.match(msg); } @Override - public final void messageReceived(ChannelHandlerContext ctx, I msg) throws Exception { - OutputMessageBuf out = OutputMessageBuf.get(); + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + MessageList out = MessageList.newInstance(); try { - decode(ctx, msg, out); - } catch (CodecException e) { + int size = msgs.size(); + for (int i = 0; i < size; i ++) { + Object m = msgs.get(i); + if (acceptInboundMessage(m)) { + @SuppressWarnings("unchecked") + I cast = (I) m; + try { + decode(ctx, cast, out); + } finally { + ByteBufUtil.release(cast); + } + } else { + out.add(m); + } + } + } catch (DecoderException e) { throw e; - } catch (Throwable cause) { - throw new DecoderException(cause); + } catch (Exception e) { + throw new DecoderException(e); } finally { - out.drainToNextInbound(ctx); + msgs.recycle(); + ctx.fireMessageReceived(out); } } /** - * Decode from one message to an other. This method will be called till either the {@link MessageBuf} has + * Decode from one message to an other. This method will be called till either the {@link MessageList} has * nothing left or till this method returns {@code null}. * * @param ctx the {@link ChannelHandlerContext} which this {@link MessageToMessageDecoder} belongs to * @param msg the message to decode to an other one - * @param out the {@link MessageBuf} to which decoded messages should be added + * @param out the {@link MessageList} to which decoded messages should be added * @throws Exception is thrown if an error accour */ - protected abstract void decode(ChannelHandlerContext ctx, I msg, MessageBuf out) throws Exception; + protected abstract void decode(ChannelHandlerContext ctx, I msg, MessageList out) throws Exception; } diff --git a/codec/src/main/java/io/netty/handler/codec/MessageToMessageEncoder.java b/codec/src/main/java/io/netty/handler/codec/MessageToMessageEncoder.java index 4195ba9c3a..f82416d1da 100644 --- a/codec/src/main/java/io/netty/handler/codec/MessageToMessageEncoder.java +++ b/codec/src/main/java/io/netty/handler/codec/MessageToMessageEncoder.java @@ -15,12 +15,15 @@ */ package io.netty.handler.codec; -import io.netty.buffer.MessageBuf; +import io.netty.buffer.ByteBufUtil; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelOutboundMessageHandlerAdapter; +import io.netty.channel.ChannelOutboundHandlerAdapter; +import io.netty.channel.ChannelPromise; +import io.netty.channel.MessageList; +import io.netty.util.internal.TypeParameterMatcher; /** - * {@link ChannelOutboundMessageHandlerAdapter} which encodes from one message to an other message + * {@link ChannelOutboundHandlerAdapter} which encodes from one message to an other message * * For example here is an implementation which decodes an {@link Integer} to an {@link String}. * @@ -29,7 +32,7 @@ import io.netty.channel.ChannelOutboundMessageHandlerAdapter; * {@link MessageToMessageEncoder}<{@link Integer}> { * * {@code @Override} - * public void encode({@link ChannelHandlerContext} ctx, {@link Integer} message, {@link MessageBuf} out) + * public void encode({@link ChannelHandlerContext} ctx, {@link Integer} message, {@link MessageList} out) * throws {@link Exception} { * out.add(message.toString()); * } @@ -37,37 +40,66 @@ import io.netty.channel.ChannelOutboundMessageHandlerAdapter; * * */ -public abstract class MessageToMessageEncoder extends ChannelOutboundMessageHandlerAdapter { +public abstract class MessageToMessageEncoder extends ChannelOutboundHandlerAdapter { - protected MessageToMessageEncoder() { } + private final TypeParameterMatcher matcher; + + protected MessageToMessageEncoder() { + matcher = TypeParameterMatcher.find(this, MessageToMessageEncoder.class, "I"); + } protected MessageToMessageEncoder(Class outboundMessageType) { - super(outboundMessageType); + matcher = TypeParameterMatcher.get(outboundMessageType); + } + + public boolean acceptOutboundMessage(Object msg) throws Exception { + return matcher.match(msg); } @Override - public final void flush(ChannelHandlerContext ctx, I msg) throws Exception { - OutputMessageBuf out = OutputMessageBuf.get(); + public void write(ChannelHandlerContext ctx, MessageList msgs, ChannelPromise promise) throws Exception { + MessageList out = MessageList.newInstance(); + boolean success = false; try { - encode(ctx, msg, out); + int size = msgs.size(); + for (int i = 0; i < size; i ++) { + Object m = msgs.get(i); + if (acceptOutboundMessage(m)) { + @SuppressWarnings("unchecked") + I cast = (I) m; + try { + encode(ctx, cast, out); + } finally { + ByteBufUtil.release(cast); + } + } else { + out.add(m); + } + } + success = true; } catch (CodecException e) { throw e; - } catch (Throwable cause) { - throw new EncoderException(cause); + } catch (Throwable t) { + throw new EncoderException(t); } finally { - out.drainToNextOutbound(ctx); + msgs.recycle(); + if (success) { + ctx.write(out, promise); + } else { + out.releaseAllAndRecycle(); + } } } /** - * Encode from one message to an other. This method will be called till either the {@link MessageBuf} has nothing + * Encode from one message to an other. This method will be called till either the {@link MessageList} has nothing * left or till this method returns {@code null}. * * @param ctx the {@link ChannelHandlerContext} which this {@link MessageToMessageEncoder} belongs to * @param msg the message to encode to an other one - * @param out the {@link MessageBuf} into which the encoded msg should be added + * @param out the {@link MessageList} into which the encoded msg should be added * needs to do some kind of aggragation * @throws Exception is thrown if an error accour */ - protected abstract void encode(ChannelHandlerContext ctx, I msg, MessageBuf out) throws Exception; + protected abstract void encode(ChannelHandlerContext ctx, I msg, MessageList out) throws Exception; } diff --git a/codec/src/main/java/io/netty/handler/codec/OutputMessageBuf.java b/codec/src/main/java/io/netty/handler/codec/OutputMessageBuf.java deleted file mode 100644 index 5491853cbd..0000000000 --- a/codec/src/main/java/io/netty/handler/codec/OutputMessageBuf.java +++ /dev/null @@ -1,177 +0,0 @@ -/* - * Copyright 2013 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; - -import io.netty.buffer.BufType; -import io.netty.buffer.BufUtil; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.DefaultMessageBuf; -import io.netty.buffer.MessageBuf; -import io.netty.channel.ChannelHandlerContext; - -final class OutputMessageBuf extends DefaultMessageBuf { - - private static final ThreadLocal output = - new ThreadLocal() { - @Override - protected OutputMessageBuf initialValue() { - return new OutputMessageBuf(); - } - - @Override - public OutputMessageBuf get() { - OutputMessageBuf buf = super.get(); - assert buf.isEmpty(); - return buf; - } - }; - - static OutputMessageBuf get() { - return output.get(); - } - - private int byteBufCnt; - - private OutputMessageBuf() { - super(2); - } - - @Override - public boolean offer(Object e) { - boolean added = super.offer(e); - if (added) { - if (e instanceof ByteBuf) { - byteBufCnt ++; - } - } - return added; - } - - @Override - public boolean remove(Object o) { - boolean removed = super.remove(o); - if (removed) { - if (o instanceof ByteBuf) { - byteBufCnt --; - } - } - return removed; - } - - @Override - public Object poll() { - Object o = super.poll(); - if (o == null) { - return o; - } - if (o instanceof ByteBuf) { - byteBufCnt --; - } - return o; - } - - @Override - public void clear() { - super.clear(); - byteBufCnt = 0; - } - - public boolean drainToNextInbound(ChannelHandlerContext ctx) { - final int size = size(); - if (size == 0) { - return false; - } - - final int byteBufCnt = this.byteBufCnt; - if (byteBufCnt == 0 || ctx.nextInboundBufferType() != BufType.BYTE) { - return drainTo(ctx.nextInboundMessageBuffer()) > 0; - } - - final ByteBuf nextByteBuf = ctx.nextInboundByteBuffer(); - if (byteBufCnt == size) { - // Contains only ByteBufs - for (Object o = poll();;) { - writeAndRelease(nextByteBuf, (ByteBuf) o); - if ((o = poll()) == null) { - break; - } - } - } else { - // Contains both ByteBufs and non-ByteBufs (0 < byteBufCnt < size()) - final MessageBuf nextMsgBuf = ctx.nextInboundMessageBuffer(); - for (Object o = poll();;) { - if (o instanceof ByteBuf) { - writeAndRelease(nextByteBuf, (ByteBuf) o); - } else { - nextMsgBuf.add(o); - } - - if ((o = poll()) == null) { - break; - } - } - } - - return true; - } - - public boolean drainToNextOutbound(ChannelHandlerContext ctx) { - final int size = size(); - if (size == 0) { - return false; - } - - final int byteBufCnt = this.byteBufCnt; - if (byteBufCnt == 0 || ctx.nextOutboundBufferType() != BufType.BYTE) { - return drainTo(ctx.nextOutboundMessageBuffer()) > 0; - } - - final ByteBuf nextByteBuf = ctx.nextOutboundByteBuffer(); - if (byteBufCnt == size) { - // Contains only ByteBufs - for (Object o = poll();;) { - writeAndRelease(nextByteBuf, (ByteBuf) o); - if ((o = poll()) == null) { - break; - } - } - } else { - // Contains both ByteBufs and non-ByteBufs (0 < byteBufCnt < size()) - final MessageBuf nextMsgBuf = ctx.nextOutboundMessageBuffer(); - for (Object o = poll();;) { - if (o instanceof ByteBuf) { - writeAndRelease(nextByteBuf, (ByteBuf) o); - } else { - nextMsgBuf.add(o); - } - - if ((o = poll()) == null) { - break; - } - } - } - - return true; - } - - private static void writeAndRelease(ByteBuf dst, ByteBuf src) { - try { - dst.writeBytes(src, src.readerIndex(), src.readableBytes()); - } finally { - BufUtil.release(src); - } - } -} diff --git a/codec/src/main/java/io/netty/handler/codec/ReplayingDecoder.java b/codec/src/main/java/io/netty/handler/codec/ReplayingDecoder.java index f35ab7e8b6..f1f4025cb5 100644 --- a/codec/src/main/java/io/netty/handler/codec/ReplayingDecoder.java +++ b/codec/src/main/java/io/netty/handler/codec/ReplayingDecoder.java @@ -19,7 +19,9 @@ import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPipeline; +import io.netty.channel.MessageList; import io.netty.util.Signal; +import io.netty.util.internal.StringUtil; /** * A specialized variation of {@link ByteToMessageDecoder} which enables implementation @@ -30,7 +32,7 @@ import io.netty.util.Signal; * implement the {@code decode()} and {@code decodeLast()} methods just like * all required bytes were received already, rather than checking the * availability of the required bytes. For example, the following - * {@link ByteToByteDecoder} implementation: + * {@link ByteToMessageDecoder} implementation: *
  * public class IntegerHeaderFrameDecoder extends {@link ByteToMessageDecoder}<{@link ByteBuf}> {
  *
@@ -226,7 +228,7 @@ import io.netty.util.Signal;
  * 

* If you are going to write a protocol multiplexer, you will probably want to * replace a {@link ReplayingDecoder} (protocol detector) with another - * {@link ReplayingDecoder}, {@link ByteToByteDecoder}, {@link ByteToMessageDecoder} or {@link MessageToMessageDecoder} + * {@link ReplayingDecoder}, {@link ByteToMessageDecoder} or {@link MessageToMessageDecoder} * (actual protocol decoder). * It is not possible to achieve this simply by calling * {@link ChannelPipeline#replace(ChannelHandler, String, ChannelHandler)}, but @@ -264,11 +266,9 @@ public abstract class ReplayingDecoder extends ByteToMessageDecoder { static final Signal REPLAY = new Signal(ReplayingDecoder.class.getName() + ".REPLAY"); - private ChannelHandlerContext ctx; private final ReplayingDecoderBuffer replayable = new ReplayingDecoderBuffer(); private S state; private int checkpoint = -1; - private boolean decodeWasNull; /** * Creates a new instance with no initial state (i.e: {@code null}). @@ -318,89 +318,47 @@ public abstract class ReplayingDecoder extends ByteToMessageDecoder { return oldState; } - /** - * Returns the actual number of readable bytes in the internal cumulative - * buffer of this decoder. You usually do not need to rely on this value - * to write a decoder. Use it only when you muse use it at your own risk. - * This method is a shortcut to {@link #internalBuffer() internalBuffer().readableBytes()}. - */ - protected int actualReadableBytes() { - return internalBuffer().readableBytes(); - } - - /** - * Returns the internal cumulative buffer of this decoder. You usually - * do not need to access the internal buffer directly to write a decoder. - * Use it only when you must use it at your own risk. - */ - protected ByteBuf internalBuffer() { - return ctx.inboundByteBuffer(); - } - - @Override - public void handlerAdded(ChannelHandlerContext ctx) throws Exception { - this.ctx = ctx; - } - - @Override - public final void discardInboundReadBytes(ChannelHandlerContext ctx) throws Exception { - ByteBuf in = ctx.inboundByteBuffer(); - final int oldReaderIndex = in.readerIndex(); - discardInboundReadBytes0(ctx); - final int newReaderIndex = in.readerIndex(); - checkpoint -= oldReaderIndex - newReaderIndex; - } - - protected void discardInboundReadBytes0(ChannelHandlerContext ctx) throws Exception { - super.discardInboundReadBytes(ctx); - } - @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { - OutputMessageBuf out = OutputMessageBuf.get(); + MessageList out = MessageList.newInstance(); try { replayable.terminate(); - ByteBuf in = internalBuffer(); - replayable.setCumulation(in); - if (in.isReadable()) { - callDecode(ctx, in); - } - + callDecode(ctx, internalBuffer(), out); decodeLast(ctx, replayable, out); } catch (Signal replay) { // Ignore replay.expect(REPLAY); - } catch (CodecException e) { + } catch (DecoderException e) { throw e; - } catch (Throwable cause) { - throw new DecoderException(cause); + } catch (Exception e) { + throw new DecoderException(e); } finally { - if (out.drainToNextInbound(ctx)) { - ctx.fireInboundBufferUpdated(); + if (cumulation != null) { + cumulation.release(); + cumulation = null; } + ctx.fireMessageReceived(out); ctx.fireChannelInactive(); } } @Override - protected void callDecode(ChannelHandlerContext ctx, ByteBuf buf) { - boolean wasNull = false; - ByteBuf in = internalBuffer(); + protected void callDecode(ChannelHandlerContext ctx, ByteBuf in, MessageList out) { replayable.setCumulation(in); - OutputMessageBuf out = OutputMessageBuf.get(); try { while (in.isReadable()) { int oldReaderIndex = checkpoint = in.readerIndex(); int outSize = out.size(); S oldState = state; + int oldInputLength = in.readableBytes(); try { decode(ctx, replayable, out); if (outSize == out.size()) { - wasNull = true; - if (oldReaderIndex == in.readerIndex() && oldState == state) { - throw new IllegalStateException( - "null cannot be returned if no data is consumed and state didn't change."); + if (oldInputLength == in.readableBytes() && oldState == state) { + throw new DecoderException( + StringUtil.simpleClassName(getClass()) + ".decode() must consume the inbound " + + "data or change its state if it did not decode anything."); } else { // Previous data has been discarded or caused state transition. // Probably it is reading on. @@ -419,43 +377,20 @@ public abstract class ReplayingDecoder extends ByteToMessageDecoder { } break; } - wasNull = false; if (oldReaderIndex == in.readerIndex() && oldState == state) { - throw new IllegalStateException( - "decode() method must consume at least one byte " + - "if it returned a decoded message (caused by: " + - getClass() + ')'); + throw new DecoderException( + StringUtil.simpleClassName(getClass()) + ".decode() method must consume the inbound data " + + "or change its state if it decoded something."); } if (isSingleDecode()) { break; } } - } catch (CodecException e) { + } catch (DecoderException e) { throw e; } catch (Throwable cause) { throw new DecoderException(cause); - } finally { - if (out.drainToNextInbound(ctx)) { - decodeWasNull = false; - ctx.fireInboundBufferUpdated(); - } else { - if (wasNull) { - decodeWasNull = true; - } - } } } - - @Override - public void channelReadSuspended(ChannelHandlerContext ctx) throws Exception { - if (decodeWasNull) { - decodeWasNull = false; - if (!ctx.channel().config().isAutoRead()) { - ctx.read(); - } - } - - super.channelReadSuspended(ctx); - } } diff --git a/codec/src/main/java/io/netty/handler/codec/ReplayingDecoderBuffer.java b/codec/src/main/java/io/netty/handler/codec/ReplayingDecoderBuffer.java index edfa277d8a..60d17fbfdb 100644 --- a/codec/src/main/java/io/netty/handler/codec/ReplayingDecoderBuffer.java +++ b/codec/src/main/java/io/netty/handler/codec/ReplayingDecoderBuffer.java @@ -15,7 +15,6 @@ */ package io.netty.handler.codec; -import io.netty.buffer.BufType; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.ByteBufIndexFinder; @@ -81,11 +80,6 @@ final class ReplayingDecoderBuffer implements ByteBuf { return capacity(); } - @Override - public BufType type() { - return BufType.BYTE; - } - @Override public ByteBufAllocator alloc() { return buffer.alloc(); @@ -925,16 +919,6 @@ final class ReplayingDecoderBuffer implements ByteBuf { throw new UnreplayableOperationException(); } - @Override - public ByteBuf suspendIntermediaryDeallocations() { - throw new UnreplayableOperationException(); - } - - @Override - public ByteBuf resumeIntermediaryDeallocations() { - throw new UnreplayableOperationException(); - } - @Override public ByteBuf unwrap() { throw new UnreplayableOperationException(); diff --git a/codec/src/main/java/io/netty/handler/codec/base64/Base64Decoder.java b/codec/src/main/java/io/netty/handler/codec/base64/Base64Decoder.java index ff1eb4458d..f8b13edaa9 100644 --- a/codec/src/main/java/io/netty/handler/codec/base64/Base64Decoder.java +++ b/codec/src/main/java/io/netty/handler/codec/base64/Base64Decoder.java @@ -16,10 +16,10 @@ package io.netty.handler.codec.base64; import io.netty.buffer.ByteBuf; -import io.netty.buffer.MessageBuf; import io.netty.channel.ChannelHandler.Sharable; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPipeline; +import io.netty.channel.MessageList; import io.netty.handler.codec.ByteToMessageDecoder; import io.netty.handler.codec.DelimiterBasedFrameDecoder; import io.netty.handler.codec.Delimiters; @@ -59,7 +59,7 @@ public class Base64Decoder extends MessageToMessageDecoder { } @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf msg, MessageBuf out) throws Exception { + protected void decode(ChannelHandlerContext ctx, ByteBuf msg, MessageList out) throws Exception { out.add(Base64.decode(msg, msg.readerIndex(), msg.readableBytes(), dialect)); } } diff --git a/codec/src/main/java/io/netty/handler/codec/base64/Base64Encoder.java b/codec/src/main/java/io/netty/handler/codec/base64/Base64Encoder.java index b79b0fbe04..5af5b71065 100644 --- a/codec/src/main/java/io/netty/handler/codec/base64/Base64Encoder.java +++ b/codec/src/main/java/io/netty/handler/codec/base64/Base64Encoder.java @@ -18,11 +18,11 @@ package io.netty.handler.codec.base64; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandler.Sharable; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelHandlerUtil; -import io.netty.channel.ChannelOutboundMessageHandlerAdapter; import io.netty.channel.ChannelPipeline; +import io.netty.channel.MessageList; import io.netty.handler.codec.DelimiterBasedFrameDecoder; import io.netty.handler.codec.Delimiters; +import io.netty.handler.codec.MessageToMessageEncoder; /** * Encodes a {@link ByteBuf} into a Base64-encoded {@link ByteBuf}. @@ -39,7 +39,7 @@ import io.netty.handler.codec.Delimiters; * */ @Sharable -public class Base64Encoder extends ChannelOutboundMessageHandlerAdapter { +public class Base64Encoder extends MessageToMessageEncoder { private final boolean breakLines; private final Base64Dialect dialect; @@ -62,9 +62,7 @@ public class Base64Encoder extends ChannelOutboundMessageHandlerAdapter } @Override - public void flush(ChannelHandlerContext ctx, - ByteBuf msg) throws Exception { - ByteBuf buf = Base64.encode(msg, msg.readerIndex(), msg.readableBytes(), breakLines, dialect); - ChannelHandlerUtil.addToNextOutboundBuffer(ctx, buf); + protected void encode(ChannelHandlerContext ctx, ByteBuf msg, MessageList out) throws Exception { + out.add(Base64.encode(msg, msg.readerIndex(), msg.readableBytes(), breakLines, dialect)); } } diff --git a/codec/src/main/java/io/netty/handler/codec/bytes/ByteArrayDecoder.java b/codec/src/main/java/io/netty/handler/codec/bytes/ByteArrayDecoder.java index 1d0f8acf9e..a1d15b6d2f 100644 --- a/codec/src/main/java/io/netty/handler/codec/bytes/ByteArrayDecoder.java +++ b/codec/src/main/java/io/netty/handler/codec/bytes/ByteArrayDecoder.java @@ -16,9 +16,9 @@ package io.netty.handler.codec.bytes; import io.netty.buffer.ByteBuf; -import io.netty.buffer.MessageBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPipeline; +import io.netty.channel.MessageList; import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import io.netty.handler.codec.LengthFieldPrepender; import io.netty.handler.codec.MessageToMessageDecoder; @@ -48,9 +48,8 @@ import io.netty.handler.codec.MessageToMessageDecoder; * */ public class ByteArrayDecoder extends MessageToMessageDecoder { - @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf msg, MessageBuf out) throws Exception { + protected void decode(ChannelHandlerContext ctx, ByteBuf msg, MessageList out) throws Exception { byte[] array; if (msg.hasArray()) { if (msg.arrayOffset() == 0 && msg.readableBytes() == msg.capacity()) { diff --git a/codec/src/main/java/io/netty/handler/codec/bytes/ByteArrayEncoder.java b/codec/src/main/java/io/netty/handler/codec/bytes/ByteArrayEncoder.java index 9a0d8bf206..1014084e63 100644 --- a/codec/src/main/java/io/netty/handler/codec/bytes/ByteArrayEncoder.java +++ b/codec/src/main/java/io/netty/handler/codec/bytes/ByteArrayEncoder.java @@ -19,10 +19,11 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandler.Sharable; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelOutboundMessageHandlerAdapter; import io.netty.channel.ChannelPipeline; +import io.netty.channel.MessageList; import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import io.netty.handler.codec.LengthFieldPrepender; +import io.netty.handler.codec.MessageToMessageEncoder; /** * Encodes the requested array of bytes into a {@link ByteBuf}. @@ -49,23 +50,9 @@ import io.netty.handler.codec.LengthFieldPrepender; * */ @Sharable -public class ByteArrayEncoder extends ChannelOutboundMessageHandlerAdapter { - +public class ByteArrayEncoder extends MessageToMessageEncoder { @Override - public void flush(ChannelHandlerContext ctx, byte[] msg) throws Exception { - if (msg.length == 0) { - return; - } - - switch (ctx.nextOutboundBufferType()) { - case BYTE: - ctx.nextOutboundByteBuffer().writeBytes(msg); - break; - case MESSAGE: - ctx.nextOutboundMessageBuffer().add(Unpooled.wrappedBuffer(msg)); - break; - default: - throw new Error(); - } + protected void encode(ChannelHandlerContext ctx, byte[] msg, MessageList out) throws Exception { + out.add(Unpooled.wrappedBuffer(msg)); } } diff --git a/codec/src/main/java/io/netty/handler/codec/compression/CompressionException.java b/codec/src/main/java/io/netty/handler/codec/compression/CompressionException.java index 89f17b4198..167e45dd7d 100644 --- a/codec/src/main/java/io/netty/handler/codec/compression/CompressionException.java +++ b/codec/src/main/java/io/netty/handler/codec/compression/CompressionException.java @@ -15,13 +15,12 @@ */ package io.netty.handler.codec.compression; -import io.netty.handler.codec.CodecException; +import io.netty.handler.codec.EncoderException; /** - * An {@link CodecException} that is raised when compression or decompression - * failed. + * An {@link EncoderException} that is raised when compression failed. */ -public class CompressionException extends CodecException { +public class CompressionException extends EncoderException { private static final long serialVersionUID = 5603413481274811897L; diff --git a/codec/src/main/java/io/netty/handler/codec/compression/DecompressionException.java b/codec/src/main/java/io/netty/handler/codec/compression/DecompressionException.java new file mode 100644 index 0000000000..8fc016bf8d --- /dev/null +++ b/codec/src/main/java/io/netty/handler/codec/compression/DecompressionException.java @@ -0,0 +1,53 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.handler.codec.compression; + +import io.netty.handler.codec.DecoderException; + +/** + * A {@link DecoderException} that is raised when decompression failed. + */ +public class DecompressionException extends DecoderException { + + private static final long serialVersionUID = 3546272712208105199L; + + /** + * Creates a new instance. + */ + public DecompressionException() { + } + + /** + * Creates a new instance. + */ + public DecompressionException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Creates a new instance. + */ + public DecompressionException(String message) { + super(message); + } + + /** + * Creates a new instance. + */ + public DecompressionException(Throwable cause) { + super(cause); + } +} diff --git a/codec/src/main/java/io/netty/handler/codec/compression/JZlibDecoder.java b/codec/src/main/java/io/netty/handler/codec/compression/JZlibDecoder.java index a4dfe4a0a9..ca9b39dd98 100644 --- a/codec/src/main/java/io/netty/handler/codec/compression/JZlibDecoder.java +++ b/codec/src/main/java/io/netty/handler/codec/compression/JZlibDecoder.java @@ -15,10 +15,11 @@ */ package io.netty.handler.codec.compression; +import com.jcraft.jzlib.Inflater; +import com.jcraft.jzlib.JZlib; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; -import com.jcraft.jzlib.JZlib; -import com.jcraft.jzlib.Inflater; +import io.netty.channel.MessageList; public class JZlibDecoder extends ZlibDecoder { @@ -81,9 +82,7 @@ public class JZlibDecoder extends ZlibDecoder { } @Override - protected void decode( - ChannelHandlerContext ctx, - ByteBuf in, ByteBuf out) throws Exception { + protected void decode(ChannelHandlerContext ctx, ByteBuf in, MessageList out) throws Exception { if (!in.isReadable()) { return; @@ -92,14 +91,13 @@ public class JZlibDecoder extends ZlibDecoder { try { // Configure input. int inputLength = in.readableBytes(); - boolean inHasArray = in.hasArray(); z.avail_in = inputLength; - if (inHasArray) { + if (in.hasArray()) { z.next_in = in.array(); z.next_in_index = in.arrayOffset() + in.readerIndex(); } else { byte[] array = new byte[inputLength]; - in.readBytes(array); + in.getBytes(in.readerIndex(), array); z.next_in = array; z.next_in_index = 0; } @@ -107,32 +105,21 @@ public class JZlibDecoder extends ZlibDecoder { // Configure output. int maxOutputLength = inputLength << 1; - boolean outHasArray = out.hasArray(); - if (!outHasArray) { - z.next_out = new byte[maxOutputLength]; - } + ByteBuf decompressed = ctx.alloc().heapBuffer(maxOutputLength); try { loop: for (;;) { z.avail_out = maxOutputLength; - if (outHasArray) { - out.ensureWritable(maxOutputLength); - z.next_out = out.array(); - z.next_out_index = out.arrayOffset() + out.writerIndex(); - } else { - z.next_out_index = 0; - } + decompressed.ensureWritable(maxOutputLength); + z.next_out = decompressed.array(); + z.next_out_index = decompressed.arrayOffset() + decompressed.writerIndex(); int oldNextOutIndex = z.next_out_index; // Decompress 'in' into 'out' int resultCode = z.inflate(JZlib.Z_SYNC_FLUSH); int outputLength = z.next_out_index - oldNextOutIndex; if (outputLength > 0) { - if (outHasArray) { - out.writerIndex(out.writerIndex() + outputLength); - } else { - out.writeBytes(z.next_out, 0, outputLength); - } + decompressed.writerIndex(decompressed.writerIndex() + outputLength); } switch (resultCode) { @@ -162,8 +149,11 @@ public class JZlibDecoder extends ZlibDecoder { } } } finally { - if (inHasArray) { - in.skipBytes(z.next_in_index - oldNextInIndex); + in.skipBytes(z.next_in_index - oldNextInIndex); + if (decompressed.isReadable()) { + out.add(decompressed); + } else { + decompressed.release(); } } } finally { diff --git a/codec/src/main/java/io/netty/handler/codec/compression/JZlibEncoder.java b/codec/src/main/java/io/netty/handler/codec/compression/JZlibEncoder.java index 9a4d57a8a0..2a94ab2ef8 100644 --- a/codec/src/main/java/io/netty/handler/codec/compression/JZlibEncoder.java +++ b/codec/src/main/java/io/netty/handler/codec/compression/JZlibEncoder.java @@ -280,7 +280,7 @@ public class JZlibEncoder extends ZlibEncoder { z.next_in_index = in.arrayOffset() + in.readerIndex(); } else { byte[] array = new byte[inputLength]; - in.readBytes(array); + in.getBytes(in.readerIndex(), array); z.next_in = array; z.next_in_index = 0; } @@ -288,16 +288,10 @@ public class JZlibEncoder extends ZlibEncoder { // Configure output. int maxOutputLength = (int) Math.ceil(inputLength * 1.001) + 12; - boolean outHasArray = out.hasArray(); + ByteBuf compressed = ctx.alloc().heapBuffer(maxOutputLength); z.avail_out = maxOutputLength; - if (outHasArray) { - out.ensureWritable(maxOutputLength); - z.next_out = out.array(); - z.next_out_index = out.arrayOffset() + out.writerIndex(); - } else { - z.next_out = new byte[maxOutputLength]; - z.next_out_index = 0; - } + z.next_out = out.array(); + z.next_out_index = out.arrayOffset() + out.writerIndex(); int oldNextOutIndex = z.next_out_index; // Note that Z_PARTIAL_FLUSH has been deprecated. @@ -305,9 +299,7 @@ public class JZlibEncoder extends ZlibEncoder { try { resultCode = z.deflate(JZlib.Z_SYNC_FLUSH); } finally { - if (inHasArray) { - in.skipBytes(z.next_in_index - oldNextInIndex); - } + in.skipBytes(z.next_in_index - oldNextInIndex); } if (resultCode != JZlib.Z_OK) { @@ -316,11 +308,7 @@ public class JZlibEncoder extends ZlibEncoder { int outputLength = z.next_out_index - oldNextOutIndex; if (outputLength > 0) { - if (outHasArray) { - out.writerIndex(out.writerIndex() + outputLength); - } else { - out.writeBytes(z.next_out, 0, outputLength); - } + out.writerIndex(out.writerIndex() + outputLength); } } finally { // Deference the external references explicitly to tell the VM that diff --git a/codec/src/main/java/io/netty/handler/codec/compression/JdkZlibEncoder.java b/codec/src/main/java/io/netty/handler/codec/compression/JdkZlibEncoder.java index 638b9ff16f..e225e731e3 100644 --- a/codec/src/main/java/io/netty/handler/codec/compression/JdkZlibEncoder.java +++ b/codec/src/main/java/io/netty/handler/codec/compression/JdkZlibEncoder.java @@ -255,10 +255,7 @@ public class JdkZlibEncoder extends ZlibEncoder { deflater.end(); } - ctx.nextOutboundByteBuffer().writeBytes(footer); - ctx.flush(promise); - - return promise; + return ctx.write(footer, promise); } @Override diff --git a/codec/src/main/java/io/netty/handler/codec/compression/Snappy.java b/codec/src/main/java/io/netty/handler/codec/compression/Snappy.java index 4fd273d81e..3219f0b3ce 100644 --- a/codec/src/main/java/io/netty/handler/codec/compression/Snappy.java +++ b/codec/src/main/java/io/netty/handler/codec/compression/Snappy.java @@ -15,8 +15,8 @@ */ package io.netty.handler.codec.compression; -import io.netty.buffer.BufUtil; import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufUtil; import java.util.zip.CRC32; @@ -381,7 +381,7 @@ public class Snappy { } if (byteIndex >= 4) { - throw new CompressionException("Preamble is greater than 4 bytes"); + throw new DecompressionException("Preamble is greater than 4 bytes"); } } @@ -413,19 +413,19 @@ public class Snappy { if (in.readableBytes() < 2) { return NOT_ENOUGH_INPUT; } - length = BufUtil.swapShort(in.readShort()); + length = ByteBufUtil.swapShort(in.readShort()); break; case 62: if (in.readableBytes() < 3) { return NOT_ENOUGH_INPUT; } - length = BufUtil.swapMedium(in.readUnsignedMedium()); + length = ByteBufUtil.swapMedium(in.readUnsignedMedium()); break; case 64: if (in.readableBytes() < 4) { return NOT_ENOUGH_INPUT; } - length = BufUtil.swapInt(in.readInt()); + length = ByteBufUtil.swapInt(in.readInt()); break; default: length = tag >> 2 & 0x3F; @@ -452,7 +452,7 @@ public class Snappy { * @param out The output buffer to write to * @return The number of bytes appended to the output buffer, or -1 to indicate * "try again later" - * @throws CompressionException If the read offset is invalid + * @throws DecompressionException If the read offset is invalid */ private static int decodeCopyWith1ByteOffset(byte tag, ByteBuf in, ByteBuf out, int writtenSoFar) { if (!in.isReadable()) { @@ -494,7 +494,7 @@ public class Snappy { * the length and part of the offset * @param in The input buffer to read from * @param out The output buffer to write to - * @throws CompressionException If the read offset is invalid + * @throws DecompressionException If the read offset is invalid * @return The number of bytes appended to the output buffer, or -1 to indicate * "try again later" */ @@ -505,7 +505,7 @@ public class Snappy { int initialIndex = out.writerIndex(); int length = 1 + (tag >> 2 & 0x03f); - int offset = BufUtil.swapShort(in.readShort()); + int offset = ByteBufUtil.swapShort(in.readShort()); validateOffset(offset, writtenSoFar); @@ -540,7 +540,7 @@ public class Snappy { * @param out The output buffer to write to * @return The number of bytes appended to the output buffer, or -1 to indicate * "try again later" - * @throws CompressionException If the read offset is invalid + * @throws DecompressionException If the read offset is invalid */ private static int decodeCopyWith4ByteOffset(byte tag, ByteBuf in, ByteBuf out, int writtenSoFar) { if (in.readableBytes() < 4) { @@ -549,7 +549,7 @@ public class Snappy { int initialIndex = out.writerIndex(); int length = 1 + (tag >> 2 & 0x03F); - int offset = BufUtil.swapInt(in.readInt()); + int offset = ByteBufUtil.swapInt(in.readInt()); validateOffset(offset, writtenSoFar); @@ -580,19 +580,19 @@ public class Snappy { * * @param offset The offset extracted from the compressed reference * @param chunkSizeSoFar The number of bytes read so far from this chunk - * @throws CompressionException if the offset is invalid + * @throws DecompressionException if the offset is invalid */ private static void validateOffset(int offset, int chunkSizeSoFar) { if (offset > Short.MAX_VALUE) { - throw new CompressionException("Offset exceeds maximum permissible value"); + throw new DecompressionException("Offset exceeds maximum permissible value"); } if (offset <= 0) { - throw new CompressionException("Offset is less than minimum permissible value"); + throw new DecompressionException("Offset is less than minimum permissible value"); } if (offset > chunkSizeSoFar) { - throw new CompressionException("Offset exceeds size of chunk"); + throw new DecompressionException("Offset exceeds size of chunk"); } } @@ -636,7 +636,7 @@ public class Snappy { * * @param expectedChecksum The checksum decoded from the stream to compare against * @param data The input data to calculate the CRC32 checksum of - * @throws CompressionException If the calculated and supplied checksums do not match + * @throws DecompressionException If the calculated and supplied checksums do not match */ static void validateChecksum(int expectedChecksum, ByteBuf data) { validateChecksum(expectedChecksum, data, data.readerIndex(), data.readableBytes()); @@ -649,12 +649,12 @@ public class Snappy { * * @param expectedChecksum The checksum decoded from the stream to compare against * @param data The input data to calculate the CRC32 checksum of - * @throws CompressionException If the calculated and supplied checksums do not match + * @throws DecompressionException If the calculated and supplied checksums do not match */ static void validateChecksum(int expectedChecksum, ByteBuf data, int offset, int length) { final int actualChecksum = calculateChecksum(data, offset, length); if (actualChecksum != expectedChecksum) { - throw new CompressionException( + throw new DecompressionException( "mismatching checksum: " + Integer.toHexString(actualChecksum) + " (expected: " + Integer.toHexString(expectedChecksum) + ')'); } diff --git a/codec/src/main/java/io/netty/handler/codec/compression/SnappyFramedDecoder.java b/codec/src/main/java/io/netty/handler/codec/compression/SnappyFramedDecoder.java index d10c5b7999..c0fa5eae80 100644 --- a/codec/src/main/java/io/netty/handler/codec/compression/SnappyFramedDecoder.java +++ b/codec/src/main/java/io/netty/handler/codec/compression/SnappyFramedDecoder.java @@ -15,10 +15,11 @@ */ package io.netty.handler.codec.compression; -import io.netty.buffer.BufUtil; import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufUtil; import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.ByteToByteDecoder; +import io.netty.channel.MessageList; +import io.netty.handler.codec.ByteToMessageDecoder; import java.util.Arrays; @@ -35,7 +36,7 @@ import static io.netty.handler.codec.compression.Snappy.*; * use the {@link #SnappyFramedDecoder(boolean)} constructor with the argument * set to {@code true}. */ -public class SnappyFramedDecoder extends ByteToByteDecoder { +public class SnappyFramedDecoder extends ByteToMessageDecoder { enum ChunkType { STREAM_IDENTIFIER, COMPRESSED_DATA, @@ -68,14 +69,14 @@ public class SnappyFramedDecoder extends ByteToByteDecoder { * @param validateChecksums * If true, the checksum field will be validated against the actual * uncompressed data, and if the checksums do not match, a suitable - * {@link CompressionException} will be thrown + * {@link DecompressionException} will be thrown */ public SnappyFramedDecoder(boolean validateChecksums) { this.validateChecksums = validateChecksums; } @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in, ByteBuf out) throws Exception { + protected void decode(ChannelHandlerContext ctx, ByteBuf in, MessageList out) throws Exception { if (corrupted) { in.skipBytes(in.readableBytes()); return; @@ -92,12 +93,12 @@ public class SnappyFramedDecoder extends ByteToByteDecoder { final int chunkTypeVal = in.getUnsignedByte(idx); final ChunkType chunkType = mapChunkType((byte) chunkTypeVal); - final int chunkLength = BufUtil.swapMedium(in.getUnsignedMedium(idx + 1)); + final int chunkLength = ByteBufUtil.swapMedium(in.getUnsignedMedium(idx + 1)); switch (chunkType) { case STREAM_IDENTIFIER: if (chunkLength != SNAPPY.length) { - throw new CompressionException("Unexpected length of stream identifier: " + chunkLength); + throw new DecompressionException("Unexpected length of stream identifier: " + chunkLength); } if (inSize < 4 + SNAPPY.length) { @@ -108,7 +109,7 @@ public class SnappyFramedDecoder extends ByteToByteDecoder { in.skipBytes(4).readBytes(identifier); if (!Arrays.equals(identifier, SNAPPY)) { - throw new CompressionException("Unexpected stream identifier contents. Mismatched snappy " + + throw new DecompressionException("Unexpected stream identifier contents. Mismatched snappy " + "protocol version?"); } @@ -116,7 +117,7 @@ public class SnappyFramedDecoder extends ByteToByteDecoder { break; case RESERVED_SKIPPABLE: if (!started) { - throw new CompressionException("Received RESERVED_SKIPPABLE tag before STREAM_IDENTIFIER"); + throw new DecompressionException("Received RESERVED_SKIPPABLE tag before STREAM_IDENTIFIER"); } if (inSize < 4 + chunkLength) { @@ -130,14 +131,14 @@ public class SnappyFramedDecoder extends ByteToByteDecoder { // The spec mandates that reserved unskippable chunks must immediately // return an error, as we must assume that we cannot decode the stream // correctly - throw new CompressionException( + throw new DecompressionException( "Found reserved unskippable chunk type: 0x" + Integer.toHexString(chunkTypeVal)); case UNCOMPRESSED_DATA: if (!started) { - throw new CompressionException("Received UNCOMPRESSED_DATA tag before STREAM_IDENTIFIER"); + throw new DecompressionException("Received UNCOMPRESSED_DATA tag before STREAM_IDENTIFIER"); } if (chunkLength > 65536 + 4) { - throw new CompressionException("Received UNCOMPRESSED_DATA larger than 65540 bytes"); + throw new DecompressionException("Received UNCOMPRESSED_DATA larger than 65540 bytes"); } if (inSize < 4 + chunkLength) { @@ -146,16 +147,16 @@ public class SnappyFramedDecoder extends ByteToByteDecoder { in.skipBytes(4); if (validateChecksums) { - int checksum = BufUtil.swapInt(in.readInt()); + int checksum = ByteBufUtil.swapInt(in.readInt()); validateChecksum(checksum, in, in.readerIndex(), chunkLength - 4); } else { in.skipBytes(4); } - out.writeBytes(in, chunkLength - 4); + out.add(in.readSlice(chunkLength - 4).retain()); break; case COMPRESSED_DATA: if (!started) { - throw new CompressionException("Received COMPRESSED_DATA tag before STREAM_IDENTIFIER"); + throw new DecompressionException("Received COMPRESSED_DATA tag before STREAM_IDENTIFIER"); } if (inSize < 4 + chunkLength) { @@ -163,21 +164,21 @@ public class SnappyFramedDecoder extends ByteToByteDecoder { } in.skipBytes(4); - int checksum = BufUtil.swapInt(in.readInt()); + int checksum = ByteBufUtil.swapInt(in.readInt()); + ByteBuf uncompressed = ctx.alloc().buffer(0); if (validateChecksums) { int oldWriterIndex = in.writerIndex(); - int uncompressedStart = out.writerIndex(); try { in.writerIndex(in.readerIndex() + chunkLength - 4); - snappy.decode(in, out); + snappy.decode(in, uncompressed); } finally { in.writerIndex(oldWriterIndex); } - int uncompressedLength = out.writerIndex() - uncompressedStart; - validateChecksum(checksum, out, uncompressedStart, uncompressedLength); + validateChecksum(checksum, uncompressed, 0, uncompressed.writerIndex()); } else { - snappy.decode(in.readSlice(chunkLength - 4), out); + snappy.decode(in.readSlice(chunkLength - 4), uncompressed); } + out.add(uncompressed); snappy.reset(); break; } diff --git a/codec/src/main/java/io/netty/handler/codec/compression/SnappyFramedEncoder.java b/codec/src/main/java/io/netty/handler/codec/compression/SnappyFramedEncoder.java index b7f22a0900..b22a026c2c 100644 --- a/codec/src/main/java/io/netty/handler/codec/compression/SnappyFramedEncoder.java +++ b/codec/src/main/java/io/netty/handler/codec/compression/SnappyFramedEncoder.java @@ -15,10 +15,10 @@ */ package io.netty.handler.codec.compression; -import io.netty.buffer.BufUtil; import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufUtil; import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.ByteToByteEncoder; +import io.netty.handler.codec.MessageToByteEncoder; import static io.netty.handler.codec.compression.Snappy.*; @@ -27,7 +27,7 @@ import static io.netty.handler.codec.compression.Snappy.*; * * See http://code.google.com/p/snappy/source/browse/trunk/framing_format.txt */ -public class SnappyFramedEncoder extends ByteToByteEncoder { +public class SnappyFramedEncoder extends MessageToByteEncoder { /** * The minimum amount that we'll consider actually attempting to compress. * This value is preamble + the minimum length our Snappy service will @@ -99,7 +99,7 @@ public class SnappyFramedEncoder extends ByteToByteEncoder { if (chunkLength >>> 24 != 0) { throw new CompressionException("compressed data too large: " + chunkLength); } - out.setMedium(lengthIdx, BufUtil.swapMedium(chunkLength)); + out.setMedium(lengthIdx, ByteBufUtil.swapMedium(chunkLength)); } /** @@ -109,7 +109,7 @@ public class SnappyFramedEncoder extends ByteToByteEncoder { * @param chunkLength The length to write */ private static void writeChunkLength(ByteBuf out, int chunkLength) { - out.writeMedium(BufUtil.swapMedium(chunkLength)); + out.writeMedium(ByteBufUtil.swapMedium(chunkLength)); } /** @@ -119,6 +119,6 @@ public class SnappyFramedEncoder extends ByteToByteEncoder { * @param out The output buffer to write the checksum to */ private static void calculateAndWriteChecksum(ByteBuf slice, ByteBuf out) { - out.writeInt(BufUtil.swapInt(calculateChecksum(slice))); + out.writeInt(ByteBufUtil.swapInt(calculateChecksum(slice))); } } diff --git a/codec/src/main/java/io/netty/handler/codec/compression/ZlibDecoder.java b/codec/src/main/java/io/netty/handler/codec/compression/ZlibDecoder.java index 550de06609..d01bc6b4de 100644 --- a/codec/src/main/java/io/netty/handler/codec/compression/ZlibDecoder.java +++ b/codec/src/main/java/io/netty/handler/codec/compression/ZlibDecoder.java @@ -16,12 +16,12 @@ package io.netty.handler.codec.compression; import io.netty.buffer.ByteBuf; -import io.netty.handler.codec.ByteToByteDecoder; +import io.netty.handler.codec.ByteToMessageDecoder; /** * Decompresses a {@link ByteBuf} using the deflate algorithm. */ -public abstract class ZlibDecoder extends ByteToByteDecoder { +public abstract class ZlibDecoder extends ByteToMessageDecoder { /** * Returns {@code true} if and only if the end of the compressed stream diff --git a/codec/src/main/java/io/netty/handler/codec/compression/ZlibEncoder.java b/codec/src/main/java/io/netty/handler/codec/compression/ZlibEncoder.java index 90c1b7ff45..c45d949658 100644 --- a/codec/src/main/java/io/netty/handler/codec/compression/ZlibEncoder.java +++ b/codec/src/main/java/io/netty/handler/codec/compression/ZlibEncoder.java @@ -18,12 +18,12 @@ package io.netty.handler.codec.compression; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelPromise; -import io.netty.handler.codec.ByteToByteEncoder; +import io.netty.handler.codec.MessageToByteEncoder; /** * Compresses a {@link ByteBuf} using the deflate algorithm. */ -public abstract class ZlibEncoder extends ByteToByteEncoder { +public abstract class ZlibEncoder extends MessageToByteEncoder { /** * Returns {@code true} if and only if the end of the compressed stream diff --git a/codec/src/main/java/io/netty/handler/codec/marshalling/CompatibleMarshallingDecoder.java b/codec/src/main/java/io/netty/handler/codec/marshalling/CompatibleMarshallingDecoder.java index 7af3ebd75e..1551203d64 100644 --- a/codec/src/main/java/io/netty/handler/codec/marshalling/CompatibleMarshallingDecoder.java +++ b/codec/src/main/java/io/netty/handler/codec/marshalling/CompatibleMarshallingDecoder.java @@ -16,9 +16,9 @@ package io.netty.handler.codec.marshalling; import io.netty.buffer.ByteBuf; -import io.netty.buffer.MessageBuf; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.MessageList; import io.netty.handler.codec.ReplayingDecoder; import io.netty.handler.codec.TooLongFrameException; import org.jboss.marshalling.ByteInput; @@ -55,7 +55,7 @@ public class CompatibleMarshallingDecoder extends ReplayingDecoder { } @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, MessageBuf out) throws Exception { + protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, MessageList out) throws Exception { if (discardingTooLongFrame) { buffer.skipBytes(actualReadableBytes()); checkpoint(); @@ -83,7 +83,7 @@ public class CompatibleMarshallingDecoder extends ReplayingDecoder { } @Override - protected void decodeLast(ChannelHandlerContext ctx, ByteBuf buffer, MessageBuf out) throws Exception { + protected void decodeLast(ChannelHandlerContext ctx, ByteBuf buffer, MessageList out) throws Exception { switch (buffer.readableBytes()) { case 0: return; diff --git a/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufDecoder.java b/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufDecoder.java index 7bce890497..0e5ea7fd05 100644 --- a/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufDecoder.java +++ b/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufDecoder.java @@ -19,10 +19,10 @@ import com.google.protobuf.ExtensionRegistry; import com.google.protobuf.Message; import com.google.protobuf.MessageLite; import io.netty.buffer.ByteBuf; -import io.netty.buffer.MessageBuf; import io.netty.channel.ChannelHandler.Sharable; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPipeline; +import io.netty.channel.MessageList; import io.netty.handler.codec.ByteToMessageDecoder; import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import io.netty.handler.codec.LengthFieldPrepender; @@ -95,7 +95,7 @@ public class ProtobufDecoder extends MessageToMessageDecoder { } @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf msg, MessageBuf out) throws Exception { + protected void decode(ChannelHandlerContext ctx, ByteBuf msg, MessageList out) throws Exception { final byte[] array; final int offset; final int length = msg.readableBytes(); diff --git a/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufEncoder.java b/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufEncoder.java index 874f402af6..27f1e96e3c 100644 --- a/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufEncoder.java +++ b/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufEncoder.java @@ -19,10 +19,10 @@ import com.google.protobuf.Message; import com.google.protobuf.MessageLite; import com.google.protobuf.MessageLiteOrBuilder; import io.netty.buffer.ByteBuf; -import io.netty.buffer.MessageBuf; import io.netty.channel.ChannelHandler.Sharable; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPipeline; +import io.netty.channel.MessageList; import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import io.netty.handler.codec.LengthFieldPrepender; import io.netty.handler.codec.MessageToMessageEncoder; @@ -58,10 +58,9 @@ import static io.netty.buffer.Unpooled.*; */ @Sharable public class ProtobufEncoder extends MessageToMessageEncoder { - @Override - protected void encode(ChannelHandlerContext ctx, MessageLiteOrBuilder msg, MessageBuf out) - throws Exception { + protected void encode( + ChannelHandlerContext ctx, MessageLiteOrBuilder msg, MessageList out) throws Exception { if (msg instanceof MessageLite) { out.add(wrappedBuffer(((MessageLite) msg).toByteArray())); return; diff --git a/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufVarint32FrameDecoder.java b/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufVarint32FrameDecoder.java index c9b0059112..b599f2ca56 100644 --- a/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufVarint32FrameDecoder.java +++ b/codec/src/main/java/io/netty/handler/codec/protobuf/ProtobufVarint32FrameDecoder.java @@ -17,8 +17,8 @@ package io.netty.handler.codec.protobuf; import com.google.protobuf.CodedInputStream; import io.netty.buffer.ByteBuf; -import io.netty.buffer.MessageBuf; import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.MessageList; import io.netty.handler.codec.ByteToMessageDecoder; import io.netty.handler.codec.CorruptedFrameException; @@ -43,7 +43,7 @@ public class ProtobufVarint32FrameDecoder extends ByteToMessageDecoder { // (just like LengthFieldBasedFrameDecoder) @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in, MessageBuf out) throws Exception { + protected void decode(ChannelHandlerContext ctx, ByteBuf in, MessageList out) throws Exception { in.markReaderIndex(); final byte[] buf = new byte[5]; for (int i = 0; i < buf.length; i ++) { diff --git a/codec/src/main/java/io/netty/handler/codec/string/StringDecoder.java b/codec/src/main/java/io/netty/handler/codec/string/StringDecoder.java index 2f465866fc..cfa9324583 100644 --- a/codec/src/main/java/io/netty/handler/codec/string/StringDecoder.java +++ b/codec/src/main/java/io/netty/handler/codec/string/StringDecoder.java @@ -16,10 +16,10 @@ package io.netty.handler.codec.string; import io.netty.buffer.ByteBuf; -import io.netty.buffer.MessageBuf; import io.netty.channel.ChannelHandler.Sharable; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPipeline; +import io.netty.channel.MessageList; import io.netty.handler.codec.ByteToMessageDecoder; import io.netty.handler.codec.DelimiterBasedFrameDecoder; import io.netty.handler.codec.LineBasedFrameDecoder; @@ -75,7 +75,7 @@ public class StringDecoder extends MessageToMessageDecoder { } @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf msg, MessageBuf out) throws Exception { + protected void decode(ChannelHandlerContext ctx, ByteBuf msg, MessageList out) throws Exception { out.add(msg.toString(charset)); } } diff --git a/codec/src/main/java/io/netty/handler/codec/string/StringEncoder.java b/codec/src/main/java/io/netty/handler/codec/string/StringEncoder.java index af6188f0f1..3c2059fe66 100644 --- a/codec/src/main/java/io/netty/handler/codec/string/StringEncoder.java +++ b/codec/src/main/java/io/netty/handler/codec/string/StringEncoder.java @@ -19,9 +19,9 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandler.Sharable; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelOutboundMessageHandlerAdapter; import io.netty.channel.ChannelPipeline; import io.netty.handler.codec.LineBasedFrameDecoder; +import io.netty.handler.codec.MessageToByteEncoder; import java.nio.charset.Charset; @@ -47,7 +47,7 @@ import java.nio.charset.Charset; * */ @Sharable -public class StringEncoder extends ChannelOutboundMessageHandlerAdapter { +public class StringEncoder extends MessageToByteEncoder { // TODO Use CharsetEncoder instead. private final Charset charset; @@ -70,20 +70,12 @@ public class StringEncoder extends ChannelOutboundMessageHandlerAdapter out) { + protected void decode(ChannelHandlerContext ctx, ByteBuf in, MessageList out) { ByteBuf msg = in.readBytes(in.bytesBefore(ByteBufIndexFinder.LF)); in.skipBytes(1); out.add(msg); @@ -65,7 +65,7 @@ public class ReplayingDecoderTest { @Test public void testReplacement() throws Exception { - EmbeddedByteChannel ch = new EmbeddedByteChannel(new BloatedLineDecoder()); + EmbeddedChannel ch = new EmbeddedChannel(new BloatedLineDecoder()); // "AB" should be forwarded to LineDecoder by BloatedLineDecoder. ch.writeInbound(Unpooled.wrappedBuffer(new byte[]{'A', 'B'})); @@ -79,10 +79,11 @@ public class ReplayingDecoderTest { assertNull(ch.readInbound()); } - private static final class BloatedLineDecoder extends ChannelInboundByteHandlerAdapter { + private static final class BloatedLineDecoder extends ChannelInboundHandlerAdapter { @Override - protected void inboundBufferUpdated(ChannelHandlerContext ctx, ByteBuf in) throws Exception { + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { ctx.pipeline().replace(this, "less-bloated", new LineDecoder()); + ctx.pipeline().fireMessageReceived(msgs); } } @@ -90,7 +91,7 @@ public class ReplayingDecoderTest { public void testSingleDecode() throws Exception { LineDecoder decoder = new LineDecoder(); decoder.setSingleDecode(true); - EmbeddedByteChannel ch = new EmbeddedByteChannel(decoder); + EmbeddedChannel ch = new EmbeddedChannel(decoder); // "C\n" should be appended to "AB" so that LineDecoder decodes it correctly. ch.writeInbound(Unpooled.wrappedBuffer(new byte[]{'C', '\n' , 'B', '\n'})); diff --git a/codec/src/test/java/io/netty/handler/codec/bytes/ByteArrayDecoderTest.java b/codec/src/test/java/io/netty/handler/codec/bytes/ByteArrayDecoderTest.java index 0f390249ca..acec3cbf41 100644 --- a/codec/src/test/java/io/netty/handler/codec/bytes/ByteArrayDecoderTest.java +++ b/codec/src/test/java/io/netty/handler/codec/bytes/ByteArrayDecoderTest.java @@ -15,7 +15,7 @@ */ package io.netty.handler.codec.bytes; -import io.netty.channel.embedded.EmbeddedMessageChannel; +import io.netty.channel.embedded.EmbeddedChannel; import io.netty.util.internal.EmptyArrays; import org.junit.Before; import org.junit.Test; @@ -31,11 +31,11 @@ import static org.junit.Assert.*; @SuppressWarnings("ZeroLengthArrayAllocation") public class ByteArrayDecoderTest { - private EmbeddedMessageChannel ch; + private EmbeddedChannel ch; @Before public void setUp() { - ch = new EmbeddedMessageChannel(new ByteArrayDecoder()); + ch = new EmbeddedChannel(new ByteArrayDecoder()); } @Test diff --git a/codec/src/test/java/io/netty/handler/codec/bytes/ByteArrayEncoderTest.java b/codec/src/test/java/io/netty/handler/codec/bytes/ByteArrayEncoderTest.java index c190ee3949..9e46212819 100644 --- a/codec/src/test/java/io/netty/handler/codec/bytes/ByteArrayEncoderTest.java +++ b/codec/src/test/java/io/netty/handler/codec/bytes/ByteArrayEncoderTest.java @@ -16,7 +16,8 @@ package io.netty.handler.codec.bytes; import io.netty.buffer.ByteBuf; -import io.netty.channel.embedded.EmbeddedMessageChannel; +import io.netty.buffer.Unpooled; +import io.netty.channel.embedded.EmbeddedChannel; import io.netty.util.internal.EmptyArrays; import org.junit.Before; import org.junit.Test; @@ -31,11 +32,11 @@ import static org.junit.Assert.*; @SuppressWarnings("ZeroLengthArrayAllocation") public class ByteArrayEncoderTest { - private EmbeddedMessageChannel ch; + private EmbeddedChannel ch; @Before public void setUp() { - ch = new EmbeddedMessageChannel(new ByteArrayEncoder()); + ch = new EmbeddedChannel(new ByteArrayEncoder()); } @Test @@ -49,7 +50,7 @@ public class ByteArrayEncoderTest { @Test public void testEncodeEmpty() { ch.writeOutbound(EmptyArrays.EMPTY_BYTES); - assertThat(ch.readOutbound(), nullValue()); + assertThat((ByteBuf) ch.readOutbound(), is(Unpooled.EMPTY_BUFFER)); } @Test diff --git a/codec/src/test/java/io/netty/handler/codec/compression/JZlibTest.java b/codec/src/test/java/io/netty/handler/codec/compression/JZlibTest.java index a8477f91d5..faecedd859 100644 --- a/codec/src/test/java/io/netty/handler/codec/compression/JZlibTest.java +++ b/codec/src/test/java/io/netty/handler/codec/compression/JZlibTest.java @@ -17,9 +17,8 @@ package io.netty.handler.codec.compression; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; -import org.junit.After; +import io.netty.channel.embedded.EmbeddedChannel; import org.junit.Test; -import io.netty.channel.embedded.EmbeddedByteChannel; import static org.junit.Assert.*; @@ -29,26 +28,23 @@ public class JZlibTest { public void testZLIB() throws Exception { ByteBuf data = Unpooled.wrappedBuffer("test".getBytes()); - EmbeddedByteChannel chEncoder = - new EmbeddedByteChannel(new JZlibEncoder(ZlibWrapper.ZLIB)); + EmbeddedChannel chEncoder = new EmbeddedChannel(new JZlibEncoder(ZlibWrapper.ZLIB)); chEncoder.writeOutbound(data.copy()); assertTrue(chEncoder.finish()); - byte[] deflatedData = chEncoder.readOutbound().array(); + ByteBuf deflatedData = (ByteBuf) chEncoder.readOutbound(); - EmbeddedByteChannel chDecoderZlib = - new EmbeddedByteChannel(new JZlibDecoder(ZlibWrapper.ZLIB)); + EmbeddedChannel chDecoderZlib = new EmbeddedChannel(new JZlibDecoder(ZlibWrapper.ZLIB)); - chDecoderZlib.writeInbound(Unpooled.wrappedBuffer(deflatedData)); + chDecoderZlib.writeInbound(deflatedData.copy()); assertTrue(chDecoderZlib.finish()); assertEquals(data, chDecoderZlib.readInbound()); - EmbeddedByteChannel chDecoderZlibOrNone = - new EmbeddedByteChannel(new JZlibDecoder(ZlibWrapper.ZLIB_OR_NONE)); + EmbeddedChannel chDecoderZlibOrNone = new EmbeddedChannel(new JZlibDecoder(ZlibWrapper.ZLIB_OR_NONE)); - chDecoderZlibOrNone.writeInbound(Unpooled.wrappedBuffer(deflatedData)); + chDecoderZlibOrNone.writeInbound(deflatedData); assertTrue(chDecoderZlibOrNone.finish()); assertEquals(data, chDecoderZlibOrNone.readInbound()); @@ -58,26 +54,24 @@ public class JZlibTest { public void testNONE() throws Exception { ByteBuf data = Unpooled.wrappedBuffer("test".getBytes()); - EmbeddedByteChannel chEncoder = - new EmbeddedByteChannel(new JZlibEncoder(ZlibWrapper.NONE)); + EmbeddedChannel chEncoder = new EmbeddedChannel(new JZlibEncoder(ZlibWrapper.NONE)); chEncoder.writeOutbound(data.copy()); assertTrue(chEncoder.finish()); - byte[] deflatedData = chEncoder.readOutbound().array(); + ByteBuf deflatedData = (ByteBuf) chEncoder.readOutbound(); - EmbeddedByteChannel chDecoderZlibNone = - new EmbeddedByteChannel(new JZlibDecoder(ZlibWrapper.NONE)); + EmbeddedChannel chDecoderZlibNone = new EmbeddedChannel(new JZlibDecoder(ZlibWrapper.NONE)); - chDecoderZlibNone.writeInbound(Unpooled.wrappedBuffer(deflatedData)); + chDecoderZlibNone.writeInbound(deflatedData.copy()); assertTrue(chDecoderZlibNone.finish()); assertEquals(data, chDecoderZlibNone.readInbound()); - EmbeddedByteChannel chDecoderZlibOrNone = - new EmbeddedByteChannel(new JZlibDecoder(ZlibWrapper.ZLIB_OR_NONE)); + EmbeddedChannel chDecoderZlibOrNone = + new EmbeddedChannel(new JZlibDecoder(ZlibWrapper.ZLIB_OR_NONE)); - chDecoderZlibOrNone.writeInbound(Unpooled.wrappedBuffer(deflatedData)); + chDecoderZlibOrNone.writeInbound(deflatedData); assertTrue(chDecoderZlibOrNone.finish()); assertEquals(data, chDecoderZlibOrNone.readInbound()); @@ -87,26 +81,24 @@ public class JZlibTest { public void testGZIP() throws Exception { ByteBuf data = Unpooled.wrappedBuffer("test".getBytes()); - EmbeddedByteChannel chEncoder = - new EmbeddedByteChannel(new JZlibEncoder(ZlibWrapper.GZIP)); + EmbeddedChannel chEncoder = new EmbeddedChannel(new JZlibEncoder(ZlibWrapper.GZIP)); chEncoder.writeOutbound(data.copy()); assertTrue(chEncoder.finish()); - byte[] deflatedData = chEncoder.readOutbound().array(); + ByteBuf deflatedData = (ByteBuf) chEncoder.readOutbound(); - EmbeddedByteChannel chDecoderGZip = - new EmbeddedByteChannel(new JZlibDecoder(ZlibWrapper.GZIP)); + EmbeddedChannel chDecoderGZip = new EmbeddedChannel(new JZlibDecoder(ZlibWrapper.GZIP)); - chDecoderGZip.writeInbound(Unpooled.wrappedBuffer(deflatedData)); + chDecoderGZip.writeInbound(deflatedData.copy()); assertTrue(chDecoderGZip.finish()); assertEquals(data, chDecoderGZip.readInbound()); - EmbeddedByteChannel chDecoderZlibOrNone = - new EmbeddedByteChannel(new JZlibDecoder(ZlibWrapper.ZLIB_OR_NONE)); + EmbeddedChannel chDecoderZlibOrNone = + new EmbeddedChannel(new JZlibDecoder(ZlibWrapper.ZLIB_OR_NONE)); - chDecoderZlibOrNone.writeInbound(Unpooled.wrappedBuffer(deflatedData)); + chDecoderZlibOrNone.writeInbound(deflatedData); assertTrue(chDecoderZlibOrNone.finish()); assertEquals(data, chDecoderZlibOrNone.readInbound()); diff --git a/codec/src/test/java/io/netty/handler/codec/compression/SnappyFramedDecoderTest.java b/codec/src/test/java/io/netty/handler/codec/compression/SnappyFramedDecoderTest.java index 37d068c34a..2b25544309 100644 --- a/codec/src/test/java/io/netty/handler/codec/compression/SnappyFramedDecoderTest.java +++ b/codec/src/test/java/io/netty/handler/codec/compression/SnappyFramedDecoderTest.java @@ -17,21 +17,21 @@ package io.netty.handler.codec.compression; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedByteChannel; +import io.netty.channel.embedded.EmbeddedChannel; import org.junit.Before; import org.junit.Test; import static org.junit.Assert.*; public class SnappyFramedDecoderTest { - private EmbeddedByteChannel channel; + private EmbeddedChannel channel; @Before public void initChannel() { - channel = new EmbeddedByteChannel(new SnappyFramedDecoder()); + channel = new EmbeddedChannel(new SnappyFramedDecoder()); } - @Test(expected = CompressionException.class) + @Test(expected = DecompressionException.class) public void testReservedUnskippableChunkTypeCausesError() throws Exception { ByteBuf in = Unpooled.wrappedBuffer(new byte[] { 0x03, 0x01, 0x00, 0x00, 0x00 @@ -40,7 +40,7 @@ public class SnappyFramedDecoderTest { channel.writeInbound(in); } - @Test(expected = CompressionException.class) + @Test(expected = DecompressionException.class) public void testInvalidStreamIdentifierLength() throws Exception { ByteBuf in = Unpooled.wrappedBuffer(new byte[] { -0x80, 0x05, 0x00, 0x00, 'n', 'e', 't', 't', 'y' @@ -49,7 +49,7 @@ public class SnappyFramedDecoderTest { channel.writeInbound(in); } - @Test(expected = CompressionException.class) + @Test(expected = DecompressionException.class) public void testInvalidStreamIdentifierValue() throws Exception { ByteBuf in = Unpooled.wrappedBuffer(new byte[] { -0x80, 0x06, 0x00, 0x00, 's', 'n', 'e', 't', 't', 'y' @@ -58,7 +58,7 @@ public class SnappyFramedDecoderTest { channel.writeInbound(in); } - @Test(expected = CompressionException.class) + @Test(expected = DecompressionException.class) public void testReservedSkippableBeforeStreamIdentifier() throws Exception { ByteBuf in = Unpooled.wrappedBuffer(new byte[] { -0x7f, 0x06, 0x00, 0x00, 's', 'n', 'e', 't', 't', 'y' @@ -67,7 +67,7 @@ public class SnappyFramedDecoderTest { channel.writeInbound(in); } - @Test(expected = CompressionException.class) + @Test(expected = DecompressionException.class) public void testUncompressedDataBeforeStreamIdentifier() throws Exception { ByteBuf in = Unpooled.wrappedBuffer(new byte[] { 0x01, 0x05, 0x00, 0x00, 'n', 'e', 't', 't', 'y' @@ -76,7 +76,7 @@ public class SnappyFramedDecoderTest { channel.writeInbound(in); } - @Test(expected = CompressionException.class) + @Test(expected = DecompressionException.class) public void testCompressedDataBeforeStreamIdentifier() throws Exception { ByteBuf in = Unpooled.wrappedBuffer(new byte[] { 0x00, 0x05, 0x00, 0x00, 'n', 'e', 't', 't', 'y' @@ -130,9 +130,9 @@ public class SnappyFramedDecoderTest { // The following two tests differ in only the checksum provided for the literal // uncompressed string "netty" - @Test(expected = CompressionException.class) + @Test(expected = DecompressionException.class) public void testInvalidChecksumThrowsException() throws Exception { - EmbeddedByteChannel channel = new EmbeddedByteChannel(new SnappyFramedDecoder(true)); + EmbeddedChannel channel = new EmbeddedChannel(new SnappyFramedDecoder(true)); // checksum here is presented as 0 ByteBuf in = Unpooled.wrappedBuffer(new byte[] { @@ -145,7 +145,7 @@ public class SnappyFramedDecoderTest { @Test public void testInvalidChecksumDoesNotThrowException() throws Exception { - EmbeddedByteChannel channel = new EmbeddedByteChannel(new SnappyFramedDecoder(true)); + EmbeddedChannel channel = new EmbeddedChannel(new SnappyFramedDecoder(true)); // checksum here is presented as -1568496083 (little endian) ByteBuf in = Unpooled.wrappedBuffer(new byte[] { diff --git a/codec/src/test/java/io/netty/handler/codec/compression/SnappyFramedEncoderTest.java b/codec/src/test/java/io/netty/handler/codec/compression/SnappyFramedEncoderTest.java index 661a99159c..7dfde03d91 100644 --- a/codec/src/test/java/io/netty/handler/codec/compression/SnappyFramedEncoderTest.java +++ b/codec/src/test/java/io/netty/handler/codec/compression/SnappyFramedEncoderTest.java @@ -16,19 +16,20 @@ package io.netty.handler.codec.compression; import io.netty.buffer.ByteBuf; +import io.netty.buffer.CompositeByteBuf; import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedByteChannel; +import io.netty.channel.embedded.EmbeddedChannel; import org.junit.Before; import org.junit.Test; import static org.junit.Assert.*; public class SnappyFramedEncoderTest { - private EmbeddedByteChannel channel; + private EmbeddedChannel channel; @Before public void setUp() { - channel = new EmbeddedByteChannel(new SnappyFramedEncoder()); + channel = new EmbeddedChannel(new SnappyFramedEncoder()); } @Test @@ -83,7 +84,17 @@ public class SnappyFramedEncoderTest { 0x01, 0x09, 0x00, 0x00, 0x2d, -0x5a, -0x7e, -0x5e, 'n', 'e', 't', 't', 'y', 0x01, 0x09, 0x00, 0x00, 0x2d, -0x5a, -0x7e, -0x5e, 'n', 'e', 't', 't', 'y', }); - assertEquals(expected, channel.readOutbound()); + + CompositeByteBuf actual = Unpooled.compositeBuffer(); + for (;;) { + ByteBuf m = (ByteBuf) channel.readOutbound(); + if (m == null) { + break; + } + actual.addComponent(m); + actual.writerIndex(actual.writerIndex() + m.readableBytes()); + } + assertEquals(expected, actual); in.release(); } diff --git a/codec/src/test/java/io/netty/handler/codec/compression/SnappyIntegrationTest.java b/codec/src/test/java/io/netty/handler/codec/compression/SnappyIntegrationTest.java index f92b83c6a5..a073ec9945 100644 --- a/codec/src/test/java/io/netty/handler/codec/compression/SnappyIntegrationTest.java +++ b/codec/src/test/java/io/netty/handler/codec/compression/SnappyIntegrationTest.java @@ -16,7 +16,9 @@ package io.netty.handler.codec.compression; import io.netty.buffer.ByteBuf; -import io.netty.channel.embedded.EmbeddedByteChannel; +import io.netty.buffer.CompositeByteBuf; +import io.netty.buffer.Unpooled; +import io.netty.channel.embedded.EmbeddedChannel; import io.netty.util.CharsetUtil; import org.junit.Test; @@ -116,17 +118,25 @@ public class SnappyIntegrationTest { } private static void testIdentity(ByteBuf in) { - EmbeddedByteChannel encoder = new EmbeddedByteChannel(new SnappyFramedEncoder()); - EmbeddedByteChannel decoder = new EmbeddedByteChannel(new SnappyFramedDecoder()); + EmbeddedChannel encoder = new EmbeddedChannel(new SnappyFramedEncoder()); + EmbeddedChannel decoder = new EmbeddedChannel(new SnappyFramedDecoder()); try { encoder.writeOutbound(in.copy()); - ByteBuf compressed = encoder.readOutbound(); + ByteBuf compressed = (ByteBuf) encoder.readOutbound(); assertThat(compressed, is(notNullValue())); assertThat(compressed, is(not(in))); - decoder.writeInbound(compressed); + decoder.writeInbound(compressed.retain()); assertFalse(compressed.isReadable()); - compressed.discardReadBytes(); - ByteBuf decompressed = (ByteBuf) decoder.readInbound(); + compressed.release(); + CompositeByteBuf decompressed = Unpooled.compositeBuffer(); + for (;;) { + Object o = decoder.readInbound(); + if (o == null) { + break; + } + decompressed.addComponent((ByteBuf) o); + decompressed.writerIndex(decompressed.writerIndex() + ((ByteBuf) o).readableBytes()); + } assertEquals(in, decompressed); } finally { // Avoids memory leak through AbstractChannel.allChannels diff --git a/codec/src/test/java/io/netty/handler/codec/compression/SnappyTest.java b/codec/src/test/java/io/netty/handler/codec/compression/SnappyTest.java index 5fcc03ea60..c7122d1913 100644 --- a/codec/src/test/java/io/netty/handler/codec/compression/SnappyTest.java +++ b/codec/src/test/java/io/netty/handler/codec/compression/SnappyTest.java @@ -67,7 +67,7 @@ public class SnappyTest { assertEquals("Copy was not decoded correctly", expected, out); } - @Test(expected = CompressionException.class) + @Test(expected = DecompressionException.class) public void testDecodeCopyWithTinyOffset() throws Exception { ByteBuf in = Unpooled.wrappedBuffer(new byte[] { 0x0b, // preamble length @@ -80,7 +80,7 @@ public class SnappyTest { snappy.decode(in, out); } - @Test(expected = CompressionException.class) + @Test(expected = DecompressionException.class) public void testDecodeCopyWithOffsetBeforeChunk() throws Exception { ByteBuf in = Unpooled.wrappedBuffer(new byte[] { 0x0a, // preamble length @@ -93,7 +93,7 @@ public class SnappyTest { snappy.decode(in, out); } - @Test(expected = CompressionException.class) + @Test(expected = DecompressionException.class) public void testDecodeWithOverlyLongPreamble() throws Exception { ByteBuf in = Unpooled.wrappedBuffer(new byte[] { -0x80, -0x80, -0x80, -0x80, 0x7f, // preamble length @@ -187,7 +187,7 @@ public class SnappyTest { validateChecksum(maskChecksum(0x37c55159), input); } - @Test(expected = CompressionException.class) + @Test(expected = DecompressionException.class) public void testValidateChecksumFails() { ByteBuf input = Unpooled.wrappedBuffer(new byte[] { 'y', 't', 't', 'e', 'n' diff --git a/codec/src/test/java/io/netty/handler/codec/frame/DelimiterBasedFrameDecoderTest.java b/codec/src/test/java/io/netty/handler/codec/frame/DelimiterBasedFrameDecoderTest.java index f9f7e5388a..77f7fa0232 100644 --- a/codec/src/test/java/io/netty/handler/codec/frame/DelimiterBasedFrameDecoderTest.java +++ b/codec/src/test/java/io/netty/handler/codec/frame/DelimiterBasedFrameDecoderTest.java @@ -15,23 +15,23 @@ */ package io.netty.handler.codec.frame; -import static org.junit.Assert.*; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedByteChannel; +import io.netty.channel.embedded.EmbeddedChannel; import io.netty.handler.codec.DecoderException; import io.netty.handler.codec.DelimiterBasedFrameDecoder; import io.netty.handler.codec.Delimiters; import io.netty.handler.codec.TooLongFrameException; import io.netty.util.CharsetUtil; - import org.junit.Test; +import static org.junit.Assert.*; + public class DelimiterBasedFrameDecoderTest { @Test public void testFailSlowTooLongFrameRecovery() throws Exception { - EmbeddedByteChannel ch = new EmbeddedByteChannel( + EmbeddedChannel ch = new EmbeddedChannel( new DelimiterBasedFrameDecoder(1, true, false, Delimiters.nulDelimiter())); for (int i = 0; i < 2; i ++) { @@ -51,7 +51,7 @@ public class DelimiterBasedFrameDecoderTest { @Test public void testFailFastTooLongFrameRecovery() throws Exception { - EmbeddedByteChannel ch = new EmbeddedByteChannel( + EmbeddedChannel ch = new EmbeddedChannel( new DelimiterBasedFrameDecoder(1, Delimiters.nulDelimiter())); for (int i = 0; i < 2; i ++) { diff --git a/codec/src/test/java/io/netty/handler/codec/frame/LengthFieldBasedFrameDecoderTest.java b/codec/src/test/java/io/netty/handler/codec/frame/LengthFieldBasedFrameDecoderTest.java index 80ceb6a009..401eed24f9 100644 --- a/codec/src/test/java/io/netty/handler/codec/frame/LengthFieldBasedFrameDecoderTest.java +++ b/codec/src/test/java/io/netty/handler/codec/frame/LengthFieldBasedFrameDecoderTest.java @@ -15,21 +15,21 @@ */ package io.netty.handler.codec.frame; -import static org.junit.Assert.*; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; -import io.netty.channel.embedded.EmbeddedByteChannel; +import io.netty.channel.embedded.EmbeddedChannel; import io.netty.handler.codec.DecoderException; import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import io.netty.handler.codec.TooLongFrameException; import io.netty.util.CharsetUtil; - import org.junit.Test; +import static org.junit.Assert.*; + public class LengthFieldBasedFrameDecoderTest { @Test public void testFailSlowTooLongFrameRecovery() throws Exception { - EmbeddedByteChannel ch = new EmbeddedByteChannel( + EmbeddedChannel ch = new EmbeddedChannel( new LengthFieldBasedFrameDecoder(5, 0, 4, 0, 4, false)); for (int i = 0; i < 2; i ++) { @@ -49,7 +49,7 @@ public class LengthFieldBasedFrameDecoderTest { @Test public void testFailFastTooLongFrameRecovery() throws Exception { - EmbeddedByteChannel ch = new EmbeddedByteChannel( + EmbeddedChannel ch = new EmbeddedChannel( new LengthFieldBasedFrameDecoder(5, 0, 4, 0, 4)); for (int i = 0; i < 2; i ++) { diff --git a/codec/src/test/java/io/netty/handler/codec/frame/LengthFieldPrependerTest.java b/codec/src/test/java/io/netty/handler/codec/frame/LengthFieldPrependerTest.java index 16bfa998ab..266034fdb8 100644 --- a/codec/src/test/java/io/netty/handler/codec/frame/LengthFieldPrependerTest.java +++ b/codec/src/test/java/io/netty/handler/codec/frame/LengthFieldPrependerTest.java @@ -17,7 +17,8 @@ package io.netty.handler.codec.frame; import io.netty.buffer.ByteBuf; import io.netty.channel.IncompleteFlushException; -import io.netty.channel.embedded.EmbeddedByteChannel; +import io.netty.channel.embedded.EmbeddedChannel; +import io.netty.handler.codec.EncoderException; import io.netty.handler.codec.LengthFieldPrepender; import io.netty.util.CharsetUtil; import org.junit.Before; @@ -38,32 +39,32 @@ public class LengthFieldPrependerTest { @Test public void testPrependLength() throws Exception { - final EmbeddedByteChannel ch = new EmbeddedByteChannel(new LengthFieldPrepender(4)); + final EmbeddedChannel ch = new EmbeddedChannel(new LengthFieldPrepender(4)); ch.writeOutbound(msg); - assertThat(ch.readOutbound(), is(wrappedBuffer(new byte[]{0, 0, 0, 1, 'A'}))); + assertThat((ByteBuf) ch.readOutbound(), is(wrappedBuffer(new byte[]{0, 0, 0, 1, 'A'}))); } @Test public void testPrependLengthIncludesLengthFieldLength() throws Exception { - final EmbeddedByteChannel ch = new EmbeddedByteChannel(new LengthFieldPrepender(4, true)); + final EmbeddedChannel ch = new EmbeddedChannel(new LengthFieldPrepender(4, true)); ch.writeOutbound(msg); - assertThat(ch.readOutbound(), is(wrappedBuffer(new byte[]{0, 0, 0, 5, 'A'}))); + assertThat((ByteBuf) ch.readOutbound(), is(wrappedBuffer(new byte[]{0, 0, 0, 5, 'A'}))); } @Test public void testPrependAdjustedLength() throws Exception { - final EmbeddedByteChannel ch = new EmbeddedByteChannel(new LengthFieldPrepender(4, -1)); + final EmbeddedChannel ch = new EmbeddedChannel(new LengthFieldPrepender(4, -1)); ch.writeOutbound(msg); - assertThat(ch.readOutbound(), is(wrappedBuffer(new byte[]{0, 0, 0, 0, 'A'}))); + assertThat((ByteBuf) ch.readOutbound(), is(wrappedBuffer(new byte[]{0, 0, 0, 0, 'A'}))); } @Test public void testAdjustedLengthLessThanZero() throws Exception { - final EmbeddedByteChannel ch = new EmbeddedByteChannel(new LengthFieldPrepender(4, -2)); + final EmbeddedChannel ch = new EmbeddedChannel(new LengthFieldPrepender(4, -2)); try { ch.writeOutbound(msg); - fail(IncompleteFlushException.class.getSimpleName() + " must be raised."); - } catch (IncompleteFlushException e) { + fail(EncoderException.class.getSimpleName() + " must be raised."); + } catch (EncoderException e) { // Expected } } diff --git a/codec/src/test/java/io/netty/handler/codec/marshalling/AbstractCompatibleMarshallingDecoderTest.java b/codec/src/test/java/io/netty/handler/codec/marshalling/AbstractCompatibleMarshallingDecoderTest.java index 3be464deff..1b65d5fbb0 100644 --- a/codec/src/test/java/io/netty/handler/codec/marshalling/AbstractCompatibleMarshallingDecoderTest.java +++ b/codec/src/test/java/io/netty/handler/codec/marshalling/AbstractCompatibleMarshallingDecoderTest.java @@ -18,7 +18,7 @@ package io.netty.handler.codec.marshalling; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandler; -import io.netty.channel.embedded.EmbeddedByteChannel; +import io.netty.channel.embedded.EmbeddedChannel; import org.jboss.marshalling.Marshaller; import org.jboss.marshalling.MarshallerFactory; import org.jboss.marshalling.Marshalling; @@ -39,7 +39,7 @@ public abstract class AbstractCompatibleMarshallingDecoderTest { MarshallerFactory marshallerFactory = createMarshallerFactory(); MarshallingConfiguration configuration = createMarshallingConfig(); - EmbeddedByteChannel ch = new EmbeddedByteChannel(createDecoder(Integer.MAX_VALUE)); + EmbeddedChannel ch = new EmbeddedChannel(createDecoder(Integer.MAX_VALUE)); ByteArrayOutputStream bout = new ByteArrayOutputStream(); Marshaller marshaller = marshallerFactory.createMarshaller(configuration); @@ -69,7 +69,7 @@ public abstract class AbstractCompatibleMarshallingDecoderTest { MarshallerFactory marshallerFactory = createMarshallerFactory(); MarshallingConfiguration configuration = createMarshallingConfig(); - EmbeddedByteChannel ch = new EmbeddedByteChannel(createDecoder(Integer.MAX_VALUE)); + EmbeddedChannel ch = new EmbeddedChannel(createDecoder(Integer.MAX_VALUE)); ByteArrayOutputStream bout = new ByteArrayOutputStream(); Marshaller marshaller = marshallerFactory.createMarshaller(configuration); @@ -83,7 +83,7 @@ public abstract class AbstractCompatibleMarshallingDecoderTest { ByteBuf buffer = input(testBytes); ByteBuf slice = buffer.readSlice(2); - ch.writeInbound(slice); + ch.writeInbound(slice.retain()); ch.writeInbound(buffer); assertTrue(ch.finish()); @@ -100,7 +100,7 @@ public abstract class AbstractCompatibleMarshallingDecoderTest { MarshallingConfiguration configuration = createMarshallingConfig(); ChannelHandler mDecoder = createDecoder(4); - EmbeddedByteChannel ch = new EmbeddedByteChannel(mDecoder); + EmbeddedChannel ch = new EmbeddedChannel(mDecoder); ByteArrayOutputStream bout = new ByteArrayOutputStream(); Marshaller marshaller = marshallerFactory.createMarshaller(configuration); @@ -113,7 +113,7 @@ public abstract class AbstractCompatibleMarshallingDecoderTest { onTooBigFrame(ch, input(testBytes)); } - protected void onTooBigFrame(EmbeddedByteChannel ch, ByteBuf input) { + protected void onTooBigFrame(EmbeddedChannel ch, ByteBuf input) { ch.writeInbound(input); assertFalse(ch.isActive()); } diff --git a/codec/src/test/java/io/netty/handler/codec/marshalling/AbstractCompatibleMarshallingEncoderTest.java b/codec/src/test/java/io/netty/handler/codec/marshalling/AbstractCompatibleMarshallingEncoderTest.java index dc520738e6..2a03e827cb 100644 --- a/codec/src/test/java/io/netty/handler/codec/marshalling/AbstractCompatibleMarshallingEncoderTest.java +++ b/codec/src/test/java/io/netty/handler/codec/marshalling/AbstractCompatibleMarshallingEncoderTest.java @@ -17,7 +17,7 @@ package io.netty.handler.codec.marshalling; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandler; -import io.netty.channel.embedded.EmbeddedByteChannel; +import io.netty.channel.embedded.EmbeddedChannel; import org.jboss.marshalling.MarshallerFactory; import org.jboss.marshalling.Marshalling; import org.jboss.marshalling.MarshallingConfiguration; @@ -38,12 +38,12 @@ public abstract class AbstractCompatibleMarshallingEncoderTest { final MarshallerFactory marshallerFactory = createMarshallerFactory(); final MarshallingConfiguration configuration = createMarshallingConfig(); - EmbeddedByteChannel ch = new EmbeddedByteChannel(createEncoder()); + EmbeddedChannel ch = new EmbeddedChannel(createEncoder()); ch.writeOutbound(testObject); assertTrue(ch.finish()); - ByteBuf buffer = ch.readOutbound(); + ByteBuf buffer = (ByteBuf) ch.readOutbound(); Unmarshaller unmarshaller = marshallerFactory.createUnmarshaller(configuration); unmarshaller.start(Marshalling.createByteInput(truncate(buffer).nioBuffer())); diff --git a/codec/src/test/java/io/netty/handler/codec/marshalling/RiverMarshallingDecoderTest.java b/codec/src/test/java/io/netty/handler/codec/marshalling/RiverMarshallingDecoderTest.java index 17d7cb2d59..b68e89a43c 100644 --- a/codec/src/test/java/io/netty/handler/codec/marshalling/RiverMarshallingDecoderTest.java +++ b/codec/src/test/java/io/netty/handler/codec/marshalling/RiverMarshallingDecoderTest.java @@ -18,7 +18,7 @@ package io.netty.handler.codec.marshalling; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandler; -import io.netty.channel.embedded.EmbeddedByteChannel; +import io.netty.channel.embedded.EmbeddedChannel; import io.netty.handler.codec.CodecException; import io.netty.handler.codec.TooLongFrameException; @@ -40,7 +40,7 @@ public class RiverMarshallingDecoderTest extends RiverCompatibleMarshallingDecod } @Override - protected void onTooBigFrame(EmbeddedByteChannel ch, ByteBuf input) { + protected void onTooBigFrame(EmbeddedChannel ch, ByteBuf input) { try { ch.writeInbound(input); fail(); diff --git a/codec/src/test/java/io/netty/handler/codec/marshalling/SerialMarshallingDecoderTest.java b/codec/src/test/java/io/netty/handler/codec/marshalling/SerialMarshallingDecoderTest.java index 6f27baf078..b86dcfffe1 100644 --- a/codec/src/test/java/io/netty/handler/codec/marshalling/SerialMarshallingDecoderTest.java +++ b/codec/src/test/java/io/netty/handler/codec/marshalling/SerialMarshallingDecoderTest.java @@ -18,7 +18,7 @@ package io.netty.handler.codec.marshalling; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandler; -import io.netty.channel.embedded.EmbeddedByteChannel; +import io.netty.channel.embedded.EmbeddedChannel; import io.netty.handler.codec.CodecException; import io.netty.handler.codec.TooLongFrameException; @@ -40,7 +40,7 @@ public class SerialMarshallingDecoderTest extends SerialCompatibleMarshallingDec } @Override - protected void onTooBigFrame(EmbeddedByteChannel ch, ByteBuf input) { + protected void onTooBigFrame(EmbeddedChannel ch, ByteBuf input) { try { ch.writeInbound(input); fail(); diff --git a/codec/src/test/java/io/netty/handler/codec/protobuf/ProtobufVarint32FrameDecoderTest.java b/codec/src/test/java/io/netty/handler/codec/protobuf/ProtobufVarint32FrameDecoderTest.java index 65ebf5956a..00cd74ad1c 100644 --- a/codec/src/test/java/io/netty/handler/codec/protobuf/ProtobufVarint32FrameDecoderTest.java +++ b/codec/src/test/java/io/netty/handler/codec/protobuf/ProtobufVarint32FrameDecoderTest.java @@ -15,23 +15,23 @@ */ package io.netty.handler.codec.protobuf; +import io.netty.buffer.ByteBuf; +import io.netty.channel.embedded.EmbeddedChannel; +import org.junit.Before; +import org.junit.Test; + import static io.netty.buffer.Unpooled.*; import static org.hamcrest.core.Is.*; import static org.hamcrest.core.IsNull.*; import static org.junit.Assert.*; -import io.netty.buffer.ByteBuf; -import io.netty.channel.embedded.EmbeddedByteChannel; - -import org.junit.Before; -import org.junit.Test; public class ProtobufVarint32FrameDecoderTest { - private EmbeddedByteChannel ch; + private EmbeddedChannel ch; @Before public void setUp() { - ch = new EmbeddedByteChannel(new ProtobufVarint32FrameDecoder()); + ch = new EmbeddedChannel(new ProtobufVarint32FrameDecoder()); } @Test diff --git a/codec/src/test/java/io/netty/handler/codec/protobuf/ProtobufVarint32LengthFieldPrependerTest.java b/codec/src/test/java/io/netty/handler/codec/protobuf/ProtobufVarint32LengthFieldPrependerTest.java index 681a13f11b..7b946f4148 100644 --- a/codec/src/test/java/io/netty/handler/codec/protobuf/ProtobufVarint32LengthFieldPrependerTest.java +++ b/codec/src/test/java/io/netty/handler/codec/protobuf/ProtobufVarint32LengthFieldPrependerTest.java @@ -15,28 +15,29 @@ */ package io.netty.handler.codec.protobuf; -import static io.netty.buffer.Unpooled.*; -import static org.hamcrest.core.Is.*; -import static org.junit.Assert.*; -import io.netty.channel.embedded.EmbeddedByteChannel; - +import io.netty.buffer.ByteBuf; +import io.netty.channel.embedded.EmbeddedChannel; import org.junit.Before; import org.junit.Test; +import static io.netty.buffer.Unpooled.*; +import static org.hamcrest.core.Is.*; +import static org.junit.Assert.*; + public class ProtobufVarint32LengthFieldPrependerTest { - private EmbeddedByteChannel ch; + private EmbeddedChannel ch; @Before public void setUp() { - ch = new EmbeddedByteChannel(new ProtobufVarint32LengthFieldPrepender()); + ch = new EmbeddedChannel(new ProtobufVarint32LengthFieldPrepender()); } @Test public void testTinyEncode() { byte[] b = { 4, 1, 1, 1, 1 }; ch.writeOutbound(wrappedBuffer(b, 1, b.length - 1)); - assertThat(ch.readOutbound(), is(wrappedBuffer(b))); + assertThat((ByteBuf) ch.readOutbound(), is(wrappedBuffer(b))); } @Test @@ -48,6 +49,6 @@ public class ProtobufVarint32LengthFieldPrependerTest { b[0] = -2; b[1] = 15; ch.writeOutbound(wrappedBuffer(b, 2, b.length - 2)); - assertThat(ch.readOutbound(), is(wrappedBuffer(b))); + assertThat((ByteBuf) ch.readOutbound(), is(wrappedBuffer(b))); } } diff --git a/common/pom.xml b/common/pom.xml index a0bc558816..438222a815 100644 --- a/common/pom.xml +++ b/common/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.0.0.Final-SNAPSHOT + 4.0.0.CR4-SNAPSHOT netty-common diff --git a/common/src/main/java/io/netty/util/Recycler.java b/common/src/main/java/io/netty/util/Recycler.java new file mode 100644 index 0000000000..4d14285137 --- /dev/null +++ b/common/src/main/java/io/netty/util/Recycler.java @@ -0,0 +1,90 @@ +/* + * Copyright 2013 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.netty.util; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.IdentityHashMap; +import java.util.Map; + +/** + * Light-weight object pool based on a thread-local stack. + * + * @param the type of the pooled object + */ +public abstract class Recycler { + + private final ThreadLocal> threadLocal = new ThreadLocal>() { + @Override + protected Stack initialValue() { + return new Stack(Recycler.this, Thread.currentThread()); + } + }; + + public final T get() { + Stack stack = threadLocal.get(); + T o = stack.pop(); + if (o == null) { + o = newObject(stack); + } + return o; + } + + public final boolean recycle(T o, Handle handle) { + @SuppressWarnings("unchecked") + Stack stack = (Stack) handle; + if (stack.parent != this) { + return false; + } + + if (Thread.currentThread() != stack.thread) { + return false; + } + + stack.push(o); + return true; + } + + protected abstract T newObject(Handle handle); + + public interface Handle { } + + static final class Stack implements Handle { + final Recycler parent; + final Thread thread; + private final Deque deque = new ArrayDeque(); + private final Map map = new IdentityHashMap(); + + Stack(Recycler parent, Thread thread) { + this.parent = parent; + this.thread = thread; + } + + T pop() { + T ret = deque.pollLast(); + map.remove(ret); + return ret; + } + + void push(T o) { + if (map.put(o, Boolean.TRUE) != null) { + throw new IllegalStateException("recycled already"); + } + deque.addLast(o); + } + } +} diff --git a/common/src/main/java/io/netty/util/ResourceLeakDetector.java b/common/src/main/java/io/netty/util/ResourceLeakDetector.java index 26d45785e1..a6ea748718 100644 --- a/common/src/main/java/io/netty/util/ResourceLeakDetector.java +++ b/common/src/main/java/io/netty/util/ResourceLeakDetector.java @@ -28,12 +28,12 @@ import java.util.concurrent.atomic.AtomicBoolean; public final class ResourceLeakDetector { - private static final boolean ENABLED = SystemPropertyUtil.getBoolean("io.netty.resourceLeakDetection", false); + private static final boolean DISABLED = SystemPropertyUtil.getBoolean("io.netty.noResourceLeakDetection", false); private static final InternalLogger logger = InternalLoggerFactory.getInstance(ResourceLeakDetector.class); static { - logger.debug("io.netty.resourceLeakDetection: {}", ENABLED); + logger.debug("io.netty.noResourceLeakDetection: {}", DISABLED); } private static final int DEFAULT_SAMPLING_INTERVAL = 113; @@ -92,7 +92,7 @@ public final class ResourceLeakDetector { } public ResourceLeak open(T obj) { - if (!ENABLED || leakCheckCnt ++ % samplingInterval != 0) { + if (DISABLED || leakCheckCnt ++ % samplingInterval != 0) { return NOOP; } diff --git a/example/pom.xml b/example/pom.xml index 410d2b71ba..26597f79df 100644 --- a/example/pom.xml +++ b/example/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.0.0.Final-SNAPSHOT + 4.0.0.CR4-SNAPSHOT netty-example diff --git a/example/src/main/java/io/netty/example/applet/AppletDiscardServer.java b/example/src/main/java/io/netty/example/applet/AppletDiscardServer.java index e86e7adf53..878b14fb37 100644 --- a/example/src/main/java/io/netty/example/applet/AppletDiscardServer.java +++ b/example/src/main/java/io/netty/example/applet/AppletDiscardServer.java @@ -19,9 +19,10 @@ import io.netty.bootstrap.ServerBootstrap; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundByteHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInitializer; import io.netty.channel.EventLoopGroup; +import io.netty.channel.MessageList; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; @@ -72,12 +73,15 @@ public class AppletDiscardServer extends JApplet { } } - private static final class DiscardServerHandler extends ChannelInboundByteHandlerAdapter { + private static final class DiscardServerHandler extends ChannelInboundHandlerAdapter { @Override - public void inboundBufferUpdated(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - System.out.println("Received: " + in.toString(CharsetUtil.UTF_8)); - in.clear(); + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + MessageList bufs = msgs.cast(); + for (int i = 0; i < bufs.size(); i++) { + System.out.println("Received: " + bufs.get(i).toString(CharsetUtil.UTF_8)); + } + msgs.releaseAllAndRecycle(); } @Override diff --git a/example/src/main/java/io/netty/example/discard/DiscardClientHandler.java b/example/src/main/java/io/netty/example/discard/DiscardClientHandler.java index 22d1be1318..c28c57fb6e 100644 --- a/example/src/main/java/io/netty/example/discard/DiscardClientHandler.java +++ b/example/src/main/java/io/netty/example/discard/DiscardClientHandler.java @@ -19,7 +19,8 @@ import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundByteHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.MessageList; import java.util.logging.Level; import java.util.logging.Logger; @@ -27,7 +28,7 @@ import java.util.logging.Logger; /** * Handles a client-side channel. */ -public class DiscardClientHandler extends ChannelInboundByteHandlerAdapter { +public class DiscardClientHandler extends ChannelInboundHandlerAdapter { private static final Logger logger = Logger.getLogger( DiscardClientHandler.class.getName()); @@ -57,10 +58,14 @@ public class DiscardClientHandler extends ChannelInboundByteHandlerAdapter { } @Override - public void inboundBufferUpdated(ChannelHandlerContext ctx, ByteBuf in) - throws Exception { + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + content.release(); + } + + @Override + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { // Server is supposed to send nothing, but if it sends something, discard it. - in.clear(); + msgs.releaseAllAndRecycle(); } @Override @@ -77,15 +82,9 @@ public class DiscardClientHandler extends ChannelInboundByteHandlerAdapter { long counter; private void generateTraffic() { - // Fill the outbound buffer up to 64KiB - ByteBuf out = ctx.nextOutboundByteBuffer(); - while (out.readableBytes() < 65536) { - out.writeBytes(content, 0, content.readableBytes()); - } - // Flush the outbound buffer to the socket. // Once flushed, generate the same amount of traffic again. - ctx.flush().addListener(trafficGenerator); + ctx.write(content.duplicate().retain()).addListener(trafficGenerator); } private final ChannelFutureListener trafficGenerator = new ChannelFutureListener() { diff --git a/example/src/main/java/io/netty/example/discard/DiscardServerHandler.java b/example/src/main/java/io/netty/example/discard/DiscardServerHandler.java index 8e43955d0d..e2386fd2d0 100644 --- a/example/src/main/java/io/netty/example/discard/DiscardServerHandler.java +++ b/example/src/main/java/io/netty/example/discard/DiscardServerHandler.java @@ -15,9 +15,9 @@ */ package io.netty.example.discard; -import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundByteHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.MessageList; import java.util.logging.Level; import java.util.logging.Logger; @@ -25,16 +25,15 @@ import java.util.logging.Logger; /** * Handles a server-side channel. */ -public class DiscardServerHandler extends ChannelInboundByteHandlerAdapter { +public class DiscardServerHandler extends ChannelInboundHandlerAdapter { private static final Logger logger = Logger.getLogger( DiscardServerHandler.class.getName()); @Override - public void inboundBufferUpdated(ChannelHandlerContext ctx, ByteBuf in) - throws Exception { + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { // Discard the received data silently. - in.clear(); + msgs.releaseAllAndRecycle(); } @Override diff --git a/example/src/main/java/io/netty/example/echo/EchoClient.java b/example/src/main/java/io/netty/example/echo/EchoClient.java index d124ac2899..15eafffab9 100644 --- a/example/src/main/java/io/netty/example/echo/EchoClient.java +++ b/example/src/main/java/io/netty/example/echo/EchoClient.java @@ -23,8 +23,6 @@ import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioSocketChannel; -import io.netty.handler.logging.LogLevel; -import io.netty.handler.logging.LoggingHandler; /** * Sends one message when a connection is open and echoes back any received @@ -56,7 +54,7 @@ public class EchoClient { @Override public void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast( - new LoggingHandler(LogLevel.INFO), + //new LoggingHandler(LogLevel.INFO), new EchoClientHandler(firstMessageSize)); } }); diff --git a/example/src/main/java/io/netty/example/echo/EchoClientHandler.java b/example/src/main/java/io/netty/example/echo/EchoClientHandler.java index ad52c616dd..fd56fce11e 100644 --- a/example/src/main/java/io/netty/example/echo/EchoClientHandler.java +++ b/example/src/main/java/io/netty/example/echo/EchoClientHandler.java @@ -18,7 +18,8 @@ package io.netty.example.echo; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundByteHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.MessageList; import java.util.logging.Level; import java.util.logging.Logger; @@ -28,7 +29,7 @@ import java.util.logging.Logger; * traffic between the echo client and server by sending the first message to * the server. */ -public class EchoClientHandler extends ChannelInboundByteHandlerAdapter { +public class EchoClientHandler extends ChannelInboundHandlerAdapter { private static final Logger logger = Logger.getLogger( EchoClientHandler.class.getName()); @@ -54,10 +55,8 @@ public class EchoClientHandler extends ChannelInboundByteHandlerAdapter { } @Override - public void inboundBufferUpdated(ChannelHandlerContext ctx, ByteBuf in) { - ByteBuf out = ctx.nextOutboundByteBuffer(); - out.writeBytes(in); - ctx.flush(); + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + ctx.write(msgs); } @Override diff --git a/example/src/main/java/io/netty/example/echo/EchoServer.java b/example/src/main/java/io/netty/example/echo/EchoServer.java index ac6f14398b..55d8d6b48a 100644 --- a/example/src/main/java/io/netty/example/echo/EchoServer.java +++ b/example/src/main/java/io/netty/example/echo/EchoServer.java @@ -51,7 +51,7 @@ public class EchoServer { @Override public void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast( - new LoggingHandler(LogLevel.INFO), + //new LoggingHandler(LogLevel.INFO), new EchoServerHandler()); } }); diff --git a/example/src/main/java/io/netty/example/echo/EchoServerHandler.java b/example/src/main/java/io/netty/example/echo/EchoServerHandler.java index 4d050b073e..110af40f70 100644 --- a/example/src/main/java/io/netty/example/echo/EchoServerHandler.java +++ b/example/src/main/java/io/netty/example/echo/EchoServerHandler.java @@ -15,10 +15,10 @@ */ package io.netty.example.echo; -import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandler.Sharable; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundByteHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.MessageList; import java.util.logging.Level; import java.util.logging.Logger; @@ -27,16 +27,14 @@ import java.util.logging.Logger; * Handler implementation for the echo server. */ @Sharable -public class EchoServerHandler extends ChannelInboundByteHandlerAdapter { +public class EchoServerHandler extends ChannelInboundHandlerAdapter { private static final Logger logger = Logger.getLogger( EchoServerHandler.class.getName()); @Override - public void inboundBufferUpdated(ChannelHandlerContext ctx, ByteBuf in) { - ByteBuf out = ctx.nextOutboundByteBuffer(); - out.writeBytes(in); - ctx.flush(); + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + ctx.write(msgs); } @Override diff --git a/example/src/main/java/io/netty/example/factorial/BigIntegerDecoder.java b/example/src/main/java/io/netty/example/factorial/BigIntegerDecoder.java index 459e842b32..8ed01fa4f0 100644 --- a/example/src/main/java/io/netty/example/factorial/BigIntegerDecoder.java +++ b/example/src/main/java/io/netty/example/factorial/BigIntegerDecoder.java @@ -16,8 +16,8 @@ package io.netty.example.factorial; import io.netty.buffer.ByteBuf; -import io.netty.buffer.MessageBuf; import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.MessageList; import io.netty.handler.codec.ByteToMessageDecoder; import io.netty.handler.codec.CorruptedFrameException; @@ -32,7 +32,7 @@ import java.math.BigInteger; public class BigIntegerDecoder extends ByteToMessageDecoder { @Override - protected void decode(ChannelHandlerContext ctx, ByteBuf in, MessageBuf out) { + protected void decode(ChannelHandlerContext ctx, ByteBuf in, MessageList out) { // Wait until the length prefix is available. if (in.readableBytes() < 5) { return; diff --git a/example/src/main/java/io/netty/example/factorial/FactorialClientHandler.java b/example/src/main/java/io/netty/example/factorial/FactorialClientHandler.java index 6821590b84..28fd76e80f 100644 --- a/example/src/main/java/io/netty/example/factorial/FactorialClientHandler.java +++ b/example/src/main/java/io/netty/example/factorial/FactorialClientHandler.java @@ -15,11 +15,11 @@ */ package io.netty.example.factorial; -import io.netty.buffer.MessageBuf; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.MessageList; +import io.netty.channel.SimpleChannelInboundHandler; import java.math.BigInteger; import java.util.concurrent.BlockingQueue; @@ -34,7 +34,7 @@ import java.util.logging.Logger; * to create a new handler instance whenever you create a new channel and insert * this handler to avoid a race condition. */ -public class FactorialClientHandler extends ChannelInboundMessageHandlerAdapter { +public class FactorialClientHandler extends SimpleChannelInboundHandler { private static final Logger logger = Logger.getLogger( FactorialClientHandler.class.getName()); @@ -98,7 +98,7 @@ public class FactorialClientHandler extends ChannelInboundMessageHandlerAdapter< private void sendNumbers() { // Do not send more than 4096 numbers. boolean finished = false; - MessageBuf out = ctx.nextOutboundMessageBuffer(); + MessageList out = MessageList.newInstance(4096); while (out.size() < 4096) { if (i <= count) { out.add(Integer.valueOf(i)); @@ -109,7 +109,7 @@ public class FactorialClientHandler extends ChannelInboundMessageHandlerAdapter< } } - ChannelFuture f = ctx.flush(); + ChannelFuture f = ctx.write(out); if (!finished) { f.addListener(numberSender); } diff --git a/example/src/main/java/io/netty/example/factorial/FactorialServerHandler.java b/example/src/main/java/io/netty/example/factorial/FactorialServerHandler.java index 3cc4592680..a21b765bfa 100644 --- a/example/src/main/java/io/netty/example/factorial/FactorialServerHandler.java +++ b/example/src/main/java/io/netty/example/factorial/FactorialServerHandler.java @@ -16,7 +16,8 @@ package io.netty.example.factorial; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.MessageList; import java.math.BigInteger; import java.util.Formatter; @@ -30,7 +31,7 @@ import java.util.logging.Logger; * to create a new handler instance whenever you create a new channel and insert * this handler to avoid a race condition. */ -public class FactorialServerHandler extends ChannelInboundMessageHandlerAdapter { +public class FactorialServerHandler extends ChannelInboundHandlerAdapter { private static final Logger logger = Logger.getLogger( FactorialServerHandler.class.getName()); @@ -40,11 +41,16 @@ public class FactorialServerHandler extends ChannelInboundMessageHandlerAdapter< @Override public void messageReceived( - ChannelHandlerContext ctx, BigInteger msg) throws Exception { - // Calculate the cumulative factorial and send it to the client. - lastMultiplier = msg; - factorial = factorial.multiply(msg); - ctx.write(factorial); + ChannelHandlerContext ctx, MessageList msgs) throws Exception { + MessageList ints = msgs.cast(); + for (int i = 0; i < ints.size(); i++) { + BigInteger msg = ints.get(i); + // Calculate the cumulative factorial and send it to the client. + lastMultiplier = msg; + factorial = factorial.multiply(msg); + ctx.write(factorial); + } + msgs.recycle(); } @Override diff --git a/example/src/main/java/io/netty/example/filetransfer/FileServer.java b/example/src/main/java/io/netty/example/filetransfer/FileServer.java index 8b4a182722..efe864a9a7 100644 --- a/example/src/main/java/io/netty/example/filetransfer/FileServer.java +++ b/example/src/main/java/io/netty/example/filetransfer/FileServer.java @@ -18,15 +18,13 @@ package io.netty.example.filetransfer; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; -import io.netty.channel.ChannelProgressiveFuture; -import io.netty.channel.ChannelProgressiveFutureListener; -import io.netty.channel.ChannelProgressivePromise; import io.netty.channel.DefaultFileRegion; import io.netty.channel.EventLoopGroup; import io.netty.channel.FileRegion; +import io.netty.channel.MessageList; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; @@ -94,35 +92,31 @@ public class FileServer { new FileServer(port).run(); } - private static final class FileHandler extends ChannelInboundMessageHandlerAdapter { + private static final class FileHandler extends ChannelInboundHandlerAdapter { @Override - public void messageReceived(ChannelHandlerContext ctx, String msg) throws Exception { - File file = new File(msg); - if (file.exists()) { - if (!file.isFile()) { - ctx.write("Not a file: " + file + '\n'); - return; + public void messageReceived(ChannelHandlerContext ctx, MessageList messages) throws Exception { + MessageList msgs = messages.cast(); + MessageList out = MessageList.newInstance(); + + for (int i = 0; i < msgs.size(); i++) { + String msg = msgs.get(i); + File file = new File(msg); + if (file.exists()) { + if (!file.isFile()) { + ctx.write("Not a file: " + file + '\n'); + return; + } + ctx.write(file + " " + file.length() + '\n'); + FileRegion region = new DefaultFileRegion(new FileInputStream(file).getChannel(), 0, file.length()); + out.add(region); + out.add("\n"); + } else { + out.add("File not found: " + file + '\n'); } - ctx.write(file + " " + file.length() + '\n'); - FileRegion region = new DefaultFileRegion(new FileInputStream(file).getChannel(), 0, file.length()); - ChannelProgressivePromise promise = ctx.newProgressivePromise(); - promise.addListener(new ChannelProgressiveFutureListener() { - @Override - public void operationProgressed(ChannelProgressiveFuture f, long progress, long total) { - System.err.println("progress: " + progress + " / " + total); - } - - @Override - public void operationComplete(ChannelProgressiveFuture future) { - System.err.println("file transfer complete"); - } - }); - - ctx.sendFile(region, promise); - ctx.write("\n"); - } else { - ctx.write("File not found: " + file + '\n'); } + + msgs.recycle(); + ctx.write(out); } @Override diff --git a/example/src/main/java/io/netty/example/http/file/HttpStaticFileServerHandler.java b/example/src/main/java/io/netty/example/http/file/HttpStaticFileServerHandler.java index e9411ce8fc..a460789c13 100644 --- a/example/src/main/java/io/netty/example/http/file/HttpStaticFileServerHandler.java +++ b/example/src/main/java/io/netty/example/http/file/HttpStaticFileServerHandler.java @@ -19,7 +19,8 @@ import io.netty.buffer.Unpooled; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.MessageList; import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.DefaultHttpResponse; import io.netty.handler.codec.http.FullHttpRequest; @@ -27,6 +28,7 @@ import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpResponse; import io.netty.handler.codec.http.HttpResponseStatus; +import io.netty.handler.codec.http.LastHttpContent; import io.netty.handler.stream.ChunkedFile; import io.netty.util.CharsetUtil; @@ -96,7 +98,7 @@ import static io.netty.handler.codec.http.HttpVersion.*; * * */ -public class HttpStaticFileServerHandler extends ChannelInboundMessageHandlerAdapter { +public class HttpStaticFileServerHandler extends ChannelInboundHandlerAdapter { public static final String HTTP_DATE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss zzz"; public static final String HTTP_DATE_GMT_TIMEZONE = "GMT"; @@ -104,89 +106,96 @@ public class HttpStaticFileServerHandler extends ChannelInboundMessageHandlerAda @Override public void messageReceived( - ChannelHandlerContext ctx, FullHttpRequest request) throws Exception { - - if (!request.getDecoderResult().isSuccess()) { - sendError(ctx, BAD_REQUEST); - return; - } - - if (request.getMethod() != GET) { - sendError(ctx, METHOD_NOT_ALLOWED); - return; - } - - final String uri = request.getUri(); - final String path = sanitizeUri(uri); - if (path == null) { - sendError(ctx, FORBIDDEN); - return; - } - - File file = new File(path); - if (file.isHidden() || !file.exists()) { - sendError(ctx, NOT_FOUND); - return; - } - - if (file.isDirectory()) { - if (uri.endsWith("/")) { - sendListing(ctx, file); - } else { - sendRedirect(ctx, uri + '/'); + ChannelHandlerContext ctx, MessageList msgs) throws Exception { + MessageList requests = msgs.cast(); + for (int i = 0; i < requests.size(); i++) { + FullHttpRequest request = requests.get(i); + if (!request.getDecoderResult().isSuccess()) { + sendError(ctx, BAD_REQUEST); + continue; } - return; - } - if (!file.isFile()) { - sendError(ctx, FORBIDDEN); - return; - } + if (request.getMethod() != GET) { + sendError(ctx, METHOD_NOT_ALLOWED); + continue; + } - // Cache Validation - String ifModifiedSince = request.headers().get(IF_MODIFIED_SINCE); - if (ifModifiedSince != null && !ifModifiedSince.isEmpty()) { - SimpleDateFormat dateFormatter = new SimpleDateFormat(HTTP_DATE_FORMAT, Locale.US); - Date ifModifiedSinceDate = dateFormatter.parse(ifModifiedSince); + final String uri = request.getUri(); + final String path = sanitizeUri(uri); + if (path == null) { + sendError(ctx, FORBIDDEN); + continue; + } - // Only compare up to the second because the datetime format we send to the client - // does not have milliseconds - long ifModifiedSinceDateSeconds = ifModifiedSinceDate.getTime() / 1000; - long fileLastModifiedSeconds = file.lastModified() / 1000; - if (ifModifiedSinceDateSeconds == fileLastModifiedSeconds) { - sendNotModified(ctx); - return; + File file = new File(path); + if (file.isHidden() || !file.exists()) { + sendError(ctx, NOT_FOUND); + continue; + } + + if (file.isDirectory()) { + if (uri.endsWith("/")) { + sendListing(ctx, file); + } else { + sendRedirect(ctx, uri + '/'); + } + continue; + } + + if (!file.isFile()) { + sendError(ctx, FORBIDDEN); + continue; + } + + // Cache Validation + String ifModifiedSince = request.headers().get(IF_MODIFIED_SINCE); + if (ifModifiedSince != null && !ifModifiedSince.isEmpty()) { + SimpleDateFormat dateFormatter = new SimpleDateFormat(HTTP_DATE_FORMAT, Locale.US); + Date ifModifiedSinceDate = dateFormatter.parse(ifModifiedSince); + + // Only compare up to the second because the datetime format we send to the client + // does not have milliseconds + long ifModifiedSinceDateSeconds = ifModifiedSinceDate.getTime() / 1000; + long fileLastModifiedSeconds = file.lastModified() / 1000; + if (ifModifiedSinceDateSeconds == fileLastModifiedSeconds) { + sendNotModified(ctx); + continue; + } + } + + RandomAccessFile raf; + try { + raf = new RandomAccessFile(file, "r"); + } catch (FileNotFoundException fnfe) { + sendError(ctx, NOT_FOUND); + continue; + } + long fileLength = raf.length(); + + HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK); + setContentLength(response, fileLength); + setContentTypeHeader(response, file); + setDateAndCacheHeaders(response, file); + if (isKeepAlive(request)) { + response.headers().set(CONNECTION, HttpHeaders.Values.KEEP_ALIVE); + } + + MessageList out = MessageList.newInstance(); + // Write the initial line and the header. + out.add(response); + // Write the content. + out.add(new ChunkedFile(raf, 0, fileLength, 8192)); + // Write the end marker + out.add(LastHttpContent.EMPTY_LAST_CONTENT); + + ChannelFuture writeFuture = ctx.write(out); + // Decide whether to close the connection or not. + if (!isKeepAlive(request)) { + // Close the connection when the whole content is written out. + writeFuture.addListener(ChannelFutureListener.CLOSE); } } - - RandomAccessFile raf; - try { - raf = new RandomAccessFile(file, "r"); - } catch (FileNotFoundException fnfe) { - sendError(ctx, NOT_FOUND); - return; - } - long fileLength = raf.length(); - - HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK); - setContentLength(response, fileLength); - setContentTypeHeader(response, file); - setDateAndCacheHeaders(response, file); - if (isKeepAlive(request)) { - response.headers().set(CONNECTION, HttpHeaders.Values.KEEP_ALIVE); - } - - // Write the initial line and the header. - ctx.write(response); - - // Write the content. - ChannelFuture writeFuture = ctx.write(new ChunkedFile(raf, 0, fileLength, 8192)); - - // Decide whether to close the connection or not. - if (!isKeepAlive(request)) { - // Close the connection when the whole content is written out. - writeFuture.addListener(ChannelFutureListener.CLOSE); - } + msgs.releaseAllAndRecycle(); } @Override diff --git a/example/src/main/java/io/netty/example/http/snoop/HttpSnoopClientHandler.java b/example/src/main/java/io/netty/example/http/snoop/HttpSnoopClientHandler.java index 60f1c641c4..7262fde8d9 100644 --- a/example/src/main/java/io/netty/example/http/snoop/HttpSnoopClientHandler.java +++ b/example/src/main/java/io/netty/example/http/snoop/HttpSnoopClientHandler.java @@ -16,49 +16,54 @@ package io.netty.example.http.snoop; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.MessageList; import io.netty.handler.codec.http.HttpContent; import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpResponse; import io.netty.handler.codec.http.LastHttpContent; import io.netty.util.CharsetUtil; -public class HttpSnoopClientHandler extends ChannelInboundMessageHandlerAdapter { +public class HttpSnoopClientHandler extends ChannelInboundHandlerAdapter { @Override - public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { - if (msg instanceof HttpResponse) { - HttpResponse response = (HttpResponse) msg; + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + for (int i = 0; i < msgs.size(); i++) { + Object msg = msgs.get(i); + if (msg instanceof HttpResponse) { + HttpResponse response = (HttpResponse) msg; - System.out.println("STATUS: " + response.getStatus()); - System.out.println("VERSION: " + response.getProtocolVersion()); - System.out.println(); - - if (!response.headers().isEmpty()) { - for (String name: response.headers().names()) { - for (String value: response.headers().getAll(name)) { - System.out.println("HEADER: " + name + " = " + value); - } - } + System.out.println("STATUS: " + response.getStatus()); + System.out.println("VERSION: " + response.getProtocolVersion()); System.out.println(); - } - if (HttpHeaders.isTransferEncodingChunked(response)) { - System.out.println("CHUNKED CONTENT {"); - } else { - System.out.println("CONTENT {"); - } - } - if (msg instanceof HttpContent) { - HttpContent content = (HttpContent) msg; - - System.out.print(content.content().toString(CharsetUtil.UTF_8)); - System.out.flush(); - - if (content instanceof LastHttpContent) { - System.out.println("} END OF CONTENT"); + if (!response.headers().isEmpty()) { + for (String name: response.headers().names()) { + for (String value: response.headers().getAll(name)) { + System.out.println("HEADER: " + name + " = " + value); + } + } + System.out.println(); + } + + if (HttpHeaders.isTransferEncodingChunked(response)) { + System.out.println("CHUNKED CONTENT {"); + } else { + System.out.println("CONTENT {"); + } + } + if (msg instanceof HttpContent) { + HttpContent content = (HttpContent) msg; + + System.out.print(content.content().toString(CharsetUtil.UTF_8)); + System.out.flush(); + + if (content instanceof LastHttpContent) { + System.out.println("} END OF CONTENT"); + } } } + msgs.releaseAllAndRecycle(); } @Override diff --git a/example/src/main/java/io/netty/example/http/snoop/HttpSnoopServerHandler.java b/example/src/main/java/io/netty/example/http/snoop/HttpSnoopServerHandler.java index 59c8a2da8b..178aebe984 100644 --- a/example/src/main/java/io/netty/example/http/snoop/HttpSnoopServerHandler.java +++ b/example/src/main/java/io/netty/example/http/snoop/HttpSnoopServerHandler.java @@ -17,9 +17,9 @@ package io.netty.example.http.snoop; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.MessageList; import io.netty.handler.codec.DecoderResult; import io.netty.handler.codec.http.Cookie; import io.netty.handler.codec.http.CookieDecoder; @@ -44,19 +44,34 @@ import static io.netty.handler.codec.http.HttpHeaders.*; import static io.netty.handler.codec.http.HttpResponseStatus.*; import static io.netty.handler.codec.http.HttpVersion.*; -public class HttpSnoopServerHandler extends ChannelInboundMessageHandlerAdapter { +public class HttpSnoopServerHandler extends ChannelInboundHandlerAdapter { private HttpRequest request; /** Buffer that stores the response content */ private final StringBuilder buf = new StringBuilder(); @Override - public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + MessageList out = MessageList.newInstance(); + int size = msgs.size(); + try { + for (int i = 0; i < size; i ++) { + if (!messageReceived(ctx, msgs.get(i), out)) { + break; + } + } + } finally { + msgs.releaseAllAndRecycle(); + ctx.write(out); + } + } + + private boolean messageReceived(ChannelHandlerContext ctx, Object msg, MessageList out) { if (msg instanceof HttpRequest) { HttpRequest request = this.request = (HttpRequest) msg; if (is100ContinueExpected(request)) { - send100Continue(ctx); + send100Continue(out); } buf.setLength(0); @@ -119,9 +134,10 @@ public class HttpSnoopServerHandler extends ChannelInboundMessageHandlerAdapter< buf.append("\r\n"); } - writeResponse(ctx, trailer); + return writeResponse(ctx, trailer, out); } } + return true; } private static void appendDecoderResult(StringBuilder buf, HttpObject o) { @@ -135,7 +151,7 @@ public class HttpSnoopServerHandler extends ChannelInboundMessageHandlerAdapter< buf.append("\r\n"); } - private void writeResponse(ChannelHandlerContext ctx, HttpObject currentObj) { + private boolean writeResponse(ChannelHandlerContext ctx, HttpObject currentObj, MessageList out) { // Decide whether to close the connection or not. boolean keepAlive = isKeepAlive(request); // Build the response object. @@ -170,27 +186,18 @@ public class HttpSnoopServerHandler extends ChannelInboundMessageHandlerAdapter< } // Write the response. - ctx.nextOutboundMessageBuffer().add(response); + out.add(response); - // Close the non-keep-alive connection after the write operation is done. - if (!keepAlive) { - ctx.flush().addListener(ChannelFutureListener.CLOSE); - } + return keepAlive; } - private static void send100Continue(ChannelHandlerContext ctx) { + private static void send100Continue(MessageList out) { FullHttpResponse response = new DefaultFullHttpResponse(HTTP_1_1, CONTINUE); - ctx.nextOutboundMessageBuffer().add(response); + out.add(response); } @Override - public void endMessageReceived(ChannelHandlerContext ctx) throws Exception { - ctx.flush(); - } - - @Override - public void exceptionCaught( - ChannelHandlerContext ctx, Throwable cause) throws Exception { + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { cause.printStackTrace(); ctx.close(); } diff --git a/example/src/main/java/io/netty/example/http/upload/HttpUploadClientHandler.java b/example/src/main/java/io/netty/example/http/upload/HttpUploadClientHandler.java index 0c3ee3c258..6c70bd6ebd 100644 --- a/example/src/main/java/io/netty/example/http/upload/HttpUploadClientHandler.java +++ b/example/src/main/java/io/netty/example/http/upload/HttpUploadClientHandler.java @@ -16,7 +16,8 @@ package io.netty.example.http.upload; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.MessageList; import io.netty.handler.codec.http.HttpContent; import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpResponse; @@ -28,50 +29,54 @@ import java.util.logging.Logger; /** * Handler that just dumps the contents of the response from the server */ -public class HttpUploadClientHandler extends ChannelInboundMessageHandlerAdapter { +public class HttpUploadClientHandler extends ChannelInboundHandlerAdapter { private static final Logger logger = Logger.getLogger(HttpUploadClientHandler.class.getName()); private boolean readingChunks; @Override - public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { - if (msg instanceof HttpResponse) { - HttpResponse response = (HttpResponse) msg; + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + for (int i = 0; i < msgs.size(); i++) { + Object msg = msgs.get(i); + if (msg instanceof HttpResponse) { + HttpResponse response = (HttpResponse) msg; - logger.info("STATUS: " + response.getStatus()); - logger.info("VERSION: " + response.getProtocolVersion()); + logger.info("STATUS: " + response.getStatus()); + logger.info("VERSION: " + response.getProtocolVersion()); - if (!response.headers().isEmpty()) { - for (String name : response.headers().names()) { - for (String value : response.headers().getAll(name)) { - logger.info("HEADER: " + name + " = " + value); + if (!response.headers().isEmpty()) { + for (String name : response.headers().names()) { + for (String value : response.headers().getAll(name)) { + logger.info("HEADER: " + name + " = " + value); + } } } - } - if (response.getStatus().code() == 200 && HttpHeaders.isTransferEncodingChunked(response)) { - readingChunks = true; - logger.info("CHUNKED CONTENT {"); - } else { - logger.info("CONTENT {"); - } - } - if (msg instanceof HttpContent) { - HttpContent chunk = (HttpContent) msg; - logger.info(chunk.content().toString(CharsetUtil.UTF_8)); - - if (chunk instanceof LastHttpContent) { - if (readingChunks) { - logger.info("} END OF CHUNKED CONTENT"); + if (response.getStatus().code() == 200 && HttpHeaders.isTransferEncodingChunked(response)) { + readingChunks = true; + logger.info("CHUNKED CONTENT {"); } else { - logger.info("} END OF CONTENT"); + logger.info("CONTENT {"); } - readingChunks = false; - } else { + } + if (msg instanceof HttpContent) { + HttpContent chunk = (HttpContent) msg; logger.info(chunk.content().toString(CharsetUtil.UTF_8)); + + if (chunk instanceof LastHttpContent) { + if (readingChunks) { + logger.info("} END OF CHUNKED CONTENT"); + } else { + logger.info("} END OF CONTENT"); + } + readingChunks = false; + } else { + logger.info(chunk.content().toString(CharsetUtil.UTF_8)); + } } } + msgs.releaseAllAndRecycle(); } @Override diff --git a/example/src/main/java/io/netty/example/http/upload/HttpUploadServerHandler.java b/example/src/main/java/io/netty/example/http/upload/HttpUploadServerHandler.java index 6297213990..63adb8523f 100644 --- a/example/src/main/java/io/netty/example/http/upload/HttpUploadServerHandler.java +++ b/example/src/main/java/io/netty/example/http/upload/HttpUploadServerHandler.java @@ -15,13 +15,13 @@ */ package io.netty.example.http.upload; -import io.netty.buffer.BufUtil; import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.MessageList; import io.netty.handler.codec.http.Cookie; import io.netty.handler.codec.http.CookieDecoder; import io.netty.handler.codec.http.DefaultFullHttpResponse; @@ -62,7 +62,7 @@ import java.util.logging.Logger; import static io.netty.buffer.Unpooled.*; import static io.netty.handler.codec.http.HttpHeaders.Names.*; -public class HttpUploadServerHandler extends ChannelInboundMessageHandlerAdapter { +public class HttpUploadServerHandler extends ChannelInboundHandlerAdapter { private static final Logger logger = Logger.getLogger(HttpUploadServerHandler.class.getName()); @@ -96,110 +96,114 @@ public class HttpUploadServerHandler extends ChannelInboundMessageHandlerAdapter } @Override - public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { - if (msg instanceof HttpRequest) { - HttpRequest request = this.request = (HttpRequest) msg; - URI uri = new URI(request.getUri()); - if (!uri.getPath().startsWith("/form")) { - // Write Menu - writeMenu(ctx); - return; - } - responseContent.setLength(0); - responseContent.append("WELCOME TO THE WILD WILD WEB SERVER\r\n"); - responseContent.append("===================================\r\n"); - - responseContent.append("VERSION: " + request.getProtocolVersion().text() + "\r\n"); - - responseContent.append("REQUEST_URI: " + request.getUri() + "\r\n\r\n"); - responseContent.append("\r\n\r\n"); - - // new getMethod - List> headers = request.headers().entries(); - for (Entry entry : headers) { - responseContent.append("HEADER: " + entry.getKey() + '=' + entry.getValue() + "\r\n"); - } - responseContent.append("\r\n\r\n"); - - // new getMethod - Set cookies; - String value = request.headers().get(COOKIE); - if (value == null) { - cookies = Collections.emptySet(); - } else { - cookies = CookieDecoder.decode(value); - } - for (Cookie cookie : cookies) { - responseContent.append("COOKIE: " + cookie.toString() + "\r\n"); - } - responseContent.append("\r\n\r\n"); - - QueryStringDecoder decoderQuery = new QueryStringDecoder(request.getUri()); - Map> uriAttributes = decoderQuery.parameters(); - for (Entry> attr: uriAttributes.entrySet()) { - for (String attrVal: attr.getValue()) { - responseContent.append("URI: " + attr.getKey() + '=' + attrVal + "\r\n"); + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + for (int i = 0; i < msgs.size(); i++) { + Object msg = msgs.get(i); + if (msg instanceof HttpRequest) { + HttpRequest request = this.request = (HttpRequest) msg; + URI uri = new URI(request.getUri()); + if (!uri.getPath().startsWith("/form")) { + // Write Menu + writeMenu(ctx); + return; } - } - responseContent.append("\r\n\r\n"); + responseContent.setLength(0); + responseContent.append("WELCOME TO THE WILD WILD WEB SERVER\r\n"); + responseContent.append("===================================\r\n"); - // if GET Method: should not try to create a HttpPostRequestDecoder - try { - decoder = new HttpPostRequestDecoder(factory, request); - } catch (ErrorDataDecoderException e1) { - e1.printStackTrace(); - responseContent.append(e1.getMessage()); - writeResponse(ctx.channel()); - ctx.channel().close(); - return; - } catch (IncompatibleDataDecoderException e1) { - // GET Method: should not try to create a HttpPostRequestDecoder - // So OK but stop here - responseContent.append(e1.getMessage()); - responseContent.append("\r\n\r\nEND OF GET CONTENT\r\n"); - writeResponse(ctx.channel()); - return; - } + responseContent.append("VERSION: " + request.getProtocolVersion().text() + "\r\n"); - readingChunks = HttpHeaders.isTransferEncodingChunked(request); - responseContent.append("Is Chunked: " + readingChunks + "\r\n"); - responseContent.append("IsMultipart: " + decoder.isMultipart() + "\r\n"); - if (readingChunks) { - // Chunk version - responseContent.append("Chunks: "); - readingChunks = true; - } - } + responseContent.append("REQUEST_URI: " + request.getUri() + "\r\n\r\n"); + responseContent.append("\r\n\r\n"); - // check if the decoder was constructed before - // if not it handles the form get - if (decoder != null) { - if (msg instanceof HttpContent) { - // New chunk is received - HttpContent chunk = (HttpContent) msg; + // new getMethod + List> headers = request.headers().entries(); + for (Entry entry : headers) { + responseContent.append("HEADER: " + entry.getKey() + '=' + entry.getValue() + "\r\n"); + } + responseContent.append("\r\n\r\n"); + + // new getMethod + Set cookies; + String value = request.headers().get(COOKIE); + if (value == null) { + cookies = Collections.emptySet(); + } else { + cookies = CookieDecoder.decode(value); + } + for (Cookie cookie : cookies) { + responseContent.append("COOKIE: " + cookie.toString() + "\r\n"); + } + responseContent.append("\r\n\r\n"); + + QueryStringDecoder decoderQuery = new QueryStringDecoder(request.getUri()); + Map> uriAttributes = decoderQuery.parameters(); + for (Entry> attr: uriAttributes.entrySet()) { + for (String attrVal: attr.getValue()) { + responseContent.append("URI: " + attr.getKey() + '=' + attrVal + "\r\n"); + } + } + responseContent.append("\r\n\r\n"); + + // if GET Method: should not try to create a HttpPostRequestDecoder try { - decoder.offer(chunk); + decoder = new HttpPostRequestDecoder(factory, request); } catch (ErrorDataDecoderException e1) { e1.printStackTrace(); responseContent.append(e1.getMessage()); writeResponse(ctx.channel()); ctx.channel().close(); return; - } - responseContent.append('o'); - // example of reading chunk by chunk (minimize memory usage due to - // Factory) - readHttpDataChunkByChunk(); - // example of reading only if at the end - if (chunk instanceof LastHttpContent) { - readHttpDataAllReceive(ctx.channel()); + } catch (IncompatibleDataDecoderException e1) { + // GET Method: should not try to create a HttpPostRequestDecoder + // So OK but stop here + responseContent.append(e1.getMessage()); + responseContent.append("\r\n\r\nEND OF GET CONTENT\r\n"); writeResponse(ctx.channel()); - readingChunks = false; + return; + } - reset(); + readingChunks = HttpHeaders.isTransferEncodingChunked(request); + responseContent.append("Is Chunked: " + readingChunks + "\r\n"); + responseContent.append("IsMultipart: " + decoder.isMultipart() + "\r\n"); + if (readingChunks) { + // Chunk version + responseContent.append("Chunks: "); + readingChunks = true; + } + } + + // check if the decoder was constructed before + // if not it handles the form get + if (decoder != null) { + if (msg instanceof HttpContent) { + // New chunk is received + HttpContent chunk = (HttpContent) msg; + try { + decoder.offer(chunk); + } catch (ErrorDataDecoderException e1) { + e1.printStackTrace(); + responseContent.append(e1.getMessage()); + writeResponse(ctx.channel()); + ctx.channel().close(); + return; + } + responseContent.append('o'); + // example of reading chunk by chunk (minimize memory usage due to + // Factory) + readHttpDataChunkByChunk(); + // example of reading only if at the end + if (chunk instanceof LastHttpContent) { + readHttpDataAllReceive(ctx.channel()); + writeResponse(ctx.channel()); + readingChunks = false; + + reset(); + } } } } + msgs.releaseAllAndRecycle(); } private void reset() { diff --git a/example/src/main/java/io/netty/example/http/websocketx/autobahn/AutobahnServerHandler.java b/example/src/main/java/io/netty/example/http/websocketx/autobahn/AutobahnServerHandler.java index 301d5f41fc..c106da74a3 100644 --- a/example/src/main/java/io/netty/example/http/websocketx/autobahn/AutobahnServerHandler.java +++ b/example/src/main/java/io/netty/example/http/websocketx/autobahn/AutobahnServerHandler.java @@ -19,7 +19,8 @@ import io.netty.buffer.Unpooled; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.MessageList; import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpResponse; @@ -46,21 +47,28 @@ import static io.netty.handler.codec.http.HttpVersion.*; /** * Handles handshakes and messages */ -public class AutobahnServerHandler extends ChannelInboundMessageHandlerAdapter { +public class AutobahnServerHandler extends ChannelInboundHandlerAdapter { private static final Logger logger = Logger.getLogger(AutobahnServerHandler.class.getName()); private WebSocketServerHandshaker handshaker; @Override - public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { - if (msg instanceof FullHttpRequest) { - handleHttpRequest(ctx, (FullHttpRequest) msg); - } else if (msg instanceof WebSocketFrame) { - handleWebSocketFrame(ctx, (WebSocketFrame) msg); + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + MessageList out = MessageList.newInstance(msgs.size()); + for (int i = 0; i < msgs.size(); i++) { + Object msg = msgs.get(i); + if (msg instanceof FullHttpRequest) { + handleHttpRequest(ctx, (FullHttpRequest) msg); + } else if (msg instanceof WebSocketFrame) { + handleWebSocketFrame(ctx, (WebSocketFrame) msg, out); + } } + msgs.recycle(); + ctx.write(out); } - private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) throws Exception { + private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) + throws Exception { // Handle a bad request. if (!req.getDecoderResult().isSuccess()) { sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, BAD_REQUEST)); @@ -84,24 +92,24 @@ public class AutobahnServerHandler extends ChannelInboundMessageHandlerAdapter out) { if (logger.isLoggable(Level.FINE)) { logger.fine(String.format( "Channel %s received %s", ctx.channel().id(), frame.getClass().getSimpleName())); } if (frame instanceof CloseWebSocketFrame) { - handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain()); + handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame); } else if (frame instanceof PingWebSocketFrame) { - ctx.nextOutboundMessageBuffer().add( - new PongWebSocketFrame(frame.isFinalFragment(), frame.rsv(), frame.content().retain())); + out.add(new PongWebSocketFrame(frame.isFinalFragment(), frame.rsv(), frame.content())); } else if (frame instanceof TextWebSocketFrame) { - ctx.nextOutboundMessageBuffer().add(frame.retain()); + out.add(frame); } else if (frame instanceof BinaryWebSocketFrame) { - ctx.nextOutboundMessageBuffer().add(frame.retain()); + out.add(frame); } else if (frame instanceof ContinuationWebSocketFrame) { - ctx.nextOutboundMessageBuffer().add(frame.retain()); + out.add(frame); } else if (frame instanceof PongWebSocketFrame) { + frame.release(); // Ignore } else { throw new UnsupportedOperationException(String.format("%s frame types not supported", frame.getClass() @@ -109,13 +117,6 @@ public class AutobahnServerHandler extends ChannelInboundMessageHandlerAdapter { +public class WebSocketClientHandler extends ChannelInboundHandlerAdapter { private final WebSocketClientHandshaker handshaker; private ChannelPromise handshakeFuture; @@ -79,31 +80,35 @@ public class WebSocketClientHandler extends ChannelInboundMessageHandlerAdapter< } @Override - public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { - Channel ch = ctx.channel(); - if (!handshaker.isHandshakeComplete()) { - handshaker.finishHandshake(ch, (FullHttpResponse) msg); - System.out.println("WebSocket Client connected!"); - handshakeFuture.setSuccess(); - return; - } + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + for (int i = 0; i < msgs.size(); i++) { + Object msg = msgs.get(i); + Channel ch = ctx.channel(); + if (!handshaker.isHandshakeComplete()) { + handshaker.finishHandshake(ch, (FullHttpResponse) msg); + System.out.println("WebSocket Client connected!"); + handshakeFuture.setSuccess(); + continue; + } - if (msg instanceof FullHttpResponse) { - FullHttpResponse response = (FullHttpResponse) msg; - throw new Exception("Unexpected FullHttpResponse (getStatus=" + response.getStatus() + ", content=" - + response.content().toString(CharsetUtil.UTF_8) + ')'); - } + if (msg instanceof FullHttpResponse) { + FullHttpResponse response = (FullHttpResponse) msg; + throw new Exception("Unexpected FullHttpResponse (getStatus=" + response.getStatus() + ", content=" + + response.content().toString(CharsetUtil.UTF_8) + ')'); + } - WebSocketFrame frame = (WebSocketFrame) msg; - if (frame instanceof TextWebSocketFrame) { - TextWebSocketFrame textFrame = (TextWebSocketFrame) frame; - System.out.println("WebSocket Client received message: " + textFrame.text()); - } else if (frame instanceof PongWebSocketFrame) { - System.out.println("WebSocket Client received pong"); - } else if (frame instanceof CloseWebSocketFrame) { - System.out.println("WebSocket Client received closing"); - ch.close(); + WebSocketFrame frame = (WebSocketFrame) msg; + if (frame instanceof TextWebSocketFrame) { + TextWebSocketFrame textFrame = (TextWebSocketFrame) frame; + System.out.println("WebSocket Client received message: " + textFrame.text()); + } else if (frame instanceof PongWebSocketFrame) { + System.out.println("WebSocket Client received pong"); + } else if (frame instanceof CloseWebSocketFrame) { + System.out.println("WebSocket Client received closing"); + ch.close(); + } } + msgs.releaseAllAndRecycle(); } @Override diff --git a/example/src/main/java/io/netty/example/http/websocketx/html5/CustomTextFrameHandler.java b/example/src/main/java/io/netty/example/http/websocketx/html5/CustomTextFrameHandler.java index 7ee566d137..f3e62a373b 100644 --- a/example/src/main/java/io/netty/example/http/websocketx/html5/CustomTextFrameHandler.java +++ b/example/src/main/java/io/netty/example/http/websocketx/html5/CustomTextFrameHandler.java @@ -16,10 +16,10 @@ package io.netty.example.http.websocketx.html5; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.SimpleChannelInboundHandler; import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; -public class CustomTextFrameHandler extends ChannelInboundMessageHandlerAdapter { +public class CustomTextFrameHandler extends SimpleChannelInboundHandler { @Override public void messageReceived(ChannelHandlerContext ctx, TextWebSocketFrame frame) throws Exception { diff --git a/example/src/main/java/io/netty/example/http/websocketx/server/WebSocketServerHandler.java b/example/src/main/java/io/netty/example/http/websocketx/server/WebSocketServerHandler.java index 4eb39247ab..0c6f130502 100644 --- a/example/src/main/java/io/netty/example/http/websocketx/server/WebSocketServerHandler.java +++ b/example/src/main/java/io/netty/example/http/websocketx/server/WebSocketServerHandler.java @@ -20,7 +20,8 @@ import io.netty.buffer.Unpooled; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.MessageList; import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpResponse; @@ -45,7 +46,7 @@ import static io.netty.handler.codec.http.HttpVersion.*; /** * Handles handshakes and messages */ -public class WebSocketServerHandler extends ChannelInboundMessageHandlerAdapter { +public class WebSocketServerHandler extends ChannelInboundHandlerAdapter { private static final Logger logger = Logger.getLogger(WebSocketServerHandler.class.getName()); private static final String WEBSOCKET_PATH = "/websocket"; @@ -53,12 +54,16 @@ public class WebSocketServerHandler extends ChannelInboundMessageHandlerAdapter< private WebSocketServerHandshaker handshaker; @Override - public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { - if (msg instanceof FullHttpRequest) { - handleHttpRequest(ctx, (FullHttpRequest) msg); - } else if (msg instanceof WebSocketFrame) { - handleWebSocketFrame(ctx, (WebSocketFrame) msg); + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + for (int i = 0; i < msgs.size(); i++) { + Object msg = msgs.get(i); + if (msg instanceof FullHttpRequest) { + handleHttpRequest(ctx, (FullHttpRequest) msg); + } else if (msg instanceof WebSocketFrame) { + handleWebSocketFrame(ctx, (WebSocketFrame) msg); + } } + msgs.releaseAllAndRecycle(); } private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) throws Exception { @@ -106,13 +111,11 @@ public class WebSocketServerHandler extends ChannelInboundMessageHandlerAdapter< // Check for closing frame if (frame instanceof CloseWebSocketFrame) { - frame.retain(); - handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame); + handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain()); return; } if (frame instanceof PingWebSocketFrame) { - frame.content().retain(); - ctx.channel().write(new PongWebSocketFrame(frame.content())); + ctx.channel().write(new PongWebSocketFrame(frame.content().retain())); return; } if (!(frame instanceof TextWebSocketFrame)) { diff --git a/example/src/main/java/io/netty/example/http/websocketx/sslserver/WebSocketSslServerHandler.java b/example/src/main/java/io/netty/example/http/websocketx/sslserver/WebSocketSslServerHandler.java index a49b43eafa..050dd6200e 100644 --- a/example/src/main/java/io/netty/example/http/websocketx/sslserver/WebSocketSslServerHandler.java +++ b/example/src/main/java/io/netty/example/http/websocketx/sslserver/WebSocketSslServerHandler.java @@ -20,7 +20,8 @@ import io.netty.buffer.Unpooled; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.MessageList; import io.netty.example.http.websocketx.server.WebSocketServerIndexPage; import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.FullHttpRequest; @@ -46,7 +47,7 @@ import static io.netty.handler.codec.http.HttpVersion.*; /** * Handles handshakes and messages */ -public class WebSocketSslServerHandler extends ChannelInboundMessageHandlerAdapter { +public class WebSocketSslServerHandler extends ChannelInboundHandlerAdapter { private static final Logger logger = Logger.getLogger(WebSocketSslServerHandler.class.getName()); private static final String WEBSOCKET_PATH = "/websocket"; @@ -54,12 +55,16 @@ public class WebSocketSslServerHandler extends ChannelInboundMessageHandlerAdapt private WebSocketServerHandshaker handshaker; @Override - public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { - if (msg instanceof FullHttpRequest) { - handleHttpRequest(ctx, (FullHttpRequest) msg); - } else if (msg instanceof WebSocketFrame) { - handleWebSocketFrame(ctx, (WebSocketFrame) msg); + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + for (int i = 0; i < msgs.size(); i++) { + Object msg = msgs.get(i); + if (msg instanceof FullHttpRequest) { + handleHttpRequest(ctx, (FullHttpRequest) msg); + } else if (msg instanceof WebSocketFrame) { + handleWebSocketFrame(ctx, (WebSocketFrame) msg); + } } + msgs.releaseAllAndRecycle(); } private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest req) throws Exception { @@ -108,13 +113,11 @@ public class WebSocketSslServerHandler extends ChannelInboundMessageHandlerAdapt // Check for closing frame if (frame instanceof CloseWebSocketFrame) { - frame.retain(); - handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame); + handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain()); return; } if (frame instanceof PingWebSocketFrame) { - frame.content().retain(); - ctx.channel().write(new PongWebSocketFrame(frame.content())); + ctx.channel().write(new PongWebSocketFrame(frame.content().retain())); return; } if (!(frame instanceof TextWebSocketFrame)) { diff --git a/example/src/main/java/io/netty/example/localecho/LocalEchoClientHandler.java b/example/src/main/java/io/netty/example/localecho/LocalEchoClientHandler.java index f6129b4ee6..c10774b644 100644 --- a/example/src/main/java/io/netty/example/localecho/LocalEchoClientHandler.java +++ b/example/src/main/java/io/netty/example/localecho/LocalEchoClientHandler.java @@ -16,13 +16,18 @@ package io.netty.example.localecho; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.MessageList; + +public class LocalEchoClientHandler extends ChannelInboundHandlerAdapter { -public class LocalEchoClientHandler extends ChannelInboundMessageHandlerAdapter { @Override - public void messageReceived(ChannelHandlerContext ctx, String msg) { - // Print as received - System.out.println(msg); + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + for (int i = 0; i < msgs.size(); i++) { + // Print as received + System.out.println(msgs.get(i)); + } + msgs.releaseAllAndRecycle(); } @Override diff --git a/example/src/main/java/io/netty/example/localecho/LocalEchoServerHandler.java b/example/src/main/java/io/netty/example/localecho/LocalEchoServerHandler.java index 40f3d0cfbb..861e62d01f 100644 --- a/example/src/main/java/io/netty/example/localecho/LocalEchoServerHandler.java +++ b/example/src/main/java/io/netty/example/localecho/LocalEchoServerHandler.java @@ -16,14 +16,15 @@ package io.netty.example.localecho; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.MessageList; -public class LocalEchoServerHandler extends ChannelInboundMessageHandlerAdapter { +public class LocalEchoServerHandler extends ChannelInboundHandlerAdapter { @Override - public void messageReceived(ChannelHandlerContext ctx, String msg) { + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { // Write back as received - ctx.write(msg); + ctx.write(msgs.copy()); } @Override diff --git a/example/src/main/java/io/netty/example/objectecho/ObjectEchoClientHandler.java b/example/src/main/java/io/netty/example/objectecho/ObjectEchoClientHandler.java index 62acef8ddd..f4e2869237 100644 --- a/example/src/main/java/io/netty/example/objectecho/ObjectEchoClientHandler.java +++ b/example/src/main/java/io/netty/example/objectecho/ObjectEchoClientHandler.java @@ -16,7 +16,8 @@ package io.netty.example.objectecho; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.MessageList; import java.util.ArrayList; import java.util.List; @@ -28,7 +29,7 @@ import java.util.logging.Logger; * ping-pong traffic between the object echo client and server by sending the * first message to the server. */ -public class ObjectEchoClientHandler extends ChannelInboundMessageHandlerAdapter> { +public class ObjectEchoClientHandler extends ChannelInboundHandlerAdapter { private static final Logger logger = Logger.getLogger( ObjectEchoClientHandler.class.getName()); @@ -56,9 +57,9 @@ public class ObjectEchoClientHandler extends ChannelInboundMessageHandlerAdapter } @Override - public void messageReceived(ChannelHandlerContext ctx, List msg) throws Exception { + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { // Echo back the received object to the server. - ctx.write(msg); + ctx.write(msgs); } @Override diff --git a/example/src/main/java/io/netty/example/objectecho/ObjectEchoServerHandler.java b/example/src/main/java/io/netty/example/objectecho/ObjectEchoServerHandler.java index 712da3986a..f5f4d2ffc5 100644 --- a/example/src/main/java/io/netty/example/objectecho/ObjectEchoServerHandler.java +++ b/example/src/main/java/io/netty/example/objectecho/ObjectEchoServerHandler.java @@ -16,9 +16,9 @@ package io.netty.example.objectecho; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.MessageList; -import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; @@ -26,16 +26,16 @@ import java.util.logging.Logger; * Handles both client-side and server-side handler depending on which * constructor was called. */ -public class ObjectEchoServerHandler extends ChannelInboundMessageHandlerAdapter> { +public class ObjectEchoServerHandler extends ChannelInboundHandlerAdapter { private static final Logger logger = Logger.getLogger( ObjectEchoServerHandler.class.getName()); @Override public void messageReceived( - ChannelHandlerContext ctx, List msg) throws Exception { + ChannelHandlerContext ctx, MessageList msgs) throws Exception { // Echo back the received object to the client. - ctx.write(msg); + ctx.write(msgs); } @Override diff --git a/example/src/main/java/io/netty/example/portunification/PortUnificationServerHandler.java b/example/src/main/java/io/netty/example/portunification/PortUnificationServerHandler.java index 71a0e9305e..4594fea6f0 100644 --- a/example/src/main/java/io/netty/example/portunification/PortUnificationServerHandler.java +++ b/example/src/main/java/io/netty/example/portunification/PortUnificationServerHandler.java @@ -17,13 +17,14 @@ package io.netty.example.portunification; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundByteHandlerAdapter; import io.netty.channel.ChannelPipeline; +import io.netty.channel.MessageList; import io.netty.example.factorial.BigIntegerDecoder; import io.netty.example.factorial.FactorialServerHandler; import io.netty.example.factorial.NumberEncoder; import io.netty.example.http.snoop.HttpSnoopServerHandler; import io.netty.example.securechat.SecureChatSslContextFactory; +import io.netty.handler.codec.ByteToMessageDecoder; import io.netty.handler.codec.compression.ZlibCodecFactory; import io.netty.handler.codec.compression.ZlibWrapper; import io.netty.handler.codec.http.HttpContentCompressor; @@ -37,7 +38,7 @@ import javax.net.ssl.SSLEngine; * Manipulates the current pipeline dynamically to switch protocols or enable * SSL or GZIP. */ -public class PortUnificationServerHandler extends ChannelInboundByteHandlerAdapter { +public class PortUnificationServerHandler extends ByteToMessageDecoder { private final boolean detectSsl; private final boolean detectGzip; @@ -52,7 +53,7 @@ public class PortUnificationServerHandler extends ChannelInboundByteHandlerAdapt } @Override - public void inboundBufferUpdated(ChannelHandlerContext ctx, ByteBuf in) throws Exception { + protected void decode(ChannelHandlerContext ctx, ByteBuf in, MessageList out) throws Exception { // Will use the first five bytes to detect a protocol. if (in.readableBytes() < 5) { return; diff --git a/example/src/main/java/io/netty/example/proxy/HexDumpProxyBackendHandler.java b/example/src/main/java/io/netty/example/proxy/HexDumpProxyBackendHandler.java index 614395a56e..cb917efd81 100644 --- a/example/src/main/java/io/netty/example/proxy/HexDumpProxyBackendHandler.java +++ b/example/src/main/java/io/netty/example/proxy/HexDumpProxyBackendHandler.java @@ -15,14 +15,15 @@ */ package io.netty.example.proxy; -import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundByteHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.MessageList; -public class HexDumpProxyBackendHandler extends ChannelInboundByteHandlerAdapter { +public class HexDumpProxyBackendHandler extends ChannelInboundHandlerAdapter { private final Channel inboundChannel; @@ -33,14 +34,12 @@ public class HexDumpProxyBackendHandler extends ChannelInboundByteHandlerAdapter @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { ctx.read(); - ctx.flush(); + ctx.write(Unpooled.EMPTY_BUFFER); } @Override - public void inboundBufferUpdated(final ChannelHandlerContext ctx, ByteBuf in) throws Exception { - ByteBuf out = inboundChannel.outboundByteBuffer(); - out.writeBytes(in); - inboundChannel.flush().addListener(new ChannelFutureListener() { + public void messageReceived(final ChannelHandlerContext ctx, MessageList msgs) throws Exception { + inboundChannel.write(msgs).addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { if (future.isSuccess()) { diff --git a/example/src/main/java/io/netty/example/proxy/HexDumpProxyFrontendHandler.java b/example/src/main/java/io/netty/example/proxy/HexDumpProxyFrontendHandler.java index 5fd535a2b3..26b83d63dc 100644 --- a/example/src/main/java/io/netty/example/proxy/HexDumpProxyFrontendHandler.java +++ b/example/src/main/java/io/netty/example/proxy/HexDumpProxyFrontendHandler.java @@ -16,15 +16,16 @@ package io.netty.example.proxy; import io.netty.bootstrap.Bootstrap; -import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundByteHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelOption; +import io.netty.channel.MessageList; -public class HexDumpProxyFrontendHandler extends ChannelInboundByteHandlerAdapter { +public class HexDumpProxyFrontendHandler extends ChannelInboundHandlerAdapter { private final String remoteHost; private final int remotePort; @@ -63,11 +64,9 @@ public class HexDumpProxyFrontendHandler extends ChannelInboundByteHandlerAdapte } @Override - public void inboundBufferUpdated(final ChannelHandlerContext ctx, ByteBuf in) throws Exception { - ByteBuf out = outboundChannel.outboundByteBuffer(); - out.writeBytes(in); + public void messageReceived(final ChannelHandlerContext ctx, MessageList msgs) throws Exception { if (outboundChannel.isActive()) { - outboundChannel.flush().addListener(new ChannelFutureListener() { + outboundChannel.write(msgs).addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { if (future.isSuccess()) { @@ -99,7 +98,7 @@ public class HexDumpProxyFrontendHandler extends ChannelInboundByteHandlerAdapte */ static void closeOnFlush(Channel ch) { if (ch.isActive()) { - ch.flush().addListener(ChannelFutureListener.CLOSE); + ch.write(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE); } } } diff --git a/example/src/main/java/io/netty/example/qotm/QuoteOfTheMomentClientHandler.java b/example/src/main/java/io/netty/example/qotm/QuoteOfTheMomentClientHandler.java index cd0a4a1c3b..640a6a8b13 100644 --- a/example/src/main/java/io/netty/example/qotm/QuoteOfTheMomentClientHandler.java +++ b/example/src/main/java/io/netty/example/qotm/QuoteOfTheMomentClientHandler.java @@ -16,21 +16,24 @@ package io.netty.example.qotm; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.MessageList; import io.netty.channel.socket.DatagramPacket; import io.netty.util.CharsetUtil; -public class QuoteOfTheMomentClientHandler extends ChannelInboundMessageHandlerAdapter { +public class QuoteOfTheMomentClientHandler extends ChannelInboundHandlerAdapter { @Override - public void messageReceived( - ChannelHandlerContext ctx, DatagramPacket msg) - throws Exception { - String response = msg.content().toString(CharsetUtil.UTF_8); - if (response.startsWith("QOTM: ")) { - System.out.println("Quote of the Moment: " + response.substring(6)); - ctx.close(); + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + MessageList packets = msgs.cast(); + for (int i = 0; i < packets.size(); i++) { + String response = packets.get(i).content().toString(CharsetUtil.UTF_8); + if (response.startsWith("QOTM: ")) { + System.out.println("Quote of the Moment: " + response.substring(6)); + ctx.close(); + } } + msgs.releaseAllAndRecycle(); } @Override diff --git a/example/src/main/java/io/netty/example/qotm/QuoteOfTheMomentServerHandler.java b/example/src/main/java/io/netty/example/qotm/QuoteOfTheMomentServerHandler.java index 514030a9ea..ed0c54c487 100644 --- a/example/src/main/java/io/netty/example/qotm/QuoteOfTheMomentServerHandler.java +++ b/example/src/main/java/io/netty/example/qotm/QuoteOfTheMomentServerHandler.java @@ -17,13 +17,14 @@ package io.netty.example.qotm; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.MessageList; import io.netty.channel.socket.DatagramPacket; import io.netty.util.CharsetUtil; import java.util.Random; -public class QuoteOfTheMomentServerHandler extends ChannelInboundMessageHandlerAdapter { +public class QuoteOfTheMomentServerHandler extends ChannelInboundHandlerAdapter { private static final Random random = new Random(); @@ -44,12 +45,17 @@ public class QuoteOfTheMomentServerHandler extends ChannelInboundMessageHandlerA } @Override - public void messageReceived(ChannelHandlerContext ctx, DatagramPacket msg) throws Exception { - System.err.println(msg); - if ("QOTM?".equals(msg.content().toString(CharsetUtil.UTF_8))) { - ctx.write(new DatagramPacket( - Unpooled.copiedBuffer("QOTM: " + nextQuote(), CharsetUtil.UTF_8), msg.sender())); + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + MessageList packets = msgs.cast(); + for (int i = 0; i < packets.size(); i++) { + DatagramPacket packet = packets.get(i); + System.err.println(packet); + if ("QOTM?".equals(packet.content().toString(CharsetUtil.UTF_8))) { + ctx.write(new DatagramPacket( + Unpooled.copiedBuffer("QOTM: " + nextQuote(), CharsetUtil.UTF_8), packet.sender())); + } } + msgs.releaseAllAndRecycle(); } @Override diff --git a/example/src/main/java/io/netty/example/rxtx/RxtxClientHandler.java b/example/src/main/java/io/netty/example/rxtx/RxtxClientHandler.java index 01bf057bfd..34885c32e4 100644 --- a/example/src/main/java/io/netty/example/rxtx/RxtxClientHandler.java +++ b/example/src/main/java/io/netty/example/rxtx/RxtxClientHandler.java @@ -16,9 +16,10 @@ package io.netty.example.rxtx; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.MessageList; -public class RxtxClientHandler extends ChannelInboundMessageHandlerAdapter { +public class RxtxClientHandler extends ChannelInboundHandlerAdapter { @Override public void channelActive(ChannelHandlerContext ctx) { @@ -26,12 +27,16 @@ public class RxtxClientHandler extends ChannelInboundMessageHandlerAdapter msgs) throws Exception { + for (int i = 0; i < msgs.size(); i++) { + String msg = msgs.get(i).toString(); + if ("OK".equals(msg)) { + System.out.println("Serial port responded to AT"); + } else { + System.out.println("Serial port responded with not-OK: " + msg); + } } + msgs.releaseAllAndRecycle(); ctx.close(); } } diff --git a/example/src/main/java/io/netty/example/sctp/SctpEchoClientHandler.java b/example/src/main/java/io/netty/example/sctp/SctpEchoClientHandler.java index 341cfa921d..79ed5b2f14 100644 --- a/example/src/main/java/io/netty/example/sctp/SctpEchoClientHandler.java +++ b/example/src/main/java/io/netty/example/sctp/SctpEchoClientHandler.java @@ -16,10 +16,11 @@ package io.netty.example.sctp; import io.netty.buffer.ByteBuf; -import io.netty.buffer.MessageBuf; +import io.netty.buffer.ByteBufUtil; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.MessageList; import io.netty.channel.sctp.SctpMessage; import java.util.logging.Level; @@ -30,7 +31,7 @@ import java.util.logging.Logger; * traffic between the echo client and server by sending the first message to * the server. */ -public class SctpEchoClientHandler extends ChannelInboundMessageHandlerAdapter { +public class SctpEchoClientHandler extends ChannelInboundHandlerAdapter { private static final Logger logger = Logger.getLogger( SctpEchoClientHandler.class.getName()); @@ -56,10 +57,8 @@ public class SctpEchoClientHandler extends ChannelInboundMessageHandlerAdapter out = ctx.nextOutboundMessageBuffer(); - out.add(msg); - ctx.flush(); + public void messageReceived(ChannelHandlerContext ctx, MessageList messages) throws Exception { + ctx.write(messages); } @Override diff --git a/example/src/main/java/io/netty/example/sctp/SctpEchoServerHandler.java b/example/src/main/java/io/netty/example/sctp/SctpEchoServerHandler.java index 3b853c739f..1972c56bb4 100644 --- a/example/src/main/java/io/netty/example/sctp/SctpEchoServerHandler.java +++ b/example/src/main/java/io/netty/example/sctp/SctpEchoServerHandler.java @@ -15,11 +15,10 @@ */ package io.netty.example.sctp; -import io.netty.buffer.MessageBuf; import io.netty.channel.ChannelHandler.Sharable; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; -import io.netty.channel.sctp.SctpMessage; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.MessageList; import java.util.logging.Level; import java.util.logging.Logger; @@ -28,7 +27,7 @@ import java.util.logging.Logger; * Handler implementation for the SCTP echo server. */ @Sharable -public class SctpEchoServerHandler extends ChannelInboundMessageHandlerAdapter { +public class SctpEchoServerHandler extends ChannelInboundHandlerAdapter { private static final Logger logger = Logger.getLogger( SctpEchoServerHandler.class.getName()); @@ -41,9 +40,7 @@ public class SctpEchoServerHandler extends ChannelInboundMessageHandlerAdapter out = ctx.nextOutboundMessageBuffer(); - out.add(msg); - ctx.flush(); + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + ctx.write(msgs); } } diff --git a/example/src/main/java/io/netty/example/securechat/SecureChatClientHandler.java b/example/src/main/java/io/netty/example/securechat/SecureChatClientHandler.java index 4573fd6c75..99efe31b72 100644 --- a/example/src/main/java/io/netty/example/securechat/SecureChatClientHandler.java +++ b/example/src/main/java/io/netty/example/securechat/SecureChatClientHandler.java @@ -16,7 +16,8 @@ package io.netty.example.securechat; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.MessageList; import java.util.logging.Level; import java.util.logging.Logger; @@ -24,14 +25,17 @@ import java.util.logging.Logger; /** * Handles a client-side channel. */ -public class SecureChatClientHandler extends ChannelInboundMessageHandlerAdapter { +public class SecureChatClientHandler extends ChannelInboundHandlerAdapter { private static final Logger logger = Logger.getLogger( SecureChatClientHandler.class.getName()); @Override - public void messageReceived(ChannelHandlerContext ctx, String msg) throws Exception { - System.err.println(msg); + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + for (int i = 0; i < msgs.size(); i++) { + System.err.println(msgs.get(i)); + } + msgs.releaseAllAndRecycle(); } @Override diff --git a/example/src/main/java/io/netty/example/securechat/SecureChatServerHandler.java b/example/src/main/java/io/netty/example/securechat/SecureChatServerHandler.java index 1b8b2334e2..882fae8e04 100644 --- a/example/src/main/java/io/netty/example/securechat/SecureChatServerHandler.java +++ b/example/src/main/java/io/netty/example/securechat/SecureChatServerHandler.java @@ -17,7 +17,8 @@ package io.netty.example.securechat; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.MessageList; import io.netty.channel.group.ChannelGroup; import io.netty.channel.group.DefaultChannelGroup; import io.netty.handler.ssl.SslHandler; @@ -31,7 +32,7 @@ import java.util.logging.Logger; /** * Handles a server-side channel. */ -public class SecureChatServerHandler extends ChannelInboundMessageHandlerAdapter { +public class SecureChatServerHandler extends ChannelInboundHandlerAdapter { private static final Logger logger = Logger.getLogger( SecureChatServerHandler.class.getName()); @@ -60,21 +61,26 @@ public class SecureChatServerHandler extends ChannelInboundMessageHandlerAdapter } @Override - public void messageReceived(ChannelHandlerContext ctx, String request) throws Exception { - // Send the received message to all channels but the current one. - for (Channel c: channels) { - if (c != ctx.channel()) { - c.write("[" + ctx.channel().remoteAddress() + "] " + - request + '\n'); - } else { - c.write("[you] " + request + '\n'); + public void messageReceived(ChannelHandlerContext ctx, MessageList requests) throws Exception { + MessageList msgs = requests.cast(); + for (int i = 0; i < msgs.size(); i++) { + String msg = msgs.get(i); + // Send the received message to all channels but the current one. + for (Channel c: channels) { + if (c != ctx.channel()) { + c.write("[" + ctx.channel().remoteAddress() + "] " + + msg + '\n'); + } else { + c.write("[you] " + msg + '\n'); + } + } + + // Close the connection if the client has sent 'bye'. + if ("bye".equals(msg.toLowerCase())) { + ctx.close(); } } - - // Close the connection if the client has sent 'bye'. - if ("bye".equals(request.toLowerCase())) { - ctx.close(); - } + msgs.releaseAllAndRecycle(); } @Override diff --git a/example/src/main/java/io/netty/example/socksproxy/DirectClientHandler.java b/example/src/main/java/io/netty/example/socksproxy/DirectClientHandler.java index 06a6a449a3..3cd1f7843f 100644 --- a/example/src/main/java/io/netty/example/socksproxy/DirectClientHandler.java +++ b/example/src/main/java/io/netty/example/socksproxy/DirectClientHandler.java @@ -15,12 +15,11 @@ */ package io.netty.example.socksproxy; -import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundByteHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; -public final class DirectClientHandler extends ChannelInboundByteHandlerAdapter { +public final class DirectClientHandler extends ChannelInboundHandlerAdapter { private static final String name = "DIRECT_CLIENT_HANDLER"; public static String getName() { @@ -42,8 +41,4 @@ public final class DirectClientHandler extends ChannelInboundByteHandlerAdapter public void exceptionCaught(ChannelHandlerContext ctx, Throwable throwable) throws Exception { cb.onFailure(ctx, throwable); } - - @Override - public void inboundBufferUpdated(ChannelHandlerContext ctx, ByteBuf byteBuf) throws Exception { - } } diff --git a/example/src/main/java/io/netty/example/socksproxy/RelayHandler.java b/example/src/main/java/io/netty/example/socksproxy/RelayHandler.java index b148b5d8fb..c05f2d1494 100644 --- a/example/src/main/java/io/netty/example/socksproxy/RelayHandler.java +++ b/example/src/main/java/io/netty/example/socksproxy/RelayHandler.java @@ -15,13 +15,14 @@ */ package io.netty.example.socksproxy; -import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundByteHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.MessageList; -public final class RelayHandler extends ChannelInboundByteHandlerAdapter { +public final class RelayHandler extends ChannelInboundHandlerAdapter { private static final String name = "RELAY_HANDLER"; public static String getName() { @@ -36,15 +37,13 @@ public final class RelayHandler extends ChannelInboundByteHandlerAdapter { @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { - ctx.flush(); + ctx.write(Unpooled.EMPTY_BUFFER); } @Override - public void inboundBufferUpdated(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - ByteBuf out = relayChannel.outboundByteBuffer(); - out.writeBytes(in); + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { if (relayChannel.isActive()) { - relayChannel.flush(); + relayChannel.write(msgs); } } diff --git a/example/src/main/java/io/netty/example/socksproxy/SocksServerConnectHandler.java b/example/src/main/java/io/netty/example/socksproxy/SocksServerConnectHandler.java index f65dcf8008..89fb5af1d1 100644 --- a/example/src/main/java/io/netty/example/socksproxy/SocksServerConnectHandler.java +++ b/example/src/main/java/io/netty/example/socksproxy/SocksServerConnectHandler.java @@ -21,15 +21,15 @@ import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; import io.netty.channel.ChannelOption; +import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.handler.codec.socks.SocksCmdRequest; import io.netty.handler.codec.socks.SocksCmdResponse; import io.netty.handler.codec.socks.SocksCmdStatus; @ChannelHandler.Sharable -public final class SocksServerConnectHandler extends ChannelInboundMessageHandlerAdapter { +public final class SocksServerConnectHandler extends SimpleChannelInboundHandler { private static final String name = "SOCKS_SERVER_CONNECT_HANDLER"; public static String getName() { diff --git a/example/src/main/java/io/netty/example/socksproxy/SocksServerHandler.java b/example/src/main/java/io/netty/example/socksproxy/SocksServerHandler.java index fdba9226d0..19739f84ba 100644 --- a/example/src/main/java/io/netty/example/socksproxy/SocksServerHandler.java +++ b/example/src/main/java/io/netty/example/socksproxy/SocksServerHandler.java @@ -17,7 +17,7 @@ package io.netty.example.socksproxy; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.SimpleChannelInboundHandler; import io.netty.handler.codec.socks.SocksAuthResponse; import io.netty.handler.codec.socks.SocksAuthScheme; import io.netty.handler.codec.socks.SocksAuthStatus; @@ -29,7 +29,7 @@ import io.netty.handler.codec.socks.SocksRequest; @ChannelHandler.Sharable -public final class SocksServerHandler extends ChannelInboundMessageHandlerAdapter { +public final class SocksServerHandler extends SimpleChannelInboundHandler { private static final String name = "SOCKS_SERVER_HANDLER"; public static String getName() { @@ -56,8 +56,7 @@ public final class SocksServerHandler extends ChannelInboundMessageHandlerAdapte if (req.cmdType() == SocksCmdType.CONNECT) { ctx.pipeline().addLast(SocksServerConnectHandler.getName(), new SocksServerConnectHandler()); ctx.pipeline().remove(this); - ctx.nextInboundMessageBuffer().add(socksRequest); - ctx.fireInboundBufferUpdated(); + ctx.fireMessageReceived(socksRequest); } else { ctx.close(); } diff --git a/example/src/main/java/io/netty/example/socksproxy/SocksServerUtils.java b/example/src/main/java/io/netty/example/socksproxy/SocksServerUtils.java index 4e0e7b147d..577b9debbe 100644 --- a/example/src/main/java/io/netty/example/socksproxy/SocksServerUtils.java +++ b/example/src/main/java/io/netty/example/socksproxy/SocksServerUtils.java @@ -15,6 +15,7 @@ */ package io.netty.example.socksproxy; +import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelFutureListener; @@ -29,7 +30,7 @@ public final class SocksServerUtils { */ public static void closeOnFlush(Channel ch) { if (ch.isActive()) { - ch.flush().addListener(ChannelFutureListener.CLOSE); + ch.write(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE); } } } diff --git a/example/src/main/java/io/netty/example/telnet/TelnetClientHandler.java b/example/src/main/java/io/netty/example/telnet/TelnetClientHandler.java index cfb4b0e51a..f1ef0ca9d9 100644 --- a/example/src/main/java/io/netty/example/telnet/TelnetClientHandler.java +++ b/example/src/main/java/io/netty/example/telnet/TelnetClientHandler.java @@ -17,7 +17,7 @@ package io.netty.example.telnet; import io.netty.channel.ChannelHandler.Sharable; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.SimpleChannelInboundHandler; import java.util.logging.Level; import java.util.logging.Logger; @@ -26,14 +26,13 @@ import java.util.logging.Logger; * Handles a client-side channel. */ @Sharable -public class TelnetClientHandler extends ChannelInboundMessageHandlerAdapter { +public class TelnetClientHandler extends SimpleChannelInboundHandler { private static final Logger logger = Logger.getLogger( TelnetClientHandler.class.getName()); @Override - public void messageReceived(ChannelHandlerContext ctx, String msg) throws Exception { - // Print out the line received from the server. + protected void messageReceived(ChannelHandlerContext ctx, String msg) throws Exception { System.err.println(msg); } diff --git a/example/src/main/java/io/netty/example/telnet/TelnetServerHandler.java b/example/src/main/java/io/netty/example/telnet/TelnetServerHandler.java index a3bf128c10..e871a6d326 100644 --- a/example/src/main/java/io/netty/example/telnet/TelnetServerHandler.java +++ b/example/src/main/java/io/netty/example/telnet/TelnetServerHandler.java @@ -19,7 +19,8 @@ import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandler.Sharable; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.MessageList; import java.net.InetAddress; import java.util.Date; @@ -30,7 +31,7 @@ import java.util.logging.Logger; * Handles a server-side channel. */ @Sharable -public class TelnetServerHandler extends ChannelInboundMessageHandlerAdapter { +public class TelnetServerHandler extends ChannelInboundHandlerAdapter { private static final Logger logger = Logger.getLogger( TelnetServerHandler.class.getName()); @@ -44,28 +45,34 @@ public class TelnetServerHandler extends ChannelInboundMessageHandlerAdapter msgs) throws Exception { + MessageList requests = msgs.cast(); + for (int i = 0; i < requests.size(); i++) { + String request = requests.get(i); - // We do not need to write a ChannelBuffer here. - // We know the encoder inserted at TelnetPipelineFactory will do the conversion. - ChannelFuture future = ctx.write(response); + // Generate and write a response. + String response; + boolean close = false; + if (request.isEmpty()) { + response = "Please type something.\r\n"; + } else if ("bye".equals(request.toLowerCase())) { + response = "Have a good day!\r\n"; + close = true; + } else { + response = "Did you say '" + request + "'?\r\n"; + } - // Close the connection after sending 'Have a good day!' - // if the client has sent 'bye'. - if (close) { - future.addListener(ChannelFutureListener.CLOSE); + // We do not need to write a ChannelBuffer here. + // We know the encoder inserted at TelnetPipelineFactory will do the conversion. + ChannelFuture future = ctx.write(response); + + // Close the connection after sending 'Have a good day!' + // if the client has sent 'bye'. + if (close) { + future.addListener(ChannelFutureListener.CLOSE); + } } + msgs.releaseAllAndRecycle(); } @Override diff --git a/example/src/main/java/io/netty/example/udt/echo/bytes/ByteEchoClientHandler.java b/example/src/main/java/io/netty/example/udt/echo/bytes/ByteEchoClientHandler.java index 4e8d401bf3..02681f8912 100644 --- a/example/src/main/java/io/netty/example/udt/echo/bytes/ByteEchoClientHandler.java +++ b/example/src/main/java/io/netty/example/udt/echo/bytes/ByteEchoClientHandler.java @@ -20,9 +20,8 @@ import com.yammer.metrics.core.Meter; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelHandlerUtil; -import io.netty.channel.ChannelInboundByteHandlerAdapter; -import io.netty.channel.ChannelOption; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.MessageList; import io.netty.channel.udt.nio.NioUdtProvider; import java.util.concurrent.TimeUnit; @@ -34,7 +33,7 @@ import java.util.logging.Logger; * traffic between the echo client and server by sending the first message to * the server on activation. */ -public class ByteEchoClientHandler extends ChannelInboundByteHandlerAdapter { +public class ByteEchoClientHandler extends ChannelInboundHandlerAdapter { private static final Logger log = Logger.getLogger(ByteEchoClientHandler.class.getName()); @@ -58,13 +57,14 @@ public class ByteEchoClientHandler extends ChannelInboundByteHandlerAdapter { } @Override - public void inboundBufferUpdated(final ChannelHandlerContext ctx, - final ByteBuf in) { - meter.mark(in.readableBytes()); - final ByteBuf out = ctx.nextOutboundByteBuffer(); - out.discardReadBytes(); - out.writeBytes(in); - ctx.flush(); + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + MessageList buffers = msgs.cast(); + + for (int i = 0; i < buffers.size(); i++) { + ByteBuf buf = buffers.get(i); + meter.mark(buf.readableBytes()); + } + ctx.write(buffers); } @Override @@ -74,11 +74,4 @@ public class ByteEchoClientHandler extends ChannelInboundByteHandlerAdapter { ctx.close(); } - @Override - public ByteBuf newInboundBuffer(final ChannelHandlerContext ctx) - throws Exception { - return ChannelHandlerUtil.allocate(ctx, - ctx.channel().config().getOption(ChannelOption.SO_RCVBUF)); - } - } diff --git a/example/src/main/java/io/netty/example/udt/echo/bytes/ByteEchoServerHandler.java b/example/src/main/java/io/netty/example/udt/echo/bytes/ByteEchoServerHandler.java index 1cc16e24a7..af3eebedee 100644 --- a/example/src/main/java/io/netty/example/udt/echo/bytes/ByteEchoServerHandler.java +++ b/example/src/main/java/io/netty/example/udt/echo/bytes/ByteEchoServerHandler.java @@ -15,12 +15,10 @@ */ package io.netty.example.udt.echo.bytes; -import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandler.Sharable; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelHandlerUtil; -import io.netty.channel.ChannelInboundByteHandlerAdapter; -import io.netty.channel.ChannelOption; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.MessageList; import io.netty.channel.udt.nio.NioUdtProvider; import java.util.logging.Level; @@ -30,17 +28,13 @@ import java.util.logging.Logger; * Handler implementation for the echo server. */ @Sharable -public class ByteEchoServerHandler extends ChannelInboundByteHandlerAdapter { +public class ByteEchoServerHandler extends ChannelInboundHandlerAdapter { private static final Logger log = Logger.getLogger(ByteEchoServerHandler.class.getName()); @Override - public void inboundBufferUpdated(final ChannelHandlerContext ctx, - final ByteBuf in) { - final ByteBuf out = ctx.nextOutboundByteBuffer(); - out.discardReadBytes(); - out.writeBytes(in); - ctx.flush(); + public void messageReceived(final ChannelHandlerContext ctx, MessageList msgs) { + ctx.write(msgs); } @Override @@ -55,11 +49,4 @@ public class ByteEchoServerHandler extends ChannelInboundByteHandlerAdapter { log.info("ECHO active " + NioUdtProvider.socketUDT(ctx.channel()).toStringOptions()); } - @Override - public ByteBuf newInboundBuffer(final ChannelHandlerContext ctx) - throws Exception { - return ChannelHandlerUtil.allocate(ctx, - ctx.channel().config().getOption(ChannelOption.SO_RCVBUF)); - } - } diff --git a/example/src/main/java/io/netty/example/udt/echo/message/MsgEchoClientHandler.java b/example/src/main/java/io/netty/example/udt/echo/message/MsgEchoClientHandler.java index 18d6b8825e..544a3ff1ea 100644 --- a/example/src/main/java/io/netty/example/udt/echo/message/MsgEchoClientHandler.java +++ b/example/src/main/java/io/netty/example/udt/echo/message/MsgEchoClientHandler.java @@ -18,10 +18,10 @@ package io.netty.example.udt.echo.message; import com.yammer.metrics.Metrics; import com.yammer.metrics.core.Meter; import io.netty.buffer.ByteBuf; -import io.netty.buffer.MessageBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.MessageList; import io.netty.channel.udt.UdtMessage; import io.netty.channel.udt.nio.NioUdtProvider; @@ -34,8 +34,7 @@ import java.util.logging.Logger; * traffic between the echo client and server by sending the first message to * the server on activation. */ -public class MsgEchoClientHandler extends - ChannelInboundMessageHandlerAdapter { +public class MsgEchoClientHandler extends ChannelInboundHandlerAdapter { private static final Logger log = Logger.getLogger(MsgEchoClientHandler.class.getName()); @@ -55,9 +54,7 @@ public class MsgEchoClientHandler extends @Override public void channelActive(final ChannelHandlerContext ctx) throws Exception { log.info("ECHO active " + NioUdtProvider.socketUDT(ctx.channel()).toStringOptions()); - final MessageBuf out = ctx.nextOutboundMessageBuffer(); - out.add(message); - ctx.flush(); + ctx.write(message); } @Override @@ -68,13 +65,13 @@ public class MsgEchoClientHandler extends } @Override - public void messageReceived(final ChannelHandlerContext ctx, - final UdtMessage message) throws Exception { - final ByteBuf byteBuf = message.content(); - meter.mark(byteBuf.readableBytes()); - final MessageBuf out = ctx.nextOutboundMessageBuffer(); - out.add(message.retain()); - ctx.flush(); - } + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + MessageList buffers = msgs.cast(); + for (int i = 0; i < buffers.size(); i++) { + UdtMessage message = buffers.get(i); + meter.mark(message.content().readableBytes()); + } + ctx.write(msgs); + } } diff --git a/example/src/main/java/io/netty/example/udt/echo/message/MsgEchoServerHandler.java b/example/src/main/java/io/netty/example/udt/echo/message/MsgEchoServerHandler.java index fa17c441e9..b08e471cf0 100644 --- a/example/src/main/java/io/netty/example/udt/echo/message/MsgEchoServerHandler.java +++ b/example/src/main/java/io/netty/example/udt/echo/message/MsgEchoServerHandler.java @@ -15,11 +15,10 @@ */ package io.netty.example.udt.echo.message; -import io.netty.buffer.MessageBuf; import io.netty.channel.ChannelHandler.Sharable; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; -import io.netty.channel.udt.UdtMessage; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.MessageList; import io.netty.channel.udt.nio.NioUdtProvider; import java.util.logging.Level; @@ -29,8 +28,7 @@ import java.util.logging.Logger; * Handler implementation for the echo server. */ @Sharable -public class MsgEchoServerHandler extends - ChannelInboundMessageHandlerAdapter { +public class MsgEchoServerHandler extends ChannelInboundHandlerAdapter { private static final Logger log = Logger.getLogger(MsgEchoServerHandler.class.getName()); @@ -47,10 +45,7 @@ public class MsgEchoServerHandler extends } @Override - public void messageReceived(final ChannelHandlerContext ctx, - final UdtMessage message) throws Exception { - final MessageBuf out = ctx.nextOutboundMessageBuffer(); - out.add(message.retain()); - ctx.flush(); + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + ctx.write(msgs); } } diff --git a/example/src/main/java/io/netty/example/udt/echo/rendezvous/MsgEchoPeerHandler.java b/example/src/main/java/io/netty/example/udt/echo/rendezvous/MsgEchoPeerHandler.java index 519f1d68de..7a78961ef4 100644 --- a/example/src/main/java/io/netty/example/udt/echo/rendezvous/MsgEchoPeerHandler.java +++ b/example/src/main/java/io/netty/example/udt/echo/rendezvous/MsgEchoPeerHandler.java @@ -18,10 +18,10 @@ package io.netty.example.udt.echo.rendezvous; import com.yammer.metrics.Metrics; import com.yammer.metrics.core.Meter; import io.netty.buffer.ByteBuf; -import io.netty.buffer.MessageBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.MessageList; import io.netty.channel.udt.UdtMessage; import io.netty.channel.udt.nio.NioUdtProvider; @@ -35,7 +35,7 @@ import java.util.logging.Logger; * activation. */ public class MsgEchoPeerHandler extends - ChannelInboundMessageHandlerAdapter { + ChannelInboundHandlerAdapter { private static final Logger log = Logger.getLogger(MsgEchoPeerHandler.class.getName()); @@ -55,9 +55,7 @@ public class MsgEchoPeerHandler extends @Override public void channelActive(final ChannelHandlerContext ctx) throws Exception { log.info("ECHO active " + NioUdtProvider.socketUDT(ctx.channel()).toStringOptions()); - final MessageBuf out = ctx.nextOutboundMessageBuffer(); - out.add(message); - ctx.flush(); + ctx.write(message); } @Override @@ -68,13 +66,13 @@ public class MsgEchoPeerHandler extends } @Override - public void messageReceived(final ChannelHandlerContext ctx, - final UdtMessage message) throws Exception { - final ByteBuf byteBuf = message.content(); - meter.mark(byteBuf.readableBytes()); - final MessageBuf out = ctx.nextOutboundMessageBuffer(); - out.add(message.retain()); - ctx.flush(); - } + public void messageReceived(ChannelHandlerContext ctx, MessageList messages) throws Exception { + MessageList msgs = messages.cast(); + for (int i = 0; i < msgs.size(); i++) { + UdtMessage message = msgs.get(i); + meter.mark(message.content().readableBytes()); + } + ctx.write(msgs); + } } diff --git a/example/src/main/java/io/netty/example/udt/echo/rendezvousBytes/ByteEchoPeerHandler.java b/example/src/main/java/io/netty/example/udt/echo/rendezvousBytes/ByteEchoPeerHandler.java index 9452aa5648..3def6e7e0d 100644 --- a/example/src/main/java/io/netty/example/udt/echo/rendezvousBytes/ByteEchoPeerHandler.java +++ b/example/src/main/java/io/netty/example/udt/echo/rendezvousBytes/ByteEchoPeerHandler.java @@ -20,9 +20,8 @@ import com.yammer.metrics.core.Meter; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelHandlerUtil; -import io.netty.channel.ChannelInboundByteHandlerAdapter; -import io.netty.channel.ChannelOption; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.MessageList; import io.netty.channel.udt.nio.NioUdtProvider; import java.util.concurrent.TimeUnit; @@ -34,7 +33,7 @@ import java.util.logging.Logger; * traffic between the echo client and server by sending the first message to * the server on activation. */ -public class ByteEchoPeerHandler extends ChannelInboundByteHandlerAdapter { +public class ByteEchoPeerHandler extends ChannelInboundHandlerAdapter { private static final Logger log = Logger.getLogger(ByteEchoPeerHandler.class.getName()); private final ByteBuf message; @@ -48,12 +47,6 @@ public class ByteEchoPeerHandler extends ChannelInboundByteHandlerAdapter { } } - @Override - public ByteBuf newInboundBuffer(ChannelHandlerContext ctx) throws Exception { - return ChannelHandlerUtil.allocate(ctx, - ctx.channel().config().getOption(ChannelOption.SO_RCVBUF)); - } - @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { log.info("ECHO active " + NioUdtProvider.socketUDT(ctx.channel()).toStringOptions()); @@ -67,10 +60,13 @@ public class ByteEchoPeerHandler extends ChannelInboundByteHandlerAdapter { } @Override - protected void inboundBufferUpdated(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - meter.mark(in.readableBytes()); - final ByteBuf out = ctx.nextOutboundByteBuffer(); - out.writeBytes(in); - ctx.flush(); + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + MessageList buffers = msgs.cast(); + + for (int i = 0; i < buffers.size(); i++) { + ByteBuf buf = buffers.get(i); + meter.mark(buf.readableBytes()); + } + ctx.write(buffers); } } diff --git a/example/src/main/java/io/netty/example/uptime/UptimeClientHandler.java b/example/src/main/java/io/netty/example/uptime/UptimeClientHandler.java index 3f61a08ce4..ca36e1502b 100644 --- a/example/src/main/java/io/netty/example/uptime/UptimeClientHandler.java +++ b/example/src/main/java/io/netty/example/uptime/UptimeClientHandler.java @@ -16,11 +16,11 @@ package io.netty.example.uptime; import io.netty.bootstrap.Bootstrap; -import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandler.Sharable; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundByteHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.EventLoop; +import io.netty.channel.MessageList; import io.netty.handler.timeout.IdleState; import io.netty.handler.timeout.IdleStateEvent; @@ -32,7 +32,7 @@ import java.util.concurrent.TimeUnit; * connection attempt getStatus. */ @Sharable -public class UptimeClientHandler extends ChannelInboundByteHandlerAdapter { +public class UptimeClientHandler extends ChannelInboundHandlerAdapter { private final UptimeClient client; private long startTime = -1; @@ -50,9 +50,9 @@ public class UptimeClientHandler extends ChannelInboundByteHandlerAdapter { } @Override - public void inboundBufferUpdated(ChannelHandlerContext ctx, ByteBuf in) throws Exception { + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { // Discard received data - in.clear(); + msgs.releaseAllAndRecycle(); } @Override diff --git a/example/src/main/java/io/netty/example/worldclock/WorldClockClientHandler.java b/example/src/main/java/io/netty/example/worldclock/WorldClockClientHandler.java index 865ed9558a..6bbcb0f253 100644 --- a/example/src/main/java/io/netty/example/worldclock/WorldClockClientHandler.java +++ b/example/src/main/java/io/netty/example/worldclock/WorldClockClientHandler.java @@ -17,7 +17,8 @@ package io.netty.example.worldclock; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.MessageList; import io.netty.example.worldclock.WorldClockProtocol.Continent; import io.netty.example.worldclock.WorldClockProtocol.LocalTime; import io.netty.example.worldclock.WorldClockProtocol.LocalTimes; @@ -34,7 +35,7 @@ import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Pattern; -public class WorldClockClientHandler extends ChannelInboundMessageHandlerAdapter { +public class WorldClockClientHandler extends ChannelInboundHandlerAdapter { private static final Logger logger = Logger.getLogger( WorldClockClientHandler.class.getName()); @@ -95,8 +96,11 @@ public class WorldClockClientHandler extends ChannelInboundMessageHandlerAdapter } @Override - public void messageReceived(ChannelHandlerContext ctx, LocalTimes msg) throws Exception { - answer.add(msg); + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + for (int i = 0; i < msgs.size(); i++) { + answer.add((LocalTimes) msgs.get(i)); + } + msgs.recycle(); } @Override diff --git a/example/src/main/java/io/netty/example/worldclock/WorldClockServerHandler.java b/example/src/main/java/io/netty/example/worldclock/WorldClockServerHandler.java index d61ada1f19..d90e28db81 100644 --- a/example/src/main/java/io/netty/example/worldclock/WorldClockServerHandler.java +++ b/example/src/main/java/io/netty/example/worldclock/WorldClockServerHandler.java @@ -16,7 +16,8 @@ package io.netty.example.worldclock; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.MessageList; import io.netty.example.worldclock.WorldClockProtocol.Continent; import io.netty.example.worldclock.WorldClockProtocol.DayOfWeek; import io.netty.example.worldclock.WorldClockProtocol.LocalTime; @@ -31,33 +32,41 @@ import java.util.logging.Logger; import static java.util.Calendar.*; -public class WorldClockServerHandler extends ChannelInboundMessageHandlerAdapter { +public class WorldClockServerHandler extends ChannelInboundHandlerAdapter { private static final Logger logger = Logger.getLogger( WorldClockServerHandler.class.getName()); @Override - public void messageReceived(ChannelHandlerContext ctx, Locations locations) throws Exception { - long currentTime = System.currentTimeMillis(); + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + int size = msgs.size(); + MessageList out = MessageList.newInstance(size); + for (int i = 0; i < size; i++) { + Locations locations = (Locations) msgs.get(i); + long currentTime = System.currentTimeMillis(); - LocalTimes.Builder builder = LocalTimes.newBuilder(); - for (Location l: locations.getLocationList()) { - TimeZone tz = TimeZone.getTimeZone( - toString(l.getContinent()) + '/' + l.getCity()); - Calendar calendar = getInstance(tz); - calendar.setTimeInMillis(currentTime); + LocalTimes.Builder builder = LocalTimes.newBuilder(); + for (Location l: locations.getLocationList()) { + TimeZone tz = TimeZone.getTimeZone( + toString(l.getContinent()) + '/' + l.getCity()); + Calendar calendar = getInstance(tz); + calendar.setTimeInMillis(currentTime); - builder.addLocalTime(LocalTime.newBuilder(). - setYear(calendar.get(YEAR)). - setMonth(calendar.get(MONTH) + 1). - setDayOfMonth(calendar.get(DAY_OF_MONTH)). - setDayOfWeek(DayOfWeek.valueOf(calendar.get(DAY_OF_WEEK))). - setHour(calendar.get(HOUR_OF_DAY)). - setMinute(calendar.get(MINUTE)). - setSecond(calendar.get(SECOND)).build()); + builder.addLocalTime(LocalTime.newBuilder(). + setYear(calendar.get(YEAR)). + setMonth(calendar.get(MONTH) + 1). + setDayOfMonth(calendar.get(DAY_OF_MONTH)). + setDayOfWeek(DayOfWeek.valueOf(calendar.get(DAY_OF_WEEK))). + setHour(calendar.get(HOUR_OF_DAY)). + setMinute(calendar.get(MINUTE)). + setSecond(calendar.get(SECOND)).build()); + } + + out.add(builder.build()); } - ctx.write(builder.build()); + msgs.recycle(); + ctx.write(out); } @Override diff --git a/handler/pom.xml b/handler/pom.xml index b160b0fb46..e0d88f82f2 100644 --- a/handler/pom.xml +++ b/handler/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.0.0.Final-SNAPSHOT + 4.0.0.CR4-SNAPSHOT netty-handler @@ -39,6 +39,11 @@ netty-transport ${project.version} + + ${project.groupId} + netty-codec + ${project.version} + diff --git a/handler/src/main/java/io/netty/handler/logging/ByteLoggingHandler.java b/handler/src/main/java/io/netty/handler/logging/ByteLoggingHandler.java index dbc288ba10..3c7aaf3448 100644 --- a/handler/src/main/java/io/netty/handler/logging/ByteLoggingHandler.java +++ b/handler/src/main/java/io/netty/handler/logging/ByteLoggingHandler.java @@ -17,13 +17,11 @@ package io.netty.handler.logging; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelHandlerUtil; -import io.netty.channel.ChannelInboundByteHandler; -import io.netty.channel.ChannelOutboundByteHandler; import io.netty.channel.ChannelPromise; +import io.netty.channel.MessageList; public class ByteLoggingHandler - extends LoggingHandler implements ChannelInboundByteHandler, ChannelOutboundByteHandler { + extends LoggingHandler { private static final String NEWLINE = String.format("%n"); @@ -108,45 +106,26 @@ public class ByteLoggingHandler } @Override - public ByteBuf newInboundBuffer(ChannelHandlerContext ctx) throws Exception { - return ChannelHandlerUtil.allocate(ctx); + public void write(ChannelHandlerContext ctx, MessageList msgs, ChannelPromise promise) throws Exception { + log(ctx, "WRITE", msgs); + ctx.write(msgs, promise); } @Override - public void discardInboundReadBytes(ChannelHandlerContext ctx) throws Exception { - ctx.inboundByteBuffer().discardSomeReadBytes(); + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + log(ctx, "RECEIVED", msgs); + ctx.fireMessageReceived(msgs); } - @Override - public ByteBuf newOutboundBuffer(ChannelHandlerContext ctx) throws Exception { - return ChannelHandlerUtil.allocate(ctx); - } - - @Override - public void discardOutboundReadBytes(ChannelHandlerContext ctx) throws Exception { - ctx.outboundByteBuffer().discardSomeReadBytes(); - } - - @Override - public void inboundBufferUpdated(ChannelHandlerContext ctx) - throws Exception { - ByteBuf buf = ctx.inboundByteBuffer(); + private void log(ChannelHandlerContext ctx, String message, MessageList msgs) { if (logger.isEnabled(internalLevel)) { - logger.log(internalLevel, format(ctx, formatBuffer("RECEIVED", buf))); + for (int i = 0; i < msgs.size(); i++) { + Object msg = msgs.get(i); + if (msg instanceof ByteBuf) { + logger.log(internalLevel, format(ctx, formatBuffer(message, (ByteBuf) msg))); + } + } } - ctx.nextInboundByteBuffer().writeBytes(buf); - ctx.fireInboundBufferUpdated(); - } - - @Override - public void flush(ChannelHandlerContext ctx, ChannelPromise promise) - throws Exception { - ByteBuf buf = ctx.outboundByteBuffer(); - if (logger.isEnabled(internalLevel)) { - logger.log(internalLevel, format(ctx, formatBuffer("WRITE", buf))); - } - ctx.nextOutboundByteBuffer().writeBytes(buf); - ctx.flush(promise); } protected String formatBuffer(String message, ByteBuf buf) { diff --git a/handler/src/main/java/io/netty/handler/logging/LoggingHandler.java b/handler/src/main/java/io/netty/handler/logging/LoggingHandler.java index 145a73fecf..f0cefaf059 100644 --- a/handler/src/main/java/io/netty/handler/logging/LoggingHandler.java +++ b/handler/src/main/java/io/netty/handler/logging/LoggingHandler.java @@ -15,11 +15,13 @@ */ package io.netty.handler.logging; +import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelDuplexHandler; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandler.Sharable; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPromise; +import io.netty.channel.MessageList; import io.netty.util.internal.logging.InternalLogLevel; import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; @@ -35,6 +37,66 @@ public class LoggingHandler extends ChannelDuplexHandler { private static final LogLevel DEFAULT_LEVEL = LogLevel.DEBUG; + private static final String NEWLINE = String.format("%n"); + + private static final String[] BYTE2HEX = new String[256]; + private static final String[] HEXPADDING = new String[16]; + private static final String[] BYTEPADDING = new String[16]; + private static final char[] BYTE2CHAR = new char[256]; + + static { + int i; + + // Generate the lookup table for byte-to-hex-dump conversion + for (i = 0; i < 10; i ++) { + StringBuilder buf = new StringBuilder(3); + buf.append(" 0"); + buf.append(i); + BYTE2HEX[i] = buf.toString(); + } + for (; i < 16; i ++) { + StringBuilder buf = new StringBuilder(3); + buf.append(" 0"); + buf.append((char) ('a' + i - 10)); + BYTE2HEX[i] = buf.toString(); + } + for (; i < BYTE2HEX.length; i ++) { + StringBuilder buf = new StringBuilder(3); + buf.append(' '); + buf.append(Integer.toHexString(i)); + BYTE2HEX[i] = buf.toString(); + } + + // Generate the lookup table for hex dump paddings + for (i = 0; i < HEXPADDING.length; i ++) { + int padding = HEXPADDING.length - i; + StringBuilder buf = new StringBuilder(padding * 3); + for (int j = 0; j < padding; j ++) { + buf.append(" "); + } + HEXPADDING[i] = buf.toString(); + } + + // Generate the lookup table for byte dump paddings + for (i = 0; i < BYTEPADDING.length; i ++) { + int padding = BYTEPADDING.length - i; + StringBuilder buf = new StringBuilder(padding); + for (int j = 0; j < padding; j ++) { + buf.append(' '); + } + BYTEPADDING[i] = buf.toString(); + } + + // Generate the lookup table for byte-to-char conversion + for (i = 0; i < BYTE2CHAR.length; i ++) { + if (i <= 0x1f || i >= 0x7f) { + BYTE2CHAR[i] = '.'; + } else { + BYTE2CHAR[i] = (char) i; + } + } + } + protected final InternalLogger logger; protected final InternalLogLevel internalLevel; @@ -230,19 +292,100 @@ public class LoggingHandler extends ChannelDuplexHandler { } @Override - public void read(ChannelHandlerContext ctx) { - ctx.read(); + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + logMessages(ctx, "RECEIVED", msgs); + ctx.fireMessageReceived(msgs); } @Override - public void flush(ChannelHandlerContext ctx, ChannelPromise promise) - throws Exception { - ctx.flush(promise); + public void write(ChannelHandlerContext ctx, MessageList msgs, ChannelPromise promise) throws Exception { + logMessages(ctx, "WRITE", msgs); + ctx.write(msgs, promise); } - @Override - public void inboundBufferUpdated(ChannelHandlerContext ctx) - throws Exception { - ctx.fireInboundBufferUpdated(); + private void logMessages(ChannelHandlerContext ctx, String message, MessageList msgs) { + if (logger.isEnabled(internalLevel)) { + int size = msgs.size(); + for (int i = 0; i < size; i ++) { + Object msg = msgs.get(i); + if (msg instanceof ByteBuf) { + logger.log(internalLevel, format(ctx, formatBuffer(message, (ByteBuf) msg))); + } else { + // ignore + } + } + } + } + + protected String formatBuffer(String message, ByteBuf buf) { + int length = buf.readableBytes(); + int rows = length / 16 + (length % 15 == 0? 0 : 1) + 4; + StringBuilder dump = new StringBuilder(rows * 80 + message.length() + 16); + + dump.append(message).append('(').append(length).append('B').append(')'); + dump.append( + NEWLINE + " +-------------------------------------------------+" + + NEWLINE + " | 0 1 2 3 4 5 6 7 8 9 a b c d e f |" + + NEWLINE + "+--------+-------------------------------------------------+----------------+"); + + final int startIndex = buf.readerIndex(); + final int endIndex = buf.writerIndex(); + + int i; + for (i = startIndex; i < endIndex; i ++) { + int relIdx = i - startIndex; + int relIdxMod16 = relIdx & 15; + if (relIdxMod16 == 0) { + dump.append(NEWLINE); + dump.append(Long.toHexString(relIdx & 0xFFFFFFFFL | 0x100000000L)); + dump.setCharAt(dump.length() - 9, '|'); + dump.append('|'); + } + dump.append(BYTE2HEX[buf.getUnsignedByte(i)]); + if (relIdxMod16 == 15) { + dump.append(" |"); + for (int j = i - 15; j <= i; j ++) { + dump.append(BYTE2CHAR[buf.getUnsignedByte(j)]); + } + dump.append('|'); + } + } + + if ((i - startIndex & 15) != 0) { + int remainder = length & 15; + dump.append(HEXPADDING[remainder]); + dump.append(" |"); + for (int j = i - remainder; j < i; j ++) { + dump.append(BYTE2CHAR[buf.getUnsignedByte(j)]); + } + dump.append(BYTEPADDING[remainder]); + dump.append('|'); + } + + dump.append( + NEWLINE + "+--------+-------------------------------------------------+----------------+"); + + return dump.toString(); + } + + protected String formatBuffer(String message, Object[] msgs, int index, int length) { + return message + '(' + length + "): " + contentToString(msgs, index, length); + } + + private static String contentToString(Object[] msgs, int index, int length) { + if (length == 0) { + return "[]"; + } + StringBuilder sb = new StringBuilder(); + sb.append('['); + for (int i = index; i < length; i++) { + Object msg = msgs[i]; + sb.append(msg); + + if (i + 1 < length) { + sb.append(", "); + } + } + return sb.append(']').toString(); } } diff --git a/handler/src/main/java/io/netty/handler/logging/MessageLoggingHandler.java b/handler/src/main/java/io/netty/handler/logging/MessageLoggingHandler.java index a1a8d431e0..87377a6048 100644 --- a/handler/src/main/java/io/netty/handler/logging/MessageLoggingHandler.java +++ b/handler/src/main/java/io/netty/handler/logging/MessageLoggingHandler.java @@ -15,17 +15,11 @@ */ package io.netty.handler.logging; -import io.netty.buffer.BufUtil; -import io.netty.buffer.MessageBuf; -import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandler; -import io.netty.channel.ChannelOutboundMessageHandler; import io.netty.channel.ChannelPromise; +import io.netty.channel.MessageList; -public class MessageLoggingHandler - extends LoggingHandler - implements ChannelInboundMessageHandler, ChannelOutboundMessageHandler { +public class MessageLoggingHandler extends LoggingHandler { public MessageLoggingHandler() { } @@ -50,54 +44,42 @@ public class MessageLoggingHandler } @Override - public MessageBuf newInboundBuffer(ChannelHandlerContext ctx) throws Exception { - return Unpooled.messageBuffer(); + public void write(ChannelHandlerContext ctx, MessageList msgs, ChannelPromise promise) throws Exception { + log("WRITE", msgs); + ctx.write(msgs); } @Override - public MessageBuf newOutboundBuffer(ChannelHandlerContext ctx) throws Exception { - return Unpooled.messageBuffer(); + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + log("RECEIVED", msgs); + ctx.fireMessageReceived(msgs); } - @Override - public void inboundBufferUpdated(ChannelHandlerContext ctx) - throws Exception { - MessageBuf buf = ctx.inboundMessageBuffer(); + private void log(String message, MessageList msgs) { if (logger.isEnabled(internalLevel)) { - logger.log(internalLevel, format(ctx, formatBuffer("RECEIVED", buf))); + logger.log(internalLevel, formatBuffer(message, msgs)); } - - MessageBuf out = ctx.nextInboundMessageBuffer(); - for (;;) { - Object o = buf.poll(); - if (o == null) { - break; - } - out.add(o); - } - ctx.fireInboundBufferUpdated(); } - @Override - public void flush(ChannelHandlerContext ctx, ChannelPromise promise) - throws Exception { - MessageBuf buf = ctx.outboundMessageBuffer(); - if (logger.isEnabled(internalLevel)) { - logger.log(internalLevel, format(ctx, formatBuffer("WRITE", buf))); - } - - MessageBuf out = ctx.nextOutboundMessageBuffer(); - for (;;) { - Object o = buf.poll(); - if (o == null) { - break; - } - out.add(o); - } - ctx.flush(promise); + protected String formatBuffer(String message, MessageList msgs) { + return message + '(' + msgs.size() + "): " + contentToString(msgs); } - protected String formatBuffer(String message, MessageBuf buf) { - return message + '(' + buf.size() + "): " + BufUtil.contentToString(buf); + private static String contentToString(MessageList msgs) { + if (msgs.isEmpty()) { + return "[]"; + } + StringBuilder sb = new StringBuilder(); + sb.append('['); + int size = msgs.size(); + for (int i = 0; i < size; i++) { + Object msg = msgs.get(i); + sb.append(msg); + + if (i + 1 < size) { + sb.append(", "); + } + } + return sb.append(']').toString(); } } 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 bfa59600c4..93b575d5b2 100644 --- a/handler/src/main/java/io/netty/handler/ssl/SslHandler.java +++ b/handler/src/main/java/io/netty/handler/ssl/SslHandler.java @@ -15,21 +15,19 @@ */ package io.netty.handler.ssl; -import io.netty.buffer.BufUtil; import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufUtil; +import io.netty.buffer.Unpooled; import io.netty.channel.Channel; -import io.netty.channel.ChannelDuplexHandler; -import io.netty.channel.ChannelFlushPromiseNotifier; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelHandlerUtil; -import io.netty.channel.ChannelInboundByteHandler; -import io.netty.channel.ChannelOutboundByteHandler; +import io.netty.channel.ChannelInboundHandler; +import io.netty.channel.ChannelOutboundHandler; import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPromise; -import io.netty.channel.ChannelStateHandler; -import io.netty.channel.FileRegion; +import io.netty.channel.MessageList; +import io.netty.handler.codec.ByteToMessageDecoder; import io.netty.util.concurrent.DefaultPromise; import io.netty.util.concurrent.EventExecutor; import io.netty.util.concurrent.Future; @@ -44,13 +42,14 @@ import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngineResult; import javax.net.ssl.SSLEngineResult.Status; import javax.net.ssl.SSLException; -import java.io.EOFException; import java.io.IOException; +import java.net.SocketAddress; import java.nio.ByteBuffer; import java.nio.channels.ClosedChannelException; import java.nio.channels.DatagramChannel; import java.nio.channels.SocketChannel; -import java.nio.channels.WritableByteChannel; +import java.util.ArrayDeque; +import java.util.Queue; import java.util.concurrent.Executor; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -71,7 +70,7 @@ import java.util.regex.Pattern; *

* Beside using the handshake {@link ChannelFuture} to get notified about the completation of the handshake it's * also possible to detect it by implement the - * {@link ChannelStateHandler#userEventTriggered(ChannelHandlerContext, Object)} + * {@link ChannelInboundHandler#userEventTriggered(ChannelHandlerContext, Object)} * method and check for a {@link SslHandshakeCompletionEvent}. * *

Handshake

@@ -152,8 +151,8 @@ import java.util.regex.Pattern; * #832 in our issue tracker. */ public class SslHandler - extends ChannelDuplexHandler - implements ChannelInboundByteHandler, ChannelOutboundByteHandler { + extends ByteToMessageDecoder + implements ChannelOutboundHandler { private static final InternalLogger logger = InternalLoggerFactory.getInstance(SslHandler.class); @@ -176,15 +175,14 @@ public class SslHandler private volatile ChannelHandlerContext ctx; private final SSLEngine engine; private final Executor delegatedTaskExecutor; - private final ChannelFlushPromiseNotifier flushFutureNotifier = new ChannelFlushPromiseNotifier(true); private final boolean startTls; private boolean sentFirstMessage; - private WritableByteChannel bufferChannel; private final LazyChannelPromise handshakePromise = new LazyChannelPromise(); private final LazyChannelPromise sslCloseFuture = new LazyChannelPromise(); private final CloseNotifyListener closeNotifyWriteListener = new CloseNotifyListener(); + private final Queue pendingUnencryptedWrites = new ArrayDeque(); private volatile long handshakeTimeoutMillis = 10000; private volatile long closeNotifyTimeoutMillis = 3000; @@ -320,7 +318,7 @@ public class SslHandler engine.closeOutbound(); future.addListener(closeNotifyWriteListener); try { - flush(ctx, future); + write(ctx, MessageList.newInstance(Unpooled.EMPTY_BUFFER), future); } catch (Exception e) { if (!future.tryFailure(e)) { logger.warn("flush() raised a masked exception.", e); @@ -345,23 +343,19 @@ public class SslHandler } @Override - public ByteBuf newInboundBuffer(ChannelHandlerContext ctx) throws Exception { - return ChannelHandlerUtil.allocate(ctx); + public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception { + ctx.bind(localAddress, promise); } @Override - public void discardInboundReadBytes(ChannelHandlerContext ctx) throws Exception { - ctx.inboundByteBuffer().discardSomeReadBytes(); + public void connect(ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress, + ChannelPromise promise) throws Exception { + ctx.connect(remoteAddress, localAddress, promise); } @Override - public ByteBuf newOutboundBuffer(ChannelHandlerContext ctx) throws Exception { - return ChannelHandlerUtil.allocate(ctx); - } - - @Override - public void discardOutboundReadBytes(ChannelHandlerContext ctx) throws Exception { - ctx.outboundByteBuffer().discardSomeReadBytes(); + public void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { + ctx.deregister(promise); } @Override @@ -382,132 +376,95 @@ public class SslHandler } @Override - public final void sendFile(ChannelHandlerContext ctx, FileRegion region, ChannelPromise promise) throws Exception { - if (bufferChannel == null) { - bufferChannel = new BufferChannel(ctx.outboundByteBuffer()); - } - long written = 0; - try { - for (;;) { - long localWritten = region.transferTo(bufferChannel, written); - if (localWritten == -1) { - checkEOF(region, written); - flush(ctx, promise); - break; - } - written += localWritten; - if (written >= region.count()) { - flush(ctx, promise); - break; - } - } - } catch (IOException e) { - promise.setFailure(e); - } finally { - region.release(); - } - } - - private static void checkEOF(FileRegion region, long writtenBytes) throws IOException { - if (writtenBytes < region.count()) { - throw new EOFException("Expected to be able to write " - + region.count() + " bytes, but only wrote " - + writtenBytes); - } - } - - private static final class BufferChannel implements WritableByteChannel { - private final ByteBuf buffer; - - BufferChannel(ByteBuf buffer) { - this.buffer = buffer; - } - @Override - public int write(ByteBuffer src) { - int bytes = src.remaining(); - buffer.writeBytes(src); - return bytes; - } - - @Override - public boolean isOpen() { - return buffer.refCnt() > 0; - } - - @Override - public void close() { - // NOOP - } - } - - @Override - public void flush(final ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { - flush0(ctx, promise, false); - } - - private void flush0(ChannelHandlerContext ctx, ChannelPromise promise, boolean internal) throws Exception { - final ByteBuf in = ctx.outboundByteBuffer(); - final ByteBuf out = ctx.nextOutboundByteBuffer(); - + public void write(final ChannelHandlerContext ctx, MessageList msgs, ChannelPromise promise) + throws Exception { // Do not encrypt the first write request if this handler is // created with startTLS flag turned on. - if (!internal && startTls && !sentFirstMessage) { + if (startTls && !sentFirstMessage) { sentFirstMessage = true; - out.writeBytes(in); - ctx.flush(promise); + ctx.write(msgs, promise); return; } - - if (ctx.executor() == ctx.channel().eventLoop()) { - flushFutureNotifier.add(promise, in.readableBytes()); - } else { - synchronized (flushFutureNotifier) { - flushFutureNotifier.add(promise, in.readableBytes()); + for (int i = 0; i < msgs.size(); i++) { + ByteBuf msg = (ByteBuf) msgs.get(i); + ChannelPromise cp; + if (i + 1 == msgs.size()) { + cp = promise; + } else { + cp = ctx.newPromise(); } + pendingUnencryptedWrites.add(new PendingWrite(msg, cp)); } + flush0(ctx); + } + + private void flush0(ChannelHandlerContext ctx) throws SSLException { boolean unwrapLater = false; - int bytesConsumed = 0; + PendingWrite pending = null; + ByteBuf out = null; try { for (;;) { - SSLEngineResult result = wrap(engine, in, out); - bytesConsumed += result.bytesConsumed(); + if (out == null) { + out = ctx.alloc().buffer(); + } + pending = pendingUnencryptedWrites.peek(); + if (pending == null) { + break; + } + SSLEngineResult result = wrap(engine, pending.buf, out); + + if (!pending.buf.isReadable()) { + pending.buf.release(); + pendingUnencryptedWrites.remove(); + } + if (result.getStatus() == Status.CLOSED) { // SSLEngine has been closed already. // Any further write attempts should be denied. - if (in.isReadable()) { - in.clear(); - promise.setFailure(SSLENGINE_CLOSED); - ctx.fireExceptionCaught(SSLENGINE_CLOSED); - flush0(ctx, bytesConsumed, SSLENGINE_CLOSED); - bytesConsumed = 0; + boolean failed = false; + for (;;) { + PendingWrite w = pendingUnencryptedWrites.poll(); + if (w == null) { + break; + } + failed = true; + w.fail(SSLENGINE_CLOSED); } - break; + if (failed) { + ctx.fireExceptionCaught(SSLENGINE_CLOSED); + } + return; } else { switch (result.getHandshakeStatus()) { - case NEED_WRAP: - ctx.flush(); - continue; - case NEED_UNWRAP: - if (ctx.inboundByteBuffer().isReadable()) { - unwrapLater = true; - } - break; - case NEED_TASK: - runDelegatedTasks(); - continue; - case FINISHED: - setHandshakeSuccess(); - continue; - case NOT_HANDSHAKING: - // Workaround for TLS False Start problem reported at: - // https://github.com/netty/netty/issues/1108#issuecomment-14266970 - if (ctx.inboundByteBuffer().isReadable()) { - unwrapLater = true; - } - break; - default: - throw new IllegalStateException("Unknown handshake status: " + result.getHandshakeStatus()); + case NEED_WRAP: + if (!pending.buf.isReadable()) { + ctx.write(out, pending.promise); + } else { + ctx.write(out); + } + out = ctx.alloc().buffer(); + continue; + case NEED_UNWRAP: + if (internalBuffer().isReadable()) { + unwrapLater = true; + } + break; + case NEED_TASK: + runDelegatedTasks(); + continue; + case FINISHED: + setHandshakeSuccess(); + continue; + case NOT_HANDSHAKING: + // Workaround for TLS False Start problem reported at: + // https://github.com/netty/netty/issues/1108#issuecomment-14266970 + if (internalBuffer().isReadable()) { + unwrapLater = true; + } + break; + default: + throw new IllegalStateException("Unknown handshake status: " + result.getHandshakeStatus()); } if (result.bytesConsumed() == 0 && result.bytesProduced() == 0) { @@ -517,64 +474,88 @@ public class SslHandler } if (unwrapLater) { - inboundBufferUpdated(ctx); + decode0(ctx); } } catch (SSLException e) { setHandshakeFailure(e); throw e; } finally { - flush0(ctx, bytesConsumed); + if (out != null && out.isReadable()) { + if (pending != null && !pending.buf.isReadable()) { + ctx.write(out, pending.promise); + } else { + ctx.write(out); + } + out = null; + } else if (pending != null && !pending.buf.isReadable()) { + pending.promise.setSuccess(); + } + if (out != null) { + out.release(); + } } } - private void flush0(final ChannelHandlerContext ctx, final int bytesConsumed) { - ctx.flush(ctx.newPromise().addListener(new ChannelFutureListener() { - @Override - public void operationComplete(ChannelFuture future) throws Exception { - if (ctx.executor() == ctx.channel().eventLoop()) { - notifyFlushFutures(bytesConsumed, future); - } else { - synchronized (flushFutureNotifier) { - notifyFlushFutures(bytesConsumed, future); - } + private void flushNonAppData0(ChannelHandlerContext ctx) throws SSLException { + boolean unwrapLater = false; + ByteBuf out = null; + try { + for (;;) { + if (out == null) { + out = ctx.alloc().buffer(); + } + SSLEngineResult result = wrap(engine, Unpooled.EMPTY_BUFFER, out); + + if (result.bytesProduced() > 0) { + ctx.write(out); + out = null; + } + + switch (result.getHandshakeStatus()) { + case NEED_WRAP: + continue; + case NEED_UNWRAP: + if (internalBuffer().isReadable()) { + unwrapLater = true; + } + break; + case NEED_TASK: + runDelegatedTasks(); + continue; + case FINISHED: + setHandshakeSuccess(); + // try to flush now just in case as there may be pending write tasks + flush0(ctx); + return; + case NOT_HANDSHAKING: + // Workaround for TLS False Start problem reported at: + // https://github.com/netty/netty/issues/1108#issuecomment-14266970 + if (internalBuffer().isReadable()) { + unwrapLater = true; + } + // try to flush now just in case as there may be pending write tasks + flush0(ctx); + break; + default: + throw new IllegalStateException("Unknown handshake status: " + result.getHandshakeStatus()); + } + + if (result.bytesProduced() == 0) { + break; } } - private void notifyFlushFutures(final int bytesConsumed, ChannelFuture future) { - if (future.isSuccess()) { - flushFutureNotifier.increaseWriteCounter(bytesConsumed); - flushFutureNotifier.notifyFlushFutures(); - } else { - flushFutureNotifier.notifyFlushFutures(future.cause()); - } + if (unwrapLater) { + decode0(ctx); } - })); - } - - private void flush0(final ChannelHandlerContext ctx, final int bytesConsumed, final Throwable cause) { - ChannelFuture flushFuture = ctx.flush(ctx.newPromise().addListener(new ChannelFutureListener() { - @Override - public void operationComplete(ChannelFuture future) throws Exception { - if (ctx.executor() == ctx.channel().eventLoop()) { - notifyFlushFutures(bytesConsumed, cause, future); - } else { - synchronized (flushFutureNotifier) { - notifyFlushFutures(bytesConsumed, cause, future); - } - } + } catch (SSLException e) { + setHandshakeFailure(e); + throw e; + } finally { + if (out != null) { + out.release(); } - - private void notifyFlushFutures(int bytesConsumed, Throwable cause, ChannelFuture future) { - flushFutureNotifier.increaseWriteCounter(bytesConsumed); - if (future.isSuccess()) { - flushFutureNotifier.notifyFlushFutures(cause); - } else { - flushFutureNotifier.notifyFlushFutures(cause, future.cause()); - } - } - })); - - safeClose(ctx, flushFuture, ctx.newPromise()); + } } private static SSLEngineResult wrap(SSLEngine engine, ByteBuf in, ByteBuf out) throws SSLException { @@ -597,12 +578,7 @@ public class SslHandler // Make sure to release SSLEngine, // and notify the handshake future if the connection has been closed during handshake. setHandshakeFailure(CHANNEL_CLOSED); - - try { - inboundBufferUpdated(ctx); - } finally { - ctx.fireChannelInactive(); - } + super.channelInactive(ctx); } @Override @@ -787,8 +763,14 @@ public class SslHandler } @Override - public void inboundBufferUpdated(final ChannelHandlerContext ctx) throws Exception { - final ByteBuf in = ctx.inboundByteBuffer(); + public void decode(final ChannelHandlerContext ctx, ByteBuf in, MessageList out) throws Exception { + decode0(ctx); + } + + private ByteBuf decodeOut; + + private void decode0(final ChannelHandlerContext ctx) throws SSLException { + final ByteBuf in = internalBuffer(); if (in.readableBytes() < 5) { return; @@ -799,7 +781,7 @@ public class SslHandler if (packetLength == -1) { // Bad data - discard the buffer and raise an exception. NotSslRecordException e = new NotSslRecordException( - "not an SSL/TLS record: " + BufUtil.hexDump(in)); + "not an SSL/TLS record: " + ByteBufUtil.hexDump(in)); in.skipBytes(in.readableBytes()); ctx.fireExceptionCaught(e); setHandshakeFailure(e); @@ -808,43 +790,44 @@ public class SslHandler assert packetLength > 0; - final ByteBuf out = ctx.nextInboundByteBuffer(); - boolean wrapLater = false; int bytesProduced = 0; try { loop: for (;;) { - SSLEngineResult result = unwrap(engine, in, out); + if (decodeOut == null) { + decodeOut = ctx.alloc().buffer(); + } + SSLEngineResult result = unwrap(engine, in, decodeOut); bytesProduced += result.bytesProduced(); switch (result.getStatus()) { - case CLOSED: - // notify about the CLOSED state of the SSLEngine. See #137 - sslCloseFuture.trySuccess(ctx.channel()); - break; - case BUFFER_UNDERFLOW: - break loop; + case CLOSED: + // notify about the CLOSED state of the SSLEngine. See #137 + sslCloseFuture.trySuccess(ctx.channel()); + break; + case BUFFER_UNDERFLOW: + break loop; } switch (result.getHandshakeStatus()) { - case NEED_UNWRAP: - break; - case NEED_WRAP: - wrapLater = true; - break; - case NEED_TASK: - runDelegatedTasks(); - break; - case FINISHED: - setHandshakeSuccess(); - wrapLater = true; - continue; - case NOT_HANDSHAKING: - break; - default: - throw new IllegalStateException( - "Unknown handshake status: " + result.getHandshakeStatus()); + case NEED_UNWRAP: + break; + case NEED_WRAP: + wrapLater = true; + break; + case NEED_TASK: + runDelegatedTasks(); + break; + case FINISHED: + setHandshakeSuccess(); + wrapLater = true; + continue; + case NOT_HANDSHAKING: + break; + default: + throw new IllegalStateException( + "Unknown handshake status: " + result.getHandshakeStatus()); } if (result.bytesConsumed() == 0 && result.bytesProduced() == 0) { @@ -853,14 +836,15 @@ public class SslHandler } if (wrapLater) { - flush0(ctx, ctx.newPromise(), true); + flushNonAppData0(ctx); } } catch (SSLException e) { setHandshakeFailure(e); throw e; } finally { if (bytesProduced > 0) { - ctx.fireInboundBufferUpdated(); + ctx.fireMessageReceived(decodeOut); + decodeOut = null; } } } @@ -935,9 +919,14 @@ public class SslHandler // closeInbound() will raise an exception with bogus truncation attack warning. } } - notifyHandshakeFailure(cause); - flush0(ctx, 0, cause); + for (;;) { + PendingWrite write = pendingUnencryptedWrites.poll(); + if (write == null) { + break; + } + write.fail(cause); + } } private void notifyHandshakeFailure(Throwable cause) { @@ -961,7 +950,7 @@ public class SslHandler engine.closeOutbound(); ChannelPromise closeNotifyFuture = ctx.newPromise().addListener(closeNotifyWriteListener); - flush0(ctx, closeNotifyFuture, true); + write(ctx, MessageList.newInstance(Unpooled.EMPTY_BUFFER), closeNotifyFuture); safeClose(ctx, closeNotifyFuture, promise); } @@ -970,7 +959,7 @@ public class SslHandler this.ctx = ctx; if (ctx.channel().isActive()) { - // channelActvie() event has been fired already, which means this.channelActive() will + // channelActive() event has been fired already, which means this.channelActive() will // not be invoked. We have to initialize here instead. handshake0(); } else { @@ -1005,7 +994,7 @@ public class SslHandler }); try { engine.beginHandshake(); - flush0(ctx, ctx.newPromise(), true); + flushNonAppData0(ctx); } catch (Exception e) { notifyHandshakeFailure(e); } @@ -1024,6 +1013,7 @@ public class SslHandler @Override public void operationComplete(Future future) throws Exception { if (!future.isSuccess()) { + future.cause().printStackTrace(); ctx.close(); } } @@ -1031,7 +1021,6 @@ public class SslHandler } ctx.fireChannelActive(); } - private void safeClose( final ChannelHandlerContext ctx, ChannelFuture flushFuture, final ChannelPromise promise) { @@ -1095,4 +1084,18 @@ public class SslHandler return ctx.executor(); } } + + private static final class PendingWrite { + final ByteBuf buf; + final ChannelPromise promise; + PendingWrite(ByteBuf buf, ChannelPromise promise) { + this.buf = buf; + this.promise = promise; + } + + void fail(Throwable cause) { + buf.release(); + promise.setFailure(cause); + } + } } diff --git a/handler/src/main/java/io/netty/handler/stream/ChunkedMessageInput.java b/handler/src/main/java/io/netty/handler/stream/ChunkedMessageInput.java index b2fca7be39..52a534a625 100644 --- a/handler/src/main/java/io/netty/handler/stream/ChunkedMessageInput.java +++ b/handler/src/main/java/io/netty/handler/stream/ChunkedMessageInput.java @@ -15,7 +15,7 @@ */ package io.netty.handler.stream; -import io.netty.buffer.MessageBuf; +import io.netty.channel.MessageList; import java.util.Queue; @@ -23,6 +23,6 @@ import java.util.Queue; * {@link ChunkedInput} which reads its chunks and transfer it to a {@link Queue} * */ -public interface ChunkedMessageInput extends ChunkedInput> { +public interface ChunkedMessageInput extends ChunkedInput> { } 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 99dd761c37..03d819e6e9 100644 --- a/handler/src/main/java/io/netty/handler/stream/ChunkedWriteHandler.java +++ b/handler/src/main/java/io/netty/handler/stream/ChunkedWriteHandler.java @@ -15,7 +15,7 @@ */ package io.netty.handler.stream; -import io.netty.buffer.MessageBuf; +import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelDuplexHandler; @@ -23,14 +23,15 @@ import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelHandlerUtil; -import io.netty.channel.ChannelOutboundMessageHandler; import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPromise; +import io.netty.channel.MessageList; import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; import java.nio.channels.ClosedChannelException; +import java.util.ArrayDeque; +import java.util.Queue; import java.util.concurrent.atomic.AtomicInteger; /** @@ -65,12 +66,12 @@ import java.util.concurrent.atomic.AtomicInteger; * call {@link #resumeTransfer()}. */ public class ChunkedWriteHandler - extends ChannelDuplexHandler implements ChannelOutboundMessageHandler { + extends ChannelDuplexHandler { private static final InternalLogger logger = InternalLoggerFactory.getInstance(ChunkedWriteHandler.class); - private final MessageBuf queue = Unpooled.messageBuffer(); + private final Queue queue = new ArrayDeque(); private final int maxPendingWrites; private volatile ChannelHandlerContext ctx; private final AtomicInteger pendingWrites = new AtomicInteger(); @@ -88,11 +89,6 @@ public class ChunkedWriteHandler this.maxPendingWrites = maxPendingWrites; } - @Override - public MessageBuf newOutboundBuffer(ChannelHandlerContext ctx) throws Exception { - return queue; - } - @Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { this.ctx = ctx; @@ -142,18 +138,17 @@ public class ChunkedWriteHandler } @Override - public void flush(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { + public void write(ChannelHandlerContext ctx, MessageList msgs, ChannelPromise promise) throws Exception { + for (int i = 0; i < msgs.size(); i++) { + queue.add(msgs.get(i)); + } + msgs.recycle(); queue.add(promise); if (isWritable() || !ctx.channel().isActive()) { doFlush(ctx); } } - @Override - public void inboundBufferUpdated(ChannelHandlerContext ctx) throws Exception { - ctx.fireInboundBufferUpdated(); - } - @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { doFlush(ctx); @@ -213,6 +208,7 @@ public class ChunkedWriteHandler discard(ctx, null); return; } + MessageList messages = MessageList.newInstance(); while (isWritable()) { if (currentEvent == null) { currentEvent = queue.poll(); @@ -225,14 +221,16 @@ public class ChunkedWriteHandler final Object currentEvent = this.currentEvent; if (currentEvent instanceof ChannelPromise) { this.currentEvent = null; - ctx.flush((ChannelPromise) currentEvent); + ctx.write(messages, (ChannelPromise) currentEvent); + messages = MessageList.newInstance(); } else if (currentEvent instanceof ChunkedInput) { + MessageList out = MessageList.newInstance(); final ChunkedInput chunks = (ChunkedInput) currentEvent; boolean read; boolean endOfInput; boolean suspend; try { - read = readChunk(ctx, chunks); + read = readChunk(ctx, chunks, out); endOfInput = chunks.isEndOfInput(); if (!read) { @@ -267,7 +265,7 @@ public class ChunkedWriteHandler } pendingWrites.incrementAndGet(); - ChannelFuture f = ctx.flush(); + ChannelFuture f = ctx.write(out); if (endOfInput) { this.currentEvent = null; @@ -307,7 +305,7 @@ public class ChunkedWriteHandler }); } } else { - ChannelHandlerUtil.addToNextOutboundBuffer(ctx, currentEvent); + ctx.write(currentEvent); this.currentEvent = null; } @@ -326,11 +324,16 @@ public class ChunkedWriteHandler * @throws Exception if something goes wrong */ @SuppressWarnings("unchecked") - protected boolean readChunk(ChannelHandlerContext ctx, ChunkedInput chunks) throws Exception { + protected boolean readChunk( + @SuppressWarnings("UnusedParameters") ChannelHandlerContext ctx, + ChunkedInput chunks, MessageList out) throws Exception { if (chunks instanceof ChunkedByteInput) { - return ((ChunkedByteInput) chunks).readChunk(ctx.nextOutboundByteBuffer()); + ByteBuf buf = Unpooled.buffer(); + boolean done = ((ChunkedByteInput) chunks).readChunk(buf); + out.add(buf); + return done; } else if (chunks instanceof ChunkedMessageInput) { - return ((ChunkedMessageInput) chunks).readChunk(ctx.nextOutboundMessageBuffer()); + return ((ChunkedMessageInput) chunks).readChunk(out); } else { throw new IllegalArgumentException("ChunkedInput instance " + chunks + " not supported"); } diff --git a/handler/src/main/java/io/netty/handler/timeout/IdleStateHandler.java b/handler/src/main/java/io/netty/handler/timeout/IdleStateHandler.java index 5ccf794809..0afd0c9c05 100644 --- a/handler/src/main/java/io/netty/handler/timeout/IdleStateHandler.java +++ b/handler/src/main/java/io/netty/handler/timeout/IdleStateHandler.java @@ -21,12 +21,12 @@ import io.netty.channel.ChannelDuplexHandler; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelOperationHandler; +import io.netty.channel.ChannelOutboundHandler; import io.netty.channel.ChannelPromise; -import io.netty.channel.ChannelStateHandlerAdapter; +import io.netty.channel.MessageList; import io.netty.util.concurrent.EventExecutor; -import io.netty.channel.FileRegion; import java.net.SocketAddress; import java.util.concurrent.ScheduledFuture; @@ -98,7 +98,7 @@ import java.util.concurrent.TimeUnit; * @see ReadTimeoutHandler * @see WriteTimeoutHandler */ -public class IdleStateHandler extends ChannelStateHandlerAdapter implements ChannelOperationHandler { +public class IdleStateHandler extends ChannelInboundHandlerAdapter implements ChannelOutboundHandler { private final long readerIdleTimeMillis; private final long writerIdleTimeMillis; @@ -251,10 +251,10 @@ public class IdleStateHandler extends ChannelStateHandlerAdapter implements Chan } @Override - public void inboundBufferUpdated(ChannelHandlerContext ctx) throws Exception { + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { lastReadTime = System.currentTimeMillis(); firstReaderIdleEvent = firstAllIdleEvent = true; - ctx.fireInboundBufferUpdated(); + ctx.fireMessageReceived(msgs); } @Override @@ -263,8 +263,7 @@ public class IdleStateHandler extends ChannelStateHandlerAdapter implements Chan } @Override - - public void flush(final ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { + public void write(ChannelHandlerContext ctx, MessageList msgs, ChannelPromise promise) throws Exception { promise.addListener(new ChannelFutureListener() { @Override public void operationComplete(ChannelFuture future) throws Exception { @@ -272,8 +271,7 @@ public class IdleStateHandler extends ChannelStateHandlerAdapter implements Chan firstWriterIdleEvent = firstAllIdleEvent = true; } }); - - ctx.flush(promise); + ctx.write(msgs, promise); } @Override @@ -302,18 +300,6 @@ public class IdleStateHandler extends ChannelStateHandlerAdapter implements Chan ctx.deregister(promise); } - @Override - public void sendFile(ChannelHandlerContext ctx, FileRegion region, ChannelPromise promise) throws Exception { - promise.addListener(new ChannelFutureListener() { - @Override - public void operationComplete(ChannelFuture future) throws Exception { - lastWriteTime = System.currentTimeMillis(); - firstWriterIdleEvent = firstAllIdleEvent = true; - } - }); - ctx.sendFile(region, promise); - } - private void initialize(ChannelHandlerContext ctx) { // Avoid the case where destroy() is called before scheduling timeouts. // See: https://github.com/netty/netty/issues/143 diff --git a/handler/src/main/java/io/netty/handler/timeout/ReadTimeoutHandler.java b/handler/src/main/java/io/netty/handler/timeout/ReadTimeoutHandler.java index 687445163e..0327906107 100644 --- a/handler/src/main/java/io/netty/handler/timeout/ReadTimeoutHandler.java +++ b/handler/src/main/java/io/netty/handler/timeout/ReadTimeoutHandler.java @@ -19,8 +19,9 @@ import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelDuplexHandler; import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelStateHandlerAdapter; +import io.netty.channel.MessageList; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -61,7 +62,7 @@ import java.util.concurrent.TimeUnit; * @see WriteTimeoutHandler * @see IdleStateHandler */ -public class ReadTimeoutHandler extends ChannelStateHandlerAdapter { +public class ReadTimeoutHandler extends ChannelInboundHandlerAdapter { private final long timeoutMillis; @@ -144,9 +145,9 @@ public class ReadTimeoutHandler extends ChannelStateHandlerAdapter { } @Override - public void inboundBufferUpdated(ChannelHandlerContext ctx) throws Exception { + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { lastReadTime = System.currentTimeMillis(); - ctx.fireInboundBufferUpdated(); + ctx.fireMessageReceived(msgs); } private void initialize(ChannelHandlerContext ctx) { 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 a43d12feae..c5f85f03be 100644 --- a/handler/src/main/java/io/netty/handler/timeout/WriteTimeoutHandler.java +++ b/handler/src/main/java/io/netty/handler/timeout/WriteTimeoutHandler.java @@ -22,9 +22,9 @@ import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelOperationHandlerAdapter; +import io.netty.channel.ChannelOutboundHandlerAdapter; import io.netty.channel.ChannelPromise; -import io.netty.channel.FileRegion; +import io.netty.channel.MessageList; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -65,7 +65,7 @@ import java.util.concurrent.TimeUnit; * @see ReadTimeoutHandler * @see IdleStateHandler */ -public class WriteTimeoutHandler extends ChannelOperationHandlerAdapter { +public class WriteTimeoutHandler extends ChannelOutboundHandlerAdapter { private final long timeoutMillis; @@ -102,15 +102,9 @@ public class WriteTimeoutHandler extends ChannelOperationHandlerAdapter { } @Override - public void flush(final ChannelHandlerContext ctx, final ChannelPromise promise) throws Exception { + public void write(ChannelHandlerContext ctx, MessageList msgs, ChannelPromise promise) throws Exception { scheduleTimeout(ctx, promise); - ctx.flush(promise); - } - - @Override - public void sendFile(ChannelHandlerContext ctx, FileRegion region, ChannelPromise promise) throws Exception { - scheduleTimeout(ctx, promise); - super.sendFile(ctx, region, promise); + super.write(ctx, msgs, promise); } private void scheduleTimeout(final ChannelHandlerContext ctx, final ChannelPromise future) { diff --git a/handler/src/main/java/io/netty/handler/traffic/AbstractTrafficShapingHandler.java b/handler/src/main/java/io/netty/handler/traffic/AbstractTrafficShapingHandler.java index 1166c407a5..8723bd8e3d 100644 --- a/handler/src/main/java/io/netty/handler/traffic/AbstractTrafficShapingHandler.java +++ b/handler/src/main/java/io/netty/handler/traffic/AbstractTrafficShapingHandler.java @@ -19,6 +19,7 @@ import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelDuplexHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPromise; +import io.netty.channel.MessageList; import io.netty.util.Attribute; import io.netty.util.AttributeKey; @@ -74,7 +75,6 @@ public abstract class AbstractTrafficShapingHandler extends ChannelDuplexHandler private static final AttributeKey READ_SUSPENDED = new AttributeKey("readSuspended"); private static final AttributeKey REOPEN_TASK = new AttributeKey("reopenTask"); - private static final AttributeKey BUFFER_UPDATE_TASK = new AttributeKey("bufferUpdateTask"); /** * @@ -210,25 +210,27 @@ public abstract class AbstractTrafficShapingHandler extends ChannelDuplexHandler } @Override - public void inboundBufferUpdated(final ChannelHandlerContext ctx) throws Exception { - ByteBuf buf = ctx.nextInboundByteBuffer(); - + public void messageReceived(final ChannelHandlerContext ctx, final MessageList msgs) throws Exception { + MessageList buffers = msgs.cast(); + long size = 0; + for (int i = 0; i < buffers.size(); i++) { + size += buffers.get(i).readableBytes(); + } long curtime = System.currentTimeMillis(); - long size = buf.readableBytes(); if (trafficCounter != null) { trafficCounter.bytesRecvFlowControl(size); if (readLimit == 0) { // no action - ctx.fireInboundBufferUpdated(); + ctx.fireMessageReceived(msgs); return; } // compute the number of ms to wait before reopening the channel long wait = getTimeToWait(readLimit, - trafficCounter.currentReadBytes(), - trafficCounter.lastTime(), curtime); + trafficCounter.currentReadBytes(), + trafficCounter.lastTime(), curtime); if (wait >= MINIMAL_WAIT) { // At least 10ms seems a minimal // time in order to // try to limit the traffic @@ -244,27 +246,22 @@ public abstract class AbstractTrafficShapingHandler extends ChannelDuplexHandler attr.set(reopenTask); } ctx.executor().schedule(reopenTask, wait, - TimeUnit.MILLISECONDS); + TimeUnit.MILLISECONDS); } else { // Create a Runnable to update the next handler in the chain. If one was create before it will // just be reused to limit object creation - Attribute attr = ctx.attr(BUFFER_UPDATE_TASK); - Runnable bufferUpdateTask = attr.get(); - if (bufferUpdateTask == null) { - bufferUpdateTask = new Runnable() { - @Override - public void run() { - ctx.fireInboundBufferUpdated(); - } - }; - attr.set(bufferUpdateTask); - } + Runnable bufferUpdateTask = new Runnable() { + @Override + public void run() { + ctx.fireMessageReceived(msgs); + } + }; ctx.executor().schedule(bufferUpdateTask, wait, TimeUnit.MILLISECONDS); return; } } } - ctx.fireInboundBufferUpdated(); + ctx.fireMessageReceived(msgs); } @Override @@ -275,14 +272,18 @@ public abstract class AbstractTrafficShapingHandler extends ChannelDuplexHandler } @Override - public void flush(final ChannelHandlerContext ctx, final ChannelPromise promise) throws Exception { + public void write(final ChannelHandlerContext ctx, final MessageList msgs, final ChannelPromise promise) + throws Exception { long curtime = System.currentTimeMillis(); - long size = ctx.nextOutboundByteBuffer().readableBytes(); + long size = 0; + for (int i = 0; i < msgs.size(); i++) { + size += ((ByteBuf) msgs.get(i)).readableBytes(); + } if (trafficCounter != null) { trafficCounter.bytesWriteFlowControl(size); if (writeLimit == 0) { - ctx.flush(promise); + ctx.write(msgs, promise); return; } // compute the number of ms to wait before continue with the @@ -294,13 +295,13 @@ public abstract class AbstractTrafficShapingHandler extends ChannelDuplexHandler ctx.executor().schedule(new Runnable() { @Override public void run() { - ctx.flush(promise); + ctx.write(msgs, promise); } }, wait, TimeUnit.MILLISECONDS); return; } } - ctx.flush(promise); + ctx.write(msgs, promise); } /** diff --git a/handler/src/test/java/io/netty/handler/stream/ChunkedWriteHandlerTest.java b/handler/src/test/java/io/netty/handler/stream/ChunkedWriteHandlerTest.java index 99990f7231..6c5fef1c87 100644 --- a/handler/src/test/java/io/netty/handler/stream/ChunkedWriteHandlerTest.java +++ b/handler/src/test/java/io/netty/handler/stream/ChunkedWriteHandlerTest.java @@ -16,12 +16,11 @@ package io.netty.handler.stream; import io.netty.buffer.ByteBuf; -import io.netty.buffer.MessageBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; -import io.netty.channel.embedded.EmbeddedByteChannel; -import io.netty.channel.embedded.EmbeddedMessageChannel; +import io.netty.channel.MessageList; +import io.netty.channel.embedded.EmbeddedChannel; import io.netty.util.CharsetUtil; import org.junit.Test; import java.io.ByteArrayInputStream; @@ -135,9 +134,8 @@ public class ChunkedWriteHandlerTest { } }; - EmbeddedByteChannel ch = new EmbeddedByteChannel(new ChunkedWriteHandler()); - ch.outboundMessageBuffer().add(input); - ch.flush().addListener(listener).syncUninterruptibly(); + EmbeddedChannel ch = new EmbeddedChannel(new ChunkedWriteHandler()); + ch.write(input).addListener(listener).syncUninterruptibly(); ch.checkException(); ch.finish(); @@ -165,7 +163,7 @@ public class ChunkedWriteHandlerTest { } @Override - public boolean readChunk(MessageBuf buffer) throws Exception { + public boolean readChunk(MessageList buffer) throws Exception { if (done) { return false; } @@ -175,9 +173,8 @@ public class ChunkedWriteHandlerTest { } }; - EmbeddedMessageChannel ch = new EmbeddedMessageChannel(new ChunkedWriteHandler()); - ch.outboundMessageBuffer().add(input); - ch.flush().syncUninterruptibly(); + EmbeddedChannel ch = new EmbeddedChannel(new ChunkedWriteHandler()); + ch.write(input).syncUninterruptibly(); ch.checkException(); assertTrue(ch.finish()); @@ -186,7 +183,7 @@ public class ChunkedWriteHandlerTest { } private static void check(ChunkedInput... inputs) { - EmbeddedByteChannel ch = new EmbeddedByteChannel(new ChunkedWriteHandler()); + EmbeddedChannel ch = new EmbeddedChannel(new ChunkedWriteHandler()); for (ChunkedInput input: inputs) { ch.writeOutbound(input); @@ -197,7 +194,7 @@ public class ChunkedWriteHandlerTest { int i = 0; int read = 0; for (;;) { - ByteBuf buffer = ch.readOutbound(); + ByteBuf buffer = (ByteBuf) ch.readOutbound(); if (buffer == null) { break; } diff --git a/microbench/pom.xml b/microbench/pom.xml index c54d7265cb..c6633a024f 100644 --- a/microbench/pom.xml +++ b/microbench/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.0.0.Final-SNAPSHOT + 4.0.0.CR4-SNAPSHOT netty-microbench diff --git a/pom.xml b/pom.xml index b92e8acdb5..505d26232d 100644 --- a/pom.xml +++ b/pom.xml @@ -26,7 +26,7 @@ io.netty netty-parent pom - 4.0.0.Final-SNAPSHOT + 4.0.0.CR4-SNAPSHOT Netty http://netty.io/ diff --git a/tarball/pom.xml b/tarball/pom.xml index 47fb068a57..5af86caa5e 100644 --- a/tarball/pom.xml +++ b/tarball/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.0.0.Final-SNAPSHOT + 4.0.0.CR4-SNAPSHOT netty-tarball diff --git a/testsuite-osgi/pom.xml b/testsuite-osgi/pom.xml index 38a41def77..d3c487768e 100644 --- a/testsuite-osgi/pom.xml +++ b/testsuite-osgi/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.0.0.Final-SNAPSHOT + 4.0.0.CR4-SNAPSHOT netty-testsuite-osgi-parent diff --git a/testsuite-osgi/testsuite-osgi-deps/pom.xml b/testsuite-osgi/testsuite-osgi-deps/pom.xml index 66e804e552..0bbde8a045 100644 --- a/testsuite-osgi/testsuite-osgi-deps/pom.xml +++ b/testsuite-osgi/testsuite-osgi-deps/pom.xml @@ -21,7 +21,7 @@ io.netty netty-testsuite-osgi-parent - 4.0.0.Final-SNAPSHOT + 4.0.0.CR4-SNAPSHOT netty-testsuite-osgi-deps diff --git a/testsuite-osgi/testsuite-osgi-exam/pom.xml b/testsuite-osgi/testsuite-osgi-exam/pom.xml index 00b89b960f..26493c10dc 100644 --- a/testsuite-osgi/testsuite-osgi-exam/pom.xml +++ b/testsuite-osgi/testsuite-osgi-exam/pom.xml @@ -21,7 +21,7 @@ io.netty netty-testsuite-osgi-parent - 4.0.0.Final-SNAPSHOT + 4.0.0.CR4-SNAPSHOT netty-testsuite-osgi-exam diff --git a/testsuite-osgi/testsuite-osgi-split/pom.xml b/testsuite-osgi/testsuite-osgi-split/pom.xml index ee743e3f54..0a752e7c3b 100644 --- a/testsuite-osgi/testsuite-osgi-split/pom.xml +++ b/testsuite-osgi/testsuite-osgi-split/pom.xml @@ -21,7 +21,7 @@ io.netty netty-testsuite-osgi-parent - 4.0.0.Final-SNAPSHOT + 4.0.0.CR4-SNAPSHOT netty-testsuite-osgi-split diff --git a/testsuite/pom.xml b/testsuite/pom.xml index 12caa73f17..76f3dbf5e5 100644 --- a/testsuite/pom.xml +++ b/testsuite/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.0.0.Final-SNAPSHOT + 4.0.0.CR4-SNAPSHOT netty-testsuite diff --git a/testsuite/src/test/java/io/netty/testsuite/transport/sctp/SctpEchoTest.java b/testsuite/src/test/java/io/netty/testsuite/transport/sctp/SctpEchoTest.java index 62fecd7efe..dd15f1df7e 100644 --- a/testsuite/src/test/java/io/netty/testsuite/transport/sctp/SctpEchoTest.java +++ b/testsuite/src/test/java/io/netty/testsuite/transport/sctp/SctpEchoTest.java @@ -21,9 +21,9 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelHandlerUtil; -import io.netty.channel.ChannelInboundByteHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInitializer; +import io.netty.channel.MessageList; import io.netty.channel.sctp.SctpChannel; import io.netty.handler.codec.sctp.SctpInboundByteStreamHandler; import io.netty.handler.codec.sctp.SctpMessageCompletionHandler; @@ -54,22 +54,12 @@ public class SctpEchoTest extends AbstractSctpTest { } public void testSimpleEcho(ServerBootstrap sb, Bootstrap cb) throws Throwable { - testSimpleEcho0(sb, cb, Integer.MAX_VALUE); + testSimpleEcho0(sb, cb); } - @Test - public void testSimpleEchoWithBoundedBuffer() throws Throwable { - Assume.assumeTrue(TestUtils.isSctpSupported()); - run(); - } - - public void testSimpleEchoWithBoundedBuffer(ServerBootstrap sb, Bootstrap cb) throws Throwable { - testSimpleEcho0(sb, cb, 4096); - } - - private static void testSimpleEcho0(ServerBootstrap sb, Bootstrap cb, int maxInboundBufferSize) throws Throwable { - final EchoHandler sh = new EchoHandler(maxInboundBufferSize); - final EchoHandler ch = new EchoHandler(maxInboundBufferSize); + private static void testSimpleEcho0(ServerBootstrap sb, Bootstrap cb) throws Throwable { + final EchoHandler sh = new EchoHandler(); + final EchoHandler ch = new EchoHandler(); sb.childHandler(new ChannelInitializer() { @Override @@ -149,21 +139,11 @@ public class SctpEchoTest extends AbstractSctpTest { } } - private static class EchoHandler extends ChannelInboundByteHandlerAdapter { - private final int maxInboundBufferSize; + private static class EchoHandler extends ChannelInboundHandlerAdapter { volatile Channel channel; final AtomicReference exception = new AtomicReference(); volatile int counter; - EchoHandler(int maxInboundBufferSize) { - this.maxInboundBufferSize = maxInboundBufferSize; - } - - @Override - public ByteBuf newInboundBuffer(ChannelHandlerContext ctx) throws Exception { - return ChannelHandlerUtil.allocate(ctx, 0, maxInboundBufferSize); - } - @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { @@ -171,22 +151,23 @@ public class SctpEchoTest extends AbstractSctpTest { } @Override - public void inboundBufferUpdated( - ChannelHandlerContext ctx, ByteBuf in) - throws Exception { - byte[] actual = new byte[in.readableBytes()]; - in.readBytes(actual); + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + for (int j = 0; j < msgs.size(); j ++) { + ByteBuf in = (ByteBuf) msgs.get(j); + byte[] actual = new byte[in.readableBytes()]; + in.readBytes(actual); - int lastIdx = counter; - for (int i = 0; i < actual.length; i++) { - assertEquals(data[i + lastIdx], actual[i]); + int lastIdx = counter; + for (int i = 0; i < actual.length; i++) { + assertEquals(data[i + lastIdx], actual[i]); + } + + if (channel.parent() != null) { + channel.write(Unpooled.wrappedBuffer(actual)); + } + + counter += actual.length; } - - if (channel.parent() != null) { - channel.write(Unpooled.wrappedBuffer(actual)); - } - - counter += actual.length; } @Override diff --git a/testsuite/src/test/java/io/netty/testsuite/transport/socket/DatagramMulticastTest.java b/testsuite/src/test/java/io/netty/testsuite/transport/socket/DatagramMulticastTest.java index a62104781e..5a59cc8615 100644 --- a/testsuite/src/test/java/io/netty/testsuite/transport/socket/DatagramMulticastTest.java +++ b/testsuite/src/test/java/io/netty/testsuite/transport/socket/DatagramMulticastTest.java @@ -19,8 +19,9 @@ import io.netty.bootstrap.Bootstrap; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelOption; +import io.netty.channel.MessageList; import io.netty.channel.socket.DatagramChannel; import io.netty.channel.socket.DatagramPacket; import io.netty.channel.socket.oio.OioDatagramChannel; @@ -43,12 +44,11 @@ public class DatagramMulticastTest extends AbstractDatagramTest { public void testMulticast(Bootstrap sb, Bootstrap cb) throws Throwable { MulticastTestHandler mhandler = new MulticastTestHandler(); - sb.handler(new ChannelInboundMessageHandlerAdapter() { + sb.handler(new ChannelInboundHandlerAdapter() { @Override - public void messageReceived( - ChannelHandlerContext ctx, - DatagramPacket msg) throws Exception { + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { // Nothing will be sent. + msgs.releaseAllAndRecycle(); } }); @@ -92,19 +92,21 @@ public class DatagramMulticastTest extends AbstractDatagramTest { cc.close().awaitUninterruptibly(); } - private static final class MulticastTestHandler extends ChannelInboundMessageHandlerAdapter { + private static final class MulticastTestHandler extends ChannelInboundHandlerAdapter { private final CountDownLatch latch = new CountDownLatch(1); private boolean done; private volatile boolean fail; @Override - public void messageReceived(ChannelHandlerContext ctx, DatagramPacket msg) throws Exception { - if (done) { + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + if (done || msgs.size() != 1) { fail = true; } - assertEquals(1, msg.content().readInt()); + assertEquals(1, ((DatagramPacket) msgs.get(0)).content().readInt()); + msgs.releaseAllAndRecycle(); + latch.countDown(); // mark the handler as done as we only are supposed to receive one message diff --git a/testsuite/src/test/java/io/netty/testsuite/transport/socket/DatagramUnicastTest.java b/testsuite/src/test/java/io/netty/testsuite/transport/socket/DatagramUnicastTest.java index 9e1237e960..4ad4c9320b 100644 --- a/testsuite/src/test/java/io/netty/testsuite/transport/socket/DatagramUnicastTest.java +++ b/testsuite/src/test/java/io/netty/testsuite/transport/socket/DatagramUnicastTest.java @@ -19,7 +19,8 @@ import io.netty.bootstrap.Bootstrap; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.MessageList; import io.netty.channel.socket.DatagramPacket; import org.junit.Test; @@ -38,22 +39,21 @@ public class DatagramUnicastTest extends AbstractDatagramTest { public void testSimpleSend(Bootstrap sb, Bootstrap cb) throws Throwable { final CountDownLatch latch = new CountDownLatch(1); - sb.handler(new ChannelInboundMessageHandlerAdapter() { + sb.handler(new ChannelInboundHandlerAdapter() { @Override - public void messageReceived( - ChannelHandlerContext ctx, - DatagramPacket msg) throws Exception { - assertEquals(1, msg.content().readInt()); + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + assertEquals(1, msgs.size()); + assertEquals(1, ((DatagramPacket) msgs.get(0)).content().readInt()); + msgs.releaseAllAndRecycle(); latch.countDown(); } }); - cb.handler(new ChannelInboundMessageHandlerAdapter() { + cb.handler(new ChannelInboundHandlerAdapter() { @Override - public void messageReceived( - ChannelHandlerContext ctx, - DatagramPacket msg) throws Exception { + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { // Nothing will be sent. + msgs.releaseAllAndRecycle(); } }); diff --git a/testsuite/src/test/java/io/netty/testsuite/transport/socket/ServerSocketSuspendTest.java b/testsuite/src/test/java/io/netty/testsuite/transport/socket/ServerSocketSuspendTest.java index 726a97c8a0..af3fe50a72 100644 --- a/testsuite/src/test/java/io/netty/testsuite/transport/socket/ServerSocketSuspendTest.java +++ b/testsuite/src/test/java/io/netty/testsuite/transport/socket/ServerSocketSuspendTest.java @@ -16,11 +16,10 @@ package io.netty.testsuite.transport.socket; import io.netty.bootstrap.ServerBootstrap; -import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundByteHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelOption; import org.junit.Ignore; import org.junit.Test; @@ -94,7 +93,7 @@ public class ServerSocketSuspendTest extends AbstractServerSocketTest { } @ChannelHandler.Sharable - private static final class AcceptedChannelCounter extends ChannelInboundByteHandlerAdapter { + private static final class AcceptedChannelCounter extends ChannelInboundHandlerAdapter { final CountDownLatch latch; @@ -106,10 +105,5 @@ public class ServerSocketSuspendTest extends AbstractServerSocketTest { public void channelActive(ChannelHandlerContext ctx) throws Exception { latch.countDown(); } - - @Override - public void inboundBufferUpdated(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - // Unused - } } } diff --git a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketBufReleaseTest.java b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketBufReleaseTest.java index fb16050f16..ae02ab802d 100644 --- a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketBufReleaseTest.java +++ b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketBufReleaseTest.java @@ -22,7 +22,8 @@ import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.MessageList; import io.netty.util.concurrent.DefaultEventExecutorGroup; import io.netty.util.concurrent.DefaultPromise; import io.netty.util.concurrent.DefaultThreadFactory; @@ -66,7 +67,7 @@ public class SocketBufReleaseTest extends AbstractSocketTest { clientHandler.check(); } - private static class BufWriterHandler extends ChannelInboundMessageHandlerAdapter { + private static class BufWriterHandler extends ChannelInboundHandlerAdapter { private final Random random = new Random(); private final CountDownLatch latch = new CountDownLatch(1); @@ -95,8 +96,8 @@ public class SocketBufReleaseTest extends AbstractSocketTest { } @Override - public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { - // Discard + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + msgs.releaseAllAndRecycle(); } public void check() throws InterruptedException { diff --git a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketEchoTest.java b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketEchoTest.java index 35b4ec84bd..2d3e2f9e94 100644 --- a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketEchoTest.java +++ b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketEchoTest.java @@ -21,9 +21,9 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelHandlerUtil; -import io.netty.channel.ChannelInboundByteHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInitializer; +import io.netty.channel.MessageList; import io.netty.channel.socket.SocketChannel; import io.netty.util.concurrent.DefaultEventExecutorGroup; import io.netty.util.concurrent.EventExecutorGroup; @@ -64,34 +64,16 @@ public class SocketEchoTest extends AbstractSocketTest { } public void testSimpleEcho(ServerBootstrap sb, Bootstrap cb) throws Throwable { - testSimpleEcho0(sb, cb, Integer.MAX_VALUE, false, false); + testSimpleEcho0(sb, cb, false, false); } @Test(timeout = 30000) - public void testSimpleEchoWithBridge() throws Throwable { + public void testSimpleEchoWithAdditionalExecutor() throws Throwable { run(); } - public void testSimpleEchoWithBridge(ServerBootstrap sb, Bootstrap cb) throws Throwable { - testSimpleEcho0(sb, cb, Integer.MAX_VALUE, true, false); - } - - @Test(timeout = 30000) - public void testSimpleEchoWithBoundedBuffer() throws Throwable { - run(); - } - - public void testSimpleEchoWithBoundedBuffer(ServerBootstrap sb, Bootstrap cb) throws Throwable { - testSimpleEcho0(sb, cb, 32, false, false); - } - - @Test(timeout = 30000) - public void testSimpleEchoWithBridgedBoundedBuffer() throws Throwable { - run(); - } - - public void testSimpleEchoWithBridgedBoundedBuffer(ServerBootstrap sb, Bootstrap cb) throws Throwable { - testSimpleEcho0(sb, cb, 32, true, false); + public void testSimpleEchoWithAdditionalExecutor(ServerBootstrap sb, Bootstrap cb) throws Throwable { + testSimpleEcho0(sb, cb, true, false); } @Test(timeout = 30000) @@ -100,26 +82,26 @@ public class SocketEchoTest extends AbstractSocketTest { } public void testSimpleEchoWithVoidPromise(ServerBootstrap sb, Bootstrap cb) throws Throwable { - testSimpleEcho0(sb, cb, Integer.MAX_VALUE, false, true); + testSimpleEcho0(sb, cb, false, true); } @Test(timeout = 30000) - public void testSimpleEchoWithBridgeAndVoidPromise() throws Throwable { + public void testSimpleEchoWithAdditionalExecutorAndVoidPromise() throws Throwable { run(); } - public void testSimpleEchoWithBridgeAndVoidPromise(ServerBootstrap sb, Bootstrap cb) throws Throwable { - testSimpleEcho0(sb, cb, Integer.MAX_VALUE, true, true); + public void testSimpleEchoWithAdditionalExecutorAndVoidPromise(ServerBootstrap sb, Bootstrap cb) throws Throwable { + testSimpleEcho0(sb, cb, true, true); } private static void testSimpleEcho0( - ServerBootstrap sb, Bootstrap cb, int maxInboundBufferSize, boolean bridge, boolean voidPromise) + ServerBootstrap sb, Bootstrap cb, boolean additionalExecutor, boolean voidPromise) throws Throwable { - final EchoHandler sh = new EchoHandler(maxInboundBufferSize); - final EchoHandler ch = new EchoHandler(maxInboundBufferSize); + final EchoHandler sh = new EchoHandler(); + final EchoHandler ch = new EchoHandler(); - if (bridge) { + if (additionalExecutor) { sb.childHandler(new ChannelInitializer() { @Override protected void initChannel(SocketChannel c) throws Exception { @@ -199,21 +181,11 @@ public class SocketEchoTest extends AbstractSocketTest { } } - private static class EchoHandler extends ChannelInboundByteHandlerAdapter { - private final int maxInboundBufferSize; + private static class EchoHandler extends ChannelInboundHandlerAdapter { volatile Channel channel; final AtomicReference exception = new AtomicReference(); volatile int counter; - EchoHandler(int maxInboundBufferSize) { - this.maxInboundBufferSize = maxInboundBufferSize; - } - - @Override - public ByteBuf newInboundBuffer(ChannelHandlerContext ctx) throws Exception { - return ChannelHandlerUtil.allocate(ctx, 0, maxInboundBufferSize); - } - @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { @@ -221,22 +193,24 @@ public class SocketEchoTest extends AbstractSocketTest { } @Override - public void inboundBufferUpdated( - ChannelHandlerContext ctx, ByteBuf in) - throws Exception { - byte[] actual = new byte[in.readableBytes()]; - in.readBytes(actual); + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + for (int j = 0; j < msgs.size(); j ++) { + ByteBuf in = (ByteBuf) msgs.get(j); + byte[] actual = new byte[in.readableBytes()]; + in.readBytes(actual); - int lastIdx = counter; - for (int i = 0; i < actual.length; i ++) { - assertEquals(data[i + lastIdx], actual[i]); + int lastIdx = counter; + for (int i = 0; i < actual.length; i ++) { + assertEquals(data[i + lastIdx], actual[i]); + } + + if (channel.parent() != null) { + channel.write(Unpooled.wrappedBuffer(actual)); + } + + counter += actual.length; } - - if (channel.parent() != null) { - channel.write(Unpooled.wrappedBuffer(actual)); - } - - counter += actual.length; + msgs.releaseAllAndRecycle(); } @Override diff --git a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketFileRegionTest.java b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketFileRegionTest.java index 0a002c30ba..7d52878508 100644 --- a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketFileRegionTest.java +++ b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketFileRegionTest.java @@ -20,9 +20,11 @@ import io.netty.bootstrap.ServerBootstrap; import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundByteHandlerAdapter; +import io.netty.channel.ChannelInboundHandler; +import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.DefaultFileRegion; import io.netty.channel.FileRegion; +import io.netty.channel.MessageList; import org.junit.Test; import java.io.File; @@ -69,10 +71,12 @@ public class SocketFileRegionTest extends AbstractSocketTest { out.write(data); out.close(); - ChannelInboundByteHandlerAdapter ch = new ChannelInboundByteHandlerAdapter() { + ChannelInboundHandler ch = new ChannelInboundHandlerAdapter() { + @Override - public void inboundBufferUpdated(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - in.clear(); + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + // discard + msgs.releaseAllAndRecycle(); } @Override @@ -88,12 +92,11 @@ public class SocketFileRegionTest extends AbstractSocketTest { Channel sc = sb.bind().sync().channel(); Channel cc = cb.connect().sync().channel(); - FileRegion region = new DefaultFileRegion(new FileInputStream(file).getChannel(), - 0L, file.length()); + FileRegion region = new DefaultFileRegion(new FileInputStream(file).getChannel(), 0L, file.length()); if (voidPromise) { - assertEquals(cc.voidPromise(), cc.sendFile(region, cc.voidPromise())); + assertEquals(cc.voidPromise(), cc.write(region, cc.voidPromise())); } else { - assertNotEquals(cc.voidPromise(), cc.sendFile(region)); + assertNotEquals(cc.voidPromise(), cc.write(region)); } while (sh.counter < data.length) { if (sh.exception.get() != null) { @@ -120,7 +123,7 @@ public class SocketFileRegionTest extends AbstractSocketTest { } } - private static class TestHandler extends ChannelInboundByteHandlerAdapter { + private static class TestHandler extends ChannelInboundHandlerAdapter { volatile Channel channel; final AtomicReference exception = new AtomicReference(); volatile int counter; @@ -132,17 +135,19 @@ public class SocketFileRegionTest extends AbstractSocketTest { } @Override - public void inboundBufferUpdated( - ChannelHandlerContext ctx, ByteBuf in) - throws Exception { - byte[] actual = new byte[in.readableBytes()]; - in.readBytes(actual); + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + for (int j = 0; j < msgs.size(); j ++) { + ByteBuf in = (ByteBuf) msgs.get(j); + byte[] actual = new byte[in.readableBytes()]; + in.readBytes(actual); - int lastIdx = counter; - for (int i = 0; i < actual.length; i ++) { - assertEquals(data[i + lastIdx], actual[i]); + int lastIdx = counter; + for (int i = 0; i < actual.length; i ++) { + assertEquals(data[i + lastIdx], actual[i]); + } + counter += actual.length; } - counter += actual.length; + msgs.releaseAllAndRecycle(); } @Override diff --git a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketFixedLengthEchoTest.java b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketFixedLengthEchoTest.java index 206406dd09..d25528fcf0 100644 --- a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketFixedLengthEchoTest.java +++ b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketFixedLengthEchoTest.java @@ -21,8 +21,9 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInitializer; +import io.netty.channel.MessageList; import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.FixedLengthFrameDecoder; import org.junit.Test; @@ -123,7 +124,7 @@ public class SocketFixedLengthEchoTest extends AbstractSocketTest { } } - private static class EchoHandler extends ChannelInboundMessageHandlerAdapter { + private static class EchoHandler extends ChannelInboundHandlerAdapter { volatile Channel channel; final AtomicReference exception = new AtomicReference(); volatile int counter; @@ -135,24 +136,26 @@ public class SocketFixedLengthEchoTest extends AbstractSocketTest { } @Override - public void messageReceived( - ChannelHandlerContext ctx, - ByteBuf msg) throws Exception { - assertEquals(1024, msg.readableBytes()); + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + for (int j = 0; j < msgs.size(); j ++) { + ByteBuf msg = (ByteBuf) msgs.get(j); + assertEquals(1024, msg.readableBytes()); - byte[] actual = new byte[msg.readableBytes()]; - msg.getBytes(0, actual); + byte[] actual = new byte[msg.readableBytes()]; + msg.getBytes(0, actual); - int lastIdx = counter; - for (int i = 0; i < actual.length; i ++) { - assertEquals(data[i + lastIdx], actual[i]); + int lastIdx = counter; + for (int i = 0; i < actual.length; i ++) { + assertEquals(data[i + lastIdx], actual[i]); + } + + if (channel.parent() != null) { + channel.write(msg.retain()); + } + + counter += actual.length; } - - if (channel.parent() != null) { - channel.write(msg.retain()); - } - - counter += actual.length; + msgs.releaseAllAndRecycle(); } @Override diff --git a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketObjectEchoTest.java b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketObjectEchoTest.java index d57abd2152..d660df2784 100644 --- a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketObjectEchoTest.java +++ b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketObjectEchoTest.java @@ -19,8 +19,9 @@ import io.netty.bootstrap.Bootstrap; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInitializer; +import io.netty.channel.MessageList; import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.serialization.ClassResolvers; import io.netty.handler.codec.serialization.ObjectDecoder; @@ -133,7 +134,7 @@ public class SocketObjectEchoTest extends AbstractSocketTest { } } - private static class EchoHandler extends ChannelInboundMessageHandlerAdapter { + private static class EchoHandler extends ChannelInboundHandlerAdapter { volatile Channel channel; final AtomicReference exception = new AtomicReference(); volatile int counter; @@ -148,15 +149,18 @@ public class SocketObjectEchoTest extends AbstractSocketTest { } @Override - public void messageReceived(ChannelHandlerContext ctx, - String msg) throws Exception { - assertEquals(data[counter], msg); + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + for (int i = 0; i < msgs.size(); i ++) { + String msg = (String) msgs.get(i); + assertEquals(data[counter], msg); - if (channel.parent() != null) { - channel.write(msg); + if (channel.parent() != null) { + channel.write(msg); + } + + counter ++; } - - counter ++; + msgs.releaseAllAndRecycle(); } @Override diff --git a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketShutdownOutputByPeerTest.java b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketShutdownOutputByPeerTest.java index e5e6a4df04..f2b30237db 100644 --- a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketShutdownOutputByPeerTest.java +++ b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketShutdownOutputByPeerTest.java @@ -18,8 +18,9 @@ package io.netty.testsuite.transport.socket; import io.netty.bootstrap.ServerBootstrap; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundByteHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelOption; +import io.netty.channel.MessageList; import io.netty.channel.socket.ChannelInputShutdownEvent; import io.netty.channel.socket.SocketChannel; import org.junit.Test; @@ -109,7 +110,7 @@ public class SocketShutdownOutputByPeerTest extends AbstractServerSocketTest { } } - private static class TestHandler extends ChannelInboundByteHandlerAdapter { + private static class TestHandler extends ChannelInboundHandlerAdapter { volatile SocketChannel ch; final BlockingQueue queue = new LinkedBlockingQueue(); final CountDownLatch halfClosure = new CountDownLatch(1); @@ -127,8 +128,11 @@ public class SocketShutdownOutputByPeerTest extends AbstractServerSocketTest { } @Override - public void inboundBufferUpdated(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - queue.offer(in.readByte()); + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + for (int i = 0; i < msgs.size(); i ++) { + queue.offer(((ByteBuf) msgs.get(i)).readByte()); + } + msgs.releaseAllAndRecycle(); } @Override diff --git a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketShutdownOutputBySelfTest.java b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketShutdownOutputBySelfTest.java index 5c377c31db..a895c6846e 100644 --- a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketShutdownOutputBySelfTest.java +++ b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketShutdownOutputBySelfTest.java @@ -19,7 +19,8 @@ import io.netty.bootstrap.Bootstrap; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundByteHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.MessageList; import io.netty.channel.socket.SocketChannel; import org.junit.Test; @@ -76,7 +77,7 @@ public class SocketShutdownOutputBySelfTest extends AbstractClientSocketTest { } } - private static class TestHandler extends ChannelInboundByteHandlerAdapter { + private static class TestHandler extends ChannelInboundHandlerAdapter { volatile SocketChannel ch; final BlockingQueue queue = new LinkedBlockingQueue(); @@ -86,8 +87,11 @@ public class SocketShutdownOutputBySelfTest extends AbstractClientSocketTest { } @Override - public void inboundBufferUpdated(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - queue.offer(in.readByte()); + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + for (int i = 0; i < msgs.size(); i ++) { + queue.offer(((ByteBuf) msgs.get(i)).readByte()); + } + msgs.releaseAllAndRecycle(); } } } diff --git a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketSpdyEchoTest.java b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketSpdyEchoTest.java index 905f315daa..19b15bfbcb 100644 --- a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketSpdyEchoTest.java +++ b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketSpdyEchoTest.java @@ -17,14 +17,13 @@ package io.netty.testsuite.transport.socket; import io.netty.bootstrap.Bootstrap; import io.netty.bootstrap.ServerBootstrap; -import io.netty.buffer.BufUtil; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundByteHandlerAdapter; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInitializer; +import io.netty.channel.MessageList; import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.spdy.SpdyConstants; import io.netty.handler.codec.spdy.SpdyFrameDecoder; @@ -230,13 +229,12 @@ public class SocketSpdyEchoTest extends AbstractSocketTest { } } - private static class SpdyEchoTestServerHandler extends ChannelInboundMessageHandlerAdapter { + private static class SpdyEchoTestServerHandler extends ChannelInboundHandlerAdapter { final AtomicReference exception = new AtomicReference(); @Override - public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { - BufUtil.retain(msg); - ctx.write(msg); + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + ctx.write(msgs); } @Override @@ -247,7 +245,7 @@ public class SocketSpdyEchoTest extends AbstractSocketTest { } } - private static class SpdyEchoTestClientHandler extends ChannelInboundByteHandlerAdapter { + private static class SpdyEchoTestClientHandler extends ChannelInboundHandlerAdapter { final AtomicReference exception = new AtomicReference(); final ByteBuf frames; volatile int counter; @@ -257,16 +255,20 @@ public class SocketSpdyEchoTest extends AbstractSocketTest { } @Override - public void inboundBufferUpdated(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - byte[] actual = new byte[in.readableBytes()]; - in.readBytes(actual); + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + for (int j = 0; j < msgs.size(); j ++) { + ByteBuf in = (ByteBuf) msgs.get(j); + byte[] actual = new byte[in.readableBytes()]; + in.readBytes(actual); - int lastIdx = counter; - for (int i = 0; i < actual.length; i ++) { - assertEquals(frames.getByte(ignoredBytes + i + lastIdx), actual[i]); + int lastIdx = counter; + for (int i = 0; i < actual.length; i ++) { + assertEquals(frames.getByte(ignoredBytes + i + lastIdx), actual[i]); + } + + counter += actual.length; } - - counter += actual.length; + msgs.releaseAllAndRecycle(); } @Override diff --git a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketSslEchoTest.java b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketSslEchoTest.java index c90f6899e6..3e75430eda 100644 --- a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketSslEchoTest.java +++ b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketSslEchoTest.java @@ -15,30 +15,29 @@ */ package io.netty.testsuite.transport.socket; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; import io.netty.bootstrap.Bootstrap; import io.netty.bootstrap.ServerBootstrap; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundByteHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInitializer; +import io.netty.channel.MessageList; import io.netty.channel.socket.SocketChannel; import io.netty.handler.ssl.SslHandler; import io.netty.handler.stream.ChunkedWriteHandler; import io.netty.testsuite.util.BogusSslContextFactory; import io.netty.util.concurrent.Future; +import org.junit.Test; +import javax.net.ssl.SSLEngine; import java.io.IOException; import java.util.Random; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; -import javax.net.ssl.SSLEngine; - -import org.junit.Test; +import static org.junit.Assert.*; public class SocketSslEchoTest extends AbstractSocketTest { @@ -163,7 +162,7 @@ public class SocketSslEchoTest extends AbstractSocketTest { } } - private class EchoHandler extends ChannelInboundByteHandlerAdapter { + private class EchoHandler extends ChannelInboundHandlerAdapter { volatile Channel channel; final AtomicReference exception = new AtomicReference(); volatile int counter; @@ -180,22 +179,24 @@ public class SocketSslEchoTest extends AbstractSocketTest { } @Override - public void inboundBufferUpdated( - ChannelHandlerContext ctx, ByteBuf in) - throws Exception { - byte[] actual = new byte[in.readableBytes()]; - in.readBytes(actual); + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + for (int j = 0; j < msgs.size(); j ++) { + ByteBuf in = (ByteBuf) msgs.get(j); + byte[] actual = new byte[in.readableBytes()]; + in.readBytes(actual); - int lastIdx = counter; - for (int i = 0; i < actual.length; i ++) { - assertEquals(data[i + lastIdx], actual[i]); + int lastIdx = counter; + for (int i = 0; i < actual.length; i ++) { + assertEquals(data[i + lastIdx], actual[i]); + } + + if (channel.parent() != null) { + channel.write(Unpooled.wrappedBuffer(actual)); + } + + counter += actual.length; } - - if (channel.parent() != null) { - channel.write(Unpooled.wrappedBuffer(actual)); - } - - counter += actual.length; + msgs.releaseAllAndRecycle(); } @Override diff --git a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketStartTlsTest.java b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketStartTlsTest.java index 54b70d15a9..d50fbaad20 100644 --- a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketStartTlsTest.java +++ b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketStartTlsTest.java @@ -19,9 +19,10 @@ import io.netty.bootstrap.Bootstrap; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; +import io.netty.channel.MessageList; import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.LineBasedFrameDecoder; import io.netty.handler.codec.string.StringDecoder; @@ -142,7 +143,7 @@ public class SocketStartTlsTest extends AbstractSocketTest { } } - private class StartTlsClientHandler extends ChannelInboundMessageHandlerAdapter { + private class StartTlsClientHandler extends ChannelInboundHandlerAdapter { private final SslHandler sslHandler; private Future handshakeFuture; final AtomicReference exception = new AtomicReference(); @@ -159,18 +160,22 @@ public class SocketStartTlsTest extends AbstractSocketTest { } @Override - public void messageReceived(final ChannelHandlerContext ctx, String msg) throws Exception { - if ("StartTlsResponse".equals(msg)) { - ctx.pipeline().addAfter("logger", "ssl", sslHandler); - handshakeFuture = sslHandler.handshakeFuture(); - ctx.write("EncryptedRequest\n"); - return; - } + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + for (int i = 0; i < msgs.size(); i ++) { + String msg = (String) msgs.get(i); + if ("StartTlsResponse".equals(msg)) { + ctx.pipeline().addAfter("logger", "ssl", sslHandler); + handshakeFuture = sslHandler.handshakeFuture(); + ctx.write("EncryptedRequest\n"); + continue; + } - assertEquals("EncryptedResponse", msg); - assertNotNull(handshakeFuture); - assertTrue(handshakeFuture.isSuccess()); - ctx.close(); + assertEquals("EncryptedResponse", msg); + assertNotNull(handshakeFuture); + assertTrue(handshakeFuture.isSuccess()); + ctx.close(); + } + msgs.releaseAllAndRecycle(); } @Override @@ -185,7 +190,7 @@ public class SocketStartTlsTest extends AbstractSocketTest { } } - private class StartTlsServerHandler extends ChannelInboundMessageHandlerAdapter { + private class StartTlsServerHandler extends ChannelInboundHandlerAdapter { private final SslHandler sslHandler; volatile Channel channel; final AtomicReference exception = new AtomicReference(); @@ -201,15 +206,19 @@ public class SocketStartTlsTest extends AbstractSocketTest { } @Override - public void messageReceived(final ChannelHandlerContext ctx, String msg) throws Exception { - if ("StartTlsRequest".equals(msg)) { - ctx.pipeline().addAfter("logger", "ssl", sslHandler); - ctx.write("StartTlsResponse\n"); - return; - } + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + for (int i = 0; i < msgs.size(); i ++) { + String msg = (String) msgs.get(i); + if ("StartTlsRequest".equals(msg)) { + ctx.pipeline().addAfter("logger", "ssl", sslHandler); + ctx.write("StartTlsResponse\n"); + continue; + } - assertEquals("EncryptedRequest", msg); - ctx.write("EncryptedResponse\n"); + assertEquals("EncryptedRequest", msg); + ctx.write("EncryptedResponse\n"); + } + msgs.releaseAllAndRecycle(); } @Override diff --git a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketStringEchoTest.java b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketStringEchoTest.java index 391f567037..9cb3b2a8f9 100644 --- a/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketStringEchoTest.java +++ b/testsuite/src/test/java/io/netty/testsuite/transport/socket/SocketStringEchoTest.java @@ -17,11 +17,11 @@ package io.netty.testsuite.transport.socket; import io.netty.bootstrap.Bootstrap; import io.netty.bootstrap.ServerBootstrap; -import io.netty.buffer.BufType; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInitializer; +import io.netty.channel.MessageList; import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.DelimiterBasedFrameDecoder; import io.netty.handler.codec.Delimiters; @@ -136,7 +136,7 @@ public class SocketStringEchoTest extends AbstractSocketTest { } } - static class StringEchoHandler extends ChannelInboundMessageHandlerAdapter { + static class StringEchoHandler extends ChannelInboundHandlerAdapter { volatile Channel channel; final AtomicReference exception = new AtomicReference(); volatile int counter; @@ -148,16 +148,19 @@ public class SocketStringEchoTest extends AbstractSocketTest { } @Override - public void messageReceived(ChannelHandlerContext ctx, - String msg) throws Exception { - assertEquals(data[counter], msg); + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + for (int i = 0; i < msgs.size(); i ++) { + String msg = (String) msgs.get(i); + assertEquals(data[counter], msg); - if (channel.parent() != null) { - String delimiter = random.nextBoolean() ? "\r\n" : "\n"; - channel.write(msg + delimiter); + if (channel.parent() != null) { + String delimiter = random.nextBoolean() ? "\r\n" : "\n"; + channel.write(msg + delimiter); + } + + counter ++; } - - counter ++; + msgs.releaseAllAndRecycle(); } @Override diff --git a/testsuite/src/test/java/io/netty/testsuite/transport/socket/WriteBeforeRegisteredTest.java b/testsuite/src/test/java/io/netty/testsuite/transport/socket/WriteBeforeRegisteredTest.java index b9a2429f22..ac553d5242 100644 --- a/testsuite/src/test/java/io/netty/testsuite/transport/socket/WriteBeforeRegisteredTest.java +++ b/testsuite/src/test/java/io/netty/testsuite/transport/socket/WriteBeforeRegisteredTest.java @@ -18,7 +18,7 @@ package io.netty.testsuite.transport.socket; import io.netty.bootstrap.Bootstrap; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelStateHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.socket.SocketChannel; import org.junit.Test; @@ -42,12 +42,7 @@ public class WriteBeforeRegisteredTest extends AbstractClientSocketTest { } } - private static class TestHandler extends ChannelStateHandlerAdapter { - - @Override - public void inboundBufferUpdated(ChannelHandlerContext ctx) throws Exception { - ctx.fireInboundBufferUpdated(); - } + private static class TestHandler extends ChannelInboundHandlerAdapter { @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { diff --git a/testsuite/src/test/java/io/netty/testsuite/transport/udt/UDTClientServerConnectionTest.java b/testsuite/src/test/java/io/netty/testsuite/transport/udt/UDTClientServerConnectionTest.java index 314f0f2b78..dcfcc59424 100644 --- a/testsuite/src/test/java/io/netty/testsuite/transport/udt/UDTClientServerConnectionTest.java +++ b/testsuite/src/test/java/io/netty/testsuite/transport/udt/UDTClientServerConnectionTest.java @@ -15,14 +15,14 @@ */ package io.netty.testsuite.transport.udt; -import static org.junit.Assert.*; import io.netty.bootstrap.Bootstrap; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; +import io.netty.channel.MessageList; import io.netty.channel.group.ChannelGroup; import io.netty.channel.group.DefaultChannelGroup; import io.netty.channel.nio.NioEventLoopGroup; @@ -33,13 +33,14 @@ import io.netty.handler.codec.Delimiters; import io.netty.handler.codec.string.StringDecoder; import io.netty.handler.codec.string.StringEncoder; import io.netty.util.CharsetUtil; - -import java.util.concurrent.atomic.AtomicInteger; - import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.concurrent.atomic.AtomicInteger; + +import static org.junit.Assert.*; + /** * Verify UDT connect/disconnect life cycle. */ @@ -136,7 +137,7 @@ public class UDTClientServerConnectionTest { } static class ClientHandler extends - ChannelInboundMessageHandlerAdapter { + ChannelInboundHandlerAdapter { static final Logger log = LoggerFactory.getLogger(ClientHandler.class); @@ -169,9 +170,11 @@ public class UDTClientServerConnectionTest { } @Override - public void messageReceived(final ChannelHandlerContext ctx, - final String msg) throws Exception { - log.info("Client received: " + msg); + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + for (int i = 0; i < msgs.size(); i ++) { + log.info("Client received: " + msgs.get(i)); + } + msgs.releaseAllAndRecycle(); } } @@ -282,7 +285,7 @@ public class UDTClientServerConnectionTest { } static class ServerHandler extends - ChannelInboundMessageHandlerAdapter { + ChannelInboundHandlerAdapter { static final Logger log = LoggerFactory.getLogger(ServerHandler.class); @@ -320,9 +323,11 @@ public class UDTClientServerConnectionTest { } @Override - public void messageReceived(final ChannelHandlerContext ctx, - final String message) throws Exception { - log.info("Server received: " + message); + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + for (int i = 0; i < msgs.size(); i ++) { + log.info("Server received: " + msgs.get(i)); + } + msgs.releaseAllAndRecycle(); } } diff --git a/transport-rxtx/pom.xml b/transport-rxtx/pom.xml index 249b43579d..8976db43ab 100644 --- a/transport-rxtx/pom.xml +++ b/transport-rxtx/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.0.0.Final-SNAPSHOT + 4.0.0.CR4-SNAPSHOT netty-transport-rxtx @@ -35,11 +35,6 @@ netty-buffer ${project.version} - - ${project.groupId} - netty-codec - ${project.version} - ${project.groupId} netty-transport diff --git a/transport-rxtx/src/main/java/io/netty/channel/rxtx/DefaultRxtxChannelConfig.java b/transport-rxtx/src/main/java/io/netty/channel/rxtx/DefaultRxtxChannelConfig.java index 6a9d7df032..63d4f3e852 100644 --- a/transport-rxtx/src/main/java/io/netty/channel/rxtx/DefaultRxtxChannelConfig.java +++ b/transport-rxtx/src/main/java/io/netty/channel/rxtx/DefaultRxtxChannelConfig.java @@ -18,6 +18,7 @@ package io.netty.channel.rxtx; import io.netty.buffer.ByteBufAllocator; import io.netty.channel.ChannelOption; import io.netty.channel.DefaultChannelConfig; +import io.netty.channel.RecvByteBufAllocator; import java.util.Map; @@ -191,13 +192,24 @@ final class DefaultRxtxChannelConfig extends DefaultChannelConfig implements Rxt return (RxtxChannelConfig) super.setAllocator(allocator); } + @Override + public RxtxChannelConfig setRecvByteBufAllocator(RecvByteBufAllocator allocator) { + super.setRecvByteBufAllocator(allocator); + return this; + } + @Override public RxtxChannelConfig setAutoRead(boolean autoRead) { return (RxtxChannelConfig) super.setAutoRead(autoRead); } @Override - public RxtxChannelConfig setDefaultHandlerByteBufType(ChannelHandlerByteBufType type) { - return (RxtxChannelConfig) super.setDefaultHandlerByteBufType(type); + public RxtxChannelConfig setWriteBufferHighWaterMark(int writeBufferHighWaterMark) { + return (RxtxChannelConfig) super.setWriteBufferHighWaterMark(writeBufferHighWaterMark); + } + + @Override + public RxtxChannelConfig setWriteBufferLowWaterMark(int writeBufferLowWaterMark) { + return (RxtxChannelConfig) super.setWriteBufferLowWaterMark(writeBufferLowWaterMark); } } diff --git a/transport-rxtx/src/main/java/io/netty/channel/rxtx/RxtxChannelConfig.java b/transport-rxtx/src/main/java/io/netty/channel/rxtx/RxtxChannelConfig.java index 382601bbf6..6085b851c3 100644 --- a/transport-rxtx/src/main/java/io/netty/channel/rxtx/RxtxChannelConfig.java +++ b/transport-rxtx/src/main/java/io/netty/channel/rxtx/RxtxChannelConfig.java @@ -18,6 +18,7 @@ package io.netty.channel.rxtx; import gnu.io.SerialPort; import io.netty.buffer.ByteBufAllocator; import io.netty.channel.ChannelConfig; +import io.netty.channel.RecvByteBufAllocator; /** * A configuration class for RXTX device connections. @@ -268,8 +269,8 @@ public interface RxtxChannelConfig extends ChannelConfig { RxtxChannelConfig setAllocator(ByteBufAllocator allocator); @Override - RxtxChannelConfig setAutoRead(boolean autoRead); + RxtxChannelConfig setRecvByteBufAllocator(RecvByteBufAllocator allocator); @Override - RxtxChannelConfig setDefaultHandlerByteBufType(ChannelHandlerByteBufType type); + RxtxChannelConfig setAutoRead(boolean autoRead); } diff --git a/transport-sctp/pom.xml b/transport-sctp/pom.xml index b6216bc99b..a6175f8612 100644 --- a/transport-sctp/pom.xml +++ b/transport-sctp/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.0.0.Final-SNAPSHOT + 4.0.0.CR4-SNAPSHOT netty-transport-sctp diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/DefaultSctpChannelConfig.java b/transport-sctp/src/main/java/io/netty/channel/sctp/DefaultSctpChannelConfig.java index 687d9a3828..baeb008f5f 100644 --- a/transport-sctp/src/main/java/io/netty/channel/sctp/DefaultSctpChannelConfig.java +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/DefaultSctpChannelConfig.java @@ -19,10 +19,10 @@ package io.netty.channel.sctp; import com.sun.nio.sctp.SctpChannel; import com.sun.nio.sctp.SctpStandardSocketOptions; import io.netty.buffer.ByteBufAllocator; -import io.netty.channel.ChannelConfig; import io.netty.channel.ChannelException; import io.netty.channel.ChannelOption; import io.netty.channel.DefaultChannelConfig; +import io.netty.channel.RecvByteBufAllocator; import io.netty.util.internal.PlatformDependent; import java.io.IOException; @@ -186,13 +186,24 @@ public class DefaultSctpChannelConfig extends DefaultChannelConfig implements Sc return (SctpChannelConfig) super.setAllocator(allocator); } + @Override + public SctpChannelConfig setRecvByteBufAllocator(RecvByteBufAllocator allocator) { + super.setRecvByteBufAllocator(allocator); + return this; + } + @Override public SctpChannelConfig setAutoRead(boolean autoRead) { return (SctpChannelConfig) super.setAutoRead(autoRead); } @Override - public SctpChannelConfig setDefaultHandlerByteBufType(ChannelHandlerByteBufType type) { - return (SctpChannelConfig) super.setDefaultHandlerByteBufType(type); + public SctpChannelConfig setWriteBufferHighWaterMark(int writeBufferHighWaterMark) { + return (SctpChannelConfig) super.setWriteBufferHighWaterMark(writeBufferHighWaterMark); + } + + @Override + public SctpChannelConfig setWriteBufferLowWaterMark(int writeBufferLowWaterMark) { + return (SctpChannelConfig) super.setWriteBufferLowWaterMark(writeBufferLowWaterMark); } } diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/DefaultSctpServerChannelConfig.java b/transport-sctp/src/main/java/io/netty/channel/sctp/DefaultSctpServerChannelConfig.java index f1e820bb02..055e6e8421 100644 --- a/transport-sctp/src/main/java/io/netty/channel/sctp/DefaultSctpServerChannelConfig.java +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/DefaultSctpServerChannelConfig.java @@ -20,6 +20,7 @@ import io.netty.buffer.ByteBufAllocator; import io.netty.channel.ChannelException; import io.netty.channel.ChannelOption; import io.netty.channel.DefaultChannelConfig; +import io.netty.channel.RecvByteBufAllocator; import io.netty.util.NetUtil; import java.io.IOException; @@ -169,13 +170,24 @@ public class DefaultSctpServerChannelConfig extends DefaultChannelConfig impleme return (SctpServerChannelConfig) super.setAllocator(allocator); } + @Override + public SctpServerChannelConfig setRecvByteBufAllocator(RecvByteBufAllocator allocator) { + super.setRecvByteBufAllocator(allocator); + return this; + } + @Override public SctpServerChannelConfig setAutoRead(boolean autoRead) { return (SctpServerChannelConfig) super.setAutoRead(autoRead); } @Override - public SctpServerChannelConfig setDefaultHandlerByteBufType(ChannelHandlerByteBufType type) { - return (SctpServerChannelConfig) super.setDefaultHandlerByteBufType(type); + public SctpServerChannelConfig setWriteBufferLowWaterMark(int writeBufferLowWaterMark) { + return (SctpServerChannelConfig) super.setWriteBufferLowWaterMark(writeBufferLowWaterMark); + } + + @Override + public SctpServerChannelConfig setWriteBufferHighWaterMark(int writeBufferHighWaterMark) { + return (SctpServerChannelConfig) super.setWriteBufferHighWaterMark(writeBufferHighWaterMark); } } diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/SctpChannelConfig.java b/transport-sctp/src/main/java/io/netty/channel/sctp/SctpChannelConfig.java index 341a756f78..7cb3da86f8 100644 --- a/transport-sctp/src/main/java/io/netty/channel/sctp/SctpChannelConfig.java +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/SctpChannelConfig.java @@ -15,10 +15,10 @@ */ package io.netty.channel.sctp; +import com.sun.nio.sctp.SctpStandardSocketOptions.InitMaxStreams; import io.netty.buffer.ByteBufAllocator; import io.netty.channel.ChannelConfig; - -import static com.sun.nio.sctp.SctpStandardSocketOptions.*; +import io.netty.channel.RecvByteBufAllocator; /** * A {@link ChannelConfig} for a {@link SctpChannel}. @@ -106,8 +106,8 @@ public interface SctpChannelConfig extends ChannelConfig { SctpChannelConfig setAllocator(ByteBufAllocator allocator); @Override - SctpChannelConfig setAutoRead(boolean autoRead); + SctpChannelConfig setRecvByteBufAllocator(RecvByteBufAllocator allocator); @Override - SctpChannelConfig setDefaultHandlerByteBufType(ChannelHandlerByteBufType type); + SctpChannelConfig setAutoRead(boolean autoRead); } diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/SctpMessage.java b/transport-sctp/src/main/java/io/netty/channel/sctp/SctpMessage.java index b3cebf8a90..84b2c03504 100644 --- a/transport-sctp/src/main/java/io/netty/channel/sctp/SctpMessage.java +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/SctpMessage.java @@ -16,8 +16,8 @@ package io.netty.channel.sctp; import com.sun.nio.sctp.MessageInfo; -import io.netty.buffer.BufUtil; import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufUtil; import io.netty.buffer.DefaultByteBufHolder; /** @@ -156,6 +156,6 @@ public final class SctpMessage extends DefaultByteBufHolder { } return "SctpFrame{" + "streamIdentifier=" + streamIdentifier + ", protocolIdentifier=" + protocolIdentifier + - ", data=" + BufUtil.hexDump(content()) + '}'; + ", data=" + ByteBufUtil.hexDump(content()) + '}'; } } diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/SctpServerChannelConfig.java b/transport-sctp/src/main/java/io/netty/channel/sctp/SctpServerChannelConfig.java index 26844dad7f..7527b46894 100644 --- a/transport-sctp/src/main/java/io/netty/channel/sctp/SctpServerChannelConfig.java +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/SctpServerChannelConfig.java @@ -15,10 +15,10 @@ */ package io.netty.channel.sctp; +import com.sun.nio.sctp.SctpStandardSocketOptions.InitMaxStreams; import io.netty.buffer.ByteBufAllocator; import io.netty.channel.ChannelConfig; - -import static com.sun.nio.sctp.SctpStandardSocketOptions.*; +import io.netty.channel.RecvByteBufAllocator; /** * A {@link ChannelConfig} for a {@link SctpServerChannelConfig}. @@ -101,8 +101,8 @@ public interface SctpServerChannelConfig extends ChannelConfig { SctpServerChannelConfig setAllocator(ByteBufAllocator allocator); @Override - SctpServerChannelConfig setAutoRead(boolean autoRead); + SctpServerChannelConfig setRecvByteBufAllocator(RecvByteBufAllocator allocator); @Override - SctpServerChannelConfig setDefaultHandlerByteBufType(ChannelHandlerByteBufType type); + SctpServerChannelConfig setAutoRead(boolean autoRead); } diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/nio/NioSctpChannel.java b/transport-sctp/src/main/java/io/netty/channel/sctp/nio/NioSctpChannel.java index 92f9555fd1..a2a42d31ff 100644 --- a/transport-sctp/src/main/java/io/netty/channel/sctp/nio/NioSctpChannel.java +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/nio/NioSctpChannel.java @@ -19,14 +19,13 @@ import com.sun.nio.sctp.Association; import com.sun.nio.sctp.MessageInfo; import com.sun.nio.sctp.NotificationHandler; import com.sun.nio.sctp.SctpChannel; -import io.netty.buffer.BufType; import io.netty.buffer.ByteBuf; -import io.netty.buffer.MessageBuf; import io.netty.channel.Channel; import io.netty.channel.ChannelException; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelMetadata; import io.netty.channel.ChannelPromise; +import io.netty.channel.MessageList; import io.netty.channel.nio.AbstractNioMessageChannel; import io.netty.channel.sctp.DefaultSctpChannelConfig; import io.netty.channel.sctp.SctpChannelConfig; @@ -57,14 +56,13 @@ import java.util.Set; * to understand what you need to do to use it. Also this feature is only supported on Java 7+. */ public class NioSctpChannel extends AbstractNioMessageChannel implements io.netty.channel.sctp.SctpChannel { - private static final ChannelMetadata METADATA = new ChannelMetadata(BufType.MESSAGE, false); + private static final ChannelMetadata METADATA = new ChannelMetadata(false); private static final InternalLogger logger = InternalLoggerFactory.getInstance(NioSctpChannel.class); private final SctpChannelConfig config; - @SuppressWarnings("rawtypes") - private final NotificationHandler notificationHandler; + private final NotificationHandler notificationHandler; private static SctpChannel newSctpChannel() { try { @@ -259,9 +257,8 @@ public class NioSctpChannel extends AbstractNioMessageChannel implements io.nett javaChannel().close(); } - @SuppressWarnings("unchecked") @Override - protected int doReadMessages(MessageBuf buf) throws Exception { + protected int doReadMessages(MessageList buf) throws Exception { SctpChannel ch = javaChannel(); ByteBuf buffer = alloc().directBuffer(config().getReceiveBufferSize()); boolean free = true; @@ -287,8 +284,8 @@ public class NioSctpChannel extends AbstractNioMessageChannel implements io.nett } @Override - protected int doWriteMessages(MessageBuf buf, boolean lastSpin) throws Exception { - SctpMessage packet = (SctpMessage) buf.peek(); + protected int doWriteMessages(MessageList msgs, int index, boolean lastSpin) throws Exception { + SctpMessage packet = (SctpMessage) msgs.get(index); ByteBuf data = packet.content(); int dataLen = data.readableBytes(); ByteBuffer nioData; @@ -322,13 +319,10 @@ public class NioSctpChannel extends AbstractNioMessageChannel implements io.nett return 0; } - // Wrote a packet. - buf.remove(); - // packet was written free up buffer packet.release(); - if (buf.isEmpty()) { + if (index + 1 == msgs.size()) { // Wrote the outbound buffer completely - clear OP_WRITE. if ((interestOps & SelectionKey.OP_WRITE) != 0) { key.interestOps(interestOps & ~SelectionKey.OP_WRITE); diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/nio/NioSctpServerChannel.java b/transport-sctp/src/main/java/io/netty/channel/sctp/nio/NioSctpServerChannel.java index a645b77a0c..c2e52bdc90 100644 --- a/transport-sctp/src/main/java/io/netty/channel/sctp/nio/NioSctpServerChannel.java +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/nio/NioSctpServerChannel.java @@ -17,12 +17,11 @@ package io.netty.channel.sctp.nio; import com.sun.nio.sctp.SctpChannel; import com.sun.nio.sctp.SctpServerChannel; -import io.netty.buffer.BufType; -import io.netty.buffer.MessageBuf; import io.netty.channel.ChannelException; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelMetadata; import io.netty.channel.ChannelPromise; +import io.netty.channel.MessageList; import io.netty.channel.nio.AbstractNioMessageChannel; import io.netty.channel.sctp.DefaultSctpServerChannelConfig; import io.netty.channel.sctp.SctpServerChannelConfig; @@ -46,7 +45,7 @@ import java.util.Set; */ public class NioSctpServerChannel extends AbstractNioMessageChannel implements io.netty.channel.sctp.SctpServerChannel { - private static final ChannelMetadata METADATA = new ChannelMetadata(BufType.MESSAGE, false); + private static final ChannelMetadata METADATA = new ChannelMetadata(false); private static SctpServerChannel newSocket() { try { @@ -135,7 +134,7 @@ public class NioSctpServerChannel extends AbstractNioMessageChannel } @Override - protected int doReadMessages(MessageBuf buf) throws Exception { + protected int doReadMessages(MessageList buf) throws Exception { SctpChannel ch = javaChannel().accept(); if (ch == null) { return 0; @@ -217,7 +216,7 @@ public class NioSctpServerChannel extends AbstractNioMessageChannel } @Override - protected int doWriteMessages(MessageBuf buf, boolean lastSpin) throws Exception { + protected int doWriteMessages(MessageList msgs, int index, boolean lastSpin) throws Exception { throw new UnsupportedOperationException(); } } diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/oio/OioSctpChannel.java b/transport-sctp/src/main/java/io/netty/channel/sctp/oio/OioSctpChannel.java index 72792f7a02..b818673285 100755 --- a/transport-sctp/src/main/java/io/netty/channel/sctp/oio/OioSctpChannel.java +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/oio/OioSctpChannel.java @@ -19,14 +19,13 @@ import com.sun.nio.sctp.Association; import com.sun.nio.sctp.MessageInfo; import com.sun.nio.sctp.NotificationHandler; import com.sun.nio.sctp.SctpChannel; -import io.netty.buffer.BufType; import io.netty.buffer.ByteBuf; -import io.netty.buffer.MessageBuf; import io.netty.channel.Channel; import io.netty.channel.ChannelException; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelMetadata; import io.netty.channel.ChannelPromise; +import io.netty.channel.MessageList; import io.netty.channel.oio.AbstractOioMessageChannel; import io.netty.channel.sctp.DefaultSctpChannelConfig; import io.netty.channel.sctp.SctpChannelConfig; @@ -62,7 +61,7 @@ public class OioSctpChannel extends AbstractOioMessageChannel private static final InternalLogger logger = InternalLoggerFactory.getInstance(OioSctpChannel.class); - private static final ChannelMetadata METADATA = new ChannelMetadata(BufType.MESSAGE, false); + private static final ChannelMetadata METADATA = new ChannelMetadata(false); private final SctpChannel ch; private final SctpChannelConfig config; @@ -166,7 +165,7 @@ public class OioSctpChannel extends AbstractOioMessageChannel } @Override - protected int doReadMessages(MessageBuf buf) throws Exception { + protected int doReadMessages(MessageList buf) throws Exception { if (!readSelector.isOpen()) { return 0; } @@ -213,17 +212,31 @@ public class OioSctpChannel extends AbstractOioMessageChannel } @Override - protected void doWriteMessages(MessageBuf buf) throws Exception { + protected int doWrite(MessageList msgs, int index) throws Exception { if (!writeSelector.isOpen()) { - return; + return 0; } final int selectedKeys = writeSelector.select(SO_TIMEOUT); if (selectedKeys > 0) { final Set writableKeys = writeSelector.selectedKeys(); - for (SelectionKey ignored : writableKeys) { - SctpMessage packet = (SctpMessage) buf.poll(); + if (writableKeys.isEmpty()) { + return 0; + } + Iterator writableKeysIt = writableKeys.iterator(); + int written = 0; + int length = msgs.size() - index; + + for (;;) { + if (written == length) { + // all written + return written; + } + writableKeysIt.next(); + writableKeysIt.remove(); + + SctpMessage packet = (SctpMessage) msgs.get(index ++); if (packet == null) { - return; + return written; } try { ByteBuf data = packet.content(); @@ -243,12 +256,17 @@ public class OioSctpChannel extends AbstractOioMessageChannel mi.streamNumber(packet.streamIdentifier()); ch.send(nioData, mi); + written ++; } finally { packet.release(); } + if (!writableKeysIt.hasNext()) { + return written; + } } - writableKeys.clear(); } + + return 0; } @Override diff --git a/transport-sctp/src/main/java/io/netty/channel/sctp/oio/OioSctpServerChannel.java b/transport-sctp/src/main/java/io/netty/channel/sctp/oio/OioSctpServerChannel.java index 4f12c3ac22..85b0d5b514 100755 --- a/transport-sctp/src/main/java/io/netty/channel/sctp/oio/OioSctpServerChannel.java +++ b/transport-sctp/src/main/java/io/netty/channel/sctp/oio/OioSctpServerChannel.java @@ -17,12 +17,12 @@ package io.netty.channel.sctp.oio; import com.sun.nio.sctp.SctpChannel; import com.sun.nio.sctp.SctpServerChannel; -import io.netty.buffer.BufType; -import io.netty.buffer.MessageBuf; +import io.netty.buffer.ByteBufUtil; import io.netty.channel.ChannelException; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelMetadata; import io.netty.channel.ChannelPromise; +import io.netty.channel.MessageList; import io.netty.channel.oio.AbstractOioMessageChannel; import io.netty.channel.sctp.DefaultSctpServerChannelConfig; import io.netty.channel.sctp.SctpServerChannelConfig; @@ -53,7 +53,7 @@ public class OioSctpServerChannel extends AbstractOioMessageChannel private static final InternalLogger logger = InternalLoggerFactory.getInstance(OioSctpServerChannel.class); - private static final ChannelMetadata METADATA = new ChannelMetadata(BufType.MESSAGE, false); + private static final ChannelMetadata METADATA = new ChannelMetadata(false); private static SctpServerChannel newServerSocket() { try { @@ -189,27 +189,32 @@ public class OioSctpServerChannel extends AbstractOioMessageChannel } @Override - protected int doReadMessages(MessageBuf buf) throws Exception { + protected int doReadMessages(MessageList buf) throws Exception { if (!isActive()) { return -1; } SctpChannel s = null; + int acceptedChannels = 0; try { final int selectedKeys = selector.select(SO_TIMEOUT); if (selectedKeys > 0) { - final Set selectionKeys = selector.selectedKeys(); - for (SelectionKey key : selectionKeys) { - if (key.isAcceptable()) { - s = sch.accept(); - if (s != null) { - buf.add(new OioSctpChannel(this, null, s)); - } - } + final Iterator selectionKeys = selector.selectedKeys().iterator(); + for (;;) { + SelectionKey key = selectionKeys.next(); + selectionKeys.remove(); + if (key.isAcceptable()) { + s = sch.accept(); + if (s != null) { + buf.add(new OioSctpChannel(this, null, s)); + acceptedChannels ++; + } + } + if (!selectionKeys.hasNext()) { + return acceptedChannels; + } } - return selectedKeys; } - } catch (Throwable t) { logger.warn("Failed to create a new channel from an accepted sctp channel.", t); if (s != null) { @@ -221,7 +226,7 @@ public class OioSctpServerChannel extends AbstractOioMessageChannel } } - return 0; + return acceptedChannels; } @Override @@ -291,7 +296,11 @@ public class OioSctpServerChannel extends AbstractOioMessageChannel } @Override - protected void doWriteMessages(MessageBuf buf) throws Exception { + protected int doWrite(MessageList msgs, int index) throws Exception { + int size = msgs.size(); + for (int i = index; i < size; i ++) { + ByteBufUtil.release(msgs.get(i)); + } throw new UnsupportedOperationException(); } } diff --git a/transport-sctp/src/main/java/io/netty/handler/codec/sctp/SctpInboundByteStreamHandler.java b/transport-sctp/src/main/java/io/netty/handler/codec/sctp/SctpInboundByteStreamHandler.java index 209aad0d78..fb1e2bb596 100644 --- a/transport-sctp/src/main/java/io/netty/handler/codec/sctp/SctpInboundByteStreamHandler.java +++ b/transport-sctp/src/main/java/io/netty/handler/codec/sctp/SctpInboundByteStreamHandler.java @@ -18,15 +18,16 @@ package io.netty.handler.codec.sctp; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.MessageList; import io.netty.channel.sctp.SctpMessage; import io.netty.handler.codec.CodecException; +import io.netty.handler.codec.MessageToMessageDecoder; /** * A ChannelHandler which receives {@link SctpMessage}s which belong to a application protocol form a specific * SCTP Stream and decode it as {@link ByteBuf}. */ -public class SctpInboundByteStreamHandler extends ChannelInboundMessageHandlerAdapter { +public class SctpInboundByteStreamHandler extends MessageToMessageDecoder { private final int protocolIdentifier; private final int streamIdentifier; @@ -52,13 +53,11 @@ public class SctpInboundByteStreamHandler extends ChannelInboundMessageHandlerAd } @Override - public void messageReceived(ChannelHandlerContext ctx, SctpMessage msg) throws Exception { + protected void decode(ChannelHandlerContext ctx, SctpMessage msg, MessageList out) throws Exception { if (!msg.isComplete()) { throw new CodecException(String.format("Received SctpMessage is not complete, please add %s in the " + "pipeline before this handler", SctpMessageCompletionHandler.class.getSimpleName())); } - - ctx.nextInboundByteBuffer().writeBytes(msg.content()); - ctx.fireInboundBufferUpdated(); + out.add(msg.content().retain()); } } diff --git a/transport-sctp/src/main/java/io/netty/handler/codec/sctp/SctpMessageCompletionHandler.java b/transport-sctp/src/main/java/io/netty/handler/codec/sctp/SctpMessageCompletionHandler.java index e84e31f260..355d36e076 100644 --- a/transport-sctp/src/main/java/io/netty/handler/codec/sctp/SctpMessageCompletionHandler.java +++ b/transport-sctp/src/main/java/io/netty/handler/codec/sctp/SctpMessageCompletionHandler.java @@ -19,47 +19,30 @@ package io.netty.handler.codec.sctp; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandler; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.ChannelInboundHandler; +import io.netty.channel.MessageList; import io.netty.channel.sctp.SctpMessage; +import io.netty.handler.codec.MessageToMessageDecoder; import java.util.HashMap; import java.util.Map; /** - * {@link ChannelInboundMessageHandlerAdapter} which will take care of handle fragmented {@link SctpMessage}s, so + * {@link MessageToMessageDecoder} which will take care of handle fragmented {@link SctpMessage}s, so * only complete {@link SctpMessage}s will be forwarded to the next - * {@link ChannelInboundMessageHandler}. + * {@link ChannelInboundHandler}. */ -public class SctpMessageCompletionHandler extends ChannelInboundMessageHandlerAdapter { +public class SctpMessageCompletionHandler extends MessageToMessageDecoder { private final Map fragments = new HashMap(); - private boolean assembled; @Override - public boolean beginMessageReceived(ChannelHandlerContext ctx) throws Exception { - assembled = false; - return super.beginMessageReceived(ctx); - } - - @Override - public void endMessageReceived(ChannelHandlerContext ctx) throws Exception { - if (assembled) { - assembled = false; - ctx.fireInboundBufferUpdated(); - } - super.endMessageReceived(ctx); - } - - @Override - public void messageReceived(ChannelHandlerContext ctx, SctpMessage msg) throws Exception { - + protected void decode(ChannelHandlerContext ctx, SctpMessage msg, MessageList out) throws Exception { final ByteBuf byteBuf = msg.content(); final int protocolIdentifier = msg.protocolIdentifier(); final int streamIdentifier = msg.streamIdentifier(); final boolean isComplete = msg.isComplete(); ByteBuf frag; - if (fragments.containsKey(streamIdentifier)) { frag = fragments.remove(streamIdentifier); } else { @@ -68,7 +51,7 @@ public class SctpMessageCompletionHandler extends ChannelInboundMessageHandlerAd if (isComplete && !frag.isReadable()) { //data chunk is not fragmented - handleAssembledMessage(ctx, msg); + out.add(msg); } else if (!isComplete && frag.isReadable()) { //more message to complete fragments.put(streamIdentifier, Unpooled.wrappedBuffer(frag, byteBuf)); @@ -79,17 +62,11 @@ public class SctpMessageCompletionHandler extends ChannelInboundMessageHandlerAd protocolIdentifier, streamIdentifier, Unpooled.wrappedBuffer(frag, byteBuf)); - handleAssembledMessage(ctx, assembledMsg); + out.add(assembledMsg); } else { //first incomplete message fragments.put(streamIdentifier, byteBuf); } - byteBuf.retain(); } - - private void handleAssembledMessage(ChannelHandlerContext ctx, SctpMessage assembledMsg) { - ctx.nextInboundMessageBuffer().add(assembledMsg); - assembled = true; - } } diff --git a/transport-sctp/src/main/java/io/netty/handler/codec/sctp/SctpOutboundByteStreamHandler.java b/transport-sctp/src/main/java/io/netty/handler/codec/sctp/SctpOutboundByteStreamHandler.java index f4ff1cfc79..09e7bbf803 100644 --- a/transport-sctp/src/main/java/io/netty/handler/codec/sctp/SctpOutboundByteStreamHandler.java +++ b/transport-sctp/src/main/java/io/netty/handler/codec/sctp/SctpOutboundByteStreamHandler.java @@ -16,20 +16,17 @@ package io.netty.handler.codec.sctp; import io.netty.buffer.ByteBuf; -import io.netty.buffer.MessageBuf; -import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelOutboundByteHandlerAdapter; -import io.netty.channel.ChannelPromise; +import io.netty.channel.MessageList; import io.netty.channel.sctp.SctpMessage; -import io.netty.handler.codec.EncoderException; +import io.netty.handler.codec.MessageToMessageEncoder; /** * A ChannelHandler which transform {@link ByteBuf} to {@link SctpMessage} and send it through a specific stream * with given protocol identifier. * */ -public class SctpOutboundByteStreamHandler extends ChannelOutboundByteHandlerAdapter { +public class SctpOutboundByteStreamHandler extends MessageToMessageEncoder { private final int streamIdentifier; private final int protocolIdentifier; @@ -43,16 +40,7 @@ public class SctpOutboundByteStreamHandler extends ChannelOutboundByteHandlerAda } @Override - protected void flush(ChannelHandlerContext ctx, ByteBuf in, ChannelPromise promise) throws Exception { - try { - MessageBuf out = ctx.nextOutboundMessageBuffer(); - ByteBuf payload = Unpooled.buffer(in.readableBytes()); - payload.writeBytes(in); - out.add(new SctpMessage(streamIdentifier, protocolIdentifier, payload)); - } catch (Throwable t) { - ctx.fireExceptionCaught(new EncoderException(t)); - } - - ctx.flush(promise); + protected void encode(ChannelHandlerContext ctx, ByteBuf msg, MessageList out) throws Exception { + out.add(new SctpMessage(streamIdentifier, protocolIdentifier, msg.retain())); } } diff --git a/transport-udt/pom.xml b/transport-udt/pom.xml index 698064801f..9fedc1701c 100644 --- a/transport-udt/pom.xml +++ b/transport-udt/pom.xml @@ -21,7 +21,7 @@ io.netty netty-parent - 4.0.0.Final-SNAPSHOT + 4.0.0.CR4-SNAPSHOT netty-transport-udt diff --git a/transport-udt/src/main/java/io/netty/channel/udt/DefaultUdtChannelConfig.java b/transport-udt/src/main/java/io/netty/channel/udt/DefaultUdtChannelConfig.java index fb5ca654cf..5495f9792b 100644 --- a/transport-udt/src/main/java/io/netty/channel/udt/DefaultUdtChannelConfig.java +++ b/transport-udt/src/main/java/io/netty/channel/udt/DefaultUdtChannelConfig.java @@ -19,9 +19,9 @@ import com.barchart.udt.OptionUDT; import com.barchart.udt.SocketUDT; import com.barchart.udt.nio.ChannelUDT; import io.netty.buffer.ByteBufAllocator; -import io.netty.channel.ChannelConfig; import io.netty.channel.ChannelOption; import io.netty.channel.DefaultChannelConfig; +import io.netty.channel.RecvByteBufAllocator; import java.io.IOException; import java.util.Map; @@ -250,6 +250,12 @@ public class DefaultUdtChannelConfig extends DefaultChannelConfig implements return this; } + @Override + public UdtChannelConfig setRecvByteBufAllocator(RecvByteBufAllocator allocator) { + super.setRecvByteBufAllocator(allocator); + return this; + } + @Override public UdtChannelConfig setAutoRead(boolean autoRead) { super.setAutoRead(autoRead); @@ -257,8 +263,12 @@ public class DefaultUdtChannelConfig extends DefaultChannelConfig implements } @Override - public UdtChannelConfig setDefaultHandlerByteBufType(ChannelHandlerByteBufType type) { - super.setDefaultHandlerByteBufType(type); - return this; + public UdtChannelConfig setWriteBufferLowWaterMark(int writeBufferLowWaterMark) { + return (UdtChannelConfig) super.setWriteBufferLowWaterMark(writeBufferLowWaterMark); + } + + @Override + public UdtChannelConfig setWriteBufferHighWaterMark(int writeBufferHighWaterMark) { + return (UdtChannelConfig) super.setWriteBufferHighWaterMark(writeBufferHighWaterMark); } } diff --git a/transport-udt/src/main/java/io/netty/channel/udt/DefaultUdtServerChannelConfig.java b/transport-udt/src/main/java/io/netty/channel/udt/DefaultUdtServerChannelConfig.java index 315eb92033..8437db011f 100644 --- a/transport-udt/src/main/java/io/netty/channel/udt/DefaultUdtServerChannelConfig.java +++ b/transport-udt/src/main/java/io/netty/channel/udt/DefaultUdtServerChannelConfig.java @@ -18,6 +18,7 @@ package io.netty.channel.udt; import com.barchart.udt.nio.ChannelUDT; import io.netty.buffer.ByteBufAllocator; import io.netty.channel.ChannelOption; +import io.netty.channel.RecvByteBufAllocator; import java.io.IOException; import java.util.Map; @@ -153,14 +154,14 @@ public class DefaultUdtServerChannelConfig extends DefaultUdtChannelConfig } @Override - public UdtServerChannelConfig setAutoRead(boolean autoRead) { - super.setAutoRead(autoRead); + public UdtServerChannelConfig setRecvByteBufAllocator(RecvByteBufAllocator allocator) { + super.setRecvByteBufAllocator(allocator); return this; } @Override - public UdtServerChannelConfig setDefaultHandlerByteBufType(ChannelHandlerByteBufType type) { - super.setDefaultHandlerByteBufType(type); + public UdtServerChannelConfig setAutoRead(boolean autoRead) { + super.setAutoRead(autoRead); return this; } } diff --git a/transport-udt/src/main/java/io/netty/channel/udt/UdtChannelConfig.java b/transport-udt/src/main/java/io/netty/channel/udt/UdtChannelConfig.java index 9bf0e50764..aa671933e6 100644 --- a/transport-udt/src/main/java/io/netty/channel/udt/UdtChannelConfig.java +++ b/transport-udt/src/main/java/io/netty/channel/udt/UdtChannelConfig.java @@ -21,6 +21,7 @@ import io.netty.buffer.ByteBufAllocator; import io.netty.channel.ChannelConfig; import io.netty.channel.ChannelException; import io.netty.channel.ChannelOption; +import io.netty.channel.RecvByteBufAllocator; /** * A {@link ChannelConfig} for a {@link UdtChannel}. @@ -119,6 +120,9 @@ public interface UdtChannelConfig extends ChannelConfig { @Override UdtChannelConfig setAllocator(ByteBufAllocator allocator); + @Override + UdtChannelConfig setRecvByteBufAllocator(RecvByteBufAllocator allocator); + @Override UdtChannelConfig setAutoRead(boolean autoRead); @@ -161,7 +165,4 @@ public interface UdtChannelConfig extends ChannelConfig { * Sets {@link OptionUDT#System_Send_Buffer_Size} */ UdtChannelConfig setSystemSendBufferSize(int size); - - @Override - UdtChannelConfig setDefaultHandlerByteBufType(ChannelHandlerByteBufType type); } diff --git a/transport-udt/src/main/java/io/netty/channel/udt/UdtServerChannelConfig.java b/transport-udt/src/main/java/io/netty/channel/udt/UdtServerChannelConfig.java index 4b0666fcfb..6b1625f7b0 100644 --- a/transport-udt/src/main/java/io/netty/channel/udt/UdtServerChannelConfig.java +++ b/transport-udt/src/main/java/io/netty/channel/udt/UdtServerChannelConfig.java @@ -21,6 +21,7 @@ import io.netty.buffer.ByteBufAllocator; import io.netty.channel.ChannelConfig; import io.netty.channel.ChannelException; import io.netty.channel.ChannelOption; +import io.netty.channel.RecvByteBufAllocator; /** * A {@link ChannelConfig} for a {@link UdtServerChannel}. @@ -53,6 +54,9 @@ public interface UdtServerChannelConfig extends UdtChannelConfig { @Override UdtServerChannelConfig setAllocator(ByteBufAllocator allocator); + @Override + UdtServerChannelConfig setRecvByteBufAllocator(RecvByteBufAllocator allocator); + @Override UdtServerChannelConfig setAutoRead(boolean autoRead); @@ -79,7 +83,4 @@ public interface UdtServerChannelConfig extends UdtChannelConfig { @Override UdtServerChannelConfig setSystemSendBufferSize(int size); - - @Override - UdtServerChannelConfig setDefaultHandlerByteBufType(ChannelHandlerByteBufType type); } diff --git a/transport-udt/src/main/java/io/netty/channel/udt/nio/NioUdtAcceptorChannel.java b/transport-udt/src/main/java/io/netty/channel/udt/nio/NioUdtAcceptorChannel.java index 9d247d3557..95ddff4339 100644 --- a/transport-udt/src/main/java/io/netty/channel/udt/nio/NioUdtAcceptorChannel.java +++ b/transport-udt/src/main/java/io/netty/channel/udt/nio/NioUdtAcceptorChannel.java @@ -17,8 +17,8 @@ package io.netty.channel.udt.nio; import com.barchart.udt.TypeUDT; import com.barchart.udt.nio.ServerSocketChannelUDT; -import io.netty.buffer.MessageBuf; import io.netty.channel.ChannelException; +import io.netty.channel.MessageList; import io.netty.channel.nio.AbstractNioMessageChannel; import io.netty.channel.udt.DefaultUdtServerChannelConfig; import io.netty.channel.udt.UdtServerChannel; @@ -95,8 +95,7 @@ public abstract class NioUdtAcceptorChannel extends AbstractNioMessageChannel } @Override - protected int doWriteMessages(final MessageBuf buf, - final boolean lastSpin) throws Exception { + protected int doWriteMessages(MessageList msg, int index, boolean lastSpin) throws Exception { throw new UnsupportedOperationException(); } diff --git a/transport-udt/src/main/java/io/netty/channel/udt/nio/NioUdtByteAcceptorChannel.java b/transport-udt/src/main/java/io/netty/channel/udt/nio/NioUdtByteAcceptorChannel.java index 185b3cb5fc..09fe795441 100644 --- a/transport-udt/src/main/java/io/netty/channel/udt/nio/NioUdtByteAcceptorChannel.java +++ b/transport-udt/src/main/java/io/netty/channel/udt/nio/NioUdtByteAcceptorChannel.java @@ -17,30 +17,27 @@ package io.netty.channel.udt.nio; import com.barchart.udt.TypeUDT; import com.barchart.udt.nio.SocketChannelUDT; -import io.netty.buffer.BufType; -import io.netty.buffer.MessageBuf; import io.netty.channel.ChannelMetadata; +import io.netty.channel.MessageList; /** * Byte Channel Acceptor for UDT Streams. */ public class NioUdtByteAcceptorChannel extends NioUdtAcceptorChannel { - private static final ChannelMetadata METADATA = new ChannelMetadata( - BufType.BYTE, false); + private static final ChannelMetadata METADATA = new ChannelMetadata(false); public NioUdtByteAcceptorChannel() { super(TypeUDT.STREAM); } @Override - protected int doReadMessages(final MessageBuf buf) throws Exception { + protected int doReadMessages(MessageList buf) throws Exception { final SocketChannelUDT channelUDT = javaChannel().accept(); if (channelUDT == null) { return 0; } else { - buf.add(new NioUdtByteConnectorChannel(this, channelUDT.socketUDT() - .id(), channelUDT)); + buf.add(new NioUdtByteConnectorChannel(this, channelUDT.socketUDT().id(), channelUDT)); return 1; } } @@ -49,5 +46,4 @@ public class NioUdtByteAcceptorChannel extends NioUdtAcceptorChannel { public ChannelMetadata metadata() { return METADATA; } - } diff --git a/transport-udt/src/main/java/io/netty/channel/udt/nio/NioUdtByteConnectorChannel.java b/transport-udt/src/main/java/io/netty/channel/udt/nio/NioUdtByteConnectorChannel.java index f2d82c6fd2..76eac99fec 100644 --- a/transport-udt/src/main/java/io/netty/channel/udt/nio/NioUdtByteConnectorChannel.java +++ b/transport-udt/src/main/java/io/netty/channel/udt/nio/NioUdtByteConnectorChannel.java @@ -17,11 +17,11 @@ package io.netty.channel.udt.nio; import com.barchart.udt.TypeUDT; import com.barchart.udt.nio.SocketChannelUDT; -import io.netty.buffer.BufType; import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; import io.netty.channel.ChannelException; import io.netty.channel.ChannelMetadata; +import io.netty.channel.FileRegion; import io.netty.channel.nio.AbstractNioByteChannel; import io.netty.channel.udt.DefaultUdtChannelConfig; import io.netty.channel.udt.UdtChannel; @@ -44,8 +44,7 @@ public class NioUdtByteConnectorChannel extends AbstractNioByteChannel private static final InternalLogger logger = InternalLoggerFactory .getInstance(NioUdtByteConnectorChannel.class); - private static final ChannelMetadata METADATA = new ChannelMetadata( - BufType.BYTE, false); + private static final ChannelMetadata METADATA = new ChannelMetadata(false); private final UdtChannelConfig config; @@ -166,6 +165,11 @@ public class NioUdtByteConnectorChannel extends AbstractNioByteChannel return writtenBytes; } + @Override + protected long doWriteFileRegion(FileRegion region, boolean lastSpin) throws Exception { + throw new UnsupportedOperationException(); + } + @Override public boolean isActive() { final SocketChannelUDT channelUDT = javaChannel(); diff --git a/transport-udt/src/main/java/io/netty/channel/udt/nio/NioUdtMessageAcceptorChannel.java b/transport-udt/src/main/java/io/netty/channel/udt/nio/NioUdtMessageAcceptorChannel.java index 12c0e63a1e..0ccb785924 100644 --- a/transport-udt/src/main/java/io/netty/channel/udt/nio/NioUdtMessageAcceptorChannel.java +++ b/transport-udt/src/main/java/io/netty/channel/udt/nio/NioUdtMessageAcceptorChannel.java @@ -17,30 +17,27 @@ package io.netty.channel.udt.nio; import com.barchart.udt.TypeUDT; import com.barchart.udt.nio.SocketChannelUDT; -import io.netty.buffer.BufType; -import io.netty.buffer.MessageBuf; import io.netty.channel.ChannelMetadata; +import io.netty.channel.MessageList; /** * Message Channel Acceptor for UDT Datagrams. */ public class NioUdtMessageAcceptorChannel extends NioUdtAcceptorChannel { - private static final ChannelMetadata METADATA = new ChannelMetadata( - BufType.MESSAGE, false); + private static final ChannelMetadata METADATA = new ChannelMetadata(false); public NioUdtMessageAcceptorChannel() { super(TypeUDT.DATAGRAM); } @Override - protected int doReadMessages(final MessageBuf buf) throws Exception { + protected int doReadMessages(MessageList buf) throws Exception { final SocketChannelUDT channelUDT = javaChannel().accept(); if (channelUDT == null) { return 0; } else { - buf.add(new NioUdtMessageConnectorChannel(this, channelUDT - .socketUDT().id(), channelUDT)); + buf.add(new NioUdtMessageConnectorChannel(this, channelUDT.socketUDT().id(), channelUDT)); return 1; } } diff --git a/transport-udt/src/main/java/io/netty/channel/udt/nio/NioUdtMessageConnectorChannel.java b/transport-udt/src/main/java/io/netty/channel/udt/nio/NioUdtMessageConnectorChannel.java index c8cd60879a..c1fb1d39f5 100644 --- a/transport-udt/src/main/java/io/netty/channel/udt/nio/NioUdtMessageConnectorChannel.java +++ b/transport-udt/src/main/java/io/netty/channel/udt/nio/NioUdtMessageConnectorChannel.java @@ -17,12 +17,11 @@ package io.netty.channel.udt.nio; import com.barchart.udt.TypeUDT; import com.barchart.udt.nio.SocketChannelUDT; -import io.netty.buffer.BufType; import io.netty.buffer.ByteBuf; -import io.netty.buffer.MessageBuf; import io.netty.channel.Channel; import io.netty.channel.ChannelException; import io.netty.channel.ChannelMetadata; +import io.netty.channel.MessageList; import io.netty.channel.nio.AbstractNioMessageChannel; import io.netty.channel.udt.DefaultUdtChannelConfig; import io.netty.channel.udt.UdtChannel; @@ -48,8 +47,7 @@ public class NioUdtMessageConnectorChannel extends AbstractNioMessageChannel private static final InternalLogger logger = InternalLoggerFactory .getInstance(NioUdtMessageConnectorChannel.class); - private static final ChannelMetadata METADATA = new ChannelMetadata( - BufType.MESSAGE, false); + private static final ChannelMetadata METADATA = new ChannelMetadata(false); private final UdtChannelConfig config; @@ -143,7 +141,7 @@ public class NioUdtMessageConnectorChannel extends AbstractNioMessageChannel } @Override - protected int doReadMessages(final MessageBuf buf) throws Exception { + protected int doReadMessages(MessageList buf) throws Exception { final int maximumMessageSize = config.getReceiveBufferSize(); @@ -171,11 +169,9 @@ public class NioUdtMessageConnectorChannel extends AbstractNioMessageChannel } @Override - protected int doWriteMessages(final MessageBuf messageQueue, - final boolean lastSpin) throws Exception { - + protected int doWriteMessages(MessageList msgs, int index, boolean lastSpin) throws Exception { // expects a message - final UdtMessage message = (UdtMessage) messageQueue.peek(); + final UdtMessage message = (UdtMessage) msgs.get(index); final ByteBuf byteBuf = message.content(); @@ -208,14 +204,12 @@ public class NioUdtMessageConnectorChannel extends AbstractNioMessageChannel } // wrote the message queue completely - clear OP_WRITE. - if (messageQueue.isEmpty()) { + if (index + 1 == msgs.size()) { if ((interestOps & OP_WRITE) != 0) { key.interestOps(interestOps & ~OP_WRITE); } } - messageQueue.remove(); - message.release(); return 1; diff --git a/transport-udt/src/test/java/io/netty/test/udt/nio/NioUdtByteAcceptorChannelTest.java b/transport-udt/src/test/java/io/netty/test/udt/nio/NioUdtByteAcceptorChannelTest.java index 1910600346..260187c5d1 100644 --- a/transport-udt/src/test/java/io/netty/test/udt/nio/NioUdtByteAcceptorChannelTest.java +++ b/transport-udt/src/test/java/io/netty/test/udt/nio/NioUdtByteAcceptorChannelTest.java @@ -16,7 +16,6 @@ package io.netty.test.udt.nio; -import io.netty.buffer.BufType; import io.netty.channel.udt.nio.NioUdtByteAcceptorChannel; import org.junit.Test; @@ -29,6 +28,6 @@ public class NioUdtByteAcceptorChannelTest extends AbstractUdtTest { */ @Test public void metadata() throws Exception { - assertEquals(BufType.BYTE, new NioUdtByteAcceptorChannel().metadata().bufferType()); + assertEquals(false, new NioUdtByteAcceptorChannel().metadata().hasDisconnect()); } } diff --git a/transport-udt/src/test/java/io/netty/test/udt/nio/NioUdtByteConnectorChannelTest.java b/transport-udt/src/test/java/io/netty/test/udt/nio/NioUdtByteConnectorChannelTest.java index 45c197bc86..6426ca69ad 100644 --- a/transport-udt/src/test/java/io/netty/test/udt/nio/NioUdtByteConnectorChannelTest.java +++ b/transport-udt/src/test/java/io/netty/test/udt/nio/NioUdtByteConnectorChannelTest.java @@ -16,11 +16,10 @@ package io.netty.test.udt.nio; -import io.netty.buffer.BufType; import io.netty.channel.udt.nio.NioUdtByteConnectorChannel; import org.junit.Test; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; public class NioUdtByteConnectorChannelTest extends AbstractUdtTest { @@ -29,6 +28,6 @@ public class NioUdtByteConnectorChannelTest extends AbstractUdtTest { */ @Test public void metadata() throws Exception { - assertEquals(BufType.BYTE, new NioUdtByteConnectorChannel().metadata().bufferType()); + assertEquals(false, new NioUdtByteConnectorChannel().metadata().hasDisconnect()); } } diff --git a/transport-udt/src/test/java/io/netty/test/udt/nio/NioUdtByteRendezvousChannelTest.java b/transport-udt/src/test/java/io/netty/test/udt/nio/NioUdtByteRendezvousChannelTest.java index dd9fe080c5..cb41cbfeab 100644 --- a/transport-udt/src/test/java/io/netty/test/udt/nio/NioUdtByteRendezvousChannelTest.java +++ b/transport-udt/src/test/java/io/netty/test/udt/nio/NioUdtByteRendezvousChannelTest.java @@ -19,7 +19,6 @@ package io.netty.test.udt.nio; import com.yammer.metrics.Metrics; import com.yammer.metrics.core.Meter; import io.netty.bootstrap.Bootstrap; -import io.netty.buffer.BufType; import io.netty.channel.ChannelFuture; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.udt.nio.NioUdtByteRendezvousChannel; @@ -45,7 +44,7 @@ public class NioUdtByteRendezvousChannelTest extends AbstractUdtTest { */ @Test public void metadata() throws Exception { - assertEquals(BufType.BYTE, new NioUdtByteRendezvousChannel().metadata().bufferType()); + assertEquals(false, new NioUdtByteRendezvousChannel().metadata().hasDisconnect()); } /** diff --git a/transport-udt/src/test/java/io/netty/test/udt/nio/NioUdtMessageAcceptorChannelTest.java b/transport-udt/src/test/java/io/netty/test/udt/nio/NioUdtMessageAcceptorChannelTest.java index e7bdfe8089..a3099a8be1 100644 --- a/transport-udt/src/test/java/io/netty/test/udt/nio/NioUdtMessageAcceptorChannelTest.java +++ b/transport-udt/src/test/java/io/netty/test/udt/nio/NioUdtMessageAcceptorChannelTest.java @@ -16,7 +16,6 @@ package io.netty.test.udt.nio; -import io.netty.buffer.BufType; import io.netty.channel.udt.nio.NioUdtMessageAcceptorChannel; import org.junit.Test; @@ -29,6 +28,6 @@ public class NioUdtMessageAcceptorChannelTest extends AbstractUdtTest { */ @Test public void metadata() throws Exception { - assertEquals(BufType.MESSAGE, new NioUdtMessageAcceptorChannel().metadata().bufferType()); + assertEquals(false, new NioUdtMessageAcceptorChannel().metadata().hasDisconnect()); } } diff --git a/transport-udt/src/test/java/io/netty/test/udt/nio/NioUdtMessageConnectorChannelTest.java b/transport-udt/src/test/java/io/netty/test/udt/nio/NioUdtMessageConnectorChannelTest.java index 4d5d7fe473..8c2e2df588 100644 --- a/transport-udt/src/test/java/io/netty/test/udt/nio/NioUdtMessageConnectorChannelTest.java +++ b/transport-udt/src/test/java/io/netty/test/udt/nio/NioUdtMessageConnectorChannelTest.java @@ -16,7 +16,6 @@ package io.netty.test.udt.nio; -import io.netty.buffer.BufType; import io.netty.channel.udt.nio.NioUdtMessageConnectorChannel; import org.junit.Test; @@ -29,6 +28,6 @@ public class NioUdtMessageConnectorChannelTest extends AbstractUdtTest { */ @Test public void metadata() throws Exception { - assertEquals(BufType.MESSAGE, new NioUdtMessageConnectorChannel().metadata().bufferType()); + assertEquals(false, new NioUdtMessageConnectorChannel().metadata().hasDisconnect()); } } diff --git a/transport-udt/src/test/java/io/netty/test/udt/nio/NioUdtMessageRendezvousChannelTest.java b/transport-udt/src/test/java/io/netty/test/udt/nio/NioUdtMessageRendezvousChannelTest.java index 073943eb0d..ec99de1e6a 100644 --- a/transport-udt/src/test/java/io/netty/test/udt/nio/NioUdtMessageRendezvousChannelTest.java +++ b/transport-udt/src/test/java/io/netty/test/udt/nio/NioUdtMessageRendezvousChannelTest.java @@ -19,7 +19,6 @@ package io.netty.test.udt.nio; import com.yammer.metrics.Metrics; import com.yammer.metrics.core.Meter; import io.netty.bootstrap.Bootstrap; -import io.netty.buffer.BufType; import io.netty.channel.ChannelFuture; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.udt.nio.NioUdtMessageRendezvousChannel; @@ -45,7 +44,7 @@ public class NioUdtMessageRendezvousChannelTest extends AbstractUdtTest { */ @Test public void metadata() throws Exception { - assertEquals(BufType.MESSAGE, new NioUdtMessageRendezvousChannel().metadata().bufferType()); + assertEquals(false, new NioUdtMessageRendezvousChannel().metadata().hasDisconnect()); } /** diff --git a/transport-udt/src/test/java/io/netty/test/udt/util/EchoByteHandler.java b/transport-udt/src/test/java/io/netty/test/udt/util/EchoByteHandler.java index ebaed66ea4..ab839cd103 100644 --- a/transport-udt/src/test/java/io/netty/test/udt/util/EchoByteHandler.java +++ b/transport-udt/src/test/java/io/netty/test/udt/util/EchoByteHandler.java @@ -20,9 +20,8 @@ import com.yammer.metrics.core.Meter; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelHandlerUtil; -import io.netty.channel.ChannelInboundByteHandlerAdapter; -import io.netty.channel.ChannelOption; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.MessageList; import io.netty.channel.udt.nio.NioUdtProvider; import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; @@ -32,7 +31,7 @@ import io.netty.util.internal.logging.InternalLoggerFactory; * traffic between the echo client and server by sending the first message to * the server on activation. */ -public class EchoByteHandler extends ChannelInboundByteHandlerAdapter { +public class EchoByteHandler extends ChannelInboundHandlerAdapter { private static final InternalLogger log = InternalLoggerFactory.getInstance(EchoByteHandler.class); @@ -62,25 +61,18 @@ public class EchoByteHandler extends ChannelInboundByteHandlerAdapter { .toStringOptions()); ctx.write(message); - - ctx.flush(); } @Override - public void inboundBufferUpdated(final ChannelHandlerContext ctx, - final ByteBuf in) { - - if (meter != null) { - meter.mark(in.readableBytes()); + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + for (int i = 0; i < msgs.size(); i ++) { + ByteBuf buf = (ByteBuf) msgs.get(i); + if (meter != null) { + meter.mark(buf.readableBytes()); + } + buf.retain(); } - - final ByteBuf out = ctx.nextOutboundByteBuffer(); - - out.discardReadBytes(); // FIXME - - out.writeBytes(in); - - ctx.flush(); + ctx.write(msgs); } @Override @@ -91,10 +83,4 @@ public class EchoByteHandler extends ChannelInboundByteHandlerAdapter { ctx.close(); } - - @Override - public ByteBuf newInboundBuffer(final ChannelHandlerContext ctx) throws Exception { - return ChannelHandlerUtil.allocate(ctx, - ctx.channel().config().getOption(ChannelOption.SO_RCVBUF)); - } } diff --git a/transport-udt/src/test/java/io/netty/test/udt/util/EchoMessageHandler.java b/transport-udt/src/test/java/io/netty/test/udt/util/EchoMessageHandler.java index 7fa2c27f3f..5ed41a28f4 100644 --- a/transport-udt/src/test/java/io/netty/test/udt/util/EchoMessageHandler.java +++ b/transport-udt/src/test/java/io/netty/test/udt/util/EchoMessageHandler.java @@ -18,10 +18,10 @@ package io.netty.test.udt.util; import com.yammer.metrics.core.Meter; import io.netty.buffer.ByteBuf; -import io.netty.buffer.MessageBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.MessageList; import io.netty.channel.udt.UdtMessage; import io.netty.channel.udt.nio.NioUdtProvider; import io.netty.util.internal.logging.InternalLogger; @@ -32,8 +32,7 @@ import io.netty.util.internal.logging.InternalLoggerFactory; * between the echo peers by sending the first message to the other peer on * activation. */ -public class EchoMessageHandler extends - ChannelInboundMessageHandlerAdapter { +public class EchoMessageHandler extends ChannelInboundHandlerAdapter { private static final InternalLogger log = InternalLoggerFactory.getInstance(EchoMessageHandler.class); @@ -61,11 +60,7 @@ public class EchoMessageHandler extends log.info("ECHO active {}", NioUdtProvider.socketUDT(ctx.channel()) .toStringOptions()); - - final MessageBuf out = ctx.nextOutboundMessageBuffer(); - - out.add(message); - ctx.flush(); + ctx.write(message); } @Override @@ -75,18 +70,13 @@ public class EchoMessageHandler extends } @Override - public void messageReceived(final ChannelHandlerContext ctx, final UdtMessage message) throws Exception { - - final ByteBuf byteBuf = message.content(); - - if (meter != null) { - meter.mark(byteBuf.readableBytes()); + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + for (int i = 0; i < msgs.size(); i ++) { + UdtMessage udtMsg = (UdtMessage) msgs.get(i); + if (meter != null) { + meter.mark(udtMsg.content().readableBytes()); + } } - - final MessageBuf out = ctx.nextOutboundMessageBuffer(); - - message.retain(); - out.add(message); - ctx.flush(); + ctx.write(msgs); } } diff --git a/transport/pom.xml b/transport/pom.xml index 381a660b37..5f11431823 100644 --- a/transport/pom.xml +++ b/transport/pom.xml @@ -20,7 +20,7 @@ io.netty netty-parent - 4.0.0.Final-SNAPSHOT + 4.0.0.CR4-SNAPSHOT netty-transport diff --git a/transport/src/main/java/io/netty/bootstrap/ServerBootstrap.java b/transport/src/main/java/io/netty/bootstrap/ServerBootstrap.java index 9835c15ea2..48a3464ada 100644 --- a/transport/src/main/java/io/netty/bootstrap/ServerBootstrap.java +++ b/transport/src/main/java/io/netty/bootstrap/ServerBootstrap.java @@ -15,18 +15,16 @@ */ package io.netty.bootstrap; -import io.netty.buffer.MessageBuf; -import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelConfig; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandler; +import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelOption; import io.netty.channel.ChannelPipeline; -import io.netty.channel.ChannelStateHandlerAdapter; import io.netty.channel.EventLoopGroup; +import io.netty.channel.MessageList; import io.netty.channel.ServerChannel; import io.netty.channel.socket.SocketChannel; import io.netty.util.AttributeKey; @@ -212,8 +210,7 @@ public final class ServerBootstrap extends AbstractBootstrap { + private static class ServerBootstrapAcceptor extends ChannelInboundHandlerAdapter { private final EventLoopGroup childGroup; private final ChannelHandler childHandler; @@ -230,21 +227,12 @@ public final class ServerBootstrap extends AbstractBootstrap newInboundBuffer(ChannelHandlerContext ctx) throws Exception { - return Unpooled.messageBuffer(); - } - @Override @SuppressWarnings("unchecked") - public void inboundBufferUpdated(ChannelHandlerContext ctx) { - MessageBuf in = ctx.inboundMessageBuffer(); - for (;;) { - Channel child = in.poll(); - if (child == null) { - break; - } - + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) { + int size = msgs.size(); + for (int i = 0; i < size; i ++) { + Channel child = (Channel) msgs.get(i); child.pipeline().addLast(childHandler); for (Entry, Object> e: childOptions) { diff --git a/transport/src/main/java/io/netty/channel/AbstractChannel.java b/transport/src/main/java/io/netty/channel/AbstractChannel.java index d0d65ef920..2f7e9d6695 100644 --- a/transport/src/main/java/io/netty/channel/AbstractChannel.java +++ b/transport/src/main/java/io/netty/channel/AbstractChannel.java @@ -15,10 +15,9 @@ */ package io.netty.channel; -import io.netty.buffer.BufType; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.MessageBuf; +import io.netty.buffer.ByteBufHolder; import io.netty.util.DefaultAttributeMap; import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.logging.InternalLogger; @@ -78,13 +77,12 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha private final Integer id; private final Unsafe unsafe; private final DefaultChannelPipeline pipeline; + private final ChannelOutboundBuffer outboundBuffer = new ChannelOutboundBuffer(this); private final ChannelFuture succeededFuture = new SucceededChannelFuture(this, null); private final VoidChannelPromise voidPromise = new VoidChannelPromise(this, true); private final VoidChannelPromise unsafeVoidPromise = new VoidChannelPromise(this, false); private final CloseFuture closeFuture = new CloseFuture(this); - protected final ChannelFlushPromiseNotifier flushFutureNotifier = new ChannelFlushPromiseNotifier(); - private volatile SocketAddress localAddress; private volatile SocketAddress remoteAddress; private volatile EventLoop eventLoop; @@ -93,7 +91,6 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha private ClosedChannelException closedChannelException; private boolean inFlushNow; private boolean flushNowPending; - private FlushTask flushTaskInProgress; /** Cache for the string representation of this channel */ private boolean strValActive; @@ -134,6 +131,11 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha }); } + @Override + public boolean isWritable() { + return outboundBuffer.getWritable(); + } + @Override public final Integer id() { return id; @@ -238,13 +240,13 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha } @Override - public ChannelFuture flush() { - return pipeline.flush(); + public ChannelFuture write(Object msg) { + return pipeline.write(msg); } @Override - public ChannelFuture write(Object message) { - return pipeline.write(message); + public ChannelFuture write(MessageList msgs) { + return pipeline.write(msgs); } @Override @@ -277,30 +279,19 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha return pipeline.deregister(promise); } - @Override - public ByteBuf outboundByteBuffer() { - return pipeline.outboundByteBuffer(); - } - - @Override - @SuppressWarnings("unchecked") - public MessageBuf outboundMessageBuffer() { - return pipeline.outboundMessageBuffer(); - } - @Override public void read() { pipeline.read(); } @Override - public ChannelFuture flush(ChannelPromise promise) { - return pipeline.flush(promise); + public ChannelFuture write(Object msg, ChannelPromise promise) { + return pipeline.write(msg, promise); } @Override - public ChannelFuture write(Object message, ChannelPromise promise) { - return pipeline.write(message, promise); + public ChannelFuture write(MessageList msgs, ChannelPromise promise) { + return pipeline.write(msgs, promise); } @Override @@ -333,45 +324,6 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha return unsafe; } - @Override - public ChannelFuture sendFile(FileRegion region) { - return pipeline.sendFile(region); - } - - @Override - public ChannelFuture sendFile(FileRegion region, ChannelPromise promise) { - return pipeline.sendFile(region, promise); - } - - // 0 - not expanded because the buffer is writable - // 1 - expanded because the buffer was not writable - // 2 - could not expand because the buffer was at its maximum although the buffer is not writable. - protected static int expandReadBuffer(ByteBuf byteBuf) { - final int writerIndex = byteBuf.writerIndex(); - final int capacity = byteBuf.capacity(); - if (capacity != writerIndex) { - return 0; - } - - final int maxCapacity = byteBuf.maxCapacity(); - if (capacity == maxCapacity) { - return 2; - } - - // FIXME: Magic number - final int increment = 4096; - - if (writerIndex + increment > maxCapacity) { - // Expand to maximum capacity. - byteBuf.capacity(maxCapacity); - } else { - // Expand by the increment. - byteBuf.ensureWritable(increment); - } - - return 1; - } - /** * Create a new {@link AbstractUnsafe} instance which will be used for the life-time of the {@link Channel} */ @@ -443,164 +395,19 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha return voidPromise; } - /** - * Task which will flush a {@link FileRegion} - */ - protected final class FlushTask { - private final FileRegion region; - private final ChannelPromise promise; - private FlushTask next; - private final AbstractUnsafe unsafe; - - FlushTask(AbstractUnsafe unsafe, FileRegion region, ChannelPromise promise) { - this.region = region; - this.promise = promise; - this.unsafe = unsafe; - } - - /** - * Mark the task as success. Multiple calls if this will throw a {@link IllegalStateException}. - * - * This also will call {@link FileRegion#release()}. - */ - public void setSuccess() { - if (eventLoop().inEventLoop()) { - promise.setSuccess(); - complete(); - } else { - eventLoop().execute(new Runnable() { - @Override - public void run() { - setSuccess(); - } - }); - } - } - - /** - * Notify the task of progress in transfer of the {@link FileRegion}. - */ - public void setProgress(long progress) { - if (promise instanceof ChannelProgressivePromise) { - ((ChannelProgressivePromise) promise).setProgress(progress, region.count()); - } - } - - /** - * Mark the task as failure. Multiple calls if this will throw a {@link IllegalStateException}. - * - * This also will call {@link FileRegion#release()}. - */ - public void setFailure(final Throwable cause) { - if (eventLoop().inEventLoop()) { - promise.setFailure(cause); - complete(); - } else { - eventLoop().execute(new Runnable() { - @Override - public void run() { - setFailure(cause); - } - }); - } - } - - /** - * Return the {@link FileRegion} which should be flushed - */ - public FileRegion region() { - return region; - } - - private void complete() { - region.release(); - flushTaskInProgress = next; - if (next != null) { - try { - FileRegion region = next.region; - if (region == null) { - // no region present means the next flush task was to directly flush - // the outbound buffer - unsafe.flushNotifierAndFlush(next.promise); - } else { - // flush the region now - doFlushFileRegion(next); - } - } catch (Throwable cause) { - next.promise.setFailure(cause); - } - } else { - // notify the flush futures - flushFutureNotifier.notifyFlushFutures(); - } - } - } - /** * {@link Unsafe} implementation which sub-classes must extend and use. */ protected abstract class AbstractUnsafe implements Unsafe { - private final Runnable beginReadTask = new Runnable() { - @Override - public void run() { - beginRead(); - } - }; - private final Runnable flushLaterTask = new Runnable() { @Override public void run() { flushNowPending = false; - flush(voidPromise()); + flush(); } }; - @Override - public final void sendFile(final FileRegion region, final ChannelPromise promise) { - if (outboundBufSize() > 0) { - flushNotifier(newPromise()).addListener(new ChannelFutureListener() { - @Override - public void operationComplete(ChannelFuture cf) throws Exception { - sendFile0(region, promise); - } - }); - } else { - // nothing pending try to send the fileRegion now! - sendFile0(region, promise); - } - } - - private void sendFile0(FileRegion region, ChannelPromise promise) { - FlushTask task = flushTaskInProgress; - if (task == null) { - flushTaskInProgress = task = new FlushTask(this, region, promise); - try { - // the first FileRegion to flush so trigger it now! - doFlushFileRegion(task); - } catch (Throwable cause) { - region.release(); - promise.setFailure(cause); - } - return; - } - - for (;;) { - FlushTask next = task.next; - if (next == null) { - break; - } - task = next; - } - // there is something that needs to get flushed first so add it as next in the chain - task.next = new FlushTask(this, region, promise); - } - - @Override - public final ChannelHandlerContext headContext() { - return pipeline.head; - } - @Override public final SocketAddress localAddress() { return localAddress0(); @@ -745,7 +552,10 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha closedChannelException = new ClosedChannelException(); } - flushFutureNotifier.notifyFlushFutures(closedChannelException); + // fail all queued messages + if (outboundBuffer.next()) { + outboundBuffer.fail(closedChannelException); + } if (wasActive && !isActive()) { invokeLater(new Runnable() { @@ -827,48 +637,12 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha } @Override - public void flush(final ChannelPromise promise) { - FlushTask task = flushTaskInProgress; - if (task == null) { - flushNotifierAndFlush(promise); - } else { - // loop over the tasks to find the last one - for (;;) { - FlushTask t = task.next; - if (t == null) { - break; - } - task = t.next; - } - task.next = new FlushTask(this, null, promise); - } + public void write(MessageList msgs, ChannelPromise promise) { + outboundBuffer.add(msgs, promise); + flush(); } - private void flushNotifierAndFlush(ChannelPromise promise) { - flushNotifier(promise); - flush0(); - } - - private int outboundBufSize() { - final int bufSize; - final ChannelHandlerContext ctx = headContext(); - if (metadata().bufferType() == BufType.BYTE) { - bufSize = ctx.outboundByteBuffer().readableBytes(); - } else { - bufSize = ctx.outboundMessageBuffer().size(); - } - return bufSize; - } - - private ChannelFuture flushNotifier(ChannelPromise promise) { - // Append flush future to the notification list. - if (promise != voidPromise) { - flushFutureNotifier.add(promise, outboundBufSize()); - } - return promise; - } - - private void flush0() { + private void flush() { if (!inFlushNow) { // Avoid re-entrance try { // Flush immediately only when there's no pending flush. @@ -878,10 +652,8 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha flushNow(); } } catch (Throwable t) { - flushFutureNotifier.notifyFlushFutures(t); - if (t instanceof IOException) { - close(voidPromise()); - } + outboundBuffer.fail(t); + close(voidPromise()); } } else { if (!flushNowPending) { @@ -890,47 +662,55 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha } } } + @Override public final void flushNow() { - if (inFlushNow || flushTaskInProgress != null) { + if (inFlushNow) { return; } inFlushNow = true; - ChannelHandlerContext ctx = headContext(); - Throwable cause = null; + final ChannelOutboundBuffer outboundBuffer = AbstractChannel.this.outboundBuffer; try { - if (metadata().bufferType() == BufType.BYTE) { - ByteBuf out = ctx.outboundByteBuffer(); - int oldSize = out.readableBytes(); - try { - doFlushByteBuffer(out); - } catch (Throwable t) { - cause = t; - } finally { - int delta = oldSize - out.readableBytes(); - out.discardSomeReadBytes(); - flushFutureNotifier.increaseWriteCounter(delta); + for (;;) { + ChannelPromise promise = outboundBuffer.currentPromise; + if (promise == null) { + if (!outboundBuffer.next()) { + break; + } + promise = outboundBuffer.currentPromise; } - } else { - MessageBuf out = ctx.outboundMessageBuffer(); - int oldSize = out.size(); - try { - doFlushMessageBuffer(out); - } catch (Throwable t) { - cause = t; - } finally { - flushFutureNotifier.increaseWriteCounter(oldSize - out.size()); + + MessageList messages = outboundBuffer.currentMessages; + int messageIndex = outboundBuffer.currentMessageIndex; + int messageCount = messages.size(); + if (messageCount == 0) { + messages.recycle(); + promise.trySuccess(); + if (!outboundBuffer.next()) { + break; + } else { + continue; + } + } + + int writtenMessages = doWrite(messages, messageIndex); + outboundBuffer.currentMessageIndex = messageIndex += writtenMessages; + if (messageIndex >= messageCount) { + messages.recycle(); + promise.trySuccess(); + if (!outboundBuffer.next()) { + break; + } + } else { + // Could not flush the current write request completely. Try again later. + break; } } - - if (cause == null) { - flushFutureNotifier.notifyFlushFutures(); - } else { - flushFutureNotifier.notifyFlushFutures(cause); - if (cause instanceof IOException) { - close(voidPromise()); - } + } catch (Throwable t) { + outboundBuffer.fail(t); + if (t instanceof IOException) { + close(voidPromise()); } } finally { inFlushNow = false; @@ -1042,37 +822,33 @@ public abstract class AbstractChannel extends DefaultAttributeMap implements Cha * Flush the content of the given {@link ByteBuf} to the remote peer. * * Sub-classes may override this as this implementation will just thrown an {@link UnsupportedOperationException} - */ - protected void doFlushByteBuffer(ByteBuf buf) throws Exception { - throw new UnsupportedOperationException(); - } - - /** - * Flush the content of the given {@link MessageBuf} to the remote peer. * - * Sub-classes may override this as this implementation will just thrown an {@link UnsupportedOperationException} + * @return the number of written messages */ - protected void doFlushMessageBuffer(MessageBuf buf) throws Exception { - throw new UnsupportedOperationException(); - } + protected abstract int doWrite(MessageList msgs, int index) throws Exception; - /** - * Flush the content of the given {@link FlushTask} to the remote peer. - * - * Sub-classes may override this as this implementation will just thrown an {@link UnsupportedOperationException} - */ - protected void doFlushFileRegion(FlushTask task) throws Exception { - throw new UnsupportedOperationException(); - } - - protected static void checkEOF(FileRegion region, long writtenBytes) throws IOException { - if (writtenBytes < region.count()) { + protected static void checkEOF(FileRegion region) throws IOException { + if (region.transfered() < region.count()) { throw new EOFException("Expected to be able to write " + region.count() + " bytes, but only wrote " - + writtenBytes); + + region.transfered()); } } + /** + * Calculate the number of bytes a message takes up in memory. Sub-classes may override this if they use different + * messages then {@link ByteBuf} or {@link ByteBufHolder}. If the size can not be calculated 0 should be returned. + */ + protected int calculateMessageSize(Object message) { + if (message instanceof ByteBuf) { + return ((ByteBuf) message).readableBytes(); + } + if (message instanceof ByteBufHolder) { + return ((ByteBufHolder) message).content().readableBytes(); + } + return 0; + } + /** * Return {@code true} if a flush to the {@link Channel} is currently pending. */ diff --git a/transport/src/main/java/io/netty/channel/AbstractServerChannel.java b/transport/src/main/java/io/netty/channel/AbstractServerChannel.java index 12d5181e1f..9334aa676f 100755 --- a/transport/src/main/java/io/netty/channel/AbstractServerChannel.java +++ b/transport/src/main/java/io/netty/channel/AbstractServerChannel.java @@ -15,9 +15,7 @@ */ package io.netty.channel; -import io.netty.buffer.BufType; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.MessageBuf; +import io.netty.buffer.ByteBufUtil; import java.net.SocketAddress; @@ -27,13 +25,13 @@ import java.net.SocketAddress; *
    *
  • {@link #connect(SocketAddress, ChannelPromise)}
  • *
  • {@link #disconnect(ChannelPromise)}
  • - *
  • {@link #flush(ChannelPromise)}
  • + *
  • {@link ChannelOutboundInvoker#write(Object, ChannelPromise)}
  • *
  • and the shortcut methods which calls the methods mentioned above *
*/ public abstract class AbstractServerChannel extends AbstractChannel implements ServerChannel { - private static final ChannelMetadata METADATA = new ChannelMetadata(BufType.MESSAGE, false); + private static final ChannelMetadata METADATA = new ChannelMetadata(false); /** * Creates a new instance. @@ -42,18 +40,6 @@ public abstract class AbstractServerChannel extends AbstractChannel implements S super(null, id); } - @Override - public ByteBuf outboundByteBuffer() { - throw new NoSuchBufferException(String.format( - "%s does not have an outbound buffer.", ServerChannel.class.getSimpleName())); - } - - @Override - public MessageBuf outboundMessageBuffer() { - throw new NoSuchBufferException(String.format( - "%s does not have an outbound buffer.", ServerChannel.class.getSimpleName())); - } - @Override public ChannelMetadata metadata() { return METADATA; @@ -84,22 +70,28 @@ public abstract class AbstractServerChannel extends AbstractChannel implements S return new DefaultServerUnsafe(); } + @Override + protected int doWrite(MessageList msgs, int index) throws Exception { + throw new UnsupportedOperationException(); + } + private final class DefaultServerUnsafe extends AbstractUnsafe { @Override - public void flush(final ChannelPromise future) { - reject(future); + public void write(MessageList msgs, ChannelPromise promise) { + reject(promise); + int size = msgs.size(); + for (int i = 0; i < size; i ++) { + ByteBufUtil.release(msgs.get(i)); + } } @Override - public void connect( - final SocketAddress remoteAddress, final SocketAddress localAddress, - final ChannelPromise future) { - reject(future); + public void connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) { + reject(promise); } - private void reject(ChannelPromise future) { - Exception cause = new UnsupportedOperationException(); - future.setFailure(cause); + private void reject(ChannelPromise promise) { + promise.setFailure(new UnsupportedOperationException()); } } } diff --git a/transport/src/main/java/io/netty/channel/AdaptiveRecvByteBufAllocator.java b/transport/src/main/java/io/netty/channel/AdaptiveRecvByteBufAllocator.java new file mode 100644 index 0000000000..4c123840b0 --- /dev/null +++ b/transport/src/main/java/io/netty/channel/AdaptiveRecvByteBufAllocator.java @@ -0,0 +1,194 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.channel; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufAllocator; + +import java.util.ArrayList; +import java.util.List; + +/** + * The {@link RecvByteBufAllocator} that automatically increases and + * decreases the predicted buffer size on feed back. + *

+ * It gradually increases the expected number of readable bytes if the previous + * read fully filled the allocated buffer. It gradually decreases the expected + * number of readable bytes if the read operation was not able to fill a certain + * amount of the allocated buffer two times consecutively. Otherwise, it keeps + * returning the same prediction. + */ +public class AdaptiveRecvByteBufAllocator implements RecvByteBufAllocator { + + static final int DEFAULT_MINIMUM = 64; + static final int DEFAULT_INITIAL = 1024; + static final int DEFAULT_MAXIMUM = 65536; + + private static final int INDEX_INCREMENT = 4; + private static final int INDEX_DECREMENT = 1; + + private static final int[] SIZE_TABLE; + + static { + List sizeTable = new ArrayList(); + for (int i = 1; i <= 8; i ++) { + sizeTable.add(i); + } + + for (int i = 4; i < 32; i ++) { + long v = 1L << i; + long inc = v >>> 4; + v -= inc << 3; + + for (int j = 0; j < 8; j ++) { + v += inc; + if (v > Integer.MAX_VALUE) { + sizeTable.add(Integer.MAX_VALUE); + } else { + sizeTable.add((int) v); + } + } + } + + SIZE_TABLE = new int[sizeTable.size()]; + for (int i = 0; i < SIZE_TABLE.length; i ++) { + SIZE_TABLE[i] = sizeTable.get(i); + } + } + + public static final AdaptiveRecvByteBufAllocator DEFAULT = new AdaptiveRecvByteBufAllocator(); + + private static int getSizeTableIndex(final int size) { + if (size <= 16) { + return size - 1; + } + + int bits = 0; + int v = size; + do { + v >>>= 1; + bits ++; + } while (v != 0); + + final int baseIdx = bits << 3; + final int startIdx = baseIdx - 18; + final int endIdx = baseIdx - 25; + + for (int i = startIdx; i >= endIdx; i --) { + if (size >= SIZE_TABLE[i]) { + return i; + } + } + + throw new Error("shouldn't reach here; please file a bug report."); + } + + private static final class HandleImpl implements Handle { + private final int minIndex; + private final int maxIndex; + private int index; + private int nextReceiveBufferSize; + private boolean decreaseNow; + + HandleImpl(int minIndex, int maxIndex, int initial) { + this.minIndex = minIndex; + this.maxIndex = maxIndex; + + index = getSizeTableIndex(initial); + nextReceiveBufferSize = SIZE_TABLE[index]; + } + + @Override + public ByteBuf allocate(ByteBufAllocator alloc) { + return alloc.ioBuffer(nextReceiveBufferSize); + } + + @Override + public int guess() { + return nextReceiveBufferSize; + } + + @Override + public void record(int actualReadBytes) { + if (actualReadBytes <= SIZE_TABLE[Math.max(0, index - INDEX_DECREMENT - 1)]) { + if (decreaseNow) { + index = Math.max(index - INDEX_DECREMENT, minIndex); + nextReceiveBufferSize = SIZE_TABLE[index]; + decreaseNow = false; + } else { + decreaseNow = true; + } + } else if (actualReadBytes >= nextReceiveBufferSize) { + index = Math.min(index + INDEX_INCREMENT, maxIndex); + nextReceiveBufferSize = SIZE_TABLE[index]; + decreaseNow = false; + } + } + } + + private final int minIndex; + private final int maxIndex; + private final int initial; + + /** + * Creates a new predictor with the default parameters. With the default + * parameters, the expected buffer size starts from {@code 1024}, does not + * go down below {@code 64}, and does not go up above {@code 65536}. + */ + private AdaptiveRecvByteBufAllocator() { + this(DEFAULT_MINIMUM, DEFAULT_INITIAL, DEFAULT_MAXIMUM); + } + + /** + * Creates a new predictor with the specified parameters. + * + * @param minimum the inclusive lower bound of the expected buffer size + * @param initial the initial buffer size when no feed back was received + * @param maximum the inclusive upper bound of the expected buffer size + */ + public AdaptiveRecvByteBufAllocator(int minimum, int initial, int maximum) { + if (minimum <= 0) { + throw new IllegalArgumentException("minimum: " + minimum); + } + if (initial < minimum) { + throw new IllegalArgumentException("initial: " + initial); + } + if (maximum < initial) { + throw new IllegalArgumentException("maximum: " + maximum); + } + + int minIndex = getSizeTableIndex(minimum); + if (SIZE_TABLE[minIndex] < minimum) { + this.minIndex = minIndex + 1; + } else { + this.minIndex = minIndex; + } + + int maxIndex = getSizeTableIndex(maximum); + if (SIZE_TABLE[maxIndex] > maximum) { + this.maxIndex = maxIndex - 1; + } else { + this.maxIndex = maxIndex; + } + + this.initial = initial; + } + + @Override + public Handle newHandle() { + return new HandleImpl(minIndex, maxIndex, initial); + } +} diff --git a/transport/src/main/java/io/netty/channel/Channel.java b/transport/src/main/java/io/netty/channel/Channel.java index a8612ce462..f50a42ee76 100755 --- a/transport/src/main/java/io/netty/channel/Channel.java +++ b/transport/src/main/java/io/netty/channel/Channel.java @@ -15,8 +15,6 @@ */ package io.netty.channel; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.MessageBuf; import io.netty.channel.socket.DatagramChannel; import io.netty.channel.socket.DatagramPacket; import io.netty.channel.socket.ServerSocketChannel; @@ -113,22 +111,6 @@ public interface Channel extends AttributeMap, ChannelOutboundInvoker, ChannelPr */ ChannelMetadata metadata(); - /** - * Return the last {@link ByteBuf} of the {@link ChannelPipeline} which belongs to this {@link Channel}. - * - * This method may throw an {@link NoSuchBufferException} if you try to access this buffer and the - * {@link ChannelPipeline} does not contain any {@link ByteBuf}. - */ - ByteBuf outboundByteBuffer(); - - /** - * Return the last {@link MessageBuf} of the {@link ChannelPipeline} which belongs to this {@link Channel}. - * - * This method may throw an {@link NoSuchBufferException} if you try to access this buffer and the - * {@link ChannelPipeline} does not contain any {@link MessageBuf}. - */ - MessageBuf outboundMessageBuffer(); - /** * Returns the local address where this channel is bound to. The returned * {@link SocketAddress} is supposed to be down-cast into more concrete @@ -162,6 +144,14 @@ public interface Channel extends AttributeMap, ChannelOutboundInvoker, ChannelPr */ ChannelFuture closeFuture(); + /** + * Returns {@code true} if and only if the I/O thread will perform the + * requested write operation immediately. Any write requests made when + * this method returns {@code false} are queued until the I/O thread is + * ready to process the queued write requests. + */ + boolean isWritable(); + /** * Returns an internal-use-only object that provides unsafe operations. */ @@ -172,7 +162,6 @@ public interface Channel extends AttributeMap, ChannelOutboundInvoker, ChannelPr * are only provided to implement the actual transport, and must be invoked from an I/O thread except for the * following methods: *

    - *
  • {@link #headContext()}
  • *
  • {@link #localAddress()}
  • *
  • {@link #remoteAddress()}
  • *
  • {@link #closeForcibly()}
  • @@ -181,11 +170,6 @@ public interface Channel extends AttributeMap, ChannelOutboundInvoker, ChannelPr *
*/ interface Unsafe { - /** - * Return the internal {@link ChannelHandlerContext} that is placed before all user handlers. - */ - ChannelHandlerContext headContext(); - /** * Return the {@link SocketAddress} to which is bound local or * {@code null} if none. @@ -250,23 +234,15 @@ public interface Channel extends AttributeMap, ChannelOutboundInvoker, ChannelPr void beginRead(); /** - * Flush out all data that was buffered in the buffer of the {@link #headContext()} and was not - * flushed out yet. After that is done the {@link ChannelFuture} will get notified + * Schedules a write operation. */ - void flush(ChannelPromise promise); + void write(MessageList msgs, ChannelPromise promise); /** * Flush out all data now. */ void flushNow(); - /** - * Send a {@link FileRegion} to the remote peer and notify the {@link ChannelPromise} once it completes - * or an error was detected. Once the {@link FileRegion} was transfered or an error was thrown it will - * automaticly call {@link FileRegion#release()}. - */ - void sendFile(FileRegion region, ChannelPromise promise); - /** * Return a special ChannelPromise which can be reused and passed to the operations in {@link Unsafe}. * It will never be notified of a success or error and so is only a placeholder for operations diff --git a/transport/src/main/java/io/netty/channel/ChannelConfig.java b/transport/src/main/java/io/netty/channel/ChannelConfig.java index ef160990f8..b9747ae210 100644 --- a/transport/src/main/java/io/netty/channel/ChannelConfig.java +++ b/transport/src/main/java/io/netty/channel/ChannelConfig.java @@ -15,7 +15,6 @@ */ package io.netty.channel; -import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; import io.netty.channel.socket.SocketChannelConfig; @@ -63,12 +62,6 @@ import java.util.Map; */ public interface ChannelConfig { - enum ChannelHandlerByteBufType { - HEAP, - DIRECT, - PREFER_DIRECT - } - /** * Return all set {@link ChannelOption}'s. */ @@ -158,6 +151,18 @@ public interface ChannelConfig { */ ChannelConfig setAllocator(ByteBufAllocator allocator); + /** + * Returns {@link RecvByteBufAllocator} which is used for the channel + * to allocate receive buffers. + */ + RecvByteBufAllocator getRecvByteBufAllocator(); + + /** + * Set the {@link ByteBufAllocator} which is used for the channel + * to allocate receive buffers. + */ + ChannelConfig setRecvByteBufAllocator(RecvByteBufAllocator allocator); + /** * Returns {@code true} if and only if {@link ChannelHandlerContext#read()} will be invoked automatically so that * a user application doesn't need to call it at all. The default value is {@code true}. @@ -171,21 +176,34 @@ public interface ChannelConfig { ChannelConfig setAutoRead(boolean autoRead); /** - * Returns the {@link ChannelHandlerByteBufType} which is used to determine what kind of {@link ByteBuf} will - * be created by the {@link ChannelInboundByteHandler#newInboundBuffer(ChannelHandlerContext)} and - * {@link ChannelOutboundByteHandler#newOutboundBuffer(ChannelHandlerContext)} methods. - *

- * The implementation of {@link ChannelInboundByteHandler} or {@link ChannelOutboundByteHandler} may still return - * another {@link ByteBuf} if it depends on a special type. - * - * The default is {@link ChannelHandlerByteBufType#PREFER_DIRECT}. + * Returns the high water mark of the write buffer. If the number of bytes + * queued in the write buffer exceeds this value, {@link Channel#isWritable()} + * will start to return {@code false}. */ - ChannelHandlerByteBufType getDefaultHandlerByteBufType(); + int getWriteBufferHighWaterMark(); /** - * Sets the {@link ChannelHandlerByteBufType} which is used to determine what kind of {@link ByteBuf} will - * be created by the {@link ChannelInboundByteHandler#newInboundBuffer(ChannelHandlerContext)} and - * {@link ChannelOutboundByteHandler#newOutboundBuffer(ChannelHandlerContext)} methods. + * Sets the high water mark of the write buffer. If the number of bytes + * queued in the write buffer exceeds this value, {@link Channel#isWritable()} + * will start to return {@code false}. */ - ChannelConfig setDefaultHandlerByteBufType(ChannelHandlerByteBufType type); + ChannelConfig setWriteBufferHighWaterMark(int writeBufferHighWaterMark); + + /** + * Returns the low water mark of the write buffer. Once the number of bytes + * queued in the write buffer exceeded the + * {@linkplain #setWriteBufferHighWaterMark(int) high water mark} and then + * dropped down below this value, {@link Channel#isWritable()} will start to return + * {@code true} again. + */ + int getWriteBufferLowWaterMark(); + + /** + * Sets the low water mark of the write buffer. Once the number of bytes + * queued in the write buffer exceeded the + * {@linkplain #setWriteBufferHighWaterMark(int) high water mark} and then + * dropped down below this value, {@link Channel#isWritable()} will start toreturn + * {@code true} again. + */ + ChannelConfig setWriteBufferLowWaterMark(int writeBufferLowWaterMark); } diff --git a/transport/src/main/java/io/netty/channel/ChannelDuplexHandler.java b/transport/src/main/java/io/netty/channel/ChannelDuplexHandler.java index 509c83ad6c..2a8b73bcdc 100644 --- a/transport/src/main/java/io/netty/channel/ChannelDuplexHandler.java +++ b/transport/src/main/java/io/netty/channel/ChannelDuplexHandler.java @@ -18,17 +18,17 @@ package io.netty.channel; import java.net.SocketAddress; /** - * {@link ChannelHandler} implementation which represents a combination out of a {@link ChannelStateHandler} and - * the {@link ChannelOperationHandler}. + * {@link ChannelHandler} implementation which represents a combination out of a {@link ChannelInboundHandler} and + * the {@link ChannelOutboundHandler}. * * It is a good starting point if your {@link ChannelHandler} implementation needs to intercept operations and also * state updates. */ -public abstract class ChannelDuplexHandler extends ChannelStateHandlerAdapter implements ChannelOperationHandler { +public class ChannelDuplexHandler extends ChannelInboundHandlerAdapter implements ChannelOutboundHandler { /** * Calls {@link ChannelHandlerContext#bind(SocketAddress, ChannelPromise)} to forward - * to the next {@link ChannelOperationHandler} in the {@link ChannelPipeline}. + * to the next {@link ChannelOutboundHandler} in the {@link ChannelPipeline}. * * Sub-classes may override this method to change behavior. */ @@ -40,7 +40,7 @@ public abstract class ChannelDuplexHandler extends ChannelStateHandlerAdapter im /** * Calls {@link ChannelHandlerContext#connect(SocketAddress, SocketAddress, ChannelPromise)} to forward - * to the next {@link ChannelOperationHandler} in the {@link ChannelPipeline}. + * to the next {@link ChannelOutboundHandler} in the {@link ChannelPipeline}. * * Sub-classes may override this method to change behavior. */ @@ -52,7 +52,7 @@ public abstract class ChannelDuplexHandler extends ChannelStateHandlerAdapter im /** * Calls {@link ChannelHandlerContext#disconnect(ChannelPromise)} to forward - * to the next {@link ChannelOperationHandler} in the {@link ChannelPipeline}. + * to the next {@link ChannelOutboundHandler} in the {@link ChannelPipeline}. * * Sub-classes may override this method to change behavior. */ @@ -64,41 +64,33 @@ public abstract class ChannelDuplexHandler extends ChannelStateHandlerAdapter im /** * Calls {@link ChannelHandlerContext#close(ChannelPromise)} to forward - * to the next {@link ChannelOperationHandler} in the {@link ChannelPipeline}. + * to the next {@link ChannelOutboundHandler} in the {@link ChannelPipeline}. * * Sub-classes may override this method to change behavior. */ @Override - public void close(ChannelHandlerContext ctx, ChannelPromise future) - throws Exception { + public void close(ChannelHandlerContext ctx, ChannelPromise future) throws Exception { ctx.close(future); } /** * Calls {@link ChannelHandlerContext#close(ChannelPromise)} to forward - * to the next {@link ChannelOperationHandler} in the {@link ChannelPipeline}. + * to the next {@link ChannelOutboundHandler} in the {@link ChannelPipeline}. * * Sub-classes may override this method to change behavior. */ @Override - public void deregister(ChannelHandlerContext ctx, ChannelPromise future) - throws Exception { + public void deregister(ChannelHandlerContext ctx, ChannelPromise future) throws Exception { ctx.deregister(future); } @Override - public void read(ChannelHandlerContext ctx) { + public void read(ChannelHandlerContext ctx) throws Exception { ctx.read(); } - /** - * Calls {@link ChannelHandlerContext#sendFile(FileRegion, ChannelPromise)} to forward - * to the next {@link ChannelOperationHandler} in the {@link ChannelPipeline}. - * - * Sub-classes may override this method to change behavior. - */ @Override - public void sendFile(ChannelHandlerContext ctx, FileRegion region, ChannelPromise future) throws Exception { - ctx.sendFile(region, future); + public void write(ChannelHandlerContext ctx, MessageList msgs, ChannelPromise promise) throws Exception { + ctx.write(msgs, promise); } } diff --git a/transport/src/main/java/io/netty/channel/ChannelHandler.java b/transport/src/main/java/io/netty/channel/ChannelHandler.java index ba6061fa78..3d10dc4c66 100644 --- a/transport/src/main/java/io/netty/channel/ChannelHandler.java +++ b/transport/src/main/java/io/netty/channel/ChannelHandler.java @@ -15,8 +15,6 @@ */ package io.netty.channel; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.MessageBuf; import io.netty.util.Attribute; import io.netty.util.AttributeKey; @@ -40,14 +38,8 @@ import java.lang.annotation.Target; * * But the most useful for developers may be: *
    - *
  • {@link ChannelInboundByteHandlerAdapter} handles and intercepts inbound operations where the inbound message - * type is a {@link ByteBuf}.
  • - *
  • {@link ChannelInboundMessageHandlerAdapter} handles and intercepts inbound operations where the inbound message - * type is a {@link MessageBuf}.
  • - * *
  • {@link ChannelOutboundByteHandlerAdapter} handles and intercepts outbound operations where the inbound message - * type is a {@link ByteBuf}.
  • - *
  • {@link ChannelOutboundMessageHandlerAdapter} handles and intercepts outbound operations where the inbound message - * type is a {@link MessageBuf}.
  • + *
  • {@link ChannelInboundHandlerAdapter} handles and intercepts inbound operations
  • + *
  • {@link ChannelOutboundHandlerAdapter} handles and intercepts outbound operations
  • *
* * You will also find more detailed explanation from the documentation of diff --git a/transport/src/main/java/io/netty/channel/ChannelHandlerContext.java b/transport/src/main/java/io/netty/channel/ChannelHandlerContext.java index 35ebb2ac32..8935012854 100755 --- a/transport/src/main/java/io/netty/channel/ChannelHandlerContext.java +++ b/transport/src/main/java/io/netty/channel/ChannelHandlerContext.java @@ -16,9 +16,6 @@ package io.netty.channel; -import io.netty.buffer.BufType; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.MessageBuf; import io.netty.util.Attribute; import io.netty.util.AttributeKey; import io.netty.util.AttributeMap; @@ -83,7 +80,7 @@ import java.nio.channels.Channels; * as how many times it is added to pipelines, regardless if it is added to the * same pipeline multiple times or added to different pipelines multiple times: *
- * public class FactorialHandler extends {@link ChannelInboundMessageHandlerAdapter}<{@link Integer}> {
+ * public class FactorialHandler extends {@link ChannelInboundHandlerAdapter}<{@link Integer}> {
  *
  *   private final {@link AttributeKey}<{@link Integer}> counter =
  *           new {@link AttributeKey}<{@link Integer}>("counter");
@@ -153,101 +150,6 @@ public interface ChannelHandlerContext
      */
     ChannelHandler handler();
 
-    /**
-     * Return {@code true} if the {@link ChannelHandlerContext} has an {@link ByteBuf} bound for inbound
-     * which can be used.
-     */
-    boolean hasInboundByteBuffer();
-
-    /**
-     * Return {@code true} if the {@link ChannelHandlerContext} has a {@link MessageBuf} bound for inbound
-     * which can be used.
-     */
-    boolean hasInboundMessageBuffer();
-
-    /**
-     * Return the bound {@link ByteBuf} for inbound data if {@link #hasInboundByteBuffer()} returned
-     * {@code true}. If {@link #hasInboundByteBuffer()} returned {@code false} it will throw a
-     * {@link UnsupportedOperationException}.
-     * 

- * This method can only be called from within the event-loop, otherwise it will throw an - * {@link IllegalStateException}. - */ - ByteBuf inboundByteBuffer(); - - /** - * Return the bound {@link MessageBuf} for inbound data if {@link #hasInboundMessageBuffer()} returned - * {@code true}. If {@link #hasInboundMessageBuffer()} returned {@code false} it will throw a - * {@link UnsupportedOperationException}. - *

- * This method can only be called from within the event-loop, otherwise it will throw an - * {@link IllegalStateException}. - */ - MessageBuf inboundMessageBuffer(); - - /** - * Return {@code true} if the {@link ChannelHandlerContext} has an {@link ByteBuf} bound for outbound - * data which can be used. - * - */ - boolean hasOutboundByteBuffer(); - - /** - * Return {@code true} if the {@link ChannelHandlerContext} has a {@link MessageBuf} bound for outbound - * which can be used. - */ - boolean hasOutboundMessageBuffer(); - - /** - * Return the bound {@link ByteBuf} for outbound data if {@link #hasOutboundByteBuffer()} returned - * {@code true}. If {@link #hasOutboundByteBuffer()} returned {@code false} it will throw - * a {@link UnsupportedOperationException}. - *

- * This method can only be called from within the event-loop, otherwise it will throw an - * {@link IllegalStateException}. - */ - ByteBuf outboundByteBuffer(); - - /** - * Return the bound {@link MessageBuf} for outbound data if {@link #hasOutboundMessageBuffer()} returned - * {@code true}. If {@link #hasOutboundMessageBuffer()} returned {@code false} it will throw a - * {@link UnsupportedOperationException}. - *

- * This method can only be called from within the event-loop, otherwise it will throw an - * {@link IllegalStateException}. - */ - MessageBuf outboundMessageBuffer(); - - /** - * Return the {@link ByteBuf} of the next {@link ChannelInboundByteHandler} in the pipeline. - */ - ByteBuf nextInboundByteBuffer(); - - /** - * Return the {@link MessageBuf} of the next {@link ChannelInboundMessageHandler} in the pipeline. - */ - MessageBuf nextInboundMessageBuffer(); - - /** - * Return the {@link ByteBuf} of the next {@link ChannelOutboundByteHandler} in the pipeline. - */ - ByteBuf nextOutboundByteBuffer(); - - /** - * Return the {@link MessageBuf} of the next {@link ChannelOutboundMessageHandler} in the pipeline. - */ - MessageBuf nextOutboundMessageBuffer(); - - /** - * Return the {@link BufType} of the next {@link ChannelInboundHandler} in the pipeline. - */ - BufType nextInboundBufferType(); - - /** - * Return the {@link BufType} of the next {@link ChannelOutboundHandler} in the pipeline. - */ - BufType nextOutboundBufferType(); - @Override ChannelHandlerContext fireChannelRegistered(); @@ -267,8 +169,14 @@ public interface ChannelHandlerContext ChannelHandlerContext fireUserEventTriggered(Object event); @Override - ChannelHandlerContext fireInboundBufferUpdated(); + ChannelHandlerContext fireMessageReceived(Object msg); + + @Override + ChannelHandlerContext fireMessageReceived(MessageList msgs); @Override ChannelHandlerContext fireChannelReadSuspended(); + + @Override + ChannelHandlerContext fireChannelWritabilityChanged(); } diff --git a/transport/src/main/java/io/netty/channel/ChannelHandlerUtil.java b/transport/src/main/java/io/netty/channel/ChannelHandlerUtil.java deleted file mode 100644 index 080bb3b4d1..0000000000 --- a/transport/src/main/java/io/netty/channel/ChannelHandlerUtil.java +++ /dev/null @@ -1,321 +0,0 @@ -/* - * Copyright 2013 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ - -package io.netty.channel; - -import io.netty.buffer.BufType; -import io.netty.buffer.BufUtil; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.MessageBuf; -import io.netty.util.Signal; -import io.netty.util.internal.logging.InternalLogger; -import io.netty.util.internal.logging.InternalLoggerFactory; - -/** - * Utility methods for use within your {@link ChannelHandler} implementation. - */ -public final class ChannelHandlerUtil { - - public static final Signal ABORT = new Signal(ChannelHandlerUtil.class.getName() + ".ABORT"); - - private static final InternalLogger logger = InternalLoggerFactory.getInstance(ChannelHandlerUtil.class); - - public static void handleInboundBufferUpdated( - ChannelHandlerContext ctx, SingleInboundMessageHandler handler) throws Exception { - MessageBuf in = ctx.inboundMessageBuffer(); - if (in.isEmpty() || !handler.beginMessageReceived(ctx)) { - return; - } - - MessageBuf out = ctx.nextInboundMessageBuffer(); - int oldOutSize = out.size(); - try { - for (;;) { - Object msg = in.poll(); - if (msg == null) { - break; - } - - if (!handler.acceptInboundMessage(msg)) { - out.add(msg); - continue; - } - - @SuppressWarnings("unchecked") - T imsg = (T) msg; - try { - handler.messageReceived(ctx, imsg); - } finally { - BufUtil.release(imsg); - } - } - } catch (Signal abort) { - abort.expect(ABORT); - } finally { - if (oldOutSize != out.size()) { - ctx.fireInboundBufferUpdated(); - } - - handler.endMessageReceived(ctx); - } - } - - public static void handleFlush( - ChannelHandlerContext ctx, ChannelPromise promise, - SingleOutboundMessageHandler handler) throws Exception { - - handleFlush(ctx, promise, true, handler); - } - - public static void handleFlush( - ChannelHandlerContext ctx, ChannelPromise promise, boolean closeOnFailedFlush, - SingleOutboundMessageHandler handler) throws Exception { - - MessageBuf in = ctx.outboundMessageBuffer(); - final int inSize = in.size(); - if (inSize == 0) { - ctx.flush(promise); - return; - } - - boolean failed = false; - int processed = 0; - try { - if (!handler.beginFlush(ctx)) { - throw new IncompleteFlushException( - "beginFlush(..) rejected the flush request by returning false. " + - "none of " + inSize + " message(s) fulshed."); - } - for (;;) { - Object msg = in.poll(); - if (msg == null) { - break; - } - - if (!handler.acceptOutboundMessage(msg)) { - addToNextOutboundBuffer(ctx, msg); - processed ++; - continue; - } - - @SuppressWarnings("unchecked") - T imsg = (T) msg; - try { - handler.flush(ctx, imsg); - processed ++; - } finally { - BufUtil.release(imsg); - } - } - } catch (Throwable t) { - failed = true; - IncompleteFlushException pfe; - if (t instanceof IncompleteFlushException) { - pfe = (IncompleteFlushException) t; - } else { - String msg = processed + " out of " + inSize + " message(s) flushed"; - if (t instanceof Signal) { - Signal abort = (Signal) t; - abort.expect(ABORT); - pfe = new IncompleteFlushException("aborted: " + msg); - } else { - pfe = new IncompleteFlushException(msg, t); - } - } - fail(ctx, promise, closeOnFailedFlush, pfe); - } - - try { - handler.endFlush(ctx); - - } catch (Throwable t) { - failed = true; - fail(ctx, promise, closeOnFailedFlush, t); - } - - if (!failed) { - ctx.flush(promise); - } - } - - private static void fail( - ChannelHandlerContext ctx, ChannelPromise promise, boolean closeOnFailedFlush, Throwable cause) { - if (promise.tryFailure(cause)) { - if (closeOnFailedFlush) { - ctx.close(); - } - } else { - logger.warn("endFlush() raised a masked exception due to failed flush().", cause); - } - } - - /** - * Allocate a {@link ByteBuf} taking the {@link ChannelConfig#getDefaultHandlerByteBufType()} - * setting into account. - */ - public static ByteBuf allocate(ChannelHandlerContext ctx) { - switch(ctx.channel().config().getDefaultHandlerByteBufType()) { - case DIRECT: - return ctx.alloc().directBuffer(); - case PREFER_DIRECT: - return ctx.alloc().ioBuffer(); - case HEAP: - return ctx.alloc().heapBuffer(); - default: - throw new IllegalStateException(); - } - } - - /** - * Allocate a {@link ByteBuf} taking the {@link ChannelConfig#getDefaultHandlerByteBufType()} - * setting into account. - */ - public static ByteBuf allocate(ChannelHandlerContext ctx, int initialCapacity) { - switch(ctx.channel().config().getDefaultHandlerByteBufType()) { - case DIRECT: - return ctx.alloc().directBuffer(initialCapacity); - case PREFER_DIRECT: - return ctx.alloc().ioBuffer(initialCapacity); - case HEAP: - return ctx.alloc().heapBuffer(initialCapacity); - default: - throw new IllegalStateException(); - } - } - - /** - * Allocate a {@link ByteBuf} taking the {@link ChannelConfig#getDefaultHandlerByteBufType()} - * setting into account. - */ - public static ByteBuf allocate(ChannelHandlerContext ctx, int initialCapacity, int maxCapacity) { - switch(ctx.channel().config().getDefaultHandlerByteBufType()) { - case DIRECT: - return ctx.alloc().directBuffer(initialCapacity, maxCapacity); - case PREFER_DIRECT: - return ctx.alloc().ioBuffer(initialCapacity, maxCapacity); - case HEAP: - return ctx.alloc().heapBuffer(initialCapacity, maxCapacity); - default: - throw new IllegalStateException(); - } - } - - /** - * Add the msg to the next outbound buffer in the {@link ChannelPipeline}. This takes special care of - * msgs that are of type {@link ByteBuf}. - */ - public static boolean addToNextOutboundBuffer(ChannelHandlerContext ctx, Object msg) { - if (msg instanceof ByteBuf) { - if (ctx.nextOutboundBufferType() == BufType.BYTE) { - ctx.nextOutboundByteBuffer().writeBytes((ByteBuf) msg); - return true; - } - } - return ctx.nextOutboundMessageBuffer().add(msg); - } - - /** - * Add the msg to the next inbound buffer in the {@link ChannelPipeline}. This takes special care of - * msgs that are of type {@link ByteBuf}. - */ - public static boolean addToNextInboundBuffer(ChannelHandlerContext ctx, Object msg) { - if (msg instanceof ByteBuf) { - if (ctx.nextInboundBufferType() == BufType.BYTE) { - ctx.nextInboundByteBuffer().writeBytes((ByteBuf) msg); - return true; - } - } - return ctx.nextInboundMessageBuffer().add(msg); - } - - private ChannelHandlerUtil() { } - - public interface SingleInboundMessageHandler { - /** - * Returns {@code true} if and only if the specified message can be handled by this handler. - * - * @param msg the message - */ - boolean acceptInboundMessage(Object msg) throws Exception; - - /** - * Will get notified once {@link ChannelStateHandler#inboundBufferUpdated(ChannelHandlerContext)} was called. - * - * If this method returns {@code false} no further processing of the {@link MessageBuf} - * will be done until the next call of {@link ChannelStateHandler#inboundBufferUpdated(ChannelHandlerContext)}. - * - * This will return {@code true} by default, and may get overriden by sub-classes for - * special handling. - * - * @param ctx the {@link ChannelHandlerContext} which this {@link ChannelHandler} belongs to - */ - boolean beginMessageReceived(ChannelHandlerContext ctx) throws Exception; - - /** - * Is called once a message was received. - * - * @param ctx the {@link ChannelHandlerContext} which this {@link ChannelHandler} belongs to - * @param msg the message to handle - */ - void messageReceived(ChannelHandlerContext ctx, T msg) throws Exception; - - /** - * Is called when {@link #messageReceived(ChannelHandlerContext, Object)} returns. - * - * Super-classes may-override this for special handling. - * - * @param ctx the {@link ChannelHandlerContext} which this {@link ChannelHandler} belongs to - */ - void endMessageReceived(ChannelHandlerContext ctx) throws Exception; - } - - public interface SingleOutboundMessageHandler { - /** - * Returns {@code true} if and only if the specified message can be handled by this handler. - * - * @param msg the message - */ - boolean acceptOutboundMessage(Object msg) throws Exception; - - /** - * Will get notified once {@link ChannelOperationHandler#flush(ChannelHandlerContext, ChannelPromise)} - * was called. - * - * @param ctx the {@link ChannelHandlerContext} which this {@link ChannelHandler} belongs to - * - * @return {@code true} to accept the flush request. {@code false} to reject the flush request and - * to fail the promise associated with the flush request with {@link IncompleteFlushException}. - */ - boolean beginFlush(ChannelHandlerContext ctx) throws Exception; - - /** - * Is called once a message is being flushed. - * - * @param ctx the {@link ChannelHandlerContext} which this {@link ChannelHandler} belongs to - * @param msg the message to handle - */ - void flush(ChannelHandlerContext ctx, T msg) throws Exception; - - /** - * Is called when {@link ChannelOperationHandler#flush(ChannelHandlerContext, ChannelPromise)} returns. - * - * Super-classes may-override this for special handling. - * - * @param ctx the {@link ChannelHandlerContext} which this {@link ChannelHandler} belongs to - */ - void endFlush(ChannelHandlerContext ctx) throws Exception; - } -} diff --git a/transport/src/main/java/io/netty/channel/ChannelInboundByteHandler.java b/transport/src/main/java/io/netty/channel/ChannelInboundByteHandler.java deleted file mode 100644 index f924cd2fda..0000000000 --- a/transport/src/main/java/io/netty/channel/ChannelInboundByteHandler.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.channel; - -import io.netty.buffer.ByteBuf; - -/** - * {@link ChannelInboundHandler} which offers a {@link ByteBuf} to store inbound data in. - * - */ -public interface ChannelInboundByteHandler extends ChannelInboundHandler { - /** - * {@inheritDoc} - *

- * An implementation should respect the {@link ChannelConfig#getDefaultHandlerByteBufType()} setting unless - * there's a good reason to ignore it. If in doubt, use {@link ChannelHandlerUtil#allocate(ChannelHandlerContext)}. - *

- */ - @Override - ByteBuf newInboundBuffer(ChannelHandlerContext ctx) throws Exception; - - /** - * Discards the read bytes of the inbound buffer and optionally trims its unused portion to reduce memory - * consumption. The most common implementation of this method will look like the following: - *
-     *     ctx.inboundByteBuffer().discardSomeReadBytes();
-     * 
- */ - void discardInboundReadBytes(ChannelHandlerContext ctx) throws Exception; -} diff --git a/transport/src/main/java/io/netty/channel/ChannelInboundByteHandlerAdapter.java b/transport/src/main/java/io/netty/channel/ChannelInboundByteHandlerAdapter.java deleted file mode 100644 index 4a50c53965..0000000000 --- a/transport/src/main/java/io/netty/channel/ChannelInboundByteHandlerAdapter.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.channel; - -import io.netty.buffer.ByteBuf; - - -/** - * Abstract base class for {@link ChannelInboundByteHandler} which should be extended by the user to - * get notified once more data is ready to get consumed from the inbound {@link ByteBuf}. - * - * This implementation is a good starting point for most users. - */ -public abstract class ChannelInboundByteHandlerAdapter - extends ChannelStateHandlerAdapter implements ChannelInboundByteHandler { - - /** - * Create a new unpooled {@link ByteBuf} by default. Sub-classes may override this to offer a more - * optimized implementation. - */ - @Override - public ByteBuf newInboundBuffer(ChannelHandlerContext ctx) throws Exception { - return ChannelHandlerUtil.allocate(ctx); - } - - @Override - public void discardInboundReadBytes(ChannelHandlerContext ctx) throws Exception { - ctx.inboundByteBuffer().discardSomeReadBytes(); - } - - @Override - public final void inboundBufferUpdated(ChannelHandlerContext ctx) throws Exception { - inboundBufferUpdated(ctx, ctx.inboundByteBuffer()); - } - - /** - * Callback which will get notifed once the given {@link ByteBuf} received more data to read. What will be done - * with the data at this point is up to the implementation. - * Implementations may choose to read it or just let it in the buffer to read it later. - */ - protected abstract void inboundBufferUpdated(ChannelHandlerContext ctx, ByteBuf in) throws Exception; -} diff --git a/transport/src/main/java/io/netty/channel/ChannelInboundHandler.java b/transport/src/main/java/io/netty/channel/ChannelInboundHandler.java old mode 100644 new mode 100755 index 82e2f687be..a7e1817010 --- a/transport/src/main/java/io/netty/channel/ChannelInboundHandler.java +++ b/transport/src/main/java/io/netty/channel/ChannelInboundHandler.java @@ -15,19 +15,55 @@ */ package io.netty.channel; -import io.netty.buffer.Buf; - /** - * {@link ChannelStateHandler} which handles inbound data. + * {@link ChannelHandler} which adds callbacks for state changes. This allows the user + * to hook in to state changes easily. */ -interface ChannelInboundHandler extends ChannelStateHandler { +public interface ChannelInboundHandler extends ChannelHandler { + /** - * Returns a new buffer which will be used to consume inbound data for the given {@link ChannelHandlerContext}. - *

- * Please note that this method can be called from any thread repeatatively, and thus you should neither perform - * stateful operation nor keep the reference of the created buffer as a member variable. Get it always using - * {@link ChannelHandlerContext#inboundByteBuffer()} or {@link ChannelHandlerContext#inboundMessageBuffer()}. - *

+ * The {@link Channel} of the {@link ChannelHandlerContext} was registered with its {@link EventLoop} */ - Buf newInboundBuffer(ChannelHandlerContext ctx) throws Exception; + void channelRegistered(ChannelHandlerContext ctx) throws Exception; + + /** + * The {@link Channel} of the {@link ChannelHandlerContext} was unregistered from its {@link EventLoop} + */ + void channelUnregistered(ChannelHandlerContext ctx) throws Exception; + + /** + * The {@link Channel} of the {@link ChannelHandlerContext} is now active + */ + void channelActive(ChannelHandlerContext ctx) throws Exception; + + /** + * The {@link Channel} of the {@link ChannelHandlerContext} was registered is now inactive and reached its + * end of lifetime. + */ + void channelInactive(ChannelHandlerContext ctx) throws Exception; + + /** + * Invoked when a {@link ChannelHandlerContext#read()} is finished and the inbound buffer of this handler will not + * be updated until another {@link ChannelHandlerContext#read()} request is issued. + */ + void channelReadSuspended(ChannelHandlerContext ctx) throws Exception; + + /** + * The inbound buffer of the {@link ChannelHandlerContext} was updated with new data. + * This means something may be ready to get processed by the actual {@link ChannelInboundHandler} + * implementation. It's up to the implementation to consume it or keep it in the buffer + * to wait for more data and consume it later. + */ + void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception; + + /** + * Gets called if an user event was triggered. + */ + void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception; + + /** + * Gets called once the writable state of a {@link Channel} changed. You can check the state with + * {@link Channel#isWritable()}. + */ + void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception; } diff --git a/transport/src/main/java/io/netty/channel/ChannelStateHandlerAdapter.java b/transport/src/main/java/io/netty/channel/ChannelInboundHandlerAdapter.java similarity index 63% rename from transport/src/main/java/io/netty/channel/ChannelStateHandlerAdapter.java rename to transport/src/main/java/io/netty/channel/ChannelInboundHandlerAdapter.java index 8a67804497..edd0856f6e 100644 --- a/transport/src/main/java/io/netty/channel/ChannelStateHandlerAdapter.java +++ b/transport/src/main/java/io/netty/channel/ChannelInboundHandlerAdapter.java @@ -17,17 +17,17 @@ package io.netty.channel; /** - * Abstract base class for {@link ChannelStateHandler} implementations which provide + * Abstract base class for {@link ChannelInboundHandler} implementations which provide * implementations of all of their methods. * * This implementation just forward the operation to the next {@link ChannelHandler} in the * {@link ChannelPipeline}. Sub-classes may override a method implementation to change this. */ -public abstract class ChannelStateHandlerAdapter extends ChannelHandlerAdapter implements ChannelStateHandler { +public class ChannelInboundHandlerAdapter extends ChannelHandlerAdapter implements ChannelInboundHandler { /** * Calls {@link ChannelHandlerContext#fireChannelRegistered()} to forward - * to the next {@link ChannelStateHandler} in the {@link ChannelPipeline}. + * to the next {@link ChannelInboundHandler} in the {@link ChannelPipeline}. * * Sub-classes may override this method to change behavior. */ @@ -38,7 +38,7 @@ public abstract class ChannelStateHandlerAdapter extends ChannelHandlerAdapter i /** * Calls {@link ChannelHandlerContext#fireChannelUnregistered()} to forward - * to the next {@link ChannelStateHandler} in the {@link ChannelPipeline}. + * to the next {@link ChannelInboundHandler} in the {@link ChannelPipeline}. * * Sub-classes may override this method to change behavior. */ @@ -49,7 +49,7 @@ public abstract class ChannelStateHandlerAdapter extends ChannelHandlerAdapter i /** * Calls {@link ChannelHandlerContext#fireChannelActive()} to forward - * to the next {@link ChannelStateHandler} in the {@link ChannelPipeline}. + * to the next {@link ChannelInboundHandler} in the {@link ChannelPipeline}. * * Sub-classes may override this method to change behavior. */ @@ -60,7 +60,7 @@ public abstract class ChannelStateHandlerAdapter extends ChannelHandlerAdapter i /** * Calls {@link ChannelHandlerContext#fireChannelInactive()} to forward - * to the next {@link ChannelStateHandler} in the {@link ChannelPipeline}. + * to the next {@link ChannelInboundHandler} in the {@link ChannelPipeline}. * * Sub-classes may override this method to change behavior. */ @@ -70,7 +70,7 @@ public abstract class ChannelStateHandlerAdapter extends ChannelHandlerAdapter i } /** * Calls {@link ChannelHandlerContext#fireChannelReadSuspended()} to forward - * to the next {@link ChannelHandler} in the {@link ChannelPipeline}. + * to the next {@link ChannelInboundHandler} in the {@link ChannelPipeline}. * * Sub-classes may override this method to change behavior. */ @@ -80,14 +80,35 @@ public abstract class ChannelStateHandlerAdapter extends ChannelHandlerAdapter i } /** - * Calls {@link ChannelHandlerContext#fireUserEventTriggered(Object)} to forward - * to the next {@link ChannelHandler} in the {@link ChannelPipeline}. + * Calls {@link ChannelHandlerContext#fireMessageReceived(MessageList)} to forward + * to the next {@link ChannelInboundHandler} in the {@link ChannelPipeline}. * * Sub-classes may override this method to change behavior. */ @Override - public void userEventTriggered(ChannelHandlerContext ctx, Object evt) - throws Exception { + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + ctx.fireMessageReceived(msgs); + } + + /** + * Calls {@link ChannelHandlerContext#fireUserEventTriggered(Object)} to forward + * to the next {@link ChannelInboundHandler} in the {@link ChannelPipeline}. + * + * Sub-classes may override this method to change behavior. + */ + @Override + public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { ctx.fireUserEventTriggered(evt); } + + /** + * Calls {@link ChannelHandlerContext#fireChannelWritabilityChanged()} to forward + * to the next {@link ChannelInboundHandler} in the {@link ChannelPipeline}. + * + * Sub-classes may override this method to change behavior. + */ + @Override + public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception { + ctx.fireChannelWritabilityChanged(); + } } diff --git a/transport/src/main/java/io/netty/channel/ChannelInboundInvoker.java b/transport/src/main/java/io/netty/channel/ChannelInboundInvoker.java index 2fb8118d93..ac12c17505 100644 --- a/transport/src/main/java/io/netty/channel/ChannelInboundInvoker.java +++ b/transport/src/main/java/io/netty/channel/ChannelInboundInvoker.java @@ -24,8 +24,8 @@ interface ChannelInboundInvoker { /** * A {@link Channel} was registered to its {@link EventLoop}. * - * This will result in having the {@link ChannelStateHandler#channelRegistered(ChannelHandlerContext)} method - * called of the next {@link ChannelStateHandler} contained in the {@link ChannelPipeline} of the + * This will result in having the {@link ChannelInboundHandler#channelRegistered(ChannelHandlerContext)} method + * called of the next {@link ChannelInboundHandler} contained in the {@link ChannelPipeline} of the * {@link Channel}. */ ChannelInboundInvoker fireChannelRegistered(); @@ -33,8 +33,8 @@ interface ChannelInboundInvoker { /** * A {@link Channel} was unregistered from its {@link EventLoop}. * - * This will result in having the {@link ChannelStateHandler#channelUnregistered(ChannelHandlerContext)} method - * called of the next {@link ChannelStateHandler} contained in the {@link ChannelPipeline} of the + * This will result in having the {@link ChannelInboundHandler#channelUnregistered(ChannelHandlerContext)} method + * called of the next {@link ChannelInboundHandler} contained in the {@link ChannelPipeline} of the * {@link Channel}. */ ChannelInboundInvoker fireChannelUnregistered(); @@ -42,8 +42,8 @@ interface ChannelInboundInvoker { /** * A {@link Channel} is active now, which means it is connected. * - * This will result in having the {@link ChannelStateHandler#channelActive(ChannelHandlerContext)} method - * called of the next {@link ChannelStateHandler} contained in the {@link ChannelPipeline} of the + * This will result in having the {@link ChannelInboundHandler#channelActive(ChannelHandlerContext)} method + * called of the next {@link ChannelInboundHandler} contained in the {@link ChannelPipeline} of the * {@link Channel}. */ ChannelInboundInvoker fireChannelActive(); @@ -51,8 +51,8 @@ interface ChannelInboundInvoker { /** * A {@link Channel} is inactive now, which means it is closed. * - * This will result in having the {@link ChannelStateHandler#channelInactive(ChannelHandlerContext)} method - * called of the next {@link ChannelStateHandler} contained in the {@link ChannelPipeline} of the + * This will result in having the {@link ChannelInboundHandler#channelInactive(ChannelHandlerContext)} method + * called of the next {@link ChannelInboundHandler} contained in the {@link ChannelPipeline} of the * {@link Channel}. */ ChannelInboundInvoker fireChannelInactive(); @@ -60,8 +60,8 @@ interface ChannelInboundInvoker { /** * A {@link Channel} received an {@link Throwable} in one of its inbound operations. * - * This will result in having the {@link ChannelStateHandler#exceptionCaught(ChannelHandlerContext, Throwable)} - * method called of the next {@link ChannelStateHandler} contained in the {@link ChannelPipeline} of the + * This will result in having the {@link ChannelInboundHandler#exceptionCaught(ChannelHandlerContext, Throwable)} + * method called of the next {@link ChannelInboundHandler} contained in the {@link ChannelPipeline} of the * {@link Channel}. */ ChannelInboundInvoker fireExceptionCaught(Throwable cause); @@ -69,8 +69,8 @@ interface ChannelInboundInvoker { /** * A {@link Channel} received an user defined event. * - * This will result in having the {@link ChannelStateHandler#userEventTriggered(ChannelHandlerContext, Object)} - * method called of the next {@link ChannelStateHandler} contained in the {@link ChannelPipeline} of the + * This will result in having the {@link ChannelInboundHandler#userEventTriggered(ChannelHandlerContext, Object)} + * method called of the next {@link ChannelInboundHandler} contained in the {@link ChannelPipeline} of the * {@link Channel}. */ ChannelInboundInvoker fireUserEventTriggered(Object event); @@ -78,15 +78,22 @@ interface ChannelInboundInvoker { /** * A {@link Channel} received bytes which are now ready to read from its inbound buffer. * - * This will result in having the {@link ChannelStateHandler#inboundBufferUpdated(ChannelHandlerContext)} - * method called of the next {@link ChannelStateHandler} contained in the {@link ChannelPipeline} of the + * This will result in having the {@link ChannelInboundHandler#messageReceived(ChannelHandlerContext, MessageList)} + * method called of the next {@link ChannelInboundHandler} contained in the {@link ChannelPipeline} of the * {@link Channel}. */ - ChannelInboundInvoker fireInboundBufferUpdated(); + ChannelInboundInvoker fireMessageReceived(Object msg); + ChannelInboundInvoker fireMessageReceived(MessageList msgs); /** - * Triggers an {@link ChannelStateHandler#channelReadSuspended(ChannelHandlerContext) channelReadSuspended} - * event to the next {@link ChannelStateHandler} in the {@link ChannelPipeline}. + * Triggers an {@link ChannelInboundHandler#channelReadSuspended(ChannelHandlerContext) channelReadSuspended} + * event to the next {@link ChannelInboundHandler} in the {@link ChannelPipeline}. */ ChannelInboundInvoker fireChannelReadSuspended(); + + /** + * Triggers an {@link ChannelInboundHandler#channelWritabilityChanged(ChannelHandlerContext)} + * event to the next {@link ChannelInboundHandler} in the {@link ChannelPipeline}. + */ + ChannelInboundInvoker fireChannelWritabilityChanged(); } diff --git a/transport/src/main/java/io/netty/channel/ChannelInboundMessageHandler.java b/transport/src/main/java/io/netty/channel/ChannelInboundMessageHandler.java deleted file mode 100644 index d3e72ca069..0000000000 --- a/transport/src/main/java/io/netty/channel/ChannelInboundMessageHandler.java +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.channel; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.MessageBuf; - -/** - * Special {@link ChannelInboundHandler} which store the inbound data in a {@link MessageBuf} for futher processing. - * - * If your {@link ChannelOutboundMessageHandler} handles messages of type {@link ByteBuf} or {@link Object} - * and you want to add a {@link ByteBuf} to the next buffer in the {@link ChannelPipeline} use - * {@link ChannelHandlerUtil#addToNextInboundBuffer(ChannelHandlerContext, Object)}. - */ -public interface ChannelInboundMessageHandler extends ChannelInboundHandler { - @Override - MessageBuf newInboundBuffer(ChannelHandlerContext ctx) throws Exception; -} diff --git a/transport/src/main/java/io/netty/channel/ChannelInboundMessageHandlerAdapter.java b/transport/src/main/java/io/netty/channel/ChannelInboundMessageHandlerAdapter.java deleted file mode 100644 index 79a5927be6..0000000000 --- a/transport/src/main/java/io/netty/channel/ChannelInboundMessageHandlerAdapter.java +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.channel; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.MessageBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandlerUtil.SingleInboundMessageHandler; -import io.netty.util.Signal; -import io.netty.util.internal.TypeParameterMatcher; - -/** - * {@link ChannelHandler} which handles inbound messages of a specific type. - * - *
- *     public class StringHandler extends
- *             {@link ChannelInboundMessageHandlerAdapter}<{@link String}> {
- *
- *         {@code @Override}
- *         public void messageReceived({@link ChannelHandlerContext} ctx, {@link String} message)
- *                 throws {@link Exception} {
- *             // Do something with the String
- *             ...
- *             ...
- *         }
- *     }
- * 
- * - * If your {@link ChannelInboundMessageHandlerAdapter} handles messages of type {@link ByteBuf} or {@link Object} - * and you want to add a {@link ByteBuf} to the next buffer in the {@link ChannelPipeline} use - * {@link ChannelHandlerUtil#addToNextInboundBuffer(ChannelHandlerContext, Object)}. - * - *

- * One limitation to keep in mind is that it is not possible to detect the handled message type of you specify - * {@code I} while instance your class. Because of this Netty does not allow to do so and will throw an Exception - * if you try. For this cases you should handle the type detection by your self by override the - * {@link #acceptInboundMessage(Object)} method and use {@link Object} as type parameter. - * - *

- *    public class GenericHandler<I> extends
- *             {@link ChannelInboundMessageHandlerAdapter}<{@link Object}> {
- *
- *         {@code @Override}
- *         public void messageReceived({@link ChannelHandlerContext} ctx, {@link Object} message)
- *                 throws {@link Exception} {
- *             I msg = (I) message;
- *             // Do something with the msg
- *             ...
- *             ...
- *         }
- *
- *         {@code @Override}
- *         public boolean acceptInboundMessage(Object msg) throws Exception {
- *             // Add your check here
- *         }
- *     }
- * 
- * - * @param The type of the messages to handle - */ -public abstract class ChannelInboundMessageHandlerAdapter - extends ChannelStateHandlerAdapter - implements ChannelInboundMessageHandler, SingleInboundMessageHandler { - - /** - * Thrown by {@link #messageReceived(ChannelHandlerContext, Object)} to abort message processing. - */ - protected static final Signal ABORT = ChannelHandlerUtil.ABORT; - - private final TypeParameterMatcher msgMatcher; - - protected ChannelInboundMessageHandlerAdapter() { - msgMatcher = TypeParameterMatcher.find(this, ChannelInboundMessageHandlerAdapter.class, "I"); - } - - protected ChannelInboundMessageHandlerAdapter(Class inboundMessageType) { - msgMatcher = TypeParameterMatcher.get(inboundMessageType); - } - - @Override - public MessageBuf newInboundBuffer(ChannelHandlerContext ctx) throws Exception { - return Unpooled.messageBuffer(); - } - - @Override - public final void inboundBufferUpdated(ChannelHandlerContext ctx) throws Exception { - ChannelHandlerUtil.handleInboundBufferUpdated(ctx, this); - } - - @Override - public boolean acceptInboundMessage(Object msg) throws Exception { - return msgMatcher.match(msg); - } - - @Override - public boolean beginMessageReceived(ChannelHandlerContext ctx) throws Exception { - return true; - } - - @Override - public void endMessageReceived(ChannelHandlerContext ctx) throws Exception { - // NOOP - } -} diff --git a/transport/src/main/java/io/netty/channel/ChannelInitializer.java b/transport/src/main/java/io/netty/channel/ChannelInitializer.java index a183b01cc0..d03cc2e585 100644 --- a/transport/src/main/java/io/netty/channel/ChannelInitializer.java +++ b/transport/src/main/java/io/netty/channel/ChannelInitializer.java @@ -22,7 +22,7 @@ import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; /** - * A special {@link ChannelStateHandler} which offers an easy way to initialize a {@link Channel} once it was + * A special {@link ChannelInboundHandler} which offers an easy way to initialize a {@link Channel} once it was * registered to its {@link EventLoop}. * * Implementations are most often used in the context of {@link Bootstrap#handler(ChannelHandler)} , @@ -47,7 +47,7 @@ import io.netty.util.internal.logging.InternalLoggerFactory; * @param A sub-type of {@link Channel} */ @Sharable -public abstract class ChannelInitializer extends ChannelStateHandlerAdapter { +public abstract class ChannelInitializer extends ChannelInboundHandlerAdapter { private static final InternalLogger logger = InternalLoggerFactory.getInstance(ChannelInitializer.class); @@ -83,9 +83,4 @@ public abstract class ChannelInitializer extends ChannelState } } } - - @Override - public void inboundBufferUpdated(ChannelHandlerContext ctx) throws Exception { - ctx.fireInboundBufferUpdated(); - } } diff --git a/transport/src/main/java/io/netty/channel/ChannelMetadata.java b/transport/src/main/java/io/netty/channel/ChannelMetadata.java index c5d2a2cd51..628ad43fdd 100644 --- a/transport/src/main/java/io/netty/channel/ChannelMetadata.java +++ b/transport/src/main/java/io/netty/channel/ChannelMetadata.java @@ -15,8 +15,6 @@ */ package io.netty.channel; -import io.netty.buffer.BufType; - import java.net.SocketAddress; /** @@ -24,33 +22,19 @@ import java.net.SocketAddress; */ public final class ChannelMetadata { - private final BufType bufferType; private final boolean hasDisconnect; /** * Create a new instance * - * @param bufferType the {@link BufType} which will be used by the {@link Channel}. * @param hasDisconnect {@code true} if and only if the channel has the {@code disconnect()} operation * that allows a user to disconnect and then call {@link Channel#connect(SocketAddress)} * again, such as UDP/IP. */ - public ChannelMetadata(BufType bufferType, boolean hasDisconnect) { - if (bufferType == null) { - throw new NullPointerException("bufferType"); - } - - this.bufferType = bufferType; + public ChannelMetadata(boolean hasDisconnect) { this.hasDisconnect = hasDisconnect; } - /** - * Returns the {@link BufType} which will be used by the {@link Channel}. - */ - public BufType bufferType() { - return bufferType; - } - /** * Returns {@code true} if and only if the channel has the {@code disconnect()} operation * that allows a user to disconnect and then call {@link Channel#connect(SocketAddress)} again, diff --git a/transport/src/main/java/io/netty/channel/ChannelOperationHandler.java b/transport/src/main/java/io/netty/channel/ChannelOperationHandler.java deleted file mode 100644 index 3ff70f5ce3..0000000000 --- a/transport/src/main/java/io/netty/channel/ChannelOperationHandler.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.channel; - -import java.net.SocketAddress; - -/** - * {@link ChannelHandler} which will get notified for IO-outbound-operations. - */ -public interface ChannelOperationHandler extends ChannelHandler { - /** - * Called once a bind operation is made. - * - * @param ctx the {@link ChannelHandlerContext} for which the bind operation is made - * @param localAddress the {@link SocketAddress} to which it should bound - * @param promise the {@link ChannelPromise} to notify once the operation completes - * @throws Exception thrown if an error accour - */ - void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception; - - /** - * Called once a connect operation is made. - * - * @param ctx the {@link ChannelHandlerContext} for which the connect operation is made - * @param remoteAddress the {@link SocketAddress} to which it should connect - * @param localAddress the {@link SocketAddress} which is used as source on connect - * @param promise the {@link ChannelPromise} to notify once the operation completes - * @throws Exception thrown if an error accour - */ - void connect( - ChannelHandlerContext ctx, SocketAddress remoteAddress, - SocketAddress localAddress, ChannelPromise promise) throws Exception; - - /** - * Called once a disconnect operation is made. - * - * @param ctx the {@link ChannelHandlerContext} for which the disconnect operation is made - * @param promise the {@link ChannelPromise} to notify once the operation completes - * @throws Exception thrown if an error accour - */ - void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception; - - /** - * Called once a close operation is made. - * - * @param ctx the {@link ChannelHandlerContext} for which the close operation is made - * @param promise the {@link ChannelPromise} to notify once the operation completes - * @throws Exception thrown if an error accour - */ - void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception; - - /** - * Called once a deregister operation is made from the current registered {@link EventLoop}. - * - * @param ctx the {@link ChannelHandlerContext} for which the close operation is made - * @param promise the {@link ChannelPromise} to notify once the operation completes - * @throws Exception thrown if an error accour - */ - void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception; - - /** - * Intercepts {@link ChannelHandlerContext#read()}. - */ - void read(ChannelHandlerContext ctx); - - /** - * Called once a flush operation is made and so the outbound data should be written. - * - * @param ctx the {@link ChannelHandlerContext} for which the flush operation is made - * @param promise the {@link ChannelPromise} to notify once the operation completes - * @throws Exception thrown if an error accour - */ - void flush(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception; - - /** - * Called once a sendFile operation is made and so the {@link FileRegion} should be transfered. - * - * @param ctx the {@link ChannelHandlerContext} for which the flush operation is made - * @param region the {@link FileRegion} to transfer - * @param promise the {@link ChannelPromise} to notify once the operation completes - * @throws Exception thrown if an error accour - */ - void sendFile(ChannelHandlerContext ctx, FileRegion region, ChannelPromise promise) throws Exception; - -} diff --git a/transport/src/main/java/io/netty/channel/ChannelOption.java b/transport/src/main/java/io/netty/channel/ChannelOption.java index 6ad40e1dd6..f7231ee844 100644 --- a/transport/src/main/java/io/netty/channel/ChannelOption.java +++ b/transport/src/main/java/io/netty/channel/ChannelOption.java @@ -35,17 +35,25 @@ public class ChannelOption extends UniqueName { private static final ConcurrentMap names = PlatformDependent.newConcurrentHashMap(); - public static final ChannelOption ALLOCATOR = new ChannelOption("ALLOCATOR"); + public static final ChannelOption ALLOCATOR = + new ChannelOption("ALLOCATOR"); + public static final ChannelOption RCVBUF_ALLOCATOR = + new ChannelOption("RCVBUF_ALLOCATOR"); + public static final ChannelOption CONNECT_TIMEOUT_MILLIS = new ChannelOption("CONNECT_TIMEOUT_MILLIS"); public static final ChannelOption WRITE_SPIN_COUNT = new ChannelOption("WRITE_SPIN_COUNT"); + public static final ChannelOption WRITE_BUFFER_HIGH_WATER_MARK = + new ChannelOption("WRITE_BUFFER_HIGH_WATER_MARK"); + public static final ChannelOption WRITE_BUFFER_LOW_WATER_MARK = + new ChannelOption("WRITE_BUFFER_LOW_WATER_MARK"); + public static final ChannelOption ALLOW_HALF_CLOSURE = new ChannelOption("ALLOW_HALF_CLOSURE"); public static final ChannelOption AUTO_READ = new ChannelOption("AUTO_READ"); - public static final ChannelOption DEFAULT_HANDLER_BYTEBUF_TYPE = - new ChannelOption("DEFAULT_HANDLER_BYTEBUF_TYPE"); + public static final ChannelOption SO_BROADCAST = new ChannelOption("SO_BROADCAST"); public static final ChannelOption SO_KEEPALIVE = @@ -74,9 +82,6 @@ public class ChannelOption extends UniqueName { public static final ChannelOption IP_MULTICAST_LOOP_DISABLED = new ChannelOption("IP_MULTICAST_LOOP_DISABLED"); - public static final ChannelOption UDP_RECEIVE_PACKET_SIZE = - new ChannelOption("UDP_RECEIVE_PACKET_SIZE"); - public static final ChannelOption TCP_NODELAY = new ChannelOption("TCP_NODELAY"); diff --git a/transport/src/main/java/io/netty/channel/ChannelOutboundBuffer.java b/transport/src/main/java/io/netty/channel/ChannelOutboundBuffer.java new file mode 100644 index 0000000000..a6457a1717 --- /dev/null +++ b/transport/src/main/java/io/netty/channel/ChannelOutboundBuffer.java @@ -0,0 +1,238 @@ +/* + * Copyright 2013 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +/* + * Written by Josh Bloch of Google Inc. and released to the public domain, + * as explained at http://creativecommons.org/publicdomain/zero/1.0/. + */ +package io.netty.channel; + +import io.netty.buffer.ByteBufUtil; +import io.netty.util.internal.logging.InternalLogger; +import io.netty.util.internal.logging.InternalLoggerFactory; + +import java.util.concurrent.atomic.AtomicIntegerFieldUpdater; + +final class ChannelOutboundBuffer { + + private static final InternalLogger logger = InternalLoggerFactory.getInstance(ChannelOutboundBuffer.class); + + private static final int MIN_INITIAL_CAPACITY = 8; + + ChannelPromise currentPromise; + MessageList currentMessages; + int currentMessageIndex; + private int currentMessageListSize; + + private ChannelPromise[] promises; + private MessageList[] messages; + + private int head; + private int tail; + private final AbstractChannel channel; + + private int pendingOutboundBytes; + + private static final AtomicIntegerFieldUpdater WRITABLE_UPDATER = + AtomicIntegerFieldUpdater.newUpdater(ChannelOutboundBuffer.class, "writable"); + + @SuppressWarnings("unused") + private volatile int writable = 1; + + ChannelOutboundBuffer(AbstractChannel channel) { + this(channel, MIN_INITIAL_CAPACITY << 1); + } + + @SuppressWarnings("unchecked") + ChannelOutboundBuffer(AbstractChannel channel, int initialCapacity) { + if (initialCapacity < 0) { + throw new IllegalArgumentException("initialCapacity: " + initialCapacity + " (expected: >= 0)"); + } + // Find the best power of two to hold elements. + // Tests "<=" because arrays aren't kept full. + if (initialCapacity >= MIN_INITIAL_CAPACITY) { + initialCapacity |= initialCapacity >>> 1; + initialCapacity |= initialCapacity >>> 2; + initialCapacity |= initialCapacity >>> 4; + initialCapacity |= initialCapacity >>> 8; + initialCapacity |= initialCapacity >>> 16; + initialCapacity ++; + + if (initialCapacity < 0) { // Too many elements, must back off + initialCapacity >>>= 1; // Good luck allocating 2 ^ 30 elements + } + } else { + initialCapacity = MIN_INITIAL_CAPACITY; + } + + promises = new ChannelPromise[initialCapacity]; + messages = new MessageList[initialCapacity]; + this.channel = channel; + } + + @SuppressWarnings("unchecked") + void add(MessageList msgs, ChannelPromise promise) { + int tail = this.tail; + promises[tail] = promise; + messages[tail] = (MessageList) msgs; + + if ((this.tail = tail + 1 & promises.length - 1) == head) { + doubleCapacity(); + } + + incrementPendingOutboundBytes(messageListSize(msgs)); + } + + private void incrementPendingOutboundBytes(int size) { + if (size == 0) { + return; + } + + int newWriteBufferSize = pendingOutboundBytes += size; + int highWaterMark = channel.config().getWriteBufferHighWaterMark(); + + if (newWriteBufferSize > highWaterMark) { + if (WRITABLE_UPDATER.compareAndSet(this, 1, 0)) { + channel.pipeline().fireChannelWritabilityChanged(); + } + } + } + + private void decrementPendingOutboundBytes(int size) { + if (size == 0) { + return; + } + + int newWriteBufferSize = pendingOutboundBytes -= size; + int lowWaterMark = channel.config().getWriteBufferLowWaterMark(); + + if (newWriteBufferSize == 0 || newWriteBufferSize < lowWaterMark) { + + if (WRITABLE_UPDATER.compareAndSet(this, 0, 1)) { + channel.pipeline().fireChannelWritabilityChanged(); + } + } + } + + private void doubleCapacity() { + assert head == tail; + + int p = head; + int n = promises.length; + int r = n - p; // number of elements to the right of p + int newCapacity = n << 1; + if (newCapacity < 0) { + throw new IllegalStateException("Sorry, deque too big"); + } + + ChannelPromise[] a1 = new ChannelPromise[newCapacity]; + System.arraycopy(promises, p, a1, 0, r); + System.arraycopy(promises, 0, a1, r, p); + promises = a1; + + @SuppressWarnings("unchecked") + MessageList[] a2 = new MessageList[newCapacity]; + System.arraycopy(messages, p, a2, 0, r); + System.arraycopy(messages, 0, a2, r, p); + messages = a2; + + head = 0; + tail = n; + } + + boolean next() { + decrementPendingOutboundBytes(currentMessageListSize); + + int h = head; + + ChannelPromise e = promises[h]; // Element is null if deque empty + if (e == null) { + currentMessageListSize = 0; + currentPromise = null; + currentMessages = null; + return false; + } + + currentPromise = e; + currentMessages = messages[h]; + currentMessageIndex = 0; + currentMessageListSize = messageListSize(currentMessages); + + promises[h] = null; + messages[h] = null; + + head = h + 1 & promises.length - 1; + return true; + } + + private int messageListSize(MessageList messages) { + int size = 0; + for (int i = 0; i < messages.size(); i++) { + size += channel.calculateMessageSize(messages.get(i)); + } + return size; + } + + boolean getWritable() { + return WRITABLE_UPDATER.get(this) == 1; + } + + int size() { + return tail - head & promises.length - 1; + } + + boolean isEmpty() { + return head == tail; + } + + void clear() { + int head = this.head; + int tail = this.tail; + if (head != tail) { + this.head = this.tail = 0; + final int mask = promises.length - 1; + int i = head; + do { + promises[i] = null; + messages[i] = null; + i = i + 1 & mask; + } while (i != tail); + } + } + + void fail(Throwable cause) { + if (currentPromise == null) { + if (!next()) { + return; + } + } + + do { + if (!currentPromise.tryFailure(cause)) { + logger.warn("Promise done already:", cause); + } + + // Release all failed messages. + try { + for (int i = currentMessageIndex; i < currentMessages.size(); i++) { + Object msg = currentMessages.get(i); + ByteBufUtil.release(msg); + } + } finally { + currentMessages.recycle(); + } + } while(next()); + } +} diff --git a/transport/src/main/java/io/netty/channel/ChannelOutboundByteHandler.java b/transport/src/main/java/io/netty/channel/ChannelOutboundByteHandler.java deleted file mode 100644 index 82b042fe0d..0000000000 --- a/transport/src/main/java/io/netty/channel/ChannelOutboundByteHandler.java +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.channel; - -import io.netty.buffer.ByteBuf; - -/** - * {@link ChannelOutboundHandler} which operates on bytes which are hold in a {@link ByteBuf}. - */ -public interface ChannelOutboundByteHandler extends ChannelOutboundHandler { - /** - * {@inheritDoc} - *

- * An implementation should respect the {@link ChannelConfig#getDefaultHandlerByteBufType()} setting unless - * there's a good reason to ignore it. If in doubt, use {@link ChannelHandlerUtil#allocate(ChannelHandlerContext)}. - *

- */ - @Override - ByteBuf newOutboundBuffer(ChannelHandlerContext ctx) throws Exception; - - /** - * Discards the read bytes of the outbound buffer and optionally trims its unused portion to reduce memory - * consumption. The most common implementation of this method will look like the following: - *
-     *     ctx.outboundByteBuffer().discardSomeReadBytes();
-     * 
- */ - void discardOutboundReadBytes(ChannelHandlerContext ctx) throws Exception; -} diff --git a/transport/src/main/java/io/netty/channel/ChannelOutboundByteHandlerAdapter.java b/transport/src/main/java/io/netty/channel/ChannelOutboundByteHandlerAdapter.java deleted file mode 100644 index d5036d6e20..0000000000 --- a/transport/src/main/java/io/netty/channel/ChannelOutboundByteHandlerAdapter.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.channel; - -import io.netty.buffer.ByteBuf; - -/** - * Abstract base class which handles outgoing bytes. - */ -public abstract class ChannelOutboundByteHandlerAdapter - extends ChannelOperationHandlerAdapter implements ChannelOutboundByteHandler { - @Override - public ByteBuf newOutboundBuffer(ChannelHandlerContext ctx) throws Exception { - return ChannelHandlerUtil.allocate(ctx); - } - - @Override - public void discardOutboundReadBytes(ChannelHandlerContext ctx) throws Exception { - ctx.outboundByteBuffer().discardSomeReadBytes(); - } - - /** - * This method merely delegates the flush request to {@link #flush(ChannelHandlerContext, ByteBuf, ChannelPromise)}. - */ - @Override - public final void flush(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { - flush(ctx, ctx.outboundByteBuffer(), promise); - } - - /** - * Invoked when a flush request has been issued. - * - * @param ctx the current context - * @param in this handler's outbound buffer - * @param promise the promise associate with the current flush request - */ - protected abstract void flush(ChannelHandlerContext ctx, ByteBuf in, ChannelPromise promise) throws Exception; -} diff --git a/transport/src/main/java/io/netty/channel/ChannelOutboundHandler.java b/transport/src/main/java/io/netty/channel/ChannelOutboundHandler.java index 1bcf3325b7..c55f9d5f5d 100644 --- a/transport/src/main/java/io/netty/channel/ChannelOutboundHandler.java +++ b/transport/src/main/java/io/netty/channel/ChannelOutboundHandler.java @@ -15,19 +15,74 @@ */ package io.netty.channel; -import io.netty.buffer.Buf; +import java.net.SocketAddress; /** - * {@link ChannelOperationHandler} which handles outbound data. + * {@link ChannelHandler} which will get notified for IO-outbound-operations. */ -interface ChannelOutboundHandler extends ChannelOperationHandler { +public interface ChannelOutboundHandler extends ChannelHandler { /** - * Returns a new buffer which will be used to transfer outbound data for the given {@link ChannelHandlerContext}. - *

- * Please note that this method can be called from any thread repeatatively, and thus you should neither perform - * stateful operation nor keep the reference of the created buffer as a member variable. Get it always using - * {@link ChannelHandlerContext#outboundByteBuffer()} or {@link ChannelHandlerContext#outboundMessageBuffer()}. - *

+ * Called once a bind operation is made. + * + * @param ctx the {@link ChannelHandlerContext} for which the bind operation is made + * @param localAddress the {@link SocketAddress} to which it should bound + * @param promise the {@link ChannelPromise} to notify once the operation completes + * @throws Exception thrown if an error accour */ - Buf newOutboundBuffer(ChannelHandlerContext ctx) throws Exception; + void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception; + + /** + * Called once a connect operation is made. + * + * @param ctx the {@link ChannelHandlerContext} for which the connect operation is made + * @param remoteAddress the {@link SocketAddress} to which it should connect + * @param localAddress the {@link SocketAddress} which is used as source on connect + * @param promise the {@link ChannelPromise} to notify once the operation completes + * @throws Exception thrown if an error accour + */ + void connect( + ChannelHandlerContext ctx, SocketAddress remoteAddress, + SocketAddress localAddress, ChannelPromise promise) throws Exception; + + /** + * Called once a disconnect operation is made. + * + * @param ctx the {@link ChannelHandlerContext} for which the disconnect operation is made + * @param promise the {@link ChannelPromise} to notify once the operation completes + * @throws Exception thrown if an error accour + */ + void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception; + + /** + * Called once a close operation is made. + * + * @param ctx the {@link ChannelHandlerContext} for which the close operation is made + * @param promise the {@link ChannelPromise} to notify once the operation completes + * @throws Exception thrown if an error accour + */ + void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception; + + /** + * Called once a deregister operation is made from the current registered {@link EventLoop}. + * + * @param ctx the {@link ChannelHandlerContext} for which the close operation is made + * @param promise the {@link ChannelPromise} to notify once the operation completes + * @throws Exception thrown if an error accour + */ + void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception; + + /** + * Intercepts {@link ChannelHandlerContext#read()}. + */ + void read(ChannelHandlerContext ctx) throws Exception; + + /** + * Called once a flush operation is made and so the outbound data should be written. + * + * + * @param ctx the {@link ChannelHandlerContext} for which the flush operation is made + * @param promise the {@link ChannelPromise} to notify once the operation completes + * @throws Exception thrown if an error accour + */ + void write(ChannelHandlerContext ctx, MessageList msgs, ChannelPromise promise) throws Exception; } diff --git a/transport/src/main/java/io/netty/channel/ChannelOperationHandlerAdapter.java b/transport/src/main/java/io/netty/channel/ChannelOutboundHandlerAdapter.java similarity index 68% rename from transport/src/main/java/io/netty/channel/ChannelOperationHandlerAdapter.java rename to transport/src/main/java/io/netty/channel/ChannelOutboundHandlerAdapter.java index 7b314b0396..abc11403be 100644 --- a/transport/src/main/java/io/netty/channel/ChannelOperationHandlerAdapter.java +++ b/transport/src/main/java/io/netty/channel/ChannelOutboundHandlerAdapter.java @@ -18,14 +18,14 @@ package io.netty.channel; import java.net.SocketAddress; /** - * Skelton implementation of a {@link ChannelOperationHandler}. This implementation just forwards each method call via + * Skelton implementation of a {@link ChannelOutboundHandler}. This implementation just forwards each method call via * the {@link ChannelHandlerContext}. */ -public abstract class ChannelOperationHandlerAdapter extends ChannelHandlerAdapter implements ChannelOperationHandler { +public class ChannelOutboundHandlerAdapter extends ChannelHandlerAdapter implements ChannelOutboundHandler { /** * Calls {@link ChannelHandlerContext#bind(SocketAddress, ChannelPromise)} to forward - * to the next {@link ChannelOperationHandler} in the {@link ChannelPipeline}. + * to the next {@link ChannelOutboundHandler} in the {@link ChannelPipeline}. * * Sub-classes may override this method to change behavior. */ @@ -37,7 +37,7 @@ public abstract class ChannelOperationHandlerAdapter extends ChannelHandlerAdapt /** * Calls {@link ChannelHandlerContext#connect(SocketAddress, SocketAddress, ChannelPromise)} to forward - * to the next {@link ChannelOperationHandler} in the {@link ChannelPipeline}. + * to the next {@link ChannelOutboundHandler} in the {@link ChannelPipeline}. * * Sub-classes may override this method to change behavior. */ @@ -49,7 +49,7 @@ public abstract class ChannelOperationHandlerAdapter extends ChannelHandlerAdapt /** * Calls {@link ChannelHandlerContext#disconnect(ChannelPromise)} to forward - * to the next {@link ChannelOperationHandler} in the {@link ChannelPipeline}. + * to the next {@link ChannelOutboundHandler} in the {@link ChannelPipeline}. * * Sub-classes may override this method to change behavior. */ @@ -61,7 +61,7 @@ public abstract class ChannelOperationHandlerAdapter extends ChannelHandlerAdapt /** * Calls {@link ChannelHandlerContext#close(ChannelPromise)} to forward - * to the next {@link ChannelOperationHandler} in the {@link ChannelPipeline}. + * to the next {@link ChannelOutboundHandler} in the {@link ChannelPipeline}. * * Sub-classes may override this method to change behavior. */ @@ -73,35 +73,28 @@ public abstract class ChannelOperationHandlerAdapter extends ChannelHandlerAdapt /** * Calls {@link ChannelHandlerContext#close(ChannelPromise)} to forward - * to the next {@link ChannelOperationHandler} in the {@link ChannelPipeline}. + * to the next {@link ChannelOutboundHandler} in the {@link ChannelPipeline}. * * Sub-classes may override this method to change behavior. */ @Override - public void deregister(ChannelHandlerContext ctx, ChannelPromise promise) - throws Exception { + public void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { ctx.deregister(promise); } /** * Calls {@link ChannelHandlerContext#read()} to forward - * to the next {@link ChannelOperationHandler} in the {@link ChannelPipeline}. + * to the next {@link ChannelOutboundHandler} in the {@link ChannelPipeline}. * * Sub-classes may override this method to change behavior. */ @Override - public void read(ChannelHandlerContext ctx) { + public void read(ChannelHandlerContext ctx) throws Exception { ctx.read(); } - /** - * Calls {@link ChannelHandlerContext#sendFile(FileRegion, ChannelPromise)} to forward - * to the next {@link ChannelOperationHandler} in the {@link ChannelPipeline}. - * - * Sub-classes may override this method to change behavior. - */ @Override - public void sendFile(ChannelHandlerContext ctx, FileRegion region, ChannelPromise promise) throws Exception { - ctx.sendFile(region, promise); + public void write(ChannelHandlerContext ctx, MessageList msgs, ChannelPromise promise) throws Exception { + ctx.write(msgs, promise); } } diff --git a/transport/src/main/java/io/netty/channel/ChannelOutboundInvoker.java b/transport/src/main/java/io/netty/channel/ChannelOutboundInvoker.java index ed922b20e0..4f16f90aec 100644 --- a/transport/src/main/java/io/netty/channel/ChannelOutboundInvoker.java +++ b/transport/src/main/java/io/netty/channel/ChannelOutboundInvoker.java @@ -29,8 +29,8 @@ interface ChannelOutboundInvoker { * completes, either because the operation was successful or because of an error. *

* This will result in having the - * {@link ChannelOperationHandler#bind(ChannelHandlerContext, SocketAddress, ChannelPromise)} method - * called of the next {@link ChannelOperationHandler} contained in the {@link ChannelPipeline} of the + * {@link ChannelOutboundHandler#bind(ChannelHandlerContext, SocketAddress, ChannelPromise)} method + * called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the * {@link Channel}. */ ChannelFuture bind(SocketAddress localAddress); @@ -44,8 +44,8 @@ interface ChannelOutboundInvoker { * will be used. *

* This will result in having the - * {@link ChannelOperationHandler#connect(ChannelHandlerContext, SocketAddress, SocketAddress, ChannelPromise)} - * method called of the next {@link ChannelOperationHandler} contained in the {@link ChannelPipeline} of the + * {@link ChannelOutboundHandler#connect(ChannelHandlerContext, SocketAddress, SocketAddress, ChannelPromise)} + * method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the * {@link Channel}. */ ChannelFuture connect(SocketAddress remoteAddress); @@ -56,8 +56,8 @@ interface ChannelOutboundInvoker { * an error. *

* This will result in having the - * {@link ChannelOperationHandler#connect(ChannelHandlerContext, SocketAddress, SocketAddress, ChannelPromise)} - * method called of the next {@link ChannelOperationHandler} contained in the {@link ChannelPipeline} of the + * {@link ChannelOutboundHandler#connect(ChannelHandlerContext, SocketAddress, SocketAddress, ChannelPromise)} + * method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the * {@link Channel}. */ ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress); @@ -67,8 +67,8 @@ interface ChannelOutboundInvoker { * either because the operation was successful or because of an error. *

* This will result in having the - * {@link ChannelOperationHandler#disconnect(ChannelHandlerContext, ChannelPromise)} - * method called of the next {@link ChannelOperationHandler} contained in the {@link ChannelPipeline} of the + * {@link ChannelOutboundHandler#disconnect(ChannelHandlerContext, ChannelPromise)} + * method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the * {@link Channel}. */ ChannelFuture disconnect(); @@ -81,8 +81,8 @@ interface ChannelOutboundInvoker { * After it is closed it is not possible to reuse it again. *

* This will result in having the - * {@link ChannelOperationHandler#close(ChannelHandlerContext, ChannelPromise)} - * method called of the next {@link ChannelOperationHandler} contained in the {@link ChannelPipeline} of the + * {@link ChannelOutboundHandler#close(ChannelHandlerContext, ChannelPromise)} + * method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the * {@link Channel}. */ ChannelFuture close(); @@ -93,31 +93,12 @@ interface ChannelOutboundInvoker { * an error. *

* This will result in having the - * {@link ChannelOperationHandler#deregister(ChannelHandlerContext, ChannelPromise)} - * method called of the next {@link ChannelOperationHandler} contained in the {@link ChannelPipeline} of the + * {@link ChannelOutboundHandler#deregister(ChannelHandlerContext, ChannelPromise)} + * method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the * {@link Channel}. */ ChannelFuture deregister(); - /** - * Request to flush all pending data which belongs to this ChannelOutboundInvoker and notify the - * {@link ChannelFuture} once the operation completes, either because the operation was successful or because of - * an error. - *

- * Be aware that the flush could be only partially successful. In such cases the {@link ChannelFuture} will be - * failed with an {@link IncompleteFlushException}. So if you are interested to know if it was partial successful - * you need to check if the returned {@link ChannelFuture#cause()} returns an instance of - * {@link IncompleteFlushException}. In such cases you may want to call {@link #flush(ChannelPromise)} or - * {@link #flush()} to flush the rest of the data or just close the connection via {@link #close(ChannelPromise)} or - * {@link #close()} if it is not possible to recover. - *

- * This will result in having the - * {@link ChannelOperationHandler#flush(ChannelHandlerContext, ChannelPromise)} - * method called of the next {@link ChannelOperationHandler} contained in the {@link ChannelPipeline} of the - * {@link Channel}. - */ - ChannelFuture flush(); - /** * Request to write a message via this ChannelOutboundInvoker and notify the {@link ChannelFuture} * once the operation completes, either because the operation was successful or because of an error. @@ -131,22 +112,12 @@ interface ChannelOutboundInvoker { * or {@link #close()} if it is not possible to recover. *

* This will result in having the message added to the outbound buffer of the next {@link ChannelOutboundHandler} - * and the {@link ChannelOperationHandler#flush(ChannelHandlerContext, ChannelPromise)} - * method called of the next {@link ChannelOperationHandler} contained in the {@link ChannelPipeline} of the + * and the {@link ChannelOutboundHandler#flush(ChannelHandlerContext, ChannelPromise)} + * method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the * {@link Channel}. */ - ChannelFuture write(Object message); - - /** - * Request to send a {@link FileRegion} via this ChannelOutboundInvoker and notify the {@link ChannelFuture} - * once the operation completes, either because the operation was successful or because of an error. - *

- * This will result in having the - * {@link ChannelOperationHandler#sendFile(ChannelHandlerContext, FileRegion, ChannelPromise)} - * method called of the next {@link ChannelOperationHandler} contained in the {@link ChannelPipeline} of the - * {@link Channel}. - */ - ChannelFuture sendFile(FileRegion region); + ChannelFuture write(Object msg); + ChannelFuture write(MessageList msgs); /** * Request to bind to the given {@link SocketAddress} and notify the {@link ChannelFuture} once the operation @@ -155,8 +126,8 @@ interface ChannelOutboundInvoker { * The given {@link ChannelPromise} will be notified. *

* This will result in having the - * {@link ChannelOperationHandler#bind(ChannelHandlerContext, SocketAddress, ChannelPromise)} method - * called of the next {@link ChannelOperationHandler} contained in the {@link ChannelPipeline} of the + * {@link ChannelOutboundHandler#bind(ChannelHandlerContext, SocketAddress, ChannelPromise)} method + * called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the * {@link Channel}. */ ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise); @@ -173,8 +144,8 @@ interface ChannelOutboundInvoker { * will be used. *

* This will result in having the - * {@link ChannelOperationHandler#connect(ChannelHandlerContext, SocketAddress, SocketAddress, ChannelPromise)} - * method called of the next {@link ChannelOperationHandler} contained in the {@link ChannelPipeline} of the + * {@link ChannelOutboundHandler#connect(ChannelHandlerContext, SocketAddress, SocketAddress, ChannelPromise)} + * method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the * {@link Channel}. */ ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise); @@ -187,8 +158,8 @@ interface ChannelOutboundInvoker { * The given {@link ChannelPromise} will be notified and also returned. *

* This will result in having the - * {@link ChannelOperationHandler#connect(ChannelHandlerContext, SocketAddress, SocketAddress, ChannelPromise)} - * method called of the next {@link ChannelOperationHandler} contained in the {@link ChannelPipeline} of the + * {@link ChannelOutboundHandler#connect(ChannelHandlerContext, SocketAddress, SocketAddress, ChannelPromise)} + * method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the * {@link Channel}. */ ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise); @@ -200,8 +171,8 @@ interface ChannelOutboundInvoker { * The given {@link ChannelPromise} will be notified. *

* This will result in having the - * {@link ChannelOperationHandler#disconnect(ChannelHandlerContext, ChannelPromise)} - * method called of the next {@link ChannelOperationHandler} contained in the {@link ChannelPipeline} of the + * {@link ChannelOutboundHandler#disconnect(ChannelHandlerContext, ChannelPromise)} + * method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the * {@link Channel}. */ ChannelFuture disconnect(ChannelPromise promise); @@ -215,8 +186,8 @@ interface ChannelOutboundInvoker { * The given {@link ChannelPromise} will be notified. *

* This will result in having the - * {@link ChannelOperationHandler#close(ChannelHandlerContext, ChannelPromise)} - * method called of the next {@link ChannelOperationHandler} contained in the {@link ChannelPipeline} of the + * {@link ChannelOutboundHandler#close(ChannelHandlerContext, ChannelPromise)} + * method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the * {@link Channel}. */ ChannelFuture close(ChannelPromise promise); @@ -229,48 +200,26 @@ interface ChannelOutboundInvoker { * The given {@link ChannelPromise} will be notified. *

* This will result in having the - * {@link ChannelOperationHandler#deregister(ChannelHandlerContext, ChannelPromise)} - * method called of the next {@link ChannelOperationHandler} contained in the {@link ChannelPipeline} of the + * {@link ChannelOutboundHandler#deregister(ChannelHandlerContext, ChannelPromise)} + * method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the * {@link Channel}. */ ChannelFuture deregister(ChannelPromise promise); /** * Request to Read data from the {@link Channel} into the first inbound buffer, triggers an - * {@link ChannelStateHandler#inboundBufferUpdated(ChannelHandlerContext) inboundBufferUpdated} event if data was + * {@link ChannelInboundHandler#inboundBufferUpdated(ChannelHandlerContext) inboundBufferUpdated} event if data was * read, and triggers an - * {@link ChannelStateHandler#channelReadSuspended(ChannelHandlerContext) channelReadSuspended} event so the + * {@link ChannelInboundHandler#channelReadSuspended(ChannelHandlerContext) channelReadSuspended} event so the * handler can decide to continue reading. If there's a pending read operation already, this method does nothing. *

* This will result in having the - * {@link ChannelOperationHandler#read(ChannelHandlerContext)} - * method called of the next {@link ChannelOperationHandler} contained in the {@link ChannelPipeline} of the + * {@link ChannelOutboundHandler#read(ChannelHandlerContext)} + * method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the * {@link Channel}. */ void read(); - /** - * Request to flush all pending data which belongs to this ChannelOutboundInvoker and notify the - * {@link ChannelFuture} once the operation completes, either because the operation was successful or because of - * an error. - *

- * Be aware that the flush could be only partially successful. In such cases the {@link ChannelFuture} will be - * failed with an {@link IncompleteFlushException}. So if you are interested to know if it was partial successful - * you need to check if the returned {@link ChannelFuture#cause()} returns an instance of - * {@link IncompleteFlushException}. In such cases you may want to call {@link #flush(ChannelPromise)} or - * {@link #flush()} to flush the rest of the data or just close the connection via {@link #close(ChannelPromise)} or - * {@link #close()} if it is not possible to recover. - * - * The given {@link ChannelPromise} will be notified. - *

- * This will result in having the - * {@link ChannelOperationHandler#flush(ChannelHandlerContext, ChannelPromise)} - * method called of the next {@link ChannelOperationHandler} contained in the {@link ChannelPipeline} of the - * {@link Channel}. - * - */ - ChannelFuture flush(ChannelPromise promise); - /** * Request to write a message via this ChannelOutboundInvoker and notify the {@link ChannelFuture} * once the operation completes, either because the operation was successful or because of an error. @@ -286,22 +235,10 @@ interface ChannelOutboundInvoker { * The given {@link ChannelPromise} will be notified. *

* This will result in having the message added to the outbound buffer of the next {@link ChannelOutboundHandler} - * and the {@link ChannelOperationHandler#flush(ChannelHandlerContext, ChannelPromise)} - * method called of the next {@link ChannelOperationHandler} contained in the {@link ChannelPipeline} of the + * and the {@link ChannelOutboundHandler#flush(ChannelHandlerContext, ChannelPromise)} + * method called of the next {@link ChannelOutboundHandler} contained in the {@link ChannelPipeline} of the * {@link Channel}. */ - ChannelFuture write(Object message, ChannelPromise promise); - - /** - * Request to send a {@link FileRegion} via this ChannelOutboundInvoker and notify the {@link ChannelFuture} - * once the operation completes, either because the operation was successful or because of an error. - * - * The given {@link ChannelPromise} will be notified. - *

- * This will result in having the - * {@link ChannelOperationHandler#sendFile(ChannelHandlerContext, FileRegion, ChannelPromise)} - * method called of the next {@link ChannelOperationHandler} contained in the {@link ChannelPipeline} of the - * {@link Channel}. - */ - ChannelFuture sendFile(FileRegion region, ChannelPromise promise); + ChannelFuture write(Object msg, ChannelPromise promise); + ChannelFuture write(MessageList msgs, ChannelPromise promise); } diff --git a/transport/src/main/java/io/netty/channel/ChannelOutboundMessageHandler.java b/transport/src/main/java/io/netty/channel/ChannelOutboundMessageHandler.java deleted file mode 100644 index eb2a1ad4be..0000000000 --- a/transport/src/main/java/io/netty/channel/ChannelOutboundMessageHandler.java +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.channel; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.MessageBuf; - -/** - * ChannelOutboundHandler implementation which operates on messages of a specific type - * by pass them in a {@link MessageBuf} and consume then from there. - * - * If your {@link ChannelOutboundMessageHandler} handles messages of type {@link ByteBuf} or {@link Object} - * and you want to add a {@link ByteBuf} to the next buffer in the {@link ChannelPipeline} use - * {@link ChannelHandlerUtil#addToNextOutboundBuffer(ChannelHandlerContext, Object)}. - * - * @param the message type - */ -public interface ChannelOutboundMessageHandler extends ChannelOutboundHandler { - @Override - MessageBuf newOutboundBuffer(ChannelHandlerContext ctx) throws Exception; -} diff --git a/transport/src/main/java/io/netty/channel/ChannelOutboundMessageHandlerAdapter.java b/transport/src/main/java/io/netty/channel/ChannelOutboundMessageHandlerAdapter.java deleted file mode 100644 index 4889e035a5..0000000000 --- a/transport/src/main/java/io/netty/channel/ChannelOutboundMessageHandlerAdapter.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.channel; - -import io.netty.buffer.ByteBuf; -import io.netty.buffer.MessageBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.ChannelHandlerUtil.SingleOutboundMessageHandler; -import io.netty.util.Signal; -import io.netty.util.internal.TypeParameterMatcher; - -/** - * Abstract base class which handles messages of a specific type. - * - * If your {@link ChannelOutboundMessageHandlerAdapter} handles messages of type {@link ByteBuf} or {@link Object} - * and you want to add a {@link ByteBuf} to the next buffer in the {@link ChannelPipeline} use - * {@link ChannelHandlerUtil#addToNextOutboundBuffer(ChannelHandlerContext, Object)}. - * - *

- * One limitation to keep in mind is that it is not possible to detect the handled message type of you specify - * {@code I} while instance your class. Because of this Netty does not allow to do so and will throw an Exception - * if you try. For this cases you should handle the type detection by your self by override the - * {@link #acceptOutboundMessage(Object)} method and use {@link Object} as type parameter. - * - *

- *    public class GenericHandler<I> extends
- *             {@link ChannelOutboundMessageHandlerAdapter}<{@link Object}> {
- *
- *         {@code @Override}
- *         public void flush({@link ChannelHandlerContext} ctx, {@link Object} message)
- *                 throws {@link Exception} {
- *             I msg = (I) message;
- *             // Do something with the msg
- *             ...
- *             ...
- *         }
- *
- *         {@code @Override}
- *         public boolean acceptOutboundMessage(Object msg) throws Exception {
- *             // Add your check here
- *         }
- *     }
- * 
- * @param The type of the messages to handle - */ -public abstract class ChannelOutboundMessageHandlerAdapter - extends ChannelOperationHandlerAdapter - implements ChannelOutboundMessageHandler, SingleOutboundMessageHandler { - - /** - * Thrown by {@link #flush(ChannelHandlerContext, Object)} to abort message processing. - */ - protected static final Signal ABORT = ChannelHandlerUtil.ABORT; - - private final TypeParameterMatcher msgMatcher; - private boolean closeOnFailedFlush = true; - - protected ChannelOutboundMessageHandlerAdapter() { - msgMatcher = TypeParameterMatcher.find(this, ChannelOutboundMessageHandlerAdapter.class, "I"); - } - - protected ChannelOutboundMessageHandlerAdapter(Class outboundMessageType) { - msgMatcher = TypeParameterMatcher.get(outboundMessageType); - } - - protected final boolean isCloseOnFailedFlush() { - return closeOnFailedFlush; - } - - protected final void setCloseOnFailedFlush(boolean closeOnFailedFlush) { - this.closeOnFailedFlush = closeOnFailedFlush; - } - - @Override - public MessageBuf newOutboundBuffer(ChannelHandlerContext ctx) throws Exception { - return Unpooled.messageBuffer(); - } - - @Override - public final void flush(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { - ChannelHandlerUtil.handleFlush(ctx, promise, isCloseOnFailedFlush(), this); - } - - @Override - public boolean acceptOutboundMessage(Object msg) throws Exception { - return msgMatcher.match(msg); - } - - @Override - public boolean beginFlush(ChannelHandlerContext ctx) throws Exception { - return true; - } - - @Override - public void endFlush(ChannelHandlerContext ctx) throws Exception { } -} diff --git a/transport/src/main/java/io/netty/channel/ChannelPipeline.java b/transport/src/main/java/io/netty/channel/ChannelPipeline.java index f9f7944dd0..26eb387053 100644 --- a/transport/src/main/java/io/netty/channel/ChannelPipeline.java +++ b/transport/src/main/java/io/netty/channel/ChannelPipeline.java @@ -15,9 +15,7 @@ */ package io.netty.channel; -import io.netty.buffer.Buf; import io.netty.buffer.ByteBuf; -import io.netty.buffer.MessageBuf; import io.netty.util.concurrent.EventExecutorGroup; import java.io.InputStream; @@ -176,46 +174,6 @@ import java.util.NoSuchElementException; public interface ChannelPipeline extends ChannelInboundInvoker, ChannelOutboundInvoker, Iterable> { - /** - * Return the bound {@link MessageBuf} of the first {@link ChannelInboundMessageHandler} in the - * {@link ChannelPipeline}. If no {@link ChannelInboundMessageHandler} exists in the {@link ChannelPipeline} - * it will throw a {@link UnsupportedOperationException}. - *

- * This method can only be called from within the event-loop, otherwise it will throw an - * {@link IllegalStateException}. - */ - MessageBuf inboundMessageBuffer(); - - /** - * Return the bound {@link ByteBuf} of the first {@link ChannelInboundByteHandler} in the - * {@link ChannelPipeline}. If no {@link ChannelInboundByteHandler} exists in the {@link ChannelPipeline} - * it will throw a {@link UnsupportedOperationException}. - *

- * This method can only be called from within the event-loop, otherwise it will throw an - * {@link IllegalStateException}. - */ - ByteBuf inboundByteBuffer(); - - /** - * Return the bound {@link MessageBuf} of the first {@link ChannelOutboundMessageHandler} in the - * {@link ChannelPipeline}. If no {@link ChannelOutboundMessageHandler} exists in the {@link ChannelPipeline} - * it will throw a {@link UnsupportedOperationException}. - *

- * This method can only be called from within the event-loop, otherwise it will throw an - * {@link IllegalStateException}. - */ - MessageBuf outboundMessageBuffer(); - - /** - * Return the bound {@link ByteBuf} of the first {@link ChannelOutboundByteHandler} in the - * {@link ChannelPipeline}. If no {@link ChannelOutboundByteHandler} exists in the {@link ChannelPipeline} - * it will throw a {@link UnsupportedOperationException}. - *

- * This method can only be called from within the event-loop, otherwise it will throw an - * {@link IllegalStateException}. - */ - ByteBuf outboundByteBuffer(); - /** * Inserts a {@link ChannelHandler} at the first position of this pipeline. * @@ -381,9 +339,7 @@ public interface ChannelPipeline extends ChannelInboundInvoker, ChannelOutboundI ChannelPipeline addLast(EventExecutorGroup group, ChannelHandler... handlers); /** - * Removes the specified {@link ChannelHandler} from this pipeline - * and transfer the content of its {@link Buf} to the next - * {@link ChannelHandler} in the {@link ChannelPipeline}. + * Removes the specified {@link ChannelHandler} from this pipeline. * * @param handler the {@link ChannelHandler} to remove * @@ -395,9 +351,7 @@ public interface ChannelPipeline extends ChannelInboundInvoker, ChannelOutboundI ChannelPipeline remove(ChannelHandler handler); /** - * Removes the {@link ChannelHandler} with the specified name from this - * pipeline and transfer the content of its {@link Buf} to the next - * {@link ChannelHandler} in the {@link ChannelPipeline}. + * Removes the {@link ChannelHandler} with the specified name from this pipeline. * * @param name the name under which the {@link ChannelHandler} was stored. * @@ -411,9 +365,7 @@ public interface ChannelPipeline extends ChannelInboundInvoker, ChannelOutboundI ChannelHandler remove(String name); /** - * Removes the {@link ChannelHandler} of the specified type from this - * pipeline and transfer the content of its {@link Buf} to the next - * {@link ChannelHandler} in the {@link ChannelPipeline}. + * Removes the {@link ChannelHandler} of the specified type from this pipeline. * * @param the type of the handler * @param handlerType the type of the handler @@ -430,9 +382,6 @@ public interface ChannelPipeline extends ChannelInboundInvoker, ChannelOutboundI /** * Removes the first {@link ChannelHandler} in this pipeline. * - * All the remaining content in the {@link Buf) (if any) of the {@link ChannelHandler} - * will be discarded. - * * @return the removed handler * * @throws NoSuchElementException @@ -443,9 +392,6 @@ public interface ChannelPipeline extends ChannelInboundInvoker, ChannelOutboundI /** * Removes the last {@link ChannelHandler} in this pipeline. * - * All the remaining content in the {@link Buf) (if any) of the {@link ChannelHandler} - * will be discarded. - * * @return the removed handler * * @throws NoSuchElementException @@ -454,9 +400,7 @@ public interface ChannelPipeline extends ChannelInboundInvoker, ChannelOutboundI ChannelHandler removeLast(); /** - * Replaces the specified {@link ChannelHandler} with a new handler in - * this pipeline and transfer the content of its {@link Buf} to the next - * {@link ChannelHandler} in the {@link ChannelPipeline}. + * Replaces the specified {@link ChannelHandler} with a new handler in this pipeline. * * @param oldHandler the {@link ChannelHandler} to be replaced * @param newName the name under which the replacement should be added @@ -476,9 +420,7 @@ public interface ChannelPipeline extends ChannelInboundInvoker, ChannelOutboundI ChannelPipeline replace(ChannelHandler oldHandler, String newName, ChannelHandler newHandler); /** - * Replaces the {@link ChannelHandler} of the specified name with a new - * handler in this pipeline and transfer the content of its {@link Buf} to the next - * {@link ChannelHandler} in the {@link ChannelPipeline}. + * Replaces the {@link ChannelHandler} of the specified name with a new handler in this pipeline. * * @param oldName the name of the {@link ChannelHandler} to be replaced * @param newName the name under which the replacement should be added @@ -498,9 +440,7 @@ public interface ChannelPipeline extends ChannelInboundInvoker, ChannelOutboundI ChannelHandler replace(String oldName, String newName, ChannelHandler newHandler); /** - * Replaces the {@link ChannelHandler} of the specified type with a new - * handler in this pipeline and transfer the content of its {@link Buf} to the next - * {@link ChannelHandler} in the {@link ChannelPipeline}. + * Replaces the {@link ChannelHandler} of the specified type with a new handler in this pipeline. * * @param oldHandlerType the type of the handler to be removed * @param newName the name under which the replacement should be added @@ -631,8 +571,14 @@ public interface ChannelPipeline extends ChannelInboundInvoker, ChannelOutboundI ChannelPipeline fireUserEventTriggered(Object event); @Override - ChannelPipeline fireInboundBufferUpdated(); + ChannelPipeline fireMessageReceived(Object msg); + + @Override + ChannelPipeline fireMessageReceived(MessageList msgs); @Override ChannelPipeline fireChannelReadSuspended(); + + @Override + ChannelPipeline fireChannelWritabilityChanged(); } diff --git a/transport/src/main/java/io/netty/channel/ChannelStateHandler.java b/transport/src/main/java/io/netty/channel/ChannelStateHandler.java deleted file mode 100755 index 9ce18bcaff..0000000000 --- a/transport/src/main/java/io/netty/channel/ChannelStateHandler.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.channel; - -/** - * {@link ChannelHandler} which adds callbacks for state changes. This allows the user - * to hook in to state changes easily. - */ -public interface ChannelStateHandler extends ChannelHandler { - - /** - * The {@link Channel} of the {@link ChannelHandlerContext} was registered with its {@link EventLoop} - */ - void channelRegistered(ChannelHandlerContext ctx) throws Exception; - - /** - * The {@link Channel} of the {@link ChannelHandlerContext} was unregistered from its {@link EventLoop} - */ - void channelUnregistered(ChannelHandlerContext ctx) throws Exception; - - /** - * The {@link Channel} of the {@link ChannelHandlerContext} is now active - */ - void channelActive(ChannelHandlerContext ctx) throws Exception; - - /** - * The {@link Channel} of the {@link ChannelHandlerContext} was registered is now inactive and reached its - * end of lifetime. - */ - void channelInactive(ChannelHandlerContext ctx) throws Exception; - - /** - * Invoked when a {@link ChannelHandlerContext#read()} is finished and the inbound buffer of this handler will not - * be updated until another {@link ChannelHandlerContext#read()} request is issued. - */ - void channelReadSuspended(ChannelHandlerContext ctx) throws Exception; - - /** - * The inbound buffer of the {@link ChannelHandlerContext} was updated with new data. - * This means something may be ready to get processed by the actual {@link ChannelStateHandler} - * implementation. It's up to the implementation to consume it or keep it in the buffer - * to wait for more data and consume it later. - */ - void inboundBufferUpdated(ChannelHandlerContext ctx) throws Exception; - - /** - * Gets called if an user event was triggered. - */ - void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception; -} diff --git a/transport/src/main/java/io/netty/channel/CombinedChannelDuplexHandler.java b/transport/src/main/java/io/netty/channel/CombinedChannelDuplexHandler.java index ab7faf2e6c..34dd384fa8 100644 --- a/transport/src/main/java/io/netty/channel/CombinedChannelDuplexHandler.java +++ b/transport/src/main/java/io/netty/channel/CombinedChannelDuplexHandler.java @@ -18,17 +18,18 @@ package io.netty.channel; import java.net.SocketAddress; /** - * Combines a {@link ChannelStateHandler} and a {@link ChannelOperationHandler} into one {@link ChannelHandler}. + * Combines a {@link ChannelInboundHandler} and a {@link ChannelOutboundHandler} into one {@link ChannelHandler}. * */ -public class CombinedChannelDuplexHandler extends ChannelDuplexHandler { +public class CombinedChannelDuplexHandler + extends ChannelDuplexHandler { - private ChannelStateHandler stateHandler; - private ChannelOperationHandler operationHandler; + private I inboundHandler; + private O outboundHandler; /** * Creates a new uninitialized instance. A class that extends this handler must invoke - * {@link #init(ChannelStateHandler, ChannelOperationHandler)} before adding this handler into a + * {@link #init(ChannelInboundHandler, ChannelOutboundHandler)} before adding this handler into a * {@link ChannelPipeline}. */ protected CombinedChannelDuplexHandler() { } @@ -36,8 +37,8 @@ public class CombinedChannelDuplexHandler extends ChannelDuplexHandler { /** * Creates a new instance that combines the specified two handlers into one. */ - public CombinedChannelDuplexHandler(ChannelStateHandler stateHandler, ChannelOperationHandler operationHandler) { - init(stateHandler, operationHandler); + public CombinedChannelDuplexHandler(I inboundHandler, O outboundHandler) { + init(inboundHandler, outboundHandler); } /** @@ -48,141 +49,110 @@ public class CombinedChannelDuplexHandler extends ChannelDuplexHandler { * @throws IllegalArgumentException if the specified handlers cannot be combined into one due to a conflict * in the type hierarchy */ - protected final void init(ChannelStateHandler stateHandler, ChannelOperationHandler operationHandler) { - validate(stateHandler, operationHandler); - this.stateHandler = stateHandler; - this.operationHandler = operationHandler; + protected final void init(I inboundHandler, O outboundHandler) { + validate(inboundHandler, outboundHandler); + this.inboundHandler = inboundHandler; + this.outboundHandler = outboundHandler; } @SuppressWarnings("InstanceofIncompatibleInterface") - private void validate(ChannelStateHandler stateHandler, ChannelOperationHandler operationHandler) { - if (this.stateHandler != null) { + private void validate(I inboundHandler, O outboundHandler) { + if (this.inboundHandler != null) { throw new IllegalStateException( "init() can not be invoked if " + CombinedChannelDuplexHandler.class.getSimpleName() + " was constructed with non-default constructor."); } - if (stateHandler == null) { - throw new NullPointerException("stateHandler"); + if (inboundHandler == null) { + throw new NullPointerException("inboundHandler"); } - if (operationHandler == null) { - throw new NullPointerException("operationHandler"); + if (outboundHandler == null) { + throw new NullPointerException("outboundHandler"); } - if (stateHandler instanceof ChannelOperationHandler) { + if (inboundHandler instanceof ChannelOutboundHandler) { throw new IllegalArgumentException( - "stateHandler must not implement " + - ChannelOperationHandler.class.getSimpleName() + " to get combined."); + "inboundHandler must not implement " + + ChannelOutboundHandler.class.getSimpleName() + " to get combined."); } - if (operationHandler instanceof ChannelStateHandler) { + if (outboundHandler instanceof ChannelInboundHandler) { throw new IllegalArgumentException( - "operationHandler must not implement " + - ChannelStateHandler.class.getSimpleName() + " to get combined."); - } - - if (stateHandler instanceof ChannelInboundByteHandler && !(this instanceof ChannelInboundByteHandler)) { - throw new IllegalStateException( - getClass().getSimpleName() + " must implement " + ChannelInboundByteHandler.class.getSimpleName() + - " if stateHandler implements " + ChannelInboundByteHandler.class.getSimpleName()); - } - - if (stateHandler instanceof ChannelInboundMessageHandler && !(this instanceof ChannelInboundMessageHandler)) { - throw new IllegalStateException( - getClass().getSimpleName() + " must implement " + - ChannelInboundMessageHandler.class.getSimpleName() + " if stateHandler implements " + - ChannelInboundMessageHandler.class.getSimpleName()); - } - - if (operationHandler instanceof ChannelOutboundByteHandler && !(this instanceof ChannelOutboundByteHandler)) { - throw new IllegalStateException( - getClass().getSimpleName() + " must implement " + - ChannelOutboundByteHandler.class.getSimpleName() + " if operationHandler implements " + - ChannelOutboundByteHandler.class.getSimpleName()); - } - - if (operationHandler instanceof ChannelOutboundMessageHandler && - !(this instanceof ChannelOutboundMessageHandler)) { - throw new IllegalStateException( - getClass().getSimpleName() + " must implement " + - ChannelOutboundMessageHandler.class.getSimpleName() + " if operationHandler implements " + - ChannelOutboundMessageHandler.class.getSimpleName()); + "outboundHandler must not implement " + + ChannelInboundHandler.class.getSimpleName() + " to get combined."); } } - protected final ChannelStateHandler stateHandler() { - return stateHandler; + protected final I inboundHandler() { + return inboundHandler; } - protected final ChannelOperationHandler operationHandler() { - return operationHandler; + protected final O outboundHandler() { + return outboundHandler; } @Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { - if (stateHandler == null) { + if (inboundHandler == null) { throw new IllegalStateException( "init() must be invoked before being added to a " + ChannelPipeline.class.getSimpleName() + " if " + CombinedChannelDuplexHandler.class.getSimpleName() + " was constructed with the default constructor."); } try { - stateHandler.handlerAdded(ctx); + inboundHandler.handlerAdded(ctx); } finally { - operationHandler.handlerAdded(ctx); + outboundHandler.handlerAdded(ctx); } } @Override public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { try { - stateHandler.handlerRemoved(ctx); + inboundHandler.handlerRemoved(ctx); } finally { - operationHandler.handlerRemoved(ctx); + outboundHandler.handlerRemoved(ctx); } } @Override public void channelRegistered(ChannelHandlerContext ctx) throws Exception { - stateHandler.channelRegistered(ctx); + inboundHandler.channelRegistered(ctx); } @Override public void channelUnregistered(ChannelHandlerContext ctx) throws Exception { - stateHandler.channelUnregistered(ctx); + inboundHandler.channelUnregistered(ctx); } @Override public void channelActive(ChannelHandlerContext ctx) throws Exception { - stateHandler.channelActive(ctx); + inboundHandler.channelActive(ctx); } @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { - stateHandler.channelInactive(ctx); + inboundHandler.channelInactive(ctx); } @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { - stateHandler.exceptionCaught(ctx, cause); + inboundHandler.exceptionCaught(ctx, cause); } @Override public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception { - stateHandler.userEventTriggered(ctx, evt); + inboundHandler.userEventTriggered(ctx, evt); } @Override - public void inboundBufferUpdated(ChannelHandlerContext ctx) throws Exception { - stateHandler.inboundBufferUpdated(ctx); - if (stateHandler instanceof ChannelInboundByteHandler) { - ((ChannelInboundByteHandler) stateHandler).discardInboundReadBytes(ctx); - } + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + inboundHandler.messageReceived(ctx, msgs); } @Override public void bind( ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception { - operationHandler.bind(ctx, localAddress, promise); + outboundHandler.bind(ctx, localAddress, promise); } @Override @@ -190,36 +160,31 @@ public class CombinedChannelDuplexHandler extends ChannelDuplexHandler { ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) throws Exception { - operationHandler.connect(ctx, remoteAddress, localAddress, promise); + outboundHandler.connect(ctx, remoteAddress, localAddress, promise); } @Override public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { - operationHandler.disconnect(ctx, promise); + outboundHandler.disconnect(ctx, promise); } @Override public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { - operationHandler.close(ctx, promise); + outboundHandler.close(ctx, promise); } @Override public void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { - operationHandler.deregister(ctx, promise); + outboundHandler.deregister(ctx, promise); } @Override - public void read(ChannelHandlerContext ctx) { - operationHandler.read(ctx); + public void read(ChannelHandlerContext ctx) throws Exception { + outboundHandler.read(ctx); } @Override - public void flush(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { - operationHandler.flush(ctx, promise); - } - - @Override - public void sendFile(ChannelHandlerContext ctx, FileRegion region, ChannelPromise promise) throws Exception { - operationHandler.sendFile(ctx, region, promise); + public void write(ChannelHandlerContext ctx, MessageList msgs, ChannelPromise promise) throws Exception { + outboundHandler.write(ctx, msgs, promise); } } diff --git a/transport/src/main/java/io/netty/channel/DefaultAddressedEnvelope.java b/transport/src/main/java/io/netty/channel/DefaultAddressedEnvelope.java index a9a544c03c..20766e7165 100644 --- a/transport/src/main/java/io/netty/channel/DefaultAddressedEnvelope.java +++ b/transport/src/main/java/io/netty/channel/DefaultAddressedEnvelope.java @@ -16,7 +16,7 @@ package io.netty.channel; -import io.netty.buffer.BufUtil; +import io.netty.buffer.ByteBufUtil; import io.netty.buffer.ReferenceCounted; import io.netty.util.internal.StringUtil; @@ -82,24 +82,24 @@ public class DefaultAddressedEnvelope implements Add @Override public AddressedEnvelope retain() { - BufUtil.retain(message); + ByteBufUtil.retain(message); return this; } @Override public AddressedEnvelope retain(int increment) { - BufUtil.retain(message, increment); + ByteBufUtil.retain(message, increment); return this; } @Override public boolean release() { - return BufUtil.release(message); + return ByteBufUtil.release(message); } @Override public boolean release(int decrement) { - return BufUtil.release(message, decrement); + return ByteBufUtil.release(message, decrement); } @Override diff --git a/transport/src/main/java/io/netty/channel/DefaultChannelConfig.java b/transport/src/main/java/io/netty/channel/DefaultChannelConfig.java index febf689089..3138aeba67 100644 --- a/transport/src/main/java/io/netty/channel/DefaultChannelConfig.java +++ b/transport/src/main/java/io/netty/channel/DefaultChannelConfig.java @@ -31,15 +31,18 @@ import static io.netty.channel.ChannelOption.*; public class DefaultChannelConfig implements ChannelConfig { private static final ByteBufAllocator DEFAULT_ALLOCATOR = PooledByteBufAllocator.DEFAULT; + private static final RecvByteBufAllocator DEFAULT_RCVBUF_ALLOCATOR = AdaptiveRecvByteBufAllocator.DEFAULT; private static final int DEFAULT_CONNECT_TIMEOUT = 30000; protected final Channel channel; - private volatile ChannelHandlerByteBufType handlerByteBufType = ChannelHandlerByteBufType.PREFER_DIRECT; private volatile ByteBufAllocator allocator = DEFAULT_ALLOCATOR; + private volatile RecvByteBufAllocator rcvBufAllocator = DEFAULT_RCVBUF_ALLOCATOR; private volatile int connectTimeoutMillis = DEFAULT_CONNECT_TIMEOUT; private volatile int writeSpinCount = 16; private volatile boolean autoRead = true; + private volatile int writeBufferHighWaterMark = 64 * 1024; + private volatile int writeBufferLowWaterMark = 32 * 1024; public DefaultChannelConfig(Channel channel) { if (channel == null) { @@ -50,8 +53,7 @@ public class DefaultChannelConfig implements ChannelConfig { @Override public Map, Object> getOptions() { - return getOptions(null, CONNECT_TIMEOUT_MILLIS, WRITE_SPIN_COUNT, ALLOCATOR, AUTO_READ, - DEFAULT_HANDLER_BYTEBUF_TYPE); + return getOptions(null, CONNECT_TIMEOUT_MILLIS, WRITE_SPIN_COUNT, ALLOCATOR, AUTO_READ, RCVBUF_ALLOCATOR); } protected Map, Object> getOptions( @@ -98,12 +100,12 @@ public class DefaultChannelConfig implements ChannelConfig { if (option == ALLOCATOR) { return (T) getAllocator(); } + if (option == RCVBUF_ALLOCATOR) { + return (T) getRecvByteBufAllocator(); + } if (option == AUTO_READ) { return (T) Boolean.valueOf(isAutoRead()); } - if (option == DEFAULT_ALLOCATOR) { - return (T) getDefaultHandlerByteBufType(); - } return null; } @@ -118,10 +120,10 @@ public class DefaultChannelConfig implements ChannelConfig { setWriteSpinCount((Integer) value); } else if (option == ALLOCATOR) { setAllocator((ByteBufAllocator) value); + } else if (option == RCVBUF_ALLOCATOR) { + setRecvByteBufAllocator((RecvByteBufAllocator) value); } else if (option == AUTO_READ) { setAutoRead((Boolean) value); - } else if (option == DEFAULT_HANDLER_BYTEBUF_TYPE) { - setDefaultHandlerByteBufType((ChannelHandlerByteBufType) value); } else { return false; } @@ -180,6 +182,20 @@ public class DefaultChannelConfig implements ChannelConfig { return this; } + @Override + public RecvByteBufAllocator getRecvByteBufAllocator() { + return rcvBufAllocator; + } + + @Override + public ChannelConfig setRecvByteBufAllocator(RecvByteBufAllocator allocator) { + if (allocator == null) { + throw new NullPointerException("allocator"); + } + rcvBufAllocator = allocator; + return this; + } + @Override public boolean isAutoRead() { return autoRead; @@ -196,13 +212,44 @@ public class DefaultChannelConfig implements ChannelConfig { } @Override - public ChannelHandlerByteBufType getDefaultHandlerByteBufType() { - return handlerByteBufType; + public int getWriteBufferHighWaterMark() { + return writeBufferHighWaterMark; } @Override - public ChannelConfig setDefaultHandlerByteBufType(ChannelHandlerByteBufType handlerByteBufType) { - this.handlerByteBufType = handlerByteBufType; + public ChannelConfig setWriteBufferHighWaterMark(int writeBufferHighWaterMark) { + if (writeBufferHighWaterMark < getWriteBufferLowWaterMark()) { + throw new IllegalArgumentException( + "writeBufferHighWaterMark cannot be less than " + + "writeBufferLowWaterMark (" + getWriteBufferLowWaterMark() + "): " + + writeBufferHighWaterMark); + } + if (writeBufferHighWaterMark < 0) { + throw new IllegalArgumentException( + "writeBufferHighWaterMark must be >= 0"); + } + this.writeBufferHighWaterMark = writeBufferHighWaterMark; + return this; + } + + @Override + public int getWriteBufferLowWaterMark() { + return writeBufferLowWaterMark; + } + + @Override + public ChannelConfig setWriteBufferLowWaterMark(int writeBufferLowWaterMark) { + if (writeBufferLowWaterMark > getWriteBufferHighWaterMark()) { + throw new IllegalArgumentException( + "writeBufferLowWaterMark cannot be greater than " + + "writeBufferHighWaterMark (" + getWriteBufferHighWaterMark() + "): " + + writeBufferLowWaterMark); + } + if (writeBufferLowWaterMark < 0) { + throw new IllegalArgumentException( + "writeBufferLowWaterMark must be >= 0"); + } + this.writeBufferLowWaterMark = writeBufferLowWaterMark; return this; } } diff --git a/transport/src/main/java/io/netty/channel/DefaultChannelHandlerContext.java b/transport/src/main/java/io/netty/channel/DefaultChannelHandlerContext.java index 5f32964e45..9a2f2fa4e5 100755 --- a/transport/src/main/java/io/netty/channel/DefaultChannelHandlerContext.java +++ b/transport/src/main/java/io/netty/channel/DefaultChannelHandlerContext.java @@ -15,34 +15,17 @@ */ package io.netty.channel; -import io.netty.buffer.Buf; -import io.netty.buffer.BufType; -import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.MessageBuf; -import io.netty.buffer.Unpooled; import io.netty.util.DefaultAttributeMap; import io.netty.util.concurrent.EventExecutor; import io.netty.util.concurrent.EventExecutorGroup; -import io.netty.util.concurrent.Future; -import io.netty.util.internal.PlatformDependent; import java.net.SocketAddress; -import java.nio.channels.ClosedChannelException; -import java.util.Queue; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.atomic.AtomicReferenceFieldUpdater; import static io.netty.channel.DefaultChannelPipeline.*; final class DefaultChannelHandlerContext extends DefaultAttributeMap implements ChannelHandlerContext { - private static final int FLAG_REMOVED = 1; - private static final int FLAG_FREED = 2; - private static final int FLAG_FREED_INBOUND = 4; - private static final int FLAG_FREED_OUTBOUND = 8; - volatile DefaultChannelHandlerContext next; volatile DefaultChannelHandlerContext prev; @@ -56,48 +39,10 @@ final class DefaultChannelHandlerContext extends DefaultAttributeMap implements final EventExecutor executor; private ChannelFuture succeededFuture; - private final MessageBuf inMsgBuf; - private final ByteBuf inByteBuf; - private MessageBuf outMsgBuf; - private ByteBuf outByteBuf; - private short callDepth; - private short flags; - - // When the two handlers run in a different thread and they are next to each other, - // each other's buffers can be accessed at the same time resulting in a race condition. - // To avoid such situation, we lazily creates an additional thread-safe buffer called - // 'bridge' so that the two handlers access each other's buffer only via the bridges. - // The content written into a bridge is flushed into the actual buffer by flushBridge(). - // - // Note we use an AtomicReferenceFieldUpdater for atomic operations on these to save memory. This will save us - // 64 bytes per Bridge. - @SuppressWarnings("UnusedDeclaration") - private volatile Queue inBridge; - @SuppressWarnings("UnusedDeclaration") - private volatile Queue outBridge; - @SuppressWarnings("UnusedDeclaration") - private volatile NextBridgeFeeder nextInBridgeFeeder; - @SuppressWarnings("UnusedDeclaration") - private volatile NextBridgeFeeder nextOutBridgeFeeder; - - @SuppressWarnings("rawtypes") - private static final AtomicReferenceFieldUpdater IN_BRIDGE_UPDATER = - AtomicReferenceFieldUpdater.newUpdater(DefaultChannelHandlerContext.class, Queue.class, "inBridge"); - @SuppressWarnings("rawtypes") - private static final AtomicReferenceFieldUpdater OUT_BRIDGE_UPDATER = - AtomicReferenceFieldUpdater.newUpdater(DefaultChannelHandlerContext.class, Queue.class, "outBridge"); - private static final AtomicReferenceFieldUpdater - NEXT_IN_BRIDGE_FEEDER = AtomicReferenceFieldUpdater.newUpdater( - DefaultChannelHandlerContext.class, NextBridgeFeeder.class, "nextInBridgeFeeder"); - private static final AtomicReferenceFieldUpdater - NEXT_OUT_BRIDGE_FEEDER = AtomicReferenceFieldUpdater.newUpdater( - DefaultChannelHandlerContext.class, NextBridgeFeeder.class, "nextOutBridgeFeeder"); - // Lazily instantiated tasks used to trigger events to a handler with different executor. - private Runnable invokeInboundBufferUpdatedTask; - private Runnable fireInboundBufferUpdated0Task; private Runnable invokeChannelReadSuspendedTask; private Runnable invokeRead0Task; + private Runnable invokeChannelWritableStateChangedTask; @SuppressWarnings("unchecked") DefaultChannelHandlerContext( @@ -127,340 +72,6 @@ final class DefaultChannelHandlerContext extends DefaultAttributeMap implements } else { executor = null; } - - if (handler instanceof ChannelInboundHandler) { - Buf buf; - try { - buf = ((ChannelInboundHandler) handler).newInboundBuffer(this); - } catch (Exception e) { - throw new ChannelPipelineException( - handler.getClass().getSimpleName() + ".newInboundBuffer() raised an exception.", e); - } - - if (buf instanceof ByteBuf) { - inByteBuf = (ByteBuf) buf; - inMsgBuf = null; - } else if (buf instanceof MessageBuf) { - inMsgBuf = (MessageBuf) buf; - inByteBuf = null; - } else { - throw new ChannelPipelineException( - handler.getClass().getSimpleName() + ".newInboundBuffer() returned neither " + - ByteBuf.class.getSimpleName() + " nor " + MessageBuf.class.getSimpleName() + ": " + buf); - } - } else { - inByteBuf = null; - inMsgBuf = null; - } - - if (handler instanceof ChannelOutboundHandler) { - Buf buf; - try { - buf = ((ChannelOutboundHandler) handler).newOutboundBuffer(this); - } catch (Exception e) { - throw new ChannelPipelineException( - handler.getClass().getSimpleName() + ".newOutboundBuffer() raised an exception.", e); - } - - if (buf instanceof ByteBuf) { - outByteBuf = (ByteBuf) buf; - } else if (buf instanceof MessageBuf) { - @SuppressWarnings("unchecked") - MessageBuf msgBuf = (MessageBuf) buf; - outMsgBuf = msgBuf; - } else { - throw new ChannelPipelineException( - handler.getClass().getSimpleName() + ".newOutboundBuffer() returned neither " + - ByteBuf.class.getSimpleName() + " nor " + MessageBuf.class.getSimpleName() + ": " + buf); - } - } - } - - DefaultChannelHandlerContext(DefaultChannelPipeline pipeline, String name, HeadHandler handler) { - channel = pipeline.channel; - this.pipeline = pipeline; - this.name = name; - this.handler = handler; - executor = null; - inByteBuf = null; - inMsgBuf = null; - } - - DefaultChannelHandlerContext(DefaultChannelPipeline pipeline, String name, TailHandler handler) { - channel = pipeline.channel; - this.pipeline = pipeline; - this.name = name; - this.handler = handler; - executor = null; - inByteBuf = handler.byteSink; - inMsgBuf = handler.msgSink; - outByteBuf = null; - outMsgBuf = null; - } - - void forwardBufferContentAndFree( - final DefaultChannelHandlerContext forwardPrev, final DefaultChannelHandlerContext forwardNext) { - - boolean flush = false; - boolean inboundBufferUpdated = false; - try { - if (!isOutboundFreed()) { - if (hasOutboundByteBuffer() && outboundByteBuffer().isReadable()) { - ByteBuf forwardPrevBuf; - if (forwardPrev.hasOutboundByteBuffer()) { - forwardPrevBuf = forwardPrev.outboundByteBuffer(); - } else { - forwardPrevBuf = forwardPrev.nextOutboundByteBuffer(); - } - forwardPrevBuf.writeBytes(outboundByteBuffer()); - flush = true; - } - if (hasOutboundMessageBuffer() && !outboundMessageBuffer().isEmpty()) { - MessageBuf forwardPrevBuf; - if (forwardPrev.hasOutboundMessageBuffer()) { - forwardPrevBuf = forwardPrev.outboundMessageBuffer(); - } else { - forwardPrevBuf = forwardPrev.nextOutboundMessageBuffer(); - } - if (outboundMessageBuffer().drainTo(forwardPrevBuf) > 0) { - flush = true; - } - } - } - - if (!isInboundFreed()) { - if (hasInboundByteBuffer() && inboundByteBuffer().isReadable()) { - ByteBuf forwardNextBuf; - if (forwardNext.hasInboundByteBuffer()) { - forwardNextBuf = forwardNext.inboundByteBuffer(); - } else { - forwardNextBuf = forwardNext.nextInboundByteBuffer(); - } - forwardNextBuf.writeBytes(inboundByteBuffer()); - inboundBufferUpdated = true; - } - if (hasInboundMessageBuffer() && !inboundMessageBuffer().isEmpty()) { - MessageBuf forwardNextBuf; - if (forwardNext.hasInboundMessageBuffer()) { - forwardNextBuf = forwardNext.inboundMessageBuffer(); - } else { - forwardNextBuf = forwardNext.nextInboundMessageBuffer(); - } - if (inboundMessageBuffer().drainTo(forwardNextBuf) > 0) { - inboundBufferUpdated = true; - } - } - } - } finally { - flags |= FLAG_REMOVED; - freeAllIfRemoved(); - } - - if (flush) { - EventExecutor executor = executor(); - Thread currentThread = Thread.currentThread(); - if (executor.inEventLoop(currentThread)) { - invokePrevFlush(newPromise(), currentThread, findContextOutboundInclusive(forwardPrev)); - } else { - executor.execute(new Runnable() { - @Override - public void run() { - invokePrevFlush(newPromise(), Thread.currentThread(), - findContextOutboundInclusive(forwardPrev)); - } - }); - } - } - - if (inboundBufferUpdated) { - EventExecutor executor = executor(); - if (executor.inEventLoop()) { - fireInboundBufferUpdated0(findContextInboundInclusive(forwardNext)); - } else { - executor.execute(new Runnable() { - @Override - public void run() { - fireInboundBufferUpdated0(findContextInboundInclusive(forwardNext)); - } - }); - } - } - } - - private static DefaultChannelHandlerContext findContextOutboundInclusive(DefaultChannelHandlerContext ctx) { - if (ctx.handler() instanceof ChannelOperationHandler) { - return ctx; - } - return ctx.findContextOutbound(); - } - - private static DefaultChannelHandlerContext findContextInboundInclusive(DefaultChannelHandlerContext ctx) { - if (ctx.handler() instanceof ChannelStateHandler) { - return ctx; - } - return ctx.findContextInbound(); - } - - void clearBuffer() { - if (hasOutboundByteBuffer()) { - outboundByteBuffer().clear(); - } - if (hasOutboundMessageBuffer()) { - outboundMessageBuffer().clear(); - } - if (hasInboundByteBuffer()) { - inboundByteBuffer().clear(); - } - if (hasInboundMessageBuffer()) { - inboundMessageBuffer().clear(); - } - } - - void initHeadHandler() { - // Must be called for the head handler. - EventExecutor executor = executor(); - if (executor.inEventLoop()) { - HeadHandler h = (HeadHandler) handler; - if (h.initialized) { - return; - } - - h.init(this); - h.initialized = true; - outByteBuf = h.byteSink; - outMsgBuf = h.msgSink; - } else { - Future f = executor.submit(new Runnable() { - @Override - public void run() { - initHeadHandler(); - } - }); - - boolean interrupted = false; - try { - while (!f.isDone()) { - try { - f.get(); - } catch (InterruptedException e) { - interrupted = true; - } catch (ExecutionException e) { - PlatformDependent.throwException(e); - } - } - } finally { - if (interrupted) { - Thread.currentThread().interrupt(); - } - } - } - } - - private boolean flushInboundBridge() { - Queue inBridge = this.inBridge; - if (inBridge == null) { - return true; - } - return flushBridge(inBridge, inMsgBuf, inByteBuf); - } - - private boolean flushOutboundBridge() { - Queue outBridge = this.outBridge; - if (outBridge == null) { - return true; - } - return flushBridge(outBridge, outMsgBuf, outByteBuf); - } - - private static boolean flushBridge(Queue bridge, MessageBuf msgBuf, ByteBuf byteBuf) { - if (bridge == null) { - return true; - } - - boolean nextBufferHadEnoughRoom = true; - for (;;) { - Object o = bridge.peek(); - if (o == null) { - break; - } - - try { - if (o instanceof Object[]) { - Object[] data = (Object[]) o; - int i; - for (i = 0; i < data.length; i ++) { - Object m = data[i]; - if (m == null) { - break; - } - - if (msgBuf.offer(m)) { - data[i] = null; - } else { - System.arraycopy(data, i, data, 0, data.length - i); - for (int j = i + 1; j < data.length; j ++) { - data[j] = null; - } - nextBufferHadEnoughRoom = false; - break; - } - } - } else if (o instanceof ByteBuf) { - ByteBuf data = (ByteBuf) o; - if (byteBuf.writerIndex() > byteBuf.maxCapacity() - data.readableBytes()) { - // The target buffer is not going to be able to accept all data in the bridge. - byteBuf.capacity(byteBuf.maxCapacity()); - byteBuf.writeBytes(data, byteBuf.writableBytes()); - nextBufferHadEnoughRoom = false; - break; - } else { - try { - byteBuf.writeBytes(data); - } finally { - data.release(); - } - } - } else { - throw new Error(); - } - } finally { - if (nextBufferHadEnoughRoom) { - Object removed = bridge.remove(); - assert removed == o; - } - } - } - - return nextBufferHadEnoughRoom; - } - - private boolean isInboundFreed() { - return (flags & FLAG_FREED_INBOUND) != 0; - } - - private boolean isOutboundFreed() { - return (flags & FLAG_FREED_OUTBOUND) != 0; - } - - private void freeAllIfRemoved() { - if (callDepth != 0) { - // Free only when the current context's handler is not being called. - return; - } - - final int flags = this.flags; - if ((flags & FLAG_REMOVED) != 0 && (flags & FLAG_FREED) == 0) { // Removed, but not freed yet - try { - safeFree(inByteBuf); - safeFree(inMsgBuf); - safeFree(outByteBuf); - safeFree(outMsgBuf); - } finally { - this.flags = (short) (flags | FLAG_FREED | FLAG_FREED_INBOUND | FLAG_FREED_OUTBOUND); - freeNextInboundBridgeFeeder(); - freeNextOutboundBridgeFeeder(); - } - } } void freeInbound() { @@ -478,14 +89,6 @@ final class DefaultChannelHandlerContext extends DefaultAttributeMap implements } private void freeInbound0() { - try { - safeFree(inByteBuf); - safeFree(inMsgBuf); - } finally { - flags |= FLAG_FREED_INBOUND; - freeNextInboundBridgeFeeder(); - } - if (next != null) { DefaultChannelHandlerContext nextCtx = findContextInbound(); nextCtx.freeInbound(); @@ -494,90 +97,36 @@ final class DefaultChannelHandlerContext extends DefaultAttributeMap implements // to head (inclusive) to trigger handlerRemoved(). If the removed handler has an outbound buffer, free it, // too. Note that the tail handler is excluded because it's neither an outbound buffer and it doesn't // do anything in handlerRemoved(). - pipeline.tail.prev.freeOutboundAndRemove(); + pipeline.tail.prev.teardown(); } } + void teardownAll() { + pipeline.tail.prev.teardown(); + } + /** Invocation initiated by {@link #freeInbound0()} after freeing all inbound buffers. */ - private void freeOutboundAndRemove() { + private void teardown() { EventExecutor executor = executor(); if (executor.inEventLoop()) { - freeOutboundAndRemove0(); + teardown0(); } else { executor.execute(new Runnable() { @Override public void run() { - freeOutboundAndRemove0(); + teardown0(); } }); } } - private void freeOutboundAndRemove0() { - if (handler instanceof ChannelOperationHandler) { - // Outbound handler - free the buffers / bridge feeders - try { - safeFree(outByteBuf); - safeFree(outMsgBuf); - } finally { - // We also OR FLAG_FREED because at this point we are sure both inbound and outbound were freed. - flags |= FLAG_FREED | FLAG_FREED_OUTBOUND; - freeNextOutboundBridgeFeeder(); - } - } - + private void teardown0() { DefaultChannelHandlerContext prev = this.prev; if (prev != null) { synchronized (pipeline) { - pipeline.remove0(this, false); - } - prev.freeOutboundAndRemove(); - } - } - - private void freeNextInboundBridgeFeeder() { - // Release the bridge feeder - NextBridgeFeeder feeder; - feeder = nextInBridgeFeeder; - if (feeder != null) { - feeder.release(); - nextInBridgeFeeder = null; - } - - // Warn if the bridge has unflushed elements. - if (logger.isWarnEnabled()) { - Queue bridge; - bridge = inBridge; - if (bridge != null && !bridge.isEmpty()) { - logger.warn("inbound bridge not empty - bug?: {}", bridge.size()); - } - } - } - - private void freeNextOutboundBridgeFeeder() { - // Release the bridge feeder - NextBridgeFeeder feeder = nextOutBridgeFeeder; - if (feeder != null) { - feeder.release(); - nextOutBridgeFeeder = null; - } - - // Warn if the bridge has unflushed elements. - if (logger.isWarnEnabled()) { - Queue bridge = outBridge; - if (bridge != null && !bridge.isEmpty()) { - logger.warn("outbound bridge not empty - bug?: {}", bridge.size()); - } - } - } - - private static void safeFree(Buf buf) { - if (buf != null) { - try { - buf.release(); - } catch (Exception e) { - logger.warn("Failed to release a handler buffer.", e); + pipeline.remove0(this); } + prev.teardown(); } } @@ -615,164 +164,6 @@ final class DefaultChannelHandlerContext extends DefaultAttributeMap implements return name; } - @Override - public boolean hasInboundByteBuffer() { - return inByteBuf != null; - } - - @Override - public boolean hasInboundMessageBuffer() { - return inMsgBuf != null; - } - - @Override - public ByteBuf inboundByteBuffer() { - if (inByteBuf == null) { - throw new NoSuchBufferException(String.format( - "the handler '%s' has no inbound byte buffer; it does not implement %s.", - name, ChannelInboundByteHandler.class.getSimpleName())); - } - return inByteBuf; - } - - @Override - @SuppressWarnings("unchecked") - public MessageBuf inboundMessageBuffer() { - if (inMsgBuf == null) { - throw new NoSuchBufferException(String.format( - "the handler '%s' has no inbound message buffer; it does not implement %s.", - name, ChannelInboundMessageHandler.class.getSimpleName())); - } - return (MessageBuf) inMsgBuf; - } - - @Override - public boolean hasOutboundByteBuffer() { - return outByteBuf != null; - } - - @Override - public boolean hasOutboundMessageBuffer() { - return outMsgBuf != null; - } - - @Override - public ByteBuf outboundByteBuffer() { - if (outByteBuf == null) { - throw new NoSuchBufferException(String.format( - "the handler '%s' has no outbound byte buffer; it does not implement %s.", - name, ChannelOutboundByteHandler.class.getSimpleName())); - } - return outByteBuf; - } - - @Override - @SuppressWarnings("unchecked") - public MessageBuf outboundMessageBuffer() { - if (outMsgBuf == null) { - throw new NoSuchBufferException(String.format( - "the handler '%s' has no outbound message buffer; it does not implement %s.", - name, ChannelOutboundMessageHandler.class.getSimpleName())); - } - return (MessageBuf) outMsgBuf; - } - - @Override - public ByteBuf nextInboundByteBuffer() { - DefaultChannelHandlerContext ctx = next; - for (;;) { - if (ctx.hasInboundByteBuffer()) { - Thread currentThread = Thread.currentThread(); - if (ctx.executor().inEventLoop(currentThread)) { - return ctx.inByteBuf; - } - if (executor().inEventLoop(currentThread)) { - return nextInBridgeFeeder().byteBuf; - } - throw new IllegalStateException("nextInboundByteBuffer() called from outside the eventLoop"); - } - ctx = ctx.next; - } - } - - @Override - public MessageBuf nextInboundMessageBuffer() { - DefaultChannelHandlerContext ctx = next; - for (;;) { - if (ctx.hasInboundMessageBuffer()) { - Thread currentThread = Thread.currentThread(); - if (ctx.executor().inEventLoop(currentThread)) { - return ctx.inMsgBuf; - } - if (executor().inEventLoop(currentThread)) { - return nextInBridgeFeeder().msgBuf; - } - throw new IllegalStateException("nextInboundMessageBuffer() called from outside the eventLoop"); - } - ctx = ctx.next; - } - } - - private NextBridgeFeeder nextInBridgeFeeder() { - NextBridgeFeeder feeder = nextInBridgeFeeder; - if (feeder == null) { - feeder = new NextInboundBridgeFeeder(); - if (!NEXT_IN_BRIDGE_FEEDER.compareAndSet(this, null, feeder)) { - feeder.release(); - feeder = nextInBridgeFeeder; - } - } - return feeder; - } - - @Override - public ByteBuf nextOutboundByteBuffer() { - DefaultChannelHandlerContext ctx = prev; - for (;;) { - if (ctx.hasOutboundByteBuffer()) { - Thread currentThread = Thread.currentThread(); - if (ctx.executor().inEventLoop(currentThread)) { - return ctx.outboundByteBuffer(); - } - if (executor().inEventLoop(currentThread)) { - return nextOutBridgeFeeder().byteBuf; - } - throw new IllegalStateException("nextOutboundByteBuffer() called from outside the eventLoop"); - } - ctx = ctx.prev; - } - } - - @Override - public MessageBuf nextOutboundMessageBuffer() { - DefaultChannelHandlerContext ctx = prev; - for (;;) { - if (ctx.hasOutboundMessageBuffer()) { - Thread currentThread = Thread.currentThread(); - if (ctx.executor().inEventLoop(currentThread)) { - return ctx.outboundMessageBuffer(); - } - if (executor().inEventLoop(currentThread)) { - return nextOutBridgeFeeder().msgBuf; - } - throw new IllegalStateException("nextOutboundMessageBuffer() called from outside the eventLoop"); - } - ctx = ctx.prev; - } - } - - private NextBridgeFeeder nextOutBridgeFeeder() { - NextBridgeFeeder feeder = nextOutBridgeFeeder; - if (feeder == null) { - feeder = new NextOutboundBridgeFeeder(); - if (!NEXT_OUT_BRIDGE_FEEDER.compareAndSet(this, null, feeder)) { - feeder.release(); - feeder = nextOutBridgeFeeder; - } - } - return feeder; - } - @Override public ChannelHandlerContext fireChannelRegistered() { final DefaultChannelHandlerContext next = findContextInbound(); @@ -791,14 +182,10 @@ final class DefaultChannelHandlerContext extends DefaultAttributeMap implements } private void invokeChannelRegistered() { - callDepth ++; try { - ((ChannelStateHandler) handler()).channelRegistered(this); + ((ChannelInboundHandler) handler()).channelRegistered(this); } catch (Throwable t) { notifyHandlerException(t); - } finally { - callDepth --; - freeAllIfRemoved(); } } @@ -820,13 +207,10 @@ final class DefaultChannelHandlerContext extends DefaultAttributeMap implements } private void invokeChannelUnregistered() { - callDepth ++; try { - ((ChannelStateHandler) handler()).channelUnregistered(this); + ((ChannelInboundHandler) handler()).channelUnregistered(this); } catch (Throwable t) { notifyHandlerException(t); - } finally { - callDepth --; } } @@ -848,14 +232,10 @@ final class DefaultChannelHandlerContext extends DefaultAttributeMap implements } private void invokeChannelActive() { - callDepth ++; try { - ((ChannelStateHandler) handler()).channelActive(this); + ((ChannelInboundHandler) handler()).channelActive(this); } catch (Throwable t) { notifyHandlerException(t); - } finally { - callDepth --; - freeAllIfRemoved(); } } @@ -877,14 +257,10 @@ final class DefaultChannelHandlerContext extends DefaultAttributeMap implements } private void invokeChannelInactive() { - callDepth ++; try { - ((ChannelStateHandler) handler()).channelInactive(this); + ((ChannelInboundHandler) handler()).channelInactive(this); } catch (Throwable t) { notifyHandlerException(t); - } finally { - callDepth --; - freeAllIfRemoved(); } } @@ -921,7 +297,6 @@ final class DefaultChannelHandlerContext extends DefaultAttributeMap implements private void invokeExceptionCaught0(Throwable cause) { ChannelHandler handler = handler(); - callDepth ++; try { handler.exceptionCaught(this, cause); } catch (Throwable t) { @@ -930,9 +305,6 @@ final class DefaultChannelHandlerContext extends DefaultAttributeMap implements "An exception was thrown by a user handler's " + "exceptionCaught() method while handling the following exception:", cause); } - } finally { - callDepth --; - freeAllIfRemoved(); } } @@ -958,108 +330,54 @@ final class DefaultChannelHandlerContext extends DefaultAttributeMap implements } private void invokeUserEventTriggered(Object event) { - ChannelStateHandler handler = (ChannelStateHandler) handler(); - - callDepth ++; + ChannelInboundHandler handler = (ChannelInboundHandler) handler(); try { handler.userEventTriggered(this, event); } catch (Throwable t) { notifyHandlerException(t); - } finally { - callDepth --; - freeAllIfRemoved(); } } @Override - public ChannelHandlerContext fireInboundBufferUpdated() { - EventExecutor executor = executor(); + public ChannelHandlerContext fireMessageReceived(Object msg) { + if (msg == null) { + throw new NullPointerException("msg"); + } + return fireMessageReceived(MessageList.newInstance(msg)); + } + + @Override + public ChannelHandlerContext fireMessageReceived(final MessageList msgs) { + if (msgs == null) { + throw new NullPointerException("msgs"); + } + + if (msgs.isEmpty()) { + msgs.recycle(); + return this; + } + + final DefaultChannelHandlerContext next = findContextInbound(); + EventExecutor executor = next.executor(); if (executor.inEventLoop()) { - fireInboundBufferUpdated0(findContextInbound()); + next.invokeMessageReceived(msgs); } else { - Runnable task = fireInboundBufferUpdated0Task; - if (task == null) { - fireInboundBufferUpdated0Task = task = new Runnable() { - @Override - public void run() { - fireInboundBufferUpdated0(findContextInbound()); - } - }; - } - executor.execute(task); + executor.execute(new Runnable() { + @Override + public void run() { + next.invokeMessageReceived(msgs); + } + }); } return this; } - private void fireInboundBufferUpdated0(final DefaultChannelHandlerContext next) { - feedNextInBridge(); - // This comparison is safe because this method is always executed from the executor. - if (next.executor == executor) { - next.invokeInboundBufferUpdated(); - } else { - Runnable task = next.invokeInboundBufferUpdatedTask; - if (task == null) { - next.invokeInboundBufferUpdatedTask = task = new Runnable() { - @Override - public void run() { - next.invokeInboundBufferUpdated(); - } - }; - } - next.executor().execute(task); - } - } - - private void feedNextInBridge() { - NextBridgeFeeder feeder = nextInBridgeFeeder; - if (feeder != null) { - feeder.feed(); - } - } - - private void invokeInboundBufferUpdated() { - if (isInboundFreed()) { - return; - } - - ChannelStateHandler handler = (ChannelStateHandler) handler(); - if (handler instanceof ChannelInboundHandler) { - for (;;) { - callDepth ++; - try { - boolean flushedAll = flushInboundBridge(); - handler.inboundBufferUpdated(this); - if (flushedAll) { - break; - } - } catch (Throwable t) { - notifyHandlerException(t); - break; - } finally { - callDepth --; - if (handler instanceof ChannelInboundByteHandler && !isInboundFreed()) { - try { - ((ChannelInboundByteHandler) handler).discardInboundReadBytes(this); - } catch (Throwable t) { - notifyHandlerException(t); - } - } - freeAllIfRemoved(); - } - - if (isInboundFreed()) { - break; - } - } - } else { - callDepth ++; - try { - handler.inboundBufferUpdated(this); - } catch (Throwable t) { - notifyHandlerException(t); - } finally { - callDepth --; - } + private void invokeMessageReceived(MessageList msgs) { + ChannelInboundHandler handler = (ChannelInboundHandler) handler(); + try { + handler.messageReceived(this, msgs.cast()); + } catch (Throwable t) { + notifyHandlerException(t); } } @@ -1085,14 +403,39 @@ final class DefaultChannelHandlerContext extends DefaultAttributeMap implements } private void invokeChannelReadSuspended() { - callDepth ++; try { - ((ChannelStateHandler) handler()).channelReadSuspended(this); + ((ChannelInboundHandler) handler()).channelReadSuspended(this); + } catch (Throwable t) { + notifyHandlerException(t); + } + } + + @Override + public ChannelHandlerContext fireChannelWritabilityChanged() { + final DefaultChannelHandlerContext next = findContextInbound(); + EventExecutor executor = next.executor(); + if (executor.inEventLoop()) { + next.invokeChannelWritabilityChanged(); + } else { + Runnable task = next.invokeChannelWritableStateChangedTask; + if (task == null) { + next.invokeChannelWritableStateChangedTask = task = new Runnable() { + @Override + public void run() { + next.invokeChannelReadSuspended(); + } + }; + } + executor.execute(task); + } + return this; + } + + private void invokeChannelWritabilityChanged() { + try { + ((ChannelInboundHandler) handler()).channelWritabilityChanged(this); } catch (Throwable t) { notifyHandlerException(t); - } finally { - callDepth --; - freeAllIfRemoved(); } } @@ -1127,13 +470,13 @@ final class DefaultChannelHandlerContext extends DefaultAttributeMap implements } @Override - public ChannelFuture flush() { - return flush(newPromise()); + public ChannelFuture write(Object msg) { + return write(msg, newPromise()); } @Override - public ChannelFuture write(Object message) { - return write(message, newPromise()); + public ChannelFuture write(MessageList msgs) { + return write(msgs, newPromise()); } @Override @@ -1161,14 +504,10 @@ final class DefaultChannelHandlerContext extends DefaultAttributeMap implements } private void invokeBind0(SocketAddress localAddress, ChannelPromise promise) { - callDepth ++; try { - ((ChannelOperationHandler) handler()).bind(this, localAddress, promise); + ((ChannelOutboundHandler) handler()).bind(this, localAddress, promise); } catch (Throwable t) { - notifyHandlerException(t); - } finally { - callDepth --; - freeAllIfRemoved(); + notifyHandlerException(t, promise); } } @@ -1204,14 +543,10 @@ final class DefaultChannelHandlerContext extends DefaultAttributeMap implements } private void invokeConnect0(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) { - callDepth ++; try { - ((ChannelOperationHandler) handler()).connect(this, remoteAddress, localAddress, promise); + ((ChannelOutboundHandler) handler()).connect(this, remoteAddress, localAddress, promise); } catch (Throwable t) { - notifyHandlerException(t); - } finally { - callDepth --; - freeAllIfRemoved(); + notifyHandlerException(t, promise); } } @@ -1245,14 +580,10 @@ final class DefaultChannelHandlerContext extends DefaultAttributeMap implements } private void invokeDisconnect0(ChannelPromise promise) { - callDepth ++; try { - ((ChannelOperationHandler) handler()).disconnect(this, promise); + ((ChannelOutboundHandler) handler()).disconnect(this, promise); } catch (Throwable t) { - notifyHandlerException(t); - } finally { - callDepth --; - freeAllIfRemoved(); + notifyHandlerException(t, promise); } } @@ -1279,14 +610,10 @@ final class DefaultChannelHandlerContext extends DefaultAttributeMap implements } private void invokeClose0(ChannelPromise promise) { - callDepth ++; try { - ((ChannelOperationHandler) handler()).close(this, promise); + ((ChannelOutboundHandler) handler()).close(this, promise); } catch (Throwable t) { - notifyHandlerException(t); - } finally { - callDepth --; - freeAllIfRemoved(); + notifyHandlerException(t, promise); } } @@ -1313,14 +640,10 @@ final class DefaultChannelHandlerContext extends DefaultAttributeMap implements } private void invokeDeregister0(ChannelPromise promise) { - callDepth ++; try { - ((ChannelOperationHandler) handler()).deregister(this, promise); + ((ChannelOutboundHandler) handler()).deregister(this, promise); } catch (Throwable t) { - notifyHandlerException(t); - } finally { - callDepth --; - freeAllIfRemoved(); + notifyHandlerException(t, promise); } } @@ -1348,124 +671,37 @@ final class DefaultChannelHandlerContext extends DefaultAttributeMap implements } private void invokeRead0() { - callDepth ++; try { - ((ChannelOperationHandler) handler()).read(this); + ((ChannelOutboundHandler) handler()).read(this); } catch (Throwable t) { notifyHandlerException(t); - } finally { - callDepth --; - freeAllIfRemoved(); } } @Override - public ChannelFuture flush(final ChannelPromise promise) { + public ChannelFuture write(Object msg, final ChannelPromise promise) { + if (msg == null) { + throw new NullPointerException("msg"); + } + return write(MessageList.newInstance(msg), promise); + } + + @Override + public ChannelFuture write(MessageList msgs, ChannelPromise promise) { + return findContextOutbound().invokeWrite(msgs, promise); + } + + private ChannelFuture invokeWrite(final MessageList msgs, final ChannelPromise promise) { validateFuture(promise, true); - EventExecutor executor = executor(); - Thread currentThread = Thread.currentThread(); - if (executor.inEventLoop(currentThread)) { - invokePrevFlush(promise, currentThread, findContextOutbound()); - } else { - executor.execute(new Runnable() { - @Override - public void run() { - invokePrevFlush(promise, Thread.currentThread(), findContextOutbound()); - } - }); - } - - return promise; - } - - private void invokePrevFlush(ChannelPromise promise, Thread currentThread, DefaultChannelHandlerContext prev) { - feedNextOutBridge(); - prev.invokeFlush(promise, currentThread); - } - - private void feedNextOutBridge() { - NextBridgeFeeder feeder = nextOutBridgeFeeder; - if (feeder != null) { - feeder.feed(); - } - } - - private ChannelFuture invokeFlush(final ChannelPromise promise, Thread currentThread) { - EventExecutor executor = executor(); - if (executor.inEventLoop(currentThread)) { - invokeFlush0(promise); - } else { - executor.execute(new Runnable() { - @Override - public void run() { - invokeFlush0(promise); - } - }); - } - - return promise; - } - - private void invokeFlush0(ChannelPromise promise) { - if (isOutboundFreed()) { - promise.setFailure(new ChannelPipelineException( - "Unable to flush as outbound buffer of next handler was freed already")); - return; - } - - Channel channel = channel(); - if (!channel.isActive() && !channel.isRegistered()) { - promise.setFailure(new ClosedChannelException()); - return; - } - - ChannelOperationHandler handler = (ChannelOperationHandler) handler(); - if (handler instanceof ChannelOutboundHandler) { - flushOutboundBridge(); - } - - callDepth ++; - try { - handler.flush(this, promise); - } catch (Throwable t) { - notifyHandlerException(t); - } finally { - callDepth --; - if (handler instanceof ChannelOutboundByteHandler && !isOutboundFreed()) { - try { - ((ChannelOutboundByteHandler) handler).discardOutboundReadBytes(this); - } catch (Throwable t) { - notifyHandlerException(t); - } - } - freeAllIfRemoved(); - } - } - - @Override - public ChannelFuture sendFile(FileRegion region) { - return sendFile(region, newPromise()); - } - - @Override - public ChannelFuture sendFile(FileRegion region, ChannelPromise promise) { - if (region == null) { - throw new NullPointerException("region"); - } - validateFuture(promise, true); - return findContextOutbound().invokeSendFile(region, promise); - } - - private ChannelFuture invokeSendFile(final FileRegion region, final ChannelPromise promise) { EventExecutor executor = executor(); if (executor.inEventLoop()) { - invokeSendFile0(region, promise); + invokeWrite0(msgs, promise); } else { executor.execute(new Runnable() { @Override public void run() { - invokeSendFile0(region, promise); + invokeWrite0(msgs, promise); } }); } @@ -1473,124 +709,27 @@ final class DefaultChannelHandlerContext extends DefaultAttributeMap implements return promise; } - private void invokeSendFile0(FileRegion region, ChannelPromise promise) { - ChannelOperationHandler handler = (ChannelOperationHandler) handler(); - if (handler instanceof ChannelOutboundHandler) { - flushOutboundBridge(); - } - - callDepth ++; + private void invokeWrite0(MessageList msgs, ChannelPromise promise) { + ChannelOutboundHandler handler = (ChannelOutboundHandler) handler(); try { - handler.sendFile(this, region, promise); + handler.write(this, msgs.cast(), promise); } catch (Throwable t) { - notifyHandlerException(t); - } finally { - callDepth --; - freeAllIfRemoved(); + notifyHandlerException(t, promise); } } - @Override - public ChannelFuture write(final Object message, final ChannelPromise promise) { - if (message instanceof FileRegion) { - return sendFile((FileRegion) message, promise); - } - - if (message == null) { - throw new NullPointerException("message"); - } - validateFuture(promise, true); - - DefaultChannelHandlerContext ctx = prev; - EventExecutor executor = executor(); - final boolean msgBuf; - - if (message instanceof ByteBuf) { - for (;;) { - if (ctx.hasOutboundByteBuffer()) { - msgBuf = false; - executor = ctx.executor(); - break; - } - - if (ctx.hasOutboundMessageBuffer()) { - msgBuf = true; - executor = ctx.executor(); - break; - } - - DefaultChannelHandlerContext prev = ctx.prev; - if (prev == null) { - assert ctx == pipeline.head; - // this means we reached end of pipeline but the head-handler was not yet init. - // in this case init it now and schedule the write via the executor so it is - // done after fireChannelRegistered() completes - // - // See https://github.com/netty/netty/issues/1385 - ctx.initHeadHandler(); - - executor.execute(new Runnable() { - @Override - public void run() { - write(message, promise); - } - }); - return promise; - } else { - ctx = prev; - } + private void notifyHandlerException(Throwable cause, ChannelPromise promise) { + // only try to fail the promise if its not a VoidChannelPromise, as + // the VoidChannelPromise would also fire the cause through the pipeline + if (!(promise instanceof VoidChannelPromise) && !promise.tryFailure(cause)) { + if (logger.isWarnEnabled()) { + logger.warn( + "Failed to fail the promise", cause); } - } else { - msgBuf = true; - for (;;) { - if (ctx.hasOutboundMessageBuffer()) { - executor = ctx.executor(); - break; - } - - ctx = ctx.prev; - } - } - - if (executor.inEventLoop()) { - ctx.write0(message, promise, msgBuf); - return promise; - } - - final DefaultChannelHandlerContext ctx0 = ctx; - executor.execute(new Runnable() { - @Override - public void run() { - ctx0.write0(message, promise, msgBuf); - } - }); - - return promise; - } - - private void write0(Object message, ChannelPromise promise, boolean msgBuf) { - Channel channel = channel(); - if (!channel.isRegistered() && !channel.isActive()) { - promise.setFailure(new ClosedChannelException()); return; } - if (isOutboundFreed()) { - promise.setFailure(new ChannelPipelineException( - "Unable to write as outbound buffer of next handler was freed already")); - return; - } - if (msgBuf) { - outboundMessageBuffer().add(message); - } else { - ByteBuf buf = (ByteBuf) message; - try { - outboundByteBuffer().writeBytes(buf, buf.readerIndex(), buf.readableBytes()); - } finally { - buf.release(); - } - } - invokeFlush0(promise); + notifyHandlerException(cause); } private void notifyHandlerException(Throwable cause) { @@ -1670,170 +809,21 @@ final class DefaultChannelHandlerContext extends DefaultAttributeMap implements } private DefaultChannelHandlerContext findContextInbound() { - DefaultChannelHandlerContext ctx = this; - do { - ctx = ctx.next; - } while (!(ctx.handler() instanceof ChannelStateHandler)); - return ctx; - } - - @Override - public BufType nextInboundBufferType() { DefaultChannelHandlerContext ctx = this; do { ctx = ctx.next; } while (!(ctx.handler() instanceof ChannelInboundHandler)); - - if (ctx.handler() instanceof ChannelInboundByteHandler) { - return BufType.BYTE; - } else { - return BufType.MESSAGE; - } - } - - @Override - public BufType nextOutboundBufferType() { - DefaultChannelHandlerContext ctx = this; - do { - ctx = ctx.prev; - } while (!(ctx.handler() instanceof ChannelOutboundHandler)); - - if (ctx.handler() instanceof ChannelOutboundByteHandler) { - return BufType.BYTE; - } else { - return BufType.MESSAGE; - } + return ctx; } private DefaultChannelHandlerContext findContextOutbound() { DefaultChannelHandlerContext ctx = this; do { ctx = ctx.prev; - } while (!(ctx.handler() instanceof ChannelOperationHandler)); + } while (!(ctx.handler() instanceof ChannelOutboundHandler)); return ctx; } - private abstract class NextBridgeFeeder { - final MessageBuf msgBuf; - final ByteBuf byteBuf; - - protected NextBridgeFeeder() { - msgBuf = Unpooled.messageBuffer(); - byteBuf = ChannelHandlerUtil.allocate(DefaultChannelHandlerContext.this); - } - - final void feed() { - int dataLen = byteBuf.readableBytes(); - if (dataLen != 0) { - ByteBuf data; - if (byteBuf.isDirect()) { - data = alloc().directBuffer(dataLen, dataLen); - } else { - data = alloc().heapBuffer(dataLen, dataLen); - } - - byteBuf.readBytes(data).discardSomeReadBytes(); - nextByteBridge().add(data); - } - - if (!msgBuf.isEmpty()) { - Object[] data = msgBuf.toArray(); - msgBuf.clear(); - nextMessageBridge().add(data); - } - } - - final void release() { - byteBuf.release(); - msgBuf.release(); - } - - protected abstract Queue nextByteBridge(); - protected abstract Queue nextMessageBridge(); - } - - private final class NextInboundBridgeFeeder extends NextBridgeFeeder { - @Override - protected Queue nextByteBridge() { - DefaultChannelHandlerContext ctx = next; - for (;;) { - if (ctx.hasInboundByteBuffer()) { - break; - } - ctx = ctx.next; - } - - return bridge(ctx); - } - - @Override - protected Queue nextMessageBridge() { - DefaultChannelHandlerContext ctx = next; - for (;;) { - if (ctx.hasInboundMessageBuffer()) { - break; - } - ctx = ctx.next; - } - - return bridge(ctx); - } - - private Queue bridge(DefaultChannelHandlerContext ctx) { - Queue bridge = ctx.inBridge; - if (bridge == null) { - Queue newBridge = new ConcurrentLinkedQueue(); - if (IN_BRIDGE_UPDATER.compareAndSet(ctx, null, newBridge)) { - bridge = newBridge; - } else { - bridge = ctx.inBridge; - } - } - return bridge; - } - } - - private final class NextOutboundBridgeFeeder extends NextBridgeFeeder { - @Override - protected Queue nextByteBridge() { - DefaultChannelHandlerContext ctx = prev; - for (;;) { - if (ctx.hasOutboundByteBuffer()) { - break; - } - ctx = ctx.prev; - } - - return bridge(ctx); - } - - @Override - protected Queue nextMessageBridge() { - DefaultChannelHandlerContext ctx = prev; - for (;;) { - if (ctx.hasOutboundMessageBuffer()) { - break; - } - ctx = ctx.prev; - } - - return bridge(ctx); - } - - private Queue bridge(DefaultChannelHandlerContext ctx) { - Queue bridge = ctx.outBridge; - if (bridge == null) { - Queue newBridge = new ConcurrentLinkedQueue(); - if (OUT_BRIDGE_UPDATER.compareAndSet(ctx, null, newBridge)) { - bridge = newBridge; - } else { - bridge = ctx.outBridge; - } - } - return bridge; - } - } - @Override public ChannelPromise voidPromise() { return channel.voidPromise(); diff --git a/transport/src/main/java/io/netty/channel/DefaultChannelPipeline.java b/transport/src/main/java/io/netty/channel/DefaultChannelPipeline.java index 74eb88cf6e..410d815468 100755 --- a/transport/src/main/java/io/netty/channel/DefaultChannelPipeline.java +++ b/transport/src/main/java/io/netty/channel/DefaultChannelPipeline.java @@ -15,11 +15,7 @@ */ package io.netty.channel; -import io.netty.buffer.Buf; -import io.netty.buffer.BufUtil; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.MessageBuf; -import io.netty.buffer.Unpooled; +import io.netty.buffer.ByteBufUtil; import io.netty.channel.Channel.Unsafe; import io.netty.util.concurrent.EventExecutor; import io.netty.util.concurrent.EventExecutorGroup; @@ -77,21 +73,10 @@ final class DefaultChannelPipeline implements ChannelPipeline { this.channel = channel; TailHandler tailHandler = new TailHandler(); - tail = new DefaultChannelHandlerContext(this, generateName(tailHandler), tailHandler); + tail = new DefaultChannelHandlerContext(this, null, generateName(tailHandler), tailHandler); - HeadHandler headHandler; - switch (channel.metadata().bufferType()) { - case BYTE: - headHandler = new ByteHeadHandler(channel.unsafe()); - break; - case MESSAGE: - headHandler = new MessageHeadHandler(channel.unsafe()); - break; - default: - throw new Error("unknown buffer type: " + channel.metadata().bufferType()); - } - - head = new DefaultChannelHandlerContext(this, generateName(headHandler), headHandler); + HeadHandler headHandler = new HeadHandler(channel.unsafe()); + head = new DefaultChannelHandlerContext(this, null, generateName(headHandler), headHandler); head.next = tail; tail.prev = head; @@ -331,14 +316,14 @@ final class DefaultChannelPipeline implements ChannelPipeline { synchronized (this) { if (!ctx.channel().isRegistered() || ctx.executor().inEventLoop()) { - remove0(ctx, true); + remove0(ctx); return ctx; } else { future = ctx.executor().submit(new Runnable() { @Override public void run() { synchronized (DefaultChannelPipeline.this) { - remove0(ctx, true); + remove0(ctx); } } }); @@ -354,14 +339,13 @@ final class DefaultChannelPipeline implements ChannelPipeline { return context; } - void remove0(DefaultChannelHandlerContext ctx, boolean forward) { + void remove0(DefaultChannelHandlerContext ctx) { DefaultChannelHandlerContext prev = ctx.prev; DefaultChannelHandlerContext next = ctx.next; prev.next = next; next.prev = prev; name2ctx.remove(ctx.name()); - - callHandlerRemoved(ctx, prev, next, forward); + callHandlerRemoved(ctx); } @Override @@ -458,11 +442,15 @@ final class DefaultChannelPipeline implements ChannelPipeline { } name2ctx.put(newName, newCtx); + // update the reference to the replacement so forward of buffered content will work correctly + oldCtx.prev = newCtx; + oldCtx.next = newCtx; + // Invoke newHandler.handlerAdded() first (i.e. before oldHandler.handlerRemoved() is invoked) // because callHandlerRemoved() will trigger inboundBufferUpdated() or flush() on newHandler and those // event handlers must be called after handlerAdded(). callHandlerAdded(newCtx); - callHandlerRemoved(oldCtx, newCtx, newCtx, true); + callHandlerRemoved(oldCtx); } private static void checkMultiplicity(ChannelHandlerContext ctx) { @@ -517,40 +505,23 @@ final class DefaultChannelPipeline implements ChannelPipeline { } } - private void callHandlerRemoved( - final DefaultChannelHandlerContext ctx, final DefaultChannelHandlerContext ctxPrev, - final DefaultChannelHandlerContext ctxNext, final boolean forward) { + private void callHandlerRemoved(final DefaultChannelHandlerContext ctx) { if (ctx.channel().isRegistered() && !ctx.executor().inEventLoop()) { ctx.executor().execute(new Runnable() { @Override public void run() { - callHandlerRemoved0(ctx, ctxPrev, ctxNext, forward); + callHandlerRemoved0(ctx); } }); return; } - callHandlerRemoved0(ctx, ctxPrev, ctxNext, forward); + callHandlerRemoved0(ctx); } - private void callHandlerRemoved0( - final DefaultChannelHandlerContext ctx, DefaultChannelHandlerContext ctxPrev, - DefaultChannelHandlerContext ctxNext, boolean forward) { - - final ChannelHandler handler = ctx.handler(); - - // Finish removal by forwarding buffer content and freeing the buffers. - if (forward) { - try { - ctx.forwardBufferContentAndFree(ctxPrev, ctxNext); - } catch (Throwable t) { - fireExceptionCaught(new ChannelPipelineException( - "failed to forward buffer content of " + ctx.handler().getClass().getName(), t)); - } - } - + private void callHandlerRemoved0(final DefaultChannelHandlerContext ctx) { // Notify the complete removal. try { - handler.handlerRemoved(ctx); + ctx.handler().handlerRemoved(ctx); } catch (Throwable t) { fireExceptionCaught(new ChannelPipelineException( ctx.handler().getClass().getName() + ".handlerRemoved() has thrown an exception.", t)); @@ -753,31 +724,8 @@ final class DefaultChannelPipeline implements ChannelPipeline { return buf.toString(); } - @Override - @SuppressWarnings("unchecked") - public MessageBuf inboundMessageBuffer() { - return (MessageBuf) head.nextInboundMessageBuffer(); - } - - @Override - public ByteBuf inboundByteBuffer() { - return head.nextInboundByteBuffer(); - } - - @Override - @SuppressWarnings("unchecked") - public MessageBuf outboundMessageBuffer() { - return (MessageBuf) tail.nextOutboundMessageBuffer(); - } - - @Override - public ByteBuf outboundByteBuffer() { - return tail.nextOutboundByteBuffer(); - } - @Override public ChannelPipeline fireChannelRegistered() { - head.initHeadHandler(); head.fireChannelRegistered(); return this; } @@ -795,7 +743,6 @@ final class DefaultChannelPipeline implements ChannelPipeline { @Override public ChannelPipeline fireChannelActive() { - head.initHeadHandler(); head.fireChannelActive(); if (channel.config().isAutoRead()) { @@ -828,8 +775,14 @@ final class DefaultChannelPipeline implements ChannelPipeline { } @Override - public ChannelPipeline fireInboundBufferUpdated() { - head.fireInboundBufferUpdated(); + public ChannelPipeline fireMessageReceived(Object msg) { + head.fireMessageReceived(msg); + return this; + } + + @Override + public ChannelPipeline fireMessageReceived(MessageList msgs) { + head.fireMessageReceived(msgs); return this; } @@ -842,6 +795,12 @@ final class DefaultChannelPipeline implements ChannelPipeline { return this; } + @Override + public ChannelPipeline fireChannelWritabilityChanged() { + head.fireChannelWritabilityChanged(); + return this; + } + @Override public ChannelFuture bind(SocketAddress localAddress) { return tail.bind(localAddress); @@ -873,13 +832,13 @@ final class DefaultChannelPipeline implements ChannelPipeline { } @Override - public ChannelFuture flush() { - return tail.flush(); + public ChannelFuture write(Object msg) { + return tail.write(msg); } @Override - public ChannelFuture write(Object message) { - return tail.write(message); + public ChannelFuture write(MessageList msgs) { + return tail.write(msgs); } @Override @@ -918,23 +877,13 @@ final class DefaultChannelPipeline implements ChannelPipeline { } @Override - public ChannelFuture flush(ChannelPromise promise) { - return tail.flush(promise); + public ChannelFuture write(Object msg, ChannelPromise promise) { + return tail.write(msg, promise); } @Override - public ChannelFuture sendFile(FileRegion region) { - return tail.sendFile(region); - } - - @Override - public ChannelFuture sendFile(FileRegion region, ChannelPromise promise) { - return tail.sendFile(region, promise); - } - - @Override - public ChannelFuture write(Object message, ChannelPromise promise) { - return tail.write(message, promise); + public ChannelFuture write(MessageList msgs, ChannelPromise promise) { + return tail.write(msgs, promise); } private void checkDuplicateName(String name) { @@ -973,9 +922,6 @@ final class DefaultChannelPipeline implements ChannelPipeline { // A special catch-all handler that handles both bytes and messages. static final class TailHandler implements ChannelInboundHandler { - final ByteBuf byteSink = Unpooled.buffer(0); - final MessageBuf msgSink = Unpooled.messageBuffer(0); - @Override public void channelRegistered(ChannelHandlerContext ctx) throws Exception { } @@ -991,6 +937,9 @@ final class DefaultChannelPipeline implements ChannelPipeline { @Override public void channelReadSuspended(ChannelHandlerContext ctx) throws Exception { } + @Override + public void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception { } + @Override public void handlerAdded(ChannelHandlerContext ctx) throws Exception { } @@ -1008,86 +957,54 @@ final class DefaultChannelPipeline implements ChannelPipeline { } @Override - public Buf newInboundBuffer(ChannelHandlerContext ctx) throws Exception { - throw new Error(); - } - - @Override - public void inboundBufferUpdated(ChannelHandlerContext ctx) throws Exception { - int byteSinkSize = byteSink.readableBytes(); - if (byteSinkSize != 0) { - byteSink.clear(); - logger.warn( - "Discarded {} inbound byte(s) that reached at the tail of the pipeline. " + - "Please check your pipeline configuration.", byteSinkSize); + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + int length = msgs.size(); + if (length == 0) { + return; } - int msgSinkSize = msgSink.size(); - if (msgSinkSize != 0) { - MessageBuf in = msgSink; - for (;;) { - Object m = in.poll(); - if (m == null) { - break; - } - BufUtil.release(m); - logger.debug( - "Discarded inbound message {} that reached at the tail of the pipeline. " + - "Please check your pipeline configuration.", m); - } - logger.warn( - "Discarded {} inbound message(s) that reached at the tail of the pipeline. " + - "Please check your pipeline configuration.", msgSinkSize); + for (int i = 0; i < length; i ++) { + Object m = msgs.get(i); + logger.debug( + "Discarded inbound message {} that reached at the tail of the pipeline. " + + "Please check your pipeline configuration.", m); + + ByteBufUtil.release(m); } + + logger.warn( + "Discarded {} inbound message(s) that reached at the tail of the pipeline. " + + "Please check your pipeline configuration.", length); } } - abstract static class HeadHandler implements ChannelOutboundHandler { + static final class HeadHandler implements ChannelOutboundHandler { protected final Unsafe unsafe; - ByteBuf byteSink; - MessageBuf msgSink; - boolean initialized; protected HeadHandler(Unsafe unsafe) { this.unsafe = unsafe; } - void init(ChannelHandlerContext ctx) { - assert !initialized; - switch (ctx.channel().metadata().bufferType()) { - case BYTE: - byteSink = ctx.alloc().ioBuffer(); - msgSink = Unpooled.messageBuffer(0); - break; - case MESSAGE: - byteSink = Unpooled.buffer(0); - msgSink = Unpooled.messageBuffer(); - break; - default: - throw new Error(); - } - } - @Override - public final void handlerAdded(ChannelHandlerContext ctx) throws Exception { + public void handlerAdded(ChannelHandlerContext ctx) throws Exception { // NOOP } @Override - public final void handlerRemoved(ChannelHandlerContext ctx) throws Exception { + public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { // NOOP } @Override - public final void bind( + public void bind( ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception { unsafe.bind(localAddress, promise); } @Override - public final void connect( + public void connect( ChannelHandlerContext ctx, SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) throws Exception { @@ -1095,34 +1012,29 @@ final class DefaultChannelPipeline implements ChannelPipeline { } @Override - public final void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { + public void disconnect(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { unsafe.disconnect(promise); } @Override - public final void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { + public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { unsafe.close(promise); } @Override - public final void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { + public void deregister(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { unsafe.deregister(promise); } @Override - public final void read(ChannelHandlerContext ctx) { + public void read(ChannelHandlerContext ctx) { unsafe.beginRead(); } @Override - public final void sendFile( - ChannelHandlerContext ctx, FileRegion region, ChannelPromise promise) throws Exception { - unsafe.sendFile(region, promise); - } - - @Override - public final Buf newOutboundBuffer(ChannelHandlerContext ctx) throws Exception { - throw new Error(); + public void write( + ChannelHandlerContext ctx, MessageList msgs, ChannelPromise promise) throws Exception { + unsafe.write(msgs, promise); } @Override @@ -1130,62 +1042,4 @@ final class DefaultChannelPipeline implements ChannelPipeline { ctx.fireExceptionCaught(cause); } } - - private static final class ByteHeadHandler extends HeadHandler { - - private ByteHeadHandler(Unsafe unsafe) { - super(unsafe); - } - - @Override - public void flush(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { - int discardedMessages = 0; - MessageBuf in = msgSink; - for (;;) { - Object m = in.poll(); - if (m == null) { - break; - } - - if (m instanceof ByteBuf) { - ByteBuf src = (ByteBuf) m; - byteSink.writeBytes(src, src.readerIndex(), src.readableBytes()); - } else { - logger.debug( - "Discarded outbound message {} that reached at the head of the pipeline. " + - "Please check your pipeline configuration.", m); - discardedMessages ++; - } - - BufUtil.release(m); - } - - if (discardedMessages != 0) { - logger.warn( - "Discarded {} outbound message(s) that reached at the head of the pipeline. " + - "Please check your pipeline configuration.", discardedMessages); - } - - unsafe.flush(promise); - } - } - - private static final class MessageHeadHandler extends HeadHandler { - - private MessageHeadHandler(Unsafe unsafe) { - super(unsafe); - } - - @Override - public void flush(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { - int byteSinkSize = byteSink.readableBytes(); - if (byteSinkSize != 0) { - byteSink.clear(); - logger.warn( - "Discarded {} outbound byte(s) that reached at the head of the pipeline. " + - "Please check your pipeline configuration.", byteSinkSize); - } - unsafe.flush(promise); - } - } } diff --git a/transport/src/main/java/io/netty/channel/DefaultFileRegion.java b/transport/src/main/java/io/netty/channel/DefaultFileRegion.java index dbc84db428..c0a20cf154 100644 --- a/transport/src/main/java/io/netty/channel/DefaultFileRegion.java +++ b/transport/src/main/java/io/netty/channel/DefaultFileRegion.java @@ -33,6 +33,7 @@ public class DefaultFileRegion extends AbstractReferenceCounted implements FileR private final FileChannel file; private final long position; private final long count; + private long transfered; /** * Create a new instance @@ -66,6 +67,11 @@ public class DefaultFileRegion extends AbstractReferenceCounted implements FileR return count; } + @Override + public long transfered() { + return transfered; + } + @Override public long transferTo(WritableByteChannel target, long position) throws IOException { long count = this.count - position; @@ -78,7 +84,11 @@ public class DefaultFileRegion extends AbstractReferenceCounted implements FileR return 0L; } - return file.transferTo(this.position + position, count, target); + long written = file.transferTo(this.position + position, count, target); + if (written > 0) { + transfered += written; + } + return written; } @Override diff --git a/transport/src/main/java/io/netty/channel/FileRegion.java b/transport/src/main/java/io/netty/channel/FileRegion.java index 5201939cbe..a643398012 100644 --- a/transport/src/main/java/io/netty/channel/FileRegion.java +++ b/transport/src/main/java/io/netty/channel/FileRegion.java @@ -59,6 +59,11 @@ public interface FileRegion extends ReferenceCounted { */ long position(); + /** + * Return the bytes which was transfered already + */ + long transfered(); + /** * Returns the number of bytes to transfer. */ diff --git a/transport/src/main/java/io/netty/channel/FixedRecvByteBufAllocator.java b/transport/src/main/java/io/netty/channel/FixedRecvByteBufAllocator.java new file mode 100644 index 0000000000..b4087bae66 --- /dev/null +++ b/transport/src/main/java/io/netty/channel/FixedRecvByteBufAllocator.java @@ -0,0 +1,69 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.channel; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufAllocator; + + +/** + * The {@link RecvByteBufAllocator} that always yields the same buffer + * size prediction. This predictor ignores the feed back from the I/O thread. + */ +public class FixedRecvByteBufAllocator implements RecvByteBufAllocator { + + private static final class HandleImpl implements Handle { + + private final int bufferSize; + + HandleImpl(int bufferSize) { + this.bufferSize = bufferSize; + } + + @Override + public ByteBuf allocate(ByteBufAllocator alloc) { + return alloc.ioBuffer(bufferSize); + } + + @Override + public int guess() { + return bufferSize; + } + + @Override + public void record(int actualReadBytes) { } + } + + private final Handle handle; + + /** + * Creates a new predictor that always returns the same prediction of + * the specified buffer size. + */ + public FixedRecvByteBufAllocator(int bufferSize) { + if (bufferSize <= 0) { + throw new IllegalArgumentException( + "bufferSize must greater than 0: " + bufferSize); + } + + handle = new HandleImpl(bufferSize); + } + + @Override + public Handle newHandle() { + return handle; + } +} diff --git a/transport/src/main/java/io/netty/channel/MessageList.java b/transport/src/main/java/io/netty/channel/MessageList.java new file mode 100644 index 0000000000..50023b2546 --- /dev/null +++ b/transport/src/main/java/io/netty/channel/MessageList.java @@ -0,0 +1,475 @@ +/* + * Copyright 2013 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +package io.netty.channel; + +import io.netty.buffer.ByteBufUtil; +import io.netty.util.Recycler; +import io.netty.util.Recycler.Handle; +import io.netty.util.Signal; +import io.netty.util.internal.PlatformDependent; + +import java.util.Arrays; + +public final class MessageList { + + private static final int DEFAULT_INITIAL_CAPACITY = 8; + private static final int MIN_INITIAL_CAPACITY = 4; + + private static final Recycler> RECYCLER = new Recycler>() { + @Override + protected MessageList newObject(Handle handle) { + return new MessageList(handle); + } + }; + + public static MessageList newInstance() { + return newInstance(DEFAULT_INITIAL_CAPACITY); + } + + @SuppressWarnings("unchecked") + public static MessageList newInstance(int minCapacity) { + MessageList ret = (MessageList) RECYCLER.get(); + ret.ensureCapacity(minCapacity); + return ret; + } + + public static MessageList newInstance(T value) { + MessageList ret = newInstance(MIN_INITIAL_CAPACITY); + ret.add(value); + return ret; + } + + public MessageList retainAll() { + int size = this.size; + for (int i = 0; i < size; i ++) { + ByteBufUtil.retain(elements[i]); + } + return this; + } + + public MessageList retainAll(int increment) { + int size = this.size; + for (int i = 0; i < size; i ++) { + ByteBufUtil.retain(elements[i], increment); + } + return this; + } + + public boolean releaseAll() { + boolean releasedAll = true; + int size = this.size; + for (int i = 0; i < size; i++) { + releasedAll &= ByteBufUtil.release(elements[i]); + } + return releasedAll; + } + + public boolean releaseAll(int decrement) { + boolean releasedAll = true; + int size = this.size; + for (int i = 0; i < size; i++) { + releasedAll &= ByteBufUtil.release(elements[i], decrement); + } + return releasedAll; + } + + public boolean releaseAllAndRecycle() { + return releaseAll() && recycle(); + } + + public boolean releaseAllAndRecycle(int decrement) { + return releaseAll(decrement) && recycle(); + } + + public boolean recycle() { + clear(); + return RECYCLER.recycle(this, handle); + } + + private final Handle handle; + private T[] elements; + private int size; + + MessageList(Handle handle) { + this(handle, DEFAULT_INITIAL_CAPACITY); + } + + MessageList(Handle handle, int initialCapacity) { + this.handle = handle; + initialCapacity = normalizeCapacity(initialCapacity); + elements = newArray(initialCapacity); + } + + private MessageList(Handle handle, T[] elements, int size) { + this.handle = handle; + this.elements = elements; + this.size = size; + } + + public int size() { + return size; + } + + public int capacity() { + return elements.length; + } + + public boolean isEmpty() { + return size == 0; + } + + public T get(int index) { + checkExclusive(index); + return elements[index]; + } + + public MessageList set(int index, T value) { + checkExclusive(index); + if (value == null) { + throw new NullPointerException("value"); + } + elements[index] = value; + return this; + } + + public MessageList set(int index, T[] src) { + if (src == null) { + throw new NullPointerException("src"); + } + set(index, src, 0, src.length); + return this; + } + + public MessageList set(int index, T[] src, int srcIdx, int srcLen) { + checkElements(src, srcIdx, srcLen); + + if (srcLen == 0) { + return remove(index); + } + + if (srcLen == 1) { + return set(index, src[srcIdx]); + } + + checkExclusive(index); + + int oldSize = size; + int newSize = oldSize + srcLen - 1; + ensureCapacity(newSize); + System.arraycopy(elements, index + 1, elements, index + srcLen, oldSize - (index + 1)); + System.arraycopy(src, srcIdx, elements, index, srcLen); + size = newSize; + return this; + } + + public MessageList set(int index, int length, T value) { + if (length == 0) { + return add(index, value); + } + + if (length == 1) { + return set(index, value); + } + + checkRange(index, length); + if (value == null) { + throw new NullPointerException("value"); + } + + elements[index] = value; + int nextElemIdx = index + length; + int oldSize = size; + int newSize = oldSize - length + 1; + System.arraycopy(elements, nextElemIdx, elements, index + 1, oldSize - nextElemIdx); + Arrays.fill(elements, newSize, oldSize, null); + size = newSize; + return this; + } + + public MessageList set(int index, int length, T[] src) { + if (src == null) { + throw new NullPointerException("src"); + } + return set(index, length, src, 0, src.length); + } + + public MessageList set(int index, int length, T[] src, int srcIdx, int srcLen) { + if (length == 0) { + return add(index, src, srcIdx, srcLen); + } + + if (length == 1) { + return set(index, src, srcIdx, srcLen); + } + + checkRange(index, length); + checkElements(src, srcIdx, srcLen); + + if (srcLen == length) { + System.arraycopy(src, srcIdx, elements, index, length); + } else if (srcLen < length) { + int remainderIdx = index + length; + int oldSize = size; + int newSize = oldSize - (length - srcLen); + System.arraycopy(src, srcIdx, elements, index, srcLen); + System.arraycopy(elements, remainderIdx, elements, index + srcLen, oldSize - remainderIdx); + Arrays.fill(elements, newSize, oldSize, null); + size = newSize; + } else { + int remainderIdx = index + length; + int oldSize = size; + int newSize = oldSize + srcLen - length; + ensureCapacity(newSize); + // 0 [1 2] 3 4 5 -> 0 [1 2 3] 3 4 5 + System.arraycopy(elements, remainderIdx, elements, index + srcLen, oldSize - remainderIdx); + System.arraycopy(src, srcIdx, elements, index, srcLen); + size = newSize; + } + return this; + } + + public MessageList add(T value) { + if (value == null) { + throw new NullPointerException("value"); + } + int oldSize = size; + int newSize = oldSize + 1; + ensureCapacity(newSize); + elements[oldSize] = value; + size = newSize; + return this; + } + + public MessageList add(T[] src) { + if (src == null) { + throw new NullPointerException("src"); + } + return add(src, 0, src.length); + } + + public MessageList add(T[] src, int srcIdx, int srcLen) { + checkElements(src, srcIdx, srcLen); + + int oldSize = size; + int newSize = oldSize + srcLen; + ensureCapacity(newSize); + System.arraycopy(src, srcIdx, elements, oldSize, srcLen); + size = newSize; + return this; + } + + public MessageList add(int index, T value) { + checkInclusive(index); + + if (value == null) { + throw new NullPointerException("value"); + } + + int oldSize = size; + int newSize = oldSize + 1; + ensureCapacity(newSize); + System.arraycopy(elements, index, elements, index + 1, oldSize - index); + elements[index] = value; + size = newSize; + return this; + } + + public MessageList add(int index, T[] src) { + if (src == null) { + throw new NullPointerException("src"); + } + return add(index, src, 0, src.length); + } + + public MessageList add(int index, T[] src, int srcIdx, int srcLen) { + checkInclusive(index); + checkElements(src, srcIdx, srcLen); + + if (srcLen == 0) { + return this; + } + + int oldSize = size; + int newSize = oldSize + srcLen; + ensureCapacity(newSize); + System.arraycopy(elements, index, elements, index + srcLen, oldSize - index); + System.arraycopy(src, srcIdx, elements, index, srcLen); + size = newSize; + return this; + } + + public MessageList remove(int index) { + checkExclusive(index); + int oldSize = size; + int newSize = oldSize - 1; + System.arraycopy(elements, index + 1, elements, index, newSize - index); + elements[newSize] = null; + size = newSize; + return this; + } + + public MessageList remove(int index, int length) { + checkRange(index, length); + if (length == 0) { + return this; + } + + int oldSize = size; + int newSize = oldSize - length; + System.arraycopy(elements, index + length, elements, index, newSize - index); + Arrays.fill(elements, newSize, oldSize, null); + size = newSize; + return this; + } + + public MessageList clear() { + Arrays.fill(elements, 0, size, null); + size = 0; + return this; + } + + public MessageList copy() { + return new MessageList(handle, elements.clone(), size); + } + + public MessageList copy(int index, int length) { + checkRange(index, length); + MessageList copy = new MessageList(handle, length); + System.arraycopy(elements, index, copy.elements, 0, length); + copy.size = length; + return copy; + } + + @SuppressWarnings("unchecked") + public MessageList cast() { + return (MessageList) this; + } + + public T[] array() { + return elements; + } + + public boolean forEach(MessageListProcessor proc) { + if (proc == null) { + throw new NullPointerException("proc"); + } + + @SuppressWarnings("unchecked") + MessageListProcessor p = (MessageListProcessor) proc; + + int size = this.size; + try { + for (int i = 0; i < size; i ++) { + i += p.process(this, i, elements[i]); + } + } catch (Signal abort) { + abort.expect(MessageListProcessor.ABORT); + return false; + } catch (Exception e) { + PlatformDependent.throwException(e); + } + + return true; + } + + public boolean forEach(int index, int length, MessageListProcessor proc) { + checkRange(index, length); + if (proc == null) { + throw new NullPointerException("proc"); + } + + @SuppressWarnings("unchecked") + MessageListProcessor p = (MessageListProcessor) proc; + + int end = index + length; + try { + for (int i = index; i < end;) { + i += p.process(this, i, elements[i]); + } + } catch (Signal abort) { + abort.expect(MessageListProcessor.ABORT); + return false; + } catch (Exception e) { + PlatformDependent.throwException(e); + } + + return true; + } + + private void ensureCapacity(int capacity) { + if (elements.length >= capacity) { + return; + } + + T[] newElements = newArray(normalizeCapacity(capacity)); + System.arraycopy(elements, 0, newElements, 0, size); + elements = newElements; + } + + private static int normalizeCapacity(int initialCapacity) { + if (initialCapacity <= MIN_INITIAL_CAPACITY) { + initialCapacity = MIN_INITIAL_CAPACITY; + } else { + initialCapacity |= initialCapacity >>> 1; + initialCapacity |= initialCapacity >>> 2; + initialCapacity |= initialCapacity >>> 4; + initialCapacity |= initialCapacity >>> 8; + initialCapacity |= initialCapacity >>> 16; + initialCapacity ++; + + if (initialCapacity < 0) { + initialCapacity >>>= 1; + } + } + return initialCapacity; + } + + @SuppressWarnings({ "unchecked", "SuspiciousArrayCast" }) + private T[] newArray(int initialCapacity) { + return (T[]) new Object[initialCapacity]; + } + + private void checkExclusive(int index) { + if (index >= size) { + throw new IndexOutOfBoundsException(String.valueOf(index)); + } + } + + private void checkInclusive(int index) { + if (index > size) { + throw new IndexOutOfBoundsException(String.valueOf(index)); + } + } + + private void checkRange(int index, int length) { + if (index + length > size) { + throw new IndexOutOfBoundsException("index: " + index + ", length: " + length + ", size: " + size); + } + } + + private void checkElements(T[] src, int srcIdx, int srcLen) { + if (src == null) { + throw new NullPointerException("src"); + } + int end = srcIdx + srcLen; + for (int i = srcIdx; i < end; i ++) { + if (src[i] == null) { + throw new NullPointerException("src[" + i + ']'); + } + } + } +} diff --git a/buffer/src/main/java/io/netty/buffer/BufType.java b/transport/src/main/java/io/netty/channel/MessageListProcessor.java similarity index 51% rename from buffer/src/main/java/io/netty/buffer/BufType.java rename to transport/src/main/java/io/netty/channel/MessageListProcessor.java index 8b9ffd0674..37cd20d87a 100644 --- a/buffer/src/main/java/io/netty/buffer/BufType.java +++ b/transport/src/main/java/io/netty/channel/MessageListProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 The Netty Project + * Copyright 2013 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 @@ -13,19 +13,19 @@ * License for the specific language governing permissions and limitations * under the License. */ -package io.netty.buffer; -/** - * The type of the Buf - */ -public enum BufType { - /** - * Operates on bytes. - */ - BYTE, +package io.netty.channel; + +import io.netty.util.Signal; + +public interface MessageListProcessor { + + Signal ABORT = new Signal(MessageListProcessor.class.getName() + ".ABORT"); /** - * Operates on messages, which can be of any kind. + * @return the number of elements processed. {@link MessageList#forEach(MessageListProcessor)} will determine + * the index of the next element to be processed based on this value. Usually, an implementation will + * return {@code 1} to advance the index by {@code 1}. */ - MESSAGE + int process(MessageList messages, int index, T value) throws Exception; } diff --git a/transport/src/main/java/io/netty/channel/RecvByteBufAllocator.java b/transport/src/main/java/io/netty/channel/RecvByteBufAllocator.java new file mode 100644 index 0000000000..371207a9f0 --- /dev/null +++ b/transport/src/main/java/io/netty/channel/RecvByteBufAllocator.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.channel; + +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufAllocator; + +/** + * Allocates a new receive buffer whose capacity is probably large enough to read all inbound data and small enough + * not to waste its space. + */ +public interface RecvByteBufAllocator { + + /** + * Creates a new handle. The handle provides the actual operations and keeps the internal information which is + * required for predicting an optimal buffer capacity. + */ + Handle newHandle(); + + public interface Handle { + /** + * Creates a new receive buffer whose capacity is probably large enough to read all inbound data and small + * enough not to waste its space. + */ + ByteBuf allocate(ByteBufAllocator alloc); + + /** + * Similar to {@link #allocate(ByteBufAllocator)} except that it does not allocate anything but just tells the + * capacity. + */ + int guess(); + + /** + * Records the the actual number of read bytes in the previous read operation so that the allocator allocates + * the buffer with potentially more correct capacity. + * + * @param actualReadBytes the actual number of read bytes in the previous read operation + */ + void record(int actualReadBytes); + } +} diff --git a/transport/src/main/java/io/netty/channel/SimpleChannelInboundHandler.java b/transport/src/main/java/io/netty/channel/SimpleChannelInboundHandler.java new file mode 100644 index 0000000000..e667f6fc13 --- /dev/null +++ b/transport/src/main/java/io/netty/channel/SimpleChannelInboundHandler.java @@ -0,0 +1,89 @@ +/* + * Copyright 2013 The Netty Project + * + * The Netty Project licenses this file to you under the Apache License, + * version 2.0 (the "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ +package io.netty.channel; + +import io.netty.util.internal.TypeParameterMatcher; + +/** + * {@link ChannelInboundHandlerAdapter} which allows to explicit only handle a specific type of messages. + * + * For example here is an implementation which only handle {@link String} messages. + * + *
+ *     public class StringHandler extends
+ *             {@link SimpleChannelInboundHandler}<{@link String}> {
+ *
+ *         {@code @Override}
+ *         public void messageReceived({@link ChannelHandlerContext} ctx, {@link String} message)
+ *                 throws {@link Exception} {
+ *             System.out.println(message);
+ *         }
+ *     }
+ * 
+ * + */ +public abstract class SimpleChannelInboundHandler extends ChannelInboundHandlerAdapter { + + private final TypeParameterMatcher matcher; + + protected SimpleChannelInboundHandler() { + matcher = TypeParameterMatcher.find(this, SimpleChannelInboundHandler.class, "I"); + } + + protected SimpleChannelInboundHandler(Class inboundMessageType) { + matcher = TypeParameterMatcher.get(inboundMessageType); + } + + public boolean acceptInboundMessage(Object msg) throws Exception { + return matcher.match(msg); + } + + @Override + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + MessageList unaccepted = MessageList.newInstance(); + int size = msgs.size(); + try { + for (int i = 0; i < size; i++) { + Object msg = msgs.get(i); + if (acceptInboundMessage(msg)) { + if (!unaccepted.isEmpty()) { + ctx.fireMessageReceived(unaccepted); + unaccepted = MessageList.newInstance(); + } + + @SuppressWarnings("unchecked") + I imsg = (I) msg; + messageReceived(ctx, imsg); + } else { + unaccepted.add(msg); + } + } + } finally { + msgs.recycle(); + ctx.fireMessageReceived(unaccepted); + } + } + + /** + * Is called for each message of type {@link I}. + * + * @param ctx the {@link ChannelHandlerContext} which this {@link SimpleChannelInboundHandler} + * belongs to + * @param msg the message to handle + * @throws Exception is thrown if an error accour + */ + protected abstract void messageReceived(ChannelHandlerContext ctx, I msg) throws Exception; +} diff --git a/transport/src/main/java/io/netty/channel/aio/AioCompletionHandler.java b/transport/src/main/java/io/netty/channel/aio/AioCompletionHandler.java index ceaa84eecc..47f42d3e61 100644 --- a/transport/src/main/java/io/netty/channel/aio/AioCompletionHandler.java +++ b/transport/src/main/java/io/netty/channel/aio/AioCompletionHandler.java @@ -23,17 +23,23 @@ import java.nio.channels.CompletionHandler; /** * Special {@link CompletionHandler} which makes sure that the callback methods gets executed in the {@link EventLoop} */ -public abstract class AioCompletionHandler implements CompletionHandler { +public abstract class AioCompletionHandler implements CompletionHandler { + + private final C channel; + + protected AioCompletionHandler(C channel) { + this.channel = channel; + } /** * See {@link CompletionHandler#completed(Object, Object)} */ - protected abstract void completed0(V result, A channel); + protected abstract void completed0(C channel, V result, A attachment); /** * Set {@link CompletionHandler#failed(Throwable, Object)} */ - protected abstract void failed0(Throwable exc, A channel); + protected abstract void failed0(C channel, Throwable exc, A attachment); // According to JDK AIO documentation, the ExecutorService a user specified must not call the Runnable given by // JDK AIO implementation directly. However, we violates that rull by calling Runnable.run() directly for @@ -49,14 +55,14 @@ public abstract class AioCompletionHandler implements Comp }; @Override - public final void completed(final V result, final A channel) { + public final void completed(final V result, final A attachment) { EventLoop loop = channel.eventLoop(); if (loop.inEventLoop()) { Integer d = STACK_DEPTH.get(); if (d < MAX_STACK_DEPTH) { STACK_DEPTH.set(d + 1); try { - completed0(result, channel); + completed0(channel, result, attachment); } finally { STACK_DEPTH.set(d); } @@ -66,7 +72,7 @@ public abstract class AioCompletionHandler implements Comp loop.execute(new AioEventLoop.RecursionBreakingRunnable() { @Override public void run() { - completed0(result, channel); + completed0(channel, result, attachment); } }); } @@ -74,21 +80,21 @@ public abstract class AioCompletionHandler implements Comp loop.execute(new Runnable() { @Override public void run() { - completed0(result, channel); + completed0(channel, result, attachment); } }); } } @Override - public final void failed(final Throwable exc, final A channel) { + public final void failed(final Throwable exc, final A attachment) { EventLoop loop = channel.eventLoop(); if (loop.inEventLoop()) { Integer d = STACK_DEPTH.get(); if (d < MAX_STACK_DEPTH) { STACK_DEPTH.set(d + 1); try { - failed0(exc, channel); + failed0(channel, exc, attachment); } finally { STACK_DEPTH.set(d); } @@ -98,7 +104,7 @@ public abstract class AioCompletionHandler implements Comp loop.execute(new AioEventLoop.RecursionBreakingRunnable() { @Override public void run() { - failed0(exc, channel); + failed0(channel, exc, attachment); } }); } @@ -106,7 +112,7 @@ public abstract class AioCompletionHandler implements Comp loop.execute(new Runnable() { @Override public void run() { - failed0(exc, channel); + failed0(channel, exc, attachment); } }); } diff --git a/transport/src/main/java/io/netty/channel/aio/AioEventLoop.java b/transport/src/main/java/io/netty/channel/aio/AioEventLoop.java index 05dd153ab6..e72ddfae82 100644 --- a/transport/src/main/java/io/netty/channel/aio/AioEventLoop.java +++ b/transport/src/main/java/io/netty/channel/aio/AioEventLoop.java @@ -110,10 +110,11 @@ final class AioEventLoop extends SingleThreadEventLoop { @Override protected void addTask(Runnable task) { + if (task == null) { + throw new NullPointerException("task"); + } + if (task instanceof RecursionBreakingRunnable) { - if (task == null) { - throw new NullPointerException("task"); - } if (isTerminated()) { reject(); } diff --git a/transport/src/main/java/io/netty/channel/embedded/EmbeddedByteChannel.java b/transport/src/main/java/io/netty/channel/embedded/EmbeddedByteChannel.java deleted file mode 100644 index 421a3b30ca..0000000000 --- a/transport/src/main/java/io/netty/channel/embedded/EmbeddedByteChannel.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.channel.embedded; - -import io.netty.buffer.BufType; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelMetadata; -import io.netty.channel.ChannelPipeline; - - -/** - * Embedded {@link Channel} which operates on bytes - */ -public class EmbeddedByteChannel extends AbstractEmbeddedChannel { - - private static final ChannelMetadata METADATA = new ChannelMetadata(BufType.BYTE, false); - - /** - * Create a new instance with the given {@link ChannelHandler}s in the {@link ChannelPipeline} - */ - public EmbeddedByteChannel(ChannelHandler... handlers) { - super(Unpooled.buffer(), handlers); - } - - @Override - public ChannelMetadata metadata() { - return METADATA; - } - - @Override - public ByteBuf inboundBuffer() { - return pipeline().inboundByteBuffer(); - } - - @Override - public ByteBuf lastOutboundBuffer() { - return (ByteBuf) lastOutboundBuffer; - } - - @Override - public ByteBuf readOutbound() { - if (!lastOutboundBuffer().isReadable()) { - return null; - } - try { - return lastOutboundBuffer().readBytes(lastOutboundBuffer().readableBytes()); - } finally { - lastOutboundBuffer().clear(); - } - } - - @Override - protected void writeInbound0(ByteBuf data) { - inboundBuffer().writeBytes(data); - } - - @Override - protected boolean hasReadableOutboundBuffer() { - return lastOutboundBuffer().isReadable(); - } - - @Override - protected void doFlushByteBuffer(ByteBuf buf) throws Exception { - lastOutboundBuffer().writeBytes(buf); - } -} diff --git a/transport/src/main/java/io/netty/channel/embedded/AbstractEmbeddedChannel.java b/transport/src/main/java/io/netty/channel/embedded/EmbeddedChannel.java similarity index 55% rename from transport/src/main/java/io/netty/channel/embedded/AbstractEmbeddedChannel.java rename to transport/src/main/java/io/netty/channel/embedded/EmbeddedChannel.java index f950809652..54862aea62 100755 --- a/transport/src/main/java/io/netty/channel/embedded/AbstractEmbeddedChannel.java +++ b/transport/src/main/java/io/netty/channel/embedded/EmbeddedChannel.java @@ -15,92 +15,81 @@ */ package io.netty.channel.embedded; -import io.netty.buffer.Buf; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.MessageBuf; -import io.netty.buffer.Unpooled; import io.netty.channel.AbstractChannel; import io.netty.channel.Channel; import io.netty.channel.ChannelConfig; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundByteHandler; -import io.netty.channel.ChannelInboundMessageHandler; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.ChannelMetadata; import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPromise; -import io.netty.channel.ChannelStateHandlerAdapter; import io.netty.channel.DefaultChannelConfig; import io.netty.channel.EventLoop; +import io.netty.channel.MessageList; import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; import java.net.SocketAddress; import java.nio.channels.ClosedChannelException; +import java.util.ArrayDeque; +import java.util.Queue; /** * Base class for {@link Channel} implementations that are used in an embedded fashion. - * - * @param the type of data that can be written to this {@link Channel} */ -public abstract class AbstractEmbeddedChannel extends AbstractChannel { +public class EmbeddedChannel extends AbstractChannel { - private static final InternalLogger logger = InternalLoggerFactory.getInstance(AbstractEmbeddedChannel.class); + private static final InternalLogger logger = InternalLoggerFactory.getInstance(EmbeddedChannel.class); + + private static final ChannelMetadata METADATA = new ChannelMetadata(false); private final EmbeddedEventLoop loop = new EmbeddedEventLoop(); private final ChannelConfig config = new DefaultChannelConfig(this); private final SocketAddress localAddress = new EmbeddedSocketAddress(); private final SocketAddress remoteAddress = new EmbeddedSocketAddress(); - private final MessageBuf lastInboundMessageBuffer = Unpooled.messageBuffer().retain(2); - private final ByteBuf lastInboundByteBuffer = Unpooled.buffer().retain(2); - protected final Object lastOutboundBuffer; + private final Queue lastInboundBuffer = new ArrayDeque(); + private final Queue lastOutboundBuffer = new ArrayDeque(); private Throwable lastException; private int state; // 0 = OPEN, 1 = ACTIVE, 2 = CLOSED /** * Create a new instance * - * @param lastOutboundBuffer the last outbound buffer which will hold all the written data - * @param handlers the @link ChannelHandler}s which will be add in the {@link ChannelPipeline} + * @param handlers the @link ChannelHandler}s which will be add in the {@link ChannelPipeline} */ - AbstractEmbeddedChannel(Object lastOutboundBuffer, ChannelHandler... handlers) { + public EmbeddedChannel(ChannelHandler... handlers) { super(null, null); if (handlers == null) { throw new NullPointerException("handlers"); } - this.lastOutboundBuffer = lastOutboundBuffer; - int nHandlers = 0; - boolean hasBuffer = false; ChannelPipeline p = pipeline(); for (ChannelHandler h: handlers) { if (h == null) { break; } nHandlers ++; - ChannelHandlerContext ctx = p.addLast(h).context(h); - - if (ctx.hasInboundByteBuffer() || ctx.hasOutboundByteBuffer() - || ctx.hasInboundMessageBuffer() || ctx.hasOutboundMessageBuffer()) { - hasBuffer = true; - } + p.addLast(h); } if (nHandlers == 0) { throw new IllegalArgumentException("handlers is empty."); } - if (!hasBuffer) { - throw new IllegalArgumentException("handlers does not provide any buffers."); - } - - p.addLast(new LastInboundMessageHandler(), new LastInboundByteHandler()); + p.addLast(new LastInboundHandler()); loop.register(this); } + @Override + public ChannelMetadata metadata() { + return METADATA; + } + @Override public ChannelConfig config() { return config; @@ -117,33 +106,87 @@ public abstract class AbstractEmbeddedChannel extends AbstractChannel { } /** - * Return the last inbound {@link MessageBuf} which will hold all the {@link Object}s that where received - * by this {@link Channel} + * Returns the buffer which holds all the {@link Object}s that were received by this {@link Channel}. */ - public MessageBuf lastInboundMessageBuffer() { - return lastInboundMessageBuffer; + public Queue lastInboundBuffer() { + return lastInboundBuffer; } /** - * Return the last inbound {@link ByteBuf} which will hold all the bytes that where received - * by this {@link Channel} + * Returns the buffer which holds all the {@link Object}s that were written by this {@link Channel}. */ - public ByteBuf lastInboundByteBuffer() { - return lastInboundByteBuffer; + public Queue lastOutboundBuffer() { + return lastOutboundBuffer; } /** * Return received data from this {@link Channel} */ public Object readInbound() { - if (lastInboundByteBuffer.isReadable()) { - try { - return lastInboundByteBuffer.readBytes(lastInboundByteBuffer.readableBytes()); - } finally { - lastInboundByteBuffer.clear(); - } + return lastInboundBuffer.poll(); + } + + /** + * Read data froum the outbound. This may return {@code null} if nothing is readable. + */ + public Object readOutbound() { + return lastOutboundBuffer.poll(); + } + + /** + * Write messages to the inbound of this {@link Channel}. + * + * @param msgs the messages to be written + * + * @return {@code true} if the write operation did add something to the the inbound buffer + */ + public boolean writeInbound(Object... msgs) { + ensureOpen(); + if (msgs.length == 0) { + return !lastInboundBuffer.isEmpty(); } - return lastInboundMessageBuffer.poll(); + MessageList list = MessageList.newInstance(msgs.length); + list.add(msgs); + pipeline().fireMessageReceived(list); + runPendingTasks(); + checkException(); + return !lastInboundBuffer.isEmpty(); + } + + /** + * Write messages to the outbound of this {@link Channel}. + * + * @param msgs the messages to be written + * @return bufferReadable returns {@code true} if the write operation did add something to the the outbound buffer + */ + public boolean writeOutbound(Object... msgs) { + ensureOpen(); + if (msgs.length == 0) { + return !lastOutboundBuffer.isEmpty(); + } + MessageList list = MessageList.newInstance(msgs.length); + list.add(msgs); + ChannelFuture future = write(list); + assert future.isDone(); + if (future.cause() != null) { + recordException(future.cause()); + } + runPendingTasks(); + checkException(); + return !lastOutboundBuffer.isEmpty(); + } + + /** + * Mark this {@link Channel} as finished. Any futher try to write data to it will fail. + * + * + * @return bufferReadable returns {@code true} if any of the used buffers has something left to read + */ + public boolean finish() { + close(); + runPendingTasks(); + checkException(); + return !lastInboundBuffer.isEmpty() || !lastOutboundBuffer.isEmpty(); } /** @@ -244,120 +287,34 @@ public abstract class AbstractEmbeddedChannel extends AbstractChannel { return false; } - /** - * Read data froum the outbound. This may return {@code null} if nothing is readable. - */ - public abstract O readOutbound(); - - /** - * Return the inbound buffer in which inbound messages are stored. - */ - public abstract Buf inboundBuffer(); - - /** - * Return the last outbound buffer in which all the written outbound messages are stored. - */ - public abstract Buf lastOutboundBuffer(); - - /** - * Mark this {@link Channel} as finished. Any futher try to write data to it will fail. - * - * - * @return bufferReadable returns {@code true} if any of the used buffers has something left to read - */ - public boolean finish() { - close(); - runPendingTasks(); - checkException(); - return lastInboundByteBuffer().isReadable() || !lastInboundMessageBuffer().isEmpty() || - hasReadableOutboundBuffer(); - } - - /** - * Write data to the inbound of this {@link Channel}. - * - * @param data data that should be written - * @return bufferReadable returns {@code true} if the write operation did add something to the the inbound buffer - */ - public boolean writeInbound(O data) { - ensureOpen(); - writeInbound0(data); - pipeline().fireInboundBufferUpdated(); - runPendingTasks(); - checkException(); - return lastInboundByteBuffer().isReadable() || !lastInboundMessageBuffer().isEmpty(); - } - - /** - * Write data to the outbound of this {@link Channel}. - * - * @param data data that should be written - * @return bufferReadable returns {@code true} if the write operation did add something to the the outbound buffer - */ - public boolean writeOutbound(Object data) { - ensureOpen(); - ChannelFuture future = write(data); - assert future.isDone(); - if (future.cause() != null) { - recordException(future.cause()); + @Override + protected int doWrite(MessageList msgs, int index) throws Exception { + int size = msgs.size(); + for (int i = index; i < size; i ++) { + lastOutboundBuffer.add(msgs.get(i)); } - runPendingTasks(); - checkException(); - return hasReadableOutboundBuffer(); + return size - index; } - /** - * Returns {@code true} if the outbound buffer hold some data which can be read - */ - protected abstract boolean hasReadableOutboundBuffer(); - - /** - * Add the data to the inbound buffer. - */ - protected abstract void writeInbound0(O data); - private class DefaultUnsafe extends AbstractUnsafe { @Override - public void connect(SocketAddress remoteAddress, - SocketAddress localAddress, ChannelPromise promise) { + public void connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise) { promise.setSuccess(); } } - private final class LastInboundMessageHandler extends ChannelStateHandlerAdapter - implements ChannelInboundMessageHandler { + private final class LastInboundHandler extends ChannelInboundHandlerAdapter { @Override - public MessageBuf newInboundBuffer(ChannelHandlerContext ctx) throws Exception { - return lastInboundMessageBuffer; + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + int size = msgs.size(); + for (int i = 0; i < size; i ++) { + lastInboundBuffer.add(msgs.get(i)); + } } @Override - public void inboundBufferUpdated(ChannelHandlerContext ctx) throws Exception { - // Do nothing. - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) - throws Exception { + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { recordException(cause); } } - - private final class LastInboundByteHandler extends ChannelStateHandlerAdapter - implements ChannelInboundByteHandler { - @Override - public ByteBuf newInboundBuffer(ChannelHandlerContext ctx) throws Exception { - return lastInboundByteBuffer; - } - - @Override - public void discardInboundReadBytes(ChannelHandlerContext ctx) throws Exception { - // nothing - } - - @Override - public void inboundBufferUpdated(ChannelHandlerContext ctx) throws Exception { - // No nothing - } - } } diff --git a/transport/src/main/java/io/netty/channel/embedded/EmbeddedMessageChannel.java b/transport/src/main/java/io/netty/channel/embedded/EmbeddedMessageChannel.java deleted file mode 100644 index ecb9b447c4..0000000000 --- a/transport/src/main/java/io/netty/channel/embedded/EmbeddedMessageChannel.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * Copyright 2012 The Netty Project - * - * The Netty Project licenses this file to you under the Apache License, - * version 2.0 (the "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at: - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations - * under the License. - */ -package io.netty.channel.embedded; - -import io.netty.buffer.BufType; -import io.netty.buffer.MessageBuf; -import io.netty.buffer.Unpooled; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelMetadata; -import io.netty.channel.ChannelPipeline; - -/** - * Embedded {@link Channel} which operates on messages which can be of any time. - */ -public class EmbeddedMessageChannel extends AbstractEmbeddedChannel { - - private static final ChannelMetadata METADATA = new ChannelMetadata(BufType.MESSAGE, false); - - /** - * Create a new instance with the given {@link ChannelHandler}s in the {@link ChannelPipeline} - */ - public EmbeddedMessageChannel(ChannelHandler... handlers) { - super(Unpooled.messageBuffer(), handlers); - } - - @Override - public ChannelMetadata metadata() { - return METADATA; - } - - @Override - public MessageBuf inboundBuffer() { - return pipeline().inboundMessageBuffer(); - } - - @Override - @SuppressWarnings("unchecked") - public MessageBuf lastOutboundBuffer() { - return (MessageBuf) lastOutboundBuffer; - } - - @Override - public Object readOutbound() { - return lastOutboundBuffer().poll(); - } - - @Override - protected void writeInbound0(Object data) { - inboundBuffer().add(data); - } - - @Override - protected boolean hasReadableOutboundBuffer() { - return !lastOutboundBuffer().isEmpty(); - } - - @Override - protected void doFlushMessageBuffer(MessageBuf buf) throws Exception { - buf.drainTo(lastOutboundBuffer()); - } -} diff --git a/transport/src/main/java/io/netty/channel/group/ChannelGroup.java b/transport/src/main/java/io/netty/channel/group/ChannelGroup.java index a8230de2aa..b034d94377 100644 --- a/transport/src/main/java/io/netty/channel/group/ChannelGroup.java +++ b/transport/src/main/java/io/netty/channel/group/ChannelGroup.java @@ -20,9 +20,9 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelStateHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.EventLoop; -import io.netty.channel.FileRegion; +import io.netty.channel.MessageList; import io.netty.channel.ServerChannel; import io.netty.util.CharsetUtil; @@ -78,7 +78,7 @@ import java.util.Set; * b.releaseExternalResources(); * } * - * public class MyHandler extends {@link ChannelStateHandlerAdapter} { + * public class MyHandler extends {@link ChannelInboundHandlerAdapter} { * {@code @Override} * public void channelActive({@link ChannelHandlerContext} ctx) { * // closed on shutdown. @@ -117,23 +117,17 @@ public interface ChannelGroup extends Set, Comparable { ChannelGroupFuture write(Object message); /** - * Writes the specified {@link FileRegion} to all {@link Channel}s in this - * group. Please note that this operation is asynchronous as - * {@link Channel#sendFile(FileRegion)} is. + * Writes the specified {@code messages} to all {@link Channel}s in this + * group. If the specified {@code messages} are an instance of + * {@link ByteBuf}, it is automatically + * {@linkplain ByteBuf#duplicate() duplicated} to avoid a race + * condition. Please note that this operation is asynchronous as + * {@link Channel#write(Object)} is. * * @return the {@link ChannelGroupFuture} instance that notifies when * the operation is done for all channels */ - ChannelGroupFuture sendFile(FileRegion region); - - /** - * Flush all {@link Channel} in this group. Please note that this operation - * is asynchronous as {@link Channel#flush()} is. - * - * @return the {@link ChannelGroupFuture} instance that notifies when - * the operation is done for all channels - */ - ChannelGroupFuture flush(); + ChannelGroupFuture write(MessageList messages); /** * Disconnects all {@link Channel}s in this group from their remote peers. diff --git a/transport/src/main/java/io/netty/channel/group/DefaultChannelGroup.java b/transport/src/main/java/io/netty/channel/group/DefaultChannelGroup.java index 930aa6894e..036ef3cf4c 100644 --- a/transport/src/main/java/io/netty/channel/group/DefaultChannelGroup.java +++ b/transport/src/main/java/io/netty/channel/group/DefaultChannelGroup.java @@ -15,11 +15,11 @@ */ package io.netty.channel.group; -import io.netty.buffer.BufUtil; +import io.netty.buffer.ByteBufUtil; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFutureListener; -import io.netty.channel.FileRegion; +import io.netty.channel.MessageList; import io.netty.channel.ServerChannel; import io.netty.util.concurrent.EventExecutor; import io.netty.util.concurrent.ImmediateEventExecutor; @@ -233,37 +233,27 @@ public class DefaultChannelGroup extends AbstractSet implements Channel Map futures = new LinkedHashMap(size()); for (Channel c: nonServerChannels.values()) { - BufUtil.retain(message); + ByteBufUtil.retain(message); futures.put(c.id(), c.write(message)); } - BufUtil.release(message); + ByteBufUtil.release(message); return new DefaultChannelGroupFuture(this, futures, executor); } @Override - public ChannelGroupFuture sendFile(FileRegion region) { - if (region == null) { - throw new NullPointerException("region"); + public ChannelGroupFuture write(MessageList messages) { + if (messages == null) { + throw new NullPointerException("messages"); } Map futures = new LinkedHashMap(size()); for (Channel c: nonServerChannels.values()) { - BufUtil.retain(region); - futures.put(c.id(), c.sendFile(region)); - } - - BufUtil.release(region); - return new DefaultChannelGroupFuture(this, futures, executor); - } - - @Override - public ChannelGroupFuture flush() { - Map futures = new LinkedHashMap(size()); - for (Channel c: nonServerChannels.values()) { - futures.put(c.id(), c.flush()); + MessageList messagesCopy = messages.retainAll().copy(); + futures.put(c.id(), c.write(messagesCopy)); } + messages.releaseAllAndRecycle(); return new DefaultChannelGroupFuture(this, futures, executor); } diff --git a/transport/src/main/java/io/netty/channel/local/LocalChannel.java b/transport/src/main/java/io/netty/channel/local/LocalChannel.java index 9a7f0d03a2..116f6905c1 100755 --- a/transport/src/main/java/io/netty/channel/local/LocalChannel.java +++ b/transport/src/main/java/io/netty/channel/local/LocalChannel.java @@ -15,8 +15,6 @@ */ package io.netty.channel.local; -import io.netty.buffer.BufType; -import io.netty.buffer.MessageBuf; import io.netty.channel.AbstractChannel; import io.netty.channel.Channel; import io.netty.channel.ChannelConfig; @@ -26,6 +24,7 @@ import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPromise; import io.netty.channel.DefaultChannelConfig; import io.netty.channel.EventLoop; +import io.netty.channel.MessageList; import io.netty.channel.SingleThreadEventLoop; import io.netty.util.concurrent.SingleThreadEventExecutor; @@ -34,14 +33,15 @@ import java.nio.channels.AlreadyConnectedException; import java.nio.channels.ClosedChannelException; import java.nio.channels.ConnectionPendingException; import java.nio.channels.NotYetConnectedException; -import java.util.Collections; +import java.util.ArrayDeque; +import java.util.Queue; /** * A {@link Channel} for the local transport. */ public class LocalChannel extends AbstractChannel { - private static final ChannelMetadata METADATA = new ChannelMetadata(BufType.MESSAGE, false); + private static final ChannelMetadata METADATA = new ChannelMetadata(false); private static final int MAX_READER_STACK_DEPTH = 8; private static final ThreadLocal READER_STACK_DEPTH = new ThreadLocal() { @@ -52,11 +52,18 @@ public class LocalChannel extends AbstractChannel { }; private final ChannelConfig config = new DefaultChannelConfig(this); + private final Queue> inboundBuffer = new ArrayDeque>(); private final Runnable readTask = new Runnable() { @Override public void run() { ChannelPipeline pipeline = pipeline(); - pipeline.fireInboundBufferUpdated(); + for (;;) { + MessageList m = inboundBuffer.poll(); + if (m == null) { + break; + } + pipeline.fireMessageReceived(m); + } pipeline.fireChannelReadSuspended(); } }; @@ -236,8 +243,8 @@ public class LocalChannel extends AbstractChannel { } ChannelPipeline pipeline = pipeline(); - MessageBuf buf = pipeline.inboundMessageBuffer(); - if (buf.isEmpty()) { + Queue> inboundBuffer = this.inboundBuffer; + if (inboundBuffer.isEmpty()) { readInProgress = true; return; } @@ -246,7 +253,13 @@ public class LocalChannel extends AbstractChannel { if (stackDepth < MAX_READER_STACK_DEPTH) { READER_STACK_DEPTH.set(stackDepth + 1); try { - pipeline.fireInboundBufferUpdated(); + for (;;) { + MessageList received = inboundBuffer.poll(); + if (received == null) { + break; + } + pipeline.fireMessageReceived(received); + } pipeline.fireChannelReadSuspended(); } finally { READER_STACK_DEPTH.set(stackDepth); @@ -257,7 +270,7 @@ public class LocalChannel extends AbstractChannel { } @Override - protected void doFlushMessageBuffer(MessageBuf buf) throws Exception { + protected int doWrite(MessageList msgs, int index) throws Exception { if (state < 2) { throw new NotYetConnectedException(); } @@ -268,28 +281,37 @@ public class LocalChannel extends AbstractChannel { final LocalChannel peer = this.peer; final ChannelPipeline peerPipeline = peer.pipeline(); final EventLoop peerLoop = peer.eventLoop(); + final int size = msgs.size(); + + // Use a copy because the original msgs will be recycled by AbstractChannel. + final MessageList msgsCopy = msgs.copy(); if (peerLoop == eventLoop()) { - buf.drainTo(peerPipeline.inboundMessageBuffer()); + peer.inboundBuffer.add(msgsCopy); finishPeerRead(peer, peerPipeline); } else { - final Object[] msgs = buf.toArray(); - buf.clear(); peerLoop.execute(new Runnable() { @Override public void run() { - MessageBuf buf = peerPipeline.inboundMessageBuffer(); - Collections.addAll(buf, msgs); + peer.inboundBuffer.add(msgsCopy); finishPeerRead(peer, peerPipeline); } }); } + + return size - index; } private static void finishPeerRead(LocalChannel peer, ChannelPipeline peerPipeline) { if (peer.readInProgress) { peer.readInProgress = false; - peerPipeline.fireInboundBufferUpdated(); + for (;;) { + MessageList received = peer.inboundBuffer.poll(); + if (received == null) { + break; + } + peerPipeline.fireMessageReceived(received); + } peerPipeline.fireChannelReadSuspended(); } } diff --git a/transport/src/main/java/io/netty/channel/local/LocalServerChannel.java b/transport/src/main/java/io/netty/channel/local/LocalServerChannel.java index 5366827ff0..7f0aabdac5 100755 --- a/transport/src/main/java/io/netty/channel/local/LocalServerChannel.java +++ b/transport/src/main/java/io/netty/channel/local/LocalServerChannel.java @@ -15,17 +15,19 @@ */ package io.netty.channel.local; -import io.netty.buffer.MessageBuf; import io.netty.channel.AbstractServerChannel; import io.netty.channel.ChannelConfig; import io.netty.channel.ChannelPipeline; import io.netty.channel.DefaultChannelConfig; import io.netty.channel.EventLoop; +import io.netty.channel.MessageList; import io.netty.channel.ServerChannel; import io.netty.channel.SingleThreadEventLoop; import io.netty.util.concurrent.SingleThreadEventExecutor; import java.net.SocketAddress; +import java.util.ArrayDeque; +import java.util.Queue; /** * A {@link ServerChannel} for the local transport which allows in VM communication. @@ -33,6 +35,7 @@ import java.net.SocketAddress; public class LocalServerChannel extends AbstractServerChannel { private final ChannelConfig config = new DefaultChannelConfig(this); + private final Queue inboundBuffer = new ArrayDeque(); private final Runnable shutdownHook = new Runnable() { @Override public void run() { @@ -138,13 +141,15 @@ public class LocalServerChannel extends AbstractServerChannel { } ChannelPipeline pipeline = pipeline(); - MessageBuf buf = pipeline.inboundMessageBuffer(); - if (buf.isEmpty()) { + Queue inboundBuffer = this.inboundBuffer; + if (inboundBuffer.isEmpty()) { acceptInProgress = true; return; } - pipeline.fireInboundBufferUpdated(); + Object[] messages = inboundBuffer.toArray(); + inboundBuffer.clear(); + pipeline.fireMessageReceived(messages); pipeline.fireChannelReadSuspended(); } @@ -157,10 +162,19 @@ public class LocalServerChannel extends AbstractServerChannel { private void serve0(final LocalChannel child) { if (eventLoop().inEventLoop()) { final ChannelPipeline pipeline = pipeline(); - pipeline.inboundMessageBuffer().add(child); + inboundBuffer.add(child); if (acceptInProgress) { acceptInProgress = false; - pipeline.fireInboundBufferUpdated(); + MessageList messages = MessageList.newInstance(); + for (;;) { + Object m = inboundBuffer.poll(); + if (m == null) { + break; + } + messages.add(m); + } + inboundBuffer.clear(); + pipeline.fireMessageReceived(messages); pipeline.fireChannelReadSuspended(); } } else { diff --git a/transport/src/main/java/io/netty/channel/nio/AbstractNioByteChannel.java b/transport/src/main/java/io/netty/channel/nio/AbstractNioByteChannel.java index a591073d26..93fea7f0b8 100755 --- a/transport/src/main/java/io/netty/channel/nio/AbstractNioByteChannel.java +++ b/transport/src/main/java/io/netty/channel/nio/AbstractNioByteChannel.java @@ -17,15 +17,17 @@ package io.netty.channel.nio; import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; +import io.netty.channel.ChannelConfig; import io.netty.channel.ChannelOption; import io.netty.channel.ChannelPipeline; +import io.netty.channel.FileRegion; +import io.netty.channel.MessageList; +import io.netty.channel.RecvByteBufAllocator; import io.netty.channel.socket.ChannelInputShutdownEvent; import java.io.IOException; -import java.nio.channels.ClosedChannelException; import java.nio.channels.SelectableChannel; import java.nio.channels.SelectionKey; -import java.nio.channels.WritableByteChannel; /** * {@link AbstractNioChannel} base class for {@link Channel}s that operate on bytes. @@ -50,11 +52,14 @@ public abstract class AbstractNioByteChannel extends AbstractNioChannel { } private final class NioByteUnsafe extends AbstractNioUnsafe { + private RecvByteBufAllocator.Handle allocHandle; + @Override public void read() { assert eventLoop().inEventLoop(); final SelectionKey key = selectionKey(); - if (!config().isAutoRead()) { + final ChannelConfig config = config(); + if (!config.isAutoRead()) { int interestOps = key.interestOps(); if ((interestOps & readInterestOp) != 0) { // only remove readInterestOp if needed @@ -63,62 +68,37 @@ public abstract class AbstractNioByteChannel extends AbstractNioChannel { } final ChannelPipeline pipeline = pipeline(); - final ByteBuf byteBuf = pipeline.inboundByteBuffer(); - boolean closed = false; - boolean read = false; - boolean firedChannelReadSuspended = false; - try { - expandReadBuffer(byteBuf); - loop: for (;;) { - int localReadAmount = doReadBytes(byteBuf); - if (localReadAmount > 0) { - read = true; - } else if (localReadAmount < 0) { - closed = true; - break; - } - switch (expandReadBuffer(byteBuf)) { - case 0: - // Read all - stop reading. - break loop; - case 1: - // Keep reading until everything is read. - break; - case 2: - // Let the inbound handler drain the buffer and continue reading. - if (read) { - read = false; - pipeline.fireInboundBufferUpdated(); - if (!byteBuf.isWritable()) { - throw new IllegalStateException( - "an inbound handler whose buffer is full must consume at " + - "least one byte."); - } - } - if (!config().isAutoRead()) { - // stop reading until next Channel.read() call - // See https://github.com/netty/netty/issues/1363 - break loop; - } - } + RecvByteBufAllocator.Handle allocHandle = this.allocHandle; + if (allocHandle == null) { + this.allocHandle = allocHandle = config.getRecvByteBufAllocator().newHandle(); + } + + ByteBuf byteBuf = allocHandle.allocate(config.getAllocator()); + boolean closed = false; + Throwable exception = null; + try { + int localReadAmount = doReadBytes(byteBuf); + if (localReadAmount < 0) { + closed = true; } } catch (Throwable t) { - if (read) { - read = false; - pipeline.fireInboundBufferUpdated(); + exception = t; + } finally { + int readBytes = byteBuf.readableBytes(); + allocHandle.record(readBytes); + if (readBytes != 0) { + pipeline.fireMessageReceived(byteBuf); + } else { + byteBuf.release(); } - if (t instanceof IOException) { - closed = true; - } else if (!closed) { - firedChannelReadSuspended = true; - pipeline.fireChannelReadSuspended(); - } - pipeline().fireExceptionCaught(t); - } finally { - if (read) { - pipeline.fireInboundBufferUpdated(); + if (exception != null) { + if (exception instanceof IOException) { + closed = true; + } + + pipeline().fireExceptionCaught(exception); } if (closed) { @@ -131,7 +111,7 @@ public abstract class AbstractNioByteChannel extends AbstractNioChannel { close(voidPromise()); } } - } else if (!firedChannelReadSuspended) { + } else { pipeline.fireChannelReadSuspended(); } } @@ -139,86 +119,55 @@ public abstract class AbstractNioByteChannel extends AbstractNioChannel { } @Override - protected void doFlushByteBuffer(ByteBuf buf) throws Exception { - for (int i = config().getWriteSpinCount() - 1; i >= 0; i --) { - int localFlushedAmount = doWriteBytes(buf, i == 0); - if (localFlushedAmount > 0 || !buf.isReadable()) { - break; - } - } - } + protected int doWrite(MessageList msgs, int index) throws Exception { + Object msg = msgs.get(index); - @Override - protected void doFlushFileRegion(final FlushTask task) throws Exception { - if (javaChannel() instanceof WritableByteChannel) { - TransferTask transferTask = new TransferTask(task, (WritableByteChannel) javaChannel()); - transferTask.transfer(); + if (msg instanceof ByteBuf) { + ByteBuf buf = (ByteBuf) msg; + for (int i = config().getWriteSpinCount() - 1; i >= 0; i --) { + int localFlushedAmount = doWriteBytes(buf, i == 0); + if (localFlushedAmount > 0 || !buf.isReadable()) { + break; + } + } + // We may could optimize this to write multiple buffers at once (scattering) + if (!buf.isReadable()) { + buf.release(); + return 1; + } + } else if (msg instanceof FileRegion) { + FileRegion region = (FileRegion) msg; + + for (int i = config().getWriteSpinCount() - 1; i >= 0; i --) { + long localFlushedAmount = doWriteFileRegion(region, i == 0); + if (localFlushedAmount == -1) { + checkEOF(region); + return 1; + } + if (localFlushedAmount > 0) { + break; + } + } + if (region.transfered() >= region.count()) { + region.release(); + return 1; + } } else { - throw new UnsupportedOperationException("Underlying Channel is not of instance " - + WritableByteChannel.class); + throw new UnsupportedOperationException("Not support writing of message " + msg); } + + return 0; } - private final class TransferTask implements NioTask { - private long writtenBytes; - private final FlushTask task; - private final WritableByteChannel wch; - - TransferTask(FlushTask task, WritableByteChannel wch) { - this.task = task; - this.wch = wch; - } - - void transfer() { - try { - for (;;) { - long localWrittenBytes = task.region().transferTo(wch, writtenBytes); - if (localWrittenBytes == 0) { - // reschedule for write once the channel is writable again - eventLoop().executeWhenWritable( - AbstractNioByteChannel.this, this); - return; - } else if (localWrittenBytes == -1) { - checkEOF(task.region(), writtenBytes); - task.setSuccess(); - return; - } else { - writtenBytes += localWrittenBytes; - task.setProgress(writtenBytes); - - if (writtenBytes >= task.region().count()) { - task.setSuccess(); - return; - } - } - } - } catch (Throwable cause) { - task.setFailure(cause); - } - } - - @Override - public void channelReady(SelectableChannel ch, SelectionKey key) throws Exception { - transfer(); - } - - @Override - public void channelUnregistered(SelectableChannel ch, Throwable cause) throws Exception { - if (cause != null) { - task.setFailure(cause); - return; - } - - if (writtenBytes < task.region().count()) { - if (!isOpen()) { - task.setFailure(new ClosedChannelException()); - } else { - task.setFailure(new IllegalStateException( - "Channel was unregistered before the region could be fully written")); - } - } - } - } + /** + * Write a {@link FileRegion} + * + * @param region the {@link FileRegion} from which the bytes should be written + * @param lastSpin {@code true} if this is the last write try + * @return amount the amount of written bytes + * @throws Exception thrown if an error accour + */ + protected abstract long doWriteFileRegion(FileRegion region, boolean lastSpin) throws Exception; /** * Read bytes into the given {@link ByteBuf} and return the amount. diff --git a/transport/src/main/java/io/netty/channel/nio/AbstractNioMessageChannel.java b/transport/src/main/java/io/netty/channel/nio/AbstractNioMessageChannel.java index c5cce02bfb..180cf07024 100755 --- a/transport/src/main/java/io/netty/channel/nio/AbstractNioMessageChannel.java +++ b/transport/src/main/java/io/netty/channel/nio/AbstractNioMessageChannel.java @@ -15,9 +15,9 @@ */ package io.netty.channel.nio; -import io.netty.buffer.MessageBuf; import io.netty.channel.Channel; import io.netty.channel.ChannelPipeline; +import io.netty.channel.MessageList; import java.io.IOException; import java.nio.channels.SelectableChannel; @@ -28,9 +28,6 @@ import java.nio.channels.SelectionKey; */ public abstract class AbstractNioMessageChannel extends AbstractNioChannel { - // Hard coded for now. - private static final int READ_BATCH_SIZE = 16; - /** * @see {@link AbstractNioChannel#AbstractNioChannel(Channel, Integer, SelectableChannel, int)} */ @@ -58,93 +55,73 @@ public abstract class AbstractNioMessageChannel extends AbstractNioChannel { } final ChannelPipeline pipeline = pipeline(); - final MessageBuf msgBuf = pipeline.inboundMessageBuffer(); boolean closed = false; - boolean read = false; - boolean firedChannelReadSuspended = false; + MessageList msgBuf = MessageList.newInstance(); + Throwable exception = null; loop: for (;;) { - int reads = 0; - try { for (;;) { - int localReadAmount = doReadMessages(msgBuf); - if (localReadAmount > 0) { - read = true; - } else if (localReadAmount == 0) { - break loop; - } else if (localReadAmount < 0) { - closed = true; + int localRead = doReadMessages(msgBuf); + if (localRead == 0) { break loop; } - - if (reads++ > READ_BATCH_SIZE) { - break; + if (localRead < 0) { + closed = true; + break loop; } if (!config().isAutoRead()) { break loop; } } } catch (Throwable t) { - if (read) { - read = false; - pipeline.fireInboundBufferUpdated(); - } - - if (t instanceof IOException) { - closed = true; - } else if (!closed) { - firedChannelReadSuspended = true; - pipeline.fireChannelReadSuspended(); - } - - pipeline().fireExceptionCaught(t); - - // break the loop now + exception = t; break; - } finally { - if (read) { - pipeline.fireInboundBufferUpdated(); - } - if (closed && isOpen()) { - close(voidPromise()); - } else if (!firedChannelReadSuspended) { - pipeline.fireChannelReadSuspended(); - } } } + + pipeline.fireMessageReceived(msgBuf); + + if (exception != null) { + if (exception instanceof IOException) { + closed = true; + } + + pipeline().fireExceptionCaught(exception); + } + + if (closed) { + if (isOpen()) { + close(voidPromise()); + } + } else { + pipeline.fireChannelReadSuspended(); + } } } @Override - protected void doFlushMessageBuffer(MessageBuf buf) throws Exception { + protected int doWrite(MessageList msgs, int index) throws Exception { final int writeSpinCount = config().getWriteSpinCount() - 1; - while (!buf.isEmpty()) { - boolean wrote = false; - for (int i = writeSpinCount; i >= 0; i --) { - int localFlushedAmount = doWriteMessages(buf, i == 0); - if (localFlushedAmount > 0) { - wrote = true; - break; - } - } - - if (!wrote) { - break; + for (int i = writeSpinCount; i >= 0; i --) { + int written = doWriteMessages(msgs, index, i == 0); + if (written > 0) { + return written; } } + return 0; } /** - * Read messages into the given {@link MessageBuf} and return the amount. + * Read messages into the given array and return the amount which was read. */ - protected abstract int doReadMessages(MessageBuf buf) throws Exception; + protected abstract int doReadMessages(MessageList buf) throws Exception; /** - * Write messages form the given {@link MessageBuf} to the underlying {@link java.nio.channels.Channel}. - * @param buf the {@link MessageBuf} from which the bytes should be written + * Write messages to the underlying {@link java.nio.channels.Channel}. + * @param msg Object to write * @param lastSpin {@code true} if this is the last write try - * @return amount the amount of written bytes + * @return written the amount of written messages * @throws Exception thrown if an error accour */ - protected abstract int doWriteMessages(MessageBuf buf, boolean lastSpin) throws Exception; + protected abstract int doWriteMessages(MessageList msg, int index, boolean lastSpin) throws Exception; } diff --git a/transport/src/main/java/io/netty/channel/oio/AbstractOioByteChannel.java b/transport/src/main/java/io/netty/channel/oio/AbstractOioByteChannel.java index 37857503aa..ef8bcb9534 100755 --- a/transport/src/main/java/io/netty/channel/oio/AbstractOioByteChannel.java +++ b/transport/src/main/java/io/netty/channel/oio/AbstractOioByteChannel.java @@ -15,12 +15,13 @@ */ package io.netty.channel.oio; -import io.netty.buffer.BufType; import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; import io.netty.channel.ChannelMetadata; import io.netty.channel.ChannelOption; import io.netty.channel.ChannelPipeline; +import io.netty.channel.FileRegion; +import io.netty.channel.MessageList; import io.netty.channel.socket.ChannelInputShutdownEvent; import java.io.IOException; @@ -31,7 +32,7 @@ import java.io.IOException; public abstract class AbstractOioByteChannel extends AbstractOioChannel { private volatile boolean inputShutdown; - private static final ChannelMetadata METADATA = new ChannelMetadata(BufType.BYTE, false); + private static final ChannelMetadata METADATA = new ChannelMetadata(false); /** * @see AbstractOioByteChannel#AbstractOioByteChannel(Channel, Integer) @@ -72,7 +73,9 @@ public abstract class AbstractOioByteChannel extends AbstractOioChannel { } final ChannelPipeline pipeline = pipeline(); - final ByteBuf byteBuf = pipeline.inboundByteBuffer(); + + // TODO: calculate size as in 3.x + ByteBuf byteBuf = alloc().buffer(); boolean closed = false; boolean read = false; boolean firedInboundBufferSuspeneded = false; @@ -96,12 +99,8 @@ public abstract class AbstractOioByteChannel extends AbstractOioChannel { if (capacity == maxCapacity) { if (read) { read = false; - pipeline.fireInboundBufferUpdated(); - if (!byteBuf.isWritable()) { - throw new IllegalStateException( - "an inbound handler whose buffer is full must consume at " + - "least one byte."); - } + pipeline.fireMessageReceived(byteBuf); + byteBuf = alloc().buffer(); } } else { final int writerIndex = byteBuf.writerIndex(); @@ -121,7 +120,7 @@ public abstract class AbstractOioByteChannel extends AbstractOioChannel { } catch (Throwable t) { if (read) { read = false; - pipeline.fireInboundBufferUpdated(); + pipeline.fireMessageReceived(byteBuf); } if (t instanceof IOException) { @@ -135,7 +134,10 @@ public abstract class AbstractOioByteChannel extends AbstractOioChannel { } } finally { if (read) { - pipeline.fireInboundBufferUpdated(); + pipeline.fireMessageReceived(byteBuf); + } else { + // nothing read into the buffer so release it + byteBuf.release(); } if (closed) { inputShutdown = true; @@ -153,11 +155,23 @@ public abstract class AbstractOioByteChannel extends AbstractOioChannel { } @Override - protected void doFlushByteBuffer(ByteBuf buf) throws Exception { - while (buf.isReadable()) { - doWriteBytes(buf); + protected int doWrite(MessageList msgs, int index) throws Exception { + Object msg = msgs.get(index); + if (msg instanceof ByteBuf) { + ByteBuf buf = (ByteBuf) msg; + while (buf.isReadable()) { + doWriteBytes(buf); + } + buf.release(); + return 1; + } else if (msg instanceof FileRegion) { + FileRegion region = (FileRegion) msg; + doWriteFileRegion(region); + region.release(); + return 1; + } else { + throw new UnsupportedOperationException(); } - buf.clear(); } /** @@ -182,4 +196,12 @@ public abstract class AbstractOioByteChannel extends AbstractOioChannel { * @throws Exception is thrown if an error accoured */ protected abstract void doWriteBytes(ByteBuf buf) throws Exception; + + /** + * Write the data which is hold by the {@link FileRegion} to the underlying Socket. + * + * @param region the {@link FileRegion} which holds the data to transfer + * @throws Exception is thrown if an error accoured + */ + protected abstract void doWriteFileRegion(FileRegion region) throws Exception; } diff --git a/transport/src/main/java/io/netty/channel/oio/AbstractOioMessageChannel.java b/transport/src/main/java/io/netty/channel/oio/AbstractOioMessageChannel.java index 22c7a4a2c6..36ae20987b 100755 --- a/transport/src/main/java/io/netty/channel/oio/AbstractOioMessageChannel.java +++ b/transport/src/main/java/io/netty/channel/oio/AbstractOioMessageChannel.java @@ -15,9 +15,9 @@ */ package io.netty.channel.oio; -import io.netty.buffer.MessageBuf; import io.netty.channel.Channel; import io.netty.channel.ChannelPipeline; +import io.netty.channel.MessageList; import java.io.IOException; @@ -26,9 +26,6 @@ import java.io.IOException; */ public abstract class AbstractOioMessageChannel extends AbstractOioChannel { - /** - * @see AbstractOioChannel#AbstractOioChannel(Channel, Integer) - */ protected AbstractOioMessageChannel(Channel parent, Integer id) { super(parent, id); } @@ -36,63 +33,39 @@ public abstract class AbstractOioMessageChannel extends AbstractOioChannel { @Override protected void doRead() { final ChannelPipeline pipeline = pipeline(); - final MessageBuf msgBuf = pipeline.inboundMessageBuffer(); boolean closed = false; - boolean read = false; - boolean firedChannelReadSuspended = false; + MessageList msgs = MessageList.newInstance(); + Throwable exception = null; try { - int localReadAmount = doReadMessages(msgBuf); - if (localReadAmount > 0) { - read = true; - } else if (localReadAmount < 0) { + int localReadAmount = doReadMessages(msgs); + if (localReadAmount < 0) { closed = true; } } catch (Throwable t) { - if (read) { - read = false; - pipeline.fireInboundBufferUpdated(); + exception = t; + } + + pipeline.fireMessageReceived(msgs); + + if (exception != null) { + if (exception instanceof IOException) { + closed = true; } - firedChannelReadSuspended = true; + + pipeline().fireExceptionCaught(exception); + } + + if (closed) { + if (isOpen()) { + unsafe().close(unsafe().voidPromise()); + } + } else { pipeline.fireChannelReadSuspended(); - pipeline.fireExceptionCaught(t); - if (t instanceof IOException) { - unsafe().close(unsafe().voidPromise()); - } - } finally { - if (read) { - pipeline.fireInboundBufferUpdated(); - } - if (!firedChannelReadSuspended) { - pipeline.fireChannelReadSuspended(); - } - if (closed && isOpen()) { - unsafe().close(unsafe().voidPromise()); - } - } - } - - @Override - protected void doFlushMessageBuffer(MessageBuf buf) throws Exception { - while (!buf.isEmpty()) { - doWriteMessages(buf); } } /** - * Read Objects from the underlying Socket. - * - * @param buf the {@link MessageBuf} into which the read objects will be written - * @return amount the number of objects read. This may return a negative amount if the underlying - * Socket was closed - * @throws Exception is thrown if an error accoured + * Read messages into the given array and return the amount which was read. */ - protected abstract int doReadMessages(MessageBuf buf) throws Exception; - - /** - * Write the Objects which is hold by the {@link MessageBuf} to the underlying Socket. - * - * @param buf the {@link MessageBuf} which holds the data to transfer - * @throws Exception is thrown if an error accoured - */ - protected abstract void doWriteMessages(MessageBuf buf) throws Exception; + protected abstract int doReadMessages(MessageList msgs) throws Exception; } diff --git a/transport/src/main/java/io/netty/channel/oio/OioByteStreamChannel.java b/transport/src/main/java/io/netty/channel/oio/OioByteStreamChannel.java index b71f900348..dd0d3ab294 100644 --- a/transport/src/main/java/io/netty/channel/oio/OioByteStreamChannel.java +++ b/transport/src/main/java/io/netty/channel/oio/OioByteStreamChannel.java @@ -17,6 +17,7 @@ package io.netty.channel.oio; import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; +import io.netty.channel.FileRegion; import java.io.IOException; import java.io.InputStream; @@ -95,7 +96,7 @@ public abstract class OioByteStreamChannel extends AbstractOioByteChannel { } @Override - protected void doFlushFileRegion(FlushTask task) throws Exception { + protected void doWriteFileRegion(FileRegion region) throws Exception { OutputStream os = this.os; if (os == null) { throw new NotYetConnectedException(); @@ -106,17 +107,14 @@ public abstract class OioByteStreamChannel extends AbstractOioByteChannel { long written = 0; for (;;) { - long localWritten = task.region().transferTo(outChannel, written); + long localWritten = region.transferTo(outChannel, written); if (localWritten == -1) { - checkEOF(task.region(), written); - task.setSuccess(); + checkEOF(region); return; } written += localWritten; - task.setProgress(written); - if (written >= task.region().count()) { - task.setSuccess(); + if (written >= region.count()) { return; } } diff --git a/transport/src/main/java/io/netty/channel/socket/ChannelInputShutdownEvent.java b/transport/src/main/java/io/netty/channel/socket/ChannelInputShutdownEvent.java index 9d98ab16e1..40ed4140b5 100644 --- a/transport/src/main/java/io/netty/channel/socket/ChannelInputShutdownEvent.java +++ b/transport/src/main/java/io/netty/channel/socket/ChannelInputShutdownEvent.java @@ -16,11 +16,11 @@ package io.netty.channel.socket; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelStateHandler; +import io.netty.channel.ChannelInboundHandler; /** * Special event which will be fired and passed to the - * {@link ChannelStateHandler#userEventTriggered(ChannelHandlerContext, Object)} methods once the input of + * {@link ChannelInboundHandler#userEventTriggered(ChannelHandlerContext, Object)} methods once the input of * a {@link SocketChannel} was shutdown and the {@link SocketChannelConfig#isAllowHalfClosure()} method returns * {@code true}. */ diff --git a/transport/src/main/java/io/netty/channel/socket/DatagramChannelConfig.java b/transport/src/main/java/io/netty/channel/socket/DatagramChannelConfig.java index 3ab48b8aeb..a626712376 100644 --- a/transport/src/main/java/io/netty/channel/socket/DatagramChannelConfig.java +++ b/transport/src/main/java/io/netty/channel/socket/DatagramChannelConfig.java @@ -18,6 +18,7 @@ package io.netty.channel.socket; import io.netty.buffer.ByteBufAllocator; import io.netty.channel.ChannelConfig; import io.netty.channel.ChannelOption; +import io.netty.channel.RecvByteBufAllocator; import java.net.InetAddress; import java.net.NetworkInterface; @@ -54,8 +55,6 @@ import java.net.StandardSocketOptions; * {@link ChannelOption#IP_MULTICAST_TTL}{@link #setTimeToLive(int)} * * {@link ChannelOption#IP_TOS}{@link #setTrafficClass(int)} - * - * {@link ChannelOption#UDP_RECEIVE_PACKET_SIZE}{@link #setReceivePacketSize(int)} * * */ @@ -81,18 +80,6 @@ public interface DatagramChannelConfig extends ChannelConfig { */ DatagramChannelConfig setReceiveBufferSize(int receiveBufferSize); - /** - * Gets the size of the {@link DatagramPacket#data()} which will be used to store the received data. - * This should match the maximal packet size that you expect to receive. - */ - int getReceivePacketSize(); - - /** - * Sets the size of the {@link DatagramPacket#data()} which will be used to store the received data. - * This should match the maximal packet size that you expect to receive. - */ - DatagramChannelConfig setReceivePacketSize(int receivePacketSize); - /** * Gets the {@link StandardSocketOptions#IP_TOS} option. */ @@ -178,8 +165,8 @@ public interface DatagramChannelConfig extends ChannelConfig { DatagramChannelConfig setAllocator(ByteBufAllocator allocator); @Override - DatagramChannelConfig setAutoRead(boolean autoRead); + DatagramChannelConfig setRecvByteBufAllocator(RecvByteBufAllocator allocator); @Override - DatagramChannelConfig setDefaultHandlerByteBufType(ChannelHandlerByteBufType type); + DatagramChannelConfig setAutoRead(boolean autoRead); } diff --git a/transport/src/main/java/io/netty/channel/socket/DefaultDatagramChannelConfig.java b/transport/src/main/java/io/netty/channel/socket/DefaultDatagramChannelConfig.java index 559a70c74c..a542754a0e 100644 --- a/transport/src/main/java/io/netty/channel/socket/DefaultDatagramChannelConfig.java +++ b/transport/src/main/java/io/netty/channel/socket/DefaultDatagramChannelConfig.java @@ -16,10 +16,11 @@ package io.netty.channel.socket; import io.netty.buffer.ByteBufAllocator; -import io.netty.channel.ChannelConfig; import io.netty.channel.ChannelException; import io.netty.channel.ChannelOption; import io.netty.channel.DefaultChannelConfig; +import io.netty.channel.FixedRecvByteBufAllocator; +import io.netty.channel.RecvByteBufAllocator; import io.netty.util.internal.PlatformDependent; import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; @@ -41,10 +42,9 @@ public class DefaultDatagramChannelConfig extends DefaultChannelConfig implement private static final InternalLogger logger = InternalLoggerFactory.getInstance(DefaultDatagramChannelConfig.class); - private static final int DEFAULT_RECEIVE_PACKET_SIZE = 2048; + private static final RecvByteBufAllocator DEFAULT_RCVBUF_ALLOCATOR = new FixedRecvByteBufAllocator(2048); private final DatagramSocket javaSocket; - private volatile int receivePacketSize = DEFAULT_RECEIVE_PACKET_SIZE; /** * Creates a new instance. @@ -55,6 +55,7 @@ public class DefaultDatagramChannelConfig extends DefaultChannelConfig implement throw new NullPointerException("javaSocket"); } this.javaSocket = javaSocket; + setRecvByteBufAllocator(DEFAULT_RCVBUF_ALLOCATOR); } @Override @@ -62,7 +63,7 @@ public class DefaultDatagramChannelConfig extends DefaultChannelConfig implement return getOptions( super.getOptions(), SO_BROADCAST, SO_RCVBUF, SO_SNDBUF, SO_REUSEADDR, IP_MULTICAST_LOOP_DISABLED, - IP_MULTICAST_ADDR, IP_MULTICAST_IF, IP_MULTICAST_TTL, IP_TOS, UDP_RECEIVE_PACKET_SIZE); + IP_MULTICAST_ADDR, IP_MULTICAST_IF, IP_MULTICAST_TTL, IP_TOS); } @SuppressWarnings("unchecked") @@ -77,9 +78,6 @@ public class DefaultDatagramChannelConfig extends DefaultChannelConfig implement if (option == SO_SNDBUF) { return (T) Integer.valueOf(getSendBufferSize()); } - if (option == UDP_RECEIVE_PACKET_SIZE) { - return (T) Integer.valueOf(getReceivePacketSize()); - } if (option == SO_REUSEADDR) { return (T) Boolean.valueOf(isReuseAddress()); } @@ -126,8 +124,6 @@ public class DefaultDatagramChannelConfig extends DefaultChannelConfig implement setTimeToLive((Integer) value); } else if (option == IP_TOS) { setTrafficClass((Integer) value); - } else if (option == UDP_RECEIVE_PACKET_SIZE) { - setReceivePacketSize((Integer) value); } else { return super.setOption(option, value); } @@ -305,21 +301,6 @@ public class DefaultDatagramChannelConfig extends DefaultChannelConfig implement return this; } - @Override - public int getReceivePacketSize() { - return receivePacketSize; - } - - @Override - public DatagramChannelConfig setReceivePacketSize(int receivePacketSize) { - if (receivePacketSize <= 0) { - throw new IllegalArgumentException( - String.format("receivePacketSize: %d (expected: > 0)", receivePacketSize)); - } - this.receivePacketSize = receivePacketSize; - return this; - } - @Override public int getTimeToLive() { if (javaSocket instanceof MulticastSocket) { @@ -381,13 +362,24 @@ public class DefaultDatagramChannelConfig extends DefaultChannelConfig implement return (DatagramChannelConfig) super.setAllocator(allocator); } + @Override + public DatagramChannelConfig setRecvByteBufAllocator(RecvByteBufAllocator allocator) { + super.setRecvByteBufAllocator(allocator); + return this; + } + @Override public DatagramChannelConfig setAutoRead(boolean autoRead) { return (DatagramChannelConfig) super.setAutoRead(autoRead); } @Override - public DatagramChannelConfig setDefaultHandlerByteBufType(ChannelHandlerByteBufType type) { - return (DatagramChannelConfig) super.setDefaultHandlerByteBufType(type); + public DatagramChannelConfig setWriteBufferHighWaterMark(int writeBufferHighWaterMark) { + return (DatagramChannelConfig) super.setWriteBufferHighWaterMark(writeBufferHighWaterMark); + } + + @Override + public DatagramChannelConfig setWriteBufferLowWaterMark(int writeBufferLowWaterMark) { + return (DatagramChannelConfig) super.setWriteBufferLowWaterMark(writeBufferLowWaterMark); } } diff --git a/transport/src/main/java/io/netty/channel/socket/DefaultServerSocketChannelConfig.java b/transport/src/main/java/io/netty/channel/socket/DefaultServerSocketChannelConfig.java index e97752bf0d..02a92e89bf 100644 --- a/transport/src/main/java/io/netty/channel/socket/DefaultServerSocketChannelConfig.java +++ b/transport/src/main/java/io/netty/channel/socket/DefaultServerSocketChannelConfig.java @@ -19,6 +19,7 @@ import io.netty.buffer.ByteBufAllocator; import io.netty.channel.ChannelException; import io.netty.channel.ChannelOption; import io.netty.channel.DefaultChannelConfig; +import io.netty.channel.RecvByteBufAllocator; import io.netty.util.NetUtil; import java.net.ServerSocket; @@ -158,13 +159,24 @@ public class DefaultServerSocketChannelConfig extends DefaultChannelConfig return (ServerSocketChannelConfig) super.setAllocator(allocator); } + @Override + public ServerSocketChannelConfig setRecvByteBufAllocator(RecvByteBufAllocator allocator) { + super.setRecvByteBufAllocator(allocator); + return this; + } + @Override public ServerSocketChannelConfig setAutoRead(boolean autoRead) { return (ServerSocketChannelConfig) super.setAutoRead(autoRead); } @Override - public ServerSocketChannelConfig setDefaultHandlerByteBufType(ChannelHandlerByteBufType type) { - return (ServerSocketChannelConfig) super.setDefaultHandlerByteBufType(type); + public ServerSocketChannelConfig setWriteBufferHighWaterMark(int writeBufferHighWaterMark) { + return (ServerSocketChannelConfig) super.setWriteBufferHighWaterMark(writeBufferHighWaterMark); + } + + @Override + public ServerSocketChannelConfig setWriteBufferLowWaterMark(int writeBufferLowWaterMark) { + return (ServerSocketChannelConfig) super.setWriteBufferLowWaterMark(writeBufferLowWaterMark); } } diff --git a/transport/src/main/java/io/netty/channel/socket/DefaultSocketChannelConfig.java b/transport/src/main/java/io/netty/channel/socket/DefaultSocketChannelConfig.java index bbe14c825b..62cefb8899 100644 --- a/transport/src/main/java/io/netty/channel/socket/DefaultSocketChannelConfig.java +++ b/transport/src/main/java/io/netty/channel/socket/DefaultSocketChannelConfig.java @@ -20,6 +20,7 @@ import io.netty.channel.ChannelConfig; import io.netty.channel.ChannelException; import io.netty.channel.ChannelOption; import io.netty.channel.DefaultChannelConfig; +import io.netty.channel.RecvByteBufAllocator; import io.netty.util.internal.PlatformDependent; import java.net.Socket; @@ -293,13 +294,24 @@ public class DefaultSocketChannelConfig extends DefaultChannelConfig return (SocketChannelConfig) super.setAllocator(allocator); } + @Override + public SocketChannelConfig setRecvByteBufAllocator(RecvByteBufAllocator allocator) { + super.setRecvByteBufAllocator(allocator); + return this; + } + @Override public SocketChannelConfig setAutoRead(boolean autoRead) { return (SocketChannelConfig) super.setAutoRead(autoRead); } @Override - public SocketChannelConfig setDefaultHandlerByteBufType(ChannelHandlerByteBufType type) { - return (SocketChannelConfig) super.setDefaultHandlerByteBufType(type); + public SocketChannelConfig setWriteBufferHighWaterMark(int writeBufferHighWaterMark) { + return (SocketChannelConfig) super.setWriteBufferHighWaterMark(writeBufferHighWaterMark); + } + + @Override + public SocketChannelConfig setWriteBufferLowWaterMark(int writeBufferLowWaterMark) { + return (SocketChannelConfig) super.setWriteBufferLowWaterMark(writeBufferLowWaterMark); } } diff --git a/transport/src/main/java/io/netty/channel/socket/ServerSocketChannelConfig.java b/transport/src/main/java/io/netty/channel/socket/ServerSocketChannelConfig.java index 95c5ecc378..d9cf203ad1 100644 --- a/transport/src/main/java/io/netty/channel/socket/ServerSocketChannelConfig.java +++ b/transport/src/main/java/io/netty/channel/socket/ServerSocketChannelConfig.java @@ -17,6 +17,7 @@ package io.netty.channel.socket; import io.netty.buffer.ByteBufAllocator; import io.netty.channel.ChannelConfig; +import io.netty.channel.RecvByteBufAllocator; import java.net.ServerSocket; import java.net.StandardSocketOptions; @@ -92,8 +93,8 @@ public interface ServerSocketChannelConfig extends ChannelConfig { ServerSocketChannelConfig setAllocator(ByteBufAllocator allocator); @Override - ServerSocketChannelConfig setAutoRead(boolean autoRead); + ServerSocketChannelConfig setRecvByteBufAllocator(RecvByteBufAllocator allocator); @Override - ServerSocketChannelConfig setDefaultHandlerByteBufType(ChannelHandlerByteBufType type); + ServerSocketChannelConfig setAutoRead(boolean autoRead); } diff --git a/transport/src/main/java/io/netty/channel/socket/SocketChannelConfig.java b/transport/src/main/java/io/netty/channel/socket/SocketChannelConfig.java index 8dc827324a..1a0aa2a8d0 100644 --- a/transport/src/main/java/io/netty/channel/socket/SocketChannelConfig.java +++ b/transport/src/main/java/io/netty/channel/socket/SocketChannelConfig.java @@ -18,8 +18,9 @@ package io.netty.channel.socket; import io.netty.buffer.ByteBufAllocator; import io.netty.channel.ChannelConfig; import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandler; import io.netty.channel.ChannelOption; -import io.netty.channel.ChannelStateHandler; +import io.netty.channel.RecvByteBufAllocator; import java.net.Socket; import java.net.StandardSocketOptions; @@ -149,7 +150,7 @@ public interface SocketChannelConfig extends ChannelConfig { * Sets whether the channel should not close itself when its remote peer shuts down output to * make the connection half-closed. If {@code true} the connection is not closed when the * remote peer shuts down output. Instead, - * {@link ChannelStateHandler#userEventTriggered(ChannelHandlerContext, Object)} + * {@link ChannelInboundHandler#userEventTriggered(ChannelHandlerContext, Object)} * is invoked with a {@link ChannelInputShutdownEvent} object. If {@code false}, the connection * is closed automatically. */ @@ -165,8 +166,8 @@ public interface SocketChannelConfig extends ChannelConfig { SocketChannelConfig setAllocator(ByteBufAllocator allocator); @Override - SocketChannelConfig setAutoRead(boolean autoRead); + SocketChannelConfig setRecvByteBufAllocator(RecvByteBufAllocator allocator); @Override - SocketChannelConfig setDefaultHandlerByteBufType(ChannelHandlerByteBufType type); + SocketChannelConfig setAutoRead(boolean autoRead); } diff --git a/transport/src/main/java/io/netty/channel/socket/aio/AioServerSocketChannel.java b/transport/src/main/java/io/netty/channel/socket/aio/AioServerSocketChannel.java index 89a6e99365..8e78141d86 100755 --- a/transport/src/main/java/io/netty/channel/socket/aio/AioServerSocketChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/aio/AioServerSocketChannel.java @@ -15,13 +15,12 @@ */ package io.netty.channel.socket.aio; -import io.netty.buffer.BufType; -import io.netty.buffer.MessageBuf; import io.netty.channel.ChannelException; import io.netty.channel.ChannelMetadata; import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPromise; import io.netty.channel.EventLoop; +import io.netty.channel.MessageList; import io.netty.channel.aio.AbstractAioChannel; import io.netty.channel.aio.AioCompletionHandler; import io.netty.channel.aio.AioEventLoopGroup; @@ -37,6 +36,7 @@ import java.nio.channels.AsynchronousChannelGroup; import java.nio.channels.AsynchronousCloseException; import java.nio.channels.AsynchronousServerSocketChannel; import java.nio.channels.AsynchronousSocketChannel; +import java.nio.channels.CompletionHandler; /** * {@link ServerSocketChannel} implementation which uses NIO2. @@ -45,9 +45,9 @@ import java.nio.channels.AsynchronousSocketChannel; */ public class AioServerSocketChannel extends AbstractAioChannel implements ServerSocketChannel { - private static final ChannelMetadata METADATA = new ChannelMetadata(BufType.MESSAGE, false); + private static final ChannelMetadata METADATA = new ChannelMetadata(false); - private static final AcceptHandler ACCEPT_HANDLER = new AcceptHandler(); + private final CompletionHandler acceptHandler = new AcceptHandler(this); private static final InternalLogger logger = InternalLoggerFactory.getInstance(AioServerSocketChannel.class); @@ -138,7 +138,7 @@ public class AioServerSocketChannel extends AbstractAioChannel implements Server } acceptInProgress = true; - javaChannel().accept(this, ACCEPT_HANDLER); + javaChannel().accept(null, acceptHandler); } @Override @@ -165,6 +165,11 @@ public class AioServerSocketChannel extends AbstractAioChannel implements Server throw new UnsupportedOperationException(); } + @Override + protected int doWrite(MessageList msgs, int index) throws Exception { + throw new UnsupportedOperationException(); + } + @Override protected Runnable doRegister() throws Exception { Runnable task = super.doRegister(); @@ -178,34 +183,25 @@ public class AioServerSocketChannel extends AbstractAioChannel implements Server } private static final class AcceptHandler - extends AioCompletionHandler { + extends AioCompletionHandler { + + AcceptHandler(AioServerSocketChannel channel) { + super(channel); + } @Override - protected void completed0(AsynchronousSocketChannel ch, AioServerSocketChannel channel) { + protected void completed0(AioServerSocketChannel channel, AsynchronousSocketChannel ch, Void attachment) { channel.acceptInProgress = false; ChannelPipeline pipeline = channel.pipeline(); - MessageBuf buffer = pipeline.inboundMessageBuffer(); - if (buffer.refCnt() == 0) { - try { - ch.close(); - } catch (IOException e) { - logger.warn( - "Failed to close a socket which was accepted while its server socket is being closed", - e); - } - return; - } - - // create the socket add it to the buffer and fire the event - buffer.add(new AioSocketChannel(channel, null, ch)); - pipeline.fireInboundBufferUpdated(); + // Create a new Netty channel from a JDK channel and trigger events. + pipeline.fireMessageReceived(new AioSocketChannel(channel, null, ch)); pipeline.fireChannelReadSuspended(); } @Override - protected void failed0(Throwable t, AioServerSocketChannel channel) { + protected void failed0(AioServerSocketChannel channel, Throwable t, Void attachment) { channel.acceptInProgress = false; boolean asyncClosed = false; if (t instanceof AsynchronousCloseException) { diff --git a/transport/src/main/java/io/netty/channel/socket/aio/AioServerSocketChannelConfig.java b/transport/src/main/java/io/netty/channel/socket/aio/AioServerSocketChannelConfig.java index be909e8aff..84b87d0f73 100644 --- a/transport/src/main/java/io/netty/channel/socket/aio/AioServerSocketChannelConfig.java +++ b/transport/src/main/java/io/netty/channel/socket/aio/AioServerSocketChannelConfig.java @@ -19,6 +19,7 @@ import io.netty.buffer.ByteBufAllocator; import io.netty.channel.ChannelException; import io.netty.channel.ChannelOption; import io.netty.channel.DefaultChannelConfig; +import io.netty.channel.RecvByteBufAllocator; import io.netty.channel.socket.ServerSocketChannelConfig; import io.netty.util.NetUtil; import io.netty.util.internal.PlatformDependent; @@ -218,6 +219,12 @@ final class AioServerSocketChannelConfig extends DefaultChannelConfig implements return this; } + @Override + public AioServerSocketChannelConfig setRecvByteBufAllocator(RecvByteBufAllocator allocator) { + super.setRecvByteBufAllocator(allocator); + return this; + } + @Override public AioServerSocketChannelConfig setAutoRead(boolean autoRead) { super.setAutoRead(autoRead); @@ -225,8 +232,14 @@ final class AioServerSocketChannelConfig extends DefaultChannelConfig implements } @Override - public ServerSocketChannelConfig setDefaultHandlerByteBufType(ChannelHandlerByteBufType type) { - super.setDefaultHandlerByteBufType(type); + public AioServerSocketChannelConfig setWriteBufferLowWaterMark(int writeBufferLowWaterMark) { + super.setWriteBufferLowWaterMark(writeBufferLowWaterMark); + return this; + } + + @Override + public AioServerSocketChannelConfig setWriteBufferHighWaterMark(int writeBufferHighWaterMark) { + super.setWriteBufferHighWaterMark(writeBufferHighWaterMark); return this; } } diff --git a/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java b/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java index 42fceac50f..4c00360790 100755 --- a/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannel.java @@ -15,22 +15,22 @@ */ package io.netty.channel.socket.aio; -import io.netty.buffer.BufType; import io.netty.buffer.ByteBuf; -import io.netty.channel.Channel; import io.netty.channel.ChannelException; -import io.netty.channel.ChannelFlushPromiseNotifier; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelMetadata; import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPromise; import io.netty.channel.EventLoop; +import io.netty.channel.FileRegion; +import io.netty.channel.MessageList; import io.netty.channel.aio.AbstractAioChannel; import io.netty.channel.aio.AioCompletionHandler; import io.netty.channel.aio.AioEventLoopGroup; import io.netty.channel.socket.ChannelInputShutdownEvent; import io.netty.channel.socket.ServerSocketChannel; import io.netty.channel.socket.SocketChannel; +import io.netty.util.internal.PlatformDependent; import java.io.IOException; import java.net.InetSocketAddress; @@ -52,13 +52,13 @@ import java.util.concurrent.TimeUnit; */ public class AioSocketChannel extends AbstractAioChannel implements SocketChannel { - private static final ChannelMetadata METADATA = new ChannelMetadata(BufType.BYTE, false); + private static final ChannelMetadata METADATA = new ChannelMetadata(false); - private static final CompletionHandler CONNECT_HANDLER = new ConnectHandler(); - private static final CompletionHandler WRITE_HANDLER = new WriteHandler(); - private static final CompletionHandler READ_HANDLER = new ReadHandler(); - private static final CompletionHandler GATHERING_WRITE_HANDLER = new WriteHandler(); - private static final CompletionHandler SCATTERING_READ_HANDLER = new ReadHandler(); + private final CompletionHandler connectHandler = new ConnectHandler(this); + private final CompletionHandler writeHandler = new WriteHandler(this); + private final CompletionHandler readHandler = new ReadHandler(this); + private final CompletionHandler gatheringWriteHandler = new WriteHandler(this); + private final CompletionHandler scatteringReadHandler = new ReadHandler(this); private static AsynchronousSocketChannel newSocket(AsynchronousChannelGroup group) { try { @@ -76,12 +76,10 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne private boolean inDoBeginRead; private boolean readAgain; - private static final int NO_WRITE_IN_PROGRESS = 0; - private static final int WRITE_IN_PROGRESS = 1; - private static final int WRITE_FAILED = -2; - - private int writeInProgress; - private boolean inDoFlushByteBuffer; + private Throwable writeException; + private boolean writeInProgress; + private boolean inDoWrite; + private boolean fileRegionDone; /** * Create a new instance which has not yet attached an {@link AsynchronousSocketChannel}. The @@ -188,7 +186,7 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne } } - javaChannel().connect(remoteAddress, this, CONNECT_HANDLER); + javaChannel().connect(remoteAddress, null, connectHandler); } @Override @@ -249,72 +247,99 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne } @Override - protected void doFlushByteBuffer(ByteBuf buf) throws Exception { - if (inDoFlushByteBuffer || writeInProgress != NO_WRITE_IN_PROGRESS) { - return; + protected int doWrite(MessageList msgs, int index) throws Exception { + if (inDoWrite || writeInProgress) { + return 0; } - - inDoFlushByteBuffer = true; + inDoWrite = true; try { - if (buf.isReadable()) { - for (;;) { - if (buf.refCnt() == 0) { - break; - } - // Ensure the readerIndex of the buffer is 0 before beginning an async write. - // Otherwise, JDK can write into a wrong region of the buffer when a handler calls - // discardReadBytes() later, modifying the readerIndex and the writerIndex unexpectedly. - buf.discardReadBytes(); - - writeInProgress = WRITE_IN_PROGRESS; - if (buf.nioBufferCount() == 1) { - javaChannel().write( - buf.nioBuffer(), config.getWriteTimeout(), TimeUnit.MILLISECONDS, this, WRITE_HANDLER); - } else { - ByteBuffer[] buffers = buf.nioBuffers(buf.readerIndex(), buf.readableBytes()); - if (buffers.length == 1) { - javaChannel().write( - buffers[0], config.getWriteTimeout(), TimeUnit.MILLISECONDS, this, WRITE_HANDLER); - } else { - javaChannel().write( - buffers, 0, buffers.length, config.getWriteTimeout(), TimeUnit.MILLISECONDS, - this, GATHERING_WRITE_HANDLER); - } - } - - if (writeInProgress != NO_WRITE_IN_PROGRESS) { - if (writeInProgress == WRITE_FAILED) { - // failed because of an exception so reset state and break out of the loop now - // See #1242 - writeInProgress = NO_WRITE_IN_PROGRESS; - break; - } - // JDK decided to write data (or notify handler) later. - buf.suspendIntermediaryDeallocations(); - break; - } - - // JDK performed the write operation immediately and notified the handler. - // We know this because we set asyncWriteInProgress to false in the handler. - if (!buf.isReadable()) { - // There's nothing left in the buffer. No need to retry writing. - break; - } - - // There's more to write. Continue the loop. + Object msg = msgs.get(index); + if (msg instanceof ByteBuf) { + if (doWriteBuffer((ByteBuf) msg)) { + return 1; } - } else { - flushFutureNotifier.notifyFlushFutures(); + return 0; + } + if (msg instanceof FileRegion) { + if (doWriteFileRegion((FileRegion) msg)) { + return 1; + } + return 0; } } finally { - inDoFlushByteBuffer = false; + inDoWrite = false; } + return 0; } - @Override - protected void doFlushFileRegion(FlushTask task) throws Exception { - task.region().transferTo(new WritableByteChannelAdapter(task), 0); + private boolean doWriteBuffer(ByteBuf buf) throws Exception { + if (buf.isReadable()) { + for (;;) { + checkWriteException(); + writeInProgress = true; + if (buf.nioBufferCount() == 1) { + javaChannel().write( + buf.nioBuffer(), config.getWriteTimeout(), TimeUnit.MILLISECONDS, + buf, writeHandler); + } else { + ByteBuffer[] buffers = buf.nioBuffers(buf.readerIndex(), buf.readableBytes()); + if (buffers.length == 1) { + javaChannel().write( + buffers[0], config.getWriteTimeout(), TimeUnit.MILLISECONDS, buf, writeHandler); + } else { + javaChannel().write( + buffers, 0, buffers.length, config.getWriteTimeout(), TimeUnit.MILLISECONDS, + buf, gatheringWriteHandler); + } + } + + if (writeInProgress) { + // JDK decided to write data (or notify handler) later. + return false; + } + checkWriteException(); + + // JDK performed the write operation immediately and notified the handler. + // We know this because we set asyncWriteInProgress to false in the handler. + if (!buf.isReadable()) { + // There's nothing left in the buffer. No need to retry writing. + return true; + } + + // There's more to write. Continue the loop. + } + } + return true; + } + + private boolean doWriteFileRegion(FileRegion region) throws Exception { + checkWriteException(); + + if (fileRegionDone) { + // fileregion was complete in the CompletionHandler + fileRegionDone = false; + // was written complete + return true; + } + + WritableByteChannelAdapter byteChannel = new WritableByteChannelAdapter(region); + region.transferTo(byteChannel, 0); + + // check if the FileRegion is already complete. This may be the case if all could be written directly + if (byteChannel.written >= region.count()) { + return true; + } + return false; + } + + private void checkWriteException() throws Exception { + if (writeException != null) { + fileRegionDone = false; + Throwable e = writeException; + writeException = null; + PlatformDependent.throwException(e); + } } @Override @@ -335,31 +360,23 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne break; } - ByteBuf byteBuf = pipeline().inboundByteBuffer(); - - // Ensure the readerIndex of the buffer is 0 before beginning an async read. - // Otherwise, JDK can read into a wrong region of the buffer when a handler calls - // discardReadBytes() later, modifying the readerIndex and the writerIndex unexpectedly. - // See https://github.com/netty/netty/issues/1377 - byteBuf.discardReadBytes(); - - expandReadBuffer(byteBuf); + ByteBuf byteBuf = alloc().buffer(); readInProgress = true; if (byteBuf.nioBufferCount() == 1) { // Get a ByteBuffer view on the ByteBuf ByteBuffer buffer = byteBuf.nioBuffer(byteBuf.writerIndex(), byteBuf.writableBytes()); javaChannel().read( - buffer, config.getReadTimeout(), TimeUnit.MILLISECONDS, this, READ_HANDLER); + buffer, config.getReadTimeout(), TimeUnit.MILLISECONDS, byteBuf, readHandler); } else { ByteBuffer[] buffers = byteBuf.nioBuffers(byteBuf.writerIndex(), byteBuf.writableBytes()); if (buffers.length == 1) { javaChannel().read( - buffers[0], config.getReadTimeout(), TimeUnit.MILLISECONDS, this, READ_HANDLER); + buffers[0], config.getReadTimeout(), TimeUnit.MILLISECONDS, byteBuf, readHandler); } else { javaChannel().read( buffers, 0, buffers.length, config.getReadTimeout(), TimeUnit.MILLISECONDS, - this, SCATTERING_READ_HANDLER); + byteBuf, scatteringReadHandler); } } @@ -381,51 +398,53 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne } } - private static final class WriteHandler extends AioCompletionHandler { + private void setWriteException(Throwable cause) { + writeException = cause; + } + + private static final class WriteHandler + extends AioCompletionHandler { + + WriteHandler(AioSocketChannel channel) { + super(channel); + } @Override - protected void completed0(T result, AioSocketChannel channel) { - channel.writeInProgress = NO_WRITE_IN_PROGRESS; + protected void completed0(AioSocketChannel channel, T result, ByteBuf buf) { + channel.writeException = null; + channel.writeInProgress = false; + boolean release = true; + try { + int writtenBytes = result.intValue(); + if (writtenBytes > 0) { + // Update the readerIndex with the amount of read bytes + buf.readerIndex(buf.readerIndex() + writtenBytes); + } + if (buf.isReadable()) { + // something left in the buffer so not release it + release = false; + } + } finally { + if (release) { + buf.release(); + } - ByteBuf buf = channel.unsafe().headContext().outboundByteBuffer(); - if (buf.refCnt() == 0) { - return; - } - - buf.resumeIntermediaryDeallocations(); - - int writtenBytes = result.intValue(); - if (writtenBytes > 0) { - // Update the readerIndex with the amount of read bytes - buf.readerIndex(buf.readerIndex() + writtenBytes); - } - - if (channel.inDoFlushByteBuffer) { - // JDK performed the write operation immediately and notified this handler immediately. - // doFlushByteBuffer() will do subsequent write operations if necessary for us. - return; - } - - // Update the write counter and notify flush futures only when the handler is called outside of - // unsafe().flushNow() because flushNow() will do that for us. - ChannelFlushPromiseNotifier notifier = channel.flushFutureNotifier; - notifier.increaseWriteCounter(writtenBytes); - notifier.notifyFlushFutures(); - - // Stop flushing if disconnected. - if (!channel.isActive()) { - return; - } - - if (buf.isReadable()) { - channel.unsafe().flushNow(); + if (channel.inDoWrite) { + // JDK performed the write operation immediately and notified this handler immediately. + // doWrite(...) will do subsequent write operations if necessary for us. + } else { + // trigger flush so doWrite(..) is called again. This will either trigger a new write to the + // channel or remove the empty bytebuf (which was written completely before) from the MessageList. + channel.unsafe().flushNow(); + } } } @Override - protected void failed0(Throwable cause, AioSocketChannel channel) { - channel.writeInProgress = WRITE_FAILED; - channel.flushFutureNotifier.notifyFlushFutures(cause); + protected void failed0(AioSocketChannel channel, Throwable cause, ByteBuf buf) { + buf.release(); + channel.setWriteException(cause); + channel.writeInProgress = false; // Check if the exception was raised because of an InterruptedByTimeoutException which means that the // write timeout was hit. In that case we should close the channel as it may be unusable anyway. @@ -434,13 +453,23 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne if (cause instanceof InterruptedByTimeoutException) { channel.unsafe().close(channel.unsafe().voidPromise()); } + + if (!channel.inDoWrite) { + // trigger flushNow() so the Throwable is thrown in doWrite(...). This will make sure that all + // queued MessageLists are failed. + channel.unsafe().flushNow(); + } } } - private static final class ReadHandler extends AioCompletionHandler { + private final class ReadHandler extends AioCompletionHandler { + + ReadHandler(AioSocketChannel channel) { + super(channel); + } @Override - protected void completed0(T result, AioSocketChannel channel) { + protected void completed0(AioSocketChannel channel, T result, ByteBuf byteBuf) { channel.readInProgress = false; if (channel.inputShutdown) { @@ -449,61 +478,71 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne return; } + boolean release = true; final ChannelPipeline pipeline = channel.pipeline(); - final ByteBuf byteBuf = pipeline.inboundByteBuffer(); - - boolean closed = false; - boolean read = false; - boolean firedChannelReadSuspended = false; try { - int localReadAmount = result.intValue(); - if (localReadAmount > 0) { - // Set the writerIndex of the buffer correctly to the - // current writerIndex + read amount of bytes. - // - // This is needed as the ByteBuffer and the ByteBuf does not share - // each others index - byteBuf.writerIndex(byteBuf.writerIndex() + localReadAmount); + boolean closed = false; + boolean read = false; + boolean firedChannelReadSuspended = false; + try { + int localReadAmount = result.intValue(); + if (localReadAmount > 0) { + // Set the writerIndex of the buffer correctly to the + // current writerIndex + read amount of bytes. + // + // This is needed as the ByteBuffer and the ByteBuf does not share + // each others index + byteBuf.writerIndex(byteBuf.writerIndex() + localReadAmount); - read = true; - } else if (localReadAmount < 0) { - closed = true; - } - } catch (Throwable t) { - if (read) { - read = false; - pipeline.fireInboundBufferUpdated(); - } - - if (!closed && channel.isOpen()) { - firedChannelReadSuspended = true; - pipeline.fireChannelReadSuspended(); - } - - pipeline.fireExceptionCaught(t); - } finally { - if (read) { - pipeline.fireInboundBufferUpdated(); - } - - // Double check because fireInboundBufferUpdated() might have triggered the closure by a user handler. - if (closed || !channel.isOpen()) { - channel.inputShutdown = true; - if (channel.isOpen()) { - if (channel.config().isAllowHalfClosure()) { - pipeline.fireUserEventTriggered(ChannelInputShutdownEvent.INSTANCE); - } else { - channel.unsafe().close(channel.unsafe().voidPromise()); - } + read = true; + } else if (localReadAmount < 0) { + closed = true; } - } else if (!firedChannelReadSuspended) { - pipeline.fireChannelReadSuspended(); + } catch (Throwable t) { + if (read) { + read = false; + release = false; + pipeline.fireMessageReceived(byteBuf); + } + + if (!closed && isOpen()) { + firedChannelReadSuspended = true; + pipeline.fireChannelReadSuspended(); + } + + pipeline.fireExceptionCaught(t); + } finally { + if (read) { + release = false; + pipeline.fireMessageReceived(byteBuf); + } + + // Double check because fireInboundBufferUpdated() might have triggered + // the closure by a user handler. + if (closed || !channel.isOpen()) { + channel.inputShutdown = true; + if (isOpen()) { + if (channel.config().isAllowHalfClosure()) { + pipeline.fireUserEventTriggered(ChannelInputShutdownEvent.INSTANCE); + } else { + channel.unsafe().close(channel.unsafe().voidPromise()); + } + } + } else if (!firedChannelReadSuspended) { + pipeline.fireChannelReadSuspended(); + } + } + } finally { + if (release) { + byteBuf.release(); } } } @Override - protected void failed0(Throwable t, AioSocketChannel channel) { + protected void failed0(AioSocketChannel channel, Throwable t, ByteBuf buf) { + buf.release(); + channel.readInProgress = false; if (t instanceof ClosedChannelException) { channel.inputShutdown = true; @@ -522,15 +561,18 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne } } - private static final class ConnectHandler extends AioCompletionHandler { + private static final class ConnectHandler extends AioCompletionHandler { + ConnectHandler(AioSocketChannel channel) { + super(channel); + } @Override - protected void completed0(Void result, AioSocketChannel channel) { + protected void completed0(AioSocketChannel channel, Void result, Void attachment) { ((DefaultAioUnsafe) channel.unsafe()).connectSuccess(); } @Override - protected void failed0(Throwable exc, AioSocketChannel channel) { + protected void failed0(AioSocketChannel channel, Throwable exc, Void attachment) { ((DefaultAioUnsafe) channel.unsafe()).connectFailed(exc); } } @@ -541,52 +583,17 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne } private final class WritableByteChannelAdapter implements WritableByteChannel { - private final FlushTask task; + private final FileRegionWriteHandler handler = new FileRegionWriteHandler(); + private final FileRegion region; private long written; - public WritableByteChannelAdapter(FlushTask task) { - this.task = task; + public WritableByteChannelAdapter(FileRegion region) { + this.region = region; } @Override public int write(final ByteBuffer src) { - javaChannel().write(src, AioSocketChannel.this, new AioCompletionHandler() { - - @Override - public void completed0(Integer result, Channel attachment) { - try { - if (result == 0) { - javaChannel().write(src, AioSocketChannel.this, this); - return; - } - if (result == -1) { - checkEOF(task.region(), written); - task.setSuccess(); - return; - } - written += result; - - task.setProgress(written); - - if (written >= task.region().count()) { - task.setSuccess(); - return; - } - if (src.hasRemaining()) { - javaChannel().write(src, AioSocketChannel.this, this); - } else { - task.region().transferTo(WritableByteChannelAdapter.this, written); - } - } catch (Throwable cause) { - task.setFailure(cause); - } - } - - @Override - public void failed0(Throwable exc, Channel attachment) { - task.setFailure(exc); - } - }); + javaChannel().write(src, src, handler); return 0; } @@ -599,6 +606,62 @@ public class AioSocketChannel extends AbstractAioChannel implements SocketChanne public void close() throws IOException { javaChannel().close(); } - } + private final class FileRegionWriteHandler extends AioCompletionHandler { + + FileRegionWriteHandler() { + super(AioSocketChannel.this); + } + + @Override + public void completed0(AioSocketChannel channel, Integer result, ByteBuffer src) { + try { + assert !fileRegionDone; + + if (result == -1) { + checkEOF(region); + // mark the region as done and release it + fileRegionDone = true; + region.release(); + return; + } + + written += result; + + if (written >= region.count()) { + channel.writeInProgress = false; + + // mark the region as done and release it + fileRegionDone = true; + region.release(); + return; + } + if (src.hasRemaining()) { + // something left in the buffer trigger a write again + javaChannel().write(src, src, this); + } else { + // everything was written out of the src buffer, so trigger a new transfer with new data + region.transferTo(WritableByteChannelAdapter.this, written); + } + } catch (Throwable cause) { + failed0(channel, cause, src); + } + } + + @Override + public void failed0(AioSocketChannel channel, Throwable cause, ByteBuffer src) { + assert !fileRegionDone; + + // mark the region as done and release it + fileRegionDone = true; + region.release(); + channel.setWriteException(cause); + if (!inDoWrite) { + // not executed as part of the doWrite(...) so trigger flushNow() to make sure the doWrite(...) + // will be called again and so rethrow the exception + channel.unsafe().flushNow(); + } + } + } + } } diff --git a/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannelConfig.java b/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannelConfig.java index 951e1f9093..c9878eda31 100644 --- a/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannelConfig.java +++ b/transport/src/main/java/io/netty/channel/socket/aio/AioSocketChannelConfig.java @@ -17,6 +17,7 @@ package io.netty.channel.socket.aio; import io.netty.buffer.ByteBufAllocator; import io.netty.channel.ChannelOption; +import io.netty.channel.RecvByteBufAllocator; import io.netty.channel.socket.SocketChannelConfig; import java.nio.channels.InterruptedByTimeoutException; @@ -110,8 +111,8 @@ public interface AioSocketChannelConfig extends SocketChannelConfig { AioSocketChannelConfig setAllocator(ByteBufAllocator allocator); @Override - AioSocketChannelConfig setAutoRead(boolean autoRead); + AioSocketChannelConfig setRecvByteBufAllocator(RecvByteBufAllocator allocator); @Override - AioSocketChannelConfig setDefaultHandlerByteBufType(ChannelHandlerByteBufType type); + AioSocketChannelConfig setAutoRead(boolean autoRead); } diff --git a/transport/src/main/java/io/netty/channel/socket/aio/DefaultAioSocketChannelConfig.java b/transport/src/main/java/io/netty/channel/socket/aio/DefaultAioSocketChannelConfig.java index 1f88dc78d5..6c75e741a9 100644 --- a/transport/src/main/java/io/netty/channel/socket/aio/DefaultAioSocketChannelConfig.java +++ b/transport/src/main/java/io/netty/channel/socket/aio/DefaultAioSocketChannelConfig.java @@ -19,6 +19,7 @@ import io.netty.buffer.ByteBufAllocator; import io.netty.channel.ChannelException; import io.netty.channel.ChannelOption; import io.netty.channel.DefaultChannelConfig; +import io.netty.channel.RecvByteBufAllocator; import io.netty.util.internal.PlatformDependent; import java.io.IOException; @@ -352,13 +353,24 @@ final class DefaultAioSocketChannelConfig extends DefaultChannelConfig return (AioSocketChannelConfig) super.setAllocator(allocator); } + @Override + public AioSocketChannelConfig setRecvByteBufAllocator(RecvByteBufAllocator allocator) { + super.setRecvByteBufAllocator(allocator); + return this; + } + @Override public AioSocketChannelConfig setAutoRead(boolean autoRead) { return (AioSocketChannelConfig) super.setAutoRead(autoRead); } @Override - public AioSocketChannelConfig setDefaultHandlerByteBufType(ChannelHandlerByteBufType type) { - return (AioSocketChannelConfig) super.setDefaultHandlerByteBufType(type); + public AioSocketChannelConfig setWriteBufferLowWaterMark(int writeBufferLowWaterMark) { + return (AioSocketChannelConfig) super.setWriteBufferLowWaterMark(writeBufferLowWaterMark); + } + + @Override + public AioSocketChannelConfig setWriteBufferHighWaterMark(int writeBufferHighWaterMark) { + return (AioSocketChannelConfig) super.setWriteBufferHighWaterMark(writeBufferHighWaterMark); } } 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 3c0072d704..def2ea368a 100755 --- a/transport/src/main/java/io/netty/channel/socket/nio/NioDatagramChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/nio/NioDatagramChannel.java @@ -15,17 +15,17 @@ */ package io.netty.channel.socket.nio; -import io.netty.buffer.BufType; -import io.netty.buffer.BufUtil; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufHolder; -import io.netty.buffer.MessageBuf; +import io.netty.buffer.ByteBufUtil; import io.netty.channel.AddressedEnvelope; import io.netty.channel.Channel; import io.netty.channel.ChannelException; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelMetadata; import io.netty.channel.ChannelPromise; +import io.netty.channel.MessageList; +import io.netty.channel.RecvByteBufAllocator; import io.netty.channel.nio.AbstractNioMessageChannel; import io.netty.channel.socket.DatagramChannelConfig; import io.netty.channel.socket.DatagramPacket; @@ -59,12 +59,14 @@ import java.util.Map; public final class NioDatagramChannel extends AbstractNioMessageChannel implements io.netty.channel.socket.DatagramChannel { - private static final ChannelMetadata METADATA = new ChannelMetadata(BufType.MESSAGE, true); + private static final ChannelMetadata METADATA = new ChannelMetadata(true); private final DatagramChannelConfig config; private final Map> memberships = new HashMap>(); + private RecvByteBufAllocator.Handle allocHandle; + private static DatagramChannel newSocket() { try { return DatagramChannel.open(); @@ -198,9 +200,14 @@ public final class NioDatagramChannel } @Override - protected int doReadMessages(MessageBuf buf) throws Exception { + protected int doReadMessages(MessageList buf) throws Exception { DatagramChannel ch = javaChannel(); - ByteBuf data = alloc().directBuffer(config().getReceivePacketSize()); + DatagramChannelConfig config = config(); + RecvByteBufAllocator.Handle allocHandle = this.allocHandle; + if (allocHandle == null) { + this.allocHandle = allocHandle = config.getRecvByteBufAllocator().newHandle(); + } + ByteBuf data = allocHandle.allocate(config.getAllocator()); boolean free = true; try { ByteBuffer nioData = data.nioBuffer(data.writerIndex(), data.writableBytes()); @@ -210,7 +217,10 @@ public final class NioDatagramChannel return 0; } - data.writerIndex(data.writerIndex() + nioData.position()); + int readBytes = nioData.position(); + data.writerIndex(data.writerIndex() + readBytes); + allocHandle.record(readBytes); + buf.add(new DatagramPacket(data, localAddress(), remoteAddress)); free = false; return 1; @@ -225,8 +235,8 @@ public final class NioDatagramChannel } @Override - protected int doWriteMessages(MessageBuf buf, boolean lastSpin) throws Exception { - final Object o = buf.peek(); + protected int doWriteMessages(MessageList msgs, int index, boolean lastSpin) throws Exception { + final Object o = msgs.get(index); final Object m; final ByteBuf data; final SocketAddress remoteAddress; @@ -245,7 +255,7 @@ public final class NioDatagramChannel } else if (m instanceof ByteBuf) { data = (ByteBuf) m; } else { - BufUtil.release(buf.remove()); + ByteBufUtil.release(o); throw new ChannelException("unsupported message type: " + StringUtil.simpleClassName(o)); } @@ -283,9 +293,9 @@ public final class NioDatagramChannel } // Wrote a packet - free the message. - BufUtil.release(buf.remove()); + ByteBufUtil.release(o); - if (buf.isEmpty()) { + if (index + 1 == msgs.size()) { // Wrote the outbound buffer completely - clear OP_WRITE. if ((interestOps & SelectionKey.OP_WRITE) != 0) { key.interestOps(interestOps & ~SelectionKey.OP_WRITE); diff --git a/transport/src/main/java/io/netty/channel/socket/nio/NioServerSocketChannel.java b/transport/src/main/java/io/netty/channel/socket/nio/NioServerSocketChannel.java index b9028a70d1..72e8b7eac9 100755 --- a/transport/src/main/java/io/netty/channel/socket/nio/NioServerSocketChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/nio/NioServerSocketChannel.java @@ -15,10 +15,9 @@ */ package io.netty.channel.socket.nio; -import io.netty.buffer.BufType; -import io.netty.buffer.MessageBuf; import io.netty.channel.ChannelException; import io.netty.channel.ChannelMetadata; +import io.netty.channel.MessageList; import io.netty.channel.nio.AbstractNioMessageChannel; import io.netty.channel.socket.DefaultServerSocketChannelConfig; import io.netty.channel.socket.ServerSocketChannelConfig; @@ -39,7 +38,7 @@ import java.nio.channels.SocketChannel; public class NioServerSocketChannel extends AbstractNioMessageChannel implements io.netty.channel.socket.ServerSocketChannel { - private static final ChannelMetadata METADATA = new ChannelMetadata(BufType.MESSAGE, false); + private static final ChannelMetadata METADATA = new ChannelMetadata(false); private static final InternalLogger logger = InternalLoggerFactory.getInstance(NioServerSocketChannel.class); @@ -108,7 +107,7 @@ public class NioServerSocketChannel extends AbstractNioMessageChannel } @Override - protected int doReadMessages(MessageBuf buf) throws Exception { + protected int doReadMessages(MessageList buf) throws Exception { SocketChannel ch = javaChannel().accept(); try { @@ -152,7 +151,7 @@ public class NioServerSocketChannel extends AbstractNioMessageChannel } @Override - protected int doWriteMessages(MessageBuf buf, boolean lastSpin) throws Exception { + protected int doWriteMessages(MessageList msgs, int index, boolean lastSpin) throws Exception { throw new UnsupportedOperationException(); } } 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 1d4483064c..240b8cb3b1 100755 --- a/transport/src/main/java/io/netty/channel/socket/nio/NioSocketChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/nio/NioSocketChannel.java @@ -15,7 +15,6 @@ */ package io.netty.channel.socket.nio; -import io.netty.buffer.BufType; import io.netty.buffer.ByteBuf; import io.netty.channel.Channel; import io.netty.channel.ChannelException; @@ -23,6 +22,7 @@ import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelMetadata; import io.netty.channel.ChannelPromise; import io.netty.channel.EventLoop; +import io.netty.channel.FileRegion; import io.netty.channel.nio.AbstractNioByteChannel; import io.netty.channel.socket.DefaultSocketChannelConfig; import io.netty.channel.socket.ServerSocketChannel; @@ -35,13 +35,14 @@ import java.net.InetSocketAddress; import java.net.SocketAddress; import java.nio.channels.SelectionKey; import java.nio.channels.SocketChannel; +import java.nio.channels.WritableByteChannel; /** * {@link io.netty.channel.socket.SocketChannel} which uses NIO selector based implementation. */ public class NioSocketChannel extends AbstractNioByteChannel implements io.netty.channel.socket.SocketChannel { - private static final ChannelMetadata METADATA = new ChannelMetadata(BufType.BYTE, false); + private static final ChannelMetadata METADATA = new ChannelMetadata(false); private static final InternalLogger logger = InternalLoggerFactory.getInstance(NioSocketChannel.class); @@ -258,4 +259,34 @@ public class NioSocketChannel extends AbstractNioByteChannel implements io.netty return writtenBytes; } + + @Override + protected long doWriteFileRegion(FileRegion region, boolean lastSpin) throws Exception { + if (javaChannel() instanceof WritableByteChannel) { + WritableByteChannel wch = javaChannel(); + long localWrittenBytes = region.transferTo(wch, region.transfered()); + if (localWrittenBytes > 0 || lastSpin) { + // check if the region was written complete. If not set OP_WRITE so the eventloop + // will write the rest once writable again + if (region.transfered() < region.count()) { + final SelectionKey key = selectionKey(); + final int interestOps = key.interestOps(); + if ((interestOps & SelectionKey.OP_WRITE) == 0) { + key.interestOps(interestOps | SelectionKey.OP_WRITE); + } + } + } else { + final SelectionKey key = selectionKey(); + final int interestOps = key.interestOps(); + // Wrote the region completely - clear OP_WRITE. + if ((interestOps & SelectionKey.OP_WRITE) != 0) { + key.interestOps(interestOps & ~SelectionKey.OP_WRITE); + } + } + return localWrittenBytes; + } else { + throw new UnsupportedOperationException("Underlying Channel is not of instance " + + WritableByteChannel.class); + } + } } diff --git a/transport/src/main/java/io/netty/channel/socket/oio/DefaultOioServerSocketChannelConfig.java b/transport/src/main/java/io/netty/channel/socket/oio/DefaultOioServerSocketChannelConfig.java index 99370e4b86..223ad2a513 100644 --- a/transport/src/main/java/io/netty/channel/socket/oio/DefaultOioServerSocketChannelConfig.java +++ b/transport/src/main/java/io/netty/channel/socket/oio/DefaultOioServerSocketChannelConfig.java @@ -18,6 +18,7 @@ package io.netty.channel.socket.oio; import io.netty.buffer.ByteBufAllocator; import io.netty.channel.ChannelException; import io.netty.channel.ChannelOption; +import io.netty.channel.RecvByteBufAllocator; import io.netty.channel.socket.DefaultServerSocketChannelConfig; import io.netty.channel.socket.ServerSocketChannel; @@ -126,14 +127,14 @@ public class DefaultOioServerSocketChannelConfig extends DefaultServerSocketChan } @Override - public OioServerSocketChannelConfig setAutoRead(boolean autoRead) { - super.setAutoRead(autoRead); + public OioServerSocketChannelConfig setRecvByteBufAllocator(RecvByteBufAllocator allocator) { + super.setRecvByteBufAllocator(allocator); return this; } @Override - public OioServerSocketChannelConfig setDefaultHandlerByteBufType(ChannelHandlerByteBufType type) { - super.setDefaultHandlerByteBufType(type); + public OioServerSocketChannelConfig setAutoRead(boolean autoRead) { + super.setAutoRead(autoRead); return this; } } diff --git a/transport/src/main/java/io/netty/channel/socket/oio/DefaultOioSocketChannelConfig.java b/transport/src/main/java/io/netty/channel/socket/oio/DefaultOioSocketChannelConfig.java index f0d4472d13..5c4f6df25b 100644 --- a/transport/src/main/java/io/netty/channel/socket/oio/DefaultOioSocketChannelConfig.java +++ b/transport/src/main/java/io/netty/channel/socket/oio/DefaultOioSocketChannelConfig.java @@ -18,6 +18,7 @@ package io.netty.channel.socket.oio; import io.netty.buffer.ByteBufAllocator; import io.netty.channel.ChannelException; import io.netty.channel.ChannelOption; +import io.netty.channel.RecvByteBufAllocator; import io.netty.channel.socket.DefaultSocketChannelConfig; import io.netty.channel.socket.SocketChannel; @@ -154,14 +155,14 @@ public class DefaultOioSocketChannelConfig extends DefaultSocketChannelConfig im } @Override - public OioSocketChannelConfig setAutoRead(boolean autoRead) { - super.setAutoRead(autoRead); + public OioSocketChannelConfig setRecvByteBufAllocator(RecvByteBufAllocator allocator) { + super.setRecvByteBufAllocator(allocator); return this; } @Override - public OioSocketChannelConfig setDefaultHandlerByteBufType(ChannelHandlerByteBufType type) { - super.setDefaultHandlerByteBufType(type); + public OioSocketChannelConfig setAutoRead(boolean autoRead) { + super.setAutoRead(autoRead); return this; } } 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 f1c7605ae5..1d329105e7 100755 --- a/transport/src/main/java/io/netty/channel/socket/oio/OioDatagramChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/oio/OioDatagramChannel.java @@ -15,17 +15,17 @@ */ package io.netty.channel.socket.oio; -import io.netty.buffer.BufType; -import io.netty.buffer.BufUtil; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufHolder; -import io.netty.buffer.MessageBuf; +import io.netty.buffer.ByteBufUtil; import io.netty.channel.AddressedEnvelope; import io.netty.channel.Channel; import io.netty.channel.ChannelException; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelMetadata; import io.netty.channel.ChannelPromise; +import io.netty.channel.MessageList; +import io.netty.channel.RecvByteBufAllocator; import io.netty.channel.oio.AbstractOioMessageChannel; import io.netty.channel.socket.DatagramChannel; import io.netty.channel.socket.DatagramChannelConfig; @@ -59,12 +59,14 @@ public class OioDatagramChannel extends AbstractOioMessageChannel private static final InternalLogger logger = InternalLoggerFactory.getInstance(OioDatagramChannel.class); - private static final ChannelMetadata METADATA = new ChannelMetadata(BufType.MESSAGE, true); + private static final ChannelMetadata METADATA = new ChannelMetadata(true); private final MulticastSocket socket; private final DatagramChannelConfig config; private final java.net.DatagramPacket tmpPacket = new java.net.DatagramPacket(EmptyArrays.EMPTY_BYTES, 0); + private RecvByteBufAllocator.Handle allocHandle; + private static MulticastSocket newSocket() { try { return new MulticastSocket(null); @@ -199,13 +201,17 @@ public class OioDatagramChannel extends AbstractOioMessageChannel } @Override - protected int doReadMessages(MessageBuf buf) throws Exception { - int packetSize = config().getReceivePacketSize(); - ByteBuf data = alloc().heapBuffer(packetSize); - boolean free = true; + protected int doReadMessages(MessageList buf) throws Exception { + DatagramChannelConfig config = config(); + RecvByteBufAllocator.Handle allocHandle = this.allocHandle; + if (allocHandle == null) { + this.allocHandle = allocHandle = config.getRecvByteBufAllocator().newHandle(); + } + ByteBuf data = config.getAllocator().heapBuffer(allocHandle.guess()); + boolean free = true; try { - tmpPacket.setData(data.array(), data.arrayOffset(), packetSize); + tmpPacket.setData(data.array(), data.arrayOffset(), data.capacity()); socket.receive(tmpPacket); InetSocketAddress remoteAddr = (InetSocketAddress) tmpPacket.getSocketAddress(); @@ -213,7 +219,9 @@ public class OioDatagramChannel extends AbstractOioMessageChannel remoteAddr = remoteAddress(); } - buf.add(new DatagramPacket(data.writerIndex(tmpPacket.getLength()), localAddress(), remoteAddr)); + int readBytes = tmpPacket.getLength(); + allocHandle.record(readBytes); + buf.add(new DatagramPacket(data.writerIndex(readBytes), localAddress(), remoteAddr)); free = false; return 1; } catch (SocketTimeoutException e) { @@ -235,8 +243,8 @@ public class OioDatagramChannel extends AbstractOioMessageChannel } @Override - protected void doWriteMessages(MessageBuf buf) throws Exception { - final Object o = buf.poll(); + protected int doWrite(MessageList msgs, int index) throws Exception { + final Object o = msgs.get(index); final Object m; final ByteBuf data; final SocketAddress remoteAddress; @@ -255,7 +263,7 @@ public class OioDatagramChannel extends AbstractOioMessageChannel } else if (m instanceof ByteBuf) { data = (ByteBuf) m; } else { - BufUtil.release(buf.remove()); + ByteBufUtil.release(o); throw new ChannelException("unsupported message type: " + StringUtil.simpleClassName(o)); } @@ -272,8 +280,9 @@ public class OioDatagramChannel extends AbstractOioMessageChannel tmpPacket.setData(tmp); } socket.send(tmpPacket); + return 1; } finally { - BufUtil.release(o); + ByteBufUtil.release(o); } } diff --git a/transport/src/main/java/io/netty/channel/socket/oio/OioServerSocketChannel.java b/transport/src/main/java/io/netty/channel/socket/oio/OioServerSocketChannel.java index 6e40fb97d3..374181a03c 100755 --- a/transport/src/main/java/io/netty/channel/socket/oio/OioServerSocketChannel.java +++ b/transport/src/main/java/io/netty/channel/socket/oio/OioServerSocketChannel.java @@ -15,10 +15,10 @@ */ package io.netty.channel.socket.oio; -import io.netty.buffer.BufType; -import io.netty.buffer.MessageBuf; +import io.netty.buffer.ByteBufUtil; import io.netty.channel.ChannelException; import io.netty.channel.ChannelMetadata; +import io.netty.channel.MessageList; import io.netty.channel.oio.AbstractOioMessageChannel; import io.netty.channel.socket.ServerSocketChannel; import io.netty.util.internal.logging.InternalLogger; @@ -44,7 +44,7 @@ public class OioServerSocketChannel extends AbstractOioMessageChannel private static final InternalLogger logger = InternalLoggerFactory.getInstance(OioServerSocketChannel.class); - private static final ChannelMetadata METADATA = new ChannelMetadata(BufType.MESSAGE, false); + private static final ChannelMetadata METADATA = new ChannelMetadata(false); private static ServerSocket newServerSocket() { try { @@ -155,7 +155,7 @@ public class OioServerSocketChannel extends AbstractOioMessageChannel } @Override - protected int doReadMessages(MessageBuf buf) throws Exception { + protected int doReadMessages(MessageList buf) throws Exception { if (socket.isClosed()) { return -1; } @@ -183,6 +183,15 @@ public class OioServerSocketChannel extends AbstractOioMessageChannel return 0; } + @Override + protected int doWrite(MessageList msgs, int index) throws Exception { + int size = msgs.size(); + for (int i = index; i < size; i ++) { + ByteBufUtil.release(msgs.get(i)); + } + throw new UnsupportedOperationException(); + } + @Override protected void doConnect( SocketAddress remoteAddress, SocketAddress localAddress) throws Exception { @@ -198,9 +207,4 @@ public class OioServerSocketChannel extends AbstractOioMessageChannel protected void doDisconnect() throws Exception { throw new UnsupportedOperationException(); } - - @Override - protected void doWriteMessages(MessageBuf buf) throws Exception { - throw new UnsupportedOperationException(); - } } diff --git a/transport/src/main/java/io/netty/channel/socket/oio/OioServerSocketChannelConfig.java b/transport/src/main/java/io/netty/channel/socket/oio/OioServerSocketChannelConfig.java index 34ade6cb94..7595f63040 100644 --- a/transport/src/main/java/io/netty/channel/socket/oio/OioServerSocketChannelConfig.java +++ b/transport/src/main/java/io/netty/channel/socket/oio/OioServerSocketChannelConfig.java @@ -17,6 +17,7 @@ package io.netty.channel.socket.oio; import io.netty.buffer.ByteBufAllocator; import io.netty.channel.ChannelOption; +import io.netty.channel.RecvByteBufAllocator; import io.netty.channel.socket.ServerSocketChannelConfig; @@ -71,8 +72,8 @@ public interface OioServerSocketChannelConfig extends ServerSocketChannelConfig OioServerSocketChannelConfig setAllocator(ByteBufAllocator allocator); @Override - OioServerSocketChannelConfig setAutoRead(boolean autoRead); + OioServerSocketChannelConfig setRecvByteBufAllocator(RecvByteBufAllocator allocator); @Override - OioServerSocketChannelConfig setDefaultHandlerByteBufType(ChannelHandlerByteBufType type); + OioServerSocketChannelConfig setAutoRead(boolean autoRead); } diff --git a/transport/src/main/java/io/netty/channel/socket/oio/OioSocketChannelConfig.java b/transport/src/main/java/io/netty/channel/socket/oio/OioSocketChannelConfig.java index d18db6a109..ac1a481552 100644 --- a/transport/src/main/java/io/netty/channel/socket/oio/OioSocketChannelConfig.java +++ b/transport/src/main/java/io/netty/channel/socket/oio/OioSocketChannelConfig.java @@ -18,6 +18,7 @@ package io.netty.channel.socket.oio; import io.netty.buffer.ByteBufAllocator; import io.netty.channel.ChannelConfig; import io.netty.channel.ChannelOption; +import io.netty.channel.RecvByteBufAllocator; import io.netty.channel.socket.SocketChannelConfig; /** @@ -86,8 +87,8 @@ public interface OioSocketChannelConfig extends SocketChannelConfig { OioSocketChannelConfig setAllocator(ByteBufAllocator allocator); @Override - OioSocketChannelConfig setAutoRead(boolean autoRead); + OioSocketChannelConfig setRecvByteBufAllocator(RecvByteBufAllocator allocator); @Override - OioSocketChannelConfig setDefaultHandlerByteBufType(ChannelHandlerByteBufType type); + OioSocketChannelConfig setAutoRead(boolean autoRead); } diff --git a/transport/src/test/java/io/netty/bootstrap/BootstrapTest.java b/transport/src/test/java/io/netty/bootstrap/BootstrapTest.java index ad630b5d37..e99b4fdc54 100644 --- a/transport/src/test/java/io/netty/bootstrap/BootstrapTest.java +++ b/transport/src/test/java/io/netty/bootstrap/BootstrapTest.java @@ -17,9 +17,8 @@ package io.netty.bootstrap; import io.netty.channel.ChannelHandler.Sharable; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandler; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.ChannelInboundHandler; +import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.EventLoopGroup; import io.netty.channel.local.LocalAddress; import io.netty.channel.local.LocalChannel; @@ -38,7 +37,7 @@ public class BootstrapTest { EventLoopGroup groupB = new LocalEventLoopGroup(1); try { - ChannelInboundMessageHandler dummyHandler = new DummyHandler(); + ChannelInboundHandler dummyHandler = new DummyHandler(); final Bootstrap bootstrapA = new Bootstrap(); bootstrapA.group(groupA); @@ -84,7 +83,7 @@ public class BootstrapTest { EventLoopGroup groupB = new LocalEventLoopGroup(1); try { - ChannelInboundMessageHandler dummyHandler = new DummyHandler(); + ChannelInboundHandler dummyHandler = new DummyHandler(); final Bootstrap bootstrapA = new Bootstrap(); bootstrapA.group(groupA); @@ -125,10 +124,5 @@ public class BootstrapTest { } @Sharable - private static final class DummyHandler extends ChannelInboundMessageHandlerAdapter { - @Override - public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { - // NOOP - } - } + private static final class DummyHandler extends ChannelInboundHandlerAdapter { } } diff --git a/transport/src/test/java/io/netty/channel/AbstractEventLoopTest.java b/transport/src/test/java/io/netty/channel/AbstractEventLoopTest.java index d318b9d689..d9c3f45c83 100644 --- a/transport/src/test/java/io/netty/channel/AbstractEventLoopTest.java +++ b/transport/src/test/java/io/netty/channel/AbstractEventLoopTest.java @@ -39,7 +39,6 @@ public abstract class AbstractEventLoopTest { ServerBootstrap bootstrap = new ServerBootstrap(); ChannelFuture future = bootstrap.channel(newChannel()).group(group) .childHandler(new ChannelInitializer() { - @Override public void initChannel(SocketChannel ch) throws Exception { } @@ -61,27 +60,11 @@ public abstract class AbstractEventLoopTest { assertSame(executor, future.channel().pipeline().context(TestChannelHandler2.class).executor()); } - private static final class TestChannelHandler extends ChannelDuplexHandler { - @Override - public void flush(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { - ctx.flush(promise); - } - - @Override - public void inboundBufferUpdated(ChannelHandlerContext ctx) throws Exception { - ctx.fireInboundBufferUpdated(); - } - } + private static final class TestChannelHandler extends ChannelDuplexHandler { } private static final class TestChannelHandler2 extends ChannelDuplexHandler { @Override - public void flush(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { - ctx.flush(promise); - } - - @Override - public void inboundBufferUpdated(ChannelHandlerContext ctx) throws Exception { - } + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { } } protected abstract EventLoopGroup newEventLoopGroup(); diff --git a/transport/src/test/java/io/netty/channel/DefaultChannelPipelineTest.java b/transport/src/test/java/io/netty/channel/DefaultChannelPipelineTest.java index 805bdc25c0..00c85a86e0 100644 --- a/transport/src/test/java/io/netty/channel/DefaultChannelPipelineTest.java +++ b/transport/src/test/java/io/netty/channel/DefaultChannelPipelineTest.java @@ -18,10 +18,7 @@ package io.netty.channel; import io.netty.bootstrap.Bootstrap; import io.netty.bootstrap.ServerBootstrap; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.MessageBuf; import io.netty.buffer.ReferenceCounted; -import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandler.Sharable; import io.netty.channel.local.LocalAddress; import io.netty.channel.local.LocalChannel; @@ -36,7 +33,6 @@ import java.util.Collections; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import static org.junit.Assert.*; @@ -57,14 +53,14 @@ public class DefaultChannelPipelineTest { final AtomicReference peerRef = new AtomicReference(); ServerBootstrap sb = new ServerBootstrap(); sb.group(group).channel(LocalServerChannel.class); - sb.childHandler(new ChannelInboundMessageHandlerAdapter() { + sb.childHandler(new ChannelInboundHandlerAdapter() { @Override public void channelRegistered(ChannelHandlerContext ctx) throws Exception { peerRef.set(ctx.channel()); } @Override - public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { // Swallow. } }); @@ -97,76 +93,6 @@ public class DefaultChannelPipelineTest { } } - @Test - public void testMessageCatchAllInboundSink() throws Exception { - final AtomicBoolean forwarded = new AtomicBoolean(); - - setUp(new ChannelInboundMessageHandlerAdapter() { - @Override - public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { - forwarded.set(ctx.nextInboundMessageBuffer().add(msg)); - } - - @Override - public void endMessageReceived(ChannelHandlerContext ctx) throws Exception { - ctx.fireInboundBufferUpdated(); - } - }); - - peer.write(new Object()).sync(); - - assertTrue(forwarded.get()); - } - - @Test - public void testByteCatchAllInboundSink() throws Exception { - final AtomicBoolean forwarded = new AtomicBoolean(); - setUp(new ChannelInboundByteHandlerAdapter() { - @Override - protected void inboundBufferUpdated(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - ByteBuf out = ctx.nextInboundByteBuffer(); - out.writeBytes(in); - forwarded.set(true); - ctx.fireInboundBufferUpdated(); - } - }); - - // Not using peer.write() because the pipeline will convert the bytes into a message automatically. - self.eventLoop().submit(new Runnable() { - @Override - public void run() { - self.pipeline().inboundByteBuffer().writeByte(0); - self.pipeline().fireInboundBufferUpdated(); - } - }).sync(); - - assertTrue(forwarded.get()); - } - - @Test - public void testByteCatchAllOutboundSink() throws Exception { - final AtomicBoolean forwarded = new AtomicBoolean(); - setUp(new ChannelOutboundByteHandlerAdapter() { - @Override - protected void flush(ChannelHandlerContext ctx, ByteBuf in, ChannelPromise promise) throws Exception { - ByteBuf out = ctx.nextOutboundByteBuffer(); - out.writeBytes(in); - forwarded.set(true); - ctx.flush(promise); - } - }); - - self.eventLoop().submit(new Runnable() { - @Override - public void run() { - self.pipeline().outboundByteBuffer().writeByte(0); - self.pipeline().flush(); - } - }).sync(); - - assertTrue(forwarded.get()); - } - @Test public void testFreeCalled() throws Exception { final CountDownLatch free = new CountDownLatch(1); @@ -214,18 +140,22 @@ public class DefaultChannelPipelineTest { assertTrue(handler.called); } - private static final class StringInboundHandler extends ChannelInboundMessageHandlerAdapter { + private static final class StringInboundHandler extends ChannelInboundHandlerAdapter { boolean called; @Override - public boolean acceptInboundMessage(Object msg) throws Exception { + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { called = true; - return super.acceptInboundMessage(msg); - } + MessageList out = MessageList.newInstance(); + for (int i = 0; i < msgs.size(); i ++) { + Object m = msgs.get(i); + if (!(m instanceof String)) { + out.add(m); + } + } - @Override - public void messageReceived(ChannelHandlerContext ctx, String msg) throws Exception { - fail(); + msgs.recycle(); + ctx.fireMessageReceived(out); } } @@ -347,294 +277,6 @@ public class DefaultChannelPipelineTest { verifyContextNumber(pipeline, 8); } - @Test(timeout = 100000) - public void testRemoveAndForwardInboundByte() throws Exception { - final ChannelInboundByteHandlerImpl handler1 = new ChannelInboundByteHandlerImpl(); - final ChannelInboundByteHandlerImpl handler2 = new ChannelInboundByteHandlerImpl(); - - setUp(handler1, handler2); - - self.eventLoop().submit(new Runnable() { - @Override - public void run() { - ChannelPipeline p = self.pipeline(); - p.context(handler1).inboundByteBuffer().writeLong(8); - assertEquals(8, p.context(handler1).inboundByteBuffer().readableBytes()); - assertEquals(0, p.context(handler2).inboundByteBuffer().readableBytes()); - p.remove(handler1); - assertEquals(8, p.context(handler2).inboundByteBuffer().readableBytes()); - } - }).sync(); - - assertTrue(handler2.updated); - } - - @Test(timeout = 100000) - public void testReplaceAndForwardInboundByte() throws Exception { - final ChannelInboundByteHandlerImpl handler1 = new ChannelInboundByteHandlerImpl(); - final ChannelInboundByteHandlerImpl handler2 = new ChannelInboundByteHandlerImpl(); - - setUp(handler1); - - self.eventLoop().submit(new Runnable() { - @Override - public void run() { - ChannelPipeline p = self.pipeline(); - p.context(handler1).inboundByteBuffer().writeLong(8); - assertEquals(8, p.context(handler1).inboundByteBuffer().readableBytes()); - p.replace(handler1, "handler2", handler2); - assertEquals(8, p.context(handler2).inboundByteBuffer().readableBytes()); - } - }).sync(); - - assertTrue(handler2.updated); - } - - @Test(timeout = 10000) - public void testRemoveAndForwardOutboundByte() throws Exception { - final ChannelOutboundByteHandlerImpl handler1 = new ChannelOutboundByteHandlerImpl(); - final ChannelOutboundByteHandlerImpl handler2 = new ChannelOutboundByteHandlerImpl(); - - setUp(handler1, handler2); - - self.eventLoop().submit(new Runnable() { - @Override - public void run() { - ChannelPipeline p = self.pipeline(); - p.context(handler2).outboundByteBuffer().writeLong(8); - assertEquals(8, p.context(handler2).outboundByteBuffer().readableBytes()); - assertEquals(0, p.context(handler1).outboundByteBuffer().readableBytes()); - self.pipeline().remove(handler2); - assertEquals(8, p.context(handler1).outboundByteBuffer().readableBytes()); - } - }).sync(); - - assertTrue(handler1.flushed); - } - - @Test(timeout = 10000) - public void testReplaceAndForwardOutboundByte() throws Exception { - final ChannelOutboundByteHandlerImpl handler1 = new ChannelOutboundByteHandlerImpl(); - final ChannelOutboundByteHandlerImpl handler2 = new ChannelOutboundByteHandlerImpl(); - - setUp(handler1); - - self.eventLoop().submit(new Runnable() { - @Override - public void run() { - ChannelPipeline p = self.pipeline(); - p.context(handler1).outboundByteBuffer().writeLong(8); - assertEquals(8, p.context(handler1).outboundByteBuffer().readableBytes()); - p.replace(handler1, "handler2", handler2); - assertEquals(8, p.context(handler2).outboundByteBuffer().readableBytes()); - } - }).sync(); - - assertTrue(handler2.flushed); - } - - @Test(timeout = 10000) - public void testReplaceAndForwardDuplexByte() throws Exception { - final ByteHandlerImpl handler1 = new ByteHandlerImpl(); - final ByteHandlerImpl handler2 = new ByteHandlerImpl(); - - setUp(handler1); - - self.eventLoop().submit(new Runnable() { - @Override - public void run() { - ChannelPipeline p = self.pipeline(); - p.context(handler1).outboundByteBuffer().writeLong(8); - p.context(handler1).inboundByteBuffer().writeLong(8); - - assertEquals(8, p.context(handler1).outboundByteBuffer().readableBytes()); - assertEquals(8, p.context(handler1).inboundByteBuffer().readableBytes()); - - p.replace(handler1, "handler2", handler2); - assertEquals(8, p.context(handler2).outboundByteBuffer().readableBytes()); - assertEquals(8, p.context(handler2).inboundByteBuffer().readableBytes()); - } - }).sync(); - - assertTrue(((ChannelInboundByteHandlerImpl) handler2.stateHandler()).updated); - assertTrue(((ChannelOutboundByteHandlerImpl) handler2.operationHandler()).flushed); - } - - @Test(timeout = 10000) - public void testRemoveAndForwardDuplexByte() throws Exception { - final ChannelOutboundByteHandlerImpl handler1 = new ChannelOutboundByteHandlerImpl(); - final ByteHandlerImpl handler2 = new ByteHandlerImpl(); - final ChannelInboundByteHandlerImpl handler3 = new ChannelInboundByteHandlerImpl(); - - setUp(handler1, handler2, handler3); - - self.eventLoop().submit(new Runnable() { - @Override - public void run() { - ChannelPipeline p = self.pipeline(); - p.context(handler2).outboundByteBuffer().writeLong(8); - p.context(handler2).inboundByteBuffer().writeLong(8); - - assertEquals(8, p.context(handler2).outboundByteBuffer().readableBytes()); - assertEquals(8, p.context(handler2).inboundByteBuffer().readableBytes()); - - assertEquals(0, p.context(handler1).outboundByteBuffer().readableBytes()); - assertEquals(0, p.context(handler3).inboundByteBuffer().readableBytes()); - - p.remove(handler2); - assertEquals(8, p.context(handler1).outboundByteBuffer().readableBytes()); - assertEquals(8, p.context(handler3).inboundByteBuffer().readableBytes()); - } - }).sync(); - - assertTrue(handler1.flushed); - assertTrue(handler3.updated); - } - - @Test(timeout = 10000) - public void testRemoveAndForwardInboundMessage() throws Exception { - final ChannelInboundMessageHandlerImpl handler1 = new ChannelInboundMessageHandlerImpl(); - final ChannelInboundMessageHandlerImpl handler2 = new ChannelInboundMessageHandlerImpl(); - - setUp(handler1, handler2); - - self.eventLoop().submit(new Runnable() { - @Override - public void run() { - ChannelPipeline p = self.pipeline(); - p.context(handler1).inboundMessageBuffer().add(new Object()); - assertEquals(1, p.context(handler1).inboundMessageBuffer().size()); - assertEquals(0, p.context(handler2).inboundMessageBuffer().size()); - p.remove(handler1); - assertEquals(1, p.context(handler2).inboundMessageBuffer().size()); - } - }).sync(); - - assertTrue(handler2.updated); - } - - @Test(timeout = 10000) - public void testReplaceAndForwardInboundMessage() throws Exception { - final ChannelInboundMessageHandlerImpl handler1 = new ChannelInboundMessageHandlerImpl(); - final ChannelInboundMessageHandlerImpl handler2 = new ChannelInboundMessageHandlerImpl(); - - setUp(handler1); - - self.eventLoop().submit(new Runnable() { - @Override - public void run() { - ChannelPipeline p = self.pipeline(); - p.context(handler1).inboundMessageBuffer().add(new Object()); - assertEquals(1, p.context(handler1).inboundMessageBuffer().size()); - p.replace(handler1, "handler2", handler2); - assertEquals(1, p.context(handler2).inboundMessageBuffer().size()); - } - }).sync(); - - assertTrue(handler2.updated); - } - - @Test(timeout = 10000) - public void testRemoveAndForwardOutboundMessage() throws Exception { - final ChannelOutboundMessageHandlerImpl handler1 = new ChannelOutboundMessageHandlerImpl(); - final ChannelOutboundMessageHandlerImpl handler2 = new ChannelOutboundMessageHandlerImpl(); - - setUp(handler1, handler2); - - self.eventLoop().submit(new Runnable() { - @Override - public void run() { - ChannelPipeline p = self.pipeline(); - p.context(handler2).outboundMessageBuffer().add(new Object()); - assertEquals(1, p.context(handler2).outboundMessageBuffer().size()); - assertEquals(0, p.context(handler1).outboundMessageBuffer().size()); - p.remove(handler2); - assertEquals(1, p.context(handler1).outboundMessageBuffer().size()); - } - }).sync(); - - assertTrue(handler1.flushed); - } - - @Test(timeout = 10000) - public void testReplaceAndForwardOutboundMessage() throws Exception { - final ChannelOutboundMessageHandlerImpl handler1 = new ChannelOutboundMessageHandlerImpl(); - final ChannelOutboundMessageHandlerImpl handler2 = new ChannelOutboundMessageHandlerImpl(); - - setUp(handler1); - - self.eventLoop().submit(new Runnable() { - @Override - public void run() { - ChannelPipeline p = self.pipeline(); - p.context(handler1).outboundMessageBuffer().add(new Object()); - assertEquals(1, p.context(handler1).outboundMessageBuffer().size()); - p.replace(handler1, "handler2", handler2); - assertEquals(1, p.context(handler2).outboundMessageBuffer().size()); - } - }).sync(); - - assertTrue(handler2.flushed); - } - - @Test(timeout = 10000) - public void testReplaceAndForwardDuplexMessage() throws Exception { - final MessageHandlerImpl handler1 = new MessageHandlerImpl(); - final MessageHandlerImpl handler2 = new MessageHandlerImpl(); - - setUp(handler1); - - self.eventLoop().submit(new Runnable() { - @Override - public void run() { - ChannelPipeline p = self.pipeline(); - p.context(handler1).outboundMessageBuffer().add(new Object()); - p.context(handler1).inboundMessageBuffer().add(new Object()); - - assertEquals(1, p.context(handler1).outboundMessageBuffer().size()); - assertEquals(1, p.context(handler1).inboundMessageBuffer().size()); - - p.replace(handler1, "handler2", handler2); - assertEquals(1, p.context(handler2).outboundMessageBuffer().size()); - assertEquals(1, p.context(handler2).inboundMessageBuffer().size()); - } - }).sync(); - - assertTrue(((ChannelInboundMessageHandlerImpl) handler2.stateHandler()).updated); - assertTrue(((ChannelOutboundMessageHandlerImpl) handler2.operationHandler()).flushed); - } - - @Test(timeout = 10000) - public void testRemoveAndForwardDuplexMessage() throws Exception { - final ChannelOutboundMessageHandlerImpl handler1 = new ChannelOutboundMessageHandlerImpl(); - final MessageHandlerImpl handler2 = new MessageHandlerImpl(); - final ChannelInboundMessageHandlerImpl handler3 = new ChannelInboundMessageHandlerImpl(); - - setUp(handler1, handler2, handler3); - - self.eventLoop().submit(new Runnable() { - @Override - public void run() { - ChannelPipeline p = self.pipeline(); - p.context(handler2).outboundMessageBuffer().add(new Object()); - p.context(handler2).inboundMessageBuffer().add(new Object()); - - assertEquals(1, p.context(handler2).outboundMessageBuffer().size()); - assertEquals(1, p.context(handler2).inboundMessageBuffer().size()); - - assertEquals(0, p.context(handler1).outboundMessageBuffer().size()); - assertEquals(0, p.context(handler3).inboundMessageBuffer().size()); - - p.remove(handler2); - assertEquals(1, p.context(handler1).outboundMessageBuffer().size()); - assertEquals(1, p.context(handler3).inboundMessageBuffer().size()); - } - }).sync(); - - assertTrue(handler1.flushed); - assertTrue(handler3.updated); - } - @Test(timeout = 10000) public void testLifeCycleAwareness() throws Exception { setUp(); @@ -684,6 +326,122 @@ public class DefaultChannelPipelineTest { removeLatch.await(); } + @Test(timeout = 100000) + public void testRemoveAndForwardInbound() throws Exception { + final BufferedTestHandler handler1 = new BufferedTestHandler(); + final BufferedTestHandler handler2 = new BufferedTestHandler(); + + setUp(handler1, handler2); + + self.eventLoop().submit(new Runnable() { + @Override + public void run() { + ChannelPipeline p = self.pipeline(); + handler1.inboundBuffer.add(8); + assertEquals(8, handler1.inboundBuffer.get(0)); + assertTrue(handler2.inboundBuffer.isEmpty()); + p.remove(handler1); + assertEquals(1, handler2.inboundBuffer.size()); + assertEquals(8, handler2.inboundBuffer.get(0)); + } + }).sync(); + } + + @Test(timeout = 10000) + public void testRemoveAndForwardOutbound() throws Exception { + final BufferedTestHandler handler1 = new BufferedTestHandler(); + final BufferedTestHandler handler2 = new BufferedTestHandler(); + + setUp(handler1, handler2); + + self.eventLoop().submit(new Runnable() { + @Override + public void run() { + ChannelPipeline p = self.pipeline(); + handler2.outboundBuffer.add(8); + assertEquals(8, handler2.outboundBuffer.get(0)); + assertTrue(handler1.outboundBuffer.isEmpty()); + p.remove(handler2); + assertEquals(1, handler1.outboundBuffer.size()); + assertEquals(8, handler1.outboundBuffer.get(0)); + } + }).sync(); + } + + @Test(timeout = 10000) + public void testReplaceAndForwardOutbound() throws Exception { + final BufferedTestHandler handler1 = new BufferedTestHandler(); + final BufferedTestHandler handler2 = new BufferedTestHandler(); + + setUp(handler1); + + self.eventLoop().submit(new Runnable() { + @Override + public void run() { + ChannelPipeline p = self.pipeline(); + handler1.outboundBuffer.add(8); + assertEquals(8, handler1.outboundBuffer.get(0)); + assertTrue(handler2.outboundBuffer.isEmpty()); + p.replace(handler1, "handler2", handler2); + assertEquals(8, handler2.outboundBuffer.get(0)); + } + }).sync(); + } + + @Test(timeout = 10000) + public void testReplaceAndForwardInboundAndOutbound() throws Exception { + final BufferedTestHandler handler1 = new BufferedTestHandler(); + final BufferedTestHandler handler2 = new BufferedTestHandler(); + + setUp(handler1); + + self.eventLoop().submit(new Runnable() { + @Override + public void run() { + ChannelPipeline p = self.pipeline(); + handler1.inboundBuffer.add(8); + handler1.outboundBuffer.add(8); + + assertEquals(8, handler1.inboundBuffer.get(0)); + assertEquals(8, handler1.outboundBuffer.get(0)); + assertTrue(handler2.inboundBuffer.isEmpty()); + assertTrue(handler2.outboundBuffer.isEmpty()); + + p.replace(handler1, "handler2", handler2); + assertEquals(8, handler2.outboundBuffer.get(0)); + assertEquals(8, handler2.inboundBuffer.get(0)); + } + }).sync(); + } + + @Test(timeout = 10000) + public void testRemoveAndForwardInboundOutbound() throws Exception { + final BufferedTestHandler handler1 = new BufferedTestHandler(); + final BufferedTestHandler handler2 = new BufferedTestHandler(); + final BufferedTestHandler handler3 = new BufferedTestHandler(); + + setUp(handler1, handler2, handler3); + + self.eventLoop().submit(new Runnable() { + @Override + public void run() { + ChannelPipeline p = self.pipeline(); + handler2.inboundBuffer.add(8); + handler2.outboundBuffer.add(8); + + assertEquals(8, handler2.inboundBuffer.get(0)); + assertEquals(8, handler2.outboundBuffer.get(0)); + + assertEquals(0, handler1.outboundBuffer.size()); + assertEquals(0, handler3.inboundBuffer.size()); + + p.remove(handler2); + assertEquals(8, handler3.inboundBuffer.get(0)); + assertEquals(8, handler1.outboundBuffer.get(0)); + } + }).sync(); + } + private static int next(DefaultChannelHandlerContext ctx) { DefaultChannelHandlerContext next = ctx.next; if (next == null) { @@ -727,109 +485,37 @@ public class DefaultChannelPipelineTest { } @Sharable - private static class TestHandler extends ChannelDuplexHandler { + private static class TestHandler extends ChannelDuplexHandler { } + + private static class BufferedTestHandler extends ChannelDuplexHandler { + final MessageList inboundBuffer = MessageList.newInstance(); + final MessageList outboundBuffer = MessageList.newInstance(); + @Override - public void flush(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { - ctx.flush(promise); + public void write(ChannelHandlerContext ctx, MessageList msgs, ChannelPromise promise) + throws Exception { + for (int i = 0; i < msgs.size(); i++) { + outboundBuffer.add(msgs.get(i)); + } + msgs.recycle(); } @Override - public void inboundBufferUpdated(ChannelHandlerContext ctx) throws Exception { - ctx.fireInboundBufferUpdated(); - } - } - - private static final class ChannelInboundByteHandlerImpl extends ChannelInboundByteHandlerAdapter { - boolean updated; - - @Override - protected void inboundBufferUpdated(ChannelHandlerContext ctx, ByteBuf in) throws Exception { - updated = true; - } - } - - private static final class ChannelOutboundByteHandlerImpl extends ChannelOutboundByteHandlerAdapter { - boolean flushed; - - @Override - protected void flush(ChannelHandlerContext ctx, ByteBuf in, ChannelPromise promise) throws Exception { - promise.setSuccess(); - flushed = true; - } - } - - private static final class ChannelInboundMessageHandlerImpl extends ChannelStateHandlerAdapter - implements ChannelInboundMessageHandler { - boolean updated; - @Override - public MessageBuf newInboundBuffer(ChannelHandlerContext ctx) throws Exception { - return Unpooled.messageBuffer(); + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + for (int i = 0; i < msgs.size(); i++) { + inboundBuffer.add(msgs.get(i)); + } + msgs.recycle(); } @Override - public void inboundBufferUpdated(ChannelHandlerContext ctx) throws Exception { - updated = true; - } - } - - private static final class ChannelOutboundMessageHandlerImpl extends ChannelOperationHandlerAdapter - implements ChannelOutboundMessageHandler { - boolean flushed; - @Override - public MessageBuf newOutboundBuffer(ChannelHandlerContext ctx) throws Exception { - return Unpooled.messageBuffer(); - } - - @Override - public void flush(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { - promise.setSuccess(); - flushed = true; - } - } - - private static final class ByteHandlerImpl extends CombinedChannelDuplexHandler - implements ChannelInboundByteHandler, ChannelOutboundByteHandler { - ByteHandlerImpl() { - super(new ChannelInboundByteHandlerImpl(), new ChannelOutboundByteHandlerImpl()); - } - - @Override - public ByteBuf newInboundBuffer(ChannelHandlerContext ctx) throws Exception { - return ((ChannelInboundByteHandler) stateHandler()).newInboundBuffer(ctx); - } - - @Override - public void discardInboundReadBytes(ChannelHandlerContext ctx) throws Exception { - ((ChannelInboundByteHandler) stateHandler()).discardInboundReadBytes(ctx); - } - - @Override - public ByteBuf newOutboundBuffer(ChannelHandlerContext ctx) throws Exception { - return ((ChannelOutboundByteHandler) operationHandler()).newOutboundBuffer(ctx); - } - - @Override - public void discardOutboundReadBytes(ChannelHandlerContext ctx) throws Exception { - ((ChannelOutboundByteHandler) operationHandler()).discardOutboundReadBytes(ctx); - } - } - - private static final class MessageHandlerImpl extends CombinedChannelDuplexHandler - implements ChannelInboundMessageHandler, ChannelOutboundMessageHandler { - MessageHandlerImpl() { - super(new ChannelInboundMessageHandlerImpl(), new ChannelOutboundMessageHandlerImpl()); - } - - @SuppressWarnings("unchecked") - @Override - public MessageBuf newInboundBuffer(ChannelHandlerContext ctx) throws Exception { - return ((ChannelInboundMessageHandler) stateHandler()).newInboundBuffer(ctx); - } - - @SuppressWarnings("unchecked") - @Override - public MessageBuf newOutboundBuffer(ChannelHandlerContext ctx) throws Exception { - return ((ChannelOutboundMessageHandler) operationHandler()).newOutboundBuffer(ctx); + public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { + if (!inboundBuffer.isEmpty()) { + ctx.fireMessageReceived(inboundBuffer); + } + if (!outboundBuffer.isEmpty()) { + ctx.write(outboundBuffer); + } } } diff --git a/transport/src/test/java/io/netty/channel/group/DefaultChannnelGroupTest.java b/transport/src/test/java/io/netty/channel/group/DefaultChannnelGroupTest.java index 99876ae266..43af68aa77 100644 --- a/transport/src/test/java/io/netty/channel/group/DefaultChannnelGroupTest.java +++ b/transport/src/test/java/io/netty/channel/group/DefaultChannnelGroupTest.java @@ -18,7 +18,7 @@ package io.netty.channel.group; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelStateHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; @@ -36,15 +36,11 @@ public class DefaultChannnelGroupTest { ServerBootstrap b = new ServerBootstrap(); b.group(bossGroup, workerGroup); - b.childHandler(new ChannelStateHandlerAdapter() { + b.childHandler(new ChannelInboundHandlerAdapter() { @Override public void channelActive(ChannelHandlerContext ctx) { allChannels.add(ctx.channel()); } - - @Override - public void inboundBufferUpdated(ChannelHandlerContext ctx) { - } }); b.channel(NioServerSocketChannel.class); diff --git a/transport/src/test/java/io/netty/channel/local/LocalChannelRegistryTest.java b/transport/src/test/java/io/netty/channel/local/LocalChannelRegistryTest.java index 17a1bc9393..830c77be58 100644 --- a/transport/src/test/java/io/netty/channel/local/LocalChannelRegistryTest.java +++ b/transport/src/test/java/io/netty/channel/local/LocalChannelRegistryTest.java @@ -19,9 +19,10 @@ import io.netty.bootstrap.Bootstrap; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInitializer; import io.netty.channel.EventLoopGroup; +import io.netty.channel.MessageList; import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLoggerFactory; import org.junit.Test; @@ -70,8 +71,7 @@ public class LocalChannelRegistryTest { @Override public void run() { // Send a message event up the pipeline. - cc.pipeline().inboundMessageBuffer().add("Hello, World"); - cc.pipeline().fireInboundBufferUpdated(); + cc.pipeline().fireMessageReceived("Hello, World"); latch.countDown(); } }); @@ -91,10 +91,12 @@ public class LocalChannelRegistryTest { } } - static class TestHandler extends ChannelInboundMessageHandlerAdapter { + static class TestHandler extends ChannelInboundHandlerAdapter { @Override - public void messageReceived(ChannelHandlerContext ctx, String msg) throws Exception { - logger.info(String.format("Received mesage: %s", msg)); + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + for (int i = 0; i < msgs.size(); i ++) { + logger.info(String.format("Received mesage: %s", msgs.get(i))); + } } } } diff --git a/transport/src/test/java/io/netty/channel/local/LocalTransportThreadModelTest.java b/transport/src/test/java/io/netty/channel/local/LocalTransportThreadModelTest.java index 01c741d756..59bca7c134 100644 --- a/transport/src/test/java/io/netty/channel/local/LocalTransportThreadModelTest.java +++ b/transport/src/test/java/io/netty/channel/local/LocalTransportThreadModelTest.java @@ -17,20 +17,14 @@ package io.netty.channel.local; import io.netty.bootstrap.ServerBootstrap; import io.netty.buffer.ByteBuf; -import io.netty.buffer.MessageBuf; -import io.netty.buffer.Unpooled; import io.netty.channel.Channel; import io.netty.channel.ChannelDuplexHandler; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelHandlerUtil; -import io.netty.channel.ChannelInboundByteHandler; -import io.netty.channel.ChannelInboundMessageHandler; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelOutboundByteHandler; -import io.netty.channel.ChannelOutboundMessageHandler; import io.netty.channel.ChannelPromise; import io.netty.channel.EventLoopGroup; +import io.netty.channel.MessageList; import io.netty.util.concurrent.DefaultEventExecutorGroup; import io.netty.util.concurrent.DefaultThreadFactory; import io.netty.util.concurrent.EventExecutorGroup; @@ -62,9 +56,9 @@ public class LocalTransportThreadModelTest { .childHandler(new ChannelInitializer() { @Override public void initChannel(LocalChannel ch) throws Exception { - ch.pipeline().addLast(new ChannelInboundMessageHandlerAdapter() { + ch.pipeline().addLast(new ChannelInboundHandlerAdapter() { @Override - public void messageReceived(ChannelHandlerContext ctx, Object msg) { + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) { // Discard } }); @@ -107,15 +101,15 @@ public class LocalTransportThreadModelTest { l.register(ch).sync().channel().connect(localAddr).sync(); // Fire inbound events from all possible starting points. - ch.pipeline().fireInboundBufferUpdated(); - ch.pipeline().context(h1).fireInboundBufferUpdated(); - ch.pipeline().context(h2).fireInboundBufferUpdated(); - ch.pipeline().context(h3).fireInboundBufferUpdated(); + ch.pipeline().fireMessageReceived("1"); + ch.pipeline().context(h1).fireMessageReceived("2"); + ch.pipeline().context(h2).fireMessageReceived("3"); + ch.pipeline().context(h3).fireMessageReceived("4"); // Fire outbound events from all possible starting points. - ch.pipeline().flush(); - ch.pipeline().context(h3).flush(); - ch.pipeline().context(h2).flush(); - ch.pipeline().context(h1).flush().sync(); + ch.pipeline().write("5"); + ch.pipeline().context(h3).write("6"); + ch.pipeline().context(h2).write("7"); + ch.pipeline().context(h1).write("8").sync(); ch.close().sync(); @@ -229,7 +223,6 @@ public class LocalTransportThreadModelTest { } @Test(timeout = 30000) - @Ignore("regression test") public void testConcurrentMessageBufferAccess() throws Throwable { EventLoopGroup l = new LocalEventLoopGroup(4, new DefaultThreadFactory("l")); EventExecutorGroup e1 = new DefaultEventExecutorGroup(4, new DefaultThreadFactory("e1")); @@ -270,11 +263,11 @@ public class LocalTransportThreadModelTest { ch.eventLoop().execute(new Runnable() { @Override public void run() { - MessageBuf buf = ch.pipeline().inboundMessageBuffer(); + MessageList msgs = MessageList.newInstance(end - start); for (int j = start; j < end; j ++) { - buf.add(Integer.valueOf(j)); + msgs.add(Integer.valueOf(j)); } - ch.pipeline().fireInboundBufferUpdated(); + ch.pipeline().fireMessageReceived(msgs); } }); } @@ -310,11 +303,12 @@ public class LocalTransportThreadModelTest { ch.pipeline().context(h6).executor().execute(new Runnable() { @Override public void run() { - MessageBuf buf = ch.pipeline().outboundMessageBuffer(); + MessageList msgs = MessageList.newInstance(end - start); for (int j = start; j < end; j ++) { - buf.add(Integer.valueOf(j)); + msgs.add(Integer.valueOf(j)); } - ch.pipeline().flush(); + + ch.pipeline().write(msgs); } }); } @@ -353,10 +347,7 @@ public class LocalTransportThreadModelTest { } } - private static class ThreadNameAuditor - extends ChannelDuplexHandler - implements ChannelInboundMessageHandler, - ChannelOutboundMessageHandler { + private static class ThreadNameAuditor extends ChannelDuplexHandler { private final AtomicReference exception = new AtomicReference(); @@ -364,40 +355,21 @@ public class LocalTransportThreadModelTest { private final Queue outboundThreadNames = new ConcurrentLinkedQueue(); private final Queue removalThreadNames = new ConcurrentLinkedQueue(); - @Override - public MessageBuf newInboundBuffer(ChannelHandlerContext ctx) throws Exception { - return Unpooled.messageBuffer(); - } - - @Override - public MessageBuf newOutboundBuffer(ChannelHandlerContext ctx) throws Exception { - return Unpooled.messageBuffer(); - } - @Override public void handlerRemoved(ChannelHandlerContext ctx) throws Exception { removalThreadNames.add(Thread.currentThread().getName()); } @Override - public void inboundBufferUpdated( - ChannelHandlerContext ctx) throws Exception { - ctx.inboundMessageBuffer().clear(); + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { inboundThreadNames.add(Thread.currentThread().getName()); - ctx.fireInboundBufferUpdated(); + ctx.fireMessageReceived(msgs); } @Override - public void read(ChannelHandlerContext ctx) { - ctx.read(); - } - - @Override - public void flush(ChannelHandlerContext ctx, - ChannelPromise future) throws Exception { - ctx.outboundMessageBuffer().clear(); + public void write(ChannelHandlerContext ctx, MessageList msgs, ChannelPromise future) throws Exception { outboundThreadNames.add(Thread.currentThread().getName()); - ctx.flush(future); + ctx.write(msgs, future); } @Override @@ -413,8 +385,7 @@ public class LocalTransportThreadModelTest { * Converts integers into a binary stream. */ private static class MessageForwarder1 - extends ChannelDuplexHandler - implements ChannelInboundMessageHandler, ChannelOutboundByteHandler { + extends ChannelDuplexHandler { private final AtomicReference exception = new AtomicReference(); private volatile int inCnt; @@ -422,23 +393,7 @@ public class LocalTransportThreadModelTest { private volatile Thread t; @Override - public MessageBuf newInboundBuffer(ChannelHandlerContext ctx) throws Exception { - return Unpooled.messageBuffer(); - } - - @Override - public ByteBuf newOutboundBuffer(ChannelHandlerContext ctx) throws Exception { - return ChannelHandlerUtil.allocate(ctx); - } - - @Override - public void discardOutboundReadBytes(ChannelHandlerContext ctx) throws Exception { - // NOOP - } - - @Override - public void inboundBufferUpdated( - ChannelHandlerContext ctx) throws Exception { + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { Thread t = this.t; if (t == null) { this.t = Thread.currentThread(); @@ -446,55 +401,44 @@ public class LocalTransportThreadModelTest { Assert.assertSame(t, Thread.currentThread()); } - MessageBuf in = ctx.inboundMessageBuffer(); - ByteBuf out = ctx.nextInboundByteBuffer(); - - for (;;) { - Integer msg = in.poll(); - if (msg == null) { - break; - } + ByteBuf out = ctx.alloc().buffer(msgs.size() * 4); + for (int i = 0; i < msgs.size(); i ++) { + int m = ((Integer) msgs.get(i)).intValue(); int expected = inCnt ++; - Assert.assertEquals(expected, msg.intValue()); - if (out.maxCapacity() - out.writerIndex() < 4) { - // Next inbound buffer is full - attempt to flush some data. - ctx.fireInboundBufferUpdated(); - } - out.writeInt(msg); + Assert.assertEquals(expected, m); + out.writeInt(m); } - ctx.fireInboundBufferUpdated(); + ctx.fireMessageReceived(out); } @Override - public void read(ChannelHandlerContext ctx) { - ctx.read(); - } - - @Override - public void flush(ChannelHandlerContext ctx, - ChannelPromise future) throws Exception { + public void write(ChannelHandlerContext ctx, MessageList msgs, ChannelPromise future) throws Exception { Assert.assertSame(t, Thread.currentThread()); // Don't let the write request go to the server-side channel - just swallow. boolean swallow = this == ctx.pipeline().first(); - ByteBuf in = ctx.outboundByteBuffer(); - MessageBuf out = ctx.nextOutboundMessageBuffer(); - while (in.readableBytes() >= 4) { - int msg = in.readInt(); - int expected = outCnt ++; - Assert.assertEquals(expected, msg); - if (!swallow) { - out.add(msg); + for (int i = 0; i < msgs.size(); i ++) { + ByteBuf m = (ByteBuf) msgs.get(i); + int count = m.readableBytes() / 4; + MessageList out = MessageList.newInstance(count); + for (int j = 0; j < count; j ++) { + int actual = m.readInt(); + int expected = outCnt ++; + Assert.assertEquals(expected, actual); + if (!swallow) { + out.add(actual); + } + } + m.release(); + + if (swallow) { + future.setSuccess(); + } else { + ctx.write(out); } - } - in.discardSomeReadBytes(); - if (swallow) { - future.setSuccess(); - } else { - ctx.flush(future); } } @@ -510,9 +454,7 @@ public class LocalTransportThreadModelTest { /** * Converts a binary stream into integers. */ - private static class MessageForwarder2 - extends ChannelDuplexHandler - implements ChannelInboundByteHandler, ChannelOutboundMessageHandler { + private static class MessageForwarder2 extends ChannelDuplexHandler { private final AtomicReference exception = new AtomicReference(); private volatile int inCnt; @@ -520,25 +462,7 @@ public class LocalTransportThreadModelTest { private volatile Thread t; @Override - public ByteBuf newInboundBuffer( - ChannelHandlerContext ctx) throws Exception { - return ctx.alloc().buffer(); - } - - @Override - public void discardInboundReadBytes(ChannelHandlerContext ctx) throws Exception { - ctx.inboundByteBuffer().discardSomeReadBytes(); - } - - @Override - public MessageBuf newOutboundBuffer( - ChannelHandlerContext ctx) throws Exception { - return Unpooled.messageBuffer(); - } - - @Override - public void inboundBufferUpdated( - ChannelHandlerContext ctx) throws Exception { + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { Thread t = this.t; if (t == null) { this.t = Thread.currentThread(); @@ -546,49 +470,35 @@ public class LocalTransportThreadModelTest { Assert.assertSame(t, Thread.currentThread()); } - ByteBuf in = ctx.inboundByteBuffer(); - MessageBuf out = ctx.nextInboundMessageBuffer(); - while (in.readableBytes() >= 4) { - int msg = in.readInt(); - int expected = inCnt ++; - Assert.assertEquals(expected, msg); - out.add(msg); + for (int i = 0; i < msgs.size(); i ++) { + ByteBuf m = (ByteBuf) msgs.get(i); + int count = m.readableBytes() / 4; + MessageList out = MessageList.newInstance(count); + for (int j = 0; j < count; j ++) { + int actual = m.readInt(); + int expected = inCnt ++; + Assert.assertEquals(expected, actual); + out.add(actual); + } + m.release(); + ctx.fireMessageReceived(out); } - in.discardReadBytes(); - ctx.fireInboundBufferUpdated(); } @Override - public void read(ChannelHandlerContext ctx) { - ctx.read(); - } - - @Override - public void flush(final ChannelHandlerContext ctx, - ChannelPromise future) throws Exception { + public void write( + ChannelHandlerContext ctx, MessageList msgs, ChannelPromise promise) throws Exception { Assert.assertSame(t, Thread.currentThread()); - MessageBuf in = ctx.outboundMessageBuffer(); - ByteBuf out = ctx.nextOutboundByteBuffer(); - - for (;;) { - Integer msg = in.poll(); - if (msg == null) { - break; - } - + ByteBuf out = ctx.alloc().buffer(msgs.size() * 4); + for (int i = 0; i < msgs.size(); i ++) { + int m = (Integer) msgs.get(i); int expected = outCnt ++; - Assert.assertEquals(expected, msg.intValue()); - - if (out.maxCapacity() - out.writerIndex() < 4) { - // Next outbound buffer is full - attempt to flush some data. - ctx.flush(); - } - - out.writeInt(msg); + Assert.assertEquals(expected, m); + out.writeInt(m); } - ctx.flush(future); + ctx.write(out); } @Override @@ -603,9 +513,7 @@ public class LocalTransportThreadModelTest { /** * Simply forwards the received object to the next handler. */ - private static class MessageForwarder3 - extends ChannelDuplexHandler - implements ChannelInboundMessageHandler, ChannelOutboundMessageHandler { + private static class MessageForwarder3 extends ChannelDuplexHandler { private final AtomicReference exception = new AtomicReference(); private volatile int inCnt; @@ -613,17 +521,7 @@ public class LocalTransportThreadModelTest { private volatile Thread t; @Override - public MessageBuf newInboundBuffer(ChannelHandlerContext ctx) throws Exception { - return Unpooled.messageBuffer(); - } - - @Override - public MessageBuf newOutboundBuffer(ChannelHandlerContext ctx) throws Exception { - return Unpooled.messageBuffer(); - } - - @Override - public void inboundBufferUpdated(ChannelHandlerContext ctx) throws Exception { + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { Thread t = this.t; if (t == null) { this.t = Thread.currentThread(); @@ -631,44 +529,25 @@ public class LocalTransportThreadModelTest { Assert.assertSame(t, Thread.currentThread()); } - MessageBuf in = ctx.inboundMessageBuffer(); - MessageBuf out = ctx.nextInboundMessageBuffer(); - for (;;) { - Object msg = in.poll(); - if (msg == null) { - break; - } - + for (int i = 0; i < msgs.size(); i ++) { + int actual = (Integer) msgs.get(i); int expected = inCnt ++; - Assert.assertEquals(expected, msg); - out.add(msg); + Assert.assertEquals(expected, actual); } - ctx.fireInboundBufferUpdated(); + ctx.fireMessageReceived(msgs); } @Override - public void read(ChannelHandlerContext ctx) { - ctx.read(); - } - - @Override - public void flush(ChannelHandlerContext ctx, - ChannelPromise future) throws Exception { + public void write( + ChannelHandlerContext ctx, MessageList msgs, ChannelPromise promise) throws Exception { Assert.assertSame(t, Thread.currentThread()); - MessageBuf in = ctx.outboundMessageBuffer(); - MessageBuf out = ctx.nextOutboundMessageBuffer(); - for (;;) { - Object msg = in.poll(); - if (msg == null) { - break; - } - + for (int i = 0; i < msgs.size(); i ++) { + int actual = (Integer) msgs.get(i); int expected = outCnt ++; - Assert.assertEquals(expected, msg); - out.add(msg); + Assert.assertEquals(expected, actual); } - ctx.flush(future); + ctx.write(msgs, promise); } @Override @@ -683,9 +562,7 @@ public class LocalTransportThreadModelTest { /** * Discards all received messages. */ - private static class MessageDiscarder - extends ChannelDuplexHandler - implements ChannelInboundMessageHandler, ChannelOutboundMessageHandler { + private static class MessageDiscarder extends ChannelDuplexHandler { private final AtomicReference exception = new AtomicReference(); private volatile int inCnt; @@ -693,17 +570,7 @@ public class LocalTransportThreadModelTest { private volatile Thread t; @Override - public MessageBuf newInboundBuffer(ChannelHandlerContext ctx) throws Exception { - return Unpooled.messageBuffer(); - } - - @Override - public MessageBuf newOutboundBuffer(ChannelHandlerContext ctx) throws Exception { - return Unpooled.messageBuffer(); - } - - @Override - public void inboundBufferUpdated(ChannelHandlerContext ctx) throws Exception { + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { Thread t = this.t; if (t == null) { this.t = Thread.currentThread(); @@ -711,40 +578,24 @@ public class LocalTransportThreadModelTest { Assert.assertSame(t, Thread.currentThread()); } - MessageBuf in = ctx.inboundMessageBuffer(); - for (;;) { - Object msg = in.poll(); - if (msg == null) { - break; - } + for (int i = 0; i < msgs.size(); i ++) { + int actual = (Integer) msgs.get(i); int expected = inCnt ++; - Assert.assertEquals(expected, msg); + Assert.assertEquals(expected, actual); } } @Override - public void read(ChannelHandlerContext ctx) { - ctx.read(); - } - - @Override - public void flush(ChannelHandlerContext ctx, - ChannelPromise future) throws Exception { + public void write( + ChannelHandlerContext ctx, MessageList msgs, ChannelPromise promise) throws Exception { Assert.assertSame(t, Thread.currentThread()); - MessageBuf in = ctx.outboundMessageBuffer(); - MessageBuf out = ctx.nextOutboundMessageBuffer(); - for (;;) { - Object msg = in.poll(); - if (msg == null) { - break; - } - + for (int i = 0; i < msgs.size(); i ++) { + int actual = (Integer) msgs.get(i); int expected = outCnt ++; - Assert.assertEquals(expected, msg); - out.add(msg); + Assert.assertEquals(expected, actual); } - ctx.flush(future); + ctx.write(msgs, promise); } @Override diff --git a/transport/src/test/java/io/netty/channel/local/LocalTransportThreadModelTest2.java b/transport/src/test/java/io/netty/channel/local/LocalTransportThreadModelTest2.java index fba75ce135..174bf7e041 100644 --- a/transport/src/test/java/io/netty/channel/local/LocalTransportThreadModelTest2.java +++ b/transport/src/test/java/io/netty/channel/local/LocalTransportThreadModelTest2.java @@ -17,15 +17,14 @@ package io.netty.channel.local; import io.netty.bootstrap.Bootstrap; import io.netty.bootstrap.ServerBootstrap; -import io.netty.buffer.MessageBuf; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelHandler.Sharable; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.channel.MessageList; import org.junit.Test; -import java.util.Iterator; import java.util.concurrent.atomic.AtomicInteger; import static org.junit.Assert.*; @@ -73,30 +72,11 @@ public class LocalTransportThreadModelTest2 { public void close(final Channel localChannel, final LocalHander localRegistrationHandler) { // we want to make sure we actually shutdown IN the event loop if (localChannel.eventLoop().inEventLoop()) { - MessageBuf outboundMessageBuffer = - localChannel.pipeline().outboundMessageBuffer(); - if (!outboundMessageBuffer.isEmpty()) { - System.err.println("NOT EMPTY TO SEND!"); - } - // Wait until all messages are flushed before closing the channel. if (localRegistrationHandler.lastWriteFuture != null) { localRegistrationHandler.lastWriteFuture.awaitUninterruptibly(); } - MessageBuf inboundMessageBuffer = - localChannel.pipeline().inboundMessageBuffer(); - if (!inboundMessageBuffer.isEmpty()) { - // sometimes we close the pipeline before everything on it has been notified/received. - // we want these messages, since they are in our queue. - Iterator iterator = inboundMessageBuffer.iterator(); - while (iterator.hasNext()) { - Object next = iterator.next(); - System.err.println("DEFERRED on close: " + next); - iterator.remove(); - } - } - localChannel.close(); return; } @@ -113,7 +93,7 @@ public class LocalTransportThreadModelTest2 { } @Sharable - static class LocalHander extends ChannelInboundMessageHandlerAdapter { + static class LocalHander extends ChannelInboundHandlerAdapter { private final String name; public volatile ChannelFuture lastWriteFuture; @@ -132,8 +112,9 @@ public class LocalTransportThreadModelTest2 { } @Override - public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { - count.incrementAndGet(); + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { + count.addAndGet(msgs.size()); + msgs.recycle(); } } } diff --git a/transport/src/test/java/io/netty/channel/local/LocalTransportThreadModelTest3.java b/transport/src/test/java/io/netty/channel/local/LocalTransportThreadModelTest3.java index ccc01297b2..dad52b2226 100644 --- a/transport/src/test/java/io/netty/channel/local/LocalTransportThreadModelTest3.java +++ b/transport/src/test/java/io/netty/channel/local/LocalTransportThreadModelTest3.java @@ -20,10 +20,11 @@ import io.netty.channel.Channel; import io.netty.channel.ChannelDuplexHandler; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPromise; import io.netty.channel.EventLoopGroup; +import io.netty.channel.MessageList; import io.netty.util.concurrent.DefaultEventExecutorGroup; import io.netty.util.concurrent.DefaultThreadFactory; import io.netty.util.concurrent.EventExecutorGroup; @@ -50,8 +51,8 @@ public class LocalTransportThreadModelTest3 { ACTIVE, UNREGISTERED, REGISTERED, - INBOUND_BUFFER_UPDATED, - FLUSH, + MESSAGE_RECEIVED, + WRITE, READ } @@ -68,9 +69,10 @@ public class LocalTransportThreadModelTest3 { .childHandler(new ChannelInitializer() { @Override public void initChannel(LocalChannel ch) throws Exception { - ch.pipeline().addLast(new ChannelInboundMessageHandlerAdapter() { + ch.pipeline().addLast(new ChannelInboundHandlerAdapter() { @Override - public void messageReceived(ChannelHandlerContext ctx, Object msg) { + public void messageReceived( + ChannelHandlerContext ctx, MessageList msgs) throws Exception { // Discard } }); @@ -176,8 +178,8 @@ public class LocalTransportThreadModelTest3 { case EXCEPTION_CAUGHT: ch.pipeline().fireExceptionCaught(cause); break; - case INBOUND_BUFFER_UPDATED: - ch.pipeline().fireInboundBufferUpdated(); + case MESSAGE_RECEIVED: + ch.pipeline().fireMessageReceived(""); break; case READ_SUSPEND: ch.pipeline().fireChannelReadSuspended(); @@ -185,8 +187,8 @@ public class LocalTransportThreadModelTest3 { case USER_EVENT: ch.pipeline().fireUserEventTriggered(""); break; - case FLUSH: - ch.pipeline().flush(); + case WRITE: + ch.pipeline().write(""); break; case READ: ch.pipeline().read(); @@ -227,11 +229,11 @@ public class LocalTransportThreadModelTest3 { EventType[] events; if (inbound) { events = new EventType[] { - EventType.USER_EVENT, EventType.INBOUND_BUFFER_UPDATED, EventType.READ_SUSPEND, + EventType.USER_EVENT, EventType.MESSAGE_RECEIVED, EventType.READ_SUSPEND, EventType.EXCEPTION_CAUGHT}; } else { events = new EventType[] { - EventType.READ, EventType.FLUSH, EventType.EXCEPTION_CAUGHT }; + EventType.READ, EventType.WRITE, EventType.EXCEPTION_CAUGHT }; } Random random = new Random(); @@ -243,17 +245,7 @@ public class LocalTransportThreadModelTest3 { } @ChannelHandler.Sharable - private static final class EventForwarder extends ChannelDuplexHandler { - @Override - public void flush(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { - ctx.flush(promise); - } - - @Override - public void inboundBufferUpdated(ChannelHandlerContext ctx) throws Exception { - ctx.fireInboundBufferUpdated(); - } - } + private static final class EventForwarder extends ChannelDuplexHandler { } private static final class EventRecorder extends ChannelDuplexHandler { private final Queue events; @@ -304,16 +296,17 @@ public class LocalTransportThreadModelTest3 { } @Override - public void inboundBufferUpdated(ChannelHandlerContext ctx) throws Exception { + public void messageReceived(ChannelHandlerContext ctx, MessageList msgs) throws Exception { if (inbound) { - events.add(EventType.INBOUND_BUFFER_UPDATED); + events.add(EventType.MESSAGE_RECEIVED); } } @Override - public void flush(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { + public void write( + ChannelHandlerContext ctx, MessageList msgs, ChannelPromise promise) throws Exception { if (!inbound) { - events.add(EventType.FLUSH); + events.add(EventType.WRITE); } } diff --git a/transport/src/test/java/io/netty/channel/nio/NioDatagramChannelTest.java b/transport/src/test/java/io/netty/channel/nio/NioDatagramChannelTest.java index 283b37fd3b..fc35c993bc 100644 --- a/transport/src/test/java/io/netty/channel/nio/NioDatagramChannelTest.java +++ b/transport/src/test/java/io/netty/channel/nio/NioDatagramChannelTest.java @@ -17,11 +17,11 @@ package io.netty.channel.nio; import io.netty.bootstrap.Bootstrap; import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundMessageHandlerAdapter; +import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelOption; +import io.netty.channel.MessageList; import io.netty.channel.group.DefaultChannelGroup; import io.netty.channel.socket.DatagramChannel; -import io.netty.channel.socket.DatagramPacket; import io.netty.channel.socket.nio.NioDatagramChannel; import org.junit.Assert; import org.junit.Test; @@ -43,10 +43,10 @@ public class NioDatagramChannelTest { Bootstrap udpBootstrap = new Bootstrap(); udpBootstrap.group(group).channel(NioDatagramChannel.class) .option(ChannelOption.SO_BROADCAST, true) - .handler(new ChannelInboundMessageHandlerAdapter() { + .handler(new ChannelInboundHandlerAdapter() { @Override - public void messageReceived(ChannelHandlerContext ctx, DatagramPacket msg) - throws Exception { + public void messageReceived( + ChannelHandlerContext ctx, MessageList msgs) throws Exception { // noop } });