Zap marshalling and spdy codec to forwardport again

This commit is contained in:
Trustin Lee 2012-05-30 23:14:18 -07:00
parent 7fb64e2046
commit d7a198a60f
60 changed files with 0 additions and 6437 deletions

View File

@ -1,101 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.spdy;
import io.netty.buffer.ChannelBuffer;
import io.netty.buffer.ChannelBuffers;
import io.netty.util.internal.StringUtil;
/**
* The default {@link SpdyDataFrame} implementation.
*/
public class DefaultSpdyDataFrame implements SpdyDataFrame {
private int streamID;
private boolean last;
private boolean compressed;
private ChannelBuffer data = ChannelBuffers.EMPTY_BUFFER;
/**
* Creates a new instance.
*
* @param streamID the Stream-ID of this frame
*/
public DefaultSpdyDataFrame(int streamID) {
setStreamID(streamID);
}
public int getStreamID() {
return streamID;
}
public void setStreamID(int streamID) {
if (streamID <= 0) {
throw new IllegalArgumentException(
"Stream-ID must be positive: " + streamID);
}
this.streamID = streamID;
}
public boolean isLast() {
return last;
}
public void setLast(boolean last) {
this.last = last;
}
public boolean isCompressed() {
return compressed;
}
public void setCompressed(boolean compressed) {
this.compressed = compressed;
}
public ChannelBuffer getData() {
return data;
}
public void setData(ChannelBuffer data) {
if (data == null) {
data = ChannelBuffers.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
public String toString() {
StringBuilder buf = new StringBuilder();
buf.append(getClass().getSimpleName());
buf.append("(last: ");
buf.append(isLast());
buf.append("; compressed: ");
buf.append(isCompressed());
buf.append(')');
buf.append(StringUtil.NEWLINE);
buf.append("--> Stream-ID = ");
buf.append(streamID);
buf.append(StringUtil.NEWLINE);
buf.append("--> Size = ");
buf.append(data.readableBytes());
return buf.toString();
}
}

View File

@ -1,57 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.spdy;
import io.netty.util.internal.StringUtil;
/**
* The default {@link SpdyGoAwayFrame} implementation.
*/
public class DefaultSpdyGoAwayFrame implements SpdyGoAwayFrame {
private int lastGoodStreamID;
/**
* Creates a new instance.
*
* @param lastGoodStreamID the Last-good-stream-ID of this frame
*/
public DefaultSpdyGoAwayFrame(int lastGoodStreamID) {
setLastGoodStreamID(lastGoodStreamID);
}
public int getLastGoodStreamID() {
return lastGoodStreamID;
}
public void setLastGoodStreamID(int lastGoodStreamID) {
if (lastGoodStreamID < 0) {
throw new IllegalArgumentException("Last-good-stream-ID"
+ " cannot be negative: " + lastGoodStreamID);
}
this.lastGoodStreamID = lastGoodStreamID;
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder();
buf.append(getClass().getSimpleName());
buf.append(StringUtil.NEWLINE);
buf.append("--> Last-good-stream-ID = ");
buf.append(lastGoodStreamID);
return buf.toString();
}
}

View File

@ -1,95 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.spdy;
import java.util.List;
import java.util.Map;
import java.util.Set;
import io.netty.util.internal.StringUtil;
/**
* The default {@link SpdyHeaderBlock} implementation.
*/
public class DefaultSpdyHeaderBlock implements SpdyHeaderBlock {
private boolean invalid;
private final SpdyHeaders headers = new SpdyHeaders();
/**
* Creates a new instance.
*/
protected DefaultSpdyHeaderBlock() {
}
public boolean isInvalid() {
return invalid;
}
public void setInvalid() {
this.invalid = true;
}
public void addHeader(final String name, final Object value) {
headers.addHeader(name, value);
}
public void setHeader(final String name, final Object value) {
headers.setHeader(name, value);
}
public void setHeader(final String name, final Iterable<?> values) {
headers.setHeader(name, values);
}
public void removeHeader(final String name) {
headers.removeHeader(name);
}
public void clearHeaders() {
headers.clearHeaders();
}
public String getHeader(final String name) {
return headers.getHeader(name);
}
public List<String> getHeaders(final String name) {
return headers.getHeaders(name);
}
public List<Map.Entry<String, String>> getHeaders() {
return headers.getHeaders();
}
public boolean containsHeader(final String name) {
return headers.containsHeader(name);
}
public Set<String> getHeaderNames() {
return headers.getHeaderNames();
}
protected void appendHeaders(StringBuilder buf) {
for (Map.Entry<String, String> e: getHeaders()) {
buf.append(" ");
buf.append(e.getKey());
buf.append(": ");
buf.append(e.getValue());
buf.append(StringUtil.NEWLINE);
}
}
}

View File

@ -1,66 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.spdy;
import io.netty.util.internal.StringUtil;
/**
* The default {@link SpdyHeadersFrame} implementation.
*/
public class DefaultSpdyHeadersFrame extends DefaultSpdyHeaderBlock
implements SpdyHeadersFrame {
private int streamID;
/**
* Creates a new instance.
*
* @param streamID the Stream-ID of this frame
*/
public DefaultSpdyHeadersFrame(int streamID) {
super();
setStreamID(streamID);
}
public int getStreamID() {
return streamID;
}
public void setStreamID(int streamID) {
if (streamID <= 0) {
throw new IllegalArgumentException(
"Stream-ID must be positive: " + streamID);
}
this.streamID = streamID;
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder();
buf.append(getClass().getSimpleName());
buf.append(StringUtil.NEWLINE);
buf.append("--> Stream-ID = ");
buf.append(streamID);
buf.append(StringUtil.NEWLINE);
buf.append("--> Headers:");
buf.append(StringUtil.NEWLINE);
appendHeaders(buf);
// Remove the last newline.
buf.setLength(buf.length() - StringUtil.NEWLINE.length());
return buf.toString();
}
}

View File

@ -1,33 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.spdy;
/**
* The default {@link SpdyNoOpFrame} implementation.
*/
public class DefaultSpdyNoOpFrame implements SpdyNoOpFrame {
/**
* Creates a new instance.
*/
public DefaultSpdyNoOpFrame() {
}
@Override
public String toString() {
return getClass().getSimpleName();
}
}

View File

@ -1,53 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.spdy;
import io.netty.util.internal.StringUtil;
/**
* The default {@link SpdyPingFrame} implementation.
*/
public class DefaultSpdyPingFrame implements SpdyPingFrame {
private int ID;
/**
* Creates a new instance.
*
* @param ID the unique ID of this frame
*/
public DefaultSpdyPingFrame(int ID) {
setID(ID);
}
public int getID() {
return ID;
}
public void setID(int ID) {
this.ID = ID;
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder();
buf.append(getClass().getSimpleName());
buf.append(StringUtil.NEWLINE);
buf.append("--> ID = ");
buf.append(ID);
return buf.toString();
}
}

View File

@ -1,81 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.spdy;
import io.netty.util.internal.StringUtil;
/**
* The default {@link SpdyRstStreamFrame} implementation.
*/
public class DefaultSpdyRstStreamFrame implements SpdyRstStreamFrame {
private int streamID;
private SpdyStreamStatus status;
/**
* Creates a new instance.
*
* @param streamID the Stream-ID of this frame
* @param statusCode the Status code of this frame
*/
public DefaultSpdyRstStreamFrame(int streamID, int statusCode) {
this(streamID, SpdyStreamStatus.valueOf(statusCode));
}
/**
* Creates a new instance.
*
* @param streamID the Stream-ID of this frame
* @param status the status of this frame
*/
public DefaultSpdyRstStreamFrame(int streamID, SpdyStreamStatus status) {
setStreamID(streamID);
setStatus(status);
}
public int getStreamID() {
return streamID;
}
public void setStreamID(int streamID) {
if (streamID <= 0) {
throw new IllegalArgumentException(
"Stream-ID must be positive: " + streamID);
}
this.streamID = streamID;
}
public SpdyStreamStatus getStatus() {
return status;
}
public void setStatus(SpdyStreamStatus status) {
this.status = status;
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder();
buf.append(getClass().getSimpleName());
buf.append(StringUtil.NEWLINE);
buf.append("--> Stream-ID = ");
buf.append(streamID);
buf.append(StringUtil.NEWLINE);
buf.append("--> Status: ");
buf.append(status.toString());
return buf.toString();
}
}

View File

@ -1,188 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.spdy;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import io.netty.util.internal.StringUtil;
/**
* The default {@link SpdySettingsFrame} implementation.
*/
public class DefaultSpdySettingsFrame implements SpdySettingsFrame {
private boolean clear;
private final Map<Integer, Setting> settingsMap = new TreeMap<Integer, Setting>();
/**
* Creates a new instance.
*/
public DefaultSpdySettingsFrame() {
}
public Set<Integer> getIDs() {
return settingsMap.keySet();
}
public boolean isSet(int ID) {
Integer key = new Integer(ID);
return settingsMap.containsKey(key);
}
public int getValue(int ID) {
Integer key = new Integer(ID);
if (settingsMap.containsKey(key)) {
return settingsMap.get(key).getValue();
} else {
return -1;
}
}
public void setValue(int ID, int value) {
setValue(ID, value, false, false);
}
public void setValue(int ID, int value, boolean persistValue, boolean persisted) {
if (ID <= 0 || ID > SpdyCodecUtil.SPDY_SETTINGS_MAX_ID) {
throw new IllegalArgumentException("Setting ID is not valid: " + ID);
}
Integer key = new Integer(ID);
if (settingsMap.containsKey(key)) {
Setting setting = settingsMap.get(key);
setting.setValue(value);
setting.setPersist(persistValue);
setting.setPersisted(persisted);
} else {
settingsMap.put(key, new Setting(value, persistValue, persisted));
}
}
public void removeValue(int ID) {
Integer key = new Integer(ID);
if (settingsMap.containsKey(key)) {
settingsMap.remove(key);
}
}
public boolean persistValue(int ID) {
Integer key = new Integer(ID);
if (settingsMap.containsKey(key)) {
return settingsMap.get(key).getPersist();
} else {
return false;
}
}
public void setPersistValue(int ID, boolean persistValue) {
Integer key = new Integer(ID);
if (settingsMap.containsKey(key)) {
settingsMap.get(key).setPersist(persistValue);
}
}
public boolean isPersisted(int ID) {
Integer key = new Integer(ID);
if (settingsMap.containsKey(key)) {
return settingsMap.get(key).getPersisted();
} else {
return false;
}
}
public void setPersisted(int ID, boolean persisted) {
Integer key = new Integer(ID);
if (settingsMap.containsKey(key)) {
settingsMap.get(key).setPersisted(persisted);
}
}
public boolean clearPreviouslyPersistedSettings() {
return clear;
}
public void setClearPreviouslyPersistedSettings(boolean clear) {
this.clear = clear;
}
private Set<Map.Entry<Integer, Setting>> getSettings() {
return settingsMap.entrySet();
}
private void appendSettings(StringBuilder buf) {
for (Map.Entry<Integer, Setting> e: getSettings()) {
Setting setting = e.getValue();
buf.append("--> ");
buf.append(e.getKey().toString());
buf.append(":");
buf.append(setting.getValue());
buf.append(" (persist value: ");
buf.append(setting.getPersist());
buf.append("; persisted: ");
buf.append(setting.getPersisted());
buf.append(')');
buf.append(StringUtil.NEWLINE);
}
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder();
buf.append(getClass().getSimpleName());
buf.append(StringUtil.NEWLINE);
appendSettings(buf);
buf.setLength(buf.length() - StringUtil.NEWLINE.length());
return buf.toString();
}
private static final class Setting {
private int value;
private boolean persist;
private boolean persisted;
public Setting(int value, boolean persist, boolean persisted) {
this.value = value;
this.persist = persist;
this.persisted = persisted;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public boolean getPersist() {
return persist;
}
public void setPersist(boolean persist) {
this.persist = persist;
}
public boolean getPersisted() {
return persisted;
}
public void setPersisted(boolean persisted) {
this.persisted = persisted;
}
}
}

View File

@ -1,78 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.spdy;
import io.netty.util.internal.StringUtil;
/**
* The default {@link SpdySynReplyFrame} implementation.
*/
public class DefaultSpdySynReplyFrame extends DefaultSpdyHeaderBlock
implements SpdySynReplyFrame {
private int streamID;
private boolean last;
/**
* Creates a new instance.
*
* @param streamID the Stream-ID of this frame
*/
public DefaultSpdySynReplyFrame(int streamID) {
super();
setStreamID(streamID);
}
public int getStreamID() {
return streamID;
}
public void setStreamID(int streamID) {
if (streamID <= 0) {
throw new IllegalArgumentException(
"Stream-ID must be positive: " + streamID);
}
this.streamID = streamID;
}
public boolean isLast() {
return last;
}
public void setLast(boolean last) {
this.last = last;
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder();
buf.append(getClass().getSimpleName());
buf.append("(last: ");
buf.append(isLast());
buf.append(')');
buf.append(StringUtil.NEWLINE);
buf.append("--> Stream-ID = ");
buf.append(streamID);
buf.append(StringUtil.NEWLINE);
buf.append("--> Headers:");
buf.append(StringUtil.NEWLINE);
appendHeaders(buf);
// Remove the last newline.
buf.setLength(buf.length() - StringUtil.NEWLINE.length());
return buf.toString();
}
}

View File

@ -1,129 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.spdy;
import io.netty.util.internal.StringUtil;
/**
* The default {@link SpdySynStreamFrame} implementation.
*/
public class DefaultSpdySynStreamFrame extends DefaultSpdyHeaderBlock
implements SpdySynStreamFrame {
private int streamID;
private int associatedToStreamID;
private byte priority;
private boolean last;
private boolean unidirectional;
/**
* Creates a new instance.
*
* @param streamID the Stream-ID of this frame
* @param associatedToStreamID the Associated-To-Stream-ID of this frame
* @param priority the priority of the stream
*/
public DefaultSpdySynStreamFrame(
int streamID, int associatedToStreamID, byte priority) {
super();
setStreamID(streamID);
setAssociatedToStreamID(associatedToStreamID);
setPriority(priority);
}
public int getStreamID() {
return streamID;
}
public void setStreamID(int streamID) {
if (streamID <= 0) {
throw new IllegalArgumentException(
"Stream-ID must be positive: " + streamID);
}
this.streamID = streamID;
}
public int getAssociatedToStreamID() {
return associatedToStreamID;
}
public void setAssociatedToStreamID(int associatedToStreamID) {
if (associatedToStreamID < 0) {
throw new IllegalArgumentException(
"Associated-To-Stream-ID cannot be negative: " +
associatedToStreamID);
}
this.associatedToStreamID = associatedToStreamID;
}
public byte getPriority() {
return priority;
}
public void setPriority(byte priority) {
if (priority < 0 || priority > 3) {
throw new IllegalArgumentException(
"Priortiy must be between 0 and 3 inclusive: " + priority);
}
this.priority = priority;
}
public boolean isLast() {
return last;
}
public void setLast(boolean last) {
this.last = last;
}
public boolean isUnidirectional() {
return unidirectional;
}
public void setUnidirectional(boolean unidirectional) {
this.unidirectional = unidirectional;
}
@Override
public String toString() {
StringBuilder buf = new StringBuilder();
buf.append(getClass().getSimpleName());
buf.append("(last: ");
buf.append(isLast());
buf.append("; unidirectional: ");
buf.append(isUnidirectional());
buf.append(')');
buf.append(StringUtil.NEWLINE);
buf.append("--> Stream-ID = ");
buf.append(streamID);
buf.append(StringUtil.NEWLINE);
if (associatedToStreamID != 0) {
buf.append("--> Associated-To-Stream-ID = ");
buf.append(associatedToStreamID);
buf.append(StringUtil.NEWLINE);
}
buf.append("--> Priority = ");
buf.append(priority);
buf.append(StringUtil.NEWLINE);
buf.append("--> Headers:");
buf.append(StringUtil.NEWLINE);
appendHeaders(buf);
// Remove the last newline.
buf.setLength(buf.length() - StringUtil.NEWLINE.length());
return buf.toString();
}
}

View File

@ -1,184 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.spdy;
import io.netty.buffer.ChannelBuffer;
final class SpdyCodecUtil {
static final int SPDY_VERSION = 2;
static final int SPDY_HEADER_TYPE_OFFSET = 2;
static final int SPDY_HEADER_FLAGS_OFFSET = 4;
static final int SPDY_HEADER_LENGTH_OFFSET = 5;
static final int SPDY_HEADER_SIZE = 8;
static final int SPDY_MAX_LENGTH = 0xFFFFFF; // Length is a 24-bit field
static final byte SPDY_DATA_FLAG_FIN = 0x01;
static final byte SPDY_DATA_FLAG_COMPRESS = 0x02;
static final int SPDY_SYN_STREAM_FRAME = 1;
static final int SPDY_SYN_REPLY_FRAME = 2;
static final int SPDY_RST_STREAM_FRAME = 3;
static final int SPDY_SETTINGS_FRAME = 4;
static final int SPDY_NOOP_FRAME = 5;
static final int SPDY_PING_FRAME = 6;
static final int SPDY_GOAWAY_FRAME = 7;
static final int SPDY_HEADERS_FRAME = 8;
static final int SPDY_WINDOW_UPDATE_FRAME = 9;
static final byte SPDY_FLAG_FIN = 0x01;
static final byte SPDY_FLAG_UNIDIRECTIONAL = 0x02;
static final byte SPDY_SETTINGS_CLEAR = 0x01;
static final byte SPDY_SETTINGS_PERSIST_VALUE = 0x01;
static final byte SPDY_SETTINGS_PERSISTED = 0x02;
static final int SPDY_SETTINGS_MAX_ID = 0xFFFFFF; // ID is a 24-bit field
static final int SPDY_MAX_NV_LENGTH = 0xFFFF; // Length is a 16-bit field
// Zlib Dictionary
private static final String SPDY_DICT_S =
"optionsgetheadpostputdeletetraceacceptaccept-charsetaccept-encodingaccept-" +
"languageauthorizationexpectfromhostif-modified-sinceif-matchif-none-matchi" +
"f-rangeif-unmodifiedsincemax-forwardsproxy-authorizationrangerefererteuser" +
"-agent10010120020120220320420520630030130230330430530630740040140240340440" +
"5406407408409410411412413414415416417500501502503504505accept-rangesageeta" +
"glocationproxy-authenticatepublicretry-afterservervarywarningwww-authentic" +
"ateallowcontent-basecontent-encodingcache-controlconnectiondatetrailertran" +
"sfer-encodingupgradeviawarningcontent-languagecontent-lengthcontent-locati" +
"oncontent-md5content-rangecontent-typeetagexpireslast-modifiedset-cookieMo" +
"ndayTuesdayWednesdayThursdayFridaySaturdaySundayJanFebMarAprMayJunJulAugSe" +
"pOctNovDecchunkedtext/htmlimage/pngimage/jpgimage/gifapplication/xmlapplic" +
"ation/xhtmltext/plainpublicmax-agecharset=iso-8859-1utf-8gzipdeflateHTTP/1" +
".1statusversionurl ";
static final byte[] SPDY_DICT;
static {
byte[] SPDY_DICT_ = null;
try {
SPDY_DICT_ = SPDY_DICT_S.getBytes("US-ASCII");
// dictionary is null terminated
SPDY_DICT_[SPDY_DICT_.length - 1] = (byte) 0;
} catch (Exception e) {
SPDY_DICT_ = new byte[1];
}
SPDY_DICT = SPDY_DICT_;
}
private SpdyCodecUtil() {
}
/**
* Reads a big-endian unsigned short integer from the buffer.
*/
static int getUnsignedShort(ChannelBuffer buf, int offset) {
return (int) ((buf.getByte(offset) & 0xFF) << 8 |
(buf.getByte(offset + 1) & 0xFF));
}
/**
* Reads a big-endian unsigned medium integer from the buffer.
*/
static int getUnsignedMedium(ChannelBuffer buf, int offset) {
return (int) ((buf.getByte(offset) & 0xFF) << 16 |
(buf.getByte(offset + 1) & 0xFF) << 8 |
(buf.getByte(offset + 2) & 0xFF));
}
/**
* Reads a big-endian (31-bit) integer from the buffer.
*/
static int getUnsignedInt(ChannelBuffer buf, int offset) {
return (int) ((buf.getByte(offset) & 0x7F) << 24 |
(buf.getByte(offset + 1) & 0xFF) << 16 |
(buf.getByte(offset + 2) & 0xFF) << 8 |
(buf.getByte(offset + 3) & 0xFF));
}
/**
* Reads a big-endian signed integer from the buffer.
*/
static int getSignedInt(ChannelBuffer buf, int offset) {
return (int) ((buf.getByte(offset) & 0xFF) << 24 |
(buf.getByte(offset + 1) & 0xFF) << 16 |
(buf.getByte(offset + 2) & 0xFF) << 8 |
(buf.getByte(offset + 3) & 0xFF));
}
/**
* Returns {@code true} if ID is for a server initiated stream or ping.
*/
static boolean isServerID(int ID) {
// Server initiated streams and pings have even IDs
return ID % 2 == 0;
}
/**
* Validate a SPDY header name.
*/
static void validateHeaderName(String name) {
if (name == null) {
throw new NullPointerException("name");
}
if (name.length() == 0) {
throw new IllegalArgumentException(
"name cannot be length zero");
}
// Since name may only contain ascii characters, for valid names
// name.length() returns the number of bytes when UTF-8 encoded.
if (name.length() > SPDY_MAX_NV_LENGTH) {
throw new IllegalArgumentException(
"name exceeds allowable length: " + name);
}
for (int i = 0; i < name.length(); i ++) {
char c = name.charAt(i);
if (c == 0) {
throw new IllegalArgumentException(
"name contains null character: " + name);
}
if (c > 127) {
throw new IllegalArgumentException(
"name contains non-ascii character: " + name);
}
}
}
/**
* Validate a SPDY header value. Does not validate max length.
*/
static void validateHeaderValue(String value) {
if (value == null) {
throw new NullPointerException("value");
}
if (value.length() == 0) {
throw new IllegalArgumentException(
"value cannot be length zero");
}
for (int i = 0; i < value.length(); i ++) {
char c = value.charAt(i);
if (c == 0) {
throw new IllegalArgumentException(
"value contains null character: " + value);
}
}
}
}

View File

@ -1,69 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.spdy;
import io.netty.buffer.ChannelBuffer;
import io.netty.buffer.ChannelBuffers;
/**
* A SPDY Protocol Data Frame
*/
public interface SpdyDataFrame {
/**
* Returns the Stream-ID of this frame.
*/
int getStreamID();
/**
* Sets the Stream-ID of this frame. The Stream-ID must be positive.
*/
void setStreamID(int streamID);
/**
* Returns {@code true} if this frame is the last frame to be transmitted
* on the stream.
*/
boolean isLast();
/**
* Sets if this frame is the last frame to be transmitted on the stream.
*/
void setLast(boolean last);
/**
* Returns {@code true} if the data in this frame has been compressed.
*/
boolean isCompressed();
/**
* Sets if the data in this frame has been compressed.
*/
void setCompressed(boolean compressed);
/**
* Returns the data payload of this frame. If there is no data payload
* {@link ChannelBuffers#EMPTY_BUFFER} is returned.
*/
ChannelBuffer getData();
/**
* Sets the data payload of this frame. If {@code null} is specified,
* the data payload will be set to {@link ChannelBuffers#EMPTY_BUFFER}.
* The data payload cannot exceed 16777215 bytes.
*/
void setData(ChannelBuffer data);
}

View File

@ -1,47 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.spdy;
import io.netty.channel.CombinedChannelHandler;
/**
* A combination of {@link SpdyFrameDecoder} and {@link SpdyFrameEncoder}.
* @apiviz.has io.netty.handler.codec.spdy.SpdyFrameDecoder
* @apiviz.has io.netty.handler.codec.spdy.SpdyFrameEncoder
*/
public class SpdyFrameCodec extends CombinedChannelHandler {
/**
* Creates a new instance with the default decoder and encoder options
* ({@code maxChunkSize (8192)}, {@code maxFrameSize (65536)},
* {@code maxHeaderSize (16384)}, {@code compressionLevel (6)},
* {@code windowBits (15)}, and {@code memLevel (8)}).
*/
public SpdyFrameCodec() {
this(8192, 65536, 16384, 6, 15, 8);
}
/**
* Creates a new instance with the specified decoder and encoder options.
*/
public SpdyFrameCodec(
int maxChunkSize, int maxFrameSize, int maxHeaderSize,
int compressionLevel, int windowBits, int memLevel) {
super(
new SpdyFrameDecoder(maxChunkSize, maxFrameSize, maxHeaderSize),
new SpdyFrameEncoder(compressionLevel, windowBits, memLevel));
}
}

View File

@ -1,395 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.spdy;
import static io.netty.handler.codec.spdy.SpdyCodecUtil.*;
import io.netty.buffer.ChannelBuffer;
import io.netty.buffer.ChannelBuffers;
import io.netty.channel.ChannelInboundHandlerContext;
import io.netty.handler.codec.StreamToMessageDecoder;
/**
* Decodes {@link ChannelBuffer}s into SPDY Data and Control Frames.
*/
public class SpdyFrameDecoder extends StreamToMessageDecoder<Object> {
private final int maxChunkSize;
private final int maxFrameSize;
private final int maxHeaderSize;
private final SpdyHeaderBlockDecompressor headerBlockDecompressor =
SpdyHeaderBlockDecompressor.newInstance();
/**
* Creates a new instance with the default {@code maxChunkSize (8192)},
* {@code maxFrameSize (65536)}, and {@code maxHeaderSize (16384)}.
*/
public SpdyFrameDecoder() {
this(8192, 65536, 16384);
}
/**
* Creates a new instance with the specified parameters.
*/
public SpdyFrameDecoder(
int maxChunkSize, int maxFrameSize, int maxHeaderSize) {
if (maxChunkSize <= 0) {
throw new IllegalArgumentException(
"maxChunkSize must be a positive integer: " + maxChunkSize);
}
if (maxFrameSize <= 0) {
throw new IllegalArgumentException(
"maxFrameSize must be a positive integer: " + maxFrameSize);
}
if (maxHeaderSize <= 0) {
throw new IllegalArgumentException(
"maxHeaderSize must be a positive integer: " + maxHeaderSize);
}
this.maxChunkSize = maxChunkSize;
this.maxFrameSize = maxFrameSize;
this.maxHeaderSize = maxHeaderSize;
}
@Override
public Object decodeLast(ChannelInboundHandlerContext<Byte> ctx,
ChannelBuffer in) throws Exception {
try {
return decode(ctx, in);
} finally {
headerBlockDecompressor.end();
}
}
@Override
public Object decode(ChannelInboundHandlerContext<Byte> ctx,
ChannelBuffer in) throws Exception {
// Must read common header to determine frame length
if (in.readableBytes() < SPDY_HEADER_SIZE) {
return null;
}
// Get frame length from common header
int frameOffset = in.readerIndex();
int lengthOffset = frameOffset + SPDY_HEADER_LENGTH_OFFSET;
int dataLength = getUnsignedMedium(in, lengthOffset);
int frameLength = SPDY_HEADER_SIZE + dataLength;
// Throw exception if frameLength exceeds maxFrameSize
if (frameLength > maxFrameSize) {
throw new SpdyProtocolException(
"Frame length exceeds " + maxFrameSize + ": " + frameLength);
}
// Wait until entire frame is readable
if (in.readableBytes() < frameLength) {
return null;
}
// Read common header fields
boolean control = (in.getByte(frameOffset) & 0x80) != 0;
int flagsOffset = frameOffset + SPDY_HEADER_FLAGS_OFFSET;
byte flags = in.getByte(flagsOffset);
if (control) {
// Decode control frame common header
int version = getUnsignedShort(in, frameOffset) & 0x7FFF;
// Spdy versioning spec is broken
if (version != SPDY_VERSION) {
in.skipBytes(frameLength);
throw new SpdyProtocolException(
"Unsupported version: " + version);
}
int typeOffset = frameOffset + SPDY_HEADER_TYPE_OFFSET;
int type = getUnsignedShort(in, typeOffset);
in.skipBytes(SPDY_HEADER_SIZE);
int readerIndex = in.readerIndex();
in.skipBytes(dataLength);
return decodeControlFrame(type, flags, in.slice(readerIndex, dataLength));
} else {
// Decode data frame common header
int streamID = getUnsignedInt(in, frameOffset);
in.skipBytes(SPDY_HEADER_SIZE);
// Generate data frames that do not exceed maxChunkSize
int numFrames = dataLength / maxChunkSize;
if (dataLength % maxChunkSize != 0) {
numFrames ++;
}
SpdyDataFrame[] frames = new SpdyDataFrame[numFrames];
for (int i = 0; i < numFrames; i++) {
int chunkSize = Math.min(maxChunkSize, dataLength);
SpdyDataFrame spdyDataFrame = new DefaultSpdyDataFrame(streamID);
spdyDataFrame.setCompressed((flags & SPDY_DATA_FLAG_COMPRESS) != 0);
spdyDataFrame.setData(in.readBytes(chunkSize));
dataLength -= chunkSize;
if (dataLength == 0) {
spdyDataFrame.setLast((flags & SPDY_DATA_FLAG_FIN) != 0);
}
frames[i] = spdyDataFrame;
}
return frames;
}
}
private Object decodeControlFrame(int type, byte flags, ChannelBuffer data)
throws Exception {
int streamID;
boolean last;
switch (type) {
case SPDY_SYN_STREAM_FRAME:
if (data.readableBytes() < 12) {
throw new SpdyProtocolException(
"Received invalid SYN_STREAM control frame");
}
streamID = getUnsignedInt(data, data.readerIndex());
int associatedToStreamID = getUnsignedInt(data, data.readerIndex() + 4);
byte priority = (byte) (data.getByte(data.readerIndex() + 8) >> 6 & 0x03);
data.skipBytes(10);
SpdySynStreamFrame spdySynStreamFrame =
new DefaultSpdySynStreamFrame(streamID, associatedToStreamID, priority);
last = (flags & SPDY_FLAG_FIN) != 0;
boolean unid = (flags & SPDY_FLAG_UNIDIRECTIONAL) != 0;
spdySynStreamFrame.setLast(last);
spdySynStreamFrame.setUnidirectional(unid);
decodeHeaderBlock(spdySynStreamFrame, data);
return spdySynStreamFrame;
case SPDY_SYN_REPLY_FRAME:
if (data.readableBytes() < 8) {
throw new SpdyProtocolException(
"Received invalid SYN_REPLY control frame");
}
streamID = getUnsignedInt(data, data.readerIndex());
data.skipBytes(6);
SpdySynReplyFrame spdySynReplyFrame =
new DefaultSpdySynReplyFrame(streamID);
last = (flags & SPDY_FLAG_FIN) != 0;
spdySynReplyFrame.setLast(last);
decodeHeaderBlock(spdySynReplyFrame, data);
return spdySynReplyFrame;
case SPDY_RST_STREAM_FRAME:
if (flags != 0 || data.readableBytes() != 8) {
throw new SpdyProtocolException(
"Received invalid RST_STREAM control frame");
}
streamID = getUnsignedInt(data, data.readerIndex());
int statusCode = getSignedInt(data, data.readerIndex() + 4);
if (statusCode == 0) {
throw new SpdyProtocolException(
"Received invalid RST_STREAM status code");
}
return new DefaultSpdyRstStreamFrame(streamID, statusCode);
case SPDY_SETTINGS_FRAME:
if (data.readableBytes() < 4) {
throw new SpdyProtocolException(
"Received invalid SETTINGS control frame");
}
// Each ID/Value entry is 8 bytes
// The number of entries cannot exceed SPDY_MAX_LENGTH / 8;
int numEntries = getUnsignedInt(data, data.readerIndex());
if (numEntries > (SPDY_MAX_LENGTH - 4) / 8 ||
data.readableBytes() != numEntries * 8 + 4) {
throw new SpdyProtocolException(
"Received invalid SETTINGS control frame");
}
data.skipBytes(4);
SpdySettingsFrame spdySettingsFrame = new DefaultSpdySettingsFrame();
boolean clear = (flags & SPDY_SETTINGS_CLEAR) != 0;
spdySettingsFrame.setClearPreviouslyPersistedSettings(clear);
for (int i = 0; i < numEntries; i ++) {
// Chromium Issue 79156
// SPDY setting ids are not written in network byte order
// Read id assuming the architecture is little endian
int ID = data.readByte() & 0xFF |
(data.readByte() & 0xFF) << 8 |
(data.readByte() & 0xFF) << 16;
byte ID_flags = data.readByte();
int value = getSignedInt(data, data.readerIndex());
data.skipBytes(4);
if (!spdySettingsFrame.isSet(ID)) {
boolean persistVal = (ID_flags & SPDY_SETTINGS_PERSIST_VALUE) != 0;
boolean persisted = (ID_flags & SPDY_SETTINGS_PERSISTED) != 0;
spdySettingsFrame.setValue(ID, value, persistVal, persisted);
}
}
return spdySettingsFrame;
case SPDY_NOOP_FRAME:
if (data.readableBytes() != 0) {
throw new SpdyProtocolException(
"Received invalid NOOP control frame");
}
return null;
case SPDY_PING_FRAME:
if (data.readableBytes() != 4) {
throw new SpdyProtocolException(
"Received invalid PING control frame");
}
int ID = getSignedInt(data, data.readerIndex());
return new DefaultSpdyPingFrame(ID);
case SPDY_GOAWAY_FRAME:
if (data.readableBytes() != 4) {
throw new SpdyProtocolException(
"Received invalid GOAWAY control frame");
}
int lastGoodStreamID = getUnsignedInt(data, data.readerIndex());
return new DefaultSpdyGoAwayFrame(lastGoodStreamID);
case SPDY_HEADERS_FRAME:
// Protocol allows length 4 frame when there are no name/value pairs
if (data.readableBytes() == 4) {
streamID = getUnsignedInt(data, data.readerIndex());
return new DefaultSpdyHeadersFrame(streamID);
}
if (data.readableBytes() < 8) {
throw new SpdyProtocolException(
"Received invalid HEADERS control frame");
}
streamID = getUnsignedInt(data, data.readerIndex());
data.skipBytes(6);
SpdyHeadersFrame spdyHeadersFrame = new DefaultSpdyHeadersFrame(streamID);
decodeHeaderBlock(spdyHeadersFrame, data);
return spdyHeadersFrame;
case SPDY_WINDOW_UPDATE_FRAME:
return null;
default:
return null;
}
}
private boolean ensureBytes(ChannelBuffer decompressed, int bytes) throws Exception {
if (decompressed.readableBytes() >= bytes) {
return true;
}
decompressed.discardReadBytes();
headerBlockDecompressor.decode(decompressed);
return decompressed.readableBytes() >= bytes;
}
private void decodeHeaderBlock(SpdyHeaderBlock headerFrame, ChannelBuffer headerBlock)
throws Exception {
if (headerBlock.readableBytes() == 2 &&
headerBlock.getShort(headerBlock.readerIndex()) == 0) {
return;
}
headerBlockDecompressor.setInput(headerBlock);
ChannelBuffer decompressed = ChannelBuffers.dynamicBuffer(8192);
headerBlockDecompressor.decode(decompressed);
if (decompressed.readableBytes() < 2) {
throw new SpdyProtocolException(
"Received invalid header block");
}
int headerSize = 0;
int numEntries = decompressed.readUnsignedShort();
for (int i = 0; i < numEntries; i ++) {
if (!ensureBytes(decompressed, 2)) {
throw new SpdyProtocolException(
"Received invalid header block");
}
int nameLength = decompressed.readUnsignedShort();
if (nameLength == 0) {
headerFrame.setInvalid();
return;
}
headerSize += nameLength;
if (headerSize > maxHeaderSize) {
throw new SpdyProtocolException(
"Header block exceeds " + maxHeaderSize);
}
if (!ensureBytes(decompressed, nameLength)) {
throw new SpdyProtocolException(
"Received invalid header block");
}
byte[] nameBytes = new byte[nameLength];
decompressed.readBytes(nameBytes);
String name = new String(nameBytes, "UTF-8");
if (headerFrame.containsHeader(name)) {
throw new SpdyProtocolException(
"Received duplicate header name: " + name);
}
if (!ensureBytes(decompressed, 2)) {
throw new SpdyProtocolException(
"Received invalid header block");
}
int valueLength = decompressed.readUnsignedShort();
if (valueLength == 0) {
headerFrame.setInvalid();
return;
}
headerSize += valueLength;
if (headerSize > maxHeaderSize) {
throw new SpdyProtocolException(
"Header block exceeds " + maxHeaderSize);
}
if (!ensureBytes(decompressed, valueLength)) {
throw new SpdyProtocolException(
"Received invalid header block");
}
byte[] valueBytes = new byte[valueLength];
decompressed.readBytes(valueBytes);
int index = 0;
int offset = 0;
while (index < valueLength) {
while (index < valueBytes.length && valueBytes[index] != (byte) 0) {
index ++;
}
if (index < valueBytes.length && valueBytes[index + 1] == (byte) 0) {
// Received multiple, in-sequence NULL characters
headerFrame.setInvalid();
return;
}
String value = new String(valueBytes, offset, index - offset, "UTF-8");
headerFrame.addHeader(name, value);
index ++;
offset = index;
}
}
}
}

View File

@ -1,279 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.spdy;
import static io.netty.handler.codec.spdy.SpdyCodecUtil.*;
import io.netty.buffer.ChannelBuffer;
import io.netty.buffer.ChannelBuffers;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelOutboundHandlerContext;
import io.netty.handler.codec.MessageToStreamEncoder;
import io.netty.handler.codec.UnsupportedMessageTypeException;
import java.nio.ByteOrder;
import java.util.Set;
/**
* Encodes a SPDY Data or Control Frame into a {@link ChannelBuffer}.
*/
public class SpdyFrameEncoder extends MessageToStreamEncoder<Object> {
private volatile boolean finished;
private final SpdyHeaderBlockCompressor headerBlockCompressor;
/**
* Creates a new instance with the default {@code compressionLevel (6)},
* {@code windowBits (15)}, and {@code memLevel (8)}.
*/
public SpdyFrameEncoder() {
this(6, 15, 8);
}
/**
* Creates a new instance with the specified parameters.
*/
public SpdyFrameEncoder(int compressionLevel, int windowBits, int memLevel) {
super();
headerBlockCompressor = SpdyHeaderBlockCompressor.newInstance(compressionLevel, windowBits, memLevel);
}
@Override
public void disconnect(ChannelOutboundHandlerContext<Object> ctx,
ChannelFuture future) throws Exception {
finish();
super.disconnect(ctx, future);
}
@Override
public void close(ChannelOutboundHandlerContext<Object> ctx,
ChannelFuture future) throws Exception {
finish();
super.close(ctx, future);
}
private void finish() {
synchronized (headerBlockCompressor) {
if (!finished) {
finished = true;
headerBlockCompressor.end();
}
}
}
@Override
public boolean isEncodable(Object msg) throws Exception {
// TODO: Introduce a super type.
return msg instanceof SpdyDataFrame ||
msg instanceof SpdySynStreamFrame ||
msg instanceof SpdySynReplyFrame ||
msg instanceof SpdyRstStreamFrame ||
msg instanceof SpdySettingsFrame ||
msg instanceof SpdyNoOpFrame ||
msg instanceof SpdyGoAwayFrame ||
msg instanceof SpdyHeadersFrame ||
msg instanceof SpdyPingFrame;
}
@Override
public void encode(ChannelOutboundHandlerContext<Object> ctx, Object msg,
ChannelBuffer out) throws Exception {
if (msg instanceof SpdyDataFrame) {
SpdyDataFrame spdyDataFrame = (SpdyDataFrame) msg;
ChannelBuffer data = spdyDataFrame.getData();
byte flags = spdyDataFrame.isLast() ? SPDY_DATA_FLAG_FIN : 0;
if (spdyDataFrame.isCompressed()) {
flags |= SPDY_DATA_FLAG_COMPRESS;
}
out.ensureWritableBytes(SPDY_HEADER_SIZE + data.readableBytes());
out.writeInt(spdyDataFrame.getStreamID() & 0x7FFFFFFF);
out.writeByte(flags);
out.writeMedium(data.readableBytes());
out.writeBytes(data, data.readerIndex(), data.readableBytes());
} else if (msg instanceof SpdySynStreamFrame) {
SpdySynStreamFrame spdySynStreamFrame = (SpdySynStreamFrame) msg;
ChannelBuffer data = compressHeaderBlock(
encodeHeaderBlock(spdySynStreamFrame));
byte flags = spdySynStreamFrame.isLast() ? SPDY_FLAG_FIN : 0;
if (spdySynStreamFrame.isUnidirectional()) {
flags |= SPDY_FLAG_UNIDIRECTIONAL;
}
int headerBlockLength = data.readableBytes();
int length = headerBlockLength == 0 ? 12 : 10 + headerBlockLength;
out.ensureWritableBytes(SPDY_HEADER_SIZE + length + data.readableBytes());
out.writeShort(SPDY_VERSION | 0x8000);
out.writeShort(SPDY_SYN_STREAM_FRAME);
out.writeByte(flags);
out.writeMedium(length);
out.writeInt(spdySynStreamFrame.getStreamID());
out.writeInt(spdySynStreamFrame.getAssociatedToStreamID());
out.writeShort(spdySynStreamFrame.getPriority() << 14);
if (data.readableBytes() == 0) {
out.writeShort(0);
} else {
out.writeBytes(data, data.readerIndex(), data.readableBytes());
}
} else if (msg instanceof SpdySynReplyFrame) {
SpdySynReplyFrame spdySynReplyFrame = (SpdySynReplyFrame) msg;
ChannelBuffer data = compressHeaderBlock(
encodeHeaderBlock(spdySynReplyFrame));
byte flags = spdySynReplyFrame.isLast() ? SPDY_FLAG_FIN : 0;
int headerBlockLength = data.readableBytes();
int length = headerBlockLength == 0 ? 8 : 6 + headerBlockLength;
out.ensureWritableBytes(SPDY_HEADER_SIZE + length + data.readableBytes());
out.writeShort(SPDY_VERSION | 0x8000);
out.writeShort(SPDY_SYN_REPLY_FRAME);
out.writeByte(flags);
out.writeMedium(length);
out.writeInt(spdySynReplyFrame.getStreamID());
if (data.readableBytes() == 0) {
out.writeInt(0);
} else {
out.writeShort(0);
out.writeBytes(data, data.readerIndex(), data.readableBytes());
}
} else if (msg instanceof SpdyRstStreamFrame) {
SpdyRstStreamFrame spdyRstStreamFrame = (SpdyRstStreamFrame) msg;
out.ensureWritableBytes(SPDY_HEADER_SIZE + 8);
out.writeShort(SPDY_VERSION | 0x8000);
out.writeShort(SPDY_RST_STREAM_FRAME);
out.writeInt(8);
out.writeInt(spdyRstStreamFrame.getStreamID());
out.writeInt(spdyRstStreamFrame.getStatus().getCode());
} else if (msg instanceof SpdySettingsFrame) {
SpdySettingsFrame spdySettingsFrame = (SpdySettingsFrame) msg;
byte flags = spdySettingsFrame.clearPreviouslyPersistedSettings() ?
SPDY_SETTINGS_CLEAR : 0;
Set<Integer> IDs = spdySettingsFrame.getIDs();
int numEntries = IDs.size();
int length = 4 + numEntries * 8;
out.ensureWritableBytes(SPDY_HEADER_SIZE + length);
out.writeShort(SPDY_VERSION | 0x8000);
out.writeShort(SPDY_SETTINGS_FRAME);
out.writeByte(flags);
out.writeMedium(length);
out.writeInt(numEntries);
for (Integer ID: IDs) {
int id = ID.intValue();
byte ID_flags = (byte) 0;
if (spdySettingsFrame.persistValue(id)) {
ID_flags |= SPDY_SETTINGS_PERSIST_VALUE;
}
if (spdySettingsFrame.isPersisted(id)) {
ID_flags |= SPDY_SETTINGS_PERSISTED;
}
// Chromium Issue 79156
// SPDY setting ids are not written in network byte order
// Write id assuming the architecture is little endian
out.writeByte(id >> 0 & 0xFF);
out.writeByte(id >> 8 & 0xFF);
out.writeByte(id >> 16 & 0xFF);
out.writeByte(ID_flags);
out.writeInt(spdySettingsFrame.getValue(id));
}
} else if (msg instanceof SpdyNoOpFrame) {
out.ensureWritableBytes(SPDY_HEADER_SIZE);
out.writeShort(SPDY_VERSION | 0x8000);
out.writeShort(SPDY_NOOP_FRAME);
out.writeInt(0);
} else if (msg instanceof SpdyPingFrame) {
SpdyPingFrame spdyPingFrame = (SpdyPingFrame) msg;
out.ensureWritableBytes(SPDY_HEADER_SIZE + 4);
out.writeShort(SPDY_VERSION | 0x8000);
out.writeShort(SPDY_PING_FRAME);
out.writeInt(4);
out.writeInt(spdyPingFrame.getID());
} else if (msg instanceof SpdyGoAwayFrame) {
SpdyGoAwayFrame spdyGoAwayFrame = (SpdyGoAwayFrame) msg;
out.ensureWritableBytes(SPDY_HEADER_SIZE + 4);
out.writeShort(SPDY_VERSION | 0x8000);
out.writeShort(SPDY_GOAWAY_FRAME);
out.writeInt(4);
out.writeInt(spdyGoAwayFrame.getLastGoodStreamID());
} else if (msg instanceof SpdyHeadersFrame) {
SpdyHeadersFrame spdyHeadersFrame = (SpdyHeadersFrame) msg;
ChannelBuffer data = compressHeaderBlock(
encodeHeaderBlock(spdyHeadersFrame));
int headerBlockLength = data.readableBytes();
int length = headerBlockLength == 0 ? 4 : 6 + headerBlockLength;
out.ensureWritableBytes(SPDY_HEADER_SIZE + length + data.readableBytes());
out.writeShort(SPDY_VERSION | 0x8000);
out.writeShort(SPDY_HEADERS_FRAME);
out.writeInt(length);
out.writeInt(spdyHeadersFrame.getStreamID());
if (data.readableBytes() != 0) {
out.writeShort(0);
out.writeBytes(data, data.readerIndex(), data.readableBytes());
}
} else {
// Unknown message type
throw new UnsupportedMessageTypeException(msg);
}
}
private static ChannelBuffer encodeHeaderBlock(SpdyHeaderBlock headerFrame)
throws Exception {
Set<String> names = headerFrame.getHeaderNames();
int numHeaders = names.size();
if (numHeaders == 0) {
return ChannelBuffers.EMPTY_BUFFER;
}
if (numHeaders > SPDY_MAX_NV_LENGTH) {
throw new IllegalArgumentException(
"header block contains too many headers");
}
ChannelBuffer headerBlock = ChannelBuffers.dynamicBuffer(
ByteOrder.BIG_ENDIAN, 256);
headerBlock.writeShort(numHeaders);
for (String name: names) {
byte[] nameBytes = name.getBytes("UTF-8");
headerBlock.writeShort(nameBytes.length);
headerBlock.writeBytes(nameBytes);
int savedIndex = headerBlock.writerIndex();
int valueLength = 0;
headerBlock.writeShort(valueLength);
for (String value: headerFrame.getHeaders(name)) {
byte[] valueBytes = value.getBytes("UTF-8");
headerBlock.writeBytes(valueBytes);
headerBlock.writeByte(0);
valueLength += valueBytes.length + 1;
}
valueLength --;
if (valueLength > SPDY_MAX_NV_LENGTH) {
throw new IllegalArgumentException(
"header exceeds allowable length: " + name);
}
headerBlock.setShort(savedIndex, valueLength);
headerBlock.writerIndex(headerBlock.writerIndex() - 1);
}
return headerBlock;
}
private synchronized ChannelBuffer compressHeaderBlock(
ChannelBuffer uncompressed) throws Exception {
if (uncompressed.readableBytes() == 0) {
return ChannelBuffers.EMPTY_BUFFER;
}
ChannelBuffer compressed = ChannelBuffers.dynamicBuffer();
synchronized (headerBlockCompressor) {
if (!finished) {
headerBlockCompressor.setInput(uncompressed);
headerBlockCompressor.encode(compressed);
}
}
return compressed;
}
}

View File

@ -1,33 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.spdy;
/**
* A SPDY Protocol GOAWAY Control Frame
*/
public interface SpdyGoAwayFrame {
/**
* Returns the Last-good-stream-ID of this frame.
*/
int getLastGoodStreamID();
/**
* Sets the Last-good-stream-ID of this frame. The Last-good-stream-ID
* cannot be negative.
*/
void setLastGoodStreamID(int lastGoodStreamID);
}

View File

@ -1,103 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.spdy;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* A SPDY Name/Value Header Block which provides common properties for
* {@link SpdySynStreamFrame}, {@link SpdySynReplyFrame}, and
* {@link SpdyHeadersFrame}.
* @see SpdyHeaders
*/
public interface SpdyHeaderBlock {
/**
* Returns {@code true} if this header block is invalid.
* A RST_STREAM frame with code PROTOCOL_ERROR should be sent.
*/
boolean isInvalid();
/**
* Marks this header block as invalid.
*/
void setInvalid();
/**
* Returns the header value with the specified header name. If there is
* more than one header value for the specified header name, the first
* value is returned.
*
* @return the header value or {@code null} if there is no such header
*/
String getHeader(String name);
/**
* Returns the header values with the specified header name.
*
* @return the {@link List} of header values. An empty list if there is no
* such header.
*/
List<String> getHeaders(String name);
/**
* Returns all header names and values that this block contains.
*
* @return the {@link List} of the header name-value pairs. An empty list
* if there is no header in this message.
*/
List<Map.Entry<String, String>> getHeaders();
/**
* Returns {@code true} if and only if there is a header with the specified
* header name.
*/
boolean containsHeader(String name);
/**
* Returns the {@link Set} of all header names that this block contains.
*/
Set<String> getHeaderNames();
/**
* Adds a new header with the specified name and value.
*/
void addHeader(String name, Object value);
/**
* Sets a new header with the specified name and value. If there is an
* existing header with the same name, the existing header is removed.
*/
void setHeader(String name, Object value);
/**
* Sets a new header with the specified name and values. If there is an
* existing header with the same name, the existing header is removed.
*/
void setHeader(String name, Iterable<?> values);
/**
* Removes the header with the specified name.
*/
void removeHeader(String name);
/**
* Removes all headers from this block.
*/
void clearHeaders();
}

View File

@ -1,37 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.spdy;
import io.netty.buffer.ChannelBuffer;
import io.netty.util.internal.DetectionUtil;
abstract class SpdyHeaderBlockCompressor {
static SpdyHeaderBlockCompressor newInstance(
int compressionLevel, int windowBits, int memLevel) {
if (DetectionUtil.javaVersion() >= 7) {
return new SpdyHeaderBlockZlibCompressor(compressionLevel);
} else {
return new SpdyHeaderBlockJZlibCompressor(
compressionLevel, windowBits, memLevel);
}
}
abstract void setInput(ChannelBuffer decompressed);
abstract void encode(ChannelBuffer compressed);
abstract void end();
}

View File

@ -1,29 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.spdy;
import io.netty.buffer.ChannelBuffer;
abstract class SpdyHeaderBlockDecompressor {
static SpdyHeaderBlockDecompressor newInstance() {
return new SpdyHeaderBlockZlibDecompressor();
}
abstract void setInput(ChannelBuffer compressed);
abstract void decode(ChannelBuffer decompressed) throws Exception;
abstract void end();
}

View File

@ -1,98 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.spdy;
import static io.netty.handler.codec.spdy.SpdyCodecUtil.*;
import io.netty.buffer.ChannelBuffer;
import io.netty.handler.codec.compression.CompressionException;
import io.netty.util.internal.jzlib.JZlib;
import io.netty.util.internal.jzlib.ZStream;
class SpdyHeaderBlockJZlibCompressor extends SpdyHeaderBlockCompressor {
private final ZStream z = new ZStream();
public SpdyHeaderBlockJZlibCompressor(int compressionLevel, int windowBits, int memLevel) {
if (compressionLevel < 0 || compressionLevel > 9) {
throw new IllegalArgumentException(
"compressionLevel: " + compressionLevel + " (expected: 0-9)");
}
if (windowBits < 9 || windowBits > 15) {
throw new IllegalArgumentException(
"windowBits: " + windowBits + " (expected: 9-15)");
}
if (memLevel < 1 || memLevel > 9) {
throw new IllegalArgumentException(
"memLevel: " + memLevel + " (expected: 1-9)");
}
int resultCode = z.deflateInit(
compressionLevel, windowBits, memLevel, JZlib.W_ZLIB);
if (resultCode != JZlib.Z_OK) {
throw new CompressionException(
"failed to initialize an SPDY header block deflater: " + resultCode);
} else {
resultCode = z.deflateSetDictionary(SPDY_DICT, SPDY_DICT.length);
if (resultCode != JZlib.Z_OK) {
throw new CompressionException(
"failed to set the SPDY dictionary: " + resultCode);
}
}
}
@Override
public void setInput(ChannelBuffer decompressed) {
byte[] in = new byte[decompressed.readableBytes()];
decompressed.readBytes(in);
z.next_in = in;
z.next_in_index = 0;
z.avail_in = in.length;
}
@Override
public void encode(ChannelBuffer compressed) {
try {
byte[] out = new byte[(int) Math.ceil(z.next_in.length * 1.001) + 12];
z.next_out = out;
z.next_out_index = 0;
z.avail_out = out.length;
int resultCode = z.deflate(JZlib.Z_SYNC_FLUSH);
if (resultCode != JZlib.Z_OK) {
throw new CompressionException("compression failure: " + resultCode);
}
if (z.next_out_index != 0) {
compressed.writeBytes(out, 0, z.next_out_index);
}
} finally {
// Deference the external references explicitly to tell the VM that
// the allocated byte arrays are temporary so that the call stack
// can be utilized.
// I'm not sure if the modern VMs do this optimization though.
z.next_in = null;
z.next_out = null;
}
}
@Override
public void end() {
z.deflateEnd();
z.next_in = null;
z.next_out = null;
}
}

View File

@ -1,57 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.spdy;
import java.util.zip.Deflater;
import io.netty.buffer.ChannelBuffer;
import static io.netty.handler.codec.spdy.SpdyCodecUtil.*;
class SpdyHeaderBlockZlibCompressor extends SpdyHeaderBlockCompressor {
private final byte[] out = new byte[8192];
private final Deflater compressor;
public SpdyHeaderBlockZlibCompressor(int compressionLevel) {
if (compressionLevel < 0 || compressionLevel > 9) {
throw new IllegalArgumentException(
"compressionLevel: " + compressionLevel + " (expected: 0-9)");
}
compressor = new Deflater(compressionLevel);
compressor.setDictionary(SPDY_DICT);
}
@Override
public void setInput(ChannelBuffer decompressed) {
byte[] in = new byte[decompressed.readableBytes()];
decompressed.readBytes(in);
compressor.setInput(in);
}
@Override
public void encode(ChannelBuffer compressed) {
while (!compressor.needsInput()) {
int numBytes = compressor.deflate(out, 0, out.length, Deflater.SYNC_FLUSH);
compressed.writeBytes(out, 0, numBytes);
}
}
@Override
public void end() {
compressor.end();
}
}

View File

@ -1,56 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.spdy;
import java.util.zip.DataFormatException;
import java.util.zip.Inflater;
import io.netty.buffer.ChannelBuffer;
import static io.netty.handler.codec.spdy.SpdyCodecUtil.*;
class SpdyHeaderBlockZlibDecompressor extends SpdyHeaderBlockDecompressor {
private final byte[] out = new byte[8192];
private final Inflater decompressor = new Inflater();
@Override
public void setInput(ChannelBuffer compressed) {
byte[] in = new byte[compressed.readableBytes()];
compressed.readBytes(in);
decompressor.setInput(in);
}
@Override
public void decode(ChannelBuffer decompressed) throws Exception {
try {
int numBytes = decompressor.inflate(out);
if (numBytes == 0 && decompressor.needsDictionary()) {
decompressor.setDictionary(SPDY_DICT);
numBytes = decompressor.inflate(out);
}
decompressed.writeBytes(out, 0, numBytes);
} catch (DataFormatException e) {
throw new SpdyProtocolException(
"Received invalid header block", e);
}
}
@Override
public void end() {
decompressor.end();
}
}

View File

@ -1,530 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.spdy;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
/**
* Provides the constants for the standard SPDY HTTP header names and commonly
* used utility methods that access an {@link SpdyHeaderBlock}.
* @apiviz.stereotype static
*/
public class SpdyHeaders {
/**
* SPDY HTTP header names
* @apiviz.stereotype static
*/
public static final class HttpNames {
/**
* {@code "method"}
*/
public static final String METHOD = "method";
/**
* {@code "scheme"}
*/
public static final String SCHEME = "scheme";
/**
* {@code "status"}
*/
public static final String STATUS = "status";
/**
* {@code "url"}
*/
public static final String URL = "url";
/**
* {@code "version"}
*/
public static final String VERSION = "version";
private HttpNames() {
super();
}
}
/**
* Returns the header value with the specified header name. If there are
* more than one header value for the specified header name, the first
* value is returned.
*
* @return the header value or {@code null} if there is no such header
*/
public static String getHeader(SpdyHeaderBlock block, String name) {
return block.getHeader(name);
}
/**
* Returns the header value with the specified header name. If there are
* more than one header value for the specified header name, the first
* value is returned.
*
* @return the header value or the {@code defaultValue} if there is no such
* header
*/
public static String getHeader(SpdyHeaderBlock block, String name, String defaultValue) {
String value = block.getHeader(name);
if (value == null) {
return defaultValue;
}
return value;
}
/**
* Sets a new header with the specified name and value. If there is an
* existing header with the same name, the existing header is removed.
*/
public static void setHeader(SpdyHeaderBlock block, String name, Object value) {
block.setHeader(name, value);
}
/**
* Sets a new header with the specified name and values. If there is an
* existing header with the same name, the existing header is removed.
*/
public static void setHeader(SpdyHeaderBlock block, String name, Iterable<?> values) {
block.setHeader(name, values);
}
/**
* Adds a new header with the specified name and value.
*/
public static void addHeader(SpdyHeaderBlock block, String name, Object value) {
block.addHeader(name, value);
}
/**
* Removes the {@code "method"} header.
*/
public static void removeMethod(SpdyHeaderBlock block) {
block.removeHeader(HttpNames.METHOD);
}
/**
* Returns the {@link HttpMethod} represented by the {@code "method"} header.
*/
public static HttpMethod getMethod(SpdyHeaderBlock block) {
try {
return HttpMethod.valueOf(block.getHeader(HttpNames.METHOD));
} catch (Exception e) {
return null;
}
}
/**
* Sets the {@code "method"} header.
*/
public static void setMethod(SpdyHeaderBlock block, HttpMethod method) {
block.setHeader(HttpNames.METHOD, method.getName());
}
/**
* Removes the {@code "scheme"} header.
*/
public static void removeScheme(SpdyHeaderBlock block) {
block.removeHeader(HttpNames.SCHEME);
}
/**
* Returns the value of the {@code "scheme"} header.
*/
public static String getScheme(SpdyHeaderBlock block) {
return block.getHeader(HttpNames.SCHEME);
}
/**
* Sets the {@code "scheme"} header.
*/
public static void setScheme(SpdyHeaderBlock block, String value) {
block.setHeader(HttpNames.SCHEME, value);
}
/**
* Removes the {@code "status"} header.
*/
public static void removeStatus(SpdyHeaderBlock block) {
block.removeHeader(HttpNames.STATUS);
}
/**
* Returns the {@link HttpResponseStatus} represented by the {@code "status"} header.
*/
public static HttpResponseStatus getStatus(SpdyHeaderBlock block) {
try {
String status = block.getHeader(HttpNames.STATUS);
int space = status.indexOf(' ');
if (space == -1) {
return HttpResponseStatus.valueOf(Integer.parseInt(status));
} else {
int code = Integer.parseInt(status.substring(0, space));
String reasonPhrase = status.substring(space + 1);
HttpResponseStatus responseStatus = HttpResponseStatus.valueOf(code);
if (responseStatus.getReasonPhrase().equals(responseStatus)) {
return responseStatus;
} else {
return new HttpResponseStatus(code, reasonPhrase);
}
}
} catch (Exception e) {
return null;
}
}
/**
* Sets the {@code "status"} header.
*/
public static void setStatus(SpdyHeaderBlock block, HttpResponseStatus status) {
block.setHeader(HttpNames.STATUS, status.toString());
}
/**
* Removes the {@code "url"} header.
*/
public static void removeUrl(SpdyHeaderBlock block) {
block.removeHeader(HttpNames.URL);
}
/**
* Returns the value of the {@code "url"} header.
*/
public static String getUrl(SpdyHeaderBlock block) {
return block.getHeader(HttpNames.URL);
}
/**
* Sets the {@code "url"} header.
*/
public static void setUrl(SpdyHeaderBlock block, String value) {
block.setHeader(HttpNames.URL, value);
}
/**
* Removes the {@code "version"} header.
*/
public static void removeVersion(SpdyHeaderBlock block) {
block.removeHeader(HttpNames.VERSION);
}
/**
* Returns the {@link HttpVersion} represented by the {@code "version"} header.
*/
public static HttpVersion getVersion(SpdyHeaderBlock block) {
try {
return HttpVersion.valueOf(block.getHeader(HttpNames.VERSION));
} catch (Exception e) {
return null;
}
}
/**
* Sets the {@code "version"} header.
*/
public static void setVersion(SpdyHeaderBlock block, HttpVersion version) {
block.setHeader(HttpNames.VERSION, version.getText());
}
private static final int BUCKET_SIZE = 17;
private static int hash(String name) {
int h = 0;
for (int i = name.length() - 1; i >= 0; i --) {
char c = name.charAt(i);
if (c >= 'A' && c <= 'Z') {
c += 32;
}
h = 31 * h + c;
}
if (h > 0) {
return h;
} else if (h == Integer.MIN_VALUE) {
return Integer.MAX_VALUE;
} else {
return -h;
}
}
private static boolean eq(String name1, String name2) {
int nameLen = name1.length();
if (nameLen != name2.length()) {
return false;
}
for (int i = nameLen - 1; i >= 0; i --) {
char c1 = name1.charAt(i);
char c2 = name2.charAt(i);
if (c1 != c2) {
if (c1 >= 'A' && c1 <= 'Z') {
c1 += 32;
}
if (c2 >= 'A' && c2 <= 'Z') {
c2 += 32;
}
if (c1 != c2) {
return false;
}
}
}
return true;
}
private static int index(int hash) {
return hash % BUCKET_SIZE;
}
private final Entry[] entries = new Entry[BUCKET_SIZE];
private final Entry head = new Entry(-1, null, null);
SpdyHeaders() {
head.before = head.after = head;
}
void addHeader(final String name, final Object value) {
String lowerCaseName = name.toLowerCase();
SpdyCodecUtil.validateHeaderName(lowerCaseName);
String strVal = toString(value);
SpdyCodecUtil.validateHeaderValue(strVal);
int h = hash(lowerCaseName);
int i = index(h);
addHeader0(h, i, lowerCaseName, strVal);
}
private void addHeader0(int h, int i, final String name, final String value) {
// Update the hash table.
Entry e = entries[i];
Entry newEntry;
entries[i] = newEntry = new Entry(h, name, value);
newEntry.next = e;
// Update the linked list.
newEntry.addBefore(head);
}
void removeHeader(final String name) {
if (name == null) {
throw new NullPointerException("name");
}
String lowerCaseName = name.toLowerCase();
int h = hash(lowerCaseName);
int i = index(h);
removeHeader0(h, i, lowerCaseName);
}
private void removeHeader0(int h, int i, String name) {
Entry e = entries[i];
if (e == null) {
return;
}
for (;;) {
if (e.hash == h && eq(name, e.key)) {
e.remove();
Entry next = e.next;
if (next != null) {
entries[i] = next;
e = next;
} else {
entries[i] = null;
return;
}
} else {
break;
}
}
for (;;) {
Entry next = e.next;
if (next == null) {
break;
}
if (next.hash == h && eq(name, next.key)) {
e.next = next.next;
next.remove();
} else {
e = next;
}
}
}
void setHeader(final String name, final Object value) {
String lowerCaseName = name.toLowerCase();
SpdyCodecUtil.validateHeaderName(lowerCaseName);
String strVal = toString(value);
SpdyCodecUtil.validateHeaderValue(strVal);
int h = hash(lowerCaseName);
int i = index(h);
removeHeader0(h, i, lowerCaseName);
addHeader0(h, i, lowerCaseName, strVal);
}
void setHeader(final String name, final Iterable<?> values) {
if (values == null) {
throw new NullPointerException("values");
}
String lowerCaseName = name.toLowerCase();
SpdyCodecUtil.validateHeaderName(lowerCaseName);
int h = hash(lowerCaseName);
int i = index(h);
removeHeader0(h, i, lowerCaseName);
for (Object v: values) {
if (v == null) {
break;
}
String strVal = toString(v);
SpdyCodecUtil.validateHeaderValue(strVal);
addHeader0(h, i, lowerCaseName, strVal);
}
}
void clearHeaders() {
for (int i = 0; i < entries.length; i ++) {
entries[i] = null;
}
head.before = head.after = head;
}
String getHeader(final String name) {
if (name == null) {
throw new NullPointerException("name");
}
int h = hash(name);
int i = index(h);
Entry e = entries[i];
while (e != null) {
if (e.hash == h && eq(name, e.key)) {
return e.value;
}
e = e.next;
}
return null;
}
List<String> getHeaders(final String name) {
if (name == null) {
throw new NullPointerException("name");
}
LinkedList<String> values = new LinkedList<String>();
int h = hash(name);
int i = index(h);
Entry e = entries[i];
while (e != null) {
if (e.hash == h && eq(name, e.key)) {
values.addFirst(e.value);
}
e = e.next;
}
return values;
}
List<Map.Entry<String, String>> getHeaders() {
List<Map.Entry<String, String>> all =
new LinkedList<Map.Entry<String, String>>();
Entry e = head.after;
while (e != head) {
all.add(e);
e = e.after;
}
return all;
}
boolean containsHeader(String name) {
return getHeader(name) != null;
}
Set<String> getHeaderNames() {
Set<String> names = new TreeSet<String>();
Entry e = head.after;
while (e != head) {
names.add(e.key);
e = e.after;
}
return names;
}
private static String toString(Object value) {
if (value == null) {
return null;
}
return value.toString();
}
private static final class Entry implements Map.Entry<String, String> {
final int hash;
final String key;
String value;
Entry next;
Entry before, after;
Entry(int hash, String key, String value) {
this.hash = hash;
this.key = key;
this.value = value;
}
void remove() {
before.after = after;
after.before = before;
}
void addBefore(Entry e) {
after = e;
before = e.before;
before.after = this;
after.before = this;
}
public String getKey() {
return key;
}
public String getValue() {
return value;
}
public String setValue(String value) {
if (value == null) {
throw new NullPointerException("value");
}
SpdyCodecUtil.validateHeaderValue(value);
String oldValue = this.value;
this.value = value;
return oldValue;
}
@Override
public String toString() {
return key + "=" + value;
}
}
}

View File

@ -1,32 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.spdy;
/**
* A SPDY Protocol HEADERS Control Frame
*/
public interface SpdyHeadersFrame extends SpdyHeaderBlock {
/**
* Returns the Stream-ID of this frame.
*/
int getStreamID();
/**
* Sets the Stream-ID of this frame. The Stream-ID must be positive.
*/
void setStreamID(int streamID);
}

View File

@ -1,33 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.spdy;
import io.netty.channel.CombinedChannelHandler;
/**
* A combination of {@link SpdyHttpDecoder} and {@link SpdyHttpEncoder}
* @apiviz.has io.netty.handler.codec.sdpy.SpdyHttpDecoder
* @apiviz.has io.netty.handler.codec.spdy.SpdyHttpEncoder
*/
public class SpdyHttpCodec extends CombinedChannelHandler {
/**
* Creates a new instance with the specified decoder options.
*/
public SpdyHttpCodec(int maxContentLength) {
super(new SpdyHttpDecoder(maxContentLength), new SpdyHttpEncoder());
}
}

View File

@ -1,288 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.spdy;
import io.netty.buffer.ChannelBuffer;
import io.netty.buffer.ChannelBuffers;
import io.netty.channel.ChannelInboundHandlerContext;
import io.netty.handler.codec.MessageToMessageDecoder;
import io.netty.handler.codec.TooLongFrameException;
import io.netty.handler.codec.http.DefaultHttpRequest;
import io.netty.handler.codec.http.DefaultHttpResponse;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMessage;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Decodes {@link SpdySynStreamFrame}s, {@link SpdySynReplyFrame}s,
* and {@link SpdyDataFrame}s into {@link HttpRequest}s and {@link HttpResponse}s.
*/
public class SpdyHttpDecoder extends MessageToMessageDecoder<Object, Object> {
private final int maxContentLength;
private final Map<Integer, HttpMessage> messageMap = new HashMap<Integer, HttpMessage>();
/**
* Creates a new instance.
*
* @param maxContentLength the maximum length of the message content.
* If the length of the message content exceeds this value,
* a {@link TooLongFrameException} will be raised.
*/
public SpdyHttpDecoder(int maxContentLength) {
super();
if (maxContentLength <= 0) {
throw new IllegalArgumentException(
"maxContentLength must be a positive integer: " + maxContentLength);
}
this.maxContentLength = maxContentLength;
}
@Override
public boolean isDecodable(Object msg) throws Exception {
return msg instanceof SpdySynStreamFrame ||
msg instanceof SpdySynReplyFrame ||
msg instanceof SpdyHeadersFrame ||
msg instanceof SpdyDataFrame;
}
@Override
public Object decode(ChannelInboundHandlerContext<Object> ctx, Object msg)
throws Exception {
if (msg instanceof SpdySynStreamFrame) {
// HTTP requests/responses are mapped one-to-one to SPDY streams.
SpdySynStreamFrame spdySynStreamFrame = (SpdySynStreamFrame) msg;
int streamID = spdySynStreamFrame.getStreamID();
if (SpdyCodecUtil.isServerID(streamID)) {
// SYN_STREAM frames inititated by the server are pushed resources
int associatedToStreamID = spdySynStreamFrame.getAssociatedToStreamID();
// If a client receives a SYN_STREAM with an Associated-To-Stream-ID of 0
// it must reply with a RST_STREAM with error code INVALID_STREAM
if (associatedToStreamID == 0) {
SpdyRstStreamFrame spdyRstStreamFrame =
new DefaultSpdyRstStreamFrame(streamID, SpdyStreamStatus.INVALID_STREAM);
ctx.write(spdyRstStreamFrame);
}
String URL = SpdyHeaders.getUrl(spdySynStreamFrame);
// If a client receives a SYN_STREAM without a 'url' header
// it must reply with a RST_STREAM with error code PROTOCOL_ERROR
if (URL == null) {
SpdyRstStreamFrame spdyRstStreamFrame =
new DefaultSpdyRstStreamFrame(streamID, SpdyStreamStatus.PROTOCOL_ERROR);
ctx.write(spdyRstStreamFrame);
}
try {
HttpResponse httpResponse = createHttpResponse(spdySynStreamFrame);
// Set the Stream-ID, Associated-To-Stream-ID, Priority, and URL as headers
SpdyHttpHeaders.setStreamID(httpResponse, streamID);
SpdyHttpHeaders.setAssociatedToStreamID(httpResponse, associatedToStreamID);
SpdyHttpHeaders.setPriority(httpResponse, spdySynStreamFrame.getPriority());
SpdyHttpHeaders.setUrl(httpResponse, URL);
if (spdySynStreamFrame.isLast()) {
HttpHeaders.setContentLength(httpResponse, 0);
return httpResponse;
} else {
// Response body will follow in a series of Data Frames
messageMap.put(new Integer(streamID), httpResponse);
}
} catch (Exception e) {
SpdyRstStreamFrame spdyRstStreamFrame =
new DefaultSpdyRstStreamFrame(streamID, SpdyStreamStatus.PROTOCOL_ERROR);
ctx.write(spdyRstStreamFrame);
}
} else {
// SYN_STREAM frames initiated by the client are HTTP requests
try {
HttpRequest httpRequest = createHttpRequest(spdySynStreamFrame);
// Set the Stream-ID as a header
SpdyHttpHeaders.setStreamID(httpRequest, streamID);
if (spdySynStreamFrame.isLast()) {
return httpRequest;
} else {
// Request body will follow in a series of Data Frames
messageMap.put(new Integer(streamID), httpRequest);
}
} catch (Exception e) {
// If a client sends a SYN_STREAM without method, url, and version headers
// the server must reply with a HTTP 400 BAD REQUEST reply
// Also sends HTTP 400 BAD REQUEST reply if header name/value pairs are invalid
SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamID);
spdySynReplyFrame.setLast(true);
SpdyHeaders.setStatus(spdySynReplyFrame, HttpResponseStatus.BAD_REQUEST);
SpdyHeaders.setVersion(spdySynReplyFrame, HttpVersion.HTTP_1_0);
ctx.write(spdySynReplyFrame);
}
}
} else if (msg instanceof SpdySynReplyFrame) {
SpdySynReplyFrame spdySynReplyFrame = (SpdySynReplyFrame) msg;
int streamID = spdySynReplyFrame.getStreamID();
try {
HttpResponse httpResponse = createHttpResponse(spdySynReplyFrame);
// Set the Stream-ID as a header
SpdyHttpHeaders.setStreamID(httpResponse, streamID);
if (spdySynReplyFrame.isLast()) {
HttpHeaders.setContentLength(httpResponse, 0);
return httpResponse;
} else {
// Response body will follow in a series of Data Frames
messageMap.put(new Integer(streamID), httpResponse);
}
} catch (Exception e) {
// If a client receives a SYN_REPLY without valid status and version headers
// the client must reply with a RST_STREAM frame indicating a PROTOCOL_ERROR
SpdyRstStreamFrame spdyRstStreamFrame =
new DefaultSpdyRstStreamFrame(streamID, SpdyStreamStatus.PROTOCOL_ERROR);
ctx.write(spdyRstStreamFrame);
}
} else if (msg instanceof SpdyHeadersFrame) {
SpdyHeadersFrame spdyHeadersFrame = (SpdyHeadersFrame) msg;
Integer streamID = new Integer(spdyHeadersFrame.getStreamID());
HttpMessage httpMessage = messageMap.get(streamID);
// If message is not in map discard HEADERS frame.
// SpdySessionHandler should prevent this from happening.
if (httpMessage == null) {
return null;
}
for (Map.Entry<String, String> e: spdyHeadersFrame.getHeaders()) {
httpMessage.addHeader(e.getKey(), e.getValue());
}
} else if (msg instanceof SpdyDataFrame) {
SpdyDataFrame spdyDataFrame = (SpdyDataFrame) msg;
Integer streamID = new Integer(spdyDataFrame.getStreamID());
HttpMessage httpMessage = messageMap.get(streamID);
// If message is not in map discard Data Frame.
// SpdySessionHandler should prevent this from happening.
if (httpMessage == null) {
return null;
}
ChannelBuffer content = httpMessage.getContent();
if (content.readableBytes() > maxContentLength - spdyDataFrame.getData().readableBytes()) {
messageMap.remove(streamID);
throw new TooLongFrameException(
"HTTP content length exceeded " + maxContentLength + " bytes.");
}
if (content == ChannelBuffers.EMPTY_BUFFER) {
ChannelBuffer data = spdyDataFrame.getData();
content = ChannelBuffers.dynamicBuffer(data.readableBytes());
content.writeBytes(data, data.readerIndex(), data.readableBytes());
httpMessage.setContent(content);
} else {
content.writeBytes(spdyDataFrame.getData());
}
if (spdyDataFrame.isLast()) {
HttpHeaders.setContentLength(httpMessage, content.readableBytes());
messageMap.remove(streamID);
return httpMessage;
}
}
return null;
}
private static HttpRequest createHttpRequest(SpdyHeaderBlock requestFrame)
throws Exception {
// Create the first line of the request from the name/value pairs
HttpMethod method = SpdyHeaders.getMethod(requestFrame);
String url = SpdyHeaders.getUrl(requestFrame);
HttpVersion version = SpdyHeaders.getVersion(requestFrame);
SpdyHeaders.removeMethod(requestFrame);
SpdyHeaders.removeUrl(requestFrame);
SpdyHeaders.removeVersion(requestFrame);
HttpRequest httpRequest = new DefaultHttpRequest(version, method, url);
for (Map.Entry<String, String> e: requestFrame.getHeaders()) {
httpRequest.addHeader(e.getKey(), e.getValue());
}
// Chunked encoding is no longer valid
List<String> encodings = httpRequest.getHeaders(HttpHeaders.Names.TRANSFER_ENCODING);
encodings.remove(HttpHeaders.Values.CHUNKED);
if (encodings.isEmpty()) {
httpRequest.removeHeader(HttpHeaders.Names.TRANSFER_ENCODING);
} else {
httpRequest.setHeader(HttpHeaders.Names.TRANSFER_ENCODING, encodings);
}
// The Connection and Keep-Alive headers are no longer valid
HttpHeaders.setKeepAlive(httpRequest, true);
return httpRequest;
}
private static HttpResponse createHttpResponse(SpdyHeaderBlock responseFrame)
throws Exception {
// Create the first line of the response from the name/value pairs
HttpResponseStatus status = SpdyHeaders.getStatus(responseFrame);
HttpVersion version = SpdyHeaders.getVersion(responseFrame);
SpdyHeaders.removeStatus(responseFrame);
SpdyHeaders.removeVersion(responseFrame);
HttpResponse httpResponse = new DefaultHttpResponse(version, status);
for (Map.Entry<String, String> e: responseFrame.getHeaders()) {
httpResponse.addHeader(e.getKey(), e.getValue());
}
// Chunked encoding is no longer valid
List<String> encodings = httpResponse.getHeaders(HttpHeaders.Names.TRANSFER_ENCODING);
encodings.remove(HttpHeaders.Values.CHUNKED);
if (encodings.isEmpty()) {
httpResponse.removeHeader(HttpHeaders.Names.TRANSFER_ENCODING);
} else {
httpResponse.setHeader(HttpHeaders.Names.TRANSFER_ENCODING, encodings);
}
httpResponse.removeHeader(HttpHeaders.Names.TRAILER);
// The Connection and Keep-Alive headers are no longer valid
HttpHeaders.setKeepAlive(httpResponse, true);
return httpResponse;
}
}

View File

@ -1,267 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.spdy;
import io.netty.channel.ChannelOutboundHandlerContext;
import io.netty.handler.codec.MessageToMessageEncoder;
import io.netty.handler.codec.UnsupportedMessageTypeException;
import io.netty.handler.codec.http.HttpChunk;
import io.netty.handler.codec.http.HttpChunkTrailer;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMessage;
import io.netty.handler.codec.http.HttpRequest;
import io.netty.handler.codec.http.HttpResponse;
import java.util.List;
import java.util.Map;
/**
* Encodes {@link HttpRequest}s, {@link HttpResponse}s, and {@link HttpChunk}s
* into {@link SpdySynStreamFrame}s and {@link SpdySynReplyFrame}s.
*
* <h3>Request Annotations</h3>
*
* SPDY specific headers must be added to {@link HttpRequest}s:
* <table border=1>
* <tr>
* <th>Header Name</th><th>Header Value</th>
* </tr>
* <tr>
* <td>{@code "X-SPDY-Stream-ID"}</td>
* <td>The Stream-ID for this request.
* Stream-IDs must be odd, positive integers, and must increase monotonically.</td>
* </tr>
* <tr>
* <td>{@code "X-SPDY-Priority"}</td>
* <td>The priority value for this request.
* The priority should be between 0 and 3 inclusive.
* 0 represents the highest priority and 3 represents the lowest.
* This header is optional and defaults to 0.</td>
* </tr>
* </table>
*
* <h3>Response Annotations</h3>
*
* SPDY specific headers must be added to {@link HttpResponse}s:
* <table border=1>
* <tr>
* <th>Header Name</th><th>Header Value</th>
* </tr>
* <tr>
* <td>{@code "X-SPDY-Stream-ID"}</td>
* <td>The Stream-ID of the request corresponding to this response.</td>
* </tr>
* </table>
*
* <h3>Pushed Resource Annotations</h3>
*
* SPDY specific headers must be added to pushed {@link HttpResponse}s:
* <table border=1>
* <tr>
* <th>Header Name</th><th>Header Value</th>
* </tr>
* <tr>
* <td>{@code "X-SPDY-Stream-ID"}</td>
* <td>The Stream-ID for this resource.
* Stream-IDs must be even, positive integers, and must increase monotonically.</td>
* </tr>
* <tr>
* <td>{@code "X-SPDY-Associated-To-Stream-ID"}</td>
* <td>The Stream-ID of the request that inititated this pushed resource.</td>
* </tr>
* <tr>
* <td>{@code "X-SPDY-Priority"}</td>
* <td>The priority value for this resource.
* The priority should be between 0 and 3 inclusive.
* 0 represents the highest priority and 3 represents the lowest.
* This header is optional and defaults to 0.</td>
* </tr>
* <tr>
* <td>{@code "X-SPDY-URL"}</td>
* <td>The full URL for the resource being pushed.</td>
* </tr>
* </table>
*
* <h3>Chunked Content</h3>
*
* This encoder associates all {@link HttpChunk}s that it receives
* with the most recently received 'chunked' {@link HttpRequest}
* or {@link HttpResponse}.
*
* <h3>Pushed Resources</h3>
*
* All pushed resources should be sent before sending the response
* that corresponds to the initial request.
*/
public class SpdyHttpEncoder extends MessageToMessageEncoder<Object, Object> {
private volatile int currentStreamID;
@Override
public boolean isEncodable(Object msg) throws Exception {
return msg instanceof HttpMessage || msg instanceof HttpChunk;
}
@Override
public Object encode(ChannelOutboundHandlerContext<Object> ctx, Object msg)
throws Exception {
if (msg instanceof HttpRequest) {
HttpRequest httpRequest = (HttpRequest) msg;
SpdySynStreamFrame spdySynStreamFrame = createSynStreamFrame(httpRequest);
int streamID = spdySynStreamFrame.getStreamID();
return new Object[] { spdySynStreamFrame, dataFrame(streamID, httpRequest) };
} else if (msg instanceof HttpResponse) {
HttpResponse httpResponse = (HttpResponse) msg;
if (httpResponse.containsHeader(SpdyHttpHeaders.Names.ASSOCIATED_TO_STREAM_ID)) {
SpdySynStreamFrame spdySynStreamFrame = createSynStreamFrame(httpResponse);
int streamID = spdySynStreamFrame.getStreamID();
return new Object[] { spdySynStreamFrame, dataFrame(streamID, httpResponse) };
} else {
SpdySynReplyFrame spdySynReplyFrame = createSynReplyFrame(httpResponse);
int streamID = spdySynReplyFrame.getStreamID();
return new Object[] { spdySynReplyFrame, dataFrame(streamID, httpResponse) };
}
} else if (msg instanceof HttpChunk) {
HttpChunk chunk = (HttpChunk) msg;
SpdyDataFrame spdyDataFrame = new DefaultSpdyDataFrame(currentStreamID);
spdyDataFrame.setData(chunk.getContent());
spdyDataFrame.setLast(chunk.isLast());
if (chunk instanceof HttpChunkTrailer) {
HttpChunkTrailer trailer = (HttpChunkTrailer) chunk;
List<Map.Entry<String, String>> trailers = trailer.getHeaders();
if (trailers.isEmpty()) {
return spdyDataFrame;
} else {
// Create SPDY HEADERS frame out of trailers
SpdyHeadersFrame spdyHeadersFrame = new DefaultSpdyHeadersFrame(currentStreamID);
for (Map.Entry<String, String> entry: trailers) {
spdyHeadersFrame.addHeader(entry.getKey(), entry.getValue());
}
// Write HEADERS frame and append Data Frame
return new Object[] { spdyHeadersFrame, spdyDataFrame };
}
} else {
return spdyDataFrame;
}
} else {
// Unknown message type
throw new UnsupportedMessageTypeException();
}
}
private static SpdyDataFrame dataFrame(int streamID, HttpMessage httpMessage) {
if (httpMessage.getContent().readableBytes() == 0) {
return null;
}
// Create SPDY Data Frame out of message content
SpdyDataFrame spdyDataFrame = new DefaultSpdyDataFrame(streamID);
spdyDataFrame.setData(httpMessage.getContent());
spdyDataFrame.setLast(true);
return spdyDataFrame;
}
private SpdySynStreamFrame createSynStreamFrame(HttpMessage httpMessage)
throws Exception {
boolean chunked = httpMessage.isChunked();
// Get the Stream-ID, Associated-To-Stream-ID, Priority, and URL from the headers
int streamID = SpdyHttpHeaders.getStreamID(httpMessage);
int associatedToStreamID = SpdyHttpHeaders.getAssociatedToStreamID(httpMessage);
byte priority = SpdyHttpHeaders.getPriority(httpMessage);
String URL = SpdyHttpHeaders.getUrl(httpMessage);
SpdyHttpHeaders.removeStreamID(httpMessage);
SpdyHttpHeaders.removeAssociatedToStreamID(httpMessage);
SpdyHttpHeaders.removePriority(httpMessage);
SpdyHttpHeaders.removeUrl(httpMessage);
// The Connection, Keep-Alive, Proxy-Connection, and Transfer-Encoding
// headers are not valid and MUST not be sent.
httpMessage.removeHeader(HttpHeaders.Names.CONNECTION);
httpMessage.removeHeader("Keep-Alive");
httpMessage.removeHeader("Proxy-Connection");
httpMessage.removeHeader(HttpHeaders.Names.TRANSFER_ENCODING);
SpdySynStreamFrame spdySynStreamFrame = new DefaultSpdySynStreamFrame(streamID, associatedToStreamID, priority);
for (Map.Entry<String, String> entry: httpMessage.getHeaders()) {
spdySynStreamFrame.addHeader(entry.getKey(), entry.getValue());
}
// Unfold the first line of the message into name/value pairs
SpdyHeaders.setVersion(spdySynStreamFrame, httpMessage.getProtocolVersion());
if (httpMessage instanceof HttpRequest) {
HttpRequest httpRequest = (HttpRequest) httpMessage;
SpdyHeaders.setMethod(spdySynStreamFrame, httpRequest.getMethod());
SpdyHeaders.setUrl(spdySynStreamFrame, httpRequest.getUri());
}
if (httpMessage instanceof HttpResponse) {
HttpResponse httpResponse = (HttpResponse) httpMessage;
SpdyHeaders.setStatus(spdySynStreamFrame, httpResponse.getStatus());
SpdyHeaders.setUrl(spdySynStreamFrame, URL);
spdySynStreamFrame.setUnidirectional(true);
}
if (chunked) {
currentStreamID = streamID;
spdySynStreamFrame.setLast(false);
} else {
spdySynStreamFrame.setLast(httpMessage.getContent().readableBytes() == 0);
}
return spdySynStreamFrame;
}
private SpdySynReplyFrame createSynReplyFrame(HttpResponse httpResponse)
throws Exception {
boolean chunked = httpResponse.isChunked();
// Get the Stream-ID from the headers
int streamID = SpdyHttpHeaders.getStreamID(httpResponse);
SpdyHttpHeaders.removeStreamID(httpResponse);
// The Connection, Keep-Alive, Proxy-Connection, and Transfer-ENcoding
// headers are not valid and MUST not be sent.
httpResponse.removeHeader(HttpHeaders.Names.CONNECTION);
httpResponse.removeHeader("Keep-Alive");
httpResponse.removeHeader("Proxy-Connection");
httpResponse.removeHeader(HttpHeaders.Names.TRANSFER_ENCODING);
SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamID);
for (Map.Entry<String, String> entry: httpResponse.getHeaders()) {
spdySynReplyFrame.addHeader(entry.getKey(), entry.getValue());
}
// Unfold the first line of the repsonse into name/value pairs
SpdyHeaders.setStatus(spdySynReplyFrame, httpResponse.getStatus());
SpdyHeaders.setVersion(spdySynReplyFrame, httpResponse.getProtocolVersion());
if (chunked) {
currentStreamID = streamID;
spdySynReplyFrame.setLast(false);
} else {
spdySynReplyFrame.setLast(httpResponse.getContent().readableBytes() == 0);
}
return spdySynReplyFrame;
}
}

View File

@ -1,147 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.spdy;
import io.netty.handler.codec.http.HttpHeaders;
import io.netty.handler.codec.http.HttpMessage;
/**
* Provides the constants for the header names and the utility methods
* used by the {@link SpdyHttpDecoder} and {@link SpdyHttpEncoder}.
* @apiviz.stereotype static
*/
public final class SpdyHttpHeaders {
/**
* SPDY HTTP header names
* @apiviz.stereotype static
*/
public static final class Names {
/**
* {@code "X-SPDY-Stream-ID"}
*/
public static final String STREAM_ID = "X-SPDY-Stream-ID";
/**
* {@code "X-SPDY-Associated-To-Stream-ID"}
*/
public static final String ASSOCIATED_TO_STREAM_ID = "X-SPDY-Associated-To-Stream-ID";
/**
* {@code "X-SPDY-Priority"}
*/
public static final String PRIORITY = "X-SPDY-Priority";
/**
* {@code "X-SPDY-URL"}
*/
public static final String URL = "X-SPDY-URL";
private Names() {
super();
}
}
private SpdyHttpHeaders() {
}
/**
* Removes the {@code "X-SPDY-Stream-ID"} header.
*/
public static void removeStreamID(HttpMessage message) {
message.removeHeader(Names.STREAM_ID);
}
/**
* Returns the value of the {@code "X-SPDY-Stream-ID"} header.
*/
public static int getStreamID(HttpMessage message) {
return HttpHeaders.getIntHeader(message, Names.STREAM_ID);
}
/**
* Sets the {@code "X-SPDY-Stream-ID"} header.
*/
public static void setStreamID(HttpMessage message, int streamID) {
HttpHeaders.setIntHeader(message, Names.STREAM_ID, streamID);
}
/**
* Removes the {@code "X-SPDY-Associated-To-Stream-ID"} header.
*/
public static void removeAssociatedToStreamID(HttpMessage message) {
message.removeHeader(Names.ASSOCIATED_TO_STREAM_ID);
}
/**
* Returns the value of the {@code "X-SPDY-Associated-To-Stream-ID"} header.
*
* @return the header value or {@code 0} if there is no such header or
* if the header value is not a number
*/
public static int getAssociatedToStreamID(HttpMessage message) {
return HttpHeaders.getIntHeader(message, Names.ASSOCIATED_TO_STREAM_ID, 0);
}
/**
* Sets the {@code "X-SPDY-Associated-To-Stream-ID"} header.
*/
public static void setAssociatedToStreamID(HttpMessage message, int associatedToStreamID) {
HttpHeaders.setIntHeader(message, Names.ASSOCIATED_TO_STREAM_ID, associatedToStreamID);
}
/**
* Removes the {@code "X-SPDY-Priority"} header.
*/
public static void removePriority(HttpMessage message) {
message.removeHeader(Names.PRIORITY);
}
/**
* Returns the value of the {@code "X-SPDY-Priority"} header.
*
* @return the header value or {@code 0} if there is no such header or
* if the header value is not a number
*/
public static byte getPriority(HttpMessage message) {
return (byte) HttpHeaders.getIntHeader(message, Names.PRIORITY, 0);
}
/**
* Sets the {@code "X-SPDY-Priority"} header.
*/
public static void setPriority(HttpMessage message, byte priority) {
HttpHeaders.setIntHeader(message, Names.PRIORITY, priority);
}
/**
* Removes the {@code "X-SPDY-URL"} header.
*/
public static void removeUrl(HttpMessage message) {
message.removeHeader(Names.URL);
}
/**
* Returns the value of the {@code "X-SPDY-URL"} header.
*/
public static String getUrl(HttpMessage message) {
return message.getHeader(Names.URL);
}
/**
* Sets the {@code "X-SPDY-URL"} header.
*/
public static void setUrl(HttpMessage message, String url) {
message.setHeader(Names.URL, url);
}
}

View File

@ -1,22 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.spdy;
/**
* A SPDY Protocol NOOP Control Frame
*/
public interface SpdyNoOpFrame {
}

View File

@ -1,32 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.spdy;
/**
* A SPDY Protocol PING Control Frame
*/
public interface SpdyPingFrame {
/**
* Returns the ID of this frame.
*/
int getID();
/**
* Sets the ID of this frame.
*/
void setID(int ID);
}

View File

@ -1,56 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.spdy;
import io.netty.handler.codec.CodecException;
/**
* An {@link Exception} which is thrown when the received frame cannot
* be decoded by the {@link SpdyFrameDecoder}.
* @apiviz.exclude
*/
public class SpdyProtocolException extends CodecException {
private static final long serialVersionUID = -1097979786367505658L;
/**
* Creates a new instance.
*/
public SpdyProtocolException() {
super();
}
/**
* Creates a new instance.
*/
public SpdyProtocolException(String message, Throwable cause) {
super(message, cause);
}
/**
* Creates a new instance.
*/
public SpdyProtocolException(String message) {
super(message);
}
/**
* Creates a new instance.
*/
public SpdyProtocolException(Throwable cause) {
super(cause);
}
}

View File

@ -1,42 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.spdy;
/**
* A SPDY Protocol RST_STREAM Control Frame
*/
public interface SpdyRstStreamFrame {
/**
* Returns the Stream-ID of this frame.
*/
int getStreamID();
/**
* Sets the Stream-ID of this frame. The Stream-ID must be positive.
*/
void setStreamID(int streamID);
/**
* Returns the status of this frame.
*/
SpdyStreamStatus getStatus();
/**
* Sets the status of this frame.
*/
void setStatus(SpdyStreamStatus status);
}

View File

@ -1,133 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.spdy;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
final class SpdySession {
private final Map<Integer, StreamState> activeStreams =
new ConcurrentHashMap<Integer, StreamState>();
SpdySession() {
}
public int numActiveStreams() {
return activeStreams.size();
}
public boolean noActiveStreams() {
return activeStreams.isEmpty();
}
public boolean isActiveStream(int streamID) {
return activeStreams.containsKey(new Integer(streamID));
}
public void acceptStream(int streamID, boolean remoteSideClosed, boolean localSideClosed) {
if (!remoteSideClosed || !localSideClosed) {
activeStreams.put(new Integer(streamID),
new StreamState(remoteSideClosed, localSideClosed));
}
return;
}
public void removeStream(int streamID) {
activeStreams.remove(new Integer(streamID));
return;
}
public boolean isRemoteSideClosed(int streamID) {
StreamState state = activeStreams.get(new Integer(streamID));
return state == null || state.isRemoteSideClosed();
}
public void closeRemoteSide(int streamID) {
Integer StreamID = new Integer(streamID);
StreamState state = activeStreams.get(StreamID);
if (state != null) {
state.closeRemoteSide();
if (state.isLocalSideClosed()) {
activeStreams.remove(StreamID);
}
}
}
public boolean isLocalSideClosed(int streamID) {
StreamState state = activeStreams.get(new Integer(streamID));
return state == null || state.isLocalSideClosed();
}
public void closeLocalSide(int streamID) {
Integer StreamID = new Integer(streamID);
StreamState state = activeStreams.get(StreamID);
if (state != null) {
state.closeLocalSide();
if (state.isRemoteSideClosed()) {
activeStreams.remove(StreamID);
}
}
}
public boolean hasReceivedReply(int streamID) {
StreamState state = activeStreams.get(new Integer(streamID));
return state != null && state.hasReceivedReply();
}
public void receivedReply(int streamID) {
StreamState state = activeStreams.get(new Integer(streamID));
if (state != null) {
state.receivedReply();
}
}
private static final class StreamState {
private boolean remoteSideClosed;
private boolean localSideClosed;
private boolean receivedReply;
public StreamState(boolean remoteSideClosed, boolean localSideClosed) {
this.remoteSideClosed = remoteSideClosed;
this.localSideClosed = localSideClosed;
}
public boolean isRemoteSideClosed() {
return remoteSideClosed;
}
public void closeRemoteSide() {
remoteSideClosed = true;
}
public boolean isLocalSideClosed() {
return localSideClosed;
}
public void closeLocalSide() {
localSideClosed = true;
}
public boolean hasReceivedReply() {
return receivedReply;
}
public void receivedReply() {
receivedReply = true;
}
}
}

View File

@ -1,496 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.spdy;
import io.netty.channel.ChannelBufferHolder;
import io.netty.channel.ChannelBufferHolders;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerAdapter;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerContext;
import io.netty.channel.ChannelOutboundHandlerContext;
import java.util.Queue;
import java.util.concurrent.atomic.AtomicInteger;
/**
* Manages streams within a SPDY session.
*/
public class SpdySessionHandler extends ChannelHandlerAdapter<Object, Object> {
private static final SpdyProtocolException PROTOCOL_EXCEPTION = new SpdyProtocolException();
private final SpdySession spdySession = new SpdySession();
private volatile int lastGoodStreamID;
private volatile int remoteConcurrentStreams;
private volatile int localConcurrentStreams;
private volatile int maxConcurrentStreams;
private final AtomicInteger pings = new AtomicInteger();
private volatile boolean sentGoAwayFrame;
private volatile boolean receivedGoAwayFrame;
private volatile ChannelFuture closeSessionFuture;
private final boolean server;
/**
* Creates a new session handler.
*
* @param server {@code true} if and only if this session handler should
* handle the server endpoint of the connection.
* {@code false} if and only if this session handler should
* handle the client endpoint of the connection.
*/
public SpdySessionHandler(boolean server) {
super();
this.server = server;
}
@Override
public ChannelBufferHolder<Object> newOutboundBuffer(
ChannelOutboundHandlerContext<Object> ctx) throws Exception {
return ChannelBufferHolders.messageBuffer();
}
@Override
public ChannelBufferHolder<Object> newInboundBuffer(
ChannelInboundHandlerContext<Object> ctx) throws Exception {
return ChannelBufferHolders.messageBuffer();
}
@Override
public void inboundBufferUpdated(ChannelInboundHandlerContext<Object> ctx)
throws Exception {
Queue<Object> in = ctx.inbound().messageBuffer();
for (;;) {
Object msg = in.poll();
if (msg == null) {
break;
}
handleInboundMessage(ctx, msg);
}
ctx.fireInboundBufferUpdated();
}
private void handleInboundMessage(ChannelInboundHandlerContext<Object> ctx, Object msg)
throws Exception {
if (msg instanceof SpdyDataFrame) {
/*
* SPDY Data frame processing requirements:
*
* If an endpoint receives a data frame for a Stream-ID which does not exist,
* it must return a RST_STREAM with error code INVALID_STREAM for the Stream-ID.
*
* If an endpoint which created the stream receives a data frame before receiving
* a SYN_REPLY on that stream, it is a protocol error, and the receiver should
* close the connection immediately.
*
* If an endpoint receives multiple data frames for invalid Stream-IDs,
* it may terminate the session.
*
* If an endpoint refuses a stream it must ignore any data frames for that stream.
*
* If an endpoint receives data on a stream which has already been torn down,
* it must ignore the data received after the teardown.
*/
SpdyDataFrame spdyDataFrame = (SpdyDataFrame) msg;
int streamID = spdyDataFrame.getStreamID();
// Check if we received a data frame for a Stream-ID which is not open
if (spdySession.isRemoteSideClosed(streamID)) {
if (!sentGoAwayFrame) {
issueStreamError(ctx, streamID, SpdyStreamStatus.INVALID_STREAM);
}
return;
}
// Check if we received a data frame before receiving a SYN_REPLY
if (!isRemoteInitiatedID(streamID) && !spdySession.hasReceivedReply(streamID)) {
issueStreamError(ctx, streamID, SpdyStreamStatus.PROTOCOL_ERROR);
return;
}
if (spdyDataFrame.isLast()) {
// Close remote side of stream
halfCloseStream(streamID, true);
}
} else if (msg instanceof SpdySynStreamFrame) {
/*
* SPDY SYN_STREAM frame processing requirements:
*
* If an endpoint receives a SYN_STREAM with a Stream-ID that is not monotonically
* increasing, it must issue a session error with the status PROTOCOL_ERROR.
*
* If an endpoint receives multiple SYN_STREAM frames with the same active
* Stream-ID, it must issue a stream error with the status code PROTOCOL_ERROR.
*/
SpdySynStreamFrame spdySynStreamFrame = (SpdySynStreamFrame) msg;
int streamID = spdySynStreamFrame.getStreamID();
// Check if we received a valid SYN_STREAM frame
if (spdySynStreamFrame.isInvalid() ||
!isRemoteInitiatedID(streamID) ||
spdySession.isActiveStream(streamID)) {
issueStreamError(ctx, streamID, SpdyStreamStatus.PROTOCOL_ERROR);
return;
}
// Stream-IDs must be monotonically increassing
if (streamID < lastGoodStreamID) {
issueSessionError(ctx);
return;
}
// Try to accept the stream
boolean remoteSideClosed = spdySynStreamFrame.isLast();
boolean localSideClosed = spdySynStreamFrame.isUnidirectional();
if (!acceptStream(streamID, remoteSideClosed, localSideClosed)) {
issueStreamError(ctx, streamID, SpdyStreamStatus.REFUSED_STREAM);
return;
}
} else if (msg instanceof SpdySynReplyFrame) {
/*
* SPDY SYN_REPLY frame processing requirements:
*
* If an endpoint receives multiple SYN_REPLY frames for the same active Stream-ID
* it must issue a stream error with the status code PROTOCOL_ERROR.
*/
SpdySynReplyFrame spdySynReplyFrame = (SpdySynReplyFrame) msg;
int streamID = spdySynReplyFrame.getStreamID();
// Check if we received a valid SYN_REPLY frame
if (spdySynReplyFrame.isInvalid() ||
isRemoteInitiatedID(streamID) ||
spdySession.isRemoteSideClosed(streamID)) {
issueStreamError(ctx, streamID, SpdyStreamStatus.INVALID_STREAM);
return;
}
// Check if we have received multiple frames for the same Stream-ID
if (spdySession.hasReceivedReply(streamID)) {
issueStreamError(ctx, streamID, SpdyStreamStatus.PROTOCOL_ERROR);
return;
}
spdySession.receivedReply(streamID);
if (spdySynReplyFrame.isLast()) {
// Close remote side of stream
halfCloseStream(streamID, true);
}
} else if (msg instanceof SpdyRstStreamFrame) {
/*
* SPDY RST_STREAM frame processing requirements:
*
* After receiving a RST_STREAM on a stream, the receiver must not send additional
* frames on that stream.
*/
SpdyRstStreamFrame spdyRstStreamFrame = (SpdyRstStreamFrame) msg;
removeStream(spdyRstStreamFrame.getStreamID());
} else if (msg instanceof SpdySettingsFrame) {
/*
* Only concerned with MAX_CONCURRENT_STREAMS
*/
SpdySettingsFrame spdySettingsFrame = (SpdySettingsFrame) msg;
updateConcurrentStreams(spdySettingsFrame, true);
} else if (msg instanceof SpdyPingFrame) {
/*
* SPDY PING frame processing requirements:
*
* Receivers of a PING frame should send an identical frame to the sender
* as soon as possible.
*
* Receivers of a PING frame must ignore frames that it did not initiate
*/
SpdyPingFrame spdyPingFrame = (SpdyPingFrame) msg;
if (isRemoteInitiatedID(spdyPingFrame.getID())) {
ctx.write(spdyPingFrame);
return;
}
// Note: only checks that there are outstanding pings since uniqueness is not inforced
if (pings.get() == 0) {
return;
}
pings.getAndDecrement();
} else if (msg instanceof SpdyGoAwayFrame) {
receivedGoAwayFrame = true;
} else if (msg instanceof SpdyHeadersFrame) {
SpdyHeadersFrame spdyHeadersFrame = (SpdyHeadersFrame) msg;
int streamID = spdyHeadersFrame.getStreamID();
// Check if we received a valid HEADERS frame
if (spdyHeadersFrame.isInvalid()) {
issueStreamError(ctx, streamID, SpdyStreamStatus.PROTOCOL_ERROR);
return;
}
if (spdySession.isRemoteSideClosed(streamID)) {
issueStreamError(ctx, streamID, SpdyStreamStatus.INVALID_STREAM);
return;
}
}
ctx.nextInboundMessageBuffer().add(msg);
}
@Override
public void disconnect(final ChannelOutboundHandlerContext<Object> ctx,
final ChannelFuture future) throws Exception {
sendGoAwayFrame(ctx).addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture f)
throws Exception {
ctx.disconnect(future);
}
});
}
@Override
public void close(final ChannelOutboundHandlerContext<Object> ctx,
final ChannelFuture future) throws Exception {
sendGoAwayFrame(ctx).addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture f)
throws Exception {
ctx.close(future);
}
});
}
@Override
public void flush(ChannelOutboundHandlerContext<Object> ctx,
ChannelFuture future) throws Exception {
Queue<Object> in = ctx.outbound().messageBuffer();
for (;;) {
Object msg = in.poll();
if (msg == null) {
break;
}
handleOutboundMessage(ctx, msg);
}
ctx.flush(future);
}
private void handleOutboundMessage(ChannelOutboundHandlerContext<Object> ctx, Object msg)
throws Exception {
if (msg instanceof SpdyDataFrame) {
SpdyDataFrame spdyDataFrame = (SpdyDataFrame) msg;
int streamID = spdyDataFrame.getStreamID();
if (spdySession.isLocalSideClosed(streamID)) {
issueStreamError(ctx, streamID, SpdyStreamStatus.PROTOCOL_ERROR);
return;
}
if (spdyDataFrame.isLast()) {
halfCloseStream(streamID, false);
}
} else if (msg instanceof SpdySynStreamFrame) {
SpdySynStreamFrame spdySynStreamFrame = (SpdySynStreamFrame) msg;
int streamID = spdySynStreamFrame.getStreamID();
boolean remoteSideClosed = spdySynStreamFrame.isUnidirectional();
boolean localSideClosed = spdySynStreamFrame.isLast();
if (!acceptStream(streamID, remoteSideClosed, localSideClosed)) {
issueStreamError(ctx, streamID, SpdyStreamStatus.PROTOCOL_ERROR);
return;
}
} else if (msg instanceof SpdySynReplyFrame) {
SpdySynReplyFrame spdySynReplyFrame = (SpdySynReplyFrame) msg;
int streamID = spdySynReplyFrame.getStreamID();
if (!isRemoteInitiatedID(streamID) || spdySession.isLocalSideClosed(streamID)) {
ctx.fireExceptionCaught(PROTOCOL_EXCEPTION);
return;
}
if (spdySynReplyFrame.isLast()) {
halfCloseStream(streamID, false);
}
} else if (msg instanceof SpdyRstStreamFrame) {
SpdyRstStreamFrame spdyRstStreamFrame = (SpdyRstStreamFrame) msg;
removeStream(spdyRstStreamFrame.getStreamID());
} else if (msg instanceof SpdySettingsFrame) {
SpdySettingsFrame spdySettingsFrame = (SpdySettingsFrame) msg;
updateConcurrentStreams(spdySettingsFrame, false);
} else if (msg instanceof SpdyPingFrame) {
SpdyPingFrame spdyPingFrame = (SpdyPingFrame) msg;
if (isRemoteInitiatedID(spdyPingFrame.getID())) {
ctx.fireExceptionCaught(new IllegalArgumentException(
"invalid PING ID: " + spdyPingFrame.getID()));
return;
}
pings.getAndIncrement();
} else if (msg instanceof SpdyGoAwayFrame) {
// Should send a CLOSE ChannelStateEvent
ctx.fireExceptionCaught(PROTOCOL_EXCEPTION);
return;
} else if (msg instanceof SpdyHeadersFrame) {
SpdyHeadersFrame spdyHeadersFrame = (SpdyHeadersFrame) msg;
int streamID = spdyHeadersFrame.getStreamID();
if (spdySession.isLocalSideClosed(streamID)) {
ctx.fireExceptionCaught(PROTOCOL_EXCEPTION);
return;
}
}
ctx.nextOutboundMessageBuffer().add(msg);
}
/*
* Error Handling
*/
private void issueSessionError(ChannelHandlerContext ctx) {
sendGoAwayFrame(ctx).addListener(ChannelFutureListener.CLOSE);
}
// Send a RST_STREAM frame in response to an incoming MessageEvent
// Only called in the upstream direction
private void issueStreamError(
ChannelHandlerContext ctx, int streamID, SpdyStreamStatus status) {
removeStream(streamID);
SpdyRstStreamFrame spdyRstStreamFrame = new DefaultSpdyRstStreamFrame(streamID, status);
ctx.write(spdyRstStreamFrame);
}
/*
* Helper functions
*/
private boolean isRemoteInitiatedID(int ID) {
boolean serverID = SpdyCodecUtil.isServerID(ID);
return server && !serverID || !server && serverID;
}
private synchronized void updateConcurrentStreams(SpdySettingsFrame settings, boolean remote) {
int newConcurrentStreams = settings.getValue(SpdySettingsFrame.SETTINGS_MAX_CONCURRENT_STREAMS);
if (remote) {
remoteConcurrentStreams = newConcurrentStreams;
} else {
localConcurrentStreams = newConcurrentStreams;
}
if (localConcurrentStreams == remoteConcurrentStreams) {
maxConcurrentStreams = localConcurrentStreams;
return;
}
if (localConcurrentStreams == 0) {
maxConcurrentStreams = remoteConcurrentStreams;
return;
}
if (remoteConcurrentStreams == 0) {
maxConcurrentStreams = localConcurrentStreams;
return;
}
if (localConcurrentStreams > remoteConcurrentStreams) {
maxConcurrentStreams = remoteConcurrentStreams;
} else {
maxConcurrentStreams = localConcurrentStreams;
}
}
// need to synchronize accesses to sentGoAwayFrame and lastGoodStreamID
private synchronized boolean acceptStream(
int streamID, boolean remoteSideClosed, boolean localSideClosed) {
// Cannot initiate any new streams after receiving or sending GOAWAY
if (receivedGoAwayFrame || sentGoAwayFrame) {
return false;
}
if (maxConcurrentStreams != 0 &&
spdySession.numActiveStreams() >= maxConcurrentStreams) {
return false;
}
spdySession.acceptStream(streamID, remoteSideClosed, localSideClosed);
if (isRemoteInitiatedID(streamID)) {
lastGoodStreamID = streamID;
}
return true;
}
private void halfCloseStream(int streamID, boolean remote) {
if (remote) {
spdySession.closeRemoteSide(streamID);
} else {
spdySession.closeLocalSide(streamID);
}
if (closeSessionFuture != null && spdySession.noActiveStreams()) {
closeSessionFuture.setSuccess();
}
}
private void removeStream(int streamID) {
spdySession.removeStream(streamID);
if (closeSessionFuture != null && spdySession.noActiveStreams()) {
closeSessionFuture.setSuccess();
}
}
private synchronized ChannelFuture sendGoAwayFrame(ChannelHandlerContext ctx) {
if (!sentGoAwayFrame) {
sentGoAwayFrame = true;
return ctx.write(new DefaultSpdyGoAwayFrame(lastGoodStreamID));
}
return ctx.newSucceededFuture();
}
}

