[#910] Make use of ByteBufHolder in SPDY, HTTP multipart and WebSockets to allow for buffer pooling
This commit is contained in:
parent
f568aa42f0
commit
dfbecb796c
@ -353,4 +353,12 @@ public abstract class AbstractDiskHttpData extends AbstractHttpData {
|
|||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isFreed() {
|
||||||
|
if (file == null || !file.exists()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -15,8 +15,11 @@
|
|||||||
*/
|
*/
|
||||||
package io.netty.handler.codec.http.multipart;
|
package io.netty.handler.codec.http.multipart;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.channel.ChannelException;
|
||||||
import io.netty.handler.codec.http.HttpConstants;
|
import io.netty.handler.codec.http.HttpConstants;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -96,4 +99,19 @@ public abstract class AbstractHttpData implements HttpData {
|
|||||||
public long length() {
|
public long length() {
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ByteBuf data() {
|
||||||
|
try {
|
||||||
|
return getByteBuf();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new ChannelException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void free() {
|
||||||
|
delete();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -226,4 +226,9 @@ public abstract class AbstractMemoryHttpData extends AbstractHttpData {
|
|||||||
public File getFile() throws IOException {
|
public File getFile() throws IOException {
|
||||||
throw new IOException("Not represented by a file");
|
throw new IOException("Not represented by a file");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isFreed() {
|
||||||
|
return data().isFreed();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,4 +30,7 @@ public interface Attribute extends HttpData {
|
|||||||
* Sets the value of this HttpData.
|
* Sets the value of this HttpData.
|
||||||
*/
|
*/
|
||||||
void setValue(String value) throws IOException;
|
void setValue(String value) throws IOException;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
Attribute copy();
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
package io.netty.handler.codec.http.multipart;
|
package io.netty.handler.codec.http.multipart;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.channel.ChannelException;
|
||||||
import io.netty.handler.codec.http.HttpConstants;
|
import io.netty.handler.codec.http.HttpConstants;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -138,4 +139,19 @@ public class DiskAttribute extends AbstractDiskHttpData implements Attribute {
|
|||||||
protected String getPrefix() {
|
protected String getPrefix() {
|
||||||
return prefix;
|
return prefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DiskAttribute copy() {
|
||||||
|
DiskAttribute attr = new DiskAttribute(getName());
|
||||||
|
attr.setCharset(getCharset());
|
||||||
|
ByteBuf content = data();
|
||||||
|
if (content != null) {
|
||||||
|
try {
|
||||||
|
attr.setContent(content.copy());
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new ChannelException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return attr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,9 +15,12 @@
|
|||||||
*/
|
*/
|
||||||
package io.netty.handler.codec.http.multipart;
|
package io.netty.handler.codec.http.multipart;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.channel.ChannelException;
|
||||||
import io.netty.handler.codec.http.HttpHeaders;
|
import io.netty.handler.codec.http.HttpHeaders;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -159,4 +162,19 @@ public class DiskFileUpload extends AbstractDiskHttpData implements FileUpload {
|
|||||||
protected String getPrefix() {
|
protected String getPrefix() {
|
||||||
return prefix;
|
return prefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public DiskFileUpload copy() {
|
||||||
|
DiskFileUpload upload = new DiskFileUpload(getName(),
|
||||||
|
getFilename(), getContentType(), getContentTransferEncoding(), getCharset(), size);
|
||||||
|
ByteBuf buf = data();
|
||||||
|
if (buf != null) {
|
||||||
|
try {
|
||||||
|
upload.setContent(buf.copy());
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new ChannelException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return upload;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,4 +55,7 @@ public interface FileUpload extends HttpData {
|
|||||||
* @return the Content-Transfer-Encoding
|
* @return the Content-Transfer-Encoding
|
||||||
*/
|
*/
|
||||||
String getContentTransferEncoding();
|
String getContentTransferEncoding();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
FileUpload copy();
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
package io.netty.handler.codec.http.multipart;
|
package io.netty.handler.codec.http.multipart;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.ByteBufHolder;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -25,7 +26,7 @@ import java.nio.charset.Charset;
|
|||||||
/**
|
/**
|
||||||
* Extended interface for InterfaceHttpData
|
* Extended interface for InterfaceHttpData
|
||||||
*/
|
*/
|
||||||
public interface HttpData extends InterfaceHttpData {
|
public interface HttpData extends InterfaceHttpData, ByteBufHolder {
|
||||||
/**
|
/**
|
||||||
* Set the content from the ChannelBuffer (erase any previous data)
|
* Set the content from the ChannelBuffer (erase any previous data)
|
||||||
*
|
*
|
||||||
@ -176,4 +177,6 @@ public interface HttpData extends InterfaceHttpData {
|
|||||||
*/
|
*/
|
||||||
File getFile() throws IOException;
|
File getFile() throws IOException;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
HttpData copy();
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@
|
|||||||
package io.netty.handler.codec.http.multipart;
|
package io.netty.handler.codec.http.multipart;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.channel.ChannelException;
|
||||||
import io.netty.handler.codec.http.HttpConstants;
|
import io.netty.handler.codec.http.HttpConstants;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -100,4 +101,18 @@ public class MemoryAttribute extends AbstractMemoryHttpData implements Attribute
|
|||||||
return getName() + '=' + getValue();
|
return getName() + '=' + getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MemoryAttribute copy() {
|
||||||
|
MemoryAttribute attr = new MemoryAttribute(getName());
|
||||||
|
attr.setCharset(getCharset());
|
||||||
|
ByteBuf content = data();
|
||||||
|
if (content != null) {
|
||||||
|
try {
|
||||||
|
attr.setContent(content.copy());
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new ChannelException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return attr;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,8 +15,11 @@
|
|||||||
*/
|
*/
|
||||||
package io.netty.handler.codec.http.multipart;
|
package io.netty.handler.codec.http.multipart;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.channel.ChannelException;
|
||||||
import io.netty.handler.codec.http.HttpHeaders;
|
import io.netty.handler.codec.http.HttpHeaders;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.nio.charset.Charset;
|
import java.nio.charset.Charset;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -125,4 +128,20 @@ public class MemoryFileUpload extends AbstractMemoryHttpData implements FileUplo
|
|||||||
"Completed: " + isCompleted() +
|
"Completed: " + isCompleted() +
|
||||||
"\r\nIsInMemory: " + isInMemory();
|
"\r\nIsInMemory: " + isInMemory();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MemoryFileUpload copy() {
|
||||||
|
MemoryFileUpload upload = new MemoryFileUpload(getName(), getFilename(), getContentType(),
|
||||||
|
getContentTransferEncoding(), getCharset(), size);
|
||||||
|
ByteBuf buf = data();
|
||||||
|
if (buf != null) {
|
||||||
|
try {
|
||||||
|
upload.setContent(buf.copy());
|
||||||
|
return upload;
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new ChannelException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return upload;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -199,4 +199,23 @@ public class MixedAttribute implements Attribute {
|
|||||||
return attribute.getFile();
|
return attribute.getFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Attribute copy() {
|
||||||
|
return attribute.copy();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ByteBuf data() {
|
||||||
|
return attribute.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void free() {
|
||||||
|
attribute.free();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isFreed() {
|
||||||
|
return attribute.isFreed();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -224,4 +224,23 @@ public class MixedFileUpload implements FileUpload {
|
|||||||
return fileUpload.getFile();
|
return fileUpload.getFile();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public FileUpload copy() {
|
||||||
|
return fileUpload.copy();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ByteBuf data() {
|
||||||
|
return fileUpload.data();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void free() {
|
||||||
|
fileUpload.free();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isFreed() {
|
||||||
|
return fileUpload.isFreed();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ public class BinaryWebSocketFrame extends WebSocketFrame {
|
|||||||
* Creates a new empty binary frame.
|
* Creates a new empty binary frame.
|
||||||
*/
|
*/
|
||||||
public BinaryWebSocketFrame() {
|
public BinaryWebSocketFrame() {
|
||||||
setBinaryData(Unpooled.EMPTY_BUFFER);
|
super(Unpooled.buffer(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -37,7 +37,7 @@ public class BinaryWebSocketFrame extends WebSocketFrame {
|
|||||||
* the content of the frame.
|
* the content of the frame.
|
||||||
*/
|
*/
|
||||||
public BinaryWebSocketFrame(ByteBuf binaryData) {
|
public BinaryWebSocketFrame(ByteBuf binaryData) {
|
||||||
setBinaryData(binaryData);
|
super(binaryData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -51,14 +51,11 @@ public class BinaryWebSocketFrame extends WebSocketFrame {
|
|||||||
* the content of the frame.
|
* the content of the frame.
|
||||||
*/
|
*/
|
||||||
public BinaryWebSocketFrame(boolean finalFragment, int rsv, ByteBuf binaryData) {
|
public BinaryWebSocketFrame(boolean finalFragment, int rsv, ByteBuf binaryData) {
|
||||||
setFinalFragment(finalFragment);
|
super(finalFragment, rsv, binaryData);
|
||||||
setRsv(rsv);
|
|
||||||
setBinaryData(binaryData);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public BinaryWebSocketFrame copy() {
|
||||||
return getClass().getSimpleName() + "(data: " + getBinaryData() + ')';
|
return new BinaryWebSocketFrame(isFinalFragment(), rsv(), data().copy());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -30,7 +30,7 @@ public class CloseWebSocketFrame extends WebSocketFrame {
|
|||||||
* Creates a new empty close frame.
|
* Creates a new empty close frame.
|
||||||
*/
|
*/
|
||||||
public CloseWebSocketFrame() {
|
public CloseWebSocketFrame() {
|
||||||
setBinaryData(Unpooled.EMPTY_BUFFER);
|
super(Unpooled.buffer(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -72,9 +72,10 @@ public class CloseWebSocketFrame extends WebSocketFrame {
|
|||||||
* Reason text. Set to null if no text.
|
* Reason text. Set to null if no text.
|
||||||
*/
|
*/
|
||||||
public CloseWebSocketFrame(boolean finalFragment, int rsv, int statusCode, String reasonText) {
|
public CloseWebSocketFrame(boolean finalFragment, int rsv, int statusCode, String reasonText) {
|
||||||
setFinalFragment(finalFragment);
|
super(finalFragment, rsv, newBinaryData(statusCode, reasonText));
|
||||||
setRsv(rsv);
|
}
|
||||||
|
|
||||||
|
private static ByteBuf newBinaryData(int statusCode, String reasonText) {
|
||||||
byte[] reasonBytes = EMTPY_REASON;
|
byte[] reasonBytes = EMTPY_REASON;
|
||||||
if (reasonText != null) {
|
if (reasonText != null) {
|
||||||
reasonBytes = reasonText.getBytes(CharsetUtil.UTF_8);
|
reasonBytes = reasonText.getBytes(CharsetUtil.UTF_8);
|
||||||
@ -87,7 +88,7 @@ public class CloseWebSocketFrame extends WebSocketFrame {
|
|||||||
}
|
}
|
||||||
|
|
||||||
binaryData.readerIndex(0);
|
binaryData.readerIndex(0);
|
||||||
setBinaryData(binaryData);
|
return binaryData;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -101,21 +102,15 @@ public class CloseWebSocketFrame extends WebSocketFrame {
|
|||||||
* the content of the frame. Must be 2 byte integer followed by optional UTF-8 encoded string.
|
* the content of the frame. Must be 2 byte integer followed by optional UTF-8 encoded string.
|
||||||
*/
|
*/
|
||||||
public CloseWebSocketFrame(boolean finalFragment, int rsv, ByteBuf binaryData) {
|
public CloseWebSocketFrame(boolean finalFragment, int rsv, ByteBuf binaryData) {
|
||||||
setFinalFragment(finalFragment);
|
super(finalFragment, rsv, binaryData);
|
||||||
setRsv(rsv);
|
|
||||||
if (binaryData == null) {
|
|
||||||
setBinaryData(Unpooled.EMPTY_BUFFER);
|
|
||||||
} else {
|
|
||||||
setBinaryData(binaryData);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the closing status code as per <a href="http://tools.ietf.org/html/rfc6455#section-7.4">RFC 6455</a>. If
|
* Returns the closing status code as per <a href="http://tools.ietf.org/html/rfc6455#section-7.4">RFC 6455</a>. If
|
||||||
* a status code is set, -1 is returned.
|
* a status code is set, -1 is returned.
|
||||||
*/
|
*/
|
||||||
public int getStatusCode() {
|
public int statusCode() {
|
||||||
ByteBuf binaryData = getBinaryData();
|
ByteBuf binaryData = data();
|
||||||
if (binaryData == null || binaryData.capacity() == 0) {
|
if (binaryData == null || binaryData.capacity() == 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@ -131,8 +126,8 @@ public class CloseWebSocketFrame extends WebSocketFrame {
|
|||||||
* Returns the reason text as per <a href="http://tools.ietf.org/html/rfc6455#section-7.4">RFC 6455</a> If a reason
|
* Returns the reason text as per <a href="http://tools.ietf.org/html/rfc6455#section-7.4">RFC 6455</a> If a reason
|
||||||
* text is not supplied, an empty string is returned.
|
* text is not supplied, an empty string is returned.
|
||||||
*/
|
*/
|
||||||
public String getReasonText() {
|
public String reasonText() {
|
||||||
ByteBuf binaryData = getBinaryData();
|
ByteBuf binaryData = data();
|
||||||
if (binaryData == null || binaryData.capacity() <= 2) {
|
if (binaryData == null || binaryData.capacity() <= 2) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
@ -145,7 +140,7 @@ public class CloseWebSocketFrame extends WebSocketFrame {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public CloseWebSocketFrame copy() {
|
||||||
return getClass().getSimpleName();
|
return new CloseWebSocketFrame(isFinalFragment(), rsv(), data().copy());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,7 +31,7 @@ public class ContinuationWebSocketFrame extends WebSocketFrame {
|
|||||||
* Creates a new empty continuation frame.
|
* Creates a new empty continuation frame.
|
||||||
*/
|
*/
|
||||||
public ContinuationWebSocketFrame() {
|
public ContinuationWebSocketFrame() {
|
||||||
setBinaryData(Unpooled.EMPTY_BUFFER);
|
super(Unpooled.buffer(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -41,7 +41,7 @@ public class ContinuationWebSocketFrame extends WebSocketFrame {
|
|||||||
* @param binaryData the content of the frame.
|
* @param binaryData the content of the frame.
|
||||||
*/
|
*/
|
||||||
public ContinuationWebSocketFrame(ByteBuf binaryData) {
|
public ContinuationWebSocketFrame(ByteBuf binaryData) {
|
||||||
setBinaryData(binaryData);
|
super(binaryData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -55,9 +55,7 @@ public class ContinuationWebSocketFrame extends WebSocketFrame {
|
|||||||
* the content of the frame.
|
* the content of the frame.
|
||||||
*/
|
*/
|
||||||
public ContinuationWebSocketFrame(boolean finalFragment, int rsv, ByteBuf binaryData) {
|
public ContinuationWebSocketFrame(boolean finalFragment, int rsv, ByteBuf binaryData) {
|
||||||
setFinalFragment(finalFragment);
|
super(finalFragment, rsv, binaryData);
|
||||||
setRsv(rsv);
|
|
||||||
setBinaryData(binaryData);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -75,9 +73,7 @@ public class ContinuationWebSocketFrame extends WebSocketFrame {
|
|||||||
*/
|
*/
|
||||||
public ContinuationWebSocketFrame(
|
public ContinuationWebSocketFrame(
|
||||||
boolean finalFragment, int rsv, ByteBuf binaryData, String aggregatedText) {
|
boolean finalFragment, int rsv, ByteBuf binaryData, String aggregatedText) {
|
||||||
setFinalFragment(finalFragment);
|
super(finalFragment, rsv, binaryData);
|
||||||
setRsv(rsv);
|
|
||||||
setBinaryData(binaryData);
|
|
||||||
this.aggregatedText = aggregatedText;
|
this.aggregatedText = aggregatedText;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,19 +88,14 @@ public class ContinuationWebSocketFrame extends WebSocketFrame {
|
|||||||
* text content of the frame.
|
* text content of the frame.
|
||||||
*/
|
*/
|
||||||
public ContinuationWebSocketFrame(boolean finalFragment, int rsv, String text) {
|
public ContinuationWebSocketFrame(boolean finalFragment, int rsv, String text) {
|
||||||
setFinalFragment(finalFragment);
|
this(finalFragment, rsv, fromText(text), null);
|
||||||
setRsv(rsv);
|
|
||||||
setText(text);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the text data in this frame
|
* Returns the text data in this frame
|
||||||
*/
|
*/
|
||||||
public String getText() {
|
public String text() {
|
||||||
if (getBinaryData() == null) {
|
return data().toString(CharsetUtil.UTF_8);
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return getBinaryData().toString(CharsetUtil.UTF_8);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -113,28 +104,24 @@ public class ContinuationWebSocketFrame extends WebSocketFrame {
|
|||||||
* @param text
|
* @param text
|
||||||
* text to store
|
* text to store
|
||||||
*/
|
*/
|
||||||
public void setText(String text) {
|
private static ByteBuf fromText(String text) {
|
||||||
if (text == null || text.isEmpty()) {
|
if (text == null || text.isEmpty()) {
|
||||||
setBinaryData(Unpooled.EMPTY_BUFFER);
|
return Unpooled.EMPTY_BUFFER;
|
||||||
} else {
|
} else {
|
||||||
setBinaryData(Unpooled.copiedBuffer(text, CharsetUtil.UTF_8));
|
return Unpooled.copiedBuffer(text, CharsetUtil.UTF_8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return getClass().getSimpleName() + "(data: " + getBinaryData() + ')';
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Aggregated text returned by decoder on the final continuation frame of a fragmented text message
|
* Aggregated text returned by decoder on the final continuation frame of a fragmented text message
|
||||||
*/
|
*/
|
||||||
public String getAggregatedText() {
|
public String aggregatedText() {
|
||||||
return aggregatedText;
|
return aggregatedText;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setAggregatedText(String aggregatedText) {
|
@Override
|
||||||
this.aggregatedText = aggregatedText;
|
public ContinuationWebSocketFrame copy() {
|
||||||
|
return new ContinuationWebSocketFrame(isFinalFragment(), rsv(), data().copy(), aggregatedText());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -27,8 +27,7 @@ public class PingWebSocketFrame extends WebSocketFrame {
|
|||||||
* Creates a new empty ping frame.
|
* Creates a new empty ping frame.
|
||||||
*/
|
*/
|
||||||
public PingWebSocketFrame() {
|
public PingWebSocketFrame() {
|
||||||
setFinalFragment(true);
|
super(true, 0, Unpooled.buffer(0));
|
||||||
setBinaryData(Unpooled.EMPTY_BUFFER);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -38,7 +37,7 @@ public class PingWebSocketFrame extends WebSocketFrame {
|
|||||||
* the content of the frame.
|
* the content of the frame.
|
||||||
*/
|
*/
|
||||||
public PingWebSocketFrame(ByteBuf binaryData) {
|
public PingWebSocketFrame(ByteBuf binaryData) {
|
||||||
setBinaryData(binaryData);
|
super(binaryData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -52,14 +51,11 @@ public class PingWebSocketFrame extends WebSocketFrame {
|
|||||||
* the content of the frame.
|
* the content of the frame.
|
||||||
*/
|
*/
|
||||||
public PingWebSocketFrame(boolean finalFragment, int rsv, ByteBuf binaryData) {
|
public PingWebSocketFrame(boolean finalFragment, int rsv, ByteBuf binaryData) {
|
||||||
setFinalFragment(finalFragment);
|
super(finalFragment, rsv, binaryData);
|
||||||
setRsv(rsv);
|
|
||||||
setBinaryData(binaryData);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public PingWebSocketFrame copy() {
|
||||||
return getClass().getSimpleName() + "(data: " + getBinaryData() + ')';
|
return new PingWebSocketFrame(isFinalFragment(), rsv(), data().copy());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ public class PongWebSocketFrame extends WebSocketFrame {
|
|||||||
* Creates a new empty pong frame.
|
* Creates a new empty pong frame.
|
||||||
*/
|
*/
|
||||||
public PongWebSocketFrame() {
|
public PongWebSocketFrame() {
|
||||||
setBinaryData(Unpooled.EMPTY_BUFFER);
|
super(Unpooled.buffer(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -37,7 +37,7 @@ public class PongWebSocketFrame extends WebSocketFrame {
|
|||||||
* the content of the frame.
|
* the content of the frame.
|
||||||
*/
|
*/
|
||||||
public PongWebSocketFrame(ByteBuf binaryData) {
|
public PongWebSocketFrame(ByteBuf binaryData) {
|
||||||
setBinaryData(binaryData);
|
super(binaryData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -51,14 +51,12 @@ public class PongWebSocketFrame extends WebSocketFrame {
|
|||||||
* the content of the frame.
|
* the content of the frame.
|
||||||
*/
|
*/
|
||||||
public PongWebSocketFrame(boolean finalFragment, int rsv, ByteBuf binaryData) {
|
public PongWebSocketFrame(boolean finalFragment, int rsv, ByteBuf binaryData) {
|
||||||
setFinalFragment(finalFragment);
|
super(finalFragment, rsv, binaryData);
|
||||||
setRsv(rsv);
|
|
||||||
setBinaryData(binaryData);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public PongWebSocketFrame copy() {
|
||||||
return getClass().getSimpleName() + "(data: " + getBinaryData() + ')';
|
return new PongWebSocketFrame(isFinalFragment(), rsv(), data().copy());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@ public class TextWebSocketFrame extends WebSocketFrame {
|
|||||||
* Creates a new empty text frame.
|
* Creates a new empty text frame.
|
||||||
*/
|
*/
|
||||||
public TextWebSocketFrame() {
|
public TextWebSocketFrame() {
|
||||||
setBinaryData(Unpooled.EMPTY_BUFFER);
|
super(Unpooled.buffer(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -38,11 +38,7 @@ public class TextWebSocketFrame extends WebSocketFrame {
|
|||||||
* String to put in the frame
|
* String to put in the frame
|
||||||
*/
|
*/
|
||||||
public TextWebSocketFrame(String text) {
|
public TextWebSocketFrame(String text) {
|
||||||
if (text == null || text.isEmpty()) {
|
super(fromText(text));
|
||||||
setBinaryData(Unpooled.EMPTY_BUFFER);
|
|
||||||
} else {
|
|
||||||
setBinaryData(Unpooled.copiedBuffer(text, CharsetUtil.UTF_8));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -52,7 +48,7 @@ public class TextWebSocketFrame extends WebSocketFrame {
|
|||||||
* the content of the frame. Must be UTF-8 encoded
|
* the content of the frame. Must be UTF-8 encoded
|
||||||
*/
|
*/
|
||||||
public TextWebSocketFrame(ByteBuf binaryData) {
|
public TextWebSocketFrame(ByteBuf binaryData) {
|
||||||
setBinaryData(binaryData);
|
super(binaryData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -66,12 +62,14 @@ public class TextWebSocketFrame extends WebSocketFrame {
|
|||||||
* String to put in the frame
|
* String to put in the frame
|
||||||
*/
|
*/
|
||||||
public TextWebSocketFrame(boolean finalFragment, int rsv, String text) {
|
public TextWebSocketFrame(boolean finalFragment, int rsv, String text) {
|
||||||
setFinalFragment(finalFragment);
|
super(finalFragment, rsv, fromText(text));
|
||||||
setRsv(rsv);
|
}
|
||||||
|
|
||||||
|
private static ByteBuf fromText(String text) {
|
||||||
if (text == null || text.isEmpty()) {
|
if (text == null || text.isEmpty()) {
|
||||||
setBinaryData(Unpooled.EMPTY_BUFFER);
|
return Unpooled.EMPTY_BUFFER;
|
||||||
} else {
|
} else {
|
||||||
setBinaryData(Unpooled.copiedBuffer(text, CharsetUtil.UTF_8));
|
return Unpooled.copiedBuffer(text, CharsetUtil.UTF_8);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,36 +84,18 @@ public class TextWebSocketFrame extends WebSocketFrame {
|
|||||||
* the content of the frame. Must be UTF-8 encoded
|
* the content of the frame. Must be UTF-8 encoded
|
||||||
*/
|
*/
|
||||||
public TextWebSocketFrame(boolean finalFragment, int rsv, ByteBuf binaryData) {
|
public TextWebSocketFrame(boolean finalFragment, int rsv, ByteBuf binaryData) {
|
||||||
setFinalFragment(finalFragment);
|
super(finalFragment, rsv, binaryData);
|
||||||
setRsv(rsv);
|
|
||||||
setBinaryData(binaryData);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the text data in this frame
|
* Returns the text data in this frame
|
||||||
*/
|
*/
|
||||||
public String getText() {
|
public String text() {
|
||||||
if (getBinaryData() == null) {
|
return data().toString(CharsetUtil.UTF_8);
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return getBinaryData().toString(CharsetUtil.UTF_8);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the string for this frame
|
|
||||||
*
|
|
||||||
* @param text
|
|
||||||
* text to store
|
|
||||||
*/
|
|
||||||
public void setText(String text) {
|
|
||||||
if (text == null) {
|
|
||||||
throw new NullPointerException("text");
|
|
||||||
}
|
|
||||||
setBinaryData(Unpooled.copiedBuffer(text, CharsetUtil.UTF_8));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public TextWebSocketFrame copy() {
|
||||||
return getClass().getSimpleName() + "(text: " + getText() + ')';
|
return new TextWebSocketFrame(isFinalFragment(), rsv(), data().copy());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,8 @@
|
|||||||
*/
|
*/
|
||||||
package io.netty.handler.codec.http.websocketx;
|
package io.netty.handler.codec.http.websocketx;
|
||||||
|
|
||||||
|
import io.netty.buffer.ByteBuf;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks UTF8 bytes for validity before converting it into a string
|
* Checks UTF8 bytes for validity before converting it into a string
|
||||||
*/
|
*/
|
||||||
@ -66,9 +68,15 @@ final class UTF8Output {
|
|||||||
|
|
||||||
private final StringBuilder stringBuilder;
|
private final StringBuilder stringBuilder;
|
||||||
|
|
||||||
UTF8Output(byte[] bytes) {
|
UTF8Output(ByteBuf buffer) {
|
||||||
stringBuilder = new StringBuilder(bytes.length);
|
stringBuilder = new StringBuilder(buffer.readableBytes());
|
||||||
write(bytes);
|
write(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void write(ByteBuf buffer) {
|
||||||
|
for (int i = buffer.readerIndex(); i < buffer.writerIndex(); i++) {
|
||||||
|
write(buffer.getByte(i));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void write(byte[] bytes) {
|
public void write(byte[] bytes) {
|
||||||
|
@ -63,14 +63,14 @@ public class WebSocket00FrameDecoder extends ReplayingDecoder<Void> {
|
|||||||
byte type = in.readByte();
|
byte type = in.readByte();
|
||||||
if ((type & 0x80) == 0x80) {
|
if ((type & 0x80) == 0x80) {
|
||||||
// If the MSB on type is set, decode the frame length
|
// If the MSB on type is set, decode the frame length
|
||||||
return decodeBinaryFrame(type, in);
|
return decodeBinaryFrame(ctx, type, in);
|
||||||
} else {
|
} else {
|
||||||
// Decode a 0xff terminated UTF-8 string
|
// Decode a 0xff terminated UTF-8 string
|
||||||
return decodeTextFrame(in);
|
return decodeTextFrame(ctx, in);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private WebSocketFrame decodeBinaryFrame(byte type, ByteBuf buffer) {
|
private WebSocketFrame decodeBinaryFrame(ChannelHandlerContext ctx, byte type, ByteBuf buffer) {
|
||||||
long frameSize = 0;
|
long frameSize = 0;
|
||||||
int lengthFieldSize = 0;
|
int lengthFieldSize = 0;
|
||||||
byte b;
|
byte b;
|
||||||
@ -92,11 +92,12 @@ public class WebSocket00FrameDecoder extends ReplayingDecoder<Void> {
|
|||||||
receivedClosingHandshake = true;
|
receivedClosingHandshake = true;
|
||||||
return new CloseWebSocketFrame();
|
return new CloseWebSocketFrame();
|
||||||
}
|
}
|
||||||
|
ByteBuf payload = ctx.alloc().buffer((int) frameSize);
|
||||||
return new BinaryWebSocketFrame(buffer.readBytes((int) frameSize));
|
buffer.readBytes(payload);
|
||||||
|
return new BinaryWebSocketFrame(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
private WebSocketFrame decodeTextFrame(ByteBuf buffer) {
|
private WebSocketFrame decodeTextFrame(ChannelHandlerContext ctx, ByteBuf buffer) {
|
||||||
int ridx = buffer.readerIndex();
|
int ridx = buffer.readerIndex();
|
||||||
int rbytes = actualReadableBytes();
|
int rbytes = actualReadableBytes();
|
||||||
int delimPos = buffer.indexOf(ridx, ridx + rbytes, (byte) 0xFF);
|
int delimPos = buffer.indexOf(ridx, ridx + rbytes, (byte) 0xFF);
|
||||||
@ -116,7 +117,8 @@ public class WebSocket00FrameDecoder extends ReplayingDecoder<Void> {
|
|||||||
throw new TooLongFrameException();
|
throw new TooLongFrameException();
|
||||||
}
|
}
|
||||||
|
|
||||||
ByteBuf binaryData = buffer.readBytes(frameSize);
|
ByteBuf binaryData = ctx.alloc().buffer(frameSize);
|
||||||
|
buffer.readBytes(binaryData);
|
||||||
buffer.skipBytes(1);
|
buffer.skipBytes(1);
|
||||||
|
|
||||||
int ffDelimPos = binaryData.indexOf(binaryData.readerIndex(), binaryData.writerIndex(), (byte) 0xFF);
|
int ffDelimPos = binaryData.indexOf(binaryData.readerIndex(), binaryData.writerIndex(), (byte) 0xFF);
|
||||||
|
@ -42,7 +42,7 @@ public class WebSocket00FrameEncoder extends MessageToByteEncoder<WebSocketFrame
|
|||||||
WebSocketFrame msg, ByteBuf out) throws Exception {
|
WebSocketFrame msg, ByteBuf out) throws Exception {
|
||||||
if (msg instanceof TextWebSocketFrame) {
|
if (msg instanceof TextWebSocketFrame) {
|
||||||
// Text frame
|
// Text frame
|
||||||
ByteBuf data = msg.getBinaryData();
|
ByteBuf data = msg.data();
|
||||||
out.writeByte((byte) 0x00);
|
out.writeByte((byte) 0x00);
|
||||||
out.writeBytes(data, data.readerIndex(), data.readableBytes());
|
out.writeBytes(data, data.readerIndex(), data.readableBytes());
|
||||||
out.writeByte((byte) 0xFF);
|
out.writeByte((byte) 0xFF);
|
||||||
@ -52,7 +52,7 @@ public class WebSocket00FrameEncoder extends MessageToByteEncoder<WebSocketFrame
|
|||||||
out.writeByte((byte) 0x00);
|
out.writeByte((byte) 0x00);
|
||||||
} else {
|
} else {
|
||||||
// Binary frame
|
// Binary frame
|
||||||
ByteBuf data = msg.getBinaryData();
|
ByteBuf data = msg.data();
|
||||||
int dataLen = data.readableBytes();
|
int dataLen = data.readableBytes();
|
||||||
out.ensureWritableBytes(dataLen + 5);
|
out.ensureWritableBytes(dataLen + 5);
|
||||||
|
|
||||||
|
@ -54,7 +54,6 @@
|
|||||||
package io.netty.handler.codec.http.websocketx;
|
package io.netty.handler.codec.http.websocketx;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
import io.netty.buffer.Unpooled;
|
|
||||||
import io.netty.channel.ChannelFutureListener;
|
import io.netty.channel.ChannelFutureListener;
|
||||||
import io.netty.channel.ChannelHandlerContext;
|
import io.netty.channel.ChannelHandlerContext;
|
||||||
import io.netty.handler.codec.CorruptedFrameException;
|
import io.netty.handler.codec.CorruptedFrameException;
|
||||||
@ -128,242 +127,246 @@ public class WebSocket08FrameDecoder extends ReplayingDecoder<WebSocket08FrameDe
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch (state()) {
|
switch (state()) {
|
||||||
case FRAME_START:
|
case FRAME_START:
|
||||||
framePayloadBytesRead = 0;
|
framePayloadBytesRead = 0;
|
||||||
framePayloadLength = -1;
|
framePayloadLength = -1;
|
||||||
framePayload = null;
|
framePayload = null;
|
||||||
|
|
||||||
// FIN, RSV, OPCODE
|
// FIN, RSV, OPCODE
|
||||||
byte b = in.readByte();
|
byte b = in.readByte();
|
||||||
frameFinalFlag = (b & 0x80) != 0;
|
frameFinalFlag = (b & 0x80) != 0;
|
||||||
frameRsv = (b & 0x70) >> 4;
|
frameRsv = (b & 0x70) >> 4;
|
||||||
frameOpcode = b & 0x0F;
|
frameOpcode = b & 0x0F;
|
||||||
|
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug("Decoding WebSocket Frame opCode=" + frameOpcode);
|
logger.debug("Decoding WebSocket Frame opCode=" + frameOpcode);
|
||||||
}
|
}
|
||||||
|
|
||||||
// MASK, PAYLOAD LEN 1
|
// MASK, PAYLOAD LEN 1
|
||||||
b = in.readByte();
|
b = in.readByte();
|
||||||
boolean frameMasked = (b & 0x80) != 0;
|
boolean frameMasked = (b & 0x80) != 0;
|
||||||
int framePayloadLen1 = b & 0x7F;
|
int framePayloadLen1 = b & 0x7F;
|
||||||
|
|
||||||
if (frameRsv != 0 && !allowExtensions) {
|
if (frameRsv != 0 && !allowExtensions) {
|
||||||
protocolViolation(ctx, "RSV != 0 and no extension negotiated, RSV:" + frameRsv);
|
protocolViolation(ctx, "RSV != 0 and no extension negotiated, RSV:" + frameRsv);
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (maskedPayload && !frameMasked) {
|
|
||||||
protocolViolation(ctx, "unmasked client to server frame");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
if (frameOpcode > 7) { // control frame (have MSB in opcode set)
|
|
||||||
|
|
||||||
// control frames MUST NOT be fragmented
|
|
||||||
if (!frameFinalFlag) {
|
|
||||||
protocolViolation(ctx, "fragmented control frame");
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// control frames MUST have payload 125 octets or less
|
if (maskedPayload && !frameMasked) {
|
||||||
if (framePayloadLen1 > 125) {
|
protocolViolation(ctx, "unmasked client to server frame");
|
||||||
protocolViolation(ctx, "control frame with payload length > 125 octets");
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
if (frameOpcode > 7) { // control frame (have MSB in opcode set)
|
||||||
|
|
||||||
// check for reserved control frame opcodes
|
// control frames MUST NOT be fragmented
|
||||||
if (!(frameOpcode == OPCODE_CLOSE || frameOpcode == OPCODE_PING || frameOpcode == OPCODE_PONG)) {
|
if (!frameFinalFlag) {
|
||||||
protocolViolation(ctx, "control frame using reserved opcode " + frameOpcode);
|
protocolViolation(ctx, "fragmented control frame");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// close frame : if there is a body, the first two bytes of the
|
// control frames MUST have payload 125 octets or less
|
||||||
// body MUST be a 2-byte unsigned integer (in network byte
|
if (framePayloadLen1 > 125) {
|
||||||
// order) representing a status code
|
protocolViolation(ctx, "control frame with payload length > 125 octets");
|
||||||
if (frameOpcode == 8 && framePayloadLen1 == 1) {
|
return null;
|
||||||
protocolViolation(ctx, "received close control frame with payload len 1");
|
}
|
||||||
return null;
|
|
||||||
}
|
|
||||||
} else { // data frame
|
|
||||||
// check for reserved data frame opcodes
|
|
||||||
if (!(frameOpcode == OPCODE_CONT || frameOpcode == OPCODE_TEXT || frameOpcode == OPCODE_BINARY)) {
|
|
||||||
protocolViolation(ctx, "data frame using reserved opcode " + frameOpcode);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check opcode vs message fragmentation state 1/2
|
// check for reserved control frame opcodes
|
||||||
if (fragmentedFramesCount == 0 && frameOpcode == OPCODE_CONT) {
|
if (!(frameOpcode == OPCODE_CLOSE || frameOpcode == OPCODE_PING || frameOpcode == OPCODE_PONG)) {
|
||||||
protocolViolation(ctx, "received continuation data frame outside fragmented message");
|
protocolViolation(ctx, "control frame using reserved opcode " + frameOpcode);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check opcode vs message fragmentation state 2/2
|
// close frame : if there is a body, the first two bytes of the
|
||||||
if (fragmentedFramesCount != 0 && frameOpcode != OPCODE_CONT && frameOpcode != OPCODE_PING) {
|
// body MUST be a 2-byte unsigned integer (in network byte
|
||||||
protocolViolation(ctx, "received non-continuation data frame while inside fragmented message");
|
// order) representing a status code
|
||||||
return null;
|
if (frameOpcode == 8 && framePayloadLen1 == 1) {
|
||||||
}
|
protocolViolation(ctx, "received close control frame with payload len 1");
|
||||||
}
|
return null;
|
||||||
|
}
|
||||||
|
} else { // data frame
|
||||||
|
// check for reserved data frame opcodes
|
||||||
|
if (!(frameOpcode == OPCODE_CONT || frameOpcode == OPCODE_TEXT || frameOpcode == OPCODE_BINARY)) {
|
||||||
|
protocolViolation(ctx, "data frame using reserved opcode " + frameOpcode);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
// Read frame payload length
|
// check opcode vs message fragmentation state 1/2
|
||||||
if (framePayloadLen1 == 126) {
|
if (fragmentedFramesCount == 0 && frameOpcode == OPCODE_CONT) {
|
||||||
framePayloadLength = in.readUnsignedShort();
|
protocolViolation(ctx, "received continuation data frame outside fragmented message");
|
||||||
if (framePayloadLength < 126) {
|
return null;
|
||||||
protocolViolation(ctx, "invalid data frame length (not using minimal length encoding)");
|
}
|
||||||
return null;
|
|
||||||
}
|
|
||||||
} else if (framePayloadLen1 == 127) {
|
|
||||||
framePayloadLength = in.readLong();
|
|
||||||
// TODO: check if it's bigger than 0x7FFFFFFFFFFFFFFF, Maybe
|
|
||||||
// just check if it's negative?
|
|
||||||
|
|
||||||
if (framePayloadLength < 65536) {
|
// check opcode vs message fragmentation state 2/2
|
||||||
protocolViolation(ctx, "invalid data frame length (not using minimal length encoding)");
|
if (fragmentedFramesCount != 0 && frameOpcode != OPCODE_CONT && frameOpcode != OPCODE_PING) {
|
||||||
return null;
|
protocolViolation(ctx, "received non-continuation data frame while inside fragmented message");
|
||||||
}
|
return null;
|
||||||
} else {
|
|
||||||
framePayloadLength = framePayloadLen1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (framePayloadLength > maxFramePayloadLength) {
|
|
||||||
protocolViolation(ctx, "Max frame length of " + maxFramePayloadLength + " has been exceeded.");
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (logger.isDebugEnabled()) {
|
|
||||||
logger.debug("Decoding WebSocket Frame length=" + framePayloadLength);
|
|
||||||
}
|
|
||||||
|
|
||||||
checkpoint(State.MASKING_KEY);
|
|
||||||
case MASKING_KEY:
|
|
||||||
if (maskedPayload) {
|
|
||||||
maskingKey = in.readBytes(4);
|
|
||||||
}
|
|
||||||
checkpoint(State.PAYLOAD);
|
|
||||||
case PAYLOAD:
|
|
||||||
// Sometimes, the payload may not be delivered in 1 nice packet
|
|
||||||
// We need to accumulate the data until we have it all
|
|
||||||
int rbytes = actualReadableBytes();
|
|
||||||
ByteBuf payloadBuffer = null;
|
|
||||||
|
|
||||||
long willHaveReadByteCount = framePayloadBytesRead + rbytes;
|
|
||||||
// logger.debug("Frame rbytes=" + rbytes + " willHaveReadByteCount="
|
|
||||||
// + willHaveReadByteCount + " framePayloadLength=" +
|
|
||||||
// framePayloadLength);
|
|
||||||
if (willHaveReadByteCount == framePayloadLength) {
|
|
||||||
// We have all our content so proceed to process
|
|
||||||
payloadBuffer = in.readBytes(rbytes);
|
|
||||||
} else if (willHaveReadByteCount < framePayloadLength) {
|
|
||||||
// We don't have all our content so accumulate payload.
|
|
||||||
// Returning null means we will get called back
|
|
||||||
payloadBuffer = in.readBytes(rbytes);
|
|
||||||
if (framePayload == null) {
|
|
||||||
framePayload = Unpooled.buffer(toFrameLength(framePayloadLength));
|
|
||||||
}
|
|
||||||
framePayload.writeBytes(payloadBuffer);
|
|
||||||
framePayloadBytesRead += rbytes;
|
|
||||||
|
|
||||||
// Return null to wait for more bytes to arrive
|
|
||||||
return null;
|
|
||||||
} else if (willHaveReadByteCount > framePayloadLength) {
|
|
||||||
// We have more than what we need so read up to the end of frame
|
|
||||||
// Leave the remainder in the buffer for next frame
|
|
||||||
payloadBuffer = in.readBytes(toFrameLength(framePayloadLength - framePayloadBytesRead));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Now we have all the data, the next checkpoint must be the next
|
|
||||||
// frame
|
|
||||||
checkpoint(State.FRAME_START);
|
|
||||||
|
|
||||||
// Take the data that we have in this packet
|
|
||||||
if (framePayload == null) {
|
|
||||||
framePayload = payloadBuffer;
|
|
||||||
} else {
|
|
||||||
framePayload.writeBytes(payloadBuffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Unmask data if needed
|
|
||||||
if (maskedPayload) {
|
|
||||||
unmask(framePayload);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Processing ping/pong/close frames because they cannot be
|
|
||||||
// fragmented
|
|
||||||
if (frameOpcode == OPCODE_PING) {
|
|
||||||
return new PingWebSocketFrame(frameFinalFlag, frameRsv, framePayload);
|
|
||||||
}
|
|
||||||
if (frameOpcode == OPCODE_PONG) {
|
|
||||||
return new PongWebSocketFrame(frameFinalFlag, frameRsv, framePayload);
|
|
||||||
}
|
|
||||||
if (frameOpcode == OPCODE_CLOSE) {
|
|
||||||
checkCloseFrameBody(ctx, framePayload);
|
|
||||||
receivedClosingHandshake = true;
|
|
||||||
return new CloseWebSocketFrame(frameFinalFlag, frameRsv, framePayload);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Processing for possible fragmented messages for text and binary
|
|
||||||
// frames
|
|
||||||
String aggregatedText = null;
|
|
||||||
if (frameFinalFlag) {
|
|
||||||
// Final frame of the sequence. Apparently ping frames are
|
|
||||||
// allowed in the middle of a fragmented message
|
|
||||||
if (frameOpcode != OPCODE_PING) {
|
|
||||||
fragmentedFramesCount = 0;
|
|
||||||
|
|
||||||
// Check text for UTF8 correctness
|
|
||||||
if (frameOpcode == OPCODE_TEXT || fragmentedFramesText != null) {
|
|
||||||
// Check UTF-8 correctness for this payload
|
|
||||||
checkUTF8String(ctx, framePayload.array());
|
|
||||||
|
|
||||||
// This does a second check to make sure UTF-8
|
|
||||||
// correctness for entire text message
|
|
||||||
aggregatedText = fragmentedFramesText.toString();
|
|
||||||
|
|
||||||
fragmentedFramesText = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// Not final frame so we can expect more frames in the
|
// Read frame payload length
|
||||||
// fragmented sequence
|
if (framePayloadLen1 == 126) {
|
||||||
if (fragmentedFramesCount == 0) {
|
framePayloadLength = in.readUnsignedShort();
|
||||||
// First text or binary frame for a fragmented set
|
if (framePayloadLength < 126) {
|
||||||
fragmentedFramesText = null;
|
protocolViolation(ctx, "invalid data frame length (not using minimal length encoding)");
|
||||||
if (frameOpcode == OPCODE_TEXT) {
|
return null;
|
||||||
checkUTF8String(ctx, framePayload.array());
|
}
|
||||||
|
} else if (framePayloadLen1 == 127) {
|
||||||
|
framePayloadLength = in.readLong();
|
||||||
|
// TODO: check if it's bigger than 0x7FFFFFFFFFFFFFFF, Maybe
|
||||||
|
// just check if it's negative?
|
||||||
|
|
||||||
|
if (framePayloadLength < 65536) {
|
||||||
|
protocolViolation(ctx, "invalid data frame length (not using minimal length encoding)");
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Subsequent frames - only check if init frame is text
|
framePayloadLength = framePayloadLen1;
|
||||||
if (fragmentedFramesText != null) {
|
|
||||||
checkUTF8String(ctx, framePayload.array());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Increment counter
|
if (framePayloadLength > maxFramePayloadLength) {
|
||||||
fragmentedFramesCount++;
|
protocolViolation(ctx, "Max frame length of " + maxFramePayloadLength + " has been exceeded.");
|
||||||
}
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
// Return the frame
|
if (logger.isDebugEnabled()) {
|
||||||
if (frameOpcode == OPCODE_TEXT) {
|
logger.debug("Decoding WebSocket Frame length=" + framePayloadLength);
|
||||||
return new TextWebSocketFrame(frameFinalFlag, frameRsv, framePayload);
|
}
|
||||||
} else if (frameOpcode == OPCODE_BINARY) {
|
|
||||||
return new BinaryWebSocketFrame(frameFinalFlag, frameRsv, framePayload);
|
checkpoint(State.MASKING_KEY);
|
||||||
} else if (frameOpcode == OPCODE_CONT) {
|
case MASKING_KEY:
|
||||||
return new ContinuationWebSocketFrame(frameFinalFlag, frameRsv, framePayload, aggregatedText);
|
if (maskedPayload) {
|
||||||
} else {
|
maskingKey = in.readBytes(4);
|
||||||
throw new UnsupportedOperationException("Cannot decode web socket frame with opcode: " + frameOpcode);
|
}
|
||||||
}
|
checkpoint(State.PAYLOAD);
|
||||||
case CORRUPT:
|
case PAYLOAD:
|
||||||
// If we don't keep reading Netty will throw an exception saying
|
// Sometimes, the payload may not be delivered in 1 nice packet
|
||||||
// we can't return null if no bytes read and state not changed.
|
// We need to accumulate the data until we have it all
|
||||||
in.readByte();
|
int rbytes = actualReadableBytes();
|
||||||
return null;
|
ByteBuf payloadBuffer = null;
|
||||||
default:
|
|
||||||
throw new Error("Shouldn't reach here.");
|
long willHaveReadByteCount = framePayloadBytesRead + rbytes;
|
||||||
|
// logger.debug("Frame rbytes=" + rbytes + " willHaveReadByteCount="
|
||||||
|
// + willHaveReadByteCount + " framePayloadLength=" +
|
||||||
|
// framePayloadLength);
|
||||||
|
if (willHaveReadByteCount == framePayloadLength) {
|
||||||
|
// We have all our content so proceed to process
|
||||||
|
payloadBuffer = ctx.alloc().buffer(rbytes);
|
||||||
|
payloadBuffer.writeBytes(in, rbytes);
|
||||||
|
} else if (willHaveReadByteCount < framePayloadLength) {
|
||||||
|
|
||||||
|
// We don't have all our content so accumulate payload.
|
||||||
|
// Returning null means we will get called back
|
||||||
|
if (framePayload == null) {
|
||||||
|
framePayload = ctx.alloc().buffer(toFrameLength(framePayloadLength));
|
||||||
|
}
|
||||||
|
framePayload.writeBytes(in, rbytes);
|
||||||
|
framePayloadBytesRead += rbytes;
|
||||||
|
|
||||||
|
// Return null to wait for more bytes to arrive
|
||||||
|
return null;
|
||||||
|
} else if (willHaveReadByteCount > framePayloadLength) {
|
||||||
|
// We have more than what we need so read up to the end of frame
|
||||||
|
// Leave the remainder in the buffer for next frame
|
||||||
|
if (framePayload == null) {
|
||||||
|
framePayload = ctx.alloc().buffer(toFrameLength(framePayloadLength));
|
||||||
|
}
|
||||||
|
framePayload.writeBytes(in, toFrameLength(framePayloadLength - framePayloadBytesRead));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now we have all the data, the next checkpoint must be the next
|
||||||
|
// frame
|
||||||
|
checkpoint(State.FRAME_START);
|
||||||
|
|
||||||
|
// Take the data that we have in this packet
|
||||||
|
if (framePayload == null) {
|
||||||
|
framePayload = payloadBuffer;
|
||||||
|
} else if (payloadBuffer != null) {
|
||||||
|
framePayload.writeBytes(payloadBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmask data if needed
|
||||||
|
if (maskedPayload) {
|
||||||
|
unmask(framePayload);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Processing ping/pong/close frames because they cannot be
|
||||||
|
// fragmented
|
||||||
|
if (frameOpcode == OPCODE_PING) {
|
||||||
|
return new PingWebSocketFrame(frameFinalFlag, frameRsv, framePayload);
|
||||||
|
}
|
||||||
|
if (frameOpcode == OPCODE_PONG) {
|
||||||
|
return new PongWebSocketFrame(frameFinalFlag, frameRsv, framePayload);
|
||||||
|
}
|
||||||
|
if (frameOpcode == OPCODE_CLOSE) {
|
||||||
|
checkCloseFrameBody(ctx, framePayload);
|
||||||
|
receivedClosingHandshake = true;
|
||||||
|
return new CloseWebSocketFrame(frameFinalFlag, frameRsv, framePayload);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Processing for possible fragmented messages for text and binary
|
||||||
|
// frames
|
||||||
|
String aggregatedText = null;
|
||||||
|
if (frameFinalFlag) {
|
||||||
|
// Final frame of the sequence. Apparently ping frames are
|
||||||
|
// allowed in the middle of a fragmented message
|
||||||
|
if (frameOpcode != OPCODE_PING) {
|
||||||
|
fragmentedFramesCount = 0;
|
||||||
|
|
||||||
|
// Check text for UTF8 correctness
|
||||||
|
if (frameOpcode == OPCODE_TEXT || fragmentedFramesText != null) {
|
||||||
|
// Check UTF-8 correctness for this payload
|
||||||
|
checkUTF8String(ctx, framePayload);
|
||||||
|
|
||||||
|
// This does a second check to make sure UTF-8
|
||||||
|
// correctness for entire text message
|
||||||
|
aggregatedText = fragmentedFramesText.toString();
|
||||||
|
|
||||||
|
fragmentedFramesText = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Not final frame so we can expect more frames in the
|
||||||
|
// fragmented sequence
|
||||||
|
if (fragmentedFramesCount == 0) {
|
||||||
|
// First text or binary frame for a fragmented set
|
||||||
|
fragmentedFramesText = null;
|
||||||
|
if (frameOpcode == OPCODE_TEXT) {
|
||||||
|
checkUTF8String(ctx, framePayload);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Subsequent frames - only check if init frame is text
|
||||||
|
if (fragmentedFramesText != null) {
|
||||||
|
checkUTF8String(ctx, framePayload);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Increment counter
|
||||||
|
fragmentedFramesCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return the frame
|
||||||
|
if (frameOpcode == OPCODE_TEXT) {
|
||||||
|
return new TextWebSocketFrame(frameFinalFlag, frameRsv, framePayload);
|
||||||
|
} else if (frameOpcode == OPCODE_BINARY) {
|
||||||
|
return new BinaryWebSocketFrame(frameFinalFlag, frameRsv, framePayload);
|
||||||
|
} else if (frameOpcode == OPCODE_CONT) {
|
||||||
|
return new ContinuationWebSocketFrame(frameFinalFlag, frameRsv, framePayload, aggregatedText);
|
||||||
|
} else {
|
||||||
|
throw new UnsupportedOperationException("Cannot decode web socket frame with opcode: "
|
||||||
|
+ frameOpcode);
|
||||||
|
}
|
||||||
|
case CORRUPT:
|
||||||
|
// If we don't keep reading Netty will throw an exception saying
|
||||||
|
// we can't return null if no bytes read and state not changed.
|
||||||
|
in.readByte();
|
||||||
|
return null;
|
||||||
|
default:
|
||||||
|
throw new Error("Shouldn't reach here.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void unmask(ByteBuf frame) {
|
private void unmask(ByteBuf frame) {
|
||||||
byte[] bytes = frame.array();
|
for (int i = frame.readerIndex(); i < frame.writerIndex(); i++) {
|
||||||
for (int i = 0; i < bytes.length; i++) {
|
|
||||||
frame.setByte(i, frame.getByte(i) ^ maskingKey.getByte(i % 4));
|
frame.setByte(i, frame.getByte(i) ^ maskingKey.getByte(i % 4));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -384,12 +387,12 @@ public class WebSocket08FrameDecoder extends ReplayingDecoder<WebSocket08FrameDe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkUTF8String(ChannelHandlerContext ctx, byte[] bytes) {
|
private void checkUTF8String(ChannelHandlerContext ctx, ByteBuf buffer) {
|
||||||
try {
|
try {
|
||||||
if (fragmentedFramesText == null) {
|
if (fragmentedFramesText == null) {
|
||||||
fragmentedFramesText = new UTF8Output(bytes);
|
fragmentedFramesText = new UTF8Output(buffer);
|
||||||
} else {
|
} else {
|
||||||
fragmentedFramesText.write(bytes);
|
fragmentedFramesText.write(buffer);
|
||||||
}
|
}
|
||||||
} catch (UTF8Exception ex) {
|
} catch (UTF8Exception ex) {
|
||||||
protocolViolation(ctx, "invalid UTF-8 bytes");
|
protocolViolation(ctx, "invalid UTF-8 bytes");
|
||||||
@ -418,11 +421,10 @@ public class WebSocket08FrameDecoder extends ReplayingDecoder<WebSocket08FrameDe
|
|||||||
}
|
}
|
||||||
|
|
||||||
// May have UTF-8 message
|
// May have UTF-8 message
|
||||||
if (buffer.readableBytes() > 0) {
|
if (buffer.readable()) {
|
||||||
byte[] b = new byte[buffer.readableBytes()];
|
|
||||||
buffer.readBytes(b);
|
|
||||||
try {
|
try {
|
||||||
new UTF8Output(b);
|
new UTF8Output(buffer);
|
||||||
} catch (UTF8Exception ex) {
|
} catch (UTF8Exception ex) {
|
||||||
protocolViolation(ctx, "Invalid close frame reason text. Invalid UTF-8 bytes");
|
protocolViolation(ctx, "Invalid close frame reason text. Invalid UTF-8 bytes");
|
||||||
}
|
}
|
||||||
|
@ -100,7 +100,7 @@ public class WebSocket08FrameEncoder extends MessageToByteEncoder<WebSocketFrame
|
|||||||
|
|
||||||
byte[] mask;
|
byte[] mask;
|
||||||
|
|
||||||
ByteBuf data = msg.getBinaryData();
|
ByteBuf data = msg.data();
|
||||||
if (data == null) {
|
if (data == null) {
|
||||||
data = Unpooled.EMPTY_BUFFER;
|
data = Unpooled.EMPTY_BUFFER;
|
||||||
}
|
}
|
||||||
@ -132,7 +132,7 @@ public class WebSocket08FrameEncoder extends MessageToByteEncoder<WebSocketFrame
|
|||||||
if (msg.isFinalFragment()) {
|
if (msg.isFinalFragment()) {
|
||||||
b0 |= 1 << 7;
|
b0 |= 1 << 7;
|
||||||
}
|
}
|
||||||
b0 |= msg.getRsv() % 8 << 4;
|
b0 |= msg.rsv() % 8 << 4;
|
||||||
b0 |= opcode % 128;
|
b0 |= opcode % 128;
|
||||||
|
|
||||||
if (opcode == OPCODE_PING && length > 125) {
|
if (opcode == OPCODE_PING && length > 125) {
|
||||||
|
@ -16,40 +16,32 @@
|
|||||||
package io.netty.handler.codec.http.websocketx;
|
package io.netty.handler.codec.http.websocketx;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.DefaultByteBufHolder;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base class for web socket frames
|
* Base class for web socket frames
|
||||||
*/
|
*/
|
||||||
public abstract class WebSocketFrame {
|
public abstract class WebSocketFrame extends DefaultByteBufHolder {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flag to indicate if this frame is the final fragment in a message. The first fragment (frame) may also be the
|
* Flag to indicate if this frame is the final fragment in a message. The first fragment (frame) may also be the
|
||||||
* final fragment.
|
* final fragment.
|
||||||
*/
|
*/
|
||||||
private boolean finalFragment = true;
|
private final boolean finalFragment;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* RSV1, RSV2, RSV3 used for extensions
|
* RSV1, RSV2, RSV3 used for extensions
|
||||||
*/
|
*/
|
||||||
private int rsv;
|
private final int rsv;
|
||||||
|
|
||||||
/**
|
protected WebSocketFrame(ByteBuf binaryData) {
|
||||||
* Contents of this frame
|
this(true, 0, binaryData);
|
||||||
*/
|
|
||||||
private ByteBuf binaryData;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns binary data
|
|
||||||
*/
|
|
||||||
public ByteBuf getBinaryData() {
|
|
||||||
return binaryData;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
protected WebSocketFrame(boolean finalFragment, int rsv, ByteBuf binaryData) {
|
||||||
* Sets the binary data for this frame
|
super(binaryData);
|
||||||
*/
|
this.finalFragment = finalFragment;
|
||||||
public void setBinaryData(ByteBuf binaryData) {
|
this.rsv = rsv;
|
||||||
this.binaryData = binaryData;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -60,19 +52,19 @@ public abstract class WebSocketFrame {
|
|||||||
return finalFragment;
|
return finalFragment;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setFinalFragment(boolean finalFragment) {
|
|
||||||
this.finalFragment = finalFragment;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bits used for extensions to the standard.
|
* Bits used for extensions to the standard.
|
||||||
*/
|
*/
|
||||||
public int getRsv() {
|
public int rsv() {
|
||||||
return rsv;
|
return rsv;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRsv(int rsv) {
|
@Override
|
||||||
this.rsv = rsv;
|
public abstract WebSocketFrame copy();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getClass().getSimpleName() + "(data: " + data().toString() + ')';
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -81,7 +81,7 @@ public class WebSocketServerProtocolHandler extends ChannelInboundMessageHandler
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (frame instanceof PingWebSocketFrame) {
|
if (frame instanceof PingWebSocketFrame) {
|
||||||
ctx.channel().write(new PongWebSocketFrame(frame.getBinaryData()));
|
ctx.channel().write(new PongWebSocketFrame(frame.data()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -100,6 +100,15 @@ public class WebSocketServerProtocolHandler extends ChannelInboundMessageHandler
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void freeInboundMessage(WebSocketFrame msg) throws Exception {
|
||||||
|
if (msg instanceof PingWebSocketFrame || msg instanceof CloseWebSocketFrame) {
|
||||||
|
// Will be freed once wrote back
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
super.freeInboundMessage(msg);
|
||||||
|
}
|
||||||
|
|
||||||
static WebSocketServerHandshaker getHandshaker(ChannelHandlerContext ctx) {
|
static WebSocketServerHandshaker getHandshaker(ChannelHandlerContext ctx) {
|
||||||
return ctx.attr(HANDSHAKER_ATTR_KEY).get();
|
return ctx.attr(HANDSHAKER_ATTR_KEY).get();
|
||||||
}
|
}
|
||||||
|
@ -16,17 +16,17 @@
|
|||||||
package io.netty.handler.codec.spdy;
|
package io.netty.handler.codec.spdy;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.DefaultByteBufHolder;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
import io.netty.util.internal.StringUtil;
|
import io.netty.util.internal.StringUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The default {@link SpdyDataFrame} implementation.
|
* The default {@link SpdyDataFrame} implementation.
|
||||||
*/
|
*/
|
||||||
public class DefaultSpdyDataFrame implements SpdyDataFrame {
|
public class DefaultSpdyDataFrame extends DefaultByteBufHolder implements SpdyDataFrame {
|
||||||
|
|
||||||
private int streamId;
|
private int streamId;
|
||||||
private boolean last;
|
private boolean last;
|
||||||
private ByteBuf data = Unpooled.EMPTY_BUFFER;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new instance.
|
* Creates a new instance.
|
||||||
@ -34,9 +34,28 @@ public class DefaultSpdyDataFrame implements SpdyDataFrame {
|
|||||||
* @param streamId the Stream-ID of this frame
|
* @param streamId the Stream-ID of this frame
|
||||||
*/
|
*/
|
||||||
public DefaultSpdyDataFrame(int streamId) {
|
public DefaultSpdyDataFrame(int streamId) {
|
||||||
|
this(streamId, Unpooled.buffer(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new instance.
|
||||||
|
*
|
||||||
|
* @param streamId the Stream-ID of this frame
|
||||||
|
* @param data the payload of the frame. Can not exceed {@link SpdyCodecUtil#SPDY_MAX_LENGTH}
|
||||||
|
*/
|
||||||
|
public DefaultSpdyDataFrame(int streamId, ByteBuf data) {
|
||||||
|
super(validate(data));
|
||||||
setStreamId(streamId);
|
setStreamId(streamId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static ByteBuf validate(ByteBuf data) {
|
||||||
|
if (data.readableBytes() > SpdyCodecUtil.SPDY_MAX_LENGTH) {
|
||||||
|
throw new IllegalArgumentException("data payload cannot exceed "
|
||||||
|
+ SpdyCodecUtil.SPDY_MAX_LENGTH + " bytes");
|
||||||
|
}
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getStreamId() {
|
public int getStreamId() {
|
||||||
return streamId;
|
return streamId;
|
||||||
@ -62,20 +81,10 @@ public class DefaultSpdyDataFrame implements SpdyDataFrame {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ByteBuf getData() {
|
public DefaultSpdyDataFrame copy() {
|
||||||
return data;
|
DefaultSpdyDataFrame frame = new DefaultSpdyDataFrame(getStreamId(), data().copy());
|
||||||
}
|
frame.setLast(isLast());
|
||||||
|
return frame;
|
||||||
@Override
|
|
||||||
public void setData(ByteBuf data) {
|
|
||||||
if (data == null) {
|
|
||||||
data = Unpooled.EMPTY_BUFFER;
|
|
||||||
}
|
|
||||||
if (data.readableBytes() > SpdyCodecUtil.SPDY_MAX_LENGTH) {
|
|
||||||
throw new IllegalArgumentException("data payload cannot exceed "
|
|
||||||
+ SpdyCodecUtil.SPDY_MAX_LENGTH + " bytes");
|
|
||||||
}
|
|
||||||
this.data = data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -90,7 +99,11 @@ public class DefaultSpdyDataFrame implements SpdyDataFrame {
|
|||||||
buf.append(streamId);
|
buf.append(streamId);
|
||||||
buf.append(StringUtil.NEWLINE);
|
buf.append(StringUtil.NEWLINE);
|
||||||
buf.append("--> Size = ");
|
buf.append("--> Size = ");
|
||||||
buf.append(data.readableBytes());
|
if (isFreed()) {
|
||||||
|
buf.append("(freed)");
|
||||||
|
} else {
|
||||||
|
buf.append(data().readableBytes());
|
||||||
|
}
|
||||||
return buf.toString();
|
return buf.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,12 +16,13 @@
|
|||||||
package io.netty.handler.codec.spdy;
|
package io.netty.handler.codec.spdy;
|
||||||
|
|
||||||
import io.netty.buffer.ByteBuf;
|
import io.netty.buffer.ByteBuf;
|
||||||
|
import io.netty.buffer.ByteBufHolder;
|
||||||
import io.netty.buffer.Unpooled;
|
import io.netty.buffer.Unpooled;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A SPDY Protocol Data Frame
|
* A SPDY Protocol Data Frame
|
||||||
*/
|
*/
|
||||||
public interface SpdyDataFrame {
|
public interface SpdyDataFrame extends ByteBufHolder {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the Stream-ID of this frame.
|
* Returns the Stream-ID of this frame.
|
||||||
@ -47,13 +48,12 @@ public interface SpdyDataFrame {
|
|||||||
/**
|
/**
|
||||||
* Returns the data payload of this frame. If there is no data payload
|
* Returns the data payload of this frame. If there is no data payload
|
||||||
* {@link Unpooled#EMPTY_BUFFER} is returned.
|
* {@link Unpooled#EMPTY_BUFFER} is returned.
|
||||||
*/
|
*
|
||||||
ByteBuf getData();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the data payload of this frame. If {@code null} is specified,
|
|
||||||
* the data payload will be set to {@link Unpooled#EMPTY_BUFFER}.
|
|
||||||
* The data payload cannot exceed 16777215 bytes.
|
* The data payload cannot exceed 16777215 bytes.
|
||||||
*/
|
*/
|
||||||
void setData(ByteBuf data);
|
@Override
|
||||||
|
ByteBuf data();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
SpdyDataFrame copy();
|
||||||
}
|
}
|
||||||
|
@ -281,8 +281,9 @@ public class SpdyFrameDecoder extends ByteToMessageDecoder {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
SpdyDataFrame spdyDataFrame = new DefaultSpdyDataFrame(streamID);
|
ByteBuf data = ctx.alloc().buffer(dataLength);
|
||||||
spdyDataFrame.setData(buffer.readBytes(dataLength));
|
data.writeBytes(buffer, dataLength);
|
||||||
|
SpdyDataFrame spdyDataFrame = new DefaultSpdyDataFrame(streamID, data);
|
||||||
length -= dataLength;
|
length -= dataLength;
|
||||||
|
|
||||||
if (length == 0) {
|
if (length == 0) {
|
||||||
|
@ -82,7 +82,7 @@ public class SpdyFrameEncoder extends MessageToByteEncoder<Object> {
|
|||||||
if (msg instanceof SpdyDataFrame) {
|
if (msg instanceof SpdyDataFrame) {
|
||||||
|
|
||||||
SpdyDataFrame spdyDataFrame = (SpdyDataFrame) msg;
|
SpdyDataFrame spdyDataFrame = (SpdyDataFrame) msg;
|
||||||
ByteBuf data = spdyDataFrame.getData();
|
ByteBuf data = spdyDataFrame.data();
|
||||||
byte flags = spdyDataFrame.isLast() ? SPDY_DATA_FLAG_FIN : 0;
|
byte flags = spdyDataFrame.isLast() ? SPDY_DATA_FLAG_FIN : 0;
|
||||||
out.ensureWritableBytes(SPDY_HEADER_SIZE + data.readableBytes());
|
out.ensureWritableBytes(SPDY_HEADER_SIZE + data.readableBytes());
|
||||||
out.writeInt(spdyDataFrame.getStreamId() & 0x7FFFFFFF);
|
out.writeInt(spdyDataFrame.getStreamId() & 0x7FFFFFFF);
|
||||||
|
@ -199,13 +199,13 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<Object> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
ByteBuf content = fullHttpMessage.data();
|
ByteBuf content = fullHttpMessage.data();
|
||||||
if (content.readableBytes() > maxContentLength - spdyDataFrame.getData().readableBytes()) {
|
if (content.readableBytes() > maxContentLength - spdyDataFrame.data().readableBytes()) {
|
||||||
messageMap.remove(streamID);
|
messageMap.remove(streamID);
|
||||||
throw new TooLongFrameException(
|
throw new TooLongFrameException(
|
||||||
"HTTP content length exceeded " + maxContentLength + " bytes.");
|
"HTTP content length exceeded " + maxContentLength + " bytes.");
|
||||||
}
|
}
|
||||||
|
|
||||||
ByteBuf spdyDataFrameData = spdyDataFrame.getData();
|
ByteBuf spdyDataFrameData = spdyDataFrame.data();
|
||||||
int spdyDataFrameDataLen = spdyDataFrameData.readableBytes();
|
int spdyDataFrameDataLen = spdyDataFrameData.readableBytes();
|
||||||
content.writeBytes(spdyDataFrameData, spdyDataFrameData.readerIndex(), spdyDataFrameDataLen);
|
content.writeBytes(spdyDataFrameData, spdyDataFrameData.readerIndex(), spdyDataFrameDataLen);
|
||||||
|
|
||||||
|
@ -164,10 +164,8 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder<Object> {
|
|||||||
if (msg instanceof HttpContent) {
|
if (msg instanceof HttpContent) {
|
||||||
|
|
||||||
HttpContent chunk = (HttpContent) msg;
|
HttpContent chunk = (HttpContent) msg;
|
||||||
SpdyDataFrame spdyDataFrame = new DefaultSpdyDataFrame(currentStreamId);
|
SpdyDataFrame spdyDataFrame = new DefaultSpdyDataFrame(currentStreamId, chunk.data());
|
||||||
spdyDataFrame.setData(chunk.data());
|
|
||||||
spdyDataFrame.setLast(chunk instanceof LastHttpContent);
|
spdyDataFrame.setLast(chunk instanceof LastHttpContent);
|
||||||
|
|
||||||
if (chunk instanceof LastHttpContent) {
|
if (chunk instanceof LastHttpContent) {
|
||||||
LastHttpContent trailer = (LastHttpContent) chunk;
|
LastHttpContent trailer = (LastHttpContent) chunk;
|
||||||
List<Map.Entry<String, String>> trailers = trailer.trailingHeaders().entries();
|
List<Map.Entry<String, String>> trailers = trailer.trailingHeaders().entries();
|
||||||
@ -288,4 +286,13 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder<Object> {
|
|||||||
|
|
||||||
return spdySynReplyFrame;
|
return spdySynReplyFrame;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void freeOutboundMessage(Object msg) throws Exception {
|
||||||
|
if (msg instanceof HttpContent) {
|
||||||
|
// Will be freed later as the content of them is just reused here
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
super.freeOutboundMessage(msg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -61,4 +61,13 @@ public class SpdyHttpResponseStreamIdHandler extends
|
|||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void freeInboundMessage(Object msg) throws Exception {
|
||||||
|
// just pass through so no free
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void freeOutboundMessage(HttpMessage msg) throws Exception {
|
||||||
|
// just pass through so no free
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -190,7 +190,7 @@ public class SpdySessionHandler
|
|||||||
|
|
||||||
if (flowControl) {
|
if (flowControl) {
|
||||||
// Update receive window size
|
// Update receive window size
|
||||||
int deltaWindowSize = -1 * spdyDataFrame.getData().readableBytes();
|
int deltaWindowSize = -1 * spdyDataFrame.data().readableBytes();
|
||||||
int newWindowSize = spdySession.updateReceiveWindowSize(streamID, deltaWindowSize);
|
int newWindowSize = spdySession.updateReceiveWindowSize(streamID, deltaWindowSize);
|
||||||
|
|
||||||
// Window size can become negative if we sent a SETTINGS frame that reduces the
|
// Window size can become negative if we sent a SETTINGS frame that reduces the
|
||||||
@ -206,9 +206,9 @@ public class SpdySessionHandler
|
|||||||
// Window size became negative due to sender writing frame before receiving SETTINGS
|
// Window size became negative due to sender writing frame before receiving SETTINGS
|
||||||
// Send data frames upstream in initialReceiveWindowSize chunks
|
// Send data frames upstream in initialReceiveWindowSize chunks
|
||||||
if (newWindowSize < 0) {
|
if (newWindowSize < 0) {
|
||||||
while (spdyDataFrame.getData().readableBytes() > initialReceiveWindowSize) {
|
while (spdyDataFrame.data().readableBytes() > initialReceiveWindowSize) {
|
||||||
SpdyDataFrame partialDataFrame = new DefaultSpdyDataFrame(streamID);
|
SpdyDataFrame partialDataFrame = new DefaultSpdyDataFrame(streamID,
|
||||||
partialDataFrame.setData(spdyDataFrame.getData().readSlice(initialReceiveWindowSize));
|
spdyDataFrame.data().readSlice(initialReceiveWindowSize));
|
||||||
ctx.nextOutboundMessageBuffer().add(partialDataFrame);
|
ctx.nextOutboundMessageBuffer().add(partialDataFrame);
|
||||||
ctx.flush();
|
ctx.flush();
|
||||||
}
|
}
|
||||||
@ -496,7 +496,7 @@ public class SpdySessionHandler
|
|||||||
|
|
||||||
if (flowControl) {
|
if (flowControl) {
|
||||||
synchronized (flowControlLock) {
|
synchronized (flowControlLock) {
|
||||||
int dataLength = spdyDataFrame.getData().readableBytes();
|
int dataLength = spdyDataFrame.data().readableBytes();
|
||||||
int sendWindowSize = spdySession.getSendWindowSize(streamID);
|
int sendWindowSize = spdySession.getSendWindowSize(streamID);
|
||||||
|
|
||||||
if (sendWindowSize >= dataLength) {
|
if (sendWindowSize >= dataLength) {
|
||||||
@ -524,8 +524,8 @@ public class SpdySessionHandler
|
|||||||
spdySession.updateSendWindowSize(streamID, -1 * sendWindowSize);
|
spdySession.updateSendWindowSize(streamID, -1 * sendWindowSize);
|
||||||
|
|
||||||
// Create a partial data frame whose length is the current window size
|
// Create a partial data frame whose length is the current window size
|
||||||
SpdyDataFrame partialDataFrame = new DefaultSpdyDataFrame(streamID);
|
SpdyDataFrame partialDataFrame = new DefaultSpdyDataFrame(streamID,
|
||||||
partialDataFrame.setData(spdyDataFrame.getData().readSlice(sendWindowSize));
|
spdyDataFrame.data().readSlice(sendWindowSize));
|
||||||
|
|
||||||
// Enqueue the remaining data (will be the first frame queued)
|
// Enqueue the remaining data (will be the first frame queued)
|
||||||
spdySession.putPendingWrite(streamID, spdyDataFrame);
|
spdySession.putPendingWrite(streamID, spdyDataFrame);
|
||||||
@ -814,7 +814,7 @@ public class SpdySessionHandler
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
int dataFrameSize = spdyDataFrame.getData().readableBytes();
|
int dataFrameSize = spdyDataFrame.data().readableBytes();
|
||||||
|
|
||||||
if (newWindowSize >= dataFrameSize) {
|
if (newWindowSize >= dataFrameSize) {
|
||||||
// Window size is large enough to send entire data frame
|
// Window size is large enough to send entire data frame
|
||||||
@ -848,8 +848,8 @@ public class SpdySessionHandler
|
|||||||
spdySession.updateSendWindowSize(streamID, -1 * newWindowSize);
|
spdySession.updateSendWindowSize(streamID, -1 * newWindowSize);
|
||||||
|
|
||||||
// Create a partial data frame whose length is the current window size
|
// Create a partial data frame whose length is the current window size
|
||||||
SpdyDataFrame partialDataFrame = new DefaultSpdyDataFrame(streamID);
|
SpdyDataFrame partialDataFrame = new DefaultSpdyDataFrame(streamID,
|
||||||
partialDataFrame.setData(spdyDataFrame.getData().readSlice(newWindowSize));
|
spdyDataFrame.data().readSlice(newWindowSize));
|
||||||
|
|
||||||
// The transfer window size is pre-decremented when sending a data frame downstream.
|
// The transfer window size is pre-decremented when sending a data frame downstream.
|
||||||
// Close the stream on write failures that leaves the transfer window in a corrupt state.
|
// Close the stream on write failures that leaves the transfer window in a corrupt state.
|
||||||
|
@ -150,7 +150,7 @@ public class WebSocketServerProtocolHandlerTest {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void messageReceived(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
|
public void messageReceived(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
|
||||||
content = "processed: " + msg.getText();
|
content = "processed: " + msg.text();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getContent() {
|
public String getContent() {
|
||||||
|
@ -145,7 +145,6 @@ public class HttpSnoopServerHandler extends ChannelInboundMessageHandlerAdapter<
|
|||||||
private void writeResponse(ChannelHandlerContext ctx, HttpObject currentObj) {
|
private void writeResponse(ChannelHandlerContext ctx, HttpObject currentObj) {
|
||||||
// Decide whether to close the connection or not.
|
// Decide whether to close the connection or not.
|
||||||
boolean keepAlive = isKeepAlive(request);
|
boolean keepAlive = isKeepAlive(request);
|
||||||
|
|
||||||
// Build the response object.
|
// Build the response object.
|
||||||
FullHttpResponse response = new DefaultFullHttpResponse(
|
FullHttpResponse response = new DefaultFullHttpResponse(
|
||||||
HTTP_1_1, currentObj.decoderResult().isSuccess()? OK : BAD_REQUEST,
|
HTTP_1_1, currentObj.decoderResult().isSuccess()? OK : BAD_REQUEST,
|
||||||
|
@ -93,17 +93,17 @@ public class AutobahnServerHandler extends ChannelInboundMessageHandlerAdapter<O
|
|||||||
handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame);
|
handshaker.close(ctx.channel(), (CloseWebSocketFrame) frame);
|
||||||
} else if (frame instanceof PingWebSocketFrame) {
|
} else if (frame instanceof PingWebSocketFrame) {
|
||||||
ctx.channel().write(
|
ctx.channel().write(
|
||||||
new PongWebSocketFrame(frame.isFinalFragment(), frame.getRsv(), frame.getBinaryData()));
|
new PongWebSocketFrame(frame.isFinalFragment(), frame.rsv(), frame.data()));
|
||||||
} else if (frame instanceof TextWebSocketFrame) {
|
} else if (frame instanceof TextWebSocketFrame) {
|
||||||
// String text = ((TextWebSocketFrame) frame).getText();
|
// String text = ((TextWebSocketFrame) frame).getText();
|
||||||
ctx.channel().write(
|
ctx.channel().write(
|
||||||
new TextWebSocketFrame(frame.isFinalFragment(), frame.getRsv(), frame.getBinaryData()));
|
new TextWebSocketFrame(frame.isFinalFragment(), frame.rsv(), frame.data()));
|
||||||
} else if (frame instanceof BinaryWebSocketFrame) {
|
} else if (frame instanceof BinaryWebSocketFrame) {
|
||||||
ctx.channel().write(
|
ctx.channel().write(
|
||||||
new BinaryWebSocketFrame(frame.isFinalFragment(), frame.getRsv(), frame.getBinaryData()));
|
new BinaryWebSocketFrame(frame.isFinalFragment(), frame.rsv(), frame.data()));
|
||||||
} else if (frame instanceof ContinuationWebSocketFrame) {
|
} else if (frame instanceof ContinuationWebSocketFrame) {
|
||||||
ctx.channel().write(
|
ctx.channel().write(
|
||||||
new ContinuationWebSocketFrame(frame.isFinalFragment(), frame.getRsv(), frame.getBinaryData()));
|
new ContinuationWebSocketFrame(frame.isFinalFragment(), frame.rsv(), frame.data()));
|
||||||
} else if (frame instanceof PongWebSocketFrame) {
|
} else if (frame instanceof PongWebSocketFrame) {
|
||||||
// Ignore
|
// Ignore
|
||||||
} else {
|
} else {
|
||||||
@ -136,4 +136,14 @@ public class AutobahnServerHandler extends ChannelInboundMessageHandlerAdapter<O
|
|||||||
private static String getWebSocketLocation(FullHttpRequest req) {
|
private static String getWebSocketLocation(FullHttpRequest req) {
|
||||||
return "ws://" + req.headers().get(HttpHeaders.Names.HOST);
|
return "ws://" + req.headers().get(HttpHeaders.Names.HOST);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void freeInboundMessage(Object msg) throws Exception {
|
||||||
|
if (!(msg instanceof PongWebSocketFrame) && msg instanceof WebSocketFrame) {
|
||||||
|
// will be freed once written by the encoder
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
super.freeInboundMessage(msg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,7 +97,7 @@ public class WebSocketClientHandler extends ChannelInboundMessageHandlerAdapter<
|
|||||||
WebSocketFrame frame = (WebSocketFrame) msg;
|
WebSocketFrame frame = (WebSocketFrame) msg;
|
||||||
if (frame instanceof TextWebSocketFrame) {
|
if (frame instanceof TextWebSocketFrame) {
|
||||||
TextWebSocketFrame textFrame = (TextWebSocketFrame) frame;
|
TextWebSocketFrame textFrame = (TextWebSocketFrame) frame;
|
||||||
System.out.println("WebSocket Client received message: " + textFrame.getText());
|
System.out.println("WebSocket Client received message: " + textFrame.text());
|
||||||
} else if (frame instanceof PongWebSocketFrame) {
|
} else if (frame instanceof PongWebSocketFrame) {
|
||||||
System.out.println("WebSocket Client received pong");
|
System.out.println("WebSocket Client received pong");
|
||||||
} else if (frame instanceof CloseWebSocketFrame) {
|
} else if (frame instanceof CloseWebSocketFrame) {
|
||||||
|
@ -23,7 +23,7 @@ public class CustomTextFrameHandler extends ChannelInboundMessageHandlerAdapter<
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void messageReceived(ChannelHandlerContext ctx, TextWebSocketFrame frame) throws Exception {
|
public void messageReceived(ChannelHandlerContext ctx, TextWebSocketFrame frame) throws Exception {
|
||||||
String request = frame.getText();
|
String request = frame.text();
|
||||||
ctx.channel().write(new TextWebSocketFrame(request.toUpperCase()));
|
ctx.channel().write(new TextWebSocketFrame(request.toUpperCase()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,7 +109,7 @@ public class WebSocketServerHandler extends ChannelInboundMessageHandlerAdapter<
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (frame instanceof PingWebSocketFrame) {
|
if (frame instanceof PingWebSocketFrame) {
|
||||||
ctx.channel().write(new PongWebSocketFrame(frame.getBinaryData()));
|
ctx.channel().write(new PongWebSocketFrame(frame.data()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!(frame instanceof TextWebSocketFrame)) {
|
if (!(frame instanceof TextWebSocketFrame)) {
|
||||||
@ -118,7 +118,7 @@ public class WebSocketServerHandler extends ChannelInboundMessageHandlerAdapter<
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Send the uppercase string back.
|
// Send the uppercase string back.
|
||||||
String request = ((TextWebSocketFrame) frame).getText();
|
String request = ((TextWebSocketFrame) frame).text();
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug(String.format("Channel %s received %s", ctx.channel().id(), request));
|
logger.debug(String.format("Channel %s received %s", ctx.channel().id(), request));
|
||||||
}
|
}
|
||||||
@ -146,6 +146,15 @@ public class WebSocketServerHandler extends ChannelInboundMessageHandlerAdapter<
|
|||||||
ctx.close();
|
ctx.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void freeInboundMessage(Object msg) throws Exception {
|
||||||
|
if (msg instanceof PingWebSocketFrame || msg instanceof CloseWebSocketFrame) {
|
||||||
|
// Will be freed once wrote back
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
super.freeInboundMessage(msg);
|
||||||
|
}
|
||||||
|
|
||||||
private static String getWebSocketLocation(FullHttpRequest req) {
|
private static String getWebSocketLocation(FullHttpRequest req) {
|
||||||
return "ws://" + req.headers().get(HOST) + WEBSOCKET_PATH;
|
return "ws://" + req.headers().get(HOST) + WEBSOCKET_PATH;
|
||||||
}
|
}
|
||||||
|
@ -111,7 +111,7 @@ public class WebSocketSslServerHandler extends ChannelInboundMessageHandlerAdapt
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (frame instanceof PingWebSocketFrame) {
|
if (frame instanceof PingWebSocketFrame) {
|
||||||
ctx.channel().write(new PongWebSocketFrame(frame.getBinaryData()));
|
ctx.channel().write(new PongWebSocketFrame(frame.data()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!(frame instanceof TextWebSocketFrame)) {
|
if (!(frame instanceof TextWebSocketFrame)) {
|
||||||
@ -120,7 +120,7 @@ public class WebSocketSslServerHandler extends ChannelInboundMessageHandlerAdapt
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Send the uppercase string back.
|
// Send the uppercase string back.
|
||||||
String request = ((TextWebSocketFrame) frame).getText();
|
String request = ((TextWebSocketFrame) frame).text();
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug(String.format("Channel %s received %s", ctx.channel().id(), request));
|
logger.debug(String.format("Channel %s received %s", ctx.channel().id(), request));
|
||||||
}
|
}
|
||||||
@ -151,4 +151,13 @@ public class WebSocketSslServerHandler extends ChannelInboundMessageHandlerAdapt
|
|||||||
private static String getWebSocketLocation(FullHttpRequest req) {
|
private static String getWebSocketLocation(FullHttpRequest req) {
|
||||||
return "wss://" + req.headers().get(HOST) + WEBSOCKET_PATH;
|
return "wss://" + req.headers().get(HOST) + WEBSOCKET_PATH;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void freeInboundMessage(Object msg) throws Exception {
|
||||||
|
if (msg instanceof PingWebSocketFrame || msg instanceof CloseWebSocketFrame) {
|
||||||
|
// Will be freed once wrote back
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
super.freeInboundMessage(msg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user