diff --git a/codec-memcache/pom.xml b/codec-memcache/pom.xml
new file mode 100644
index 0000000000..6f4b53f58a
--- /dev/null
+++ b/codec-memcache/pom.xml
@@ -0,0 +1,45 @@
+
+
+
+
+ 4.0.0
+
+ io.netty
+ netty-parent
+ 5.0.0.Alpha1-SNAPSHOT
+
+
+ netty-codec-memcache
+ bundle
+
+ Netty/Codec/Memcache
+
+
+
+ ${project.groupId}
+ netty-codec
+ ${project.version}
+
+
+ ${project.groupId}
+ netty-handler
+ ${project.version}
+
+
+
+
+
diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/DefaultLastMemcacheContent.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/DefaultLastMemcacheContent.java
new file mode 100644
index 0000000000..0d5da9b07d
--- /dev/null
+++ b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/DefaultLastMemcacheContent.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2013 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package io.netty.handler.codec.memcache;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+
+/**
+ * The default implementation for the {@link LastMemcacheContent}.
+ */
+public class DefaultLastMemcacheContent extends DefaultMemcacheContent implements LastMemcacheContent {
+
+ public DefaultLastMemcacheContent() {
+ super(Unpooled.buffer());
+ }
+
+ public DefaultLastMemcacheContent(ByteBuf content) {
+ super(content);
+ }
+
+ @Override
+ public LastMemcacheContent retain() {
+ super.retain();
+ return this;
+ }
+
+ @Override
+ public LastMemcacheContent retain(int increment) {
+ super.retain(increment);
+ return this;
+ }
+
+ @Override
+ public LastMemcacheContent copy() {
+ return new DefaultLastMemcacheContent(content().copy());
+ }
+
+ @Override
+ public LastMemcacheContent duplicate() {
+ return new DefaultLastMemcacheContent(content().duplicate());
+ }
+
+}
diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/DefaultMemcacheContent.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/DefaultMemcacheContent.java
new file mode 100644
index 0000000000..1d657b4549
--- /dev/null
+++ b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/DefaultMemcacheContent.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright 2013 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package io.netty.handler.codec.memcache;
+
+import io.netty.buffer.ByteBuf;
+
+/**
+ * The default {@link MemcacheContent} implementation.
+ */
+public class DefaultMemcacheContent extends DefaultMemcacheObject implements MemcacheContent {
+
+ private final ByteBuf content;
+
+ /**
+ * Creates a new instance with the specified content.
+ */
+ public DefaultMemcacheContent(ByteBuf content) {
+ if (content == null) {
+ throw new NullPointerException("Content cannot be null.");
+ }
+ this.content = content;
+ }
+
+ @Override
+ public ByteBuf content() {
+ return content;
+ }
+
+ @Override
+ public MemcacheContent copy() {
+ return new DefaultMemcacheContent(content.copy());
+ }
+
+ @Override
+ public MemcacheContent duplicate() {
+ return new DefaultMemcacheContent(content.duplicate());
+ }
+
+ @Override
+ public int refCnt() {
+ return content.refCnt();
+ }
+
+ @Override
+ public MemcacheContent retain() {
+ content.retain();
+ return this;
+ }
+
+ @Override
+ public MemcacheContent retain(int increment) {
+ content.retain(increment);
+ return this;
+ }
+
+ @Override
+ public boolean release() {
+ return content.release();
+ }
+
+ @Override
+ public boolean release(int decrement) {
+ return content.release(decrement);
+ }
+
+ @Override
+ public String toString() {
+ return getClass().getSimpleName() + "(data: " + content() + ", getDecoderResult: " + getDecoderResult() + ')';
+ }
+
+}
diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/DefaultMemcacheObject.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/DefaultMemcacheObject.java
new file mode 100644
index 0000000000..2806ffbb40
--- /dev/null
+++ b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/DefaultMemcacheObject.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2013 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package io.netty.handler.codec.memcache;
+
+import io.netty.handler.codec.DecoderResult;
+import io.netty.util.AbstractReferenceCounted;
+
+/**
+ * The default {@link MemcacheObject} implementation.
+ */
+public abstract class DefaultMemcacheObject implements MemcacheObject {
+
+ private DecoderResult decoderResult = DecoderResult.SUCCESS;
+
+ protected DefaultMemcacheObject() {
+ // Disallow direct instantiation
+ }
+
+ @Override
+ public DecoderResult getDecoderResult() {
+ return decoderResult;
+ }
+
+ @Override
+ public void setDecoderResult(DecoderResult result) {
+ if (result == null) {
+ throw new NullPointerException("DecoderResult should not be null.");
+ }
+
+ decoderResult = result;
+ }
+
+}
diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/FullMemcacheMessage.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/FullMemcacheMessage.java
new file mode 100644
index 0000000000..9a1d0955c9
--- /dev/null
+++ b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/FullMemcacheMessage.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2013 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package io.netty.handler.codec.memcache;
+
+/**
+ * Combines {@link MemcacheMessage} and {@link LastMemcacheContent} into one
+ * message. So it represent a complete memcache message.
+ */
+public interface FullMemcacheMessage extends MemcacheMessage, LastMemcacheContent {
+
+ @Override
+ FullMemcacheMessage copy();
+
+ @Override
+ FullMemcacheMessage retain(int increment);
+
+ @Override
+ FullMemcacheMessage retain();
+
+ @Override
+ FullMemcacheMessage duplicate();
+
+}
diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/LastMemcacheContent.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/LastMemcacheContent.java
new file mode 100644
index 0000000000..92f8efd703
--- /dev/null
+++ b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/LastMemcacheContent.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2013 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package io.netty.handler.codec.memcache;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.handler.codec.DecoderResult;
+
+/**
+ * The {@link MemcacheContent} which signals the end of the content batch.
+ *
+ * Note that by design, even when no content is emitted by the protocol, an
+ * empty {@link LastMemcacheContent} is issued to make the upstream parsing
+ * easier.
+ */
+public interface LastMemcacheContent extends MemcacheContent {
+
+ LastMemcacheContent EMPTY_LAST_CONTENT = new LastMemcacheContent() {
+
+ @Override
+ public LastMemcacheContent copy() {
+ return EMPTY_LAST_CONTENT;
+ }
+
+ @Override
+ public LastMemcacheContent retain(int increment) {
+ return this;
+ }
+
+ @Override
+ public LastMemcacheContent retain() {
+ return this;
+ }
+
+ @Override
+ public LastMemcacheContent duplicate() {
+ return this;
+ }
+
+ @Override
+ public ByteBuf content() {
+ return Unpooled.EMPTY_BUFFER;
+ }
+
+ @Override
+ public DecoderResult getDecoderResult() {
+ return DecoderResult.SUCCESS;
+ }
+
+ @Override
+ public void setDecoderResult(DecoderResult result) {
+ throw new UnsupportedOperationException("read only");
+ }
+
+ @Override
+ public int refCnt() {
+ return 1;
+ }
+
+ @Override
+ public boolean release() {
+ return false;
+ }
+
+ @Override
+ public boolean release(int decrement) {
+ return false;
+ }
+ };
+
+ @Override
+ LastMemcacheContent copy();
+
+ @Override
+ LastMemcacheContent retain(int increment);
+
+ @Override
+ LastMemcacheContent retain();
+
+ @Override
+ LastMemcacheContent duplicate();
+}
diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/MemcacheContent.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/MemcacheContent.java
new file mode 100644
index 0000000000..afec9a2281
--- /dev/null
+++ b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/MemcacheContent.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2013 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package io.netty.handler.codec.memcache;
+
+import io.netty.buffer.ByteBufHolder;
+
+/**
+ * An Memcache content chunk.
+ *
+ * A MemcacheObjectDecoder generates {@link MemcacheContent} after
+ * {@link MemcacheMessage} when the content is large. If you prefer not to
+ * receive {@link MemcacheContent} in your handler, place a aggregator
+ * after MemcacheObjectDecoder in the {@link io.netty.channel.ChannelPipeline}.
+ */
+public interface MemcacheContent extends MemcacheObject, ByteBufHolder {
+
+ @Override
+ MemcacheContent copy();
+
+ @Override
+ MemcacheContent duplicate();
+
+ @Override
+ MemcacheContent retain();
+
+ @Override
+ MemcacheContent retain(int increment);
+
+}
diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/MemcacheMessage.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/MemcacheMessage.java
new file mode 100644
index 0000000000..0a30880bd3
--- /dev/null
+++ b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/MemcacheMessage.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2013 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package io.netty.handler.codec.memcache;
+
+import io.netty.util.ReferenceCounted;
+
+/**
+ * Marker interface for both ascii and binary messages.
+ */
+public interface MemcacheMessage extends MemcacheObject, ReferenceCounted {
+
+ /**
+ * Increases the reference count by {@code 1}.
+ */
+ @Override
+ MemcacheMessage retain();
+
+ /**
+ * Increases the reference count by the specified {@code increment}.
+ */
+ @Override
+ MemcacheMessage retain(int increment);
+
+}
diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/MemcacheObject.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/MemcacheObject.java
new file mode 100644
index 0000000000..cf0a86d483
--- /dev/null
+++ b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/MemcacheObject.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2013 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package io.netty.handler.codec.memcache;
+
+import io.netty.handler.codec.DecoderResult;
+
+/**
+ * Defines a common interface for all {@link MemcacheObject} implementations.
+ */
+public interface MemcacheObject {
+
+ /**
+ * Returns the result of decoding this message.
+ */
+ DecoderResult getDecoderResult();
+
+ /**
+ * Updates the result of decoding this message.
+ *
+ * Do not call this method unless you know what you are doing.
+ */
+ void setDecoderResult(DecoderResult result);
+
+}
diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/MemcacheObjectAggregator.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/MemcacheObjectAggregator.java
new file mode 100644
index 0000000000..e1d75ce18a
--- /dev/null
+++ b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/MemcacheObjectAggregator.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2013 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package io.netty.handler.codec.memcache;
+
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.MessageToMessageDecoder;
+
+/**
+ * A {@link io.netty.channel.ChannelHandler} that aggregates an {@link MemcacheMessage}
+ * and its following {@link MemcacheContent}s into a single {@link MemcacheMessage} with
+ * no following {@link MemcacheContent}s. It is useful when you don't want to take
+ * care of memcache messages where the content comes along in chunks. Insert this
+ * handler after a MemcacheObjectDecoder in the {@link io.netty.channel.ChannelPipeline}.
+ *
+ * For example, here for the binary protocol:
+ *
+ *
+ * {@link io.netty.channel.ChannelPipeline} p = ...;
+ * ...
+ * p.addLast("decoder", new {@link io.netty.handler.codec.memcache.binary.BinaryMemcacheRequestDecoder}());
+ * p.addLast("aggregator", new {@link MemcacheObjectAggregator}(1048576) );
+ * ...
+ * p.addLast("encoder", new {@link io.netty.handler.codec.memcache.binary.BinaryMemcacheResponseEncoder}());
+ * p.addLast("handler", new YourMemcacheRequestHandler());
+ *
+ */
+public abstract class MemcacheObjectAggregator extends MessageToMessageDecoder {
+
+ /**
+ * Contains the current message that gets aggregated.
+ */
+ protected FullMemcacheMessage currentMessage;
+
+ /**
+ * Holds the current channel handler context if set.
+ */
+ protected ChannelHandlerContext ctx;
+
+ public static final int DEFAULT_MAX_COMPOSITEBUFFER_COMPONENTS = 1024;
+
+ private int maxCumulationBufferComponents = DEFAULT_MAX_COMPOSITEBUFFER_COMPONENTS;
+
+ private final int maxContentLength;
+
+ public MemcacheObjectAggregator(int maxContentLength) {
+ if (maxContentLength <= 0) {
+ throw new IllegalArgumentException("maxContentLength must be a positive integer: " + maxContentLength);
+ }
+
+ this.maxContentLength = maxContentLength;
+ }
+
+ /**
+ * Returns the maximum number of components in the cumulation buffer. If the number of
+ * the components in the cumulation buffer exceeds this value, the components of the
+ * cumulation buffer are consolidated into a single component, involving memory copies.
+ * The default value of this property is {@link #DEFAULT_MAX_COMPOSITEBUFFER_COMPONENTS}.
+ */
+ public final int getMaxCumulationBufferComponents() {
+ return maxCumulationBufferComponents;
+ }
+
+ /**
+ * Sets the maximum number of components in the cumulation buffer. If the number of
+ * the components in the cumulation buffer exceeds this value, the components of the
+ * cumulation buffer are consolidated into a single component, involving memory copies.
+ * The default value of this property is {@link #DEFAULT_MAX_COMPOSITEBUFFER_COMPONENTS}
+ * and its minimum allowed value is {@code 2}.
+ */
+ public final void setMaxCumulationBufferComponents(int maxCumulationBufferComponents) {
+ if (maxCumulationBufferComponents < 2) {
+ throw new IllegalArgumentException(
+ "maxCumulationBufferComponents: " + maxCumulationBufferComponents +
+ " (expected: >= 2)");
+ }
+
+ if (ctx == null) {
+ this.maxCumulationBufferComponents = maxCumulationBufferComponents;
+ } else {
+ throw new IllegalStateException(
+ "decoder properties cannot be changed once the decoder is added to a pipeline.");
+ }
+ }
+
+ public int getMaxContentLength() {
+ return maxContentLength;
+ }
+
+ @Override
+ public void channelInactive(ChannelHandlerContext ctx) throws Exception {
+ super.channelInactive(ctx);
+
+ if (currentMessage != null) {
+ currentMessage.release();
+ currentMessage = null;
+ }
+ }
+
+ @Override
+ public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
+ this.ctx = ctx;
+ }
+
+ @Override
+ public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
+ super.handlerRemoved(ctx);
+
+ if (currentMessage != null) {
+ currentMessage.release();
+ currentMessage = null;
+ }
+ }
+
+}
diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/MemcacheObjectDecoder.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/MemcacheObjectDecoder.java
new file mode 100644
index 0000000000..7c3a795d84
--- /dev/null
+++ b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/MemcacheObjectDecoder.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright 2013 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package io.netty.handler.codec.memcache;
+
+import io.netty.handler.codec.ByteToMessageDecoder;
+
+/**
+ * Abstract super class for both ascii and binary decoders.
+ *
+ * Currently it just acts as a common denominator, but will certainly include methods once the ascii protocol
+ * is implemented.
+ */
+public abstract class MemcacheObjectDecoder extends ByteToMessageDecoder {
+}
diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/MemcacheObjectEncoder.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/MemcacheObjectEncoder.java
new file mode 100644
index 0000000000..12c5f28217
--- /dev/null
+++ b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/MemcacheObjectEncoder.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2013 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package io.netty.handler.codec.memcache;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.FileRegion;
+import io.netty.handler.codec.MessageToMessageEncoder;
+
+import java.util.List;
+
+/**
+ * A general purpose {@link MemcacheObjectEncoder} that encodes {@link MemcacheMessage}s.
+ *
+ * Note that this class is designed to be extended, especially because both the binary and ascii protocol
+ * require different treatment of their messages. Since the content chunk writing is the same for both, the encoder
+ * abstracts this right away.
+ */
+public abstract class MemcacheObjectEncoder extends MessageToMessageEncoder {
+
+ private boolean expectingMoreContent;
+
+ @Override
+ protected void encode(ChannelHandlerContext ctx, Object msg, List out) throws Exception {
+ if (msg instanceof MemcacheMessage) {
+ if (expectingMoreContent) {
+ throw new IllegalStateException("unexpected message type: " + msg.getClass().getSimpleName());
+ }
+
+ out.add(encodeMessage(ctx, (M) msg));
+ }
+
+ if (msg instanceof MemcacheContent || msg instanceof ByteBuf || msg instanceof FileRegion) {
+ int contentLength = contentLength(msg);
+ if (contentLength > 0) {
+ out.add(encodeAndRetain(msg));
+ } else {
+ out.add(Unpooled.EMPTY_BUFFER);
+ }
+
+ expectingMoreContent = !(msg instanceof LastMemcacheContent);
+ }
+ }
+
+ @Override
+ public boolean acceptOutboundMessage(Object msg) throws Exception {
+ return msg instanceof MemcacheObject || msg instanceof ByteBuf || msg instanceof FileRegion;
+ }
+
+ /**
+ * Take the given {@link MemcacheMessage} and encode it into a writable {@link ByteBuf}.
+ *
+ * @param ctx the channel handler context.
+ * @param msg the message to encode.
+ * @return the {@link ByteBuf} representation of the message.
+ */
+ protected abstract ByteBuf encodeMessage(ChannelHandlerContext ctx, M msg);
+
+ /**
+ * Determine the content length of the given object.
+ *
+ * @param msg the object to determine the length of.
+ * @return the determined content length.
+ */
+ private static int contentLength(Object msg) {
+ if (msg instanceof MemcacheContent) {
+ return ((MemcacheContent) msg).content().readableBytes();
+ }
+ if (msg instanceof ByteBuf) {
+ return ((ByteBuf) msg).readableBytes();
+ }
+ if (msg instanceof FileRegion) {
+ return (int) ((FileRegion) msg).count();
+ }
+ throw new IllegalStateException("unexpected message type: " + msg.getClass().getSimpleName());
+ }
+
+ /**
+ * Encode the content, depending on the object type.
+ *
+ * @param msg the object to encode.
+ * @return the encoded object.
+ */
+ private static Object encodeAndRetain(Object msg) {
+ if (msg instanceof ByteBuf) {
+ return ((ByteBuf) msg).retain();
+ }
+ if (msg instanceof MemcacheContent) {
+ return ((MemcacheContent) msg).content().retain();
+ }
+ if (msg instanceof FileRegion) {
+ return ((FileRegion) msg).retain();
+ }
+ throw new IllegalStateException("unexpected message type: " + msg.getClass().getSimpleName());
+ }
+
+}
diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheClientCodec.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheClientCodec.java
new file mode 100644
index 0000000000..89ecc114d5
--- /dev/null
+++ b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheClientCodec.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2013 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package io.netty.handler.codec.memcache.binary;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.CombinedChannelDuplexHandler;
+import io.netty.handler.codec.PrematureChannelClosureException;
+import io.netty.handler.codec.memcache.LastMemcacheContent;
+
+import java.util.List;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * The client codec that combines the proper encoder and decoder.
+ *
+ * Use this codec if you want to implement a memcache client that speaks the binary protocol. It
+ * combines both the {@link BinaryMemcacheResponseDecoder} and the {@link BinaryMemcacheRequestEncoder}.
+ *
+ * Optionally, it counts the number of outstanding responses and raises an exception if - on connection
+ * close - the list is not 0 (this is turned off by default). You can also define a chunk size for the
+ * content, which defaults to 8192. This chunk size is the maximum, so if smaller chunks arrive they
+ * will be passed up the pipeline and not queued up to the chunk size.
+ */
+public final class BinaryMemcacheClientCodec
+ extends CombinedChannelDuplexHandler {
+
+ private final boolean failOnMissingResponse;
+ private final AtomicLong requestResponseCounter = new AtomicLong();
+
+ /**
+ * Create a new {@link BinaryMemcacheClientCodec} with the default settings applied.
+ */
+ public BinaryMemcacheClientCodec() {
+ this(Decoder.DEFAULT_MAX_CHUNK_SIZE);
+ }
+
+ /**
+ * Create a new {@link BinaryMemcacheClientCodec} and set a custom chunk size.
+ *
+ * @param decodeChunkSize the maximum chunk size.
+ */
+ public BinaryMemcacheClientCodec(int decodeChunkSize) {
+ this(decodeChunkSize, false);
+ }
+
+ /**
+ * Create a new {@link BinaryMemcacheClientCodec} with custom settings.
+ *
+ * @param decodeChunkSize the maximum chunk size.
+ * @param failOnMissingResponse report if after close there are outstanding requests.
+ */
+ public BinaryMemcacheClientCodec(int decodeChunkSize, boolean failOnMissingResponse) {
+ this.failOnMissingResponse = failOnMissingResponse;
+ init(new Decoder(decodeChunkSize), new Encoder());
+ }
+
+ private final class Encoder extends BinaryMemcacheRequestEncoder {
+
+ @Override
+ protected void encode(ChannelHandlerContext ctx, Object msg, List out) throws Exception {
+ super.encode(ctx, msg, out);
+
+ if (failOnMissingResponse && msg instanceof LastMemcacheContent) {
+ requestResponseCounter.incrementAndGet();
+ }
+ }
+ }
+
+ private final class Decoder extends BinaryMemcacheResponseDecoder {
+
+ public Decoder(int chunkSize) {
+ super(chunkSize);
+ }
+
+ @Override
+ protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception {
+ int oldSize = out.size();
+ super.decode(ctx, in, out);
+
+ if (failOnMissingResponse) {
+ int size = out.size();
+ for (int i = oldSize; i < size; size++) {
+ Object msg = out.get(i);
+ if (msg != null && msg instanceof LastMemcacheContent) {
+ requestResponseCounter.decrementAndGet();
+ }
+ }
+ }
+ }
+
+ @Override
+ public void channelInactive(ChannelHandlerContext ctx) throws Exception {
+ super.channelInactive(ctx);
+
+ if (failOnMissingResponse) {
+ long missingResponses = requestResponseCounter.get();
+ if (missingResponses > 0) {
+ ctx.fireExceptionCaught(new PrematureChannelClosureException(
+ "channel gone inactive with " + missingResponses +
+ " missing response(s)"));
+ }
+ }
+ }
+ }
+
+}
diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheDecoder.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheDecoder.java
new file mode 100644
index 0000000000..bc9f14e371
--- /dev/null
+++ b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheDecoder.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright 2013 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package io.netty.handler.codec.memcache.binary;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.memcache.DefaultLastMemcacheContent;
+import io.netty.handler.codec.memcache.DefaultMemcacheContent;
+import io.netty.handler.codec.memcache.LastMemcacheContent;
+import io.netty.handler.codec.memcache.MemcacheContent;
+import io.netty.handler.codec.memcache.MemcacheObjectDecoder;
+import io.netty.util.CharsetUtil;
+
+import java.util.List;
+
+import static io.netty.buffer.ByteBufUtil.readBytes;
+
+/**
+ * Decoder for both {@link BinaryMemcacheRequest} and {@link BinaryMemcacheResponse}.
+ *
+ * The difference in the protocols (header) is implemented by the subclasses.
+ */
+public abstract class BinaryMemcacheDecoder
+ extends MemcacheObjectDecoder {
+
+ public static final int DEFAULT_MAX_CHUNK_SIZE = 8192;
+
+ private final int chunkSize;
+
+ private H currentHeader;
+ private ByteBuf currentExtras;
+ private String currentKey;
+ private int alreadyReadChunkSize;
+
+ private State state = State.READ_HEADER;
+
+ /**
+ * Create a new {@link BinaryMemcacheDecoder} with default settings.
+ */
+ public BinaryMemcacheDecoder() {
+ this(DEFAULT_MAX_CHUNK_SIZE);
+ }
+
+ /**
+ * Create a new {@link BinaryMemcacheDecoder} with custom settings.
+ *
+ * @param chunkSize the maximum chunk size of the payload.
+ */
+ public BinaryMemcacheDecoder(int chunkSize) {
+ if (chunkSize < 0) {
+ throw new IllegalArgumentException("chunkSize must be a positive integer: " + chunkSize);
+ }
+
+ this.chunkSize = chunkSize;
+ }
+
+ @Override
+ protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) throws Exception {
+ switch (state) {
+ case READ_HEADER:
+ if (in.readableBytes() < 24) {
+ return;
+ }
+ resetDecoder();
+
+ currentHeader = decodeHeader(in);
+ state = State.READ_EXTRAS;
+ case READ_EXTRAS:
+ byte extrasLength = currentHeader.getExtrasLength();
+ if (extrasLength > 0) {
+ if (in.readableBytes() < extrasLength) {
+ return;
+ }
+
+ currentExtras = readBytes(ctx.alloc(), in, extrasLength);
+ }
+
+ state = State.READ_KEY;
+ case READ_KEY:
+ short keyLength = currentHeader.getKeyLength();
+ if (keyLength > 0) {
+ if (in.readableBytes() < keyLength) {
+ return;
+ }
+
+ currentKey = readBytes(ctx.alloc(), in, keyLength).toString(CharsetUtil.UTF_8);
+ }
+
+ out.add(buildMessage(currentHeader, currentExtras, currentKey));
+ currentExtras = null;
+ state = State.READ_VALUE;
+ case READ_VALUE:
+ int valueLength = currentHeader.getTotalBodyLength()
+ - currentHeader.getKeyLength()
+ - currentHeader.getExtrasLength();
+ int toRead = in.readableBytes();
+ if (valueLength > 0) {
+ if (toRead == 0) {
+ return;
+ } else if (toRead > chunkSize) {
+ toRead = chunkSize;
+ }
+
+ if (toRead > valueLength) {
+ toRead = valueLength;
+ }
+
+ ByteBuf chunkBuffer = readBytes(ctx.alloc(), in, toRead);
+ boolean isLast = (alreadyReadChunkSize + toRead) >= valueLength;
+ MemcacheContent chunk = isLast
+ ? new DefaultLastMemcacheContent(chunkBuffer)
+ : new DefaultMemcacheContent(chunkBuffer);
+ alreadyReadChunkSize += toRead;
+
+ out.add(chunk);
+ if (alreadyReadChunkSize < valueLength) {
+ return;
+ }
+ } else {
+ out.add(LastMemcacheContent.EMPTY_LAST_CONTENT);
+ }
+
+ state = State.READ_HEADER;
+ return;
+ default:
+ throw new Error("Unknown state reached: " + state);
+ }
+ }
+
+ /**
+ * When the channel goes inactive, release all frames to prevent data leaks.
+ *
+ * @param ctx handler context
+ * @throws Exception
+ */
+ @Override
+ public void channelInactive(ChannelHandlerContext ctx) throws Exception {
+ super.channelInactive(ctx);
+
+ if (currentExtras != null) {
+ currentExtras.release();
+ }
+
+ resetDecoder();
+ }
+
+ /**
+ * Prepare for next decoding iteration.
+ */
+ protected void resetDecoder() {
+ currentHeader = null;
+ currentExtras = null;
+ currentKey = null;
+ alreadyReadChunkSize = 0;
+ }
+
+ /**
+ * Decode and return the parsed {@link BinaryMemcacheMessageHeader}.
+ *
+ * @param in the incoming buffer.
+ * @return the decoded header.
+ */
+ protected abstract H decodeHeader(ByteBuf in);
+
+ /**
+ * Build the complete message, based on the information decoded.
+ *
+ * @param header the header of the message.
+ * @param extras possible extras.
+ * @param key possible key.
+ * @return the decoded message.
+ */
+ protected abstract M buildMessage(H header, ByteBuf extras, String key);
+
+ /**
+ * Contains all states this decoder can possibly be in.
+ *
+ * Note that most of the states can be optional, the only one required is reading
+ * the header ({@link #READ_HEADER}. All other steps depend on the length fields
+ * in the header and will be executed conditionally.
+ */
+ enum State {
+ /**
+ * Currently reading the header portion.
+ */
+ READ_HEADER,
+
+ /**
+ * Currently reading the extras portion (optional).
+ */
+ READ_EXTRAS,
+
+ /**
+ * Currently reading the key portion (optional).
+ */
+ READ_KEY,
+
+ /**
+ * Currently reading the value chunks (optional).
+ */
+ READ_VALUE
+ }
+
+}
diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheEncoder.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheEncoder.java
new file mode 100644
index 0000000000..242f2c4420
--- /dev/null
+++ b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheEncoder.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2013 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package io.netty.handler.codec.memcache.binary;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.memcache.MemcacheObjectEncoder;
+import io.netty.util.CharsetUtil;
+
+/**
+ * A {@link io.netty.handler.codec.MessageToByteEncoder} that encodes binary memache messages into bytes.
+ */
+public abstract class BinaryMemcacheEncoder
+ extends MemcacheObjectEncoder {
+
+ @Override
+ protected ByteBuf encodeMessage(ChannelHandlerContext ctx, M msg) {
+ ByteBuf buf = ctx.alloc().buffer();
+
+ encodeHeader(buf, (H) msg.getHeader());
+ encodeExtras(buf, msg.getExtras());
+ encodeKey(buf, msg.getKey());
+
+ return buf;
+ }
+
+ /**
+ * Encode the extras.
+ *
+ * @param buf the {@link ByteBuf} to write into.
+ * @param extras the extras to encode.
+ */
+ private void encodeExtras(ByteBuf buf, ByteBuf extras) {
+ if (extras == null) {
+ return;
+ }
+
+ buf.writeBytes(extras);
+ }
+
+ /**
+ * Encode the key.
+ *
+ * @param buf the {@link ByteBuf} to write into.
+ * @param key the key to encode.
+ */
+ private void encodeKey(ByteBuf buf, String key) {
+ if (key == null || key.isEmpty()) {
+ return;
+ }
+
+ buf.writeBytes(Unpooled.copiedBuffer(key, CharsetUtil.UTF_8));
+ }
+
+ /**
+ * Encode the header.
+ *
+ * This methods needs to be implemented by a sub class because the header is different
+ * for both requests and responses.
+ *
+ * @param buf the {@link ByteBuf} to write into.
+ * @param header the header to encode.
+ */
+ protected abstract void encodeHeader(ByteBuf buf, H header);
+
+}
diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheMessage.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheMessage.java
new file mode 100644
index 0000000000..e7371e79cd
--- /dev/null
+++ b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheMessage.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2013 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package io.netty.handler.codec.memcache.binary;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.handler.codec.memcache.MemcacheMessage;
+import io.netty.handler.codec.memcache.MemcacheObject;
+
+/**
+ * An interface that defines a binary Memcache message, providing common properties for
+ * {@link BinaryMemcacheRequest} and {@link BinaryMemcacheResponse}.
+ *
+ * A {@link BinaryMemcacheMessage} always consists of a header and optional extras or/and
+ * a key.
+ *
+ * @see BinaryMemcacheMessageHeader
+ * @see BinaryMemcacheRequest
+ * @see BinaryMemcacheResponse
+ */
+public interface BinaryMemcacheMessage extends MemcacheMessage {
+
+ /**
+ * Returns the {@link BinaryMemcacheMessageHeader} which contains the full required header.
+ *
+ * @return the required header.
+ */
+ H getHeader();
+
+ /**
+ * Returns the optional key of the document.
+ *
+ * @return the key of the document.
+ */
+ String getKey();
+
+ /**
+ * Returns a {@link ByteBuf} representation of the optional extras.
+ *
+ * @return the optional extras.
+ */
+ ByteBuf getExtras();
+
+ /**
+ * Increases the reference count by {@code 1}.
+ */
+ BinaryMemcacheMessage retain();
+
+ /**
+ * Increases the reference count by the specified {@code increment}.
+ */
+ BinaryMemcacheMessage retain(int increment);
+
+}
diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheMessageHeader.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheMessageHeader.java
new file mode 100644
index 0000000000..8646ef58ac
--- /dev/null
+++ b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheMessageHeader.java
@@ -0,0 +1,157 @@
+/*
+ * Copyright 2013 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package io.netty.handler.codec.memcache.binary;
+
+/**
+ * Contains all common header fields in a {@link BinaryMemcacheMessage}.
+ *
+ * Since the header is different for a {@link BinaryMemcacheRequest} and {@link BinaryMemcacheResponse}, see
+ * {@link BinaryMemcacheRequestHeader} and {@link BinaryMemcacheResponseHeader}.
+ *
+ * The {@link BinaryMemcacheMessageHeader} is always 24 bytes in length and needs to be filled up if values are
+ * not set.
+ *
+ * Fore more information, see the official protocol specification
+ * here .
+ */
+public interface BinaryMemcacheMessageHeader {
+
+ /**
+ * Returns the magic byte for the message.
+ *
+ * @return the magic byte.
+ */
+ byte getMagic();
+
+ /**
+ * Sets the magic byte.
+ *
+ * @param magic the magic byte to use.
+ * @see io.netty.handler.codec.memcache.binary.util.BinaryMemcacheOpcodes for typesafe opcodes.
+ */
+ BinaryMemcacheMessageHeader setMagic(byte magic);
+
+ /**
+ * Returns the opcode for the message.
+ *
+ * @return the opcode.
+ */
+ byte getOpcode();
+
+ /**
+ * Sets the opcode for the message.
+ *
+ * @param code the opcode to use.
+ */
+ BinaryMemcacheMessageHeader setOpcode(byte code);
+
+ /**
+ * Returns the key length of the message.
+ *
+ * This may return 0, since the key is optional.
+ *
+ * @return the key length.
+ */
+ short getKeyLength();
+
+ /**
+ * Set the key length of the message.
+ *
+ * This may be 0, since the key is optional.
+ *
+ * @param keyLength the key length to use.
+ */
+ BinaryMemcacheMessageHeader setKeyLength(short keyLength);
+
+ /**
+ * Return the extras length of the message.
+ *
+ * This may be 0, since the extras content is optional.
+ *
+ * @return the extras length.
+ */
+ byte getExtrasLength();
+
+ /**
+ * Set the extras length of the message.
+ *
+ * This may be 0, since the extras content is optional.
+ *
+ * @param extrasLength the extras length.
+ */
+ BinaryMemcacheMessageHeader setExtrasLength(byte extrasLength);
+
+ /**
+ * Returns the data type of the message.
+ *
+ * @return the data type of the message.
+ */
+ byte getDataType();
+
+ /**
+ * Sets the data type of the message.
+ *
+ * @param dataType the data type of the message.
+ */
+ BinaryMemcacheMessageHeader setDataType(byte dataType);
+
+ /**
+ * Returns the total body length.
+ *
+ * Note that this may be 0, since the body is optional.
+ *
+ * @return the total body length.
+ */
+ int getTotalBodyLength();
+
+ /**
+ * Sets the total body length.
+ *
+ * Note that this may be 0, since the body length is optional.
+ *
+ * @param totalBodyLength the total body length.
+ */
+ BinaryMemcacheMessageHeader setTotalBodyLength(int totalBodyLength);
+
+ /**
+ * Returns the opaque value.
+ *
+ * @return the opaque value.
+ */
+ int getOpaque();
+
+ /**
+ * Sets the opaque value.
+ *
+ * @param opaque the opqaue value to use.
+ */
+ BinaryMemcacheMessageHeader setOpaque(int opaque);
+
+ /**
+ * Returns the CAS identifier.
+ *
+ * @return the CAS identifier.
+ */
+ long getCAS();
+
+ /**
+ * Sets the CAS identifier.
+ *
+ * @param cas the CAS identifier to use.
+ */
+ BinaryMemcacheMessageHeader setCAS(long cas);
+
+}
diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheObjectAggregator.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheObjectAggregator.java
new file mode 100644
index 0000000000..577d3f6d65
--- /dev/null
+++ b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheObjectAggregator.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2013 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package io.netty.handler.codec.memcache.binary;
+
+import io.netty.buffer.CompositeByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.DecoderResult;
+import io.netty.handler.codec.TooLongFrameException;
+import io.netty.handler.codec.memcache.FullMemcacheMessage;
+import io.netty.handler.codec.memcache.LastMemcacheContent;
+import io.netty.handler.codec.memcache.MemcacheContent;
+import io.netty.handler.codec.memcache.MemcacheMessage;
+import io.netty.handler.codec.memcache.MemcacheObject;
+import io.netty.handler.codec.memcache.MemcacheObjectAggregator;
+import io.netty.util.ReferenceCountUtil;
+
+import java.util.List;
+
+/**
+ * A {@link MemcacheObjectAggregator} for the binary protocol.
+ */
+public class BinaryMemcacheObjectAggregator extends MemcacheObjectAggregator {
+
+ private boolean tooLongFrameFound;
+
+ public BinaryMemcacheObjectAggregator(int maxContentLength) {
+ super(maxContentLength);
+ }
+
+ @Override
+ protected void decode(ChannelHandlerContext ctx, MemcacheObject msg, List out) throws Exception {
+ FullMemcacheMessage currentMessage = this.currentMessage;
+
+ if (msg instanceof MemcacheMessage) {
+ tooLongFrameFound = false;
+ MemcacheMessage m = (MemcacheMessage) msg;
+
+ if (!m.getDecoderResult().isSuccess()) {
+ this.currentMessage = null;
+ out.add(ReferenceCountUtil.retain(m));
+ return;
+ }
+
+ if (msg instanceof BinaryMemcacheRequest) {
+ BinaryMemcacheRequest request = (BinaryMemcacheRequest) msg;
+ this.currentMessage = new DefaultFullBinaryMemcacheRequest(request.getHeader(), request.getKey(),
+ request.getExtras(), Unpooled.compositeBuffer(getMaxCumulationBufferComponents()));
+ } else if (msg instanceof BinaryMemcacheResponse) {
+ BinaryMemcacheResponse response = (BinaryMemcacheResponse) msg;
+ this.currentMessage = new DefaultFullBinaryMemcacheResponse(response.getHeader(), response.getKey(),
+ response.getExtras(), Unpooled.compositeBuffer(getMaxCumulationBufferComponents()));
+ } else {
+ throw new Error();
+ }
+ } else if (msg instanceof MemcacheContent) {
+ if (tooLongFrameFound) {
+ if (msg instanceof LastMemcacheContent) {
+ this.currentMessage = null;
+ }
+ return;
+ }
+
+ MemcacheContent chunk = (MemcacheContent) msg;
+ CompositeByteBuf content = (CompositeByteBuf) currentMessage.content();
+
+ if (content.readableBytes() > getMaxContentLength() - chunk.content().readableBytes()) {
+ tooLongFrameFound = true;
+
+ currentMessage.release();
+ this.currentMessage = null;
+
+ throw new TooLongFrameException("Memcache content length exceeded " + getMaxContentLength()
+ + " bytes.");
+ }
+
+ if (chunk.content().isReadable()) {
+ chunk.retain();
+ content.addComponent(chunk.content());
+ content.writerIndex(content.writerIndex() + chunk.content().readableBytes());
+ }
+
+ final boolean last;
+ if (!chunk.getDecoderResult().isSuccess()) {
+ currentMessage.setDecoderResult(
+ DecoderResult.failure(chunk.getDecoderResult().cause()));
+ last = true;
+ } else {
+ last = chunk instanceof LastMemcacheContent;
+ }
+
+ if (last) {
+ this.currentMessage = null;
+ out.add(currentMessage);
+ }
+ } else {
+ throw new Error();
+ }
+ }
+
+}
diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheRequest.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheRequest.java
new file mode 100644
index 0000000000..7be1b6ee0c
--- /dev/null
+++ b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheRequest.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2013 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package io.netty.handler.codec.memcache.binary;
+
+/**
+ * Represents a full {@link BinaryMemcacheRequest}, which contains the header and optional key and extras.
+ */
+public interface BinaryMemcacheRequest extends BinaryMemcacheMessage {
+
+ /**
+ * Returns the {@link BinaryMemcacheRequestHeader} which contains the full required request header.
+ *
+ * @return the required request header.
+ */
+ BinaryMemcacheRequestHeader getHeader();
+
+}
diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheRequestDecoder.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheRequestDecoder.java
new file mode 100644
index 0000000000..2dc74a79c1
--- /dev/null
+++ b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheRequestDecoder.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2013 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package io.netty.handler.codec.memcache.binary;
+
+import io.netty.buffer.ByteBuf;
+
+/**
+ * The decoder part which takes care of decoding the request-specific headers.
+ */
+public class BinaryMemcacheRequestDecoder
+ extends BinaryMemcacheDecoder {
+
+ public BinaryMemcacheRequestDecoder() {
+ this(DEFAULT_MAX_CHUNK_SIZE);
+ }
+
+ public BinaryMemcacheRequestDecoder(int chunkSize) {
+ super(chunkSize);
+ }
+
+ @Override
+ protected BinaryMemcacheRequestHeader decodeHeader(ByteBuf in) {
+ BinaryMemcacheRequestHeader header = new DefaultBinaryMemcacheRequestHeader();
+ header.setMagic(in.readByte());
+ header.setOpcode(in.readByte());
+ header.setKeyLength(in.readShort());
+ header.setExtrasLength(in.readByte());
+ header.setDataType(in.readByte());
+ header.setReserved(in.readShort());
+ header.setTotalBodyLength(in.readInt());
+ header.setOpaque(in.readInt());
+ header.setCAS(in.readLong());
+ return header;
+ }
+
+ @Override
+ protected BinaryMemcacheRequest buildMessage(BinaryMemcacheRequestHeader header, ByteBuf extras, String key) {
+ return new DefaultBinaryMemcacheRequest(header, key, extras);
+ }
+
+}
diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheRequestEncoder.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheRequestEncoder.java
new file mode 100644
index 0000000000..355a1518a4
--- /dev/null
+++ b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheRequestEncoder.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2013 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package io.netty.handler.codec.memcache.binary;
+
+import io.netty.buffer.ByteBuf;
+
+/**
+ * The encoder part which takes care of encoding the request headers.
+ */
+public class BinaryMemcacheRequestEncoder
+ extends BinaryMemcacheEncoder {
+
+ @Override
+ protected void encodeHeader(ByteBuf buf, BinaryMemcacheRequestHeader header) {
+ buf.writeByte(header.getMagic());
+ buf.writeByte(header.getOpcode());
+ buf.writeShort(header.getKeyLength());
+ buf.writeByte(header.getExtrasLength());
+ buf.writeByte(header.getDataType());
+ buf.writeShort(header.getReserved());
+ buf.writeInt(header.getTotalBodyLength());
+ buf.writeInt(header.getOpaque());
+ buf.writeLong(header.getCAS());
+ }
+
+}
diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheRequestHeader.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheRequestHeader.java
new file mode 100644
index 0000000000..d9f76815d8
--- /dev/null
+++ b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheRequestHeader.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2013 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package io.netty.handler.codec.memcache.binary;
+
+/**
+ * Extends the common {@link BinaryMemcacheMessageHeader} header fields with hose who can only show up in
+ * {@link BinaryMemcacheRequest} messages.
+ *
+ * Note that while the additional field in the request is called "reserved", it can still be used for a custom
+ * memcached implementation. It will not be mirrored back like the
+ * {@link io.netty.handler.codec.memcache.binary.BinaryMemcacheRequestHeader#getOpaque()} field, because in the
+ * {@link BinaryMemcacheResponseHeader}, the status field is set there instead.
+ */
+public interface BinaryMemcacheRequestHeader extends BinaryMemcacheMessageHeader {
+
+ /**
+ * Returns the reserved field value.
+ *
+ * @return the reserved field value.
+ */
+ short getReserved();
+
+ /**
+ * Sets the reserved field value.
+ *
+ * @param reserved the reserved field value.
+ */
+ BinaryMemcacheRequestHeader setReserved(short reserved);
+
+}
diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheResponse.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheResponse.java
new file mode 100644
index 0000000000..63d623ab44
--- /dev/null
+++ b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheResponse.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright 2013 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package io.netty.handler.codec.memcache.binary;
+
+/**
+ * Represents a full {@link BinaryMemcacheResponse}, which contains the header and optional key and extras.
+ */
+public interface BinaryMemcacheResponse extends BinaryMemcacheMessage {
+
+ /**
+ * Returns the {@link BinaryMemcacheResponseHeader} which contains the full required response header.
+ *
+ * @return the required response header.
+ */
+ BinaryMemcacheResponseHeader getHeader();
+
+}
diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheResponseDecoder.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheResponseDecoder.java
new file mode 100644
index 0000000000..605872b170
--- /dev/null
+++ b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheResponseDecoder.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2013 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package io.netty.handler.codec.memcache.binary;
+
+import io.netty.buffer.ByteBuf;
+
+/**
+ * The decoder which takes care of decoding the response headers.
+ */
+public class BinaryMemcacheResponseDecoder
+ extends BinaryMemcacheDecoder {
+
+ public BinaryMemcacheResponseDecoder() {
+ this(DEFAULT_MAX_CHUNK_SIZE);
+ }
+
+ public BinaryMemcacheResponseDecoder(int chunkSize) {
+ super(chunkSize);
+ }
+
+ @Override
+ protected BinaryMemcacheResponseHeader decodeHeader(ByteBuf in) {
+ BinaryMemcacheResponseHeader header = new DefaultBinaryMemcacheResponseHeader();
+ header.setMagic(in.readByte());
+ header.setOpcode(in.readByte());
+ header.setKeyLength(in.readShort());
+ header.setExtrasLength(in.readByte());
+ header.setDataType(in.readByte());
+ header.setStatus(in.readShort());
+ header.setTotalBodyLength(in.readInt());
+ header.setOpaque(in.readInt());
+ header.setCAS(in.readLong());
+ return header;
+ }
+
+ @Override
+ protected BinaryMemcacheResponse buildMessage(BinaryMemcacheResponseHeader header, ByteBuf extras, String key) {
+ return new DefaultBinaryMemcacheResponse(header, key, extras);
+ }
+
+}
diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheResponseEncoder.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheResponseEncoder.java
new file mode 100644
index 0000000000..2ddc345eca
--- /dev/null
+++ b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheResponseEncoder.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2013 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package io.netty.handler.codec.memcache.binary;
+
+import io.netty.buffer.ByteBuf;
+
+/**
+ * The encoder which takes care of encoding the response headers.
+ */
+public class BinaryMemcacheResponseEncoder
+ extends BinaryMemcacheEncoder {
+
+ @Override
+ protected void encodeHeader(ByteBuf buf, BinaryMemcacheResponseHeader header) {
+ buf.writeByte(header.getMagic());
+ buf.writeByte(header.getOpcode());
+ buf.writeShort(header.getKeyLength());
+ buf.writeByte(header.getExtrasLength());
+ buf.writeByte(header.getDataType());
+ buf.writeShort(header.getStatus());
+ buf.writeInt(header.getTotalBodyLength());
+ buf.writeInt(header.getOpaque());
+ buf.writeLong(header.getCAS());
+ }
+
+}
diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheResponseHeader.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheResponseHeader.java
new file mode 100644
index 0000000000..43e55b329a
--- /dev/null
+++ b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheResponseHeader.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2013 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package io.netty.handler.codec.memcache.binary;
+
+/**
+ * Extends the common {@link BinaryMemcacheMessageHeader} header fields with hose who can only show up in
+ * {@link BinaryMemcacheResponse} messages.
+ *
+ * @see io.netty.handler.codec.memcache.binary.util.BinaryMemcacheResponseStatus
+ */
+public interface BinaryMemcacheResponseHeader extends BinaryMemcacheMessageHeader {
+
+ /**
+ * Returns the status of the response.
+ *
+ * @return the status of the response.
+ */
+ short getStatus();
+
+ /**
+ * Sets the status of the response.
+ *
+ * @param status the status to set.
+ */
+ BinaryMemcacheResponseHeader setStatus(short status);
+
+}
diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheServerCodec.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheServerCodec.java
new file mode 100644
index 0000000000..2a0dda5ed1
--- /dev/null
+++ b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheServerCodec.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2013 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package io.netty.handler.codec.memcache.binary;
+
+import io.netty.channel.CombinedChannelDuplexHandler;
+
+/**
+ * The full server codec that combines the correct encoder and decoder.
+ *
+ * Use this codec if you need to implement a server that speaks the memache binary protocol.
+ * Internally, it combines the {@link BinaryMemcacheRequestDecoder} and the
+ * {@link BinaryMemcacheResponseEncoder} to request decoding and response encoding.
+ */
+public class BinaryMemcacheServerCodec
+ extends CombinedChannelDuplexHandler {
+
+ public BinaryMemcacheServerCodec() {
+ this(BinaryMemcacheRequestDecoder.DEFAULT_MAX_CHUNK_SIZE);
+ }
+
+ public BinaryMemcacheServerCodec(int decodeChunkSize) {
+ init(new BinaryMemcacheRequestDecoder(decodeChunkSize), new BinaryMemcacheResponseEncoder());
+ }
+}
diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/DefaultBinaryMemcacheMessage.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/DefaultBinaryMemcacheMessage.java
new file mode 100644
index 0000000000..4101e38793
--- /dev/null
+++ b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/DefaultBinaryMemcacheMessage.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2013 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package io.netty.handler.codec.memcache.binary;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.handler.codec.memcache.DefaultMemcacheObject;
+import io.netty.util.AbstractReferenceCounted;
+import io.netty.util.ReferenceCounted;
+
+/**
+ * Default implementation of a {@link BinaryMemcacheMessage}.
+ */
+public abstract class DefaultBinaryMemcacheMessage
+ extends DefaultMemcacheObject
+ implements BinaryMemcacheMessage {
+
+ /**
+ * Contains the message header.
+ */
+ private final H header;
+
+ /**
+ * Contains the optional key.
+ */
+ private final String key;
+
+ /**
+ * Contains the optional extras.
+ */
+ private final ByteBuf extras;
+
+ /**
+ * Create a new instance with all properties set.
+ *
+ * @param header the message header.
+ * @param key the message key.
+ * @param extras the message extras.
+ */
+ public DefaultBinaryMemcacheMessage(H header, String key, ByteBuf extras) {
+ this.header = header;
+ this.key = key;
+ this.extras = extras;
+ }
+
+ @Override
+ public H getHeader() {
+ return header;
+ }
+
+ @Override
+ public String getKey() {
+ return key;
+ }
+
+ @Override
+ public ByteBuf getExtras() {
+ return extras;
+ }
+
+ @Override
+ public int refCnt() {
+ if (extras != null) {
+ return extras.refCnt();
+ }
+ return 1;
+ }
+
+ @Override
+ public BinaryMemcacheMessage retain() {
+ if (extras != null) {
+ extras.retain();
+ }
+ return this;
+ }
+
+ @Override
+ public BinaryMemcacheMessage retain(int increment) {
+ if (extras != null) {
+ extras.retain(increment);
+ }
+ return this;
+ }
+
+ @Override
+ public boolean release() {
+ if (extras != null) {
+ return extras.release();
+ }
+ return false;
+ }
+
+ @Override
+ public boolean release(int decrement) {
+ if (extras != null) {
+ return extras.release(decrement);
+ }
+ return false;
+ }
+}
diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/DefaultBinaryMemcacheMessageHeader.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/DefaultBinaryMemcacheMessageHeader.java
new file mode 100644
index 0000000000..226b19e8d6
--- /dev/null
+++ b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/DefaultBinaryMemcacheMessageHeader.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright 2013 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package io.netty.handler.codec.memcache.binary;
+
+/**
+ * The default implementation of a {@link BinaryMemcacheMessageHeader}.
+ */
+public abstract class DefaultBinaryMemcacheMessageHeader implements BinaryMemcacheMessageHeader {
+
+ private byte magic;
+ private byte opcode;
+ private short keyLength;
+ private byte extrasLength;
+ private byte dataType;
+ private int totalBodyLength;
+ private int opaque;
+ private long cas;
+
+ @Override
+ public byte getMagic() {
+ return magic;
+ }
+
+ @Override
+ public BinaryMemcacheMessageHeader setMagic(byte magic) {
+ this.magic = magic;
+ return this;
+ }
+
+ @Override
+ public long getCAS() {
+ return cas;
+ }
+
+ @Override
+ public BinaryMemcacheMessageHeader setCAS(long cas) {
+ this.cas = cas;
+ return this;
+ }
+
+ @Override
+ public int getOpaque() {
+ return opaque;
+ }
+
+ @Override
+ public BinaryMemcacheMessageHeader setOpaque(int opaque) {
+ this.opaque = opaque;
+ return this;
+ }
+
+ @Override
+ public int getTotalBodyLength() {
+ return totalBodyLength;
+ }
+
+ @Override
+ public BinaryMemcacheMessageHeader setTotalBodyLength(int totalBodyLength) {
+ this.totalBodyLength = totalBodyLength;
+ return this;
+ }
+
+ @Override
+ public byte getDataType() {
+ return dataType;
+ }
+
+ @Override
+ public BinaryMemcacheMessageHeader setDataType(byte dataType) {
+ this.dataType = dataType;
+ return this;
+ }
+
+ @Override
+ public byte getExtrasLength() {
+ return extrasLength;
+ }
+
+ @Override
+ public BinaryMemcacheMessageHeader setExtrasLength(byte extrasLength) {
+ this.extrasLength = extrasLength;
+ return this;
+ }
+
+ @Override
+ public short getKeyLength() {
+ return keyLength;
+ }
+
+ @Override
+ public BinaryMemcacheMessageHeader setKeyLength(short keyLength) {
+ this.keyLength = keyLength;
+ return this;
+ }
+
+ @Override
+ public byte getOpcode() {
+ return opcode;
+ }
+
+ @Override
+ public BinaryMemcacheMessageHeader setOpcode(byte opcode) {
+ this.opcode = opcode;
+ return this;
+ }
+}
diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/DefaultBinaryMemcacheRequest.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/DefaultBinaryMemcacheRequest.java
new file mode 100644
index 0000000000..6561a8321e
--- /dev/null
+++ b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/DefaultBinaryMemcacheRequest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2013 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package io.netty.handler.codec.memcache.binary;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+
+/**
+ * The default implementation of the {@link BinaryMemcacheRequest}.
+ */
+public class DefaultBinaryMemcacheRequest extends DefaultBinaryMemcacheMessage
+ implements BinaryMemcacheRequest {
+
+ /**
+ * Create a new {@link DefaultBinaryMemcacheRequest} with the header only.
+ *
+ * @param header the header to use.
+ */
+ public DefaultBinaryMemcacheRequest(BinaryMemcacheRequestHeader header) {
+ this(header, null, Unpooled.EMPTY_BUFFER);
+ }
+
+ /**
+ * Create a new {@link DefaultBinaryMemcacheRequest} with the header and key.
+ *
+ * @param header the header to use.
+ * @param key the key to use.
+ */
+ public DefaultBinaryMemcacheRequest(BinaryMemcacheRequestHeader header, String key) {
+ this(header, key, Unpooled.EMPTY_BUFFER);
+ }
+
+ /**
+ * Create a new {@link DefaultBinaryMemcacheRequest} with the header and extras.
+ *
+ * @param header the header to use.
+ * @param extras the extras to use.
+ */
+ public DefaultBinaryMemcacheRequest(BinaryMemcacheRequestHeader header, ByteBuf extras) {
+ this(header, null, extras);
+ }
+
+ /**
+ * Create a new {@link DefaultBinaryMemcacheRequest} with the header only.
+ *
+ * @param header the header to use.
+ * @param key the key to use.
+ * @param extras the extras to use.
+ */
+ public DefaultBinaryMemcacheRequest(BinaryMemcacheRequestHeader header, String key, ByteBuf extras) {
+ super(header, key, extras);
+ }
+
+}
diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/DefaultBinaryMemcacheRequestHeader.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/DefaultBinaryMemcacheRequestHeader.java
new file mode 100644
index 0000000000..1216a0f523
--- /dev/null
+++ b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/DefaultBinaryMemcacheRequestHeader.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2013 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package io.netty.handler.codec.memcache.binary;
+
+/**
+ * The default implementation of a {@link BinaryMemcacheRequestHeader}.
+ */
+public class DefaultBinaryMemcacheRequestHeader extends DefaultBinaryMemcacheMessageHeader
+ implements BinaryMemcacheRequestHeader {
+
+ /**
+ * Default magic byte for a request.
+ */
+ public static final byte REQUEST_MAGIC_BYTE = (byte) 0x80;
+
+ private short reserved;
+
+ /**
+ * Create a new {@link BinaryMemcacheRequestHeader} and apply default values.
+ */
+ public DefaultBinaryMemcacheRequestHeader() {
+ setMagic(REQUEST_MAGIC_BYTE);
+ }
+
+ @Override
+ public short getReserved() {
+ return reserved;
+ }
+
+ @Override
+ public BinaryMemcacheRequestHeader setReserved(short reserved) {
+ this.reserved = reserved;
+ return this;
+ }
+}
diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/DefaultBinaryMemcacheResponse.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/DefaultBinaryMemcacheResponse.java
new file mode 100644
index 0000000000..5126cd1b49
--- /dev/null
+++ b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/DefaultBinaryMemcacheResponse.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2013 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package io.netty.handler.codec.memcache.binary;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+
+/**
+ * The default implementation of the {@link BinaryMemcacheResponse}.
+ */
+public class DefaultBinaryMemcacheResponse extends DefaultBinaryMemcacheMessage
+ implements BinaryMemcacheResponse {
+
+ /**
+ * Create a new {@link DefaultBinaryMemcacheResponse} with the header only.
+ *
+ * @param header the header to use.
+ */
+ public DefaultBinaryMemcacheResponse(BinaryMemcacheResponseHeader header) {
+ this(header, null, Unpooled.EMPTY_BUFFER);
+ }
+
+ /**
+ * Create a new {@link DefaultBinaryMemcacheResponse} with the header and key.
+ *
+ * @param header the header to use.
+ * @param key the key to use
+ */
+ public DefaultBinaryMemcacheResponse(BinaryMemcacheResponseHeader header, String key) {
+ this(header, key, Unpooled.EMPTY_BUFFER);
+ }
+
+ /**
+ * Create a new {@link DefaultBinaryMemcacheResponse} with the header and extras.
+ *
+ * @param header the header to use.
+ * @param extras the extras to use.
+ */
+ public DefaultBinaryMemcacheResponse(BinaryMemcacheResponseHeader header, ByteBuf extras) {
+ this(header, null, extras);
+ }
+
+ /**
+ * Create a new {@link DefaultBinaryMemcacheResponse} with the header, key and extras.
+ *
+ * @param header the header to use.
+ * @param key the key to use.
+ * @param extras the extras to use.
+ */
+ public DefaultBinaryMemcacheResponse(BinaryMemcacheResponseHeader header, String key, ByteBuf extras) {
+ super(header, key, extras);
+ }
+
+}
diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/DefaultBinaryMemcacheResponseHeader.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/DefaultBinaryMemcacheResponseHeader.java
new file mode 100644
index 0000000000..7fc94c88f3
--- /dev/null
+++ b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/DefaultBinaryMemcacheResponseHeader.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2013 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package io.netty.handler.codec.memcache.binary;
+
+/**
+ * The default implementation of a {@link BinaryMemcacheResponseHeader}.
+ */
+public class DefaultBinaryMemcacheResponseHeader extends DefaultBinaryMemcacheMessageHeader
+ implements BinaryMemcacheResponseHeader {
+
+ /**
+ * Default magic byte for a request.
+ */
+ public static final byte RESPONSE_MAGIC_BYTE = (byte) 0x81;
+
+ private short status;
+
+ /**
+ * Create a new {@link BinaryMemcacheRequestHeader} and apply default values.
+ */
+ public DefaultBinaryMemcacheResponseHeader() {
+ setMagic(RESPONSE_MAGIC_BYTE);
+ }
+
+ @Override
+ public short getStatus() {
+ return status;
+ }
+
+ @Override
+ public BinaryMemcacheResponseHeader setStatus(short status) {
+ this.status = status;
+ return this;
+ }
+}
diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/DefaultFullBinaryMemcacheRequest.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/DefaultFullBinaryMemcacheRequest.java
new file mode 100644
index 0000000000..821271b0a0
--- /dev/null
+++ b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/DefaultFullBinaryMemcacheRequest.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2013 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package io.netty.handler.codec.memcache.binary;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+
+/**
+ * The default implementation of a {@link FullBinaryMemcacheRequest}.
+ */
+public class DefaultFullBinaryMemcacheRequest extends DefaultBinaryMemcacheRequest
+ implements FullBinaryMemcacheRequest {
+
+ private final ByteBuf content;
+
+ /**
+ * Create a new {@link DefaultBinaryMemcacheRequest} with the header, key and extras.
+ *
+ * @param header the header to use.
+ * @param key the key to use.
+ * @param extras the extras to use.
+ */
+ public DefaultFullBinaryMemcacheRequest(BinaryMemcacheRequestHeader header, String key, ByteBuf extras) {
+ this(header, key, extras, Unpooled.buffer(0));
+ }
+
+ /**
+ * Create a new {@link DefaultBinaryMemcacheRequest} with the header, key, extras and content.
+ *
+ * @param header the header to use.
+ * @param key the key to use.
+ * @param extras the extras to use.
+ * @param content the content of the full request.
+ */
+ public DefaultFullBinaryMemcacheRequest(BinaryMemcacheRequestHeader header, String key, ByteBuf extras,
+ ByteBuf content) {
+ super(header, key, extras);
+ if (content == null) {
+ throw new NullPointerException("Supplied content is null.");
+ }
+
+ this.content = content;
+ }
+
+ @Override
+ public ByteBuf content() {
+ return content;
+ }
+
+ @Override
+ public int refCnt() {
+ return content.refCnt();
+ }
+
+ @Override
+ public FullBinaryMemcacheRequest retain() {
+ content.retain();
+ return this;
+ }
+
+ @Override
+ public FullBinaryMemcacheRequest retain(int increment) {
+ content.retain(increment);
+ return this;
+ }
+
+ @Override
+ public boolean release() {
+ return content.release();
+ }
+
+ @Override
+ public boolean release(int decrement) {
+ return content.release(decrement);
+ }
+
+ @Override
+ public FullBinaryMemcacheRequest copy() {
+ return new DefaultFullBinaryMemcacheRequest(getHeader(), getKey(), getExtras(), content().copy());
+ }
+
+ @Override
+ public FullBinaryMemcacheRequest duplicate() {
+ return new DefaultFullBinaryMemcacheRequest(getHeader(), getKey(), getExtras(), content().duplicate());
+ }
+
+}
diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/DefaultFullBinaryMemcacheResponse.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/DefaultFullBinaryMemcacheResponse.java
new file mode 100644
index 0000000000..f326b42205
--- /dev/null
+++ b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/DefaultFullBinaryMemcacheResponse.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2013 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package io.netty.handler.codec.memcache.binary;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+
+/**
+ * The default implementation of a {@link FullBinaryMemcacheResponse}.
+ */
+public class DefaultFullBinaryMemcacheResponse extends DefaultBinaryMemcacheResponse
+ implements FullBinaryMemcacheResponse {
+
+ private final ByteBuf content;
+
+ /**
+ * Create a new {@link DefaultFullBinaryMemcacheResponse} with the header, key and extras.
+ *
+ * @param header the header to use.
+ * @param key the key to use.
+ * @param extras the extras to use.
+ */
+ public DefaultFullBinaryMemcacheResponse(BinaryMemcacheResponseHeader header, String key, ByteBuf extras) {
+ this(header, key, extras, Unpooled.buffer(0));
+ }
+
+ /**
+ * Create a new {@link DefaultFullBinaryMemcacheResponse} with the header, key, extras and content.
+ *
+ * @param header the header to use.
+ * @param key the key to use.
+ * @param extras the extras to use.
+ * @param content the content of the full request.
+ */
+ public DefaultFullBinaryMemcacheResponse(BinaryMemcacheResponseHeader header, String key, ByteBuf extras,
+ ByteBuf content) {
+ super(header, key, extras);
+ if (content == null) {
+ throw new NullPointerException("Supplied content is null.");
+ }
+
+ this.content = content;
+ }
+
+ @Override
+ public ByteBuf content() {
+ return content;
+ }
+
+ @Override
+ public int refCnt() {
+ return content.refCnt();
+ }
+
+ @Override
+ public FullBinaryMemcacheResponse retain() {
+ content.retain();
+ return this;
+ }
+
+ @Override
+ public FullBinaryMemcacheResponse retain(int increment) {
+ content.retain(increment);
+ return this;
+ }
+
+ @Override
+ public boolean release() {
+ return content.release();
+ }
+
+ @Override
+ public boolean release(int decrement) {
+ return content.release(decrement);
+ }
+
+ @Override
+ public FullBinaryMemcacheResponse copy() {
+ return new DefaultFullBinaryMemcacheResponse(getHeader(), getKey(), getExtras(), content().copy());
+ }
+
+ @Override
+ public FullBinaryMemcacheResponse duplicate() {
+ return new DefaultFullBinaryMemcacheResponse(getHeader(), getKey(), getExtras(), content().duplicate());
+ }
+
+}
diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/FullBinaryMemcacheRequest.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/FullBinaryMemcacheRequest.java
new file mode 100644
index 0000000000..3310cb315e
--- /dev/null
+++ b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/FullBinaryMemcacheRequest.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2013 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package io.netty.handler.codec.memcache.binary;
+
+import io.netty.handler.codec.memcache.FullMemcacheMessage;
+
+/**
+ * A {@link BinaryMemcacheRequest} that also includes the content.
+ */
+public interface FullBinaryMemcacheRequest extends BinaryMemcacheRequest, FullMemcacheMessage {
+
+ @Override
+ FullBinaryMemcacheRequest copy();
+
+ @Override
+ FullBinaryMemcacheRequest retain(int increment);
+
+ @Override
+ FullBinaryMemcacheRequest retain();
+
+ @Override
+ FullBinaryMemcacheRequest duplicate();
+
+}
diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/FullBinaryMemcacheResponse.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/FullBinaryMemcacheResponse.java
new file mode 100644
index 0000000000..639c810a04
--- /dev/null
+++ b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/FullBinaryMemcacheResponse.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 2013 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package io.netty.handler.codec.memcache.binary;
+
+import io.netty.handler.codec.memcache.FullMemcacheMessage;
+
+/**
+ * A {@link BinaryMemcacheResponse} that also includes the content.
+ */
+public interface FullBinaryMemcacheResponse extends BinaryMemcacheResponse, FullMemcacheMessage {
+
+ @Override
+ FullBinaryMemcacheResponse copy();
+
+ @Override
+ FullBinaryMemcacheResponse retain(int increment);
+
+ @Override
+ FullBinaryMemcacheResponse retain();
+
+ @Override
+ FullBinaryMemcacheResponse duplicate();
+
+}
diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/package-info.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/package-info.java
new file mode 100644
index 0000000000..9b5491358e
--- /dev/null
+++ b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+/**
+ * Implementations and Interfaces for the Memcache Binary protocol.
+ */
+package io.netty.handler.codec.memcache.binary;
diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/util/BinaryMemcacheOpcodes.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/util/BinaryMemcacheOpcodes.java
new file mode 100644
index 0000000000..dcbaa0b9ce
--- /dev/null
+++ b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/util/BinaryMemcacheOpcodes.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2013 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package io.netty.handler.codec.memcache.binary.util;
+
+/**
+ * Represents all Opcodes that can occur in a {@link io.netty.handler.codec.memcache.binary.BinaryMemcacheMessage}.
+ *
+ * This class can be extended if a custom application needs to implement a superset of the normally supported
+ * operations by a vanilla memcached protocol.
+ */
+public final class BinaryMemcacheOpcodes {
+
+ private BinaryMemcacheOpcodes() {
+ // disallow construction
+ }
+
+ public static final byte GET = 0x00;
+ public static final byte SET = 0x01;
+ public static final byte ADD = 0x02;
+ public static final byte REPLACE = 0x03;
+ public static final byte DELETE = 0x04;
+ public static final byte INCREMENT = 0x05;
+ public static final byte DECREMENT = 0x06;
+ public static final byte QUIT = 0x07;
+ public static final byte FLUSH = 0x08;
+ public static final byte GETQ = 0x09;
+ public static final byte NOOP = 0x0a;
+ public static final byte VERSION = 0x0b;
+ public static final byte GETK = 0x0c;
+ public static final byte GETKQ = 0x0d;
+ public static final byte APPEND = 0x0e;
+ public static final byte PREPEND = 0x0f;
+ public static final byte STAT = 0x10;
+ public static final byte SETQ = 0x11;
+ public static final byte ADDQ = 0x12;
+ public static final byte REPLACEQ = 0x13;
+ public static final byte DELETEQ = 0x14;
+ public static final byte INCREMENTQ = 0x15;
+ public static final byte DECREMENTQ = 0x16;
+ public static final byte QUITQ = 0x17;
+ public static final byte FLUSHQ = 0x18;
+ public static final byte APPENDQ = 0x19;
+ public static final byte PREPENDQ = 0x1a;
+ public static final byte TOUCH = 0x1c;
+ public static final byte GAT = 0x1d;
+ public static final byte GATQ = 0x1e;
+ public static final byte GATK = 0x23;
+ public static final byte GATKQ = 0x24;
+ public static final byte SASL_LIST_MECHS = 0x20;
+ public static final byte SASL_AUTH = 0x21;
+ public static final byte SASL_STEP = 0x22;
+
+}
diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/util/BinaryMemcacheResponseStatus.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/util/BinaryMemcacheResponseStatus.java
new file mode 100644
index 0000000000..84c4b18728
--- /dev/null
+++ b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/util/BinaryMemcacheResponseStatus.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2013 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package io.netty.handler.codec.memcache.binary.util;
+
+/**
+ * Contains all possible status values a
+ * {@link io.netty.handler.codec.memcache.binary.BinaryMemcacheResponseHeader} can return.
+ */
+public final class BinaryMemcacheResponseStatus {
+
+ private BinaryMemcacheResponseStatus() {
+ // disallow construction
+ }
+
+ public static final short SUCCESS = 0x00;
+ public static final short KEY_ENOENT = 0x01;
+ public static final short KEY_EEXISTS = 0x02;
+ public static final short E2BIG = 0x03;
+ public static final short EINVA = 0x04;
+ public static final short NOT_STORED = 0x05;
+ public static final short DELTA_BADVAL = 0x06;
+ public static final short AUTH_ERROR = 0x20;
+ public static final short AUTH_CONTINUE = 0x21;
+ public static final short UNKNOWN_COMMAND = 0x81;
+ public static final short ENOMEM = 0x82;
+
+}
diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/util/package-info.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/util/package-info.java
new file mode 100644
index 0000000000..5f4366b629
--- /dev/null
+++ b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/binary/util/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+/**
+ * Utility helpers for the binary protocol.
+ */
+package io.netty.handler.codec.memcache.binary.util;
diff --git a/codec-memcache/src/main/java/io/netty/handler/codec/memcache/package-info.java b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/package-info.java
new file mode 100644
index 0000000000..d04be91ce9
--- /dev/null
+++ b/codec-memcache/src/main/java/io/netty/handler/codec/memcache/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * 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.
+ */
+
+/**
+ * Common superset of ascii and binary classes.
+ */
+package io.netty.handler.codec.memcache;
diff --git a/codec-memcache/src/test/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheDecoderTest.java b/codec-memcache/src/test/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheDecoderTest.java
new file mode 100644
index 0000000000..da4f3c772c
--- /dev/null
+++ b/codec-memcache/src/test/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheDecoderTest.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright 2013 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package io.netty.handler.codec.memcache.binary;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.embedded.EmbeddedChannel;
+import io.netty.handler.codec.memcache.LastMemcacheContent;
+import io.netty.handler.codec.memcache.MemcacheContent;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.IsNull.notNullValue;
+import static org.hamcrest.core.IsNull.nullValue;
+
+/**
+ * Verifies the correct functionality of the {@link BinaryMemcacheDecoder}.
+ *
+ * While technically there are both a {@link BinaryMemcacheRequestDecoder} and a {@link BinaryMemcacheResponseDecoder}
+ * they implement the same basics and just differ in the type of headers returned.
+ */
+public class BinaryMemcacheDecoderTest {
+
+ /**
+ * Represents a GET request header with a key size of three.
+ */
+ private static final byte[] GET_REQUEST = new byte[]{
+ (byte) 0x80, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x66, 0x6f, 0x6f
+ };
+
+ private static final byte[] SET_REQUEST_WITH_CONTENT = new byte[]{
+ (byte) 0x80, 0x01, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x0B,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x66, 0x6f, 0x6f,
+ 0x01, 0x02, 0x03, 0x04,
+ 0x05, 0x06, 0x07, 0x08
+ };
+
+ private EmbeddedChannel channel;
+
+ @Before
+ public void setup() throws Exception {
+ channel = new EmbeddedChannel(new BinaryMemcacheRequestDecoder());
+ }
+
+ /**
+ * This tests a simple GET request with a key as the value.
+ */
+ @Test
+ public void shouldDecodeRequestWithSimpleValue() {
+ ByteBuf incoming = Unpooled.buffer();
+ incoming.writeBytes(GET_REQUEST);
+ channel.writeInbound(incoming);
+
+ BinaryMemcacheRequest request = (BinaryMemcacheRequest) channel.readInbound();
+
+ assertThat(request, notNullValue());
+ assertThat(request.getHeader(), notNullValue());
+ assertThat(request.getKey(), notNullValue());
+ assertThat(request.getExtras(), nullValue());
+
+ BinaryMemcacheRequestHeader header = request.getHeader();
+ assertThat(header.getKeyLength(), is((short) 3));
+ assertThat(header.getExtrasLength(), is((byte) 0));
+ assertThat(header.getTotalBodyLength(), is(3));
+
+ request.release();
+ assertThat(channel.readInbound(), instanceOf(LastMemcacheContent.class));
+ }
+
+ /**
+ * This test makes sure that large content is emitted in chunks.
+ */
+ @Test
+ public void shouldDecodeRequestWithChunkedContent() {
+ int smallBatchSize = 2;
+ channel = new EmbeddedChannel(new BinaryMemcacheRequestDecoder(smallBatchSize));
+
+ ByteBuf incoming = Unpooled.buffer();
+ incoming.writeBytes(SET_REQUEST_WITH_CONTENT);
+ channel.writeInbound(incoming);
+
+ BinaryMemcacheRequest request = (BinaryMemcacheRequest) channel.readInbound();
+
+ assertThat(request, notNullValue());
+ assertThat(request.getHeader(), notNullValue());
+ assertThat(request.getKey(), notNullValue());
+ assertThat(request.getExtras(), nullValue());
+
+ BinaryMemcacheRequestHeader header = request.getHeader();
+ assertThat(header.getKeyLength(), is((short) 3));
+ assertThat(header.getExtrasLength(), is((byte) 0));
+ assertThat(header.getTotalBodyLength(), is(11));
+
+ int expectedContentChunks = 4;
+ for (int i = 1; i <= expectedContentChunks; i++) {
+ MemcacheContent content = (MemcacheContent) channel.readInbound();
+ if (i < expectedContentChunks) {
+ assertThat(content, instanceOf(MemcacheContent.class));
+ } else {
+ assertThat(content, instanceOf(LastMemcacheContent.class));
+ }
+ assertThat(content.content().readableBytes(), is(2));
+ }
+ assertThat(channel.readInbound(), nullValue());
+ }
+
+ /**
+ * This test makes sure that even when the decoder is confronted with various chunk
+ * sizes in the middle of decoding, it can recover and decode all the time eventually.
+ */
+ @Test
+ public void shouldHandleNonUniformNetworkBatches() {
+ ByteBuf incoming = Unpooled.copiedBuffer(SET_REQUEST_WITH_CONTENT);
+ while (incoming.isReadable()) {
+ channel.writeInbound(incoming.readBytes(5));
+ }
+
+ BinaryMemcacheRequest request = (BinaryMemcacheRequest) channel.readInbound();
+
+ assertThat(request, notNullValue());
+ assertThat(request.getHeader(), notNullValue());
+ assertThat(request.getKey(), notNullValue());
+ assertThat(request.getExtras(), nullValue());
+
+ MemcacheContent content1 = (MemcacheContent) channel.readInbound();
+ MemcacheContent content2 = (MemcacheContent) channel.readInbound();
+
+ assertThat(content1, instanceOf(MemcacheContent.class));
+ assertThat(content2, instanceOf(LastMemcacheContent.class));
+
+ assertThat(content1.content().readableBytes(), is(3));
+ assertThat(content2.content().readableBytes(), is(5));
+ }
+
+ /**
+ * This test makes sure that even when more requests arrive in the same batch, they
+ * get emitted as separate messages.
+ */
+ @Test
+ public void shouldHandleTwoMessagesInOneBatch() {
+ channel.writeInbound(Unpooled.buffer().writeBytes(GET_REQUEST).writeBytes(GET_REQUEST));
+
+ BinaryMemcacheRequest request = (BinaryMemcacheRequest) channel.readInbound();
+ assertThat(request, instanceOf(BinaryMemcacheRequest.class));
+ assertThat(request, notNullValue());
+ assertThat(channel.readInbound(), instanceOf(LastMemcacheContent.class));
+
+ request = (BinaryMemcacheRequest) channel.readInbound();
+ assertThat(request, instanceOf(BinaryMemcacheRequest.class));
+ assertThat(request, notNullValue());
+ assertThat(channel.readInbound(), instanceOf(LastMemcacheContent.class));
+ }
+
+}
diff --git a/codec-memcache/src/test/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheEncoderTest.java b/codec-memcache/src/test/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheEncoderTest.java
new file mode 100644
index 0000000000..5c5588f392
--- /dev/null
+++ b/codec-memcache/src/test/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheEncoderTest.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2013 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package io.netty.handler.codec.memcache.binary;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.embedded.EmbeddedChannel;
+import io.netty.handler.codec.EncoderException;
+import io.netty.handler.codec.memcache.DefaultLastMemcacheContent;
+import io.netty.handler.codec.memcache.DefaultMemcacheContent;
+import io.netty.handler.codec.memcache.binary.util.BinaryMemcacheOpcodes;
+import io.netty.util.CharsetUtil;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.core.IsEqual.equalTo;
+import static org.junit.Assert.assertThat;
+
+/**
+ * Verifies the correct functionality of the {@link BinaryMemcacheEncoder}.
+ */
+public class BinaryMemcacheEncoderTest {
+
+ public static int DEFAULT_HEADER_SIZE = 24;
+
+ private EmbeddedChannel channel;
+
+ @Before
+ public void setup() throws Exception {
+ channel = new EmbeddedChannel(new BinaryMemcacheRequestEncoder());
+ }
+
+ @Test
+ public void shouldEncodeDefaultHeader() {
+ BinaryMemcacheRequestHeader header = new DefaultBinaryMemcacheRequestHeader();
+ BinaryMemcacheRequest request = new DefaultBinaryMemcacheRequest(header);
+
+ boolean result = channel.writeOutbound(request);
+ assertThat(result, is(true));
+
+ ByteBuf written = (ByteBuf) channel.readOutbound();
+ assertThat(written.readableBytes(), is(DEFAULT_HEADER_SIZE));
+ assertThat(written.readByte(), is((byte) 0x80));
+ assertThat(written.readByte(), is((byte) 0x00));
+ }
+
+ @Test
+ public void shouldEncodeCustomHeader() {
+ BinaryMemcacheRequestHeader header = new DefaultBinaryMemcacheRequestHeader();
+ header.setMagic((byte) 0xAA);
+ header.setOpcode(BinaryMemcacheOpcodes.GET);
+ BinaryMemcacheRequest request = new DefaultBinaryMemcacheRequest(header);
+
+ boolean result = channel.writeOutbound(request);
+ assertThat(result, is(true));
+
+ ByteBuf written = (ByteBuf) channel.readOutbound();
+ assertThat(written.readableBytes(), is(DEFAULT_HEADER_SIZE));
+ assertThat(written.readByte(), is((byte) 0xAA));
+ assertThat(written.readByte(), is(BinaryMemcacheOpcodes.GET));
+ }
+
+ @Test
+ public void shouldEncodeExtras() {
+ String extrasContent = "netty<3memcache";
+ ByteBuf extras = Unpooled.copiedBuffer(extrasContent, CharsetUtil.UTF_8);
+ int extrasLength = extras.readableBytes();
+ BinaryMemcacheRequestHeader header = new DefaultBinaryMemcacheRequestHeader();
+ header.setExtrasLength((byte) extrasLength);
+ BinaryMemcacheRequest request = new DefaultBinaryMemcacheRequest(header, extras);
+
+ boolean result = channel.writeOutbound(request);
+ assertThat(result, is(true));
+
+ ByteBuf written = (ByteBuf) channel.readOutbound();
+ assertThat(written.readableBytes(), is(DEFAULT_HEADER_SIZE + extrasLength));
+ written.readBytes(DEFAULT_HEADER_SIZE);
+ assertThat(written.readBytes(extrasLength).toString(CharsetUtil.UTF_8), equalTo(extrasContent));
+ }
+
+ @Test
+ public void shouldEncodeKey() {
+ String key = "netty";
+ int keyLength = key.length();
+ BinaryMemcacheRequestHeader header = new DefaultBinaryMemcacheRequestHeader();
+ header.setKeyLength((byte) keyLength);
+ BinaryMemcacheRequest request = new DefaultBinaryMemcacheRequest(header, key);
+
+ boolean result = channel.writeOutbound(request);
+ assertThat(result, is(true));
+
+ ByteBuf written = (ByteBuf) channel.readOutbound();
+ assertThat(written.readableBytes(), is(DEFAULT_HEADER_SIZE + keyLength));
+ written.readBytes(DEFAULT_HEADER_SIZE);
+ assertThat(written.readBytes(keyLength).toString(CharsetUtil.UTF_8), equalTo(key));
+ }
+
+ @Test
+ public void shouldEncodeContent() {
+ DefaultMemcacheContent content1 =
+ new DefaultMemcacheContent(Unpooled.copiedBuffer("Netty", CharsetUtil.UTF_8));
+ DefaultLastMemcacheContent content2 =
+ new DefaultLastMemcacheContent(Unpooled.copiedBuffer(" Rocks!", CharsetUtil.UTF_8));
+ int totalBodyLength = content1.content().readableBytes() + content2.content().readableBytes();
+
+ BinaryMemcacheRequestHeader header = new DefaultBinaryMemcacheRequestHeader();
+ header.setTotalBodyLength(totalBodyLength);
+ BinaryMemcacheRequest request = new DefaultBinaryMemcacheRequest(header);
+
+ boolean result = channel.writeOutbound(request);
+ assertThat(result, is(true));
+ result = channel.writeOutbound(content1);
+ assertThat(result, is(true));
+ result = channel.writeOutbound(content2);
+ assertThat(result, is(true));
+
+ ByteBuf written = (ByteBuf) channel.readOutbound();
+ assertThat(written.readableBytes(), is(DEFAULT_HEADER_SIZE));
+ written = (ByteBuf) channel.readOutbound();
+ assertThat(written.readableBytes(), is(content1.content().readableBytes()));
+ assertThat(
+ written.readBytes(content1.content().readableBytes()).toString(CharsetUtil.UTF_8),
+ is("Netty")
+ );
+ written = (ByteBuf) channel.readOutbound();
+ assertThat(written.readableBytes(), is(content2.content().readableBytes()));
+ assertThat(
+ written.readBytes(content2.content().readableBytes()).toString(CharsetUtil.UTF_8),
+ is(" Rocks!")
+ );
+ }
+
+ @Test(expected = EncoderException.class)
+ public void shouldFailWithoutLastContent() {
+ channel.writeOutbound(new DefaultMemcacheContent(Unpooled.EMPTY_BUFFER));
+ channel.writeOutbound(
+ new DefaultBinaryMemcacheRequest(new DefaultBinaryMemcacheRequestHeader()));
+ }
+
+}
diff --git a/codec-memcache/src/test/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheObjectAggregatorTest.java b/codec-memcache/src/test/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheObjectAggregatorTest.java
new file mode 100644
index 0000000000..a98691cb73
--- /dev/null
+++ b/codec-memcache/src/test/java/io/netty/handler/codec/memcache/binary/BinaryMemcacheObjectAggregatorTest.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2013 The Netty Project
+ *
+ * The Netty Project licenses this file to you under the Apache License,
+ * version 2.0 (the "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+package io.netty.handler.codec.memcache.binary;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.embedded.EmbeddedChannel;
+import io.netty.handler.codec.memcache.LastMemcacheContent;
+import io.netty.handler.codec.memcache.MemcacheContent;
+import io.netty.util.CharsetUtil;
+import org.hamcrest.CoreMatchers;
+import org.junit.Before;
+import org.junit.Test;
+
+import java.nio.charset.Charset;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.core.IsNull.notNullValue;
+import static org.hamcrest.core.IsNull.nullValue;
+
+/**
+ * Verifies the correct functionality of the {@link BinaryMemcacheObjectAggregator}.
+ */
+public class BinaryMemcacheObjectAggregatorTest {
+
+ private static final byte[] SET_REQUEST_WITH_CONTENT = new byte[]{
+ (byte) 0x80, 0x01, 0x00, 0x03,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x0B,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00,
+ 0x66, 0x6f, 0x6f,
+ 0x01, 0x02, 0x03, 0x04,
+ 0x05, 0x06, 0x07, 0x08
+ };
+
+ public static final int MAX_CONTENT_SIZE = 2 << 10;
+
+ private EmbeddedChannel channel;
+
+ @Test
+ public void shouldAggregateChunksOnDecode() {
+ int smallBatchSize = 2;
+ channel = new EmbeddedChannel(
+ new BinaryMemcacheRequestDecoder(smallBatchSize),
+ new BinaryMemcacheObjectAggregator(MAX_CONTENT_SIZE));
+
+ ByteBuf incoming = Unpooled.buffer();
+ incoming.writeBytes(SET_REQUEST_WITH_CONTENT);
+ channel.writeInbound(incoming);
+
+ FullBinaryMemcacheRequest request = (FullBinaryMemcacheRequest) channel.readInbound();
+
+ assertThat(request, instanceOf(FullBinaryMemcacheRequest.class));
+ assertThat(request, notNullValue());
+ assertThat(request.getHeader(), notNullValue());
+ assertThat(request.getKey(), notNullValue());
+ assertThat(request.getExtras(), nullValue());
+
+ assertThat(request.content().readableBytes(), is(8));
+ assertThat(request.content().readByte(), is((byte) 0x01));
+ assertThat(request.content().readByte(), is((byte) 0x02));
+
+ assertThat(channel.readInbound(), nullValue());
+ }
+
+}
diff --git a/pom.xml b/pom.xml
index ed50b5af25..30078a636b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -89,6 +89,7 @@
buffer
codec
codec-http
+ codec-memcache
codec-socks
transport
transport-rxtx