Revamp the core API to reduce memory footprint and consumption

The API changes made so far turned out to increase the memory footprint
and consumption while our intention was actually decreasing them.

Memory consumption issue:

When there are many connections which does not exchange data frequently,
the old Netty 4 API spent a lot more memory than 3 because it always
allocates per-handler buffer for each connection unless otherwise
explicitly stated by a user.  In a usual real world load, a client
doesn't always send requests without pausing, so the idea of having a
buffer whose life cycle if bound to the life cycle of a connection
didn't work as expected.

Memory footprint issue:

The old Netty 4 API decreased overall memory footprint by a great deal
in many cases.  It was mainly because the old Netty 4 API did not
allocate a new buffer and event object for each read.  Instead, it
created a new buffer for each handler in a pipeline.  This works pretty
well as long as the number of handlers in a pipeline is only a few.
However, for a highly modular application with many handlers which
handles connections which lasts for relatively short period, it actually
makes the memory footprint issue much worse.

Changes:

All in all, this is about retaining all the good changes we made in 4 so
far such as better thread model and going back to the way how we dealt
with message events in 3.

To fix the memory consumption/footprint issue mentioned above, we made a
hard decision to break the backward compatibility again with the
following changes:

- Remove MessageBuf
- Merge Buf into ByteBuf
- Merge ChannelInboundByte/MessageHandler and ChannelStateHandler into ChannelInboundHandler
  - Similar changes were made to the adapter classes
- Merge ChannelOutboundByte/MessageHandler and ChannelOperationHandler into ChannelOutboundHandler
  - Similar changes were made to the adapter classes
- Introduce MessageList which is similar to `MessageEvent` in Netty 3
- Replace inboundBufferUpdated(ctx) with messageReceived(ctx, MessageList)
- Replace flush(ctx, promise) with write(ctx, MessageList, promise)
- Remove ByteToByteEncoder/Decoder/Codec
  - Replaced by MessageToByteEncoder<ByteBuf>, ByteToMessageDecoder<ByteBuf>, and ByteMessageCodec<ByteBuf>
- Merge EmbeddedByteChannel and EmbeddedMessageChannel into EmbeddedChannel
- Add SimpleChannelInboundHandler which is sometimes more useful than
  ChannelInboundHandlerAdapter
- Bring back Channel.isWritable() from Netty 3
- Add ChannelInboundHandler.channelWritabilityChanges() event
- Add RecvByteBufAllocator configuration property
  - Similar to ReceiveBufferSizePredictor in Netty 3
  - Some existing configuration properties such as
    DatagramChannelConfig.receivePacketSize is gone now.
- Remove suspend/resumeIntermediaryDeallocation() in ByteBuf

This change would have been impossible without @normanmaurer's help. He
fixed, ported, and improved many parts of the changes.
This commit is contained in:
Trustin Lee 2013-05-28 20:40:19 +09:00
parent fe711f65fb
commit 14158070bf
357 changed files with 6282 additions and 10019 deletions

View File

@ -20,7 +20,7 @@
<parent> <parent>
<groupId>io.netty</groupId> <groupId>io.netty</groupId>
<artifactId>netty-parent</artifactId> <artifactId>netty-parent</artifactId>
<version>4.0.0.Final-SNAPSHOT</version> <version>4.0.0.CR4-SNAPSHOT</version>
</parent> </parent>
<artifactId>netty-all</artifactId> <artifactId>netty-all</artifactId>

View File

@ -20,7 +20,7 @@
<parent> <parent>
<groupId>io.netty</groupId> <groupId>io.netty</groupId>
<artifactId>netty-parent</artifactId> <artifactId>netty-parent</artifactId>
<version>4.0.0.Final-SNAPSHOT</version> <version>4.0.0.CR4-SNAPSHOT</version>
</parent> </parent>
<artifactId>netty-buffer</artifactId> <artifactId>netty-buffer</artifactId>

View File

@ -50,11 +50,6 @@ public abstract class AbstractByteBuf implements ByteBuf {
this.maxCapacity = maxCapacity; this.maxCapacity = maxCapacity;
} }
@Override
public BufType type() {
return BufType.BYTE;
}
@Override @Override
public int maxCapacity() { public int maxCapacity() {
return maxCapacity; return maxCapacity;
@ -971,17 +966,17 @@ public abstract class AbstractByteBuf implements ByteBuf {
nioBuffer.flip(); nioBuffer.flip();
} }
return BufUtil.decodeString(nioBuffer, charset); return ByteBufUtil.decodeString(nioBuffer, charset);
} }
@Override @Override
public int indexOf(int fromIndex, int toIndex, byte value) { public int indexOf(int fromIndex, int toIndex, byte value) {
return BufUtil.indexOf(this, fromIndex, toIndex, value); return ByteBufUtil.indexOf(this, fromIndex, toIndex, value);
} }
@Override @Override
public int indexOf(int fromIndex, int toIndex, ByteBufIndexFinder indexFinder) { public int indexOf(int fromIndex, int toIndex, ByteBufIndexFinder indexFinder) {
return BufUtil.indexOf(this, fromIndex, toIndex, indexFinder); return ByteBufUtil.indexOf(this, fromIndex, toIndex, indexFinder);
} }
@Override @Override
@ -1027,7 +1022,7 @@ public abstract class AbstractByteBuf implements ByteBuf {
@Override @Override
public int hashCode() { public int hashCode() {
return BufUtil.hashCode(this); return ByteBufUtil.hashCode(this);
} }
@Override @Override
@ -1036,14 +1031,14 @@ public abstract class AbstractByteBuf implements ByteBuf {
return true; return true;
} }
if (o instanceof ByteBuf) { if (o instanceof ByteBuf) {
return BufUtil.equals(this, (ByteBuf) o); return ByteBufUtil.equals(this, (ByteBuf) o);
} }
return false; return false;
} }
@Override @Override
public int compareTo(ByteBuf that) { public int compareTo(ByteBuf that) {
return BufUtil.compare(this, that); return ByteBufUtil.compare(this, that);
} }
@Override @Override

View File

@ -52,14 +52,4 @@ public abstract class AbstractDerivedByteBuf extends AbstractByteBuf {
public final boolean release(int decrement) { public final boolean release(int decrement) {
return unwrap().release(decrement); return unwrap().release(decrement);
} }
@Override
public final ByteBuf suspendIntermediaryDeallocations() {
throw new UnsupportedOperationException("derived");
}
@Override
public final ByteBuf resumeIntermediaryDeallocations() {
throw new UnsupportedOperationException("derived");
}
} }

View File

@ -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 <T>
*/
public abstract class AbstractMessageBuf<T> extends AbstractQueue<T> implements MessageBuf<T> {
@SuppressWarnings("rawtypes")
private static final AtomicIntegerFieldUpdater<AbstractMessageBuf> 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<T> 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<T> 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<? super T> c) {
ensureAccessible();
int cnt = 0;
for (;;) {
T o = poll();
if (o == null) {
break;
}
c.add(o);
cnt ++;
}
return cnt;
}
@Override
public int drainTo(Collection<? super T> 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();
}
}

View File

@ -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);
}

View File