View File

@ -1,105 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.spdy;
import java.util.Set;
/**
* A SPDY Protocol SETTINGS Control Frame
*/
public interface SpdySettingsFrame {
int SETTINGS_UPLOAD_BANDWIDTH = 1;
int SETTINGS_DOWNLOAD_BANDWIDTH = 2;
int SETTINGS_ROUND_TRIP_TIME = 3;
int SETTINGS_MAX_CONCURRENT_STREAMS = 4;
int SETTINGS_CURRENT_CWND = 5;
int SETTINGS_DOWNLOAD_RETRANS_RATE = 6;
int SETTINGS_INITIAL_WINDOW_SIZE = 7;
/**
* Returns a {@code Set} of the setting IDs.
* The set's iterator will return the IDs in ascending order.
*/
Set<Integer> getIDs();
/**
* Returns {@code true} if the setting ID has a value.
*/
boolean isSet(int ID);
/**
* Returns the value of the setting ID.
* Returns -1 if the setting ID is not set.
*/
int getValue(int ID);
/**
* Sets the value of the setting ID.
* The ID must be positive and cannot exceeed 16777215.
*/
void setValue(int ID, int value);
/**
* Sets the value of the setting ID.
* Sets if the setting should be persisted (should only be set by the server).
* Sets if the setting is persisted (should only be set by the client).
* The ID must be positive and cannot exceed 16777215.
*/
void setValue(int ID, int value, boolean persistVal, boolean persisted);
/**
* Removes the value of the setting ID.
* Removes all persistance information for the setting.
*/
void removeValue(int ID);
/**
* Returns {@code true} if this setting should be persisted.
* Returns {@code false} if this setting should not be persisted
* or if the setting ID has no value.
*/
boolean persistValue(int ID);
/**
* Sets if this setting should be persisted.
* Has no effect if the setting ID has no value.
*/
void setPersistValue(int ID, boolean persistValue);
/**
* Returns {@code true} if this setting is persisted.
* Returns {@code false} if this setting should not be persisted
* or if the setting ID has no value.
*/
boolean isPersisted(int ID);
/**
* Sets if this setting is persisted.
* Has no effect if the setting ID has no value.
*/
void setPersisted(int ID, boolean persisted);
/**
* Returns {@code true} if previously persisted settings should be cleared.
*/
boolean clearPreviouslyPersistedSettings();
/**
* Sets if previously persisted settings should be cleared.
*/
void setClearPreviouslyPersistedSettings(boolean clear);
}

View File

@ -1,155 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.spdy;
/**
* The SPDY stream status code and its description.
* @apiviz.exclude
*/
public class SpdyStreamStatus implements Comparable<SpdyStreamStatus> {
/**
* 1 Protocol Error
*/
public static final SpdyStreamStatus PROTOCOL_ERROR =
new SpdyStreamStatus(1, "PROTOCOL_ERROR");
/**
* 2 Invalid Stream
*/
public static final SpdyStreamStatus INVALID_STREAM =
new SpdyStreamStatus(2, "INVALID_STREAM");
/**
* 3 Refused Stream
*/
public static final SpdyStreamStatus REFUSED_STREAM =
new SpdyStreamStatus(3, "REFUSED_STREAM");
/**
* 4 Unsupported Version
*/
public static final SpdyStreamStatus UNSUPPORTED_VERSION =
new SpdyStreamStatus(4, "UNSUPPORTED_VERSION");
/**
* 5 Cancel
*/
public static final SpdyStreamStatus CANCEL =
new SpdyStreamStatus(5, "CANCEL");
/**
* 6 Internal Error
*/
public static final SpdyStreamStatus INTERNAL_ERROR =
new SpdyStreamStatus(6, "INTERNAL_ERROR");
/**
* 7 Flow Control Error
*/
public static final SpdyStreamStatus FLOW_CONTROL_ERROR =
new SpdyStreamStatus(7, "FLOW_CONTROL_ERROR");
/**
* Returns the {@link SpdyStreamStatus} represented by the specified code.
* If the specified code is a defined SPDY status code, a cached instance
* will be returned. Otherwise, a new instance will be returned.
*/
public static SpdyStreamStatus valueOf(int code) {
if (code == 0) {
throw new IllegalArgumentException(
"0 is not a valid status code for a RST_STREAM");
}
switch (code) {
case 1:
return PROTOCOL_ERROR;
case 2:
return INVALID_STREAM;
case 3:
return REFUSED_STREAM;
case 4:
return UNSUPPORTED_VERSION;
case 5:
return CANCEL;
case 6:
return INTERNAL_ERROR;
case 7:
return FLOW_CONTROL_ERROR;
}
return new SpdyStreamStatus(code, "UNKNOWN (" + code + ')');
}
private final int code;
private final String statusPhrase;
/**
* Creates a new instance with the specified {@code code} and its
* {@code statusPhrase}.
*/
public SpdyStreamStatus(int code, String statusPhrase) {
if (code == 0) {
throw new IllegalArgumentException(
"0 is not a valid status code for a RST_STREAM");
}
if (statusPhrase == null) {
throw new NullPointerException("statusPhrase");
}
this.code = code;
this.statusPhrase = statusPhrase;
}
/**
* Returns the code of this status.
*/
public int getCode() {
return code;
}
/**
* Returns the status phrase of this status.
*/
public String getStatusPhrase() {
return statusPhrase;
}
@Override
public int hashCode() {
return getCode();
}
@Override
public boolean equals(Object o) {
if (!(o instanceof SpdyStreamStatus)) {
return false;
}
return getCode() == ((SpdyStreamStatus) o).getCode();
}
@Override
public String toString() {
return getStatusPhrase();
}
public int compareTo(SpdyStreamStatus o) {
return getCode() - o.getCode();
}
}