@ -227,7 +227,7 @@ import java.nio.charset.UnsupportedCharsetException;
* Please refer to {@link ByteBufInputStream} and * Please refer to {@link ByteBufInputStream} and
* {@link ByteBufOutputStream}. * {@link ByteBufOutputStream}.
*/ */
public interface ByteBuf extends Buf, Comparable<ByteBuf> { public interface ByteBuf extends ReferenceCounted, Comparable<ByteBuf> {
/** /**
* Returns the number of bytes (octets) this buffer can contain. * Returns the number of bytes (octets) this buffer can contain.
@ -248,7 +248,6 @@ public interface ByteBuf extends Buf, Comparable<ByteBuf> {
* {@link #ensureWritable(int)}, those methods will raise an * {@link #ensureWritable(int)}, those methods will raise an
* {@link IllegalArgumentException}. * {@link IllegalArgumentException}.
*/ */
@Override
int maxCapacity(); int maxCapacity();
/** /**
@ -391,9 +390,13 @@ public interface ByteBuf extends Buf, Comparable<ByteBuf> {
* if and only if {@code (this.writerIndex - this.readerIndex)} is greater * if and only if {@code (this.writerIndex - this.readerIndex)} is greater
* than {@code 0}. * than {@code 0}.
*/ */
@Override
boolean isReadable(); 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. * @deprecated Use {@link #isReadable()} or {@link #isReadable(int)} instead.
*/ */
@ -405,9 +408,14 @@ public interface ByteBuf extends Buf, Comparable<ByteBuf> {
* if and only if {@code (this.capacity - this.writerIndex)} is greater * if and only if {@code (this.capacity - this.writerIndex)} is greater
* than {@code 0}. * than {@code 0}.
*/ */
@Override
boolean isWritable(); 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. * @deprecated Use {@link #isWritable()} or {@link #isWritable(int)} instead.
*/ */
@ -1869,23 +1877,6 @@ public interface ByteBuf extends Buf, Comparable<ByteBuf> {
*/ */
String toString(int index, int length, Charset charset); 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 * Returns a hash code which was calculated from the content of this
* buffer. If there's a byte array which is * buffer. If there's a byte array which is

View File

@ -25,13 +25,11 @@ import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder; import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder; import java.nio.charset.CharsetEncoder;
import java.nio.charset.CoderResult; 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 * A collection of utility methods that is related with handling {@link ByteBuf}.
* elements.
*/ */
public final class BufUtil { public final class ByteBufUtil {
private static final char[] HEXDUMP_TABLE = new char[256 * 4]; private static final char[] HEXDUMP_TABLE = new char[256 * 4];
@ -418,29 +416,5 @@ public final class BufUtil {
return dst.flip().toString(); return dst.flip().toString();
} }
/** private ByteBufUtil() { }
* 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() { }
} }

View File

@ -387,12 +387,6 @@ public interface CompositeByteBuf extends ByteBuf, Iterable<ByteBuf> {
@Override @Override
CompositeByteBuf writeZero(int length); CompositeByteBuf writeZero(int length);
@Override
CompositeByteBuf suspendIntermediaryDeallocations();
@Override
CompositeByteBuf resumeIntermediaryDeallocations();
@Override @Override
CompositeByteBuf retain(int increment); CompositeByteBuf retain(int increment);

View File

@ -26,14 +26,12 @@ import java.nio.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.nio.channels.GatheringByteChannel; import java.nio.channels.GatheringByteChannel;
import java.nio.channels.ScatteringByteChannel; import java.nio.channels.ScatteringByteChannel;
import java.util.ArrayDeque;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.ListIterator; import java.util.ListIterator;
import java.util.Queue;
/** /**
@ -50,7 +48,6 @@ public class DefaultCompositeByteBuf extends AbstractReferenceCountedByteBuf imp
private final int maxNumComponents; private final int maxNumComponents;
private boolean freed; private boolean freed;
private Queue<ByteBuf> suspendedDeallocations;
public DefaultCompositeByteBuf(ByteBufAllocator alloc, boolean direct, int maxNumComponents) { public DefaultCompositeByteBuf(ByteBufAllocator alloc, boolean direct, int maxNumComponents) {
super(Integer.MAX_VALUE); super(Integer.MAX_VALUE);
@ -1202,11 +1199,7 @@ public class DefaultCompositeByteBuf extends AbstractReferenceCountedByteBuf imp
void freeIfNecessary() { void freeIfNecessary() {
// Unwrap so that we can free slices, too. // 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.
buf.release(); // We should not get a NPE here. If so, it must be a bug.
} else {
suspendedDeallocations.add(buf);
}
} }
} }
@ -1457,7 +1450,6 @@ public class DefaultCompositeByteBuf extends AbstractReferenceCountedByteBuf imp
} }
freed = true; freed = true;
resumeIntermediaryDeallocations();
for (Component c: components) { for (Component c: components) {
c.freeIfNecessary(); c.freeIfNecessary();
} }
@ -1465,30 +1457,6 @@ public class DefaultCompositeByteBuf extends AbstractReferenceCountedByteBuf imp
leak.close(); leak.close();
} }
@Override
public CompositeByteBuf suspendIntermediaryDeallocations() {
ensureAccessible();
if (suspendedDeallocations == null) {
suspendedDeallocations = new ArrayDeque<ByteBuf>(2);
}
return this;
}
@Override
public CompositeByteBuf resumeIntermediaryDeallocations() {
if (suspendedDeallocations == null) {
return this;
}
Queue<ByteBuf> suspendedDeallocations = this.suspendedDeallocations;
this.suspendedDeallocations = null;
for (ByteBuf buf: suspendedDeallocations) {
buf.release();
}
return this;
}
@Override @Override
public ByteBuf unwrap() { public ByteBuf unwrap() {
return null; return null;

View File

@ -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<T> extends AbstractMessageBuf<T> {
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<T> 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> 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> 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> T[] cast(Object a) {
return (T[]) a;
}
private class Itr implements Iterator<T> {
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;
}
}
}

View File

@ -800,16 +800,6 @@ public final class EmptyByteBuf implements ByteBuf {
return toString(charset); return toString(charset);
} }
@Override
public ByteBuf suspendIntermediaryDeallocations() {
return this;
}
@Override
public ByteBuf resumeIntermediaryDeallocations() {
return this;
}
@Override @Override
public int hashCode() { public int hashCode() {
return 0; return 0;
@ -830,11 +820,6 @@ public final class EmptyByteBuf implements ByteBuf {
return str; return str;
} }
@Override
public BufType type() {
return BufType.BYTE;
}
@Override @Override
public boolean isReadable(int size) { public boolean isReadable(int size) {
checkLength(size); checkLength(size);

View File

@ -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<Object> {
protected final MessageBuf<Object> buf;
@SuppressWarnings("unchecked")
protected FilteredMessageBuf(MessageBuf<?> buf) {
if (buf == null) {
throw new NullPointerException("buf");
}
this.buf = (MessageBuf<Object>) buf;
}
protected abstract Object filter(Object msg);
@Override
public int drainTo(Collection<? super Object> c) {
return buf.drainTo(c);
}
@Override
public int drainTo(Collection<? super Object> 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<Object> iterator() {
return buf.iterator();
}
@Override
public Object[] toArray() {
return buf.toArray();
}
@Override
public <T> 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<Object> retain() {
buf.retain();
return this;
}
@Override
public MessageBuf<Object> 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 + ')';
}
}

View File

@ -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 <T> the type of the messages that are hold by this {@link MessageBuf}
*/
public interface MessageBuf<T> extends Buf, Queue<T> {
/**
* 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<? super T> 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<? super T> c, int maxElements);
@Override
MessageBuf<T> retain(int increment);
@Override
MessageBuf<T> retain();
}

View File

@ -20,8 +20,6 @@ import io.netty.util.ResourceLeak;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.util.ArrayDeque;
import java.util.Queue;
abstract class PooledByteBuf<T> extends AbstractReferenceCountedByteBuf { abstract class PooledByteBuf<T> extends AbstractReferenceCountedByteBuf {
@ -35,7 +33,6 @@ abstract class PooledByteBuf<T> extends AbstractReferenceCountedByteBuf {
private int maxLength; private int maxLength;
private ByteBuffer tmpNioBuf; private ByteBuffer tmpNioBuf;
private Queue<Allocation<T>> suspendedDeallocations;
protected PooledByteBuf(int maxCapacity) { protected PooledByteBuf(int maxCapacity) {
super(maxCapacity); super(maxCapacity);
@ -108,13 +105,7 @@ abstract class PooledByteBuf<T> extends AbstractReferenceCountedByteBuf {
} }
// Reallocation required. // Reallocation required.
if (suspendedDeallocations == null) { chunk.arena.reallocate(this, newCapacity, true);
chunk.arena.reallocate(this, newCapacity, true);
} else {
Allocation<T> old = new Allocation<T>(chunk, handle);
chunk.arena.reallocate(this, newCapacity, false);
suspendedDeallocations.add(old);
}
return this; return this;
} }
@ -143,38 +134,9 @@ abstract class PooledByteBuf<T> extends AbstractReferenceCountedByteBuf {
protected abstract ByteBuffer newInternalNioBuffer(T memory); protected abstract ByteBuffer newInternalNioBuffer(T memory);
@Override
public final ByteBuf suspendIntermediaryDeallocations() {
ensureAccessible();
if (suspendedDeallocations == null) {
suspendedDeallocations = new ArrayDeque<Allocation<T>>(2);
}
return this;
}
@Override
public final ByteBuf resumeIntermediaryDeallocations() {
if (suspendedDeallocations == null) {
return this;
}
Queue<Allocation<T>> suspendedDeallocations = this.suspendedDeallocations;
this.suspendedDeallocations = null;
if (suspendedDeallocations.isEmpty()) {
return this;
}
for (Allocation<T> a: suspendedDeallocations) {
a.chunk.arena.free(a.chunk, a.handle);
}
return this;
}
@Override @Override
protected final void deallocate() { protected final void deallocate() {
if (handle >= 0) { if (handle >= 0) {
resumeIntermediaryDeallocations();
final long handle = this.handle; final long handle = this.handle;
this.handle = -1; this.handle = -1;
memory = null; memory = null;
@ -186,14 +148,4 @@ abstract class PooledByteBuf<T> extends AbstractReferenceCountedByteBuf {
protected final int idx(int index) { protected final int idx(int index) {
return offset + index; return offset + index;
} }
private static final class Allocation<T> {
final PoolChunk<T> chunk;
final long handle;
Allocation(PoolChunk<T> chunk, long handle) {
this.chunk = chunk;
this.handle = handle;
}
}
} }

View File

@ -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<T> extends AbstractMessageBuf<T> {
private Queue<T> queue;
QueueBackedMessageBuf(Queue<T> 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<T> iterator() {
ensureAccessible();
return queue.iterator();
}
@Override
public Object[] toArray() {
ensureAccessible();
return queue.toArray();
}
@Override
public <E> 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<? extends T> 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;
}
}

View File

@ -316,14 +316,4 @@ class ReadOnlyByteBufferBuf extends AbstractReferenceCountedByteBuf {
public long memoryAddress() { public long memoryAddress() {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override
public ByteBuf suspendIntermediaryDeallocations() {
return this;
}
@Override
public ByteBuf resumeIntermediaryDeallocations() {
return this;
}
} }

View File

@ -67,11 +67,6 @@ public final class SwappedByteBuf implements ByteBuf {
return buf.alloc(); return buf.alloc();
} }
@Override
public BufType type() {
return BufType.MESSAGE;
}
@Override @Override
public int capacity() { public int capacity() {
return buf.capacity(); return buf.capacity();
@ -245,7 +240,7 @@ public final class SwappedByteBuf implements ByteBuf {
@Override @Override
public short getShort(int index) { public short getShort(int index) {
return BufUtil.swapShort(buf.getShort(index)); return ByteBufUtil.swapShort(buf.getShort(index));
} }
@Override @Override
@ -255,7 +250,7 @@ public final class SwappedByteBuf implements ByteBuf {
@Override @Override
public int getMedium(int index) { public int getMedium(int index) {
return BufUtil.swapMedium(buf.getMedium(index)); return ByteBufUtil.swapMedium(buf.getMedium(index));
} }
@Override @Override
@ -265,7 +260,7 @@ public final class SwappedByteBuf implements ByteBuf {
@Override @Override
public int getInt(int index) { public int getInt(int index) {
return BufUtil.swapInt(buf.getInt(index)); return ByteBufUtil.swapInt(buf.getInt(index));
} }
@Override @Override
@ -275,7 +270,7 @@ public final class SwappedByteBuf implements ByteBuf {
@Override @Override
public long getLong(int index) { public long getLong(int index) {
return BufUtil.swapLong(buf.getLong(index)); return ByteBufUtil.swapLong(buf.getLong(index));
} }
@Override @Override
@ -354,25 +349,25 @@ public final class SwappedByteBuf implements ByteBuf {
@Override @Override
public ByteBuf setShort(int index, int value) { public ByteBuf setShort(int index, int value) {
buf.setShort(index, BufUtil.swapShort((short) value)); buf.setShort(index, ByteBufUtil.swapShort((short) value));
return this; return this;
} }
@Override @Override
public ByteBuf setMedium(int index, int value) { public ByteBuf setMedium(int index, int value) {
buf.setMedium(index, BufUtil.swapMedium(value)); buf.setMedium(index, ByteBufUtil.swapMedium(value));
return this; return this;
} }
@Override @Override
public ByteBuf setInt(int index, int value) { public ByteBuf setInt(int index, int value) {
buf.setInt(index, BufUtil.swapInt(value)); buf.setInt(index, ByteBufUtil.swapInt(value));
return this; return this;
} }
@Override @Override
public ByteBuf setLong(int index, long value) { public ByteBuf setLong(int index, long value) {
buf.setLong(index, BufUtil.swapLong(value)); buf.setLong(index, ByteBufUtil.swapLong(value));
return this; return this;
} }
@ -463,7 +458,7 @@ public final class SwappedByteBuf implements ByteBuf {
@Override @Override
public short readShort() { public short readShort() {
return BufUtil.swapShort(buf.readShort()); return ByteBufUtil.swapShort(buf.readShort());
} }
@Override @Override
@ -473,7 +468,7 @@ public final class SwappedByteBuf implements ByteBuf {
@Override @Override
public int readMedium() { public int readMedium() {
return BufUtil.swapMedium(buf.readMedium()); return ByteBufUtil.swapMedium(buf.readMedium());
} }
@Override @Override
@ -483,7 +478,7 @@ public final class SwappedByteBuf implements ByteBuf {
@Override @Override
public int readInt() { public int readInt() {
return BufUtil.swapInt(buf.readInt()); return ByteBufUtil.swapInt(buf.readInt());
} }
@Override @Override
@ -493,7 +488,7 @@ public final class SwappedByteBuf implements ByteBuf {
@Override @Override
public long readLong() { public long readLong() {
return BufUtil.swapLong(buf.readLong()); return ByteBufUtil.swapLong(buf.readLong());
} }
@Override @Override
@ -588,25 +583,25 @@ public final class SwappedByteBuf implements ByteBuf {
@Override @Override
public ByteBuf writeShort(int value) { public ByteBuf writeShort(int value) {
buf.writeShort(BufUtil.swapShort((short) value)); buf.writeShort(ByteBufUtil.swapShort((short) value));
return this; return this;
} }
@Override @Override
public ByteBuf writeMedium(int value) { public ByteBuf writeMedium(int value) {
buf.writeMedium(BufUtil.swapMedium(value)); buf.writeMedium(ByteBufUtil.swapMedium(value));
return this; return this;
} }
@Override @Override
public ByteBuf writeInt(int value) { public ByteBuf writeInt(int value) {
buf.writeInt(BufUtil.swapInt(value)); buf.writeInt(ByteBufUtil.swapInt(value));
return this; return this;
} }
@Override @Override
public ByteBuf writeLong(long value) { public ByteBuf writeLong(long value) {
buf.writeLong(BufUtil.swapLong(value)); buf.writeLong(ByteBufUtil.swapLong(value));
return this; return this;
} }
@ -813,18 +808,6 @@ public final class SwappedByteBuf implements ByteBuf {
return buf.toString(index, length, charset); return buf.toString(index, length, charset);
} }
@Override
public ByteBuf suspendIntermediaryDeallocations() {
buf.suspendIntermediaryDeallocations();
return this;
}
@Override
public ByteBuf resumeIntermediaryDeallocations() {
buf.resumeIntermediaryDeallocations();
return this;
}
@Override @Override
public int refCnt() { public int refCnt() {
return buf.refCnt(); return buf.refCnt();
@ -863,14 +846,14 @@ public final class SwappedByteBuf implements ByteBuf {
return true; return true;
} }
if (obj instanceof ByteBuf) { if (obj instanceof ByteBuf) {
return BufUtil.equals(this, (ByteBuf) obj); return ByteBufUtil.equals(this, (ByteBuf) obj);
} }
return false; return false;
} }
@Override @Override
public int compareTo(ByteBuf buffer) { public int compareTo(ByteBuf buffer) {
return BufUtil.compare(this, buffer); return ByteBufUtil.compare(this, buffer);
} }
@Override @Override

View File

@ -23,11 +23,10 @@ import java.nio.CharBuffer;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; 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. * or copying existing byte arrays, byte buffers and a string.
* *
* <h3>Use static import</h3> * <h3>Use static import</h3>
@ -94,39 +93,6 @@ public final class Unpooled {
*/ */
public static final ByteBuf EMPTY_BUFFER = ALLOC.buffer(0, 0); 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 <T> MessageBuf<T> messageBuffer() {
return new DefaultMessageBuf<T>();
}
/**
* Creates a new {@link MessageBuf} with the specified {@code initialCapacity}.
*/
public static <T> MessageBuf<T> messageBuffer(int initialCapacity) {
return new DefaultMessageBuf<T>(initialCapacity);
}
/**
* Creates a new {@link MessageBuf} with the specified {@code initialCapacity} and
* {@code maxCapacity}.
*/
public static <T> MessageBuf<T> messageBuffer(int initialCapacity, int maxCapacity) {
return new DefaultMessageBuf<T>(initialCapacity, maxCapacity);
}
/**
* Creates a new {@link MessageBuf} which wraps the given {@code queue}.
*/
public static <T> MessageBuf<T> wrappedBuffer(Queue<T> queue) {
if (queue instanceof MessageBuf) {
return (MessageBuf<T>) queue;
}
return new QueueBackedMessageBuf<T>(queue);
}
/** /**
* Creates a new big-endian Java heap buffer with reasonably small initial capacity, which * Creates a new big-endian Java heap buffer with reasonably small initial capacity, which
* expands its capacity boundlessly on demand. * expands its capacity boundlessly on demand.
@ -681,7 +647,7 @@ public final class Unpooled {
} }
private static ByteBuf copiedBuffer(CharBuffer buffer, Charset charset) { 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()); ByteBuf result = wrappedBuffer(dst.array());
result.writerIndex(dst.remaining()); result.writerIndex(dst.remaining());
return result; return result;

View File

@ -26,8 +26,6 @@ import java.nio.ByteOrder;
import java.nio.channels.ClosedChannelException; import java.nio.channels.ClosedChannelException;
import java.nio.channels.GatheringByteChannel; import java.nio.channels.GatheringByteChannel;
import java.nio.channels.ScatteringByteChannel; 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)} * 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 ByteBuffer tmpNioBuf;
private int capacity; private int capacity;
private boolean doNotFree; private boolean doNotFree;
private Queue<ByteBuffer> suspendedDeallocations;
/** /**
* Creates a new direct buffer. * Creates a new direct buffer.
@ -111,11 +108,7 @@ public class UnpooledDirectByteBuf extends AbstractReferenceCountedByteBuf {
if (doNotFree) { if (doNotFree) {
doNotFree = false; doNotFree = false;
} else { } else {
if (suspendedDeallocations == null) { PlatformDependent.freeDirectBuffer(oldBuffer);
PlatformDependent.freeDirectBuffer(oldBuffer);
} else {
suspendedDeallocations.add(oldBuffer);
}
} }
} }
@ -527,37 +520,12 @@ public class UnpooledDirectByteBuf extends AbstractReferenceCountedByteBuf {
this.buffer = null; this.buffer = null;
resumeIntermediaryDeallocations();
if (!doNotFree) { if (!doNotFree) {
PlatformDependent.freeDirectBuffer(buffer); PlatformDependent.freeDirectBuffer(buffer);
} }
leak.close(); leak.close();
} }
@Override
public ByteBuf suspendIntermediaryDeallocations() {
ensureAccessible();
if (suspendedDeallocations == null) {
suspendedDeallocations = new ArrayDeque<ByteBuffer>(2);
}
return this;
}
@Override
public ByteBuf resumeIntermediaryDeallocations() {
if (suspendedDeallocations == null) {
return this;
}
Queue<ByteBuffer> suspendedDeallocations = this.suspendedDeallocations;
this.suspendedDeallocations = null;
for (ByteBuffer buf: suspendedDeallocations) {
PlatformDependent.freeDirectBuffer(buf);
}
return this;
}
@Override @Override
public ByteBuf unwrap() { public ByteBuf unwrap() {
return null; return null;

View File

@ -417,16 +417,6 @@ public class UnpooledHeapByteBuf extends AbstractReferenceCountedByteBuf {
array = null; array = null;
} }
@Override
public ByteBuf suspendIntermediaryDeallocations() {
return this;
}
@Override
public ByteBuf resumeIntermediaryDeallocations() {
return this;
}
@Override @Override
public ByteBuf unwrap() { public ByteBuf unwrap() {
return null; return null;

View File

@ -26,8 +26,6 @@ import java.nio.ByteOrder;
import java.nio.channels.ClosedChannelException; import java.nio.channels.ClosedChannelException;
import java.nio.channels.GatheringByteChannel; import java.nio.channels.GatheringByteChannel;
import java.nio.channels.ScatteringByteChannel; 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)} * 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 ByteBuffer tmpNioBuf;
private int capacity; private int capacity;
private boolean doNotFree; private boolean doNotFree;
private Queue<ByteBuffer> suspendedDeallocations;
/** /**
* Creates a new direct buffer. * Creates a new direct buffer.
@ -114,11 +111,7 @@ public class UnpooledUnsafeDirectByteBuf extends AbstractReferenceCountedByteBuf
if (doNotFree) { if (doNotFree) {
doNotFree = false; doNotFree = false;
} else { } else {
if (suspendedDeallocations == null) { PlatformDependent.freeDirectBuffer(oldBuffer);
PlatformDependent.freeDirectBuffer(oldBuffer);
} else {
suspendedDeallocations.add(oldBuffer);
}
} }
} }
@ -459,37 +452,12 @@ public class UnpooledUnsafeDirectByteBuf extends AbstractReferenceCountedByteBuf
this.buffer = null; this.buffer = null;
resumeIntermediaryDeallocations();
if (!doNotFree) { if (!doNotFree) {
PlatformDependent.freeDirectBuffer(buffer); PlatformDependent.freeDirectBuffer(buffer);
} }
leak.close(); leak.close();
} }
@Override
public ByteBuf suspendIntermediaryDeallocations() {
ensureAccessible();
if (suspendedDeallocations == null) {
suspendedDeallocations = new ArrayDeque<ByteBuffer>(2);
}
return this;
}
@Override
public ByteBuf resumeIntermediaryDeallocations() {
if (suspendedDeallocations == null) {
return this;
}
Queue<ByteBuffer> suspendedDeallocations = this.suspendedDeallocations;
this.suspendedDeallocations = null;
for (ByteBuffer buf: suspendedDeallocations) {
PlatformDependent.freeDirectBuffer(buf);
}
return this;
}
@Override @Override
public ByteBuf unwrap() { public ByteBuf unwrap() {
return null; return null;

View File

@ -796,18 +796,6 @@ final class UnreleasableByteBuf implements ByteBuf {
return buf.toString(index, length, charset); return buf.toString(index, length, charset);
} }
@Override
public ByteBuf suspendIntermediaryDeallocations() {
buf.suspendIntermediaryDeallocations();
return this;
}
@Override
public ByteBuf resumeIntermediaryDeallocations() {
buf.resumeIntermediaryDeallocations();
return this;
}
@Override @Override
public int hashCode() { public int hashCode() {
return buf.hashCode(); return buf.hashCode();
@ -838,11 +826,6 @@ final class UnreleasableByteBuf implements ByteBuf {
return this; return this;
} }
@Override
public BufType type() {
return buf.type();
}
@Override @Override
public boolean isReadable(int size) { public boolean isReadable(int size) {
return buf.isReadable(size); return buf.isReadable(size);

View File

@ -142,20 +142,20 @@ public abstract class AbstractCompositeByteBufTest extends AbstractByteBufTest {
a.writerIndex(a.writerIndex() + 1); a.writerIndex(a.writerIndex() + 1);
b.writerIndex(b.writerIndex() + 1); b.writerIndex(b.writerIndex() + 1);
assertEquals(a.writerIndex(), b.writerIndex()); assertEquals(a.writerIndex(), b.writerIndex());
assertTrue(BufUtil.equals(a, b)); assertTrue(ByteBufUtil.equals(a, b));
// now discard // now discard
a.discardReadBytes(); a.discardReadBytes();
b.discardReadBytes(); b.discardReadBytes();
assertEquals(a.readerIndex(), b.readerIndex()); assertEquals(a.readerIndex(), b.readerIndex());
assertEquals(a.writerIndex(), b.writerIndex()); assertEquals(a.writerIndex(), b.writerIndex());
assertTrue(BufUtil.equals(a, b)); assertTrue(ByteBufUtil.equals(a, b));
a.resetReaderIndex(); a.resetReaderIndex();
b.resetReaderIndex(); b.resetReaderIndex();
assertEquals(a.readerIndex(), b.readerIndex()); assertEquals(a.readerIndex(), b.readerIndex());
a.resetWriterIndex(); a.resetWriterIndex();
b.resetWriterIndex(); b.resetWriterIndex();
assertEquals(a.writerIndex(), b.writerIndex()); assertEquals(a.writerIndex(), b.writerIndex());
assertTrue(BufUtil.equals(a, b)); assertTrue(ByteBufUtil.equals(a, b));
} }
@Test @Test
@ -231,7 +231,7 @@ public abstract class AbstractCompositeByteBufTest extends AbstractByteBufTest {
b = freeLater(wrappedBuffer( b = freeLater(wrappedBuffer(
wrappedBuffer(new byte[] { 1 }).order(order), wrappedBuffer(new byte[] { 1 }).order(order),
wrappedBuffer(new byte[] { 2 }).order(order))); wrappedBuffer(new byte[] { 2 }).order(order)));
assertFalse(BufUtil.equals(a, b)); assertFalse(ByteBufUtil.equals(a, b));
// Same content, same firstIndex, short length. // Same content, same firstIndex, short length.
a = freeLater(wrappedBuffer(new byte[] { 1, 2, 3 }).order(order)); 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[]{1}).order(order),
wrappedBuffer(new byte[]{2}).order(order), wrappedBuffer(new byte[]{2}).order(order),
wrappedBuffer(new byte[]{3}).order(order))); wrappedBuffer(new byte[]{3}).order(order)));
assertTrue(BufUtil.equals(a, b)); assertTrue(ByteBufUtil.equals(a, b));
// Same content, different firstIndex, short length. // Same content, different firstIndex, short length.
a = freeLater(wrappedBuffer(new byte[] { 1, 2, 3 }).order(order)); a = freeLater(wrappedBuffer(new byte[] { 1, 2, 3 }).order(order));
b = freeLater(wrappedBuffer( b = freeLater(wrappedBuffer(
wrappedBuffer(new byte[] { 0, 1, 2, 3, 4 }, 1, 2).order(order), wrappedBuffer(new byte[] { 0, 1, 2, 3, 4 }, 1, 2).order(order),
wrappedBuffer(new byte[] { 0, 1, 2, 3, 4 }, 3, 1).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. // Different content, same firstIndex, short length.
a = freeLater(wrappedBuffer(new byte[] { 1, 2, 3 }).order(order)); a = freeLater(wrappedBuffer(new byte[] { 1, 2, 3 }).order(order));
b = freeLater(wrappedBuffer( b = freeLater(wrappedBuffer(
wrappedBuffer(new byte[] { 1, 2 }).order(order), wrappedBuffer(new byte[] { 1, 2 }).order(order),
wrappedBuffer(new byte[] { 4 }).order(order))); wrappedBuffer(new byte[] { 4 }).order(order)));
assertFalse(BufUtil.equals(a, b)); assertFalse(ByteBufUtil.equals(a, b));
// Different content, different firstIndex, short length. // Different content, different firstIndex, short length.
a = freeLater(wrappedBuffer(new byte[] { 1, 2, 3 }).order(order)); a = freeLater(wrappedBuffer(new byte[] { 1, 2, 3 }).order(order));
b = freeLater(wrappedBuffer( b = freeLater(wrappedBuffer(
wrappedBuffer(new byte[] { 0, 1, 2, 4, 5 }, 1, 2).order(order), wrappedBuffer(new byte[] { 0, 1, 2, 4, 5 }, 1, 2).order(order),
wrappedBuffer(new byte[] { 0, 1, 2, 4, 5 }, 3, 1).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. // Same content, same firstIndex, long length.
a = freeLater(wrappedBuffer(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }).order(order)); 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[] { 1, 2, 3 }).order(order),
wrappedBuffer(new byte[] { 4, 5, 6 }).order(order), wrappedBuffer(new byte[] { 4, 5, 6 }).order(order),
wrappedBuffer(new byte[] { 7, 8, 9, 10 }).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. // Same content, different firstIndex, long length.
a = freeLater(wrappedBuffer(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }).order(order)); a = freeLater(wrappedBuffer(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }).order(order));
b = freeLater(wrappedBuffer( 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}, 1, 5).order(order),
wrappedBuffer(new byte[] { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, 6, 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. // Different content, same firstIndex, long length.
a = freeLater(wrappedBuffer(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }).order(order)); a = freeLater(wrappedBuffer(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }).order(order));
b = freeLater(wrappedBuffer( b = freeLater(wrappedBuffer(
wrappedBuffer(new byte[] { 1, 2, 3, 4, 6 }).order(order), wrappedBuffer(new byte[] { 1, 2, 3, 4, 6 }).order(order),
wrappedBuffer(new byte[] { 7, 8, 5, 9, 10 }).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. // Different content, different firstIndex, long length.
a = freeLater(wrappedBuffer(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }).order(order)); a = freeLater(wrappedBuffer(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }).order(order));
b = freeLater(wrappedBuffer( 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 }, 1, 5).order(order),
wrappedBuffer(new byte[] { 0, 1, 2, 3, 4, 6, 7, 8, 5, 9, 10, 11 }, 6, 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 @Test
@ -345,7 +345,7 @@ public abstract class AbstractCompositeByteBufTest extends AbstractByteBufTest {
// to enable writeBytes // to enable writeBytes
b.writerIndex(b.writerIndex() - 1); b.writerIndex(b.writerIndex() - 1);
b.writeBytes(wrappedBuffer(new byte[] { 2 }).order(order)); b.writeBytes(wrappedBuffer(new byte[] { 2 }).order(order));
assertFalse(BufUtil.equals(a, b)); assertFalse(ByteBufUtil.equals(a, b));
// Same content, same firstIndex, short length. // Same content, same firstIndex, short length.
a = wrappedBuffer(new byte[] { 1, 2, 3 }).order(order); 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.writerIndex(b.writerIndex() - 2);
b.writeBytes(wrappedBuffer(new byte[] { 2 }).order(order)); b.writeBytes(wrappedBuffer(new byte[] { 2 }).order(order));
b.writeBytes(wrappedBuffer(new byte[] { 3 }).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. // Same content, different firstIndex, short length.
a = wrappedBuffer(new byte[] { 1, 2, 3 }).order(order); a = wrappedBuffer(new byte[] { 1, 2, 3 }).order(order);
@ -362,7 +362,7 @@ public abstract class AbstractCompositeByteBufTest extends AbstractByteBufTest {
// to enable writeBytes // to enable writeBytes
b.writerIndex(b.writerIndex() - 1); b.writerIndex(b.writerIndex() - 1);
b.writeBytes(wrappedBuffer(new byte[] { 0, 1, 2, 3, 4 }, 3, 1).order(order)); 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. // Different content, same firstIndex, short length.
a = wrappedBuffer(new byte[] { 1, 2, 3 }).order(order); a = wrappedBuffer(new byte[] { 1, 2, 3 }).order(order);
@ -370,7 +370,7 @@ public abstract class AbstractCompositeByteBufTest extends AbstractByteBufTest {
// to enable writeBytes // to enable writeBytes
b.writerIndex(b.writerIndex() - 1); b.writerIndex(b.writerIndex() - 1);
b.writeBytes(wrappedBuffer(new byte[] { 4 }).order(order)); b.writeBytes(wrappedBuffer(new byte[] { 4 }).order(order));
assertFalse(BufUtil.equals(a, b)); assertFalse(ByteBufUtil.equals(a, b));
// Different content, different firstIndex, short length. // Different content, different firstIndex, short length.
a = wrappedBuffer(new byte[] { 1, 2, 3 }).order(order); a = wrappedBuffer(new byte[] { 1, 2, 3 }).order(order);
@ -378,7 +378,7 @@ public abstract class AbstractCompositeByteBufTest extends AbstractByteBufTest {
// to enable writeBytes // to enable writeBytes
b.writerIndex(b.writerIndex() - 1); b.writerIndex(b.writerIndex() - 1);
b.writeBytes(wrappedBuffer(new byte[] { 0, 1, 2, 4, 5 }, 3, 1).order(order)); 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. // Same content, same firstIndex, long length.
a = wrappedBuffer(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }).order(order); 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.writerIndex(b.writerIndex() - 7);
b.writeBytes(wrappedBuffer(new byte[] { 4, 5, 6 }).order(order)); b.writeBytes(wrappedBuffer(new byte[] { 4, 5, 6 }).order(order));
b.writeBytes(wrappedBuffer(new byte[] { 7, 8, 9, 10 }).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. // Same content, different firstIndex, long length.
a = wrappedBuffer(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }).order(order); 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 // to enable writeBytes
b.writerIndex(b.writerIndex() - 5); 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)); 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. // Different content, same firstIndex, long length.
a = wrappedBuffer(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }).order(order); 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 // to enable writeBytes
b.writerIndex(b.writerIndex() - 5); b.writerIndex(b.writerIndex() - 5);
b.writeBytes(wrappedBuffer(new byte[] { 7, 8, 5, 9, 10 }).order(order)); 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. // Different content, different firstIndex, long length.
a = wrappedBuffer(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }).order(order); 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 // to enable writeBytes
b.writerIndex(b.writerIndex() - 5); 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)); 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 @Test

View File

@ -97,7 +97,7 @@ public class UnpooledTest {
for (Entry<byte[], Integer> e: map.entrySet()) { for (Entry<byte[], Integer> e: map.entrySet()) {
assertEquals( assertEquals(
e.getValue().intValue(), e.getValue().intValue(),
BufUtil.hashCode(wrappedBuffer(e.getKey()))); ByteBufUtil.hashCode(wrappedBuffer(e.getKey())));
} }
} }
@ -108,47 +108,47 @@ public class UnpooledTest {
// Different length. // Different length.
a = wrappedBuffer(new byte[] { 1 }); a = wrappedBuffer(new byte[] { 1 });
b = wrappedBuffer(new byte[] { 1, 2 }); b = wrappedBuffer(new byte[] { 1, 2 });
assertFalse(BufUtil.equals(a, b)); assertFalse(ByteBufUtil.equals(a, b));
// Same content, same firstIndex, short length. // Same content, same firstIndex, short length.
a = wrappedBuffer(new byte[] { 1, 2, 3 }); a = wrappedBuffer(new byte[] { 1, 2, 3 });
b = 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. // Same content, different firstIndex, short length.
a = wrappedBuffer(new byte[] { 1, 2, 3 }); a = wrappedBuffer(new byte[] { 1, 2, 3 });
b = wrappedBuffer(new byte[] { 0, 1, 2, 3, 4 }, 1, 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. // Different content, same firstIndex, short length.
a = wrappedBuffer(new byte[] { 1, 2, 3 }); a = wrappedBuffer(new byte[] { 1, 2, 3 });
b = wrappedBuffer(new byte[] { 1, 2, 4 }); b = wrappedBuffer(new byte[] { 1, 2, 4 });
assertFalse(BufUtil.equals(a, b)); assertFalse(ByteBufUtil.equals(a, b));
// Different content, different firstIndex, short length. // Different content, different firstIndex, short length.
a = wrappedBuffer(new byte[] { 1, 2, 3 }); a = wrappedBuffer(new byte[] { 1, 2, 3 });
b = wrappedBuffer(new byte[] { 0, 1, 2, 4, 5 }, 1, 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. // Same content, same firstIndex, long length.
a = wrappedBuffer(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); 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 }); 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. // Same content, different firstIndex, long length.
a = wrappedBuffer(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); 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); 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. // Different content, same firstIndex, long length.
a = wrappedBuffer(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); 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 }); 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. // Different content, different firstIndex, long length.
a = wrappedBuffer(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); 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); 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 @Test
@ -174,11 +174,11 @@ public class UnpooledTest {
for (int i = 0; i < expected.size(); i ++) { for (int i = 0; i < expected.size(); i ++) {
for (int j = 0; j < expected.size(); j ++) { for (int j = 0; j < expected.size(); j ++) {
if (i == 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) { } 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 { } 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 @Test
public void testCompare2() { public void testCompare2() {
assertTrue(BufUtil.compare( assertTrue(ByteBufUtil.compare(
wrappedBuffer(new byte[]{(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF}), wrappedBuffer(new byte[]{(byte) 0xFF, (byte) 0xFF, (byte) 0xFF, (byte) 0xFF}),
wrappedBuffer(new byte[]{(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00})) wrappedBuffer(new byte[]{(byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00}))
> 0); > 0);
assertTrue(BufUtil.compare( assertTrue(ByteBufUtil.compare(
wrappedBuffer(new byte[]{(byte) 0xFF}), wrappedBuffer(new byte[]{(byte) 0xFF}),
wrappedBuffer(new byte[]{(byte) 0x00})) wrappedBuffer(new byte[]{(byte) 0x00}))
> 0); > 0);
@ -327,14 +327,14 @@ public class UnpooledTest {
@Test @Test
public void testHexDump() { 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[]{ new byte[]{
0x12, 0x34, 0x56 0x12, 0x34, 0x56
}))); })));
assertEquals("1234567890abcdef", BufUtil.hexDump(wrappedBuffer( assertEquals("1234567890abcdef", ByteBufUtil.hexDump(wrappedBuffer(
new byte[]{ new byte[]{
0x12, 0x34, 0x56, 0x78, 0x12, 0x34, 0x56, 0x78,
(byte) 0x90, (byte) 0xAB, (byte) 0xCD, (byte) 0xEF (byte) 0x90, (byte) 0xAB, (byte) 0xCD, (byte) 0xEF
@ -343,8 +343,8 @@ public class UnpooledTest {
@Test @Test
public void testSwapMedium() { public void testSwapMedium() {
assertEquals(0x563412, BufUtil.swapMedium(0x123456)); assertEquals(0x563412, ByteBufUtil.swapMedium(0x123456));
assertEquals(0x80, BufUtil.swapMedium(0x800000)); assertEquals(0x80, ByteBufUtil.swapMedium(0x800000));
} }
@Test @Test

View File

@ -20,7 +20,7 @@
<parent> <parent>
<groupId>io.netty</groupId> <groupId>io.netty</groupId>
<artifactId>netty-parent</artifactId> <artifactId>netty-parent</artifactId>
<version>4.0.0.Final-SNAPSHOT</version> <version>4.0.0.CR4-SNAPSHOT</version>
</parent> </parent>
<artifactId>netty-codec-http</artifactId> <artifactId>netty-codec-http</artifactId>

View File

@ -16,13 +16,10 @@
package io.netty.handler.codec.http; package io.netty.handler.codec.http;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.FilteredMessageBuf;
import io.netty.buffer.MessageBuf;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundByteHandler;
import io.netty.channel.ChannelOutboundMessageHandler;
import io.netty.channel.CombinedChannelDuplexHandler; import io.netty.channel.CombinedChannelDuplexHandler;
import io.netty.channel.MessageList;
import io.netty.handler.codec.PrematureChannelClosureException; import io.netty.handler.codec.PrematureChannelClosureException;
import java.util.ArrayDeque; import java.util.ArrayDeque;
@ -44,8 +41,7 @@ import java.util.concurrent.atomic.AtomicLong;
* @see HttpServerCodec * @see HttpServerCodec
*/ */
public final class HttpClientCodec public final class HttpClientCodec
extends CombinedChannelDuplexHandler extends CombinedChannelDuplexHandler<HttpResponseDecoder, HttpRequestEncoder> {
implements ChannelInboundByteHandler, ChannelOutboundMessageHandler<HttpObject> {
/** A queue that is used for correlating a request and a response. */ /** A queue that is used for correlating a request and a response. */
private final Queue<HttpMethod> queue = new ArrayDeque<HttpMethod>(); private final Queue<HttpMethod> queue = new ArrayDeque<HttpMethod>();
@ -66,11 +62,11 @@ public final class HttpClientCodec
} }
public void setSingleDecode(boolean singleDecode) { public void setSingleDecode(boolean singleDecode) {
decoder().setSingleDecode(singleDecode); inboundHandler().setSingleDecode(singleDecode);
} }
public boolean isSingleDecode() { public boolean isSingleDecode() {
return decoder().isSingleDecode(); return inboundHandler().isSingleDecode();
} }
/** /**
@ -86,29 +82,6 @@ public final class HttpClientCodec
this.failOnMissingResponse = failOnMissingResponse; 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<HttpObject> newOutboundBuffer(ChannelHandlerContext ctx) throws Exception {
return encoder().newOutboundBuffer(ctx);
}
private final class Encoder extends HttpRequestEncoder { private final class Encoder extends HttpRequestEncoder {
@Override @Override
@ -137,7 +110,7 @@ public final class HttpClientCodec
@Override @Override
protected void decode( protected void decode(
ChannelHandlerContext ctx, ByteBuf buffer, MessageBuf<Object> out) throws Exception { ChannelHandlerContext ctx, ByteBuf buffer, MessageList<Object> out) throws Exception {
if (done) { if (done) {
int readable = actualReadableBytes(); int readable = actualReadableBytes();
if (readable == 0) { if (readable == 0) {
@ -147,16 +120,14 @@ public final class HttpClientCodec
} }
out.add(buffer.readBytes(readable)); out.add(buffer.readBytes(readable));
} else { } else {
if (failOnMissingResponse) { int oldSize = out.size();
out = new FilteredMessageBuf(out) {
@Override
protected Object filter(Object msg) {
decrement(msg);
return msg;
}
};
}
super.decode(ctx, buffer, out); super.decode(ctx, buffer, out);
if (failOnMissingResponse) {
int size = out.size();
for (int i = oldSize; i < size; i++) {
decrement(out.get(i));
}
}
} }
} }

View File

@ -15,7 +15,7 @@
*/ */
package io.netty.handler.codec.http; 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.ZlibCodecFactory;
import io.netty.handler.codec.compression.ZlibWrapper; import io.netty.handler.codec.compression.ZlibWrapper;
import io.netty.util.internal.StringUtil; import io.netty.util.internal.StringUtil;
@ -119,7 +119,7 @@ public class HttpContentCompressor extends HttpContentEncoder {
return new Result( return new Result(
targetContentEncoding, targetContentEncoding,
new EmbeddedByteChannel(ZlibCodecFactory.newZlibEncoder( new EmbeddedChannel(ZlibCodecFactory.newZlibEncoder(
wrapper, compressionLevel, windowBits, memLevel))); wrapper, compressionLevel, windowBits, memLevel)));
} }

View File

@ -15,21 +15,19 @@
*/ */
package io.netty.handler.codec.http; package io.netty.handler.codec.http;
import io.netty.buffer.BufUtil;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufHolder; import io.netty.buffer.ByteBufHolder;
import io.netty.buffer.MessageBuf; import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext; 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 io.netty.handler.codec.MessageToMessageDecoder;
import java.util.Collections;
/** /**
* Decodes the content of the received {@link HttpRequest} and {@link HttpContent}. * Decodes the content of the received {@link HttpRequest} and {@link HttpContent}.
* The original content is replaced with the new content decoded by the * 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 <tt>'Content-Encoding'</tt> * Once decoding is finished, the value of the <tt>'Content-Encoding'</tt>
* header is set to the target content encoding, as returned by {@link #getTargetContentEncoding(String)}. * header is set to the target content encoding, as returned by {@link #getTargetContentEncoding(String)}.
* Also, the <tt>'Content-Length'</tt> header is updated to the length of the * Also, the <tt>'Content-Length'</tt> header is updated to the length of the
@ -47,20 +45,20 @@ import java.util.Collections;
*/ */
public abstract class HttpContentDecoder extends MessageToMessageDecoder<HttpObject> { public abstract class HttpContentDecoder extends MessageToMessageDecoder<HttpObject> {
private EmbeddedByteChannel decoder; private EmbeddedChannel decoder;
private HttpMessage message; private HttpMessage message;
private boolean decodeStarted; private boolean decodeStarted;
private boolean continueResponse; private boolean continueResponse;
@Override @Override
protected void decode(ChannelHandlerContext ctx, HttpObject msg, MessageBuf<Object> out) throws Exception { protected void decode(ChannelHandlerContext ctx, HttpObject msg, MessageList<Object> out) throws Exception {
if (msg instanceof HttpResponse && ((HttpResponse) msg).getStatus().code() == 100) { if (msg instanceof HttpResponse && ((HttpResponse) msg).getStatus().code() == 100) {
if (!(msg instanceof LastHttpContent)) { if (!(msg instanceof LastHttpContent)) {
continueResponse = true; continueResponse = true;
} }
// 100-continue response must be passed through. // 100-continue response must be passed through.
out.add(BufUtil.retain(msg)); out.add(ByteBufUtil.retain(msg));
return; return;
} }
@ -69,7 +67,7 @@ public abstract class HttpContentDecoder extends MessageToMessageDecoder<HttpObj
continueResponse = false; continueResponse = false;
} }
// 100-continue response must be passed through. // 100-continue response must be passed through.
out.add(BufUtil.retain(msg)); out.add(ByteBufUtil.retain(msg));
return; return;
} }
@ -117,8 +115,7 @@ public abstract class HttpContentDecoder extends MessageToMessageDecoder<HttpObj
HttpHeaders.Names.CONTENT_LENGTH, HttpHeaders.Names.CONTENT_LENGTH,
Integer.toString(((ByteBufHolder) decoded[1]).content().readableBytes())); Integer.toString(((ByteBufHolder) decoded[1]).content().readableBytes()));
} }
out.add(decoded);
Collections.addAll(out, decoded);
return; return;
} }
@ -131,7 +128,7 @@ public abstract class HttpContentDecoder extends MessageToMessageDecoder<HttpObj
} }
if (decoder != null) { if (decoder != null) {
Collections.addAll(out, decodeContent(null, c)); out.add(decodeContent(null, c));
} else { } else {
if (c instanceof LastHttpContent) { if (c instanceof LastHttpContent) {
decodeStarted = false; decodeStarted = false;
@ -175,15 +172,15 @@ public abstract class HttpContentDecoder extends MessageToMessageDecoder<HttpObj
} }
/** /**
* Returns a new {@link EmbeddedByteChannel} that decodes the HTTP message * Returns a new {@link EmbeddedChannel} that decodes the HTTP message
* content encoded in the specified <tt>contentEncoding</tt>. * content encoded in the specified <tt>contentEncoding</tt>.
* *
* @param contentEncoding the value of the {@code "Content-Encoding"} header * @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 * {@code null} otherwise (alternatively, you can throw an exception
* to block unknown encoding). * 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. * Returns the expected content encoding of the decoded content.

View File

@ -15,7 +15,7 @@
*/ */
package io.netty.handler.codec.http; 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.ZlibCodecFactory;
import io.netty.handler.codec.compression.ZlibWrapper; import io.netty.handler.codec.compression.ZlibWrapper;
@ -26,13 +26,13 @@ import io.netty.handler.codec.compression.ZlibWrapper;
*/ */
public class HttpContentDecompressor extends HttpContentDecoder { public class HttpContentDecompressor extends HttpContentDecoder {
@Override @Override
protected EmbeddedByteChannel newContentDecoder(String contentEncoding) throws Exception { protected EmbeddedChannel newContentDecoder(String contentEncoding) throws Exception {
if ("gzip".equalsIgnoreCase(contentEncoding) || "x-gzip".equalsIgnoreCase(contentEncoding)) { 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)) { if ("deflate".equalsIgnoreCase(contentEncoding) || "x-deflate".equalsIgnoreCase(contentEncoding)) {
// To be strict, 'deflate' means ZLIB, but some servers were not implemented correctly. // 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 // 'identity' or unsupported

View File

@ -15,25 +15,24 @@
*/ */
package io.netty.handler.codec.http; package io.netty.handler.codec.http;
import io.netty.buffer.BufUtil;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufHolder; import io.netty.buffer.ByteBufHolder;
import io.netty.buffer.MessageBuf; import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext; 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.MessageToMessageCodec;
import io.netty.handler.codec.http.HttpHeaders.Names; import io.netty.handler.codec.http.HttpHeaders.Names;
import io.netty.handler.codec.http.HttpHeaders.Values; import io.netty.handler.codec.http.HttpHeaders.Values;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.Collections;
import java.util.Queue; import java.util.Queue;
/** /**
* Encodes the content of the outbound {@link HttpResponse} and {@link HttpContent}. * Encodes the content of the outbound {@link HttpResponse} and {@link HttpContent}.
* The original content is replaced with the new content encoded by the * 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 <tt>'Content-Encoding'</tt> header * Once encoding is finished, the value of the <tt>'Content-Encoding'</tt> header
* is set to the target content encoding, as returned by * is set to the target content encoding, as returned by
* {@link #beginEncode(HttpResponse, String)}. * {@link #beginEncode(HttpResponse, String)}.
@ -62,7 +61,7 @@ public abstract class HttpContentEncoder extends MessageToMessageCodec<HttpReque
private final Queue<String> acceptEncodingQueue = new ArrayDeque<String>(); private final Queue<String> acceptEncodingQueue = new ArrayDeque<String>();
private String acceptEncoding; private String acceptEncoding;
private EmbeddedByteChannel encoder; private EmbeddedChannel encoder;
private State state = State.AWAIT_HEADERS; private State state = State.AWAIT_HEADERS;
@Override @Override
@ -71,18 +70,18 @@ public abstract class HttpContentEncoder extends MessageToMessageCodec<HttpReque
} }
@Override @Override
protected void decode(ChannelHandlerContext ctx, HttpRequest msg, MessageBuf<Object> out) protected void decode(ChannelHandlerContext ctx, HttpRequest msg, MessageList<Object> out)
throws Exception { throws Exception {
String acceptedEncoding = msg.headers().get(HttpHeaders.Names.ACCEPT_ENCODING); String acceptedEncoding = msg.headers().get(HttpHeaders.Names.ACCEPT_ENCODING);
if (acceptedEncoding == null) { if (acceptedEncoding == null) {
acceptedEncoding = HttpHeaders.Values.IDENTITY; acceptedEncoding = HttpHeaders.Values.IDENTITY;
} }
acceptEncodingQueue.add(acceptedEncoding); acceptEncodingQueue.add(acceptedEncoding);
out.add(BufUtil.retain(msg)); out.add(ByteBufUtil.retain(msg));
} }
@Override @Override
protected void encode(ChannelHandlerContext ctx, HttpObject msg, MessageBuf<Object> out) throws Exception { protected void encode(ChannelHandlerContext ctx, HttpObject msg, MessageList<Object> out) throws Exception {
final boolean isFull = msg instanceof HttpResponse && msg instanceof LastHttpContent; final boolean isFull = msg instanceof HttpResponse && msg instanceof LastHttpContent;
switch (state) { switch (state) {
case AWAIT_HEADERS: { case AWAIT_HEADERS: {
@ -93,7 +92,7 @@ public abstract class HttpContentEncoder extends MessageToMessageCodec<HttpReque
if (res.getStatus().code() == 100) { if (res.getStatus().code() == 100) {
if (isFull) { if (isFull) {
out.add(BufUtil.retain(res)); out.add(ByteBufUtil.retain(res));
} else { } else {
out.add(res); out.add(res);
// Pass through all following contents. // Pass through all following contents.
@ -114,7 +113,7 @@ public abstract class HttpContentEncoder extends MessageToMessageCodec<HttpReque
// Set the content length to 0. // Set the content length to 0.
res.headers().remove(Names.TRANSFER_ENCODING); res.headers().remove(Names.TRANSFER_ENCODING);
res.headers().set(Names.CONTENT_LENGTH, "0"); res.headers().set(Names.CONTENT_LENGTH, "0");
out.add(BufUtil.retain(res)); out.add(ByteBufUtil.retain(res));
break; break;
} }
} }
@ -128,7 +127,7 @@ public abstract class HttpContentEncoder extends MessageToMessageCodec<HttpReque
// As an unchunked response // As an unchunked response
res.headers().remove(Names.TRANSFER_ENCODING); res.headers().remove(Names.TRANSFER_ENCODING);
res.headers().set(Names.CONTENT_LENGTH, ((ByteBufHolder) res).content().readableBytes()); res.headers().set(Names.CONTENT_LENGTH, ((ByteBufHolder) res).content().readableBytes());
out.add(BufUtil.retain(res)); out.add(ByteBufUtil.retain(res));
} else { } else {
// As a chunked response // As a chunked response
res.headers().remove(Names.CONTENT_LENGTH); res.headers().remove(Names.CONTENT_LENGTH);
@ -165,7 +164,7 @@ public abstract class HttpContentEncoder extends MessageToMessageCodec<HttpReque
case AWAIT_CONTENT: { case AWAIT_CONTENT: {
ensureContent(msg); ensureContent(msg);
HttpContent[] encoded = encodeContent((HttpContent) msg); HttpContent[] encoded = encodeContent((HttpContent) msg);
Collections.addAll(out, encoded); out.add(encoded);
if (encoded[encoded.length - 1] instanceof LastHttpContent) { if (encoded[encoded.length - 1] instanceof LastHttpContent) {
state = State.AWAIT_HEADERS; state = State.AWAIT_HEADERS;
} }
@ -173,7 +172,7 @@ public abstract class HttpContentEncoder extends MessageToMessageCodec<HttpReque
} }
case PASS_THROUGH: { case PASS_THROUGH: {
ensureContent(msg); ensureContent(msg);
out.add(BufUtil.retain(msg)); out.add(ByteBufUtil.retain(msg));
// Passed through all following contents of the current response. // Passed through all following contents of the current response.
if (msg instanceof LastHttpContent) { if (msg instanceof LastHttpContent) {
state = State.AWAIT_HEADERS; state = State.AWAIT_HEADERS;
@ -235,7 +234,7 @@ public abstract class HttpContentEncoder extends MessageToMessageCodec<HttpReque
* the value of the {@code "Accept-Encoding"} header * the value of the {@code "Accept-Encoding"} header
* *
* @return the result of preparation, which is composed of the determined * @return the result of preparation, which is composed of the determined
* target content encoding and a new {@link EmbeddedByteChannel} that * target content encoding and a new {@link EmbeddedChannel} that
* encodes the content into the target content encoding. * encodes the content into the target content encoding.
* {@code null} if {@code acceptEncoding} is unsupported or rejected * {@code null} if {@code acceptEncoding} is unsupported or rejected
* and thus the content should be handled as-is (i.e. no encoding). * and thus the content should be handled as-is (i.e. no encoding).
@ -276,7 +275,7 @@ public abstract class HttpContentEncoder extends MessageToMessageCodec<HttpReque
private void fetchEncoderOutput(ByteBuf out) { private void fetchEncoderOutput(ByteBuf out) {
for (;;) { for (;;) {
ByteBuf buf = encoder.readOutbound(); ByteBuf buf = (ByteBuf) encoder.readOutbound();
if (buf == null) { if (buf == null) {
break; break;
} }
@ -286,9 +285,9 @@ public abstract class HttpContentEncoder extends MessageToMessageCodec<HttpReque
public static final class Result { public static final class Result {
private final String targetContentEncoding; private final String targetContentEncoding;
private final EmbeddedByteChannel contentEncoder; private final EmbeddedChannel contentEncoder;
public Result(String targetContentEncoding, EmbeddedByteChannel contentEncoder) { public Result(String targetContentEncoding, EmbeddedChannel contentEncoder) {
if (targetContentEncoding == null) { if (targetContentEncoding == null) {
throw new NullPointerException("targetContentEncoding"); throw new NullPointerException("targetContentEncoding");
} }
@ -304,7 +303,7 @@ public abstract class HttpContentEncoder extends MessageToMessageCodec<HttpReque
return targetContentEncoding; return targetContentEncoding;
} }
public EmbeddedByteChannel contentEncoder() { public EmbeddedChannel contentEncoder() {
return contentEncoder; return contentEncoder;
} }
} }

View File

@ -15,14 +15,14 @@
*/ */
package io.netty.handler.codec.http; package io.netty.handler.codec.http;
import io.netty.buffer.BufUtil;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.CompositeByteBuf; import io.netty.buffer.CompositeByteBuf;
import io.netty.buffer.MessageBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipeline;
import io.netty.channel.MessageList;
import io.netty.handler.codec.DecoderResult; import io.netty.handler.codec.DecoderResult;
import io.netty.handler.codec.MessageToMessageDecoder; import io.netty.handler.codec.MessageToMessageDecoder;
import io.netty.handler.codec.TooLongFrameException; import io.netty.handler.codec.TooLongFrameException;
@ -108,7 +108,7 @@ public class HttpObjectAggregator extends MessageToMessageDecoder<HttpObject> {
} }
@Override @Override
protected void decode(ChannelHandlerContext ctx, HttpObject msg, MessageBuf<Object> out) throws Exception { protected void decode(ChannelHandlerContext ctx, HttpObject msg, MessageList<Object> out) throws Exception {
FullHttpMessage currentMessage = this.currentMessage; FullHttpMessage currentMessage = this.currentMessage;
if (msg instanceof HttpMessage) { if (msg instanceof HttpMessage) {
@ -129,7 +129,7 @@ public class HttpObjectAggregator extends MessageToMessageDecoder<HttpObject> {
if (!m.getDecoderResult().isSuccess()) { if (!m.getDecoderResult().isSuccess()) {
removeTransferEncodingChunked(m); removeTransferEncodingChunked(m);
this.currentMessage = null; this.currentMessage = null;
out.add(BufUtil.retain(m)); out.add(ByteBufUtil.retain(m));
return; return;
} }
if (msg instanceof HttpRequest) { if (msg instanceof HttpRequest) {

View File

@ -16,10 +16,10 @@
package io.netty.handler.codec.http; package io.netty.handler.codec.http;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.MessageBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipeline;
import io.netty.channel.MessageList;
import io.netty.handler.codec.DecoderResult; import io.netty.handler.codec.DecoderResult;
import io.netty.handler.codec.ReplayingDecoder; import io.netty.handler.codec.ReplayingDecoder;
import io.netty.handler.codec.TooLongFrameException; import io.netty.handler.codec.TooLongFrameException;
@ -168,7 +168,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
} }
@Override @Override
protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, MessageBuf<Object> out) throws Exception { protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, MessageList<Object> out) throws Exception {
switch (state()) { switch (state()) {
case SKIP_CONTROL_CHARS: { case SKIP_CONTROL_CHARS: {
try { try {
@ -449,7 +449,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
private void reset() { private void reset() {
reset(null); reset(null);
} }
private void reset(MessageBuf<Object> out) { private void reset(MessageList<Object> out) {
if (out != null) { if (out != null) {
HttpMessage message = this.message; HttpMessage message = this.message;
ByteBuf content = this.content; ByteBuf content = this.content;
@ -500,7 +500,7 @@ public abstract class HttpObjectDecoder extends ReplayingDecoder<HttpObjectDecod
} }
} }
private void readFixedLengthContent(ByteBuf buffer, MessageBuf<Object> out) { private void readFixedLengthContent(ByteBuf buffer, MessageList<Object> out) {
//we have a content-length so we just read the correct number of bytes //we have a content-length so we just read the correct number of bytes
long length = HttpHeaders.getContentLength(message, -1); long length = HttpHeaders.getContentLength(message, -1);
assert length <= Integer.MAX_VALUE; assert length <= Integer.MAX_VALUE;

View File

@ -40,7 +40,7 @@ import static io.netty.handler.codec.http.HttpConstants.*;
*/ */
public abstract class HttpObjectEncoder<H extends HttpMessage> extends MessageToByteEncoder<HttpObject> { public abstract class HttpObjectEncoder<H extends HttpMessage> extends MessageToByteEncoder<HttpObject> {
private static final byte[] CRLF = { CR, LF }; 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 byte[] HEADER_SEPARATOR = { COLON , SP };
private static final int ST_INIT = 0; private static final int ST_INIT = 0;
private static final int ST_CONTENT_NON_CHUNK = 1; private static final int ST_CONTENT_NON_CHUNK = 1;
@ -93,7 +93,7 @@ public abstract class HttpObjectEncoder<H extends HttpMessage> extends MessageTo
} }
if (chunk instanceof LastHttpContent) { if (chunk instanceof LastHttpContent) {
out.writeBytes(CRLF_END); out.writeBytes(ZERO_CRLF);
encodeTrailingHeaders(out, (LastHttpContent) chunk); encodeTrailingHeaders(out, (LastHttpContent) chunk);
out.writeBytes(CRLF); out.writeBytes(CRLF);

View File

@ -15,11 +15,6 @@
*/ */
package io.netty.handler.codec.http; 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; import io.netty.channel.CombinedChannelDuplexHandler;
@ -30,8 +25,7 @@ import io.netty.channel.CombinedChannelDuplexHandler;
* @see HttpClientCodec * @see HttpClientCodec
*/ */
public final class HttpServerCodec public final class HttpServerCodec
extends CombinedChannelDuplexHandler extends CombinedChannelDuplexHandler<HttpRequestDecoder, HttpResponseEncoder> {
implements ChannelInboundByteHandler, ChannelOutboundMessageHandler<HttpObject> {
/** /**
* Creates a new instance with the default decoder options * 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) { public HttpServerCodec(int maxInitialLineLength, int maxHeaderSize, int maxChunkSize) {
super(new HttpRequestDecoder(maxInitialLineLength, maxHeaderSize, maxChunkSize), new HttpResponseEncoder()); 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<HttpObject> newOutboundBuffer(ChannelHandlerContext ctx) throws Exception {
return encoder().newOutboundBuffer(ctx);
}
} }

View File

@ -15,7 +15,6 @@
*/ */
package io.netty.handler.codec.http.multipart; package io.netty.handler.codec.http.multipart;
import io.netty.buffer.BufUtil;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.handler.codec.DecoderException; import io.netty.handler.codec.DecoderException;
import io.netty.handler.codec.http.HttpConstants; import io.netty.handler.codec.http.HttpConstants;

View File

@ -16,7 +16,7 @@
package io.netty.handler.codec.http.multipart; package io.netty.handler.codec.http.multipart;
import io.netty.buffer.ByteBuf; 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.DecoderResult;
import io.netty.handler.codec.http.DefaultFullHttpRequest; import io.netty.handler.codec.http.DefaultFullHttpRequest;
import io.netty.handler.codec.http.DefaultHttpContent; import io.netty.handler.codec.http.DefaultHttpContent;
@ -943,7 +943,7 @@ public class HttpPostRequestEncoder implements ChunkedMessageInput<HttpContent>
* if the encoding is in error * if the encoding is in error
*/ */
@Override @Override
public boolean readChunk(MessageBuf<HttpContent> buffer) throws ErrorDataEncoderException { public boolean readChunk(MessageList<HttpContent> buffer) throws ErrorDataEncoderException {
if (isLastChunkSent) { if (isLastChunkSent) {
return false; return false;
} else { } else {

View File

@ -16,8 +16,8 @@
package io.netty.handler.codec.http.websocketx; package io.netty.handler.codec.http.websocketx;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.MessageBuf;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.MessageList;
import io.netty.handler.codec.ReplayingDecoder; import io.netty.handler.codec.ReplayingDecoder;
import io.netty.handler.codec.TooLongFrameException; import io.netty.handler.codec.TooLongFrameException;
@ -50,7 +50,7 @@ public class WebSocket00FrameDecoder extends ReplayingDecoder<Void> {
} }
@Override @Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, MessageBuf<Object> out) throws Exception { protected void decode(ChannelHandlerContext ctx, ByteBuf in, MessageList<Object> out) throws Exception {
// Discard all data received if closing handshake was received before. // Discard all data received if closing handshake was received before.
if (receivedClosingHandshake) { if (receivedClosingHandshake) {
in.skipBytes(actualReadableBytes()); in.skipBytes(actualReadableBytes());

View File

@ -54,9 +54,10 @@
package io.netty.handler.codec.http.websocketx; package io.netty.handler.codec.http.websocketx;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.MessageBuf; import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.MessageList;
import io.netty.handler.codec.CorruptedFrameException; import io.netty.handler.codec.CorruptedFrameException;
import io.netty.handler.codec.ReplayingDecoder; import io.netty.handler.codec.ReplayingDecoder;
import io.netty.handler.codec.TooLongFrameException; import io.netty.handler.codec.TooLongFrameException;
@ -89,6 +90,7 @@ public class WebSocket08FrameDecoder extends ReplayingDecoder<WebSocket08FrameDe
private ByteBuf framePayload; private ByteBuf framePayload;
private int framePayloadBytesRead; private int framePayloadBytesRead;
private ByteBuf maskingKey; private ByteBuf maskingKey;
private ByteBuf payloadBuffer;
private final boolean allowExtensions; private final boolean allowExtensions;
private final boolean maskedPayload; private final boolean maskedPayload;
@ -118,7 +120,7 @@ public class WebSocket08FrameDecoder extends ReplayingDecoder<WebSocket08FrameDe
} }
@Override @Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, MessageBuf<Object> out) throws Exception { protected void decode(ChannelHandlerContext ctx, ByteBuf in, MessageList<Object> out) throws Exception {
// Discard all data received if closing handshake was received before. // Discard all data received if closing handshake was received before.
if (receivedClosingHandshake) { if (receivedClosingHandshake) {
@ -126,248 +128,267 @@ public class WebSocket08FrameDecoder extends ReplayingDecoder<WebSocket08FrameDe
return; return;
} }
switch (state()) { try {
case FRAME_START: switch (state()) {
framePayloadBytesRead = 0; case FRAME_START:
framePayloadLength = -1; framePayloadBytesRead = 0;
framePayload = null; framePayloadLength = -1;
framePayload = null;
payloadBuffer = null;
// FIN, RSV, OPCODE // FIN, RSV, OPCODE
byte b = in.readByte(); byte b = in.readByte();
frameFinalFlag = (b & 0x80) != 0; frameFinalFlag = (b & 0x80) != 0;
frameRsv = (b & 0x70) >> 4; frameRsv = (b & 0x70) >> 4;
frameOpcode = b & 0x0F; frameOpcode = b & 0x0F;
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("Decoding WebSocket Frame opCode={}", frameOpcode); logger.debug("Decoding WebSocket Frame opCode={}", frameOpcode);
} }
// MASK, PAYLOAD LEN 1 // MASK, PAYLOAD LEN 1
b = in.readByte(); b = in.readByte();
boolean frameMasked = (b & 0x80) != 0; boolean frameMasked = (b & 0x80) != 0;
int framePayloadLen1 = b & 0x7F; int framePayloadLen1 = b & 0x7F;
if (frameRsv != 0 && !allowExtensions) { if (frameRsv != 0 && !allowExtensions) {
protocolViolation(ctx, "RSV != 0 and no extension negotiated, RSV:" + frameRsv); 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");
return; return;
} }
// control frames MUST have payload 125 octets or less if (maskedPayload && !frameMasked) {
if (framePayloadLen1 > 125) { protocolViolation(ctx, "unmasked client to server frame");
protocolViolation(ctx, "control frame with payload length > 125 octets");
return; return;
} }
if (frameOpcode > 7) { // control frame (have MSB in opcode set)
// check for reserved control frame opcodes // control frames MUST NOT be fragmented
if (!(frameOpcode == OPCODE_CLOSE || frameOpcode == OPCODE_PING || frameOpcode == OPCODE_PONG)) { if (!frameFinalFlag) {
protocolViolation(ctx, "control frame using reserved opcode " + frameOpcode); protocolViolation(ctx, "fragmented control frame");
return; return;
} }
// close frame : if there is a body, the first two bytes of the // control frames MUST have payload 125 octets or less
// body MUST be a 2-byte unsigned integer (in network byte if (framePayloadLen1 > 125) {
// order) representing a getStatus code protocolViolation(ctx, "control frame with payload length > 125 octets");
if (frameOpcode == 8 && framePayloadLen1 == 1) { return;
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;
}
// check opcode vs message fragmentation state 1/2 // check for reserved control frame opcodes
if (fragmentedFramesCount == 0 && frameOpcode == OPCODE_CONT) { if (!(frameOpcode == OPCODE_CLOSE || frameOpcode == OPCODE_PING
protocolViolation(ctx, "received continuation data frame outside fragmented message"); || frameOpcode == OPCODE_PONG)) {
return; protocolViolation(ctx, "control frame using reserved opcode " + frameOpcode);
} return;
}
// check opcode vs message fragmentation state 2/2 // close frame : if there is a body, the first two bytes of the
if (fragmentedFramesCount != 0 && frameOpcode != OPCODE_CONT && frameOpcode != OPCODE_PING) { // body MUST be a 2-byte unsigned integer (in network byte
protocolViolation(ctx, "received non-continuation data frame while inside fragmented message"); // order) representing a getStatus code
return; 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 // check opcode vs message fragmentation state 1/2
if (framePayloadLen1 == 126) { if (fragmentedFramesCount == 0 && frameOpcode == OPCODE_CONT) {
framePayloadLength = in.readUnsignedShort(); protocolViolation(ctx, "received continuation data frame outside fragmented message");
if (framePayloadLength < 126) { return;
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) { // check opcode vs message fragmentation state 2/2
protocolViolation(ctx, "invalid data frame length (not using minimal length encoding)"); if (fragmentedFramesCount != 0 && frameOpcode != OPCODE_CONT && frameOpcode != OPCODE_PING) {
return; protocolViolation(ctx,
} "received non-continuation data frame while inside fragmented message");
} else { return;
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;
} }
} }
} else {
// Not final frame so we can expect more frames in the // Read frame payload length
// fragmented sequence if (framePayloadLen1 == 126) {
if (fragmentedFramesCount == 0) { framePayloadLength = in.readUnsignedShort();
// First text or binary frame for a fragmented set if (framePayloadLength < 126) {
fragmentedFramesText = null; protocolViolation(ctx, "invalid data frame length (not using minimal length encoding)");
if (frameOpcode == OPCODE_TEXT) { return;
checkUTF8String(ctx, framePayload); }
} 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 { } else {
// Subsequent frames - only check if init frame is text framePayloadLength = framePayloadLen1;
if (fragmentedFramesText != null) {
checkUTF8String(ctx, framePayload);
}
} }
// Increment counter if (framePayloadLength > maxFramePayloadLength) {
fragmentedFramesCount++; protocolViolation(ctx, "Max frame length of " + maxFramePayloadLength + " has been exceeded.");
} return;
}
// Return the frame if (logger.isDebugEnabled()) {
if (frameOpcode == OPCODE_TEXT) { logger.debug("Decoding WebSocket Frame length={}", framePayloadLength);
out.add(new TextWebSocketFrame(frameFinalFlag, frameRsv, framePayload)); }
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; return;
} else if (frameOpcode == OPCODE_BINARY) { default:
out.add(new BinaryWebSocketFrame(frameFinalFlag, frameRsv, framePayload)); throw new Error("Shouldn't reach here.");
return; }
} else if (frameOpcode == OPCODE_CONT) { } catch (Exception e) {
out.add(new ContinuationWebSocketFrame(frameFinalFlag, frameRsv, framePayload, aggregatedText)); if (payloadBuffer != null) {
return; if (payloadBuffer.refCnt() > 0) {
} else { payloadBuffer.release();
throw new UnsupportedOperationException("Cannot decode web socket frame with opcode: "
+ frameOpcode);
} }
case CORRUPT: payloadBuffer = null;
// 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. if (framePayload != null) {
in.readByte(); if (framePayload.refCnt() > 0) {
return; framePayload.release();
default: }
throw new Error("Shouldn't reach here."); framePayload = null;
}
throw e;
} }
} }
@ -380,7 +401,7 @@ public class WebSocket08FrameDecoder extends ReplayingDecoder<WebSocket08FrameDe
private void protocolViolation(ChannelHandlerContext ctx, String reason) { private void protocolViolation(ChannelHandlerContext ctx, String reason) {
checkpoint(State.CORRUPT); checkpoint(State.CORRUPT);
if (ctx.channel().isActive()) { if (ctx.channel().isActive()) {
ctx.flush().addListener(ChannelFutureListener.CLOSE); ctx.write(Unpooled.EMPTY_BUFFER).addListener(ChannelFutureListener.CLOSE);
} }
throw new CorruptedFrameException(reason); throw new CorruptedFrameException(reason);
} }

View File

@ -19,8 +19,8 @@ import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundByteHandler; import io.netty.channel.ChannelInboundHandler;
import io.netty.channel.ChannelOutboundMessageHandler; import io.netty.channel.ChannelOutboundHandler;
import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise; import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest;
@ -230,11 +230,11 @@ public abstract class WebSocketClientHandshaker {
/** /**
* Returns the decoder to use after handshake is complete. * 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. * Returns the encoder to use after the handshake is complete.
*/ */
protected abstract ChannelOutboundMessageHandler<WebSocketFrame> newWebSocketEncoder(); protected abstract ChannelOutboundHandler newWebSocketEncoder();
} }

View File

@ -17,8 +17,8 @@ package io.netty.handler.codec.http.websocketx;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelInboundByteHandler; import io.netty.channel.ChannelInboundHandler;
import io.netty.channel.ChannelOutboundMessageHandler; import io.netty.channel.ChannelOutboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpRequest; import io.netty.handler.codec.http.DefaultFullHttpRequest;
import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.FullHttpResponse;
@ -252,12 +252,12 @@ public class WebSocketClientHandshaker00 extends WebSocketClientHandshaker {
} }
@Override @Override
protected ChannelInboundByteHandler newWebsocketDecoder() { protected ChannelInboundHandler newWebsocketDecoder() {
return new WebSocket00FrameDecoder(maxFramePayloadLength()); return new WebSocket00FrameDecoder(maxFramePayloadLength());
} }
@Override @Override
protected ChannelOutboundMessageHandler<WebSocketFrame> newWebSocketEncoder() { protected ChannelOutboundHandler newWebSocketEncoder() {
return new WebSocket00FrameEncoder(); return new WebSocket00FrameEncoder();
} }
} }

View File

@ -15,8 +15,8 @@
*/ */
package io.netty.handler.codec.http.websocketx; package io.netty.handler.codec.http.websocketx;
import io.netty.channel.ChannelInboundByteHandler; import io.netty.channel.ChannelInboundHandler;
import io.netty.channel.ChannelOutboundMessageHandler; import io.netty.channel.ChannelOutboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpRequest; import io.netty.handler.codec.http.DefaultFullHttpRequest;
import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.FullHttpResponse;
@ -191,12 +191,12 @@ public class WebSocketClientHandshaker07 extends WebSocketClientHandshaker {
} }
@Override @Override
protected ChannelInboundByteHandler newWebsocketDecoder() { protected ChannelInboundHandler newWebsocketDecoder() {
return new WebSocket07FrameDecoder(false, allowExtensions, maxFramePayloadLength()); return new WebSocket07FrameDecoder(false, allowExtensions, maxFramePayloadLength());
} }
@Override @Override
protected ChannelOutboundMessageHandler<WebSocketFrame> newWebSocketEncoder() { protected ChannelOutboundHandler newWebSocketEncoder() {
return new WebSocket07FrameEncoder(true); return new WebSocket07FrameEncoder(true);
} }
} }

View File

@ -15,8 +15,8 @@
*/ */
package io.netty.handler.codec.http.websocketx; package io.netty.handler.codec.http.websocketx;
import io.netty.channel.ChannelInboundByteHandler; import io.netty.channel.ChannelInboundHandler;
import io.netty.channel.ChannelOutboundMessageHandler; import io.netty.channel.ChannelOutboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpRequest; import io.netty.handler.codec.http.DefaultFullHttpRequest;
import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.FullHttpResponse;
@ -191,12 +191,12 @@ public class WebSocketClientHandshaker08 extends WebSocketClientHandshaker {
} }
@Override @Override
protected ChannelInboundByteHandler newWebsocketDecoder() { protected ChannelInboundHandler newWebsocketDecoder() {
return new WebSocket08FrameDecoder(false, allowExtensions, maxFramePayloadLength()); return new WebSocket08FrameDecoder(false, allowExtensions, maxFramePayloadLength());
} }
@Override @Override
protected ChannelOutboundMessageHandler<WebSocketFrame> newWebSocketEncoder() { protected ChannelOutboundHandler newWebSocketEncoder() {
return new WebSocket08FrameEncoder(true); return new WebSocket08FrameEncoder(true);
} }
} }

View File

@ -15,8 +15,8 @@
*/ */
package io.netty.handler.codec.http.websocketx; package io.netty.handler.codec.http.websocketx;
import io.netty.channel.ChannelInboundByteHandler; import io.netty.channel.ChannelInboundHandler;
import io.netty.channel.ChannelOutboundMessageHandler; import io.netty.channel.ChannelOutboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpRequest; import io.netty.handler.codec.http.DefaultFullHttpRequest;
import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.FullHttpResponse;
@ -191,12 +191,12 @@ public class WebSocketClientHandshaker13 extends WebSocketClientHandshaker {
} }
@Override @Override
protected ChannelInboundByteHandler newWebsocketDecoder() { protected ChannelInboundHandler newWebsocketDecoder() {
return new WebSocket13FrameDecoder(false, allowExtensions, maxFramePayloadLength()); return new WebSocket13FrameDecoder(false, allowExtensions, maxFramePayloadLength());
} }
@Override @Override
protected ChannelOutboundMessageHandler<WebSocketFrame> newWebSocketEncoder() { protected ChannelOutboundHandler newWebSocketEncoder() {
return new WebSocket13FrameEncoder(true); return new WebSocket13FrameEncoder(true);
} }
} }

View File

@ -16,8 +16,9 @@
package io.netty.handler.codec.http.websocketx; package io.netty.handler.codec.http.websocketx;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandler;
import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelStateHandler; import io.netty.channel.MessageList;
import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpHeaders;
import java.net.URI; 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. * 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 * 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}. * {@link ClientHandshakeStateEvent#HANDSHAKE_ISSUED} or {@link ClientHandshakeStateEvent#HANDSHAKE_COMPLETE}.
*/ */
public class WebSocketClientProtocolHandler extends WebSocketProtocolHandler { public class WebSocketClientProtocolHandler extends WebSocketProtocolHandler {
@ -128,12 +129,12 @@ public class WebSocketClientProtocolHandler extends WebSocketProtocolHandler {
} }
@Override @Override
public void messageReceived(ChannelHandlerContext ctx, WebSocketFrame frame) throws Exception { protected void decode(ChannelHandlerContext ctx, WebSocketFrame frame, MessageList<Object> out) throws Exception {
if (handleCloseFrames && frame instanceof CloseWebSocketFrame) { if (handleCloseFrames && frame instanceof CloseWebSocketFrame) {
ctx.close(); ctx.close();
return; return;
} }
super.messageReceived(ctx, frame); super.decode(ctx, frame, out);
} }
@Override @Override

View File

@ -18,10 +18,11 @@ package io.netty.handler.codec.http.websocketx;
import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext; 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; import io.netty.handler.codec.http.FullHttpResponse;
class WebSocketClientProtocolHandshakeHandler extends ChannelInboundMessageHandlerAdapter<FullHttpResponse> { class WebSocketClientProtocolHandshakeHandler extends ChannelInboundHandlerAdapter {
private final WebSocketClientHandshaker handshaker; private final WebSocketClientHandshaker handshaker;
public WebSocketClientProtocolHandshakeHandler(WebSocketClientHandshaker handshaker) { public WebSocketClientProtocolHandshakeHandler(WebSocketClientHandshaker handshaker) {
@ -45,9 +46,10 @@ class WebSocketClientProtocolHandshakeHandler extends ChannelInboundMessageHandl
} }
@Override @Override
public void messageReceived(ChannelHandlerContext ctx, FullHttpResponse msg) throws Exception { public void messageReceived(ChannelHandlerContext ctx, MessageList<Object> messages) throws Exception {
if (!handshaker.isHandshakeComplete()) { if (!handshaker.isHandshakeComplete()) {
handshaker.finishHandshake(ctx.channel(), msg); handshaker.finishHandshake(ctx.channel(), (FullHttpResponse) messages.get(0));
messages.remove(0);
ctx.fireUserEventTriggered( ctx.fireUserEventTriggered(
WebSocketClientProtocolHandler.ClientHandshakeStateEvent.HANDSHAKE_COMPLETE); WebSocketClientProtocolHandler.ClientHandshakeStateEvent.HANDSHAKE_COMPLETE);
ctx.pipeline().remove(this); ctx.pipeline().remove(this);

View File

@ -17,8 +17,8 @@ package io.netty.handler.codec.http.websocketx;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.CompositeByteBuf; import io.netty.buffer.CompositeByteBuf;
import io.netty.buffer.MessageBuf;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.MessageList;
import io.netty.handler.codec.MessageToMessageDecoder; import io.netty.handler.codec.MessageToMessageDecoder;
import io.netty.handler.codec.TooLongFrameException; import io.netty.handler.codec.TooLongFrameException;
@ -47,7 +47,7 @@ public class WebSocketFrameAggregator extends MessageToMessageDecoder<WebSocketF
} }
@Override @Override
protected void decode(ChannelHandlerContext ctx, WebSocketFrame msg, MessageBuf<Object> out) throws Exception { protected void decode(ChannelHandlerContext ctx, WebSocketFrame msg, MessageList<Object> out) throws Exception {
if (currentFrame == null) { if (currentFrame == null) {
tooLongFrameFound = false; tooLongFrameFound = false;
if (msg.isFinalFragment()) { if (msg.isFinalFragment()) {

View File

@ -17,12 +17,12 @@ package io.netty.handler.codec.http.websocketx;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundMessageHandlerAdapter; import io.netty.channel.MessageList;
import io.netty.handler.codec.MessageToMessageDecoder;
abstract class WebSocketProtocolHandler extends ChannelInboundMessageHandlerAdapter<WebSocketFrame> {
abstract class WebSocketProtocolHandler extends MessageToMessageDecoder<WebSocketFrame> {
@Override @Override
public void messageReceived(ChannelHandlerContext ctx, WebSocketFrame frame) throws Exception { protected void decode(ChannelHandlerContext ctx, WebSocketFrame frame, MessageList<Object> out) throws Exception {
if (frame instanceof PingWebSocketFrame) { if (frame instanceof PingWebSocketFrame) {
frame.content().retain(); frame.content().retain();
ctx.channel().write(new PongWebSocketFrame(frame.content())); ctx.channel().write(new PongWebSocketFrame(frame.content()));
@ -33,8 +33,7 @@ abstract class WebSocketProtocolHandler extends ChannelInboundMessageHandlerAda
return; return;
} }
frame.retain(); out.add(frame.retain());
ctx.nextInboundMessageBuffer().add(frame);
} }
@Override @Override

View File

@ -19,8 +19,8 @@ import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundByteHandler; import io.netty.channel.ChannelInboundHandler;
import io.netty.channel.ChannelOutboundMessageHandler; import io.netty.channel.ChannelOutboundHandler;
import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipeline;
import io.netty.channel.ChannelPromise; import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest;
@ -267,11 +267,11 @@ public abstract class WebSocketServerHandshaker {
/** /**
* Returns the decoder to use after handshake is complete. * 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. * Returns the encoder to use after the handshake is complete.
*/ */
protected abstract ChannelOutboundMessageHandler<WebSocketFrame> newWebSocketEncoder(); protected abstract ChannelOutboundHandler newWebSocketEncoder();
} }

View File

@ -19,8 +19,8 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.Channel; import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInboundByteHandler; import io.netty.channel.ChannelInboundHandler;
import io.netty.channel.ChannelOutboundMessageHandler; import io.netty.channel.ChannelOutboundHandler;
import io.netty.channel.ChannelPromise; import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest;
@ -183,12 +183,12 @@ public class WebSocketServerHandshaker00 extends WebSocketServerHandshaker {
} }
@Override @Override
protected ChannelInboundByteHandler newWebsocketDecoder() { protected ChannelInboundHandler newWebsocketDecoder() {
return new WebSocket00FrameDecoder(maxFramePayloadLength()); return new WebSocket00FrameDecoder(maxFramePayloadLength());
} }
@Override @Override
protected ChannelOutboundMessageHandler<WebSocketFrame> newWebSocketEncoder() { protected ChannelOutboundHandler newWebSocketEncoder() {
return new WebSocket00FrameEncoder(); return new WebSocket00FrameEncoder();
} }
} }

View File

@ -15,8 +15,8 @@
*/ */
package io.netty.handler.codec.http.websocketx; package io.netty.handler.codec.http.websocketx;
import io.netty.channel.ChannelInboundByteHandler; import io.netty.channel.ChannelInboundHandler;
import io.netty.channel.ChannelOutboundMessageHandler; import io.netty.channel.ChannelOutboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.FullHttpResponse;
@ -132,12 +132,12 @@ public class WebSocketServerHandshaker07 extends WebSocketServerHandshaker {
} }
@Override @Override
protected ChannelInboundByteHandler newWebsocketDecoder() { protected ChannelInboundHandler newWebsocketDecoder() {
return new WebSocket07FrameDecoder(true, allowExtensions, maxFramePayloadLength()); return new WebSocket07FrameDecoder(true, allowExtensions, maxFramePayloadLength());
} }
@Override @Override
protected ChannelOutboundMessageHandler<WebSocketFrame> newWebSocketEncoder() { protected ChannelOutboundHandler newWebSocketEncoder() {
return new WebSocket07FrameEncoder(false); return new WebSocket07FrameEncoder(false);
} }
} }

View File

@ -15,8 +15,8 @@
*/ */
package io.netty.handler.codec.http.websocketx; package io.netty.handler.codec.http.websocketx;
import io.netty.channel.ChannelInboundByteHandler; import io.netty.channel.ChannelInboundHandler;
import io.netty.channel.ChannelOutboundMessageHandler; import io.netty.channel.ChannelOutboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.FullHttpResponse;
@ -131,12 +131,12 @@ public class WebSocketServerHandshaker08 extends WebSocketServerHandshaker {
} }
@Override @Override
protected ChannelInboundByteHandler newWebsocketDecoder() { protected ChannelInboundHandler newWebsocketDecoder() {
return new WebSocket08FrameDecoder(true, allowExtensions, maxFramePayloadLength()); return new WebSocket08FrameDecoder(true, allowExtensions, maxFramePayloadLength());
} }
@Override @Override
protected ChannelOutboundMessageHandler<WebSocketFrame> newWebSocketEncoder() { protected ChannelOutboundHandler newWebSocketEncoder() {
return new WebSocket08FrameEncoder(false); return new WebSocket08FrameEncoder(false);
} }
} }

View File

@ -15,8 +15,8 @@
*/ */
package io.netty.handler.codec.http.websocketx; package io.netty.handler.codec.http.websocketx;
import io.netty.channel.ChannelInboundByteHandler; import io.netty.channel.ChannelInboundHandler;
import io.netty.channel.ChannelOutboundMessageHandler; import io.netty.channel.ChannelOutboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.FullHttpResponse;
@ -130,12 +130,12 @@ public class WebSocketServerHandshaker13 extends WebSocketServerHandshaker {
} }
@Override @Override
protected ChannelInboundByteHandler newWebsocketDecoder() { protected ChannelInboundHandler newWebsocketDecoder() {
return new WebSocket13FrameDecoder(true, allowExtensions, maxFramePayloadLength()); return new WebSocket13FrameDecoder(true, allowExtensions, maxFramePayloadLength());
} }
@Override @Override
protected ChannelOutboundMessageHandler<WebSocketFrame> newWebSocketEncoder() { protected ChannelOutboundHandler newWebSocketEncoder() {
return new WebSocket13FrameEncoder(false); return new WebSocket13FrameEncoder(false);
} }
} }

View File

@ -19,9 +19,10 @@ import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundMessageHandlerAdapter; import io.netty.channel.ChannelInboundHandler;
import io.netty.channel.ChannelStateHandler; import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipeline;
import io.netty.channel.MessageList;
import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.FullHttpResponse;
@ -43,7 +44,7 @@ import static io.netty.handler.codec.http.HttpVersion.*;
* to the <tt>io.netty.example.http.websocketx.server.WebSocketServer</tt> example. * to the <tt>io.netty.example.http.websocketx.server.WebSocketServer</tt> example.
* *
* To know once a handshake was done you can intercept the * 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}. * {@link ServerHandshakeStateEvent#HANDSHAKE_COMPLETE}.
*/ */
public class WebSocketServerProtocolHandler extends WebSocketProtocolHandler { public class WebSocketServerProtocolHandler extends WebSocketProtocolHandler {
@ -90,14 +91,14 @@ public class WebSocketServerProtocolHandler extends WebSocketProtocolHandler {
} }
@Override @Override
public void messageReceived(ChannelHandlerContext ctx, WebSocketFrame frame) throws Exception { protected void decode(ChannelHandlerContext ctx, WebSocketFrame frame, MessageList<Object> out) throws Exception {
if (frame instanceof CloseWebSocketFrame) { if (frame instanceof CloseWebSocketFrame) {
WebSocketServerHandshaker handshaker = getHandshaker(ctx); WebSocketServerHandshaker handshaker = getHandshaker(ctx);
frame.retain(); frame.retain();
handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame); handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame);
return; return;
} }
super.messageReceived(ctx, frame); super.decode(ctx, frame, out);
} }
@Override @Override
@ -120,12 +121,19 @@ public class WebSocketServerProtocolHandler extends WebSocketProtocolHandler {
} }
static ChannelHandler forbiddenHttpRequestResponder() { static ChannelHandler forbiddenHttpRequestResponder() {
return new ChannelInboundMessageHandlerAdapter<FullHttpRequest>() { return new ChannelInboundHandlerAdapter() {
@Override @Override
public void messageReceived(ChannelHandlerContext ctx, FullHttpRequest msg) throws Exception { public void messageReceived(final ChannelHandlerContext ctx, MessageList<Object> msgs) throws Exception {
FullHttpResponse response = for (int i = 0; i < msgs.size(); i++) {
new DefaultFullHttpResponse(HTTP_1_1, HttpResponseStatus.FORBIDDEN); Object msg = msgs.get(i);
ctx.channel().write(response); if (msg instanceof FullHttpRequest) {
FullHttpResponse response =
new DefaultFullHttpResponse(HTTP_1_1, HttpResponseStatus.FORBIDDEN);
ctx.channel().write(response);
msgs.remove(i--);
}
}
ctx.fireMessageReceived(msgs);
} }
}; };
} }

View File

@ -18,8 +18,9 @@ package io.netty.handler.codec.http.websocketx;
import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundMessageHandlerAdapter; import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipeline;
import io.netty.channel.MessageList;
import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpHeaders; 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}. * Handles the HTTP handshake (the HTTP Upgrade request) for {@link WebSocketServerProtocolHandler}.
*/ */
class WebSocketServerProtocolHandshakeHandler class WebSocketServerProtocolHandshakeHandler
extends ChannelInboundMessageHandlerAdapter<FullHttpRequest> { extends ChannelInboundHandlerAdapter {
private final String websocketPath; private final String websocketPath;
private final String subprotocols; private final String subprotocols;
@ -50,33 +51,37 @@ class WebSocketServerProtocolHandshakeHandler
} }
@Override @Override
public void messageReceived(final ChannelHandlerContext ctx, FullHttpRequest req) throws Exception { public void messageReceived(final ChannelHandlerContext ctx, MessageList<Object> msgs) throws Exception {
if (req.getMethod() != GET) { MessageList<FullHttpRequest> requests = msgs.cast();
sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, FORBIDDEN)); for (int i = 0; i < requests.size(); i++) {
return; FullHttpRequest req = requests.get(i);
} if (req.getMethod() != GET) {
sendHttpResponse(ctx, req, new DefaultFullHttpResponse(HTTP_1_1, FORBIDDEN));
return;
}
final WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory( final WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(
getWebSocketLocation(ctx.pipeline(), req, websocketPath), subprotocols, allowExtensions); getWebSocketLocation(ctx.pipeline(), req, websocketPath), subprotocols, allowExtensions);
final WebSocketServerHandshaker handshaker = wsFactory.newHandshaker(req); final WebSocketServerHandshaker handshaker = wsFactory.newHandshaker(req);
if (handshaker == null) { if (handshaker == null) {
WebSocketServerHandshakerFactory.sendUnsupportedWebSocketVersionResponse(ctx.channel()); WebSocketServerHandshakerFactory.sendUnsupportedWebSocketVersionResponse(ctx.channel());
} else { } else {
final ChannelFuture handshakeFuture = handshaker.handshake(ctx.channel(), req); final ChannelFuture handshakeFuture = handshaker.handshake(ctx.channel(), req);
handshakeFuture.addListener(new ChannelFutureListener() { handshakeFuture.addListener(new ChannelFutureListener() {
@Override @Override
public void operationComplete(ChannelFuture future) throws Exception { public void operationComplete(ChannelFuture future) throws Exception {
if (!future.isSuccess()) { if (!future.isSuccess()) {
ctx.fireExceptionCaught(future.cause()); ctx.fireExceptionCaught(future.cause());
} else { } else {
ctx.fireUserEventTriggered( ctx.fireUserEventTriggered(
WebSocketServerProtocolHandler.ServerHandshakeStateEvent.HANDSHAKE_COMPLETE); WebSocketServerProtocolHandler.ServerHandshakeStateEvent.HANDSHAKE_COMPLETE);
}
} }
} });
}); WebSocketServerProtocolHandler.setHandshaker(ctx, handshaker);
WebSocketServerProtocolHandler.setHandshaker(ctx, handshaker); ctx.pipeline().replace(this, "WS403Responder",
ctx.pipeline().replace(this, "WS403Responder", WebSocketServerProtocolHandler.forbiddenHttpRequestResponder());
WebSocketServerProtocolHandler.forbiddenHttpRequestResponder()); }
} }
} }

View File

@ -15,20 +15,12 @@
*/ */
package io.netty.handler.codec.spdy; 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; import io.netty.channel.CombinedChannelDuplexHandler;
/** /**
* A combination of {@link SpdyFrameDecoder} and {@link SpdyFrameEncoder}. * A combination of {@link SpdyFrameDecoder} and {@link SpdyFrameEncoder}.
*/ */
public final class SpdyFrameCodec public final class SpdyFrameCodec extends CombinedChannelDuplexHandler<SpdyFrameDecoder, SpdyFrameEncoder> {
extends CombinedChannelDuplexHandler
implements ChannelInboundByteHandler, ChannelOutboundMessageHandler<SpdyDataOrControlFrame> {
/** /**
* Creates a new instance with the specified {@code version} and * Creates a new instance with the specified {@code version} and
@ -51,27 +43,4 @@ public final class SpdyFrameCodec
new SpdyFrameDecoder(version, maxChunkSize, maxHeaderSize), new SpdyFrameDecoder(version, maxChunkSize, maxHeaderSize),
new SpdyFrameEncoder(version, compressionLevel, windowBits, memLevel)); 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<SpdyDataOrControlFrame> newOutboundBuffer(ChannelHandlerContext ctx) throws Exception {
return encoder().newOutboundBuffer(ctx);
}
} }

View File

@ -16,9 +16,9 @@
package io.netty.handler.codec.spdy; package io.netty.handler.codec.spdy;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.MessageBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.MessageList;
import io.netty.handler.codec.ByteToMessageDecoder; import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.TooLongFrameException; import io.netty.handler.codec.TooLongFrameException;
@ -94,7 +94,7 @@ public class SpdyFrameDecoder extends ByteToMessageDecoder {
} }
@Override @Override
public void decodeLast(ChannelHandlerContext ctx, ByteBuf in, MessageBuf<Object> out) throws Exception { public void decodeLast(ChannelHandlerContext ctx, ByteBuf in, MessageList<Object> out) throws Exception {
try { try {
decode(ctx, in, out); decode(ctx, in, out);
} finally { } finally {
@ -103,7 +103,7 @@ public class SpdyFrameDecoder extends ByteToMessageDecoder {
} }
@Override @Override
protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, MessageBuf<Object> out) throws Exception { protected void decode(ChannelHandlerContext ctx, ByteBuf buffer, MessageList<Object> out) throws Exception {
switch(state) { switch(state) {
case READ_COMMON_HEADER: case READ_COMMON_HEADER:
state = readCommonHeader(buffer); state = readCommonHeader(buffer);

View File

@ -15,20 +15,13 @@
*/ */
package io.netty.handler.codec.spdy; 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.channel.CombinedChannelDuplexHandler;
import io.netty.handler.codec.http.HttpObject;
/** /**
* A combination of {@link SpdyHttpDecoder} and {@link SpdyHttpEncoder} * A combination of {@link SpdyHttpDecoder} and {@link SpdyHttpEncoder}
*/ */
public final class SpdyHttpCodec public final class SpdyHttpCodec
extends CombinedChannelDuplexHandler extends CombinedChannelDuplexHandler<SpdyHttpDecoder, SpdyHttpEncoder> {
implements ChannelInboundMessageHandler<SpdyDataOrControlFrame>, ChannelOutboundMessageHandler<HttpObject> {
/** /**
* Creates a new instance with the specified decoder options. * Creates a new instance with the specified decoder options.
@ -36,22 +29,4 @@ public final class SpdyHttpCodec
public SpdyHttpCodec(int version, int maxContentLength) { public SpdyHttpCodec(int version, int maxContentLength) {
super(new SpdyHttpDecoder(version, maxContentLength), new SpdyHttpEncoder(version)); super(new SpdyHttpDecoder(version, maxContentLength), new SpdyHttpEncoder(version));
} }
private SpdyHttpDecoder decoder() {
return (SpdyHttpDecoder) stateHandler();
}
private SpdyHttpEncoder encoder() {
return (SpdyHttpEncoder) operationHandler();
}
@Override
public MessageBuf<SpdyDataOrControlFrame> newInboundBuffer(ChannelHandlerContext ctx) throws Exception {
return decoder().newInboundBuffer(ctx);
}
@Override
public MessageBuf<HttpObject> newOutboundBuffer(ChannelHandlerContext ctx) throws Exception {
return encoder().newOutboundBuffer(ctx);
}
} }

View File

@ -16,8 +16,8 @@
package io.netty.handler.codec.spdy; package io.netty.handler.codec.spdy;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.MessageBuf;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.MessageList;
import io.netty.handler.codec.MessageToMessageDecoder; import io.netty.handler.codec.MessageToMessageDecoder;
import io.netty.handler.codec.TooLongFrameException; import io.netty.handler.codec.TooLongFrameException;
import io.netty.handler.codec.http.DefaultFullHttpRequest; import io.netty.handler.codec.http.DefaultFullHttpRequest;
@ -91,7 +91,7 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<SpdyDataOrControlFr
} }
@Override @Override
protected void decode(ChannelHandlerContext ctx, SpdyDataOrControlFrame msg, MessageBuf<Object> out) protected void decode(ChannelHandlerContext ctx, SpdyDataOrControlFrame msg, MessageList<Object> out)
throws Exception { throws Exception {
if (msg instanceof SpdySynStreamFrame) { if (msg instanceof SpdySynStreamFrame) {

View File

@ -15,8 +15,8 @@
*/ */
package io.netty.handler.codec.spdy; package io.netty.handler.codec.spdy;
import io.netty.buffer.MessageBuf;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.MessageList;
import io.netty.handler.codec.MessageToMessageEncoder; import io.netty.handler.codec.MessageToMessageEncoder;
import io.netty.handler.codec.UnsupportedMessageTypeException; import io.netty.handler.codec.UnsupportedMessageTypeException;
import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest;
@ -139,7 +139,7 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder<HttpObject> {
} }
@Override @Override
protected void encode(ChannelHandlerContext ctx, HttpObject msg, MessageBuf<Object> out) throws Exception { protected void encode(ChannelHandlerContext ctx, HttpObject msg, MessageList<Object> out) throws Exception {
boolean valid = false; boolean valid = false;

View File

@ -15,9 +15,9 @@
*/ */
package io.netty.handler.codec.spdy; package io.netty.handler.codec.spdy;
import io.netty.buffer.BufUtil; import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.MessageBuf;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.MessageList;
import io.netty.handler.codec.MessageToMessageCodec; import io.netty.handler.codec.MessageToMessageCodec;
import io.netty.handler.codec.http.HttpMessage; import io.netty.handler.codec.http.HttpMessage;
@ -40,17 +40,17 @@ public class SpdyHttpResponseStreamIdHandler extends
} }
@Override @Override
protected void encode(ChannelHandlerContext ctx, HttpMessage msg, MessageBuf<Object> out) throws Exception { protected void encode(ChannelHandlerContext ctx, HttpMessage msg, MessageList<Object> out) throws Exception {
Integer id = ids.poll(); Integer id = ids.poll();
if (id != null && id.intValue() != NO_ID && !msg.headers().contains(SpdyHttpHeaders.Names.STREAM_ID)) { if (id != null && id.intValue() != NO_ID && !msg.headers().contains(SpdyHttpHeaders.Names.STREAM_ID)) {
SpdyHttpHeaders.setStreamId(msg, id); SpdyHttpHeaders.setStreamId(msg, id);
} }
out.add(BufUtil.retain(msg)); out.add(ByteBufUtil.retain(msg));
} }
@Override @Override
protected void decode(ChannelHandlerContext ctx, Object msg, MessageBuf<Object> out) throws Exception { protected void decode(ChannelHandlerContext ctx, Object msg, MessageList<Object> out) throws Exception {
if (msg instanceof HttpMessage) { if (msg instanceof HttpMessage) {
boolean contains = ((HttpMessage) msg).headers().contains(SpdyHttpHeaders.Names.STREAM_ID); boolean contains = ((HttpMessage) msg).headers().contains(SpdyHttpHeaders.Names.STREAM_ID);
if (!contains) { if (!contains) {
@ -62,6 +62,6 @@ public class SpdyHttpResponseStreamIdHandler extends
ids.remove(((SpdyRstStreamFrame) msg).getStreamId()); ids.remove(((SpdyRstStreamFrame) msg).getStreamId());
} }
out.add(BufUtil.retain(msg)); out.add(ByteBufUtil.retain(msg));
} }
} }

View File

@ -15,14 +15,12 @@
*/ */
package io.netty.handler.codec.spdy; package io.netty.handler.codec.spdy;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelHandlerUtil; import io.netty.channel.ChannelInboundHandler;
import io.netty.channel.ChannelInboundByteHandler; import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInboundByteHandlerAdapter;
import io.netty.channel.ChannelInboundMessageHandler;
import io.netty.channel.ChannelPipeline; import io.netty.channel.ChannelPipeline;
import io.netty.channel.MessageList;
import io.netty.handler.codec.http.HttpObjectAggregator; import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpRequestDecoder; import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder; import io.netty.handler.codec.http.HttpResponseEncoder;
@ -31,11 +29,11 @@ import io.netty.handler.ssl.SslHandler;
import javax.net.ssl.SSLEngine; 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 * 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. * much about the low-level details.
*/ */
public abstract class SpdyOrHttpChooser extends ChannelInboundByteHandlerAdapter { public abstract class SpdyOrHttpChooser extends ChannelInboundHandlerAdapter {
// TODO: Replace with generic NPN handler // TODO: Replace with generic NPN handler
@ -63,25 +61,13 @@ public abstract class SpdyOrHttpChooser extends ChannelInboundByteHandlerAdapter
protected abstract SelectedProtocol getProtocol(SSLEngine engine); protected abstract SelectedProtocol getProtocol(SSLEngine engine);
@Override @Override
public ByteBuf newInboundBuffer(ChannelHandlerContext ctx) throws Exception { public void messageReceived(ChannelHandlerContext ctx, MessageList<Object> in) 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 {
if (initPipeline(ctx)) { 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 // When we reached here we can remove this handler as its now clear what protocol we want to use
// from this point on. // from this point on.
ctx.pipeline().remove(this); 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 * when the {@link SelectedProtocol} was {@link SelectedProtocol#HTTP_1_0} or
* {@link SelectedProtocol#HTTP_1_1} * {@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 * when the {@link SelectedProtocol} was {@link SelectedProtocol#SPDY_2} or
* {@link SelectedProtocol#SPDY_3}. * {@link SelectedProtocol#SPDY_3}.
* *
* Bye default this getMethod will just delecate to {@link #createHttpRequestHandlerForHttp()}, but * Bye default this getMethod will just delecate to {@link #createHttpRequestHandlerForHttp()}, but
* sub-classes may override this to change the behaviour. * sub-classes may override this to change the behaviour.
*/ */
protected ChannelInboundMessageHandler<?> createHttpRequestHandlerForSpdy() { protected ChannelInboundHandler createHttpRequestHandlerForSpdy() {
return createHttpRequestHandlerForHttp(); return createHttpRequestHandlerForHttp();
} }
} }

View File

@ -15,15 +15,12 @@
*/ */
package io.netty.handler.codec.spdy; 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.ChannelDuplexHandler;
import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundMessageHandler;
import io.netty.channel.ChannelOutboundMessageHandler;
import io.netty.channel.ChannelPromise; import io.netty.channel.ChannelPromise;
import io.netty.channel.MessageList;
import io.netty.util.internal.EmptyArrays; import io.netty.util.internal.EmptyArrays;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
@ -32,8 +29,7 @@ import java.util.concurrent.atomic.AtomicInteger;
* Manages streams within a SPDY session. * Manages streams within a SPDY session.
*/ */
public class SpdySessionHandler public class SpdySessionHandler
extends ChannelDuplexHandler extends ChannelDuplexHandler {
implements ChannelInboundMessageHandler<Object>, ChannelOutboundMessageHandler<Object> {
private static final SpdyProtocolException PROTOCOL_EXCEPTION = new SpdyProtocolException(); private static final SpdyProtocolException PROTOCOL_EXCEPTION = new SpdyProtocolException();
private static final SpdyProtocolException STREAM_CLOSED = new SpdyProtocolException("Stream closed"); private static final SpdyProtocolException STREAM_CLOSED = new SpdyProtocolException("Stream closed");
@ -85,21 +81,11 @@ public class SpdySessionHandler
} }
@Override @Override
public MessageBuf<Object> newInboundBuffer(ChannelHandlerContext ctx) throws Exception { public void messageReceived(ChannelHandlerContext ctx, MessageList<Object> in) throws Exception {
return Unpooled.messageBuffer();
}
@Override
public MessageBuf<Object> newOutboundBuffer(ChannelHandlerContext ctx) throws Exception {
return Unpooled.messageBuffer();
}
@Override
public void inboundBufferUpdated(ChannelHandlerContext ctx) throws Exception {
MessageBuf<Object> in = ctx.inboundMessageBuffer();
boolean handled = false; boolean handled = false;
for (;;) { MessageList<Object> out = MessageList.newInstance();
Object msg = in.poll(); for (int i = 0 ; i < in.size(); i++) {
Object msg = in.get(i);
if (msg == null) { if (msg == null) {
break; break;
} }
@ -108,18 +94,20 @@ public class SpdySessionHandler
// Let the next handlers handle the buffered messages before SYN_STREAM message updates the // Let the next handlers handle the buffered messages before SYN_STREAM message updates the
// lastGoodStreamId. // lastGoodStreamId.
if (handled) { if (handled) {
ctx.fireInboundBufferUpdated(); ctx.fireMessageReceived(out);
out = MessageList.newInstance();
} }
} }
handleInboundMessage(ctx, msg); handleInboundMessage(ctx, msg, out);
handled = true; 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<Object> out) throws Exception {
if (msg instanceof SpdyDataFrame) { 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 // Check if we received a data frame for a Stream-ID which is not open
if (!spdySession.isActiveStream(streamID)) { if (!spdySession.isActiveStream(streamID)) {
if (streamID <= lastGoodStreamId) { if (streamID <= lastGoodStreamId) {
issueStreamError(ctx, streamID, SpdyStreamStatus.PROTOCOL_ERROR); issueStreamError(ctx, streamID, SpdyStreamStatus.PROTOCOL_ERROR, out);
} else if (!sentGoAwayFrame) { } else if (!sentGoAwayFrame) {
issueStreamError(ctx, streamID, SpdyStreamStatus.INVALID_STREAM); issueStreamError(ctx, streamID, SpdyStreamStatus.INVALID_STREAM, out);
} }
return; return;
} }
// Check if we received a data frame for a stream which is half-closed // Check if we received a data frame for a stream which is half-closed
if (spdySession.isRemoteSideClosed(streamID)) { if (spdySession.isRemoteSideClosed(streamID)) {
issueStreamError(ctx, streamID, SpdyStreamStatus.STREAM_ALREADY_CLOSED); issueStreamError(ctx, streamID, SpdyStreamStatus.STREAM_ALREADY_CLOSED, out);
return; return;
} }
// Check if we received a data frame before receiving a SYN_REPLY // Check if we received a data frame before receiving a SYN_REPLY
if (!isRemoteInitiatedID(streamID) && !spdySession.hasReceivedReply(streamID)) { if (!isRemoteInitiatedID(streamID) && !spdySession.hasReceivedReply(streamID)) {
issueStreamError(ctx, streamID, SpdyStreamStatus.PROTOCOL_ERROR); issueStreamError(ctx, streamID, SpdyStreamStatus.PROTOCOL_ERROR, out);
return; return;
} }
@ -188,7 +176,7 @@ public class SpdySessionHandler
// This difference is stored for the session when writing the SETTINGS frame // This difference is stored for the session when writing the SETTINGS frame
// and is cleared once we send a WINDOW_UPDATE frame. // and is cleared once we send a WINDOW_UPDATE frame.
if (newWindowSize < spdySession.getReceiveWindowSizeLowerBound(streamID)) { if (newWindowSize < spdySession.getReceiveWindowSizeLowerBound(streamID)) {
issueStreamError(ctx, streamID, SpdyStreamStatus.FLOW_CONTROL_ERROR); issueStreamError(ctx, streamID, SpdyStreamStatus.FLOW_CONTROL_ERROR, out);
return; return;
} }
@ -198,8 +186,7 @@ public class SpdySessionHandler
while (spdyDataFrame.content().readableBytes() > initialReceiveWindowSize) { while (spdyDataFrame.content().readableBytes() > initialReceiveWindowSize) {
SpdyDataFrame partialDataFrame = new DefaultSpdyDataFrame(streamID, SpdyDataFrame partialDataFrame = new DefaultSpdyDataFrame(streamID,
spdyDataFrame.content().readSlice(initialReceiveWindowSize).retain()); spdyDataFrame.content().readSlice(initialReceiveWindowSize).retain());
ctx.nextOutboundMessageBuffer().add(partialDataFrame); ctx.write(partialDataFrame);
ctx.flush();
} }
} }
@ -241,7 +228,7 @@ public class SpdySessionHandler
if (spdySynStreamFrame.isInvalid() || if (spdySynStreamFrame.isInvalid() ||
!isRemoteInitiatedID(streamID) || !isRemoteInitiatedID(streamID) ||
spdySession.isActiveStream(streamID)) { spdySession.isActiveStream(streamID)) {
issueStreamError(ctx, streamID, SpdyStreamStatus.PROTOCOL_ERROR); issueStreamError(ctx, streamID, SpdyStreamStatus.PROTOCOL_ERROR, out);
return; return;
} }
@ -256,7 +243,7 @@ public class SpdySessionHandler
boolean remoteSideClosed = spdySynStreamFrame.isLast(); boolean remoteSideClosed = spdySynStreamFrame.isLast();
boolean localSideClosed = spdySynStreamFrame.isUnidirectional(); boolean localSideClosed = spdySynStreamFrame.isUnidirectional();
if (!acceptStream(streamID, priority, remoteSideClosed, localSideClosed)) { if (!acceptStream(streamID, priority, remoteSideClosed, localSideClosed)) {
issueStreamError(ctx, streamID, SpdyStreamStatus.REFUSED_STREAM); issueStreamError(ctx, streamID, SpdyStreamStatus.REFUSED_STREAM, out);
return; return;
} }
@ -276,13 +263,13 @@ public class SpdySessionHandler
if (spdySynReplyFrame.isInvalid() || if (spdySynReplyFrame.isInvalid() ||
isRemoteInitiatedID(streamID) || isRemoteInitiatedID(streamID) ||
spdySession.isRemoteSideClosed(streamID)) { spdySession.isRemoteSideClosed(streamID)) {
issueStreamError(ctx, streamID, SpdyStreamStatus.INVALID_STREAM); issueStreamError(ctx, streamID, SpdyStreamStatus.INVALID_STREAM, out);
return; return;
} }
// Check if we have received multiple frames for the same Stream-ID // Check if we have received multiple frames for the same Stream-ID
if (spdySession.hasReceivedReply(streamID)) { if (spdySession.hasReceivedReply(streamID)) {
issueStreamError(ctx, streamID, SpdyStreamStatus.STREAM_IN_USE); issueStreamError(ctx, streamID, SpdyStreamStatus.STREAM_IN_USE, out);
return; return;
} }
@ -368,12 +355,12 @@ public class SpdySessionHandler
// Check if we received a valid HEADERS frame // Check if we received a valid HEADERS frame
if (spdyHeadersFrame.isInvalid()) { if (spdyHeadersFrame.isInvalid()) {
issueStreamError(ctx, streamID, SpdyStreamStatus.PROTOCOL_ERROR); issueStreamError(ctx, streamID, SpdyStreamStatus.PROTOCOL_ERROR, out);
return; return;
} }
if (spdySession.isRemoteSideClosed(streamID)) { if (spdySession.isRemoteSideClosed(streamID)) {
issueStreamError(ctx, streamID, SpdyStreamStatus.INVALID_STREAM); issueStreamError(ctx, streamID, SpdyStreamStatus.INVALID_STREAM, out);
return; return;
} }
@ -406,15 +393,15 @@ public class SpdySessionHandler
// Check for numerical overflow // Check for numerical overflow
if (spdySession.getSendWindowSize(streamID) > Integer.MAX_VALUE - deltaWindowSize) { if (spdySession.getSendWindowSize(streamID) > Integer.MAX_VALUE - deltaWindowSize) {
issueStreamError(ctx, streamID, SpdyStreamStatus.FLOW_CONTROL_ERROR); issueStreamError(ctx, streamID, SpdyStreamStatus.FLOW_CONTROL_ERROR, out);
return; return;
} }
updateSendWindowSize(ctx, streamID, deltaWindowSize); updateSendWindowSize(streamID, deltaWindowSize, out);
} }
} }
ctx.nextInboundMessageBuffer().add(msg); out.add(msg);
} }
@Override @Override
@ -432,10 +419,10 @@ public class SpdySessionHandler
} }
@Override @Override
public void flush(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception { public void write(ChannelHandlerContext ctx, MessageList<Object> msgs, ChannelPromise promise) throws Exception {
MessageBuf<Object> in = ctx.outboundMessageBuffer(); MessageList<Object> out = MessageList.newInstance();
for (;;) { for (int i = 0; i < msgs.size(); i++) {
Object msg = in.poll(); Object msg = msgs.get(i);
if (msg == null) { if (msg == null) {
break; break;
} }
@ -449,24 +436,26 @@ public class SpdySessionHandler
msg instanceof SpdyHeadersFrame || msg instanceof SpdyHeadersFrame ||
msg instanceof SpdyWindowUpdateFrame) { msg instanceof SpdyWindowUpdateFrame) {
try { try {
handleOutboundMessage(ctx, msg); handleOutboundMessage(ctx, msg, out);
} catch (SpdyProtocolException e) { } catch (SpdyProtocolException e) {
if (e == PROTOCOL_EXCEPTION) { 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 // See #1211
promise.setFailure(PROTOCOL_EXCEPTION); promise.setFailure(PROTOCOL_EXCEPTION);
return; return;
} }
} }
} else { } 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<Object> out)
throws Exception {
if (msg instanceof SpdyDataFrame) { if (msg instanceof SpdyDataFrame) {
SpdyDataFrame spdyDataFrame = (SpdyDataFrame) msg; SpdyDataFrame spdyDataFrame = (SpdyDataFrame) msg;
@ -656,7 +645,7 @@ public class SpdySessionHandler
throw PROTOCOL_EXCEPTION; throw PROTOCOL_EXCEPTION;
} }
ctx.nextOutboundMessageBuffer().add(msg); out.add(msg);
} }
/* /*
@ -671,8 +660,7 @@ public class SpdySessionHandler
private void issueSessionError( private void issueSessionError(
ChannelHandlerContext ctx, SpdySessionStatus status) { ChannelHandlerContext ctx, SpdySessionStatus status) {
sendGoAwayFrame(ctx, status); sendGoAwayFrame(ctx, status).addListener(ChannelFutureListener.CLOSE);
ctx.flush().addListener(ChannelFutureListener.CLOSE);
} }
/* /*
@ -687,7 +675,7 @@ public class SpdySessionHandler
* Note: this is only called by the worker thread * Note: this is only called by the worker thread
*/ */
private void issueStreamError( private void issueStreamError(
ChannelHandlerContext ctx, int streamID, SpdyStreamStatus status) { ChannelHandlerContext ctx, int streamID, SpdyStreamStatus status, MessageList<Object> in) {
boolean fireMessageReceived = !spdySession.isRemoteSideClosed(streamID); boolean fireMessageReceived = !spdySession.isRemoteSideClosed(streamID);
removeStream(ctx, streamID); removeStream(ctx, streamID);
@ -695,8 +683,11 @@ public class SpdySessionHandler
SpdyRstStreamFrame spdyRstStreamFrame = new DefaultSpdyRstStreamFrame(streamID, status); SpdyRstStreamFrame spdyRstStreamFrame = new DefaultSpdyRstStreamFrame(streamID, status);
ctx.write(spdyRstStreamFrame); ctx.write(spdyRstStreamFrame);
if (fireMessageReceived) { if (fireMessageReceived) {
ctx.nextInboundMessageBuffer().add(spdyRstStreamFrame); in.add(spdyRstStreamFrame);
ctx.fireInboundBufferUpdated(); 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<Object> out) {
synchronized (flowControlLock) { synchronized (flowControlLock) {
int newWindowSize = spdySession.updateSendWindowSize(streamID, deltaWindowSize); int newWindowSize = spdySession.updateSendWindowSize(streamID, deltaWindowSize);
@ -832,7 +823,7 @@ public class SpdySessionHandler
halfCloseStream(streamID, false); halfCloseStream(streamID, false);
} }
ctx.nextOutboundMessageBuffer().add(spdyDataFrame); out.add(spdyDataFrame);
} else { } else {
// We can send a partial frame // We can send a partial frame
spdySession.updateSendWindowSize(streamID, -1 * newWindowSize); spdySession.updateSendWindowSize(streamID, -1 * newWindowSize);
@ -858,7 +849,7 @@ public class SpdySessionHandler
// } // }
//}); //});
ctx.nextOutboundMessageBuffer().add(partialDataFrame); out.add(partialDataFrame);
newWindowSize = 0; newWindowSize = 0;
} }
@ -873,8 +864,7 @@ public class SpdySessionHandler
return; return;
} }
sendGoAwayFrame(ctx, SpdySessionStatus.OK); ChannelFuture f = sendGoAwayFrame(ctx, SpdySessionStatus.OK);
ChannelFuture f = ctx.flush();
if (spdySession.noActiveStreams()) { if (spdySession.noActiveStreams()) {
f.addListener(new ClosingChannelFutureListener(ctx, future)); f.addListener(new ClosingChannelFutureListener(ctx, future));
} else { } else {
@ -884,12 +874,14 @@ public class SpdySessionHandler
// FIXME: Close the connection forcibly after timeout. // FIXME: Close the connection forcibly after timeout.
} }
private synchronized void sendGoAwayFrame( private synchronized ChannelFuture sendGoAwayFrame(
ChannelHandlerContext ctx, SpdySessionStatus status) { ChannelHandlerContext ctx, SpdySessionStatus status) {
if (!sentGoAwayFrame) { if (!sentGoAwayFrame) {
sentGoAwayFrame = true; sentGoAwayFrame = true;
SpdyGoAwayFrame spdyGoAwayFrame = new DefaultSpdyGoAwayFrame(lastGoodStreamId, status); SpdyGoAwayFrame spdyGoAwayFrame = new DefaultSpdyGoAwayFrame(lastGoodStreamId, status);
ctx.nextOutboundMessageBuffer().add(spdyGoAwayFrame); return ctx.write(spdyGoAwayFrame);
} else {
return ctx.newSucceededFuture();
} }
} }

View File

@ -16,7 +16,7 @@
package io.netty.handler.codec.http; package io.netty.handler.codec.http;
import io.netty.buffer.Unpooled; 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.CodecException;
import io.netty.handler.codec.PrematureChannelClosureException; import io.netty.handler.codec.PrematureChannelClosureException;
import io.netty.util.CharsetUtil; import io.netty.util.CharsetUtil;
@ -37,7 +37,7 @@ public class HttpClientCodecTest {
@Test @Test
public void testFailsNotOnRequestResponse() { public void testFailsNotOnRequestResponse() {
HttpClientCodec codec = new HttpClientCodec(4096, 8192, 8192, true); 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.writeOutbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "http://localhost/"));
ch.writeInbound(Unpooled.copiedBuffer(RESPONSE, CharsetUtil.ISO_8859_1)); ch.writeInbound(Unpooled.copiedBuffer(RESPONSE, CharsetUtil.ISO_8859_1));
@ -47,7 +47,7 @@ public class HttpClientCodecTest {
@Test @Test
public void testFailsNotOnRequestResponseChunked() { public void testFailsNotOnRequestResponseChunked() {
HttpClientCodec codec = new HttpClientCodec(4096, 8192, 8192, true); 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.writeOutbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "http://localhost/"));
ch.writeInbound(Unpooled.copiedBuffer(CHUNKED_RESPONSE, CharsetUtil.ISO_8859_1)); ch.writeInbound(Unpooled.copiedBuffer(CHUNKED_RESPONSE, CharsetUtil.ISO_8859_1));
@ -57,7 +57,7 @@ public class HttpClientCodecTest {
@Test @Test
public void testFailsOnMissingResponse() { public void testFailsOnMissingResponse() {
HttpClientCodec codec = new HttpClientCodec(4096, 8192, 8192, true); 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, assertTrue(ch.writeOutbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET,
"http://localhost/"))); "http://localhost/")));
@ -74,7 +74,7 @@ public class HttpClientCodecTest {
@Test @Test
public void testFailsOnIncompleteChunkedResponse() { public void testFailsOnIncompleteChunkedResponse() {
HttpClientCodec codec = new HttpClientCodec(4096, 8192, 8192, true); 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.writeOutbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "http://localhost/"));
ch.writeInbound(Unpooled.copiedBuffer(INCOMPLETE_CHUNKED_RESPONSE, CharsetUtil.ISO_8859_1)); ch.writeInbound(Unpooled.copiedBuffer(INCOMPLETE_CHUNKED_RESPONSE, CharsetUtil.ISO_8859_1));

View File

@ -15,7 +15,7 @@
*/ */
package io.netty.handler.codec.http; 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.compression.ZlibWrapper;
import io.netty.handler.codec.http.HttpHeaders.Names; import io.netty.handler.codec.http.HttpHeaders.Names;
import org.junit.Test; import org.junit.Test;
@ -63,7 +63,7 @@ public class HttpContentCompressorTest {
@Test @Test
public void testEmptyContentCompression() throws Exception { 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, "/"); FullHttpRequest req = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/");
req.headers().set(Names.ACCEPT_ENCODING, "deflate"); req.headers().set(Names.ACCEPT_ENCODING, "deflate");
ch.writeInbound(req); ch.writeInbound(req);

View File

@ -19,9 +19,8 @@ package io.netty.handler.codec.http;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.embedded.EmbeddedByteChannel; import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.channel.embedded.EmbeddedMessageChannel; import io.netty.handler.codec.MessageToByteEncoder;
import io.netty.handler.codec.ByteToByteEncoder;
import io.netty.handler.codec.http.HttpHeaders.Names; import io.netty.handler.codec.http.HttpHeaders.Names;
import io.netty.handler.codec.http.HttpHeaders.Values; import io.netty.handler.codec.http.HttpHeaders.Values;
import io.netty.util.CharsetUtil; import io.netty.util.CharsetUtil;
@ -35,9 +34,9 @@ public class HttpContentEncoderTest {
private static final class TestEncoder extends HttpContentEncoder { private static final class TestEncoder extends HttpContentEncoder {
@Override @Override
protected Result beginEncode(HttpResponse headers, String acceptEncoding) { protected Result beginEncode(HttpResponse headers, String acceptEncoding) {
return new Result("test", new EmbeddedByteChannel(new ByteToByteEncoder() { return new Result("test", new EmbeddedChannel(new MessageToByteEncoder<ByteBuf>() {
@Override @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)); out.writeBytes(String.valueOf(in.readableBytes()).getBytes(CharsetUtil.US_ASCII));
in.skipBytes(in.readableBytes()); in.skipBytes(in.readableBytes());
} }
@ -47,7 +46,7 @@ public class HttpContentEncoderTest {
@Test @Test
public void testSplitContent() throws Exception { 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.writeInbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/"));
ch.writeOutbound(new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK)); ch.writeOutbound(new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK));
@ -71,7 +70,7 @@ public class HttpContentEncoderTest {
@Test @Test
public void testChunkedContent() throws Exception { 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, "/")); ch.writeInbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/"));
HttpResponse res = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); HttpResponse res = new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
@ -98,7 +97,7 @@ public class HttpContentEncoderTest {
@Test @Test
public void testFullContent() throws Exception { 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, "/")); ch.writeInbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/"));
FullHttpResponse res = new DefaultFullHttpResponse( 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 * If the length of the content is unknown, {@link HttpContentEncoder} should not skip encoding the content
* actual length is turned out to be 0. * even if the actual length is turned out to be 0.
*/ */
@Test @Test
public void testEmptySplitContent() throws Exception { 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.writeInbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/"));
ch.writeOutbound(new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK)); ch.writeOutbound(new DefaultHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK));
@ -129,7 +128,7 @@ public class HttpContentEncoderTest {
ch.writeOutbound(LastHttpContent.EMPTY_LAST_CONTENT); ch.writeOutbound(LastHttpContent.EMPTY_LAST_CONTENT);
HttpContent chunk = (HttpContent) ch.readOutbound(); 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(chunk, is(instanceOf(LastHttpContent.class)));
assertThat(ch.readOutbound(), is(nullValue())); assertThat(ch.readOutbound(), is(nullValue()));
} }
@ -139,7 +138,7 @@ public class HttpContentEncoderTest {
*/ */
@Test @Test
public void testEmptyFullContent() throws Exception { 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, "/")); ch.writeInbound(new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, "/"));
FullHttpResponse res = new DefaultFullHttpResponse( FullHttpResponse res = new DefaultFullHttpResponse(
@ -162,7 +161,7 @@ public class HttpContentEncoderTest {
assertThat(ch.readOutbound(), is(nullValue())); assertThat(ch.readOutbound(), is(nullValue()));
} }
private static void assertEncodedResponse(EmbeddedMessageChannel ch) { private static void assertEncodedResponse(EmbeddedChannel ch) {
Object o = ch.readOutbound(); Object o = ch.readOutbound();
assertThat(o, is(instanceOf(HttpResponse.class))); assertThat(o, is(instanceOf(HttpResponse.class)));

View File

@ -17,7 +17,7 @@ package io.netty.handler.codec.http;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; 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.handler.codec.DecoderResult;
import io.netty.util.CharsetUtil; import io.netty.util.CharsetUtil;
import org.junit.Test; import org.junit.Test;
@ -32,7 +32,7 @@ public class HttpInvalidMessageTest {
@Test @Test
public void testRequestWithBadInitialLine() throws Exception { 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)); ch.writeInbound(Unpooled.copiedBuffer("GET / HTTP/1.0 with extra\r\n", CharsetUtil.UTF_8));
HttpRequest req = (HttpRequest) ch.readInbound(); HttpRequest req = (HttpRequest) ch.readInbound();
DecoderResult dr = req.getDecoderResult(); DecoderResult dr = req.getDecoderResult();
@ -43,7 +43,7 @@ public class HttpInvalidMessageTest {
@Test @Test
public void testRequestWithBadHeader() throws Exception { 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("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("Good_Name: Good Value\r\n", CharsetUtil.UTF_8));
ch.writeInbound(Unpooled.copiedBuffer("Bad=Name: Bad 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 @Test
public void testResponseWithBadInitialLine() throws Exception { 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)); ch.writeInbound(Unpooled.copiedBuffer("HTTP/1.0 BAD_CODE Bad Server\r\n", CharsetUtil.UTF_8));
HttpResponse res = (HttpResponse) ch.readInbound(); HttpResponse res = (HttpResponse) ch.readInbound();
DecoderResult dr = res.getDecoderResult(); DecoderResult dr = res.getDecoderResult();
@ -70,7 +70,7 @@ public class HttpInvalidMessageTest {
@Test @Test
public void testResponseWithBadHeader() throws Exception { 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("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("Good_Name: Good Value\r\n", CharsetUtil.UTF_8));
ch.writeInbound(Unpooled.copiedBuffer("Bad=Name: Bad 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 @Test
public void testBadChunk() throws Exception { 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("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("Transfer-Encoding: chunked\r\n\r\n", CharsetUtil.UTF_8));
ch.writeInbound(Unpooled.copiedBuffer("BAD_LENGTH\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); 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. // Generate a lot of random traffic to ensure that it's discarded silently.
byte[] data = new byte[1048576]; byte[] data = new byte[1048576];
rnd.nextBytes(data); rnd.nextBytes(data);
@ -109,9 +109,10 @@ public class HttpInvalidMessageTest {
ByteBuf buf = Unpooled.wrappedBuffer(data); ByteBuf buf = Unpooled.wrappedBuffer(data);
for (int i = 0; i < 4096; i ++) { for (int i = 0; i < 4096; i ++) {
buf.setIndex(0, data.length); buf.setIndex(0, data.length);
ch.writeInbound(buf); ch.writeInbound(buf.retain());
ch.checkException(); ch.checkException();
assertNull(ch.readInbound()); assertNull(ch.readInbound());
} }
buf.release();
} }
} }

View File

@ -19,7 +19,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.buffer.CompositeByteBuf; import io.netty.buffer.CompositeByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext; 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.handler.codec.TooLongFrameException;
import io.netty.util.CharsetUtil; import io.netty.util.CharsetUtil;
import org.easymock.EasyMock; import org.easymock.EasyMock;
@ -34,7 +34,7 @@ public class HttpObjectAggregatorTest {
@Test @Test
public void testAggregate() { public void testAggregate() {
HttpObjectAggregator aggr = new HttpObjectAggregator(1024 * 1024); HttpObjectAggregator aggr = new HttpObjectAggregator(1024 * 1024);
EmbeddedMessageChannel embedder = new EmbeddedMessageChannel(aggr); EmbeddedChannel embedder = new EmbeddedChannel(aggr);
HttpRequest message = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpRequest message = new DefaultHttpRequest(HttpVersion.HTTP_1_1,
HttpMethod.GET, "http://localhost"); HttpMethod.GET, "http://localhost");
@ -73,7 +73,7 @@ public class HttpObjectAggregatorTest {
@Test @Test
public void testAggregateWithTrailer() { public void testAggregateWithTrailer() {
HttpObjectAggregator aggr = new HttpObjectAggregator(1024 * 1024); HttpObjectAggregator aggr = new HttpObjectAggregator(1024 * 1024);
EmbeddedMessageChannel embedder = new EmbeddedMessageChannel(aggr); EmbeddedChannel embedder = new EmbeddedChannel(aggr);
HttpRequest message = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpRequest message = new DefaultHttpRequest(HttpVersion.HTTP_1_1,
HttpMethod.GET, "http://localhost"); HttpMethod.GET, "http://localhost");
HttpHeaders.setHeader(message, "X-Test", true); HttpHeaders.setHeader(message, "X-Test", true);
@ -105,7 +105,7 @@ public class HttpObjectAggregatorTest {
@Test @Test
public void testTooLongFrameException() { public void testTooLongFrameException() {
HttpObjectAggregator aggr = new HttpObjectAggregator(4); HttpObjectAggregator aggr = new HttpObjectAggregator(4);
EmbeddedMessageChannel embedder = new EmbeddedMessageChannel(aggr); EmbeddedChannel embedder = new EmbeddedChannel(aggr);
HttpRequest message = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpRequest message = new DefaultHttpRequest(HttpVersion.HTTP_1_1,
HttpMethod.GET, "http://localhost"); HttpMethod.GET, "http://localhost");
HttpContent chunk1 = new DefaultHttpContent(Unpooled.copiedBuffer("test", CharsetUtil.US_ASCII)); HttpContent chunk1 = new DefaultHttpContent(Unpooled.copiedBuffer("test", CharsetUtil.US_ASCII));
@ -159,7 +159,7 @@ public class HttpObjectAggregatorTest {
@Test @Test
public void testAggregateTransferEncodingChunked() { public void testAggregateTransferEncodingChunked() {
HttpObjectAggregator aggr = new HttpObjectAggregator(1024 * 1024); HttpObjectAggregator aggr = new HttpObjectAggregator(1024 * 1024);
EmbeddedMessageChannel embedder = new EmbeddedMessageChannel(aggr); EmbeddedChannel embedder = new EmbeddedChannel(aggr);
HttpRequest message = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpRequest message = new DefaultHttpRequest(HttpVersion.HTTP_1_1,
HttpMethod.GET, "http://localhost"); HttpMethod.GET, "http://localhost");

View File

@ -17,7 +17,7 @@ package io.netty.handler.codec.http;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.embedded.EmbeddedByteChannel; import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.util.CharsetUtil; import io.netty.util.CharsetUtil;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
@ -32,10 +32,11 @@ public class HttpServerCodecTest {
int maxChunkSize = 2000; int maxChunkSize = 2000;
HttpServerCodec httpServerCodec = new HttpServerCodec(1000, 1000, maxChunkSize); HttpServerCodec httpServerCodec = new HttpServerCodec(1000, 1000, maxChunkSize);
EmbeddedByteChannel decoderEmbedder = new EmbeddedByteChannel(httpServerCodec); EmbeddedChannel decoderEmbedder = new EmbeddedChannel(httpServerCodec);
int totalContentLength = maxChunkSize * 5; 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" + "Content-Length: " + totalContentLength + "\r\n" +
"\r\n", CharsetUtil.UTF_8)); "\r\n", CharsetUtil.UTF_8));

View File

@ -17,7 +17,7 @@ package io.netty.handler.codec.http.websocketx;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; 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.handler.codec.TooLongFrameException;
import io.netty.util.CharsetUtil; import io.netty.util.CharsetUtil;
import org.junit.Assert; import org.junit.Assert;
@ -32,7 +32,7 @@ public class WebSocketFrameAggregatorTest {
.writeBytes(content2.duplicate()).writeBytes(content3.duplicate()); .writeBytes(content2.duplicate()).writeBytes(content3.duplicate());
@Test @Test
public void testAggregationBinary() { 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(true, 1, content1.copy()));
channel.writeInbound(new BinaryWebSocketFrame(false, 0, content1.copy())); channel.writeInbound(new BinaryWebSocketFrame(false, 0, content1.copy()));
channel.writeInbound(new ContinuationWebSocketFrame(false, 0, content2.copy())); channel.writeInbound(new ContinuationWebSocketFrame(false, 0, content2.copy()));
@ -42,7 +42,7 @@ public class WebSocketFrameAggregatorTest {
Assert.assertTrue(channel.finish()); Assert.assertTrue(channel.finish());
System.out.println(channel.lastInboundMessageBuffer().size()); System.out.println(channel.lastInboundBuffer().size());
BinaryWebSocketFrame frame = (BinaryWebSocketFrame) channel.readInbound(); BinaryWebSocketFrame frame = (BinaryWebSocketFrame) channel.readInbound();
Assert.assertTrue(frame.isFinalFragment()); Assert.assertTrue(frame.isFinalFragment());
Assert.assertEquals(1, frame.rsv()); Assert.assertEquals(1, frame.rsv());
@ -68,7 +68,7 @@ public class WebSocketFrameAggregatorTest {
@Test @Test
public void testAggregationText() { 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(true, 1, content1.copy()));
channel.writeInbound(new TextWebSocketFrame(false, 0, content1.copy())); channel.writeInbound(new TextWebSocketFrame(false, 0, content1.copy()));
channel.writeInbound(new ContinuationWebSocketFrame(false, 0, content2.copy())); channel.writeInbound(new ContinuationWebSocketFrame(false, 0, content2.copy()));
@ -103,7 +103,7 @@ public class WebSocketFrameAggregatorTest {
@Test @Test
public void textFrameTooBig() { 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(true, 1, content1.copy()));
channel.writeInbound(new BinaryWebSocketFrame(false, 0, content1.copy())); channel.writeInbound(new BinaryWebSocketFrame(false, 0, content1.copy()));
try { try {

View File

@ -17,7 +17,7 @@ package io.netty.handler.codec.http.websocketx;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; 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.DefaultFullHttpRequest;
import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpHeaders.Names; import io.netty.handler.codec.http.HttpHeaders.Names;
@ -39,7 +39,7 @@ public class WebSocketServerHandshaker00Test {
@Test @Test
public void testPerformOpeningHandshake() { public void testPerformOpeningHandshake() {
EmbeddedByteChannel ch = new EmbeddedByteChannel( EmbeddedChannel ch = new EmbeddedChannel(
new HttpObjectAggregator(42), new HttpRequestDecoder(), new HttpResponseEncoder()); new HttpObjectAggregator(42), new HttpRequestDecoder(), new HttpResponseEncoder());
FullHttpRequest req = new DefaultFullHttpRequest( FullHttpRequest req = new DefaultFullHttpRequest(
@ -56,9 +56,9 @@ public class WebSocketServerHandshaker00Test {
new WebSocketServerHandshaker00( new WebSocketServerHandshaker00(
"ws://example.com/chat", "chat", Integer.MAX_VALUE).handshake(ch, req); "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); ch2.writeInbound(resBuf);
HttpResponse res = (HttpResponse) ch2.readInbound(); HttpResponse res = (HttpResponse) ch2.readInbound();

View File

@ -16,7 +16,7 @@
package io.netty.handler.codec.http.websocketx; package io.netty.handler.codec.http.websocketx;
import io.netty.buffer.ByteBuf; 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.DefaultFullHttpRequest;
import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpHeaders.Names; import io.netty.handler.codec.http.HttpHeaders.Names;
@ -36,7 +36,7 @@ public class WebSocketServerHandshaker08Test {
@Test @Test
public void testPerformOpeningHandshake() { public void testPerformOpeningHandshake() {
EmbeddedByteChannel ch = new EmbeddedByteChannel( EmbeddedChannel ch = new EmbeddedChannel(
new HttpObjectAggregator(42), new HttpRequestDecoder(), new HttpResponseEncoder()); new HttpObjectAggregator(42), new HttpRequestDecoder(), new HttpResponseEncoder());
FullHttpRequest req = new DefaultFullHttpRequest(HTTP_1_1, HttpMethod.GET, "/chat"); FullHttpRequest req = new DefaultFullHttpRequest(HTTP_1_1, HttpMethod.GET, "/chat");
@ -51,9 +51,9 @@ public class WebSocketServerHandshaker08Test {
new WebSocketServerHandshaker08( new WebSocketServerHandshaker08(
"ws://example.com/chat", "chat", false, Integer.MAX_VALUE).handshake(ch, req); "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); ch2.writeInbound(resBuf);
HttpResponse res = (HttpResponse) ch2.readInbound(); HttpResponse res = (HttpResponse) ch2.readInbound();

View File

@ -16,7 +16,7 @@
package io.netty.handler.codec.http.websocketx; package io.netty.handler.codec.http.websocketx;
import io.netty.buffer.ByteBuf; 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.DefaultFullHttpRequest;
import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpHeaders.Names; import io.netty.handler.codec.http.HttpHeaders.Names;
@ -36,7 +36,7 @@ public class WebSocketServerHandshaker13Test {
@Test @Test
public void testPerformOpeningHandshake() { public void testPerformOpeningHandshake() {
EmbeddedByteChannel ch = new EmbeddedByteChannel( EmbeddedChannel ch = new EmbeddedChannel(
new HttpObjectAggregator(42), new HttpRequestDecoder(), new HttpResponseEncoder()); new HttpObjectAggregator(42), new HttpRequestDecoder(), new HttpResponseEncoder());
FullHttpRequest req = new DefaultFullHttpRequest(HTTP_1_1, HttpMethod.GET, "/chat"); FullHttpRequest req = new DefaultFullHttpRequest(HTTP_1_1, HttpMethod.GET, "/chat");
@ -51,9 +51,9 @@ public class WebSocketServerHandshaker13Test {
new WebSocketServerHandshaker13( new WebSocketServerHandshaker13(
"ws://example.com/chat", "chat", false, Integer.MAX_VALUE).handshake(ch, req); "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); ch2.writeInbound(resBuf);
HttpResponse res = (HttpResponse) ch2.readInbound(); HttpResponse res = (HttpResponse) ch2.readInbound();

View File

@ -15,25 +15,26 @@
*/ */
package io.netty.handler.codec.http.websocketx; 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.ChannelHandler;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundMessageHandlerAdapter; import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelOperationHandlerAdapter; import io.netty.channel.ChannelOutboundHandlerAdapter;
import io.netty.channel.ChannelOutboundMessageHandler;
import io.netty.channel.ChannelPromise; 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.DefaultFullHttpRequest;
import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpRequest; import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpRequestDecoder; import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseEncoder; import io.netty.handler.codec.http.HttpResponseEncoder;
import org.junit.Before;
import org.junit.Test; 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.HttpHeaders.Values.*;
import static io.netty.handler.codec.http.HttpResponseStatus.*; import static io.netty.handler.codec.http.HttpResponseStatus.*;
import static io.netty.handler.codec.http.HttpVersion.*; import static io.netty.handler.codec.http.HttpVersion.*;
@ -41,29 +42,36 @@ import static org.junit.Assert.*;
public class WebSocketServerProtocolHandlerTest { public class WebSocketServerProtocolHandlerTest {
private final Queue<FullHttpResponse> responses = new ArrayDeque<FullHttpResponse>();
@Before
public void setUp() {
responses.clear();
}
@Test @Test
public void testHttpUpgradeRequest() throws Exception { public void testHttpUpgradeRequest() throws Exception {
EmbeddedMessageChannel ch = createChannel(new MockOutboundHandler()); EmbeddedChannel ch = createChannel(new MockOutboundHandler());
ChannelHandlerContext handshakerCtx = ch.pipeline().context(WebSocketServerProtocolHandshakeHandler.class); ChannelHandlerContext handshakerCtx = ch.pipeline().context(WebSocketServerProtocolHandshakeHandler.class);
writeUpgradeRequest(ch); writeUpgradeRequest(ch);
assertEquals(SWITCHING_PROTOCOLS, ((HttpResponse) ch.outboundMessageBuffer().poll()).getStatus()); assertEquals(SWITCHING_PROTOCOLS, responses.remove().getStatus());
assertNotNull(WebSocketServerProtocolHandler.getHandshaker(handshakerCtx)); assertNotNull(WebSocketServerProtocolHandler.getHandshaker(handshakerCtx));
} }
@Test @Test
public void testSubsequentHttpRequestsAfterUpgradeShouldReturn403() throws Exception { public void testSubsequentHttpRequestsAfterUpgradeShouldReturn403() throws Exception {
EmbeddedMessageChannel ch = createChannel(new MockOutboundHandler()); EmbeddedChannel ch = createChannel();
writeUpgradeRequest(ch); 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")); ch.writeInbound(new DefaultFullHttpRequest(HTTP_1_1, HttpMethod.GET, "/test"));
assertEquals(FORBIDDEN, ((HttpResponse) ch.outboundMessageBuffer().poll()).getStatus()); assertEquals(FORBIDDEN, responses.remove().getStatus());
} }
@Test @Test
public void testHttpUpgradeRequestInvalidUpgradeHeader() { public void testHttpUpgradeRequestInvalidUpgradeHeader() {
EmbeddedMessageChannel ch = createChannel(); EmbeddedChannel ch = createChannel();
FullHttpRequest httpRequestWithEntity = new WebSocketRequestBuilder().httpVersion(HTTP_1_1) FullHttpRequest httpRequestWithEntity = new WebSocketRequestBuilder().httpVersion(HTTP_1_1)
.method(HttpMethod.GET) .method(HttpMethod.GET)
.uri("/test") .uri("/test")
@ -74,14 +82,14 @@ public class WebSocketServerProtocolHandlerTest {
ch.writeInbound(httpRequestWithEntity); ch.writeInbound(httpRequestWithEntity);
FullHttpResponse response = getHttpResponse(ch); FullHttpResponse response = responses.remove();
assertEquals(BAD_REQUEST, response.getStatus()); assertEquals(BAD_REQUEST, response.getStatus());
assertEquals("not a WebSocket handshake request: missing upgrade", getResponseMessage(response)); assertEquals("not a WebSocket handshake request: missing upgrade", getResponseMessage(response));
} }
@Test @Test
public void testHttpUpgradeRequestMissingWSKeyHeader() { public void testHttpUpgradeRequestMissingWSKeyHeader() {
EmbeddedMessageChannel ch = createChannel(); EmbeddedChannel ch = createChannel();
HttpRequest httpRequest = new WebSocketRequestBuilder().httpVersion(HTTP_1_1) HttpRequest httpRequest = new WebSocketRequestBuilder().httpVersion(HTTP_1_1)
.method(HttpMethod.GET) .method(HttpMethod.GET)
.uri("/test") .uri("/test")
@ -93,7 +101,7 @@ public class WebSocketServerProtocolHandlerTest {
ch.writeInbound(httpRequest); ch.writeInbound(httpRequest);
FullHttpResponse response = getHttpResponse(ch); FullHttpResponse response = responses.remove();
assertEquals(BAD_REQUEST, response.getStatus()); assertEquals(BAD_REQUEST, response.getStatus());
assertEquals("not a WebSocket request: missing key", getResponseMessage(response)); assertEquals("not a WebSocket request: missing key", getResponseMessage(response));
} }
@ -101,22 +109,26 @@ public class WebSocketServerProtocolHandlerTest {
@Test @Test
public void testHandleTextFrame() { public void testHandleTextFrame() {
CustomTextFrameHandler customTextFrameHandler = new CustomTextFrameHandler(); CustomTextFrameHandler customTextFrameHandler = new CustomTextFrameHandler();
EmbeddedMessageChannel ch = createChannel(customTextFrameHandler); EmbeddedChannel ch = createChannel(customTextFrameHandler);
writeUpgradeRequest(ch); 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")); ch.writeInbound(new TextWebSocketFrame("payload"));
assertEquals("processed: payload", customTextFrameHandler.getContent()); assertEquals("processed: payload", customTextFrameHandler.getContent());
} }
private static EmbeddedMessageChannel createChannel() { private EmbeddedChannel createChannel() {
return createChannel(null); return createChannel(null);
} }
private static EmbeddedMessageChannel createChannel(ChannelHandler handler) { private EmbeddedChannel createChannel(ChannelHandler handler) {
return new EmbeddedMessageChannel( return new EmbeddedChannel(
new WebSocketServerProtocolHandler("/test", null, false), new WebSocketServerProtocolHandler("/test", null, false),
new HttpRequestDecoder(), new HttpRequestDecoder(),
new HttpResponseEncoder(), new HttpResponseEncoder(),
@ -124,7 +136,7 @@ public class WebSocketServerProtocolHandlerTest {
handler); handler);
} }
private static void writeUpgradeRequest(EmbeddedMessageChannel ch) { private static void writeUpgradeRequest(EmbeddedChannel ch) {
ch.writeInbound(WebSocketRequestBuilder.sucessful()); ch.writeInbound(WebSocketRequestBuilder.sucessful());
} }
@ -132,31 +144,25 @@ public class WebSocketServerProtocolHandlerTest {
return new String(response.content().array()); return new String(response.content().array());
} }
private static FullHttpResponse getHttpResponse(EmbeddedMessageChannel ch) { private class MockOutboundHandler extends ChannelOutboundHandlerAdapter {
MessageBuf<Object> outbound = ch.pipeline().context(MockOutboundHandler.class).outboundMessageBuffer();
return (FullHttpResponse) outbound.poll();
}
private static class MockOutboundHandler
extends ChannelOperationHandlerAdapter implements ChannelOutboundMessageHandler<Object> {
@Override @Override
public MessageBuf<Object> newOutboundBuffer(ChannelHandlerContext ctx) throws Exception { public void write(ChannelHandlerContext ctx, MessageList<Object> msgs, ChannelPromise promise)
return Unpooled.messageBuffer(); throws Exception {
} for (int i = 0; i < msgs.size(); i++) {
responses.add((FullHttpResponse) msgs.get(i));
@Override }
public void flush(ChannelHandlerContext ctx, ChannelPromise future) throws Exception { promise.setSuccess();
//NoOp
} }
} }
private static class CustomTextFrameHandler extends ChannelInboundMessageHandlerAdapter<TextWebSocketFrame> { private static class CustomTextFrameHandler extends ChannelInboundHandlerAdapter {
private String content; private String content;
@Override @Override
public void messageReceived(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception { public void messageReceived(ChannelHandlerContext ctx, MessageList<Object> msgs) throws Exception {
content = "processed: " + msg.text(); assertEquals(1, msgs.size());
content = "processed: " + ((TextWebSocketFrame) msgs.get(0)).text();
} }
String getContent() { String getContent() {

View File

@ -16,8 +16,9 @@
package io.netty.handler.codec.spdy; package io.netty.handler.codec.spdy;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundMessageHandlerAdapter; import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.embedded.EmbeddedMessageChannel; import io.netty.channel.MessageList;
import io.netty.channel.embedded.EmbeddedChannel;
import io.netty.util.internal.logging.InternalLogger; import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory; import io.netty.util.internal.logging.InternalLoggerFactory;
import org.junit.Test; import org.junit.Test;
@ -99,7 +100,7 @@ public class SpdySessionHandlerTest {
} }
private static void testSpdySessionHandler(int version, boolean server) { private static void testSpdySessionHandler(int version, boolean server) {
EmbeddedMessageChannel sessionHandler = new EmbeddedMessageChannel( EmbeddedChannel sessionHandler = new EmbeddedChannel(
new SpdySessionHandler(version, server), new EchoHandler(closeSignal, server)); new SpdySessionHandler(version, server), new EchoHandler(closeSignal, server));
while (sessionHandler.readOutbound() != null) { while (sessionHandler.readOutbound() != null) {
@ -278,7 +279,7 @@ public class SpdySessionHandlerTest {
// Echo Handler opens 4 half-closed streams on session connection // Echo Handler opens 4 half-closed streams on session connection
// and then sets the number of concurrent streams to 3 // and then sets the number of concurrent streams to 3
private static class EchoHandler extends ChannelInboundMessageHandlerAdapter<Object> { private static class EchoHandler extends ChannelInboundHandlerAdapter {
private final int closeSignal; private final int closeSignal;
private final boolean server; private final boolean server;
@ -309,35 +310,38 @@ public class SpdySessionHandlerTest {
} }
@Override @Override
public void messageReceived(ChannelHandlerContext ctx, Object msg) throws Exception { public void messageReceived(ChannelHandlerContext ctx, MessageList<Object> messages) throws Exception {
if (msg instanceof SpdyDataFrame || for (int i = 0; i < messages.size(); i++) {
msg instanceof SpdyPingFrame || Object msg = messages.get(i);
msg instanceof SpdyHeadersFrame) { if (msg instanceof SpdyDataFrame ||
msg instanceof SpdyPingFrame ||
msg instanceof SpdyHeadersFrame) {
ctx.write(msg); ctx.write(msg);
return; 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<String, String> entry: spdySynStreamFrame.headers()) {
spdySynReplyFrame.headers().add(entry.getKey(), entry.getValue());
}
ctx.write(spdySynReplyFrame);
} }
return;
}
if (msg instanceof SpdySettingsFrame) { if (msg instanceof SpdySynStreamFrame) {
SpdySettingsFrame spdySettingsFrame = (SpdySettingsFrame) msg;
if (spdySettingsFrame.isSet(closeSignal)) { SpdySynStreamFrame spdySynStreamFrame = (SpdySynStreamFrame) msg;
ctx.close(); if (!spdySynStreamFrame.isUnidirectional()) {
int streamID = spdySynStreamFrame.getStreamId();
SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamID);
spdySynReplyFrame.setLast(spdySynStreamFrame.isLast());
for (Map.Entry<String, String> 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();
}
} }
} }
} }

View File

@ -20,7 +20,7 @@
<parent> <parent>
<groupId>io.netty</groupId> <groupId>io.netty</groupId>
<artifactId>netty-parent</artifactId> <artifactId>netty-parent</artifactId>
<version>4.0.0.Final-SNAPSHOT</version> <version>4.0.0.CR4-SNAPSHOT</version>
</parent> </parent>
<artifactId>netty-codec-socks</artifactId> <artifactId>netty-codec-socks</artifactId>

View File

@ -16,8 +16,8 @@
package io.netty.handler.codec.socks; package io.netty.handler.codec.socks;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.MessageBuf;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.MessageList;
import io.netty.handler.codec.ReplayingDecoder; import io.netty.handler.codec.ReplayingDecoder;
import io.netty.util.CharsetUtil; import io.netty.util.CharsetUtil;
@ -43,7 +43,7 @@ public class SocksAuthRequestDecoder extends ReplayingDecoder<SocksAuthRequestDe
} }
@Override @Override
protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, MessageBuf<Object> out) throws Exception { protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, MessageList<Object> out) throws Exception {
switch (state()) { switch (state()) {
case CHECK_PROTOCOL_VERSION: { case CHECK_PROTOCOL_VERSION: {
version = SocksSubnegotiationVersion.fromByte(byteBuf.readByte()); version = SocksSubnegotiationVersion.fromByte(byteBuf.readByte());

View File

@ -16,8 +16,8 @@
package io.netty.handler.codec.socks; package io.netty.handler.codec.socks;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.MessageBuf;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.MessageList;
import io.netty.handler.codec.ReplayingDecoder; import io.netty.handler.codec.ReplayingDecoder;
/** /**
@ -40,7 +40,7 @@ public class SocksAuthResponseDecoder extends ReplayingDecoder<SocksAuthResponse
} }
@Override @Override
protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, MessageBuf<Object> out) protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, MessageList<Object> out)
throws Exception { throws Exception {
switch (state()) { switch (state()) {
case CHECK_PROTOCOL_VERSION: { case CHECK_PROTOCOL_VERSION: {

View File

@ -16,8 +16,8 @@
package io.netty.handler.codec.socks; package io.netty.handler.codec.socks;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.MessageBuf;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.MessageList;
import io.netty.handler.codec.ReplayingDecoder; import io.netty.handler.codec.ReplayingDecoder;
import io.netty.util.CharsetUtil; import io.netty.util.CharsetUtil;
@ -46,7 +46,7 @@ public class SocksCmdRequestDecoder extends ReplayingDecoder<SocksCmdRequestDeco
} }
@Override @Override
protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, MessageBuf<Object> out) throws Exception { protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, MessageList<Object> out) throws Exception {
switch (state()) { switch (state()) {
case CHECK_PROTOCOL_VERSION: { case CHECK_PROTOCOL_VERSION: {
version = SocksProtocolVersion.fromByte(byteBuf.readByte()); version = SocksProtocolVersion.fromByte(byteBuf.readByte());

View File

@ -16,8 +16,8 @@
package io.netty.handler.codec.socks; package io.netty.handler.codec.socks;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.MessageBuf;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.MessageList;
import io.netty.handler.codec.ReplayingDecoder; import io.netty.handler.codec.ReplayingDecoder;
import io.netty.util.CharsetUtil; import io.netty.util.CharsetUtil;
@ -46,7 +46,7 @@ public class SocksCmdResponseDecoder extends ReplayingDecoder<SocksCmdResponseDe
} }
@Override @Override
protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, MessageBuf<Object> out) throws Exception { protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, MessageList<Object> out) throws Exception {
switch (state()) { switch (state()) {
case CHECK_PROTOCOL_VERSION: { case CHECK_PROTOCOL_VERSION: {
version = SocksProtocolVersion.fromByte(byteBuf.readByte()); version = SocksProtocolVersion.fromByte(byteBuf.readByte());

View File

@ -16,8 +16,8 @@
package io.netty.handler.codec.socks; package io.netty.handler.codec.socks;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.MessageBuf;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.MessageList;
import io.netty.handler.codec.ReplayingDecoder; import io.netty.handler.codec.ReplayingDecoder;
import java.util.ArrayList; import java.util.ArrayList;
@ -44,7 +44,7 @@ public class SocksInitRequestDecoder extends ReplayingDecoder<SocksInitRequestDe
} }
@Override @Override
protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, MessageBuf<Object> out) throws Exception { protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, MessageList<Object> out) throws Exception {
switch (state()) { switch (state()) {
case CHECK_PROTOCOL_VERSION: { case CHECK_PROTOCOL_VERSION: {
version = SocksProtocolVersion.fromByte(byteBuf.readByte()); version = SocksProtocolVersion.fromByte(byteBuf.readByte());

View File

@ -16,8 +16,8 @@
package io.netty.handler.codec.socks; package io.netty.handler.codec.socks;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.MessageBuf;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.MessageList;
import io.netty.handler.codec.ReplayingDecoder; import io.netty.handler.codec.ReplayingDecoder;
/** /**
@ -41,7 +41,7 @@ public class SocksInitResponseDecoder extends ReplayingDecoder<SocksInitResponse
} }
@Override @Override
protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, MessageBuf<Object> out) throws Exception { protected void decode(ChannelHandlerContext ctx, ByteBuf byteBuf, MessageList<Object> out) throws Exception {
switch (state()) { switch (state()) {
case CHECK_PROTOCOL_VERSION: { case CHECK_PROTOCOL_VERSION: {
version = SocksProtocolVersion.fromByte(byteBuf.readByte()); version = SocksProtocolVersion.fromByte(byteBuf.readByte());

View File

@ -15,7 +15,7 @@
*/ */
package io.netty.handler.codec.socks; package io.netty.handler.codec.socks;
import io.netty.channel.embedded.EmbeddedByteChannel; import io.netty.channel.embedded.EmbeddedChannel;
import org.junit.Test; import org.junit.Test;
import static org.junit.Assert.*; import static org.junit.Assert.*;
@ -28,7 +28,7 @@ public class SocksAuthRequestDecoderTest {
String password = "test"; String password = "test";
SocksAuthRequest msg = new SocksAuthRequest(username, password); SocksAuthRequest msg = new SocksAuthRequest(username, password);
SocksAuthRequestDecoder decoder = new SocksAuthRequestDecoder(); SocksAuthRequestDecoder decoder = new SocksAuthRequestDecoder();
EmbeddedByteChannel embedder = new EmbeddedByteChannel(decoder); EmbeddedChannel embedder = new EmbeddedChannel(decoder);
SocksCommonTestUtils.writeMessageIntoEmbedder(embedder, msg); SocksCommonTestUtils.writeMessageIntoEmbedder(embedder, msg);
msg = (SocksAuthRequest) embedder.readInbound(); msg = (SocksAuthRequest) embedder.readInbound();
assertEquals(msg.username(), username); assertEquals(msg.username(), username);

View File

@ -15,7 +15,7 @@
*/ */
package io.netty.handler.codec.socks; 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.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory; import io.netty.util.internal.logging.InternalLoggerFactory;
import org.junit.Test; import org.junit.Test;
@ -29,7 +29,7 @@ public class SocksAuthResponseDecoderTest {
logger.debug("Testing SocksAuthResponseDecoder with authStatus: " + authStatus); logger.debug("Testing SocksAuthResponseDecoder with authStatus: " + authStatus);
SocksAuthResponse msg = new SocksAuthResponse(authStatus); SocksAuthResponse msg = new SocksAuthResponse(authStatus);
SocksAuthResponseDecoder decoder = new SocksAuthResponseDecoder(); SocksAuthResponseDecoder decoder = new SocksAuthResponseDecoder();
EmbeddedByteChannel embedder = new EmbeddedByteChannel(decoder); EmbeddedChannel embedder = new EmbeddedChannel(decoder);
SocksCommonTestUtils.writeMessageIntoEmbedder(embedder, msg); SocksCommonTestUtils.writeMessageIntoEmbedder(embedder, msg);
msg = (SocksAuthResponse) embedder.readInbound(); msg = (SocksAuthResponse) embedder.readInbound();
assertSame(msg.authStatus(), authStatus); assertSame(msg.authStatus(), authStatus);

View File

@ -15,7 +15,7 @@
*/ */
package io.netty.handler.codec.socks; 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.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory; import io.netty.util.internal.logging.InternalLoggerFactory;
import org.junit.Test; import org.junit.Test;
@ -34,7 +34,7 @@ public class SocksCmdRequestDecoderTest {
" port: " + port); " port: " + port);
SocksCmdRequest msg = new SocksCmdRequest(cmdType, addressType, host, port); SocksCmdRequest msg = new SocksCmdRequest(cmdType, addressType, host, port);
SocksCmdRequestDecoder decoder = new SocksCmdRequestDecoder(); SocksCmdRequestDecoder decoder = new SocksCmdRequestDecoder();
EmbeddedByteChannel embedder = new EmbeddedByteChannel(decoder); EmbeddedChannel embedder = new EmbeddedChannel(decoder);
SocksCommonTestUtils.writeMessageIntoEmbedder(embedder, msg); SocksCommonTestUtils.writeMessageIntoEmbedder(embedder, msg);
if (msg.addressType() == SocksAddressType.UNKNOWN) { if (msg.addressType() == SocksAddressType.UNKNOWN) {
assertTrue(embedder.readInbound() instanceof UnknownSocksRequest); assertTrue(embedder.readInbound() instanceof UnknownSocksRequest);

View File

@ -15,7 +15,7 @@
*/ */
package io.netty.handler.codec.socks; 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.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory; import io.netty.util.internal.logging.InternalLoggerFactory;
import org.junit.Test; import org.junit.Test;
@ -25,12 +25,12 @@ import static org.junit.Assert.*;
public class SocksCmdResponseDecoderTest { public class SocksCmdResponseDecoderTest {
private static final InternalLogger logger = InternalLoggerFactory.getInstance(SocksCmdResponseDecoderTest.class); private static final InternalLogger logger = InternalLoggerFactory.getInstance(SocksCmdResponseDecoderTest.class);
private static void testSocksCmdResponseDecoderWithDifferentParams(SocksCmdStatus cmdStatus, private static void testSocksCmdResponseDecoderWithDifferentParams(
SocksAddressType addressType) { SocksCmdStatus cmdStatus, SocksAddressType addressType) {
logger.debug("Testing cmdStatus: " + cmdStatus + " addressType: " + addressType); logger.debug("Testing cmdStatus: " + cmdStatus + " addressType: " + addressType);
SocksResponse msg = new SocksCmdResponse(cmdStatus, addressType); SocksResponse msg = new SocksCmdResponse(cmdStatus, addressType);
SocksCmdResponseDecoder decoder = new SocksCmdResponseDecoder(); SocksCmdResponseDecoder decoder = new SocksCmdResponseDecoder();
EmbeddedByteChannel embedder = new EmbeddedByteChannel(decoder); EmbeddedChannel embedder = new EmbeddedChannel(decoder);
SocksCommonTestUtils.writeMessageIntoEmbedder(embedder, msg); SocksCommonTestUtils.writeMessageIntoEmbedder(embedder, msg);
if (addressType == SocksAddressType.UNKNOWN) { if (addressType == SocksAddressType.UNKNOWN) {
assertTrue(embedder.readInbound() instanceof UnknownSocksResponse); assertTrue(embedder.readInbound() instanceof UnknownSocksResponse);

View File

@ -17,7 +17,7 @@ package io.netty.handler.codec.socks;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.embedded.EmbeddedByteChannel; import io.netty.channel.embedded.EmbeddedChannel;
final class SocksCommonTestUtils { final class SocksCommonTestUtils {
/** /**
@ -27,7 +27,7 @@ final class SocksCommonTestUtils {
//NOOP //NOOP
} }
public static void writeMessageIntoEmbedder(EmbeddedByteChannel embedder, SocksMessage msg) { public static void writeMessageIntoEmbedder(EmbeddedChannel embedder, SocksMessage msg) {
ByteBuf buf = Unpooled.buffer(); ByteBuf buf = Unpooled.buffer();
msg.encodeAsByteBuf(buf); msg.encodeAsByteBuf(buf);
embedder.writeInbound(buf); embedder.writeInbound(buf);

View File

@ -20,7 +20,7 @@
<parent> <parent>
<groupId>io.netty</groupId> <groupId>io.netty</groupId>
<artifactId>netty-parent</artifactId> <artifactId>netty-parent</artifactId>
<version>4.0.0.Final-SNAPSHOT</version> <version>4.0.0.CR4-SNAPSHOT</version>
</parent> </parent>
<artifactId>netty-codec</artifactId> <artifactId>netty-codec</artifactId>

View File

@ -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}.
* <pre>
* 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);
* }
* }
* </pre>
*/
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);
}
}

View File

@ -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}.
*
* <pre>
* 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);
* }
* }
* </pre>
*/
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);
}
}

View File

@ -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}.
*
* <pre>
* 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);
* }
* }
* </pre>
*/
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;
}

View File

@ -16,16 +16,13 @@
package io.netty.handler.codec; package io.netty.handler.codec;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.MessageBuf;
import io.netty.channel.ChannelDuplexHandler; import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundByteHandler;
import io.netty.channel.ChannelOutboundMessageHandler;
import io.netty.channel.ChannelPromise; import io.netty.channel.ChannelPromise;
import io.netty.channel.MessageList;
import io.netty.util.internal.TypeParameterMatcher; import io.netty.util.internal.TypeParameterMatcher;
public abstract class ByteToMessageCodec<I> extends ChannelDuplexHandler public abstract class ByteToMessageCodec<I> extends ChannelDuplexHandler {
implements ChannelInboundByteHandler, ChannelOutboundMessageHandler<I> {
private final TypeParameterMatcher outboundMsgMatcher; private final TypeParameterMatcher outboundMsgMatcher;
private final MessageToByteEncoder<I> encoder = new MessageToByteEncoder<I>() { private final MessageToByteEncoder<I> encoder = new MessageToByteEncoder<I>() {
@ -42,12 +39,12 @@ public abstract class ByteToMessageCodec<I> extends ChannelDuplexHandler
private final ByteToMessageDecoder decoder = new ByteToMessageDecoder() { private final ByteToMessageDecoder decoder = new ByteToMessageDecoder() {
@Override @Override
public void decode(ChannelHandlerContext ctx, ByteBuf in, MessageBuf<Object> out) throws Exception { public void decode(ChannelHandlerContext ctx, ByteBuf in, MessageList<Object> out) throws Exception {
ByteToMessageCodec.this.decode(ctx, in, out); ByteToMessageCodec.this.decode(ctx, in, out);
} }
@Override @Override
protected void decodeLast(ChannelHandlerContext ctx, ByteBuf in, MessageBuf<Object> out) throws Exception { protected void decodeLast(ChannelHandlerContext ctx, ByteBuf in, MessageList<Object> out) throws Exception {
ByteToMessageCodec.this.decodeLast(ctx, in, out); ByteToMessageCodec.this.decodeLast(ctx, in, out);
} }
}; };
@ -60,38 +57,23 @@ public abstract class ByteToMessageCodec<I> extends ChannelDuplexHandler
outboundMsgMatcher = TypeParameterMatcher.get(outboundMessageType); 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<I> 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 { public boolean acceptOutboundMessage(Object msg) throws Exception {
return outboundMsgMatcher.match(msg); return outboundMsgMatcher.match(msg);
} }
@Override
public void messageReceived(ChannelHandlerContext ctx, MessageList<Object> msgs) throws Exception {
decoder.messageReceived(ctx, msgs);
}
@Override
public void write(ChannelHandlerContext ctx, MessageList<Object> 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 encode(ChannelHandlerContext ctx, I msg, ByteBuf out) throws Exception;
protected abstract void decode(ChannelHandlerContext ctx, ByteBuf in, MessageBuf<Object> out) throws Exception; protected abstract void decode(ChannelHandlerContext ctx, ByteBuf in, MessageList<Object> out) throws Exception;
protected void decodeLast(ChannelHandlerContext ctx, ByteBuf in, MessageBuf<Object> out) throws Exception { protected void decodeLast(ChannelHandlerContext ctx, ByteBuf in, MessageList<Object> out) throws Exception {
decode(ctx, in, out); decode(ctx, in, out);
} }
} }

View File

@ -16,14 +16,15 @@
package io.netty.handler.codec; package io.netty.handler.codec;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.MessageBuf; import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundByteHandler; import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInboundByteHandlerAdapter; 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 * {@link ChannelInboundHandlerAdapter} which decodes bytes in a stream-like fashion from one {@link ByteBuf} to an
* Message type. * other Message type.
* *
* For example here is an implementation which reads all readable bytes from * For example here is an implementation which reads all readable bytes from
* the input {@link ByteBuf} and create a new {@link ByteBuf}. * the input {@link ByteBuf} and create a new {@link ByteBuf}.
@ -31,22 +32,24 @@ import io.netty.channel.ChannelInboundByteHandlerAdapter;
* <pre> * <pre>
* public class SquareDecoder extends {@link ByteToMessageDecoder} { * public class SquareDecoder extends {@link ByteToMessageDecoder} {
* {@code @Override} * {@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} { * throws {@link Exception} {
* out.add(in.readBytes(in.readableBytes())); * out.add(in.readBytes(in.readableBytes()));
* } * }
* } * }
* </pre> * </pre>
*/ */
public abstract class ByteToMessageDecoder public abstract class ByteToMessageDecoder extends ChannelInboundHandlerAdapter {
extends ChannelInboundByteHandlerAdapter {
protected ByteBuf cumulation;
private volatile boolean singleDecode; private volatile boolean singleDecode;
private boolean decodeWasNull; private boolean decodeWasNull;
private MessageList<Object> out;
private boolean removed;
/** /**
* If set then only one message is decoded on each {@link #inboundBufferUpdated(ChannelHandlerContext)} call. * If set then only one message is decoded on each {@link #messageReceived(ChannelHandlerContext, MessageList)}
* This may be useful if you need to do some protocol upgrade and want to make sure nothing is mixed up. * 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. * 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 * 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. * Default is {@code false} as this has performance impacts.
*/ */
@ -64,9 +67,119 @@ public abstract class ByteToMessageDecoder
return singleDecode; 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 @Override
public void inboundBufferUpdated(ChannelHandlerContext ctx, ByteBuf in) throws Exception { public final void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
callDecode(ctx, in); 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<Object> 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<Object> out = this.out;
this.out = null;
if (out.isEmpty()) {
decodeWasNull = true;
}
msgs.recycle();
ctx.fireMessageReceived(out);
}
} }
@Override @Override
@ -82,35 +195,36 @@ public abstract class ByteToMessageDecoder
@Override @Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception { public void channelInactive(ChannelHandlerContext ctx) throws Exception {
OutputMessageBuf out = OutputMessageBuf.get(); MessageList<Object> out = MessageList.newInstance();
try { try {
ByteBuf in = ctx.inboundByteBuffer(); if (cumulation != null) {
if (in.isReadable()) { callDecode(ctx, cumulation, out);
callDecode(ctx, in); decodeLast(ctx, cumulation, out);
} else {
decodeLast(ctx, Unpooled.EMPTY_BUFFER, out);
} }
decodeLast(ctx, in, out); } catch (DecoderException e) {
} catch (CodecException e) {
throw e; throw e;
} catch (Throwable cause) { } catch (Exception e) {
throw new DecoderException(cause); throw new DecoderException(e);
} finally { } finally {
if (out.drainToNextInbound(ctx)) { if (cumulation != null) {
ctx.fireInboundBufferUpdated(); cumulation.release();
cumulation = null;
} }
ctx.fireMessageReceived(out);
ctx.fireChannelInactive(); ctx.fireChannelInactive();
} }
} }
protected void callDecode(ChannelHandlerContext ctx, ByteBuf in) { protected void callDecode(ChannelHandlerContext ctx, ByteBuf in, MessageList<Object> out) {
boolean wasNull = false;
OutputMessageBuf out = OutputMessageBuf.get();
try { try {
while (in.isReadable()) { while (in.isReadable()) {
int outSize = out.size(); int outSize = out.size();
int oldInputLength = in.readableBytes(); int oldInputLength = in.readableBytes();
decode(ctx, in, out); decode(ctx, in, out);
if (outSize == out.size()) { if (outSize == out.size()) {
wasNull = true;
if (oldInputLength == in.readableBytes()) { if (oldInputLength == in.readableBytes()) {
break; break;
} else { } else {
@ -118,10 +232,10 @@ public abstract class ByteToMessageDecoder
} }
} }
wasNull = false;
if (oldInputLength == in.readableBytes()) { if (oldInputLength == in.readableBytes()) {
throw new IllegalStateException( throw new DecoderException(
"decode() did not read anything but decoded a message."); StringUtil.simpleClassName(getClass()) +
".decode() did not read anything but decoded a message.");
} }
if (isSingleDecode()) { if (isSingleDecode()) {
@ -132,15 +246,6 @@ public abstract class ByteToMessageDecoder
throw e; throw e;
} catch (Throwable cause) { } catch (Throwable cause) {
throw new DecoderException(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 * {@link ByteBuf} has nothing to read anymore, till nothing was read from the input {@link ByteBuf} or till
* this method returns {@code null}. * 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 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 * @throws Exception is thrown if an error accour
*/ */
protected abstract void decode(ChannelHandlerContext ctx, ByteBuf in, MessageBuf<Object> out) throws Exception; protected abstract void decode(ChannelHandlerContext ctx, ByteBuf in, MessageList<Object> out) throws Exception;
/** /**
* Is called one last time when the {@link ChannelHandlerContext} goes in-active. Which means the * Is called one last time when the {@link ChannelHandlerContext} goes in-active. Which means the
* {@link #channelInactive(ChannelHandlerContext)} was triggered. * {@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. * override this for some special cleanup operation.
*/ */
protected void decodeLast(ChannelHandlerContext ctx, ByteBuf in, MessageBuf<Object> out) throws Exception { protected void decodeLast(ChannelHandlerContext ctx, ByteBuf in, MessageList<Object> out) throws Exception {
decode(ctx, in, out); decode(ctx, in, out);
} }
} }

View File

@ -16,8 +16,8 @@
package io.netty.handler.codec; package io.netty.handler.codec;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.MessageBuf;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.MessageList;
/** /**
* A decoder that splits the received {@link ByteBuf}s by one or more * A decoder that splits the received {@link ByteBuf}s by one or more
@ -211,7 +211,7 @@ public class DelimiterBasedFrameDecoder extends ByteToMessageDecoder {
} }
@Override @Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, MessageBuf<Object> out) throws Exception { protected void decode(ChannelHandlerContext ctx, ByteBuf in, MessageList<Object> out) throws Exception {
Object decoded = decode(ctx, in); Object decoded = decode(ctx, in);
if (decoded != null) { if (decoded != null) {
out.add(decoded); out.add(decoded);

View File

@ -16,9 +16,8 @@
package io.netty.handler.codec; package io.netty.handler.codec;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.MessageBuf;
import io.netty.channel.ChannelHandlerContext; 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 * 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 { public class FixedLengthFrameDecoder extends ByteToMessageDecoder {
private final int frameLength; 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. * Creates a new instance.
* *
* @param frameLength * @param frameLength the length of the frame
* the length of the frame
* @param allocateFullBuffer
* {@code true} if the cumulative {@link ByteBuf} should use the
* {@link #frameLength} as its initial size
*/ */
public FixedLengthFrameDecoder(int frameLength, boolean allocateFullBuffer) { public FixedLengthFrameDecoder(int frameLength) {
if (frameLength <= 0) { if (frameLength <= 0) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"frameLength must be a positive integer: " + frameLength); "frameLength must be a positive integer: " + frameLength);
} }
this.frameLength = frameLength; this.frameLength = frameLength;
this.allocateFullBuffer = allocateFullBuffer;
} }
@Override @Override
public ByteBuf newInboundBuffer(ChannelHandlerContext ctx) throws Exception { protected void decode(ChannelHandlerContext ctx, ByteBuf in, MessageList<Object> out) throws Exception {
if (allocateFullBuffer) {
return ChannelHandlerUtil.allocate(ctx, frameLength);
} else {
return super.newInboundBuffer(ctx);
}
}
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, MessageBuf<Object> out) throws Exception {
Object decoded = decode(ctx, in); Object decoded = decode(ctx, in);
if (decoded != null) { if (decoded != null) {
out.add(decoded); 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) { if (in.readableBytes() < frameLength) {
return null; return null;
} else { } else {

View File

@ -16,9 +16,9 @@
package io.netty.handler.codec; package io.netty.handler.codec;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.MessageBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.MessageList;
import io.netty.handler.codec.serialization.ObjectDecoder; import io.netty.handler.codec.serialization.ObjectDecoder;
import java.nio.ByteOrder; import java.nio.ByteOrder;
@ -348,7 +348,7 @@ public class LengthFieldBasedFrameDecoder extends ByteToMessageDecoder {
} }
@Override @Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, MessageBuf<Object> out) throws Exception { protected void decode(ChannelHandlerContext ctx, ByteBuf in, MessageList<Object> out) throws Exception {
Object decoded = decode(ctx, in); Object decoded = decode(ctx, in);
if (decoded != null) { if (decoded != null) {
out.add(decoded); out.add(decoded);

View File

@ -16,8 +16,8 @@
package io.netty.handler.codec; package io.netty.handler.codec;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.MessageBuf;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.MessageList;
/** /**
* A decoder that splits the received {@link ByteBuf}s on line endings. * A decoder that splits the received {@link ByteBuf}s on line endings.
@ -69,7 +69,7 @@ public class LineBasedFrameDecoder extends ByteToMessageDecoder {
} }
@Override @Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, MessageBuf<Object> out) throws Exception { protected void decode(ChannelHandlerContext ctx, ByteBuf in, MessageList<Object> out) throws Exception {
Object decoded = decode(ctx, in); Object decoded = decode(ctx, in);
if (decoded != null) { if (decoded != null) {
out.add(decoded); out.add(decoded);

View File

@ -16,13 +16,16 @@
package io.netty.handler.codec; package io.netty.handler.codec;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.MessageBuf; import io.netty.buffer.ByteBufUtil;
import io.netty.channel.ChannelHandlerContext; 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}. * {@link ByteBuf}.
* *
* *
@ -38,27 +41,73 @@ import io.netty.channel.ChannelOutboundMessageHandlerAdapter;
* } * }
* </pre> * </pre>
*/ */
public abstract class MessageToByteEncoder<I> extends ChannelOutboundMessageHandlerAdapter<I> { public abstract class MessageToByteEncoder<I> extends ChannelOutboundHandlerAdapter {
protected MessageToByteEncoder() { } private final TypeParameterMatcher matcher;
protected MessageToByteEncoder() {
matcher = TypeParameterMatcher.find(this, MessageToByteEncoder.class, "I");
}
protected MessageToByteEncoder(Class<? extends I> outboundMessageType) { protected MessageToByteEncoder(Class<? extends I> outboundMessageType) {
super(outboundMessageType); matcher = TypeParameterMatcher.get(outboundMessageType);
}
public boolean acceptOutboundMessage(Object msg) throws Exception {
return matcher.match(msg);
} }
@Override @Override
public void flush(ChannelHandlerContext ctx, I msg) throws Exception { public void write(ChannelHandlerContext ctx, MessageList<Object> msgs, ChannelPromise promise) throws Exception {
MessageList<Object> out = MessageList.newInstance();
boolean success = false;
try { try {
encode(ctx, msg, ctx.nextOutboundByteBuffer()); ByteBuf buf = null;
} catch (CodecException e) { 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; throw e;
} catch (Exception e) { } catch (Throwable e) {
throw new CodecException(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. * nothing left.
* *
* @param ctx the {@link ChannelHandlerContext} which this {@link MessageToByteEncoder} belongs to * @param ctx the {@link ChannelHandlerContext} which this {@link MessageToByteEncoder} belongs to

View File

@ -15,12 +15,10 @@
*/ */
package io.netty.handler.codec; package io.netty.handler.codec;
import io.netty.buffer.MessageBuf;
import io.netty.channel.ChannelDuplexHandler; import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundMessageHandler;
import io.netty.channel.ChannelOutboundMessageHandler;
import io.netty.channel.ChannelPromise; import io.netty.channel.ChannelPromise;
import io.netty.channel.MessageList;
import io.netty.util.internal.TypeParameterMatcher; import io.netty.util.internal.TypeParameterMatcher;
/** /**
@ -48,13 +46,10 @@ import io.netty.util.internal.TypeParameterMatcher;
* } * }
* </pre> * </pre>
*/ */
public abstract class MessageToMessageCodec<INBOUND_IN, OUTBOUND_IN> public abstract class MessageToMessageCodec<INBOUND_IN, OUTBOUND_IN> extends ChannelDuplexHandler {
extends ChannelDuplexHandler
implements ChannelInboundMessageHandler<INBOUND_IN>, private final MessageToMessageEncoder<Object> encoder = new MessageToMessageEncoder<Object>() {
ChannelOutboundMessageHandler<OUTBOUND_IN> {
private final MessageToMessageEncoder<Object> encoder =
new MessageToMessageEncoder<Object>() {
@Override @Override
public boolean acceptOutboundMessage(Object msg) throws Exception { public boolean acceptOutboundMessage(Object msg) throws Exception {
return MessageToMessageCodec.this.acceptOutboundMessage(msg); return MessageToMessageCodec.this.acceptOutboundMessage(msg);
@ -62,13 +57,12 @@ public abstract class MessageToMessageCodec<INBOUND_IN, OUTBOUND_IN>
@Override @Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected void encode(ChannelHandlerContext ctx, Object msg, MessageBuf<Object> out) throws Exception { protected void encode(ChannelHandlerContext ctx, Object msg, MessageList<Object> out) throws Exception {
MessageToMessageCodec.this.encode(ctx, (OUTBOUND_IN) msg, out); MessageToMessageCodec.this.encode(ctx, (OUTBOUND_IN) msg, out);
} }
}; };
private final MessageToMessageDecoder<Object> decoder = private final MessageToMessageDecoder<Object> decoder = new MessageToMessageDecoder<Object>() {
new MessageToMessageDecoder<Object>() {
@Override @Override
public boolean acceptInboundMessage(Object msg) throws Exception { public boolean acceptInboundMessage(Object msg) throws Exception {
@ -77,7 +71,7 @@ public abstract class MessageToMessageCodec<INBOUND_IN, OUTBOUND_IN>
@Override @Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected void decode(ChannelHandlerContext ctx, Object msg, MessageBuf<Object> out) throws Exception { protected void decode(ChannelHandlerContext ctx, Object msg, MessageList<Object> out) throws Exception {
MessageToMessageCodec.this.decode(ctx, (INBOUND_IN) msg, out); MessageToMessageCodec.this.decode(ctx, (INBOUND_IN) msg, out);
} }
}; };
@ -97,26 +91,13 @@ public abstract class MessageToMessageCodec<INBOUND_IN, OUTBOUND_IN>
} }
@Override @Override
@SuppressWarnings("unchecked") public void messageReceived(ChannelHandlerContext ctx, MessageList<Object> msgs) throws Exception {
public MessageBuf<INBOUND_IN> newInboundBuffer(ChannelHandlerContext ctx) throws Exception { decoder.messageReceived(ctx, msgs);
return (MessageBuf<INBOUND_IN>) decoder.newInboundBuffer(ctx);
} }
@Override @Override
@SuppressWarnings("unchecked") public void write(ChannelHandlerContext ctx, MessageList<Object> msgs, ChannelPromise promise) throws Exception {
public MessageBuf<OUTBOUND_IN> newOutboundBuffer(ChannelHandlerContext ctx) throws Exception { encoder.write(ctx, msgs, promise);
return (MessageBuf<OUTBOUND_IN>) 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);
} }
/** /**
@ -137,6 +118,8 @@ public abstract class MessageToMessageCodec<INBOUND_IN, OUTBOUND_IN>
return outboundMsgMatcher.match(msg); return outboundMsgMatcher.match(msg);
} }
protected abstract void encode(ChannelHandlerContext ctx, OUTBOUND_IN msg, MessageBuf<Object> out) throws Exception; protected abstract void encode(ChannelHandlerContext ctx, OUTBOUND_IN msg, MessageList<Object> out)
protected abstract void decode(ChannelHandlerContext ctx, INBOUND_IN msg, MessageBuf<Object> out) throws Exception; throws Exception;
protected abstract void decode(ChannelHandlerContext ctx, INBOUND_IN msg, MessageList<Object> out)
throws Exception;
} }

View File

@ -15,13 +15,14 @@
*/ */
package io.netty.handler.codec; package io.netty.handler.codec;
import io.netty.buffer.MessageBuf; import io.netty.buffer.ByteBufUtil;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundMessageHandler; import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInboundMessageHandlerAdapter; 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 * For example here is an implementation which decodes a {@link String} to an {@link Integer} which represent
* the length of the {@link String}. * the length of the {@link String}.
@ -32,43 +33,66 @@ import io.netty.channel.ChannelInboundMessageHandlerAdapter;
* *
* {@code @Override} * {@code @Override}
* public void decode({@link ChannelHandlerContext} ctx, {@link String} message, * 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()); * out.add(message.length());
* } * }
* } * }
* </pre> * </pre>
* *
*/ */
public abstract class MessageToMessageDecoder<I> extends ChannelInboundMessageHandlerAdapter<I> { public abstract class MessageToMessageDecoder<I> extends ChannelInboundHandlerAdapter {
protected MessageToMessageDecoder() { } private final TypeParameterMatcher matcher;
protected MessageToMessageDecoder() {
matcher = TypeParameterMatcher.find(this, MessageToMessageDecoder.class, "I");
}
protected MessageToMessageDecoder(Class<? extends I> inboundMessageType) { protected MessageToMessageDecoder(Class<? extends I> inboundMessageType) {
super(inboundMessageType); matcher = TypeParameterMatcher.get(inboundMessageType);
}
public boolean acceptInboundMessage(Object msg) throws Exception {
return matcher.match(msg);
} }
@Override @Override
public final void messageReceived(ChannelHandlerContext ctx, I msg) throws Exception { public void messageReceived(ChannelHandlerContext ctx, MessageList<Object> msgs) throws Exception {
OutputMessageBuf out = OutputMessageBuf.get(); MessageList<Object> out = MessageList.newInstance();
try { try {
decode(ctx, msg, out); int size = msgs.size();
} catch (CodecException e) { 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; throw e;
} catch (Throwable cause) { } catch (Exception e) {
throw new DecoderException(cause); throw new DecoderException(e);
} finally { } 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}. * nothing left or till this method returns {@code null}.
* *
* @param ctx the {@link ChannelHandlerContext} which this {@link MessageToMessageDecoder} belongs to * @param ctx the {@link ChannelHandlerContext} which this {@link MessageToMessageDecoder} belongs to
* @param msg the message to decode to an other one * @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 * @throws Exception is thrown if an error accour
*/ */
protected abstract void decode(ChannelHandlerContext ctx, I msg, MessageBuf<Object> out) throws Exception; protected abstract void decode(ChannelHandlerContext ctx, I msg, MessageList<Object> out) throws Exception;
} }

Some files were not shown because too many files have changed in this diff Show More