View File

@ -1,43 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.spdy;
/**
* A SPDY Protocol SYN_REPLY Control Frame
*/
public interface SpdySynReplyFrame extends SpdyHeaderBlock {
/**
* Returns the Stream-ID of this frame.
*/
int getStreamID();
/**
* Sets the Stream-ID of this frame. The Stream-ID must be positive.
*/
void setStreamID(int streamID);
/**
* Returns {@code true} if this frame is the last frame to be transmitted
* on the stream.
*/
boolean isLast();
/**
* Sets if this frame is the last frame to be transmitted on the stream.
*/
void setLast(boolean last);
}

View File

@ -1,77 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.spdy;
/**
* A SPDY Protocol SYN_STREAM Control Frame
*/
public interface SpdySynStreamFrame extends SpdyHeaderBlock {
/**
* Returns the Stream-ID of this frame.
*/
int getStreamID();
/**
* Sets the Stream-ID of this frame. The Stream-ID must be positive.
*/
void setStreamID(int streamID);
/**
* Returns the Associated-To-Stream-ID of this frame.
*/
int getAssociatedToStreamID();
/**
* Sets the Associated-To-Stream-ID of this frame.
* The Associated-To-Stream-ID cannot be negative.
*/
void setAssociatedToStreamID(int associatedToStreamID);
/**
* Returns the priority of the stream.
*/
byte getPriority();
/**
* Sets the priority of the stream.
* The priority must be between 0 and 3 inclusive.
*/
void setPriority(byte priority);
/**
* Returns {@code true} if this frame is the last frame to be transmitted
* on the stream.
*/
boolean isLast();
/**
* Sets if this frame is the last frame to be transmitted on the stream.
*/
void setLast(boolean last);
/**
* Returns {@code true} if the stream created with this frame is to be
* considered half-closed to the receiver.
*/
boolean isUnidirectional();
/**
* Sets if the stream created with this frame is to be considered
* half-closed to the receiver.
*/
void setUnidirectional(boolean unidirectional);
}

View File

@ -1,27 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* Encoder, decoder, session handler and their related message types for the SPDY protocol.
*
* @apiviz.exclude ^java\.lang\.
* @apiviz.exclude OneToOne(Encoder|Decoder)$
* @apiviz.exclude \.SpdyHeaders\.
* @apiviz.exclude \.codec\.frame\.
* @apiviz.exclude \.(Simple)?Channel[A-Za-z]*Handler$
* @apiviz.exclude \.Default
* @apiviz.exclude \.SpdyFrameCodec$
*/
package io.netty.handler.codec.spdy;

View File

@ -1,271 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.spdy;
import static org.junit.Assert.*;
import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ChannelBuffer;
import io.netty.buffer.ChannelBuffers;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInboundHandlerContext;
import io.netty.channel.ChannelInboundMessageHandlerAdapter;
import io.netty.channel.ChannelInboundStreamHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.Random;
import java.util.concurrent.atomic.AtomicReference;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public abstract class AbstractSocketSpdyEchoTest {
private static final Random random = new Random();
static final ChannelBuffer frames = ChannelBuffers.buffer(1160);
static final int ignoredBytes = 20;
static {
// SPDY UNKNOWN Frame
frames.writeByte(0x80);
frames.writeByte(2);
frames.writeShort(0xFFFF);
frames.writeByte(0xFF);
frames.writeMedium(4);
frames.writeInt(random.nextInt());
// SPDY NOOP Frame
frames.writeByte(0x80);
frames.writeByte(2);
frames.writeShort(5);
frames.writeInt(0);
// SPDY Data Frame
frames.writeInt(random.nextInt() & 0x7FFFFFFF);
frames.writeByte(0x01);
frames.writeMedium(1024);
for (int i = 0; i < 256; i ++) {
frames.writeInt(random.nextInt());
}
// SPDY SYN_STREAM Frame
frames.writeByte(0x80);
frames.writeByte(2);
frames.writeShort(1);
frames.writeByte(0x03);
frames.writeMedium(12);
frames.writeInt(random.nextInt() & 0x7FFFFFFF);
frames.writeInt(random.nextInt() & 0x7FFFFFFF);
frames.writeShort(0x8000);
frames.writeShort(0);
// SPDY SYN_REPLY Frame
frames.writeByte(0x80);
frames.writeByte(2);
frames.writeShort(2);
frames.writeByte(0x01);
frames.writeMedium(8);
frames.writeInt(random.nextInt() & 0x7FFFFFFF);
frames.writeInt(0);
// SPDY RST_STREAM Frame
frames.writeByte(0x80);
frames.writeByte(2);
frames.writeShort(3);
frames.writeInt(8);
frames.writeInt(random.nextInt() & 0x7FFFFFFF);
frames.writeInt(random.nextInt() | 0x01);
// SPDY SETTINGS Frame
frames.writeByte(0x80);
frames.writeByte(2);
frames.writeShort(4);
frames.writeByte(0x01);
frames.writeMedium(12);
frames.writeInt(1);
frames.writeMedium(random.nextInt());
frames.writeByte(0x03);
frames.writeInt(random.nextInt());
// SPDY PING Frame
frames.writeByte(0x80);
frames.writeByte(2);
frames.writeShort(6);
frames.writeInt(4);
frames.writeInt(random.nextInt());
// SPDY GOAWAY Frame
frames.writeByte(0x80);
frames.writeByte(2);
frames.writeShort(7);
frames.writeInt(4);
frames.writeInt(random.nextInt() & 0x7FFFFFFF);
// SPDY HEADERS Frame
frames.writeByte(0x80);
frames.writeByte(2);
frames.writeShort(8);
frames.writeInt(4);
frames.writeInt(random.nextInt() & 0x7FFFFFFF);
}
private ServerBootstrap sb;
private Bootstrap cb;
@Before
public void initBootstrap() {
sb = newServerBootstrap();
cb = newClientBootstrap();
}
@After
public void destroyBootstrap() {
sb.shutdown();
cb.shutdown();
}
protected abstract ServerBootstrap newServerBootstrap();
protected abstract Bootstrap newClientBootstrap();
@Test(timeout = 10000)
public void testSpdyEcho() throws Throwable {
final ServerHandler sh = new ServerHandler();
final ClientHandler ch = new ClientHandler();
sb.childInitializer(new ChannelInitializer() {
@Override
public void initChannel(Channel ch) throws Exception {
ch.pipeline().addLast(
new SpdyFrameDecoder(),
new SpdyFrameEncoder(),
sh);
}
});
cb.initializer(new ChannelInitializer() {
@Override
public void initChannel(Channel channel) throws Exception {
channel.pipeline().addLast(ch);
}
});
Channel sc = sb.localAddress(new InetSocketAddress(0)).bind().sync().channel();
int port = ((InetSocketAddress) sc.localAddress()).getPort();
ChannelFuture ccf = cb.remoteAddress(new InetSocketAddress(InetAddress.getLocalHost(), port)).connect();
assertTrue(ccf.awaitUninterruptibly().isSuccess());
Channel cc = ccf.channel();
cc.write(frames);
while (ch.counter < frames.writerIndex() - ignoredBytes) {
if (sh.exception.get() != null) {
break;
}
if (ch.exception.get() != null) {
break;
}
try {
Thread.sleep(1);
} catch (InterruptedException e) {
// Ignore.
}
}
sh.channel.close().awaitUninterruptibly();
ch.channel.close().awaitUninterruptibly();
sc.close().awaitUninterruptibly();
if (sh.exception.get() != null && !(sh.exception.get() instanceof IOException)) {
throw sh.exception.get();
}
if (ch.exception.get() != null && !(ch.exception.get() instanceof IOException)) {
throw ch.exception.get();
}
if (sh.exception.get() != null) {
throw sh.exception.get();
}
if (ch.exception.get() != null) {
throw ch.exception.get();
}
}
private class ServerHandler extends ChannelInboundMessageHandlerAdapter<Object> {
volatile Channel channel;
final AtomicReference<Throwable> exception = new AtomicReference<Throwable>();
@Override
public void channelRegistered(ChannelInboundHandlerContext<Object> ctx)
throws Exception {
channel = ctx.channel();
}
@Override
public void messageReceived(ChannelInboundHandlerContext<Object> ctx, Object msg)
throws Exception {
ctx.write(msg);
}
@Override
public void exceptionCaught(ChannelInboundHandlerContext<Object> ctx,
Throwable cause) throws Exception {
if (exception.compareAndSet(null, cause)) {
ctx.close();
}
}
}
private class ClientHandler extends ChannelInboundStreamHandlerAdapter {
volatile Channel channel;
final AtomicReference<Throwable> exception = new AtomicReference<Throwable>();
volatile int counter;
@Override
public void channelRegistered(ChannelInboundHandlerContext<Byte> ctx)
throws Exception {
channel = ctx.channel();
}
@Override
public void inboundBufferUpdated(ChannelInboundHandlerContext<Byte> ctx)
throws Exception {
ChannelBuffer m = ctx.inbound().byteBuffer().readBytes(ctx.inbound().byteBuffer().readableBytes());
byte[] actual = new byte[m.readableBytes()];
m.getBytes(0, actual);
int lastIdx = counter;
for (int i = 0; i < actual.length; i ++) {
assertEquals(frames.getByte(ignoredBytes + i + lastIdx), actual[i]);
}
counter += actual.length;
}
@Override
public void exceptionCaught(ChannelInboundHandlerContext<Byte> ctx,
Throwable cause) throws Exception {
if (exception.compareAndSet(null, cause)) {
ctx.close();
}
}
}
}

View File

@ -1,40 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.spdy;
import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.channel.socket.nio.NioEventLoop;
public class NioNioSocketSpdyEchoTest extends AbstractSocketSpdyEchoTest {
@Override
protected Bootstrap newClientBootstrap() {
return new Bootstrap()
.eventLoop(new NioEventLoop())
.channel(new NioSocketChannel());
}
@Override
protected ServerBootstrap newServerBootstrap() {
return new ServerBootstrap()
.eventLoop(new NioEventLoop(), new NioEventLoop())
.channel(new NioServerSocketChannel());
}
}

View File

@ -1,41 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.spdy;
import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.channel.socket.nio.NioEventLoop;
import io.netty.channel.socket.oio.OioEventLoop;
import io.netty.channel.socket.oio.OioServerSocketChannel;
public class NioOioSocketSpdyEchoTest extends AbstractSocketSpdyEchoTest {
@Override
protected Bootstrap newClientBootstrap() {
return new Bootstrap()
.eventLoop(new NioEventLoop())
.channel(new NioSocketChannel());
}
@Override
protected ServerBootstrap newServerBootstrap() {
return new ServerBootstrap()
.eventLoop(new OioEventLoop(), new OioEventLoop())
.channel(new OioServerSocketChannel());
}
}

View File

@ -1,41 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.spdy;
import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioEventLoop;
import io.netty.channel.socket.oio.OioEventLoop;
import io.netty.channel.socket.oio.OioSocketChannel;
public class OioNioSocketSpdyEchoTest extends AbstractSocketSpdyEchoTest {
@Override
protected Bootstrap newClientBootstrap() {
return new Bootstrap()
.eventLoop(new OioEventLoop())
.channel(new OioSocketChannel());
}
@Override
protected ServerBootstrap newServerBootstrap() {
return new ServerBootstrap()
.eventLoop(new NioEventLoop(), new NioEventLoop())
.channel(new NioServerSocketChannel());
}
}

View File

@ -1,40 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.spdy;
import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.socket.oio.OioEventLoop;
import io.netty.channel.socket.oio.OioServerSocketChannel;
import io.netty.channel.socket.oio.OioSocketChannel;
public class OioOioSocketSpdyEchoTest extends AbstractSocketSpdyEchoTest {
@Override
protected Bootstrap newClientBootstrap() {
return new Bootstrap()
.eventLoop(new OioEventLoop())
.channel(new OioSocketChannel());
}
@Override
protected ServerBootstrap newServerBootstrap() {
return new ServerBootstrap()
.eventLoop(new OioEventLoop(), new OioEventLoop())
.channel(new OioServerSocketChannel());
}
}

View File

@ -1,334 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.spdy;
import io.netty.channel.ChannelInboundHandlerContext;
import io.netty.channel.ChannelInboundMessageHandlerAdapter;
import io.netty.handler.codec.embedder.DecoderEmbedder;
import java.util.List;
import java.util.Map;
import org.junit.Assert;
import org.junit.Test;
public class SpdySessionHandlerTest {
private static final int closeSignal = SpdyCodecUtil.SPDY_SETTINGS_MAX_ID;
private static final SpdySettingsFrame closeMessage = new DefaultSpdySettingsFrame();
static {
closeMessage.setValue(closeSignal, 0);
}
private static void assertHeaderBlock(SpdyHeaderBlock received, SpdyHeaderBlock expected) {
for (String name: expected.getHeaderNames()) {
List<String> expectedValues = expected.getHeaders(name);
List<String> receivedValues = received.getHeaders(name);
Assert.assertTrue(receivedValues.containsAll(expectedValues));
receivedValues.removeAll(expectedValues);
Assert.assertTrue(receivedValues.isEmpty());
received.removeHeader(name);
}
Assert.assertTrue(received.getHeaders().isEmpty());
}
private static void assertDataFrame(Object msg, int streamID, boolean last) {
Assert.assertNotNull(msg);
Assert.assertTrue(msg instanceof SpdyDataFrame);
SpdyDataFrame spdyDataFrame = (SpdyDataFrame) msg;
Assert.assertTrue(spdyDataFrame.getStreamID() == streamID);
Assert.assertTrue(spdyDataFrame.isLast() == last);
}
private static void assertSynReply(Object msg, int streamID, boolean last, SpdyHeaderBlock headers) {
Assert.assertNotNull(msg);
Assert.assertTrue(msg instanceof SpdySynReplyFrame);
SpdySynReplyFrame spdySynReplyFrame = (SpdySynReplyFrame) msg;
Assert.assertTrue(spdySynReplyFrame.getStreamID() == streamID);
Assert.assertTrue(spdySynReplyFrame.isLast() == last);
assertHeaderBlock(spdySynReplyFrame, headers);
}
private static void assertRstStream(Object msg, int streamID, SpdyStreamStatus status) {
Assert.assertNotNull(msg);
Assert.assertTrue(msg instanceof SpdyRstStreamFrame);
SpdyRstStreamFrame spdyRstStreamFrame = (SpdyRstStreamFrame) msg;
Assert.assertTrue(spdyRstStreamFrame.getStreamID() == streamID);
Assert.assertTrue(spdyRstStreamFrame.getStatus().equals(status));
}
private static void assertPing(Object msg, int ID) {
Assert.assertNotNull(msg);
Assert.assertTrue(msg instanceof SpdyPingFrame);
SpdyPingFrame spdyPingFrame = (SpdyPingFrame) msg;
Assert.assertTrue(spdyPingFrame.getID() == ID);
}
private static void assertGoAway(Object msg, int lastGoodStreamID) {
Assert.assertNotNull(msg);
Assert.assertTrue(msg instanceof SpdyGoAwayFrame);
SpdyGoAwayFrame spdyGoAwayFrame = (SpdyGoAwayFrame) msg;
Assert.assertTrue(spdyGoAwayFrame.getLastGoodStreamID() == lastGoodStreamID);
}
private static void assertHeaders(Object msg, int streamID, SpdyHeaderBlock headers) {
Assert.assertNotNull(msg);
Assert.assertTrue(msg instanceof SpdyHeadersFrame);
SpdyHeadersFrame spdyHeadersFrame = (SpdyHeadersFrame) msg;
Assert.assertTrue(spdyHeadersFrame.getStreamID() == streamID);
assertHeaderBlock(spdyHeadersFrame, headers);
}
private void testSpdySessionHandler(boolean server) {
DecoderEmbedder<Object> sessionHandler =
new DecoderEmbedder<Object>(
new SpdySessionHandler(server), new EchoHandler(closeSignal, server));
sessionHandler.pollAll();
int localStreamID = server ? 1 : 2;
int remoteStreamID = server ? 2 : 1;
SpdyPingFrame localPingFrame = new DefaultSpdyPingFrame(localStreamID);
SpdyPingFrame remotePingFrame = new DefaultSpdyPingFrame(remoteStreamID);
SpdySynStreamFrame spdySynStreamFrame =
new DefaultSpdySynStreamFrame(localStreamID, 0, (byte) 0);
spdySynStreamFrame.setHeader("Compression", "test");
SpdyDataFrame spdyDataFrame = new DefaultSpdyDataFrame(localStreamID);
spdyDataFrame.setLast(true);
// Check if session handler returns INVALID_STREAM if it receives
// a data frame for a Stream-ID that is not open
sessionHandler.offer(new DefaultSpdyDataFrame(localStreamID));
assertRstStream(sessionHandler.poll(), localStreamID, SpdyStreamStatus.INVALID_STREAM);
Assert.assertNull(sessionHandler.peek());
// Check if session handler returns PROTOCOL_ERROR if it receives
// a data frame for a Stream-ID before receiving a SYN_REPLY frame
sessionHandler.offer(new DefaultSpdyDataFrame(remoteStreamID));
assertRstStream(sessionHandler.poll(), remoteStreamID, SpdyStreamStatus.PROTOCOL_ERROR);
Assert.assertNull(sessionHandler.peek());
remoteStreamID += 2;
// Check if session handler returns PROTOCOL_ERROR if it receives
// multiple SYN_REPLY frames for the same active Stream-ID
sessionHandler.offer(new DefaultSpdySynReplyFrame(remoteStreamID));
Assert.assertNull(sessionHandler.peek());
sessionHandler.offer(new DefaultSpdySynReplyFrame(remoteStreamID));
assertRstStream(sessionHandler.poll(), remoteStreamID, SpdyStreamStatus.PROTOCOL_ERROR);
Assert.assertNull(sessionHandler.peek());
remoteStreamID += 2;
// Check if frame codec correctly compresses/uncompresses headers
sessionHandler.offer(spdySynStreamFrame);
assertSynReply(sessionHandler.poll(), localStreamID, false, spdySynStreamFrame);
Assert.assertNull(sessionHandler.peek());
SpdyHeadersFrame spdyHeadersFrame = new DefaultSpdyHeadersFrame(localStreamID);
spdyHeadersFrame.addHeader("HEADER","test1");
spdyHeadersFrame.addHeader("HEADER","test2");
sessionHandler.offer(spdyHeadersFrame);
assertHeaders(sessionHandler.poll(), localStreamID, spdyHeadersFrame);
Assert.assertNull(sessionHandler.peek());
localStreamID += 2;
// Check if session handler closed the streams using the number
// of concurrent streams and that it returns REFUSED_STREAM
// if it receives a SYN_STREAM frame it does not wish to accept
spdySynStreamFrame.setStreamID(localStreamID);
spdySynStreamFrame.setLast(true);
spdySynStreamFrame.setUnidirectional(true);
sessionHandler.offer(spdySynStreamFrame);
assertRstStream(sessionHandler.poll(), localStreamID, SpdyStreamStatus.REFUSED_STREAM);
Assert.assertNull(sessionHandler.peek());
// Check if session handler drops active streams if it receives
// a RST_STREAM frame for that Stream-ID
sessionHandler.offer(new DefaultSpdyRstStreamFrame(remoteStreamID, 3));
Assert.assertNull(sessionHandler.peek());
remoteStreamID += 2;
// Check if session handler honors UNIDIRECTIONAL streams
spdySynStreamFrame.setLast(false);
sessionHandler.offer(spdySynStreamFrame);
try {
sessionHandler.poll();
Assert.fail();
} catch (SpdyProtocolException e) {
// Expected
}
spdySynStreamFrame.setUnidirectional(false);
// Check if session handler returns PROTOCOL_ERROR if it receives
// multiple SYN_STREAM frames for the same active Stream-ID
sessionHandler.offer(spdySynStreamFrame);
assertRstStream(sessionHandler.poll(), localStreamID, SpdyStreamStatus.PROTOCOL_ERROR);
Assert.assertNull(sessionHandler.peek());
localStreamID += 2;
// Check if session handler returns PROTOCOL_ERROR if it receives
// a SYN_STREAM frame with an invalid Stream-ID
spdySynStreamFrame.setStreamID(localStreamID - 1);
sessionHandler.offer(spdySynStreamFrame);
assertRstStream(sessionHandler.poll(), localStreamID - 1, SpdyStreamStatus.PROTOCOL_ERROR);
Assert.assertNull(sessionHandler.peek());
spdySynStreamFrame.setStreamID(localStreamID);
// Check if session handler correctly limits the number of
// concurrent streams in the SETTINGS frame
SpdySettingsFrame spdySettingsFrame = new DefaultSpdySettingsFrame();
spdySettingsFrame.setValue(SpdySettingsFrame.SETTINGS_MAX_CONCURRENT_STREAMS, 2);
sessionHandler.offer(spdySettingsFrame);
Assert.assertNull(sessionHandler.peek());
sessionHandler.offer(spdySynStreamFrame);
assertRstStream(sessionHandler.poll(), localStreamID, SpdyStreamStatus.REFUSED_STREAM);
Assert.assertNull(sessionHandler.peek());
spdySettingsFrame.setValue(SpdySettingsFrame.SETTINGS_MAX_CONCURRENT_STREAMS, 4);
sessionHandler.offer(spdySettingsFrame);
Assert.assertNull(sessionHandler.peek());
sessionHandler.offer(spdySynStreamFrame);
assertSynReply(sessionHandler.poll(), localStreamID, false, spdySynStreamFrame);
Assert.assertNull(sessionHandler.peek());
// Check if session handler rejects HEADERS for closed streams
int testStreamID = spdyDataFrame.getStreamID();
sessionHandler.offer(spdyDataFrame);
assertDataFrame(sessionHandler.poll(), testStreamID, spdyDataFrame.isLast());
Assert.assertNull(sessionHandler.peek());
spdyHeadersFrame.setStreamID(testStreamID);
sessionHandler.offer(spdyHeadersFrame);
assertRstStream(sessionHandler.poll(), testStreamID, SpdyStreamStatus.INVALID_STREAM);
Assert.assertNull(sessionHandler.peek());
// Check if session handler returns PROTOCOL_ERROR if it receives
// an invalid HEADERS frame
spdyHeadersFrame.setStreamID(localStreamID);
spdyHeadersFrame.setInvalid();
sessionHandler.offer(spdyHeadersFrame);
assertRstStream(sessionHandler.poll(), localStreamID, SpdyStreamStatus.PROTOCOL_ERROR);
Assert.assertNull(sessionHandler.peek());
// Check if session handler returns identical local PINGs
sessionHandler.offer(localPingFrame);
assertPing(sessionHandler.poll(), localPingFrame.getID());
Assert.assertNull(sessionHandler.peek());
// Check if session handler ignores un-initiated remote PINGs
sessionHandler.offer(remotePingFrame);
Assert.assertNull(sessionHandler.peek());
// Check if session handler sends a GOAWAY frame when closing
sessionHandler.offer(closeMessage);
assertGoAway(sessionHandler.poll(), localStreamID);
Assert.assertNull(sessionHandler.peek());
localStreamID += 2;
// Check if session handler returns REFUSED_STREAM if it receives
// SYN_STREAM frames after sending a GOAWAY frame
spdySynStreamFrame.setStreamID(localStreamID);
sessionHandler.offer(spdySynStreamFrame);
assertRstStream(sessionHandler.poll(), localStreamID, SpdyStreamStatus.REFUSED_STREAM);
Assert.assertNull(sessionHandler.peek());
// Check if session handler ignores Data frames after sending
// a GOAWAY frame
spdyDataFrame.setStreamID(localStreamID);
sessionHandler.offer(spdyDataFrame);
Assert.assertNull(sessionHandler.peek());
sessionHandler.finish();
}
@Test
public void testSpdyClientSessionHandler() {
testSpdySessionHandler(false);
}
@Test
public void testSpdyServerSessionHandler() {
testSpdySessionHandler(true);
}
// Echo Handler opens 4 half-closed streams on session connection
// and then sets the number of concurrent streams to 3
private class EchoHandler extends ChannelInboundMessageHandlerAdapter<Object> {
private final int closeSignal;
private final boolean server;
EchoHandler(int closeSignal, boolean server) {
super();
this.closeSignal = closeSignal;
this.server = server;
}
@Override
public void channelActive(ChannelInboundHandlerContext<Object> ctx)
throws Exception {
// Initiate 4 new streams
int streamID = server ? 2 : 1;
SpdySynStreamFrame spdySynStreamFrame =
new DefaultSpdySynStreamFrame(streamID, 0, (byte) 0);
spdySynStreamFrame.setLast(true);
ctx.write(spdySynStreamFrame);
spdySynStreamFrame.setStreamID(spdySynStreamFrame.getStreamID() + 2);
ctx.write(spdySynStreamFrame);
spdySynStreamFrame.setStreamID(spdySynStreamFrame.getStreamID() + 2);
ctx.write(spdySynStreamFrame);
spdySynStreamFrame.setStreamID(spdySynStreamFrame.getStreamID() + 2);
ctx.write(spdySynStreamFrame);
// Limit the number of concurrent streams to 3
SpdySettingsFrame spdySettingsFrame = new DefaultSpdySettingsFrame();
spdySettingsFrame.setValue(SpdySettingsFrame.SETTINGS_MAX_CONCURRENT_STREAMS, 3);
ctx.write(spdySettingsFrame);
}
@Override
public void messageReceived(ChannelInboundHandlerContext<Object> ctx, Object msg) throws Exception {
if (msg instanceof SpdyDataFrame ||
msg instanceof SpdyPingFrame ||
msg instanceof SpdyHeadersFrame) {
ctx.write(msg);
return;
}
if (msg instanceof SpdySynStreamFrame) {
SpdySynStreamFrame spdySynStreamFrame = (SpdySynStreamFrame) msg;
int streamID = spdySynStreamFrame.getStreamID();
SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamID);
spdySynReplyFrame.setLast(spdySynStreamFrame.isLast());
for (Map.Entry<String, String> entry: spdySynStreamFrame.getHeaders()) {
spdySynReplyFrame.addHeader(entry.getKey(), entry.getValue());
}
ctx.write(spdySynReplyFrame);
return;
}
if (msg instanceof SpdySettingsFrame) {
SpdySettingsFrame spdySettingsFrame = (SpdySettingsFrame) msg;
if (spdySettingsFrame.isSet(closeSignal)) {
ctx.close();
}
}
}
}
}

View File

@ -1,75 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.marshalling;
import java.io.IOException;
import org.jboss.marshalling.ByteInput;
import io.netty.buffer.ChannelBuffer;
/**
* {@link ByteInput} implementation which reads its data from a {@link ChannelBuffer}
*
*
*/
class ChannelBufferByteInput implements ByteInput {
private final ChannelBuffer buffer;
public ChannelBufferByteInput(ChannelBuffer buffer) {
this.buffer = buffer;
}
public void close() throws IOException {
// nothing to do
}
public int available() throws IOException {
return buffer.readableBytes();
}
public int read() throws IOException {
if (buffer.readable()) {
return buffer.readByte() & 0xff;
}
return -1;
}
public int read(byte[] array) throws IOException {
return read(array, 0, array.length);
}
public int read(byte[] dst, int dstIndex, int length) throws IOException {
int available = available();
if (available == 0) {
return -1;
}
length = Math.min(available, length);
buffer.readBytes(dst, dstIndex, length);
return length;
}
public long skip(long bytes) throws IOException {
int readable = buffer.readableBytes();
if (readable < bytes) {
bytes = readable;
}
buffer.readerIndex((int) (buffer.readerIndex() + bytes));
return bytes;
}
}

View File

@ -1,77 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.marshalling;
import java.io.IOException;
import org.jboss.marshalling.ByteOutput;
import io.netty.buffer.ChannelBuffer;
import io.netty.buffer.ChannelBufferFactory;
import io.netty.buffer.ChannelBuffers;
/**
* {@link ByteOutput} implementation which writes the data to a {@link ChannelBuffer}
*
*
*/
class ChannelBufferByteOutput implements ByteOutput {
private final ChannelBuffer buffer;
/**
* Create a new instance which use the given {@link ChannelBuffer}
*/
public ChannelBufferByteOutput(ChannelBuffer buffer) {
this.buffer = buffer;
}
/**
* Calls {@link #ChannelBufferByteOutput(ChannelBuffer)} with a dynamic {@link ChannelBuffer}
*/
public ChannelBufferByteOutput(ChannelBufferFactory factory, int estimatedLength) {
this(ChannelBuffers.dynamicBuffer(estimatedLength, factory));
}
public void close() throws IOException {
// Nothing todo
}
public void flush() throws IOException {
// nothing to do
}
public void write(int b) throws IOException {
buffer.writeByte(b);
}
public void write(byte[] bytes) throws IOException {
buffer.writeBytes(bytes);
}
public void write(byte[] bytes, int srcIndex, int length) throws IOException {
buffer.writeBytes(bytes, srcIndex, length);
}
/**
* Return the {@link ChannelBuffer} which contains the written content
*
*/
public ChannelBuffer getBuffer() {
return buffer;
}
}

View File

@ -1,108 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.marshalling;
import java.io.IOException;
import org.jboss.marshalling.ByteInput;
/**
* {@link ByteInput} implementation which wraps another {@link ByteInput} and throws a {@link TooBigObjectException}
* if the read limit was reached.
*
*
*/
class LimitingByteInput implements ByteInput {
// Use a static instance here to remove the overhead of fillStacktrace
private static final TooBigObjectException EXCEPTION = new TooBigObjectException();
private final ByteInput input;
private final long limit;
private long read;
public LimitingByteInput(ByteInput input, long limit) {
if (limit <= 0) {
throw new IllegalArgumentException("The limit MUST be > 0");
}
this.input = input;
this.limit = limit;
}
public void close() throws IOException {
// Nothing todo
}
public int available() throws IOException {
int available = input.available();
int readable = readable(available);
return readable;
}
public int read() throws IOException {
int readable = readable(1);
if (readable > 0) {
int b = input.read();
read++;
return b;
} else {
throw EXCEPTION;
}
}
public int read(byte[] array) throws IOException {
return read(array, 0, array.length);
}
public int read(byte[] array, int offset, int length) throws IOException {
int readable = readable(length);
if (readable > 0) {
int i = input.read(array, offset, readable);
read += i;
return i;
} else {
throw EXCEPTION;
}
}
public long skip(long bytes) throws IOException {
int readable = readable((int) bytes);
if (readable > 0) {
long i = input.skip(readable);
read += i;
return i;
} else {
throw EXCEPTION;
}
}
private int readable(int length) {
return (int) Math.min(length, limit - read);
}
/**
* Exception that will get thrown if the {@link Object} is to big to unmarshall
*
*/
static final class TooBigObjectException extends IOException {
/**
*
*/
private static final long serialVersionUID = 1L;
}
}

View File

@ -1,122 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.marshalling;
import java.io.IOException;
import java.io.ObjectStreamConstants;
import org.jboss.marshalling.ByteInput;
import org.jboss.marshalling.MarshallerFactory;
import org.jboss.marshalling.MarshallingConfiguration;
import org.jboss.marshalling.Unmarshaller;
import io.netty.buffer.ChannelBuffer;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ExceptionEvent;
import io.netty.handler.codec.frame.TooLongFrameException;
import io.netty.handler.codec.replay.ReplayingDecoder;
import io.netty.handler.codec.replay.VoidEnum;
/**
* {@link ReplayingDecoder} which use an {@link Unmarshaller} to read the Object out of the {@link ChannelBuffer}.
*
* Most times you want to use {@link ThreadLocalMarshallingDecoder} to get a better performance and less overhead.
*
*
*/
public class MarshallingDecoder extends ReplayingDecoder<VoidEnum> {
protected final MarshallingConfiguration config;
protected final MarshallerFactory factory;
protected final long maxObjectSize;
/**
* Create a new instance of {@link MarshallingDecoder}.
*
* @param factory the {@link MarshallerFactory} which is used to obtain the {@link Unmarshaller} from
* @param config the {@link MarshallingConfiguration} to use
* @param maxObjectSize the maximal size (in bytes) of the {@link Object} to unmarshal. Once the size is exceeded
* the {@link Channel} will get closed. Use a a maxObjectSize of <= 0 to disable this.
* You should only do this if you are sure that the received Objects will never be big and the
* sending side are trusted, as this opens the possibility for a DOS-Attack due an {@link OutOfMemoryError}.
*
*/
public MarshallingDecoder(MarshallerFactory factory, MarshallingConfiguration config, long maxObjectSize) {
this.factory = factory;
this.config = config;
this.maxObjectSize = maxObjectSize;
}
@Override
protected Object decode(ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer, VoidEnum state) throws Exception {
Unmarshaller unmarshaller = factory.createUnmarshaller(config);
ByteInput input = new ChannelBufferByteInput(buffer);
if (maxObjectSize > 0) {
input = new LimitingByteInput(input, maxObjectSize);
}
try {
unmarshaller.start(input);
Object obj = unmarshaller.readObject();
unmarshaller.finish();
return obj;
} catch (LimitingByteInput.TooBigObjectException e) {
throw new TooLongFrameException("Object to big to unmarshal");
} finally {
// Call close in a finally block as the ReplayingDecoder will throw an Error if not enough bytes are
// readable. This helps to be sure that we do not leak resource
unmarshaller.close();
}
}
@Override
protected Object decodeLast(ChannelHandlerContext ctx, Channel channel,
ChannelBuffer buffer, VoidEnum state)
throws Exception {
switch (buffer.readableBytes()) {
case 0:
return null;
case 1:
// Ignore the last TC_RESET
if (buffer.getByte(buffer.readerIndex()) == ObjectStreamConstants.TC_RESET) {
buffer.skipBytes(1);
return null;
}
}
Object decoded = decode(ctx, channel, buffer, state);
return decoded;
}
/**
* Calls {@link Channel#close()} if a TooLongFrameException was thrown
*/
@Override
public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
if (e.getCause() instanceof TooLongFrameException) {
e.getChannel().close();
} else {
super.exceptionCaught(ctx, e);
}
}
/**
* Create a new {@link Unmarshaller} for the given {@link Channel}
*
*/
protected Unmarshaller getUnmarshaller(Channel channel) throws IOException {
return factory.createUnmarshaller(config);
}
}

View File

@ -1,59 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.marshalling;
import org.jboss.marshalling.Marshaller;
import org.jboss.marshalling.MarshallerFactory;
import org.jboss.marshalling.MarshallingConfiguration;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.handler.codec.oneone.OneToOneEncoder;
/**
* {@link OneToOneEncoder} implementation which uses JBoss Marshalling to marshal
* an Object.
*
* See <a href="http://www.jboss.org/jbossmarshalling">JBoss Marshalling website</a>
* for more informations
*
*/
@Sharable
public class MarshallingEncoder extends OneToOneEncoder {
private final MarshallerFactory factory;
private final MarshallingConfiguration config;
public MarshallingEncoder(MarshallerFactory factory, MarshallingConfiguration config) {
this.factory = factory;
this.config = config;
}
@Override
protected Object encode(ChannelHandlerContext ctx, Channel channel, Object msg) throws Exception {
Marshaller marshaller = factory.createMarshaller(config);
ChannelBufferByteOutput output = new ChannelBufferByteOutput(ctx.getChannel().getConfig().getBufferFactory(), 256);
marshaller.start(output);
marshaller.writeObject(msg);
marshaller.finish();
marshaller.close();
return output.getBuffer();
}
}

View File

@ -1,52 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.marshalling;
import java.io.IOException;
import org.jboss.marshalling.MarshallerFactory;
import org.jboss.marshalling.MarshallingConfiguration;
import org.jboss.marshalling.Unmarshaller;
import io.netty.channel.Channel;
/**
* A subclass of {@link MarshallingDecoder} which use one {@link Unmarshaller} per Thread via a {@link ThreadLocal}.
*
* For more informations see {@link MarshallingDecoder}.
*
*/
public class ThreadLocalMarshallingDecoder extends MarshallingDecoder {
private static final ThreadLocal<Unmarshaller> UNMARSHALLERS = new ThreadLocal<Unmarshaller>();
/**
* See {@link MarshallingDecoder#MarshallingDecoder(MarshallerFactory, MarshallingConfiguration, long)}
*/
public ThreadLocalMarshallingDecoder(MarshallerFactory factory, MarshallingConfiguration config, long maxObjectSize) {
super(factory, config, maxObjectSize);
}
@Override
protected Unmarshaller getUnmarshaller(Channel channel) throws IOException {
Unmarshaller unmarshaller = UNMARSHALLERS.get();
if (unmarshaller == null) {
unmarshaller = factory.createUnmarshaller(config);
UNMARSHALLERS.set(unmarshaller);
}
return unmarshaller;
}
}

View File

@ -1,21 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
/**
* Decoder and Encoder which uses <a href="http://www.jboss.org/jbossmarshalling">JBoss Marshalling</a>.
*
*/
package io.netty.handler.codec.marshalling;

View File

@ -1,135 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.marshalling;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import junit.framework.Assert;
import org.jboss.marshalling.Marshaller;
import org.jboss.marshalling.MarshallerFactory;
import org.jboss.marshalling.Marshalling;
import org.jboss.marshalling.MarshallingConfiguration;
import io.netty.buffer.ChannelBuffer;
import io.netty.buffer.ChannelBuffers;
import io.netty.handler.codec.embedder.CodecEmbedderException;
import io.netty.handler.codec.embedder.DecoderEmbedder;
import io.netty.handler.codec.frame.TooLongFrameException;
import org.junit.Test;
public abstract class AbstractMarshallingDecoderTest {
private final String testObject = new String("test");
@Test
public void testSimpleUnmarshalling() throws IOException {
MarshallerFactory marshallerFactory = createMarshallerFactory();
MarshallingConfiguration configuration = createMarshallingConfig();
DecoderEmbedder<Object> decoder = new DecoderEmbedder<Object>(createDecoder(marshallerFactory, configuration, 0));
ByteArrayOutputStream bout = new ByteArrayOutputStream();
Marshaller marshaller = marshallerFactory.createMarshaller(configuration);
marshaller.start(Marshalling.createByteOutput(bout));
marshaller.writeObject(testObject);
marshaller.finish();
marshaller.close();
byte[] testBytes = bout.toByteArray();
decoder.offer(ChannelBuffers.wrappedBuffer(testBytes));
assertTrue(decoder.finish());
String unmarshalled = (String) decoder.poll();
Assert.assertEquals(testObject, unmarshalled);
Assert.assertNull(decoder.poll());
}
@Test
public void testFragmentedUnmarshalling() throws IOException {
MarshallerFactory marshallerFactory = createMarshallerFactory();
MarshallingConfiguration configuration = createMarshallingConfig();
DecoderEmbedder<Object> decoder = new DecoderEmbedder<Object>(createDecoder(marshallerFactory, configuration, 0));
ByteArrayOutputStream bout = new ByteArrayOutputStream();
Marshaller marshaller = marshallerFactory.createMarshaller(configuration);
marshaller.start(Marshalling.createByteOutput(bout));
marshaller.writeObject(testObject);
marshaller.finish();
marshaller.close();
byte[] testBytes = bout.toByteArray();
ChannelBuffer buffer = ChannelBuffers.wrappedBuffer(testBytes);
ChannelBuffer slice = buffer.readSlice(2);
decoder.offer(slice);
decoder.offer(buffer);
assertTrue(decoder.finish());
String unmarshalled = (String) decoder.poll();
Assert.assertEquals(testObject, unmarshalled);
Assert.assertNull(decoder.poll());
}
@Test
public void testTooBigObject() throws IOException {
MarshallerFactory marshallerFactory = createMarshallerFactory();
MarshallingConfiguration configuration = createMarshallingConfig();
MarshallingDecoder mDecoder = createDecoder(marshallerFactory, configuration, 1);
DecoderEmbedder<Object> decoder = new DecoderEmbedder<Object>(mDecoder);
ByteArrayOutputStream bout = new ByteArrayOutputStream();
Marshaller marshaller = marshallerFactory.createMarshaller(configuration);
marshaller.start(Marshalling.createByteOutput(bout));
marshaller.writeObject(testObject);
marshaller.finish();
marshaller.close();
byte[] testBytes = bout.toByteArray();
try {
decoder.offer(ChannelBuffers.wrappedBuffer(testBytes));
fail();
} catch (CodecEmbedderException e) {
assertEquals(TooLongFrameException.class, e.getCause().getClass());
}
}
protected MarshallingDecoder createDecoder(MarshallerFactory factory, MarshallingConfiguration config, long maxObjectSize) {
return new MarshallingDecoder(factory, config, maxObjectSize);
}
protected abstract MarshallerFactory createMarshallerFactory();
protected abstract MarshallingConfiguration createMarshallingConfig();
}

View File

@ -1,63 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.marshalling;
import java.io.IOException;
import junit.framework.Assert;
import org.jboss.marshalling.MarshallerFactory;
import org.jboss.marshalling.Marshalling;
import org.jboss.marshalling.MarshallingConfiguration;
import org.jboss.marshalling.Unmarshaller;
import io.netty.buffer.ChannelBuffer;
import io.netty.handler.codec.embedder.EncoderEmbedder;
import org.junit.Test;
public abstract class AbstractMarshallingEncoderTest {
@Test
public void testMarshalling() throws IOException, ClassNotFoundException {
String testObject = new String("test");
final MarshallerFactory marshallerFactory = createMarshallerFactory();
final MarshallingConfiguration configuration = createMarshallingConfig();
EncoderEmbedder<ChannelBuffer> encoder = new EncoderEmbedder<ChannelBuffer>(new MarshallingEncoder(marshallerFactory, configuration));
encoder.offer(testObject);
Assert.assertTrue(encoder.finish());
ChannelBuffer buffer = encoder.poll();
Unmarshaller unmarshaller = marshallerFactory.createUnmarshaller(configuration);
unmarshaller.start(Marshalling.createByteInput(buffer.toByteBuffer()));
String read = (String) unmarshaller.readObject();
Assert.assertEquals(testObject, read);
Assert.assertEquals(-1, unmarshaller.read());
Assert.assertNull(encoder.poll());
unmarshaller.finish();
unmarshaller.close();
}
protected abstract MarshallerFactory createMarshallerFactory();
protected abstract MarshallingConfiguration createMarshallingConfig();
}

View File

@ -1,37 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.marshalling;
import org.jboss.marshalling.MarshallerFactory;
import org.jboss.marshalling.Marshalling;
import org.jboss.marshalling.MarshallingConfiguration;
public class RiverMarshallingDecoderTest extends AbstractMarshallingDecoderTest {
@Override
protected MarshallerFactory createMarshallerFactory() {
return Marshalling.getProvidedMarshallerFactory("river");
}
@Override
protected MarshallingConfiguration createMarshallingConfig() {
// Create a configuration
final MarshallingConfiguration configuration = new MarshallingConfiguration();
configuration.setVersion(3);
return configuration;
}
}

View File

@ -1,38 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.marshalling;
import org.jboss.marshalling.MarshallerFactory;
import org.jboss.marshalling.Marshalling;
import org.jboss.marshalling.MarshallingConfiguration;
public class RiverMarshallingEncoderTest extends AbstractMarshallingEncoderTest {
@Override
protected MarshallerFactory createMarshallerFactory() {
return Marshalling.getProvidedMarshallerFactory("river");
}
@Override
protected MarshallingConfiguration createMarshallingConfig() {
// Create a configuration
final MarshallingConfiguration configuration = new MarshallingConfiguration();
configuration.setVersion(3);
return configuration;
}
}

View File

@ -1,27 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.marshalling;
import org.jboss.marshalling.MarshallerFactory;
import org.jboss.marshalling.MarshallingConfiguration;
public class RiverThreadLocalMarshallingDecoderTest extends RiverMarshallingDecoderTest {
@Override
protected MarshallingDecoder createDecoder(MarshallerFactory factory, MarshallingConfiguration config, long maxObjectSize) {
return new ThreadLocalMarshallingDecoder(factory, config, maxObjectSize);
}
}

View File

@ -1,37 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.marshalling;
import org.jboss.marshalling.MarshallerFactory;
import org.jboss.marshalling.Marshalling;
import org.jboss.marshalling.MarshallingConfiguration;
public class SerialMarshallingDecoderTest extends AbstractMarshallingDecoderTest {
@Override
protected MarshallerFactory createMarshallerFactory() {
return Marshalling.getProvidedMarshallerFactory("serial");
}
@Override
protected MarshallingConfiguration createMarshallingConfig() {
// Create a configuration
final MarshallingConfiguration configuration = new MarshallingConfiguration();
configuration.setVersion(5);
return configuration;
}
}

View File

@ -1,38 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.marshalling;
import org.jboss.marshalling.MarshallerFactory;
import org.jboss.marshalling.Marshalling;
import org.jboss.marshalling.MarshallingConfiguration;
public class SerialMarshallingEncoderTest extends AbstractMarshallingEncoderTest {
@Override
protected MarshallerFactory createMarshallerFactory() {
return Marshalling.getProvidedMarshallerFactory("serial");
}
@Override
protected MarshallingConfiguration createMarshallingConfig() {
// Create a configuration
final MarshallingConfiguration configuration = new MarshallingConfiguration();
configuration.setVersion(5);
return configuration;
}
}

View File

@ -1,28 +0,0 @@
/*
* Copyright 2012 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.marshalling;
import org.jboss.marshalling.MarshallerFactory;
import org.jboss.marshalling.MarshallingConfiguration;
public class SerialThreadLocalMarshallingDecoderTest extends SerialMarshallingDecoderTest {
@Override
protected MarshallingDecoder createDecoder(MarshallerFactory factory, MarshallingConfiguration config, long maxObjectSize) {
return new ThreadLocalMarshallingDecoder(factory, config, maxObjectSize);
}
}