[SPDY] Factor out headers into extra class like we did in http and support method chaining where possible

This commit is contained in:
Norman Maurer 2013-01-17 08:31:55 +01:00
parent 238e03f75b
commit 3843cfd702
28 changed files with 710 additions and 571 deletions

View File

@ -62,12 +62,13 @@ public class DefaultSpdyDataFrame extends DefaultByteBufHolder implements SpdyDa
}
@Override
public void setStreamId(int streamId) {
public SpdyDataFrame setStreamId(int streamId) {
if (streamId <= 0) {
throw new IllegalArgumentException(
"Stream-ID must be positive: " + streamId);
}
this.streamId = streamId;
return this;
}
@Override
@ -76,8 +77,9 @@ public class DefaultSpdyDataFrame extends DefaultByteBufHolder implements SpdyDa
}
@Override
public void setLast(boolean last) {
public SpdyDataFrame setLast(boolean last) {
this.last = last;
return this;
}
@Override

View File

@ -61,12 +61,13 @@ public class DefaultSpdyGoAwayFrame implements SpdyGoAwayFrame {
}
@Override
public void setLastGoodStreamId(int lastGoodStreamId) {
public SpdyGoAwayFrame setLastGoodStreamId(int lastGoodStreamId) {
if (lastGoodStreamId < 0) {
throw new IllegalArgumentException("Last-good-stream-ID"
+ " cannot be negative: " + lastGoodStreamId);
}
this.lastGoodStreamId = lastGoodStreamId;
return this;
}
@Override
@ -75,8 +76,9 @@ public class DefaultSpdyGoAwayFrame implements SpdyGoAwayFrame {
}
@Override
public void setStatus(SpdySessionStatus status) {
public SpdyGoAwayFrame setStatus(SpdySessionStatus status) {
this.status = status;
return this;
}
@Override

View File

@ -17,9 +17,7 @@ package io.netty.handler.codec.spdy;
import io.netty.util.internal.StringUtil;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* The default {@link SpdyHeaderBlock} implementation.
@ -27,7 +25,7 @@ import java.util.Set;
public class DefaultSpdyHeaderBlock implements SpdyHeaderBlock {
private boolean invalid;
private final SpdyHeaders headers = new SpdyHeaders();
private final SpdyHeaders headers = new DefaultSpdyHeaders();
/**
* Creates a new instance.
@ -41,62 +39,18 @@ public class DefaultSpdyHeaderBlock implements SpdyHeaderBlock {
}
@Override
public void setInvalid() {
public SpdyHeaderBlock setInvalid() {
invalid = true;
return this;
}
@Override
public void addHeader(final String name, final Object value) {
headers.addHeader(name, value);
}
@Override
public void setHeader(final String name, final Object value) {
headers.setHeader(name, value);
}
@Override
public void setHeader(final String name, final Iterable<?> values) {
headers.setHeader(name, values);
}
@Override
public void removeHeader(final String name) {
headers.removeHeader(name);
}
@Override
public void clearHeaders() {
headers.clearHeaders();
}
@Override
public String getHeader(final String name) {
return headers.getHeader(name);
}
@Override
public List<String> getHeaders(final String name) {
return headers.getHeaders(name);
}
@Override
public List<Map.Entry<String, String>> getHeaders() {
return headers.getHeaders();
}
@Override
public boolean containsHeader(final String name) {
return headers.containsHeader(name);
}
@Override
public Set<String> getHeaderNames() {
return headers.getHeaderNames();
public SpdyHeaders headers() {
return headers;
}
protected void appendHeaders(StringBuilder buf) {
for (Map.Entry<String, String> e: getHeaders()) {
for (Map.Entry<String, String> e: headers().entries()) {
buf.append(" ");
buf.append(e.getKey());
buf.append(": ");

View File

@ -0,0 +1,345 @@
/*
* Copyright 2013 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.spdy;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
public class DefaultSpdyHeaders extends SpdyHeaders {
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 HeaderEntry[] entries = new HeaderEntry[BUCKET_SIZE];
private final HeaderEntry head = new HeaderEntry(-1, null, null);
DefaultSpdyHeaders() {
head.before = head.after = head;
}
@Override
public SpdyHeaders add(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);
add0(h, i, lowerCaseName, strVal);
return this;
}
private void add0(int h, int i, final String name, final String value) {
// Update the hash table.
HeaderEntry e = entries[i];
HeaderEntry newEntry;
entries[i] = newEntry = new HeaderEntry(h, name, value);
newEntry.next = e;
// Update the linked list.
newEntry.addBefore(head);
}
@Override
public SpdyHeaders remove(final String name) {
if (name == null) {
throw new NullPointerException("name");
}
String lowerCaseName = name.toLowerCase();
int h = hash(lowerCaseName);
int i = index(h);
remove0(h, i, lowerCaseName);
return this;
}
private void remove0(int h, int i, String name) {
HeaderEntry e = entries[i];
if (e == null) {
return;
}
for (;;) {
if (e.hash == h && eq(name, e.key)) {
e.remove();
HeaderEntry next = e.next;
if (next != null) {
entries[i] = next;
e = next;
} else {
entries[i] = null;
return;
}
} else {
break;
}
}
for (;;) {
HeaderEntry next = e.next;
if (next == null) {
break;
}
if (next.hash == h && eq(name, next.key)) {
e.next = next.next;
next.remove();
} else {
e = next;
}
}
}
@Override
public SpdyHeaders set(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);
remove0(h, i, lowerCaseName);
add0(h, i, lowerCaseName, strVal);
return this;
}
@Override
public SpdyHeaders set(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);
remove0(h, i, lowerCaseName);
for (Object v: values) {
if (v == null) {
break;
}
String strVal = toString(v);
SpdyCodecUtil.validateHeaderValue(strVal);
add0(h, i, lowerCaseName, strVal);
}
return this;
}
@Override
public SpdyHeaders clear() {
for (int i = 0; i < entries.length; i ++) {
entries[i] = null;
}
head.before = head.after = head;
return this;
}
@Override
public String get(final String name) {
if (name == null) {
throw new NullPointerException("name");
}
int h = hash(name);
int i = index(h);
HeaderEntry e = entries[i];
while (e != null) {
if (e.hash == h && eq(name, e.key)) {
return e.value;
}
e = e.next;
}
return null;
}
@Override
public List<String> getAll(final String name) {
if (name == null) {
throw new NullPointerException("name");
}
LinkedList<String> values = new LinkedList<String>();
int h = hash(name);
int i = index(h);
HeaderEntry e = entries[i];
while (e != null) {
if (e.hash == h && eq(name, e.key)) {
values.addFirst(e.value);
}
e = e.next;
}
return values;
}
@Override
public List<Map.Entry<String, String>> entries() {
List<Map.Entry<String, String>> all =
new LinkedList<Map.Entry<String, String>>();
HeaderEntry e = head.after;
while (e != head) {
all.add(e);
e = e.after;
}
return all;
}
@Override
public boolean contains(String name) {
return get(name) != null;
}
@Override
public Set<String> names() {
Set<String> names = new TreeSet<String>();
HeaderEntry e = head.after;
while (e != head) {
names.add(e.key);
e = e.after;
}
return names;
}
@Override
public SpdyHeaders add(String name, Iterable<?> values) {
SpdyCodecUtil.validateHeaderValue(name);
int h = hash(name);
int i = index(h);
for (Object v: values) {
String vstr = toString(v);
SpdyCodecUtil.validateHeaderValue(vstr);
add0(h, i, name, vstr);
}
return this;
}
@Override
public boolean isEmpty() {
return entries().isEmpty();
}
private static String toString(Object value) {
if (value == null) {
return null;
}
return value.toString();
}
private static final class HeaderEntry implements Map.Entry<String, String> {
final int hash;
final String key;
String value;
HeaderEntry next;
HeaderEntry before, after;
HeaderEntry(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(HeaderEntry e) {
after = e;
before = e.before;
before.after = this;
after.before = this;
}
@Override
public String getKey() {
return key;
}
@Override
public String getValue() {
return value;
}
@Override
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

@ -42,12 +42,13 @@ public class DefaultSpdyHeadersFrame extends DefaultSpdyHeaderBlock
}
@Override
public void setStreamId(int streamId) {
public SpdyHeadersFrame setStreamId(int streamId) {
if (streamId <= 0) {
throw new IllegalArgumentException(
"Stream-ID must be positive: " + streamId);
}
this.streamId = streamId;
return this;
}
@Override
@ -56,8 +57,15 @@ public class DefaultSpdyHeadersFrame extends DefaultSpdyHeaderBlock
}
@Override
public void setLast(boolean last) {
public SpdyHeadersFrame setLast(boolean last) {
this.last = last;
return this;
}
@Override
public SpdyHeadersFrame setInvalid() {
super.setInvalid();
return this;
}
@Override

View File

@ -39,8 +39,9 @@ public class DefaultSpdyPingFrame implements SpdyPingFrame {
}
@Override
public void setId(int id) {
public SpdyPingFrame setId(int id) {
this.id = id;
return this;
}
@Override

View File

@ -52,12 +52,13 @@ public class DefaultSpdyRstStreamFrame implements SpdyRstStreamFrame {
}
@Override
public void setStreamId(int streamId) {
public SpdyRstStreamFrame setStreamId(int streamId) {
if (streamId <= 0) {
throw new IllegalArgumentException(
"Stream-ID must be positive: " + streamId);
}
this.streamId = streamId;
return this;
}
@Override
@ -66,8 +67,9 @@ public class DefaultSpdyRstStreamFrame implements SpdyRstStreamFrame {
}
@Override
public void setStatus(SpdyStreamStatus status) {
public SpdyRstStreamFrame setStatus(SpdyStreamStatus status) {
this.status = status;
return this;
}
@Override

View File

@ -51,12 +51,12 @@ public class DefaultSpdySettingsFrame implements SpdySettingsFrame {
}
@Override
public void setValue(int id, int value) {
setValue(id, value, false, false);
public SpdySettingsFrame setValue(int id, int value) {
return setValue(id, value, false, false);
}
@Override
public void setValue(int id, int value, boolean persistValue, boolean persisted) {
public SpdySettingsFrame 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);
}
@ -69,14 +69,16 @@ public class DefaultSpdySettingsFrame implements SpdySettingsFrame {
} else {
settingsMap.put(key, new Setting(value, persistValue, persisted));
}
return this;
}
@Override
public void removeValue(int id) {
public SpdySettingsFrame removeValue(int id) {
Integer key = Integer.valueOf(id);
if (settingsMap.containsKey(key)) {
settingsMap.remove(key);
}
return this;
}
@Override
@ -90,11 +92,12 @@ public class DefaultSpdySettingsFrame implements SpdySettingsFrame {
}
@Override
public void setPersistValue(int id, boolean persistValue) {
public SpdySettingsFrame setPersistValue(int id, boolean persistValue) {
Integer key = Integer.valueOf(id);
if (settingsMap.containsKey(key)) {
settingsMap.get(key).setPersist(persistValue);
}
return this;
}
@Override
@ -108,11 +111,12 @@ public class DefaultSpdySettingsFrame implements SpdySettingsFrame {
}
@Override
public void setPersisted(int id, boolean persisted) {
public SpdySettingsFrame setPersisted(int id, boolean persisted) {
Integer key = Integer.valueOf(id);
if (settingsMap.containsKey(key)) {
settingsMap.get(key).setPersisted(persisted);
}
return this;
}
@Override
@ -121,8 +125,9 @@ public class DefaultSpdySettingsFrame implements SpdySettingsFrame {
}
@Override
public void setClearPreviouslyPersistedSettings(boolean clear) {
public SpdySettingsFrame setClearPreviouslyPersistedSettings(boolean clear) {
this.clear = clear;
return this;
}
private Set<Map.Entry<Integer, Setting>> getSettings() {

View File

@ -41,12 +41,13 @@ public class DefaultSpdySynReplyFrame extends DefaultSpdyHeaderBlock
}
@Override
public void setStreamId(int streamId) {
public SpdySynReplyFrame setStreamId(int streamId) {
if (streamId <= 0) {
throw new IllegalArgumentException(
"Stream-ID must be positive: " + streamId);
}
this.streamId = streamId;
return this;
}
@Override
@ -55,8 +56,15 @@ public class DefaultSpdySynReplyFrame extends DefaultSpdyHeaderBlock
}
@Override
public void setLast(boolean last) {
public SpdySynReplyFrame setLast(boolean last) {
this.last = last;
return this;
}
@Override
public SpdySynReplyFrame setInvalid() {
super.setInvalid();
return this;
}
@Override

View File

@ -49,12 +49,13 @@ public class DefaultSpdySynStreamFrame extends DefaultSpdyHeaderBlock
}
@Override
public void setStreamId(int streamId) {
public SpdySynStreamFrame setStreamId(int streamId) {
if (streamId <= 0) {
throw new IllegalArgumentException(
"Stream-ID must be positive: " + streamId);
}
this.streamId = streamId;
return this;
}
@Override
@ -63,13 +64,14 @@ public class DefaultSpdySynStreamFrame extends DefaultSpdyHeaderBlock
}
@Override
public void setAssociatedToStreamId(int associatedToStreamId) {
public SpdySynStreamFrame setAssociatedToStreamId(int associatedToStreamId) {
if (associatedToStreamId < 0) {
throw new IllegalArgumentException(
"Associated-To-Stream-ID cannot be negative: " +
associatedToStreamId);
}
this.associatedToStreamId = associatedToStreamId;
return this;
}
@Override
@ -78,12 +80,13 @@ public class DefaultSpdySynStreamFrame extends DefaultSpdyHeaderBlock
}
@Override
public void setPriority(byte priority) {
public SpdySynStreamFrame setPriority(byte priority) {
if (priority < 0 || priority > 7) {
throw new IllegalArgumentException(
"Priority must be between 0 and 7 inclusive: " + priority);
}
this.priority = priority;
return this;
}
@Override
@ -92,8 +95,9 @@ public class DefaultSpdySynStreamFrame extends DefaultSpdyHeaderBlock
}
@Override
public void setLast(boolean last) {
public SpdySynStreamFrame setLast(boolean last) {
this.last = last;
return this;
}
@Override
@ -102,8 +106,15 @@ public class DefaultSpdySynStreamFrame extends DefaultSpdyHeaderBlock
}
@Override
public void setUnidirectional(boolean unidirectional) {
public SpdySynStreamFrame setUnidirectional(boolean unidirectional) {
this.unidirectional = unidirectional;
return this;
}
@Override
public SpdySynStreamFrame setInvalid() {
super.setInvalid();
return this;
}
@Override

View File

@ -42,12 +42,13 @@ public class DefaultSpdyWindowUpdateFrame implements SpdyWindowUpdateFrame {
}
@Override
public void setStreamId(int streamId) {
public SpdyWindowUpdateFrame setStreamId(int streamId) {
if (streamId <= 0) {
throw new IllegalArgumentException(
"Stream-ID must be positive: " + streamId);
}
this.streamId = streamId;
return this;
}
@Override
@ -56,13 +57,14 @@ public class DefaultSpdyWindowUpdateFrame implements SpdyWindowUpdateFrame {
}
@Override
public void setDeltaWindowSize(int deltaWindowSize) {
public SpdyWindowUpdateFrame setDeltaWindowSize(int deltaWindowSize) {
if (deltaWindowSize <= 0) {
throw new IllegalArgumentException(
"Delta-Window-Size must be positive: " +
deltaWindowSize);
}
this.deltaWindowSize = deltaWindowSize;
return this;
}
@Override

View File

@ -22,28 +22,13 @@ import io.netty.buffer.Unpooled;
/**
* A SPDY Protocol Data Frame
*/
public interface SpdyDataFrame extends ByteBufHolder {
public interface SpdyDataFrame extends ByteBufHolder, SpdyStreamFrame {
/**
* Returns the Stream-ID of this frame.
*/
int getStreamId();
@Override
SpdyDataFrame setStreamId(int streamID);
/**
* 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);
@Override
SpdyDataFrame setLast(boolean last);
/**
* Returns the data payload of this frame. If there is no data payload

View File

@ -613,7 +613,7 @@ public class SpdyFrameDecoder extends ByteToMessageDecoder {
String name = new String(nameBytes, "UTF-8");
// Check for identically named headers
if (spdyHeaderBlock.containsHeader(name)) {
if (spdyHeaderBlock.headers().contains(name)) {
spdyHeaderBlock.setInvalid();
return;
}
@ -638,7 +638,7 @@ public class SpdyFrameDecoder extends ByteToMessageDecoder {
spdyHeaderBlock.setInvalid();
return;
} else {
spdyHeaderBlock.addHeader(name, "");
spdyHeaderBlock.headers().add(name, "");
numHeaders --;
this.headerSize = headerSize;
continue;
@ -676,7 +676,7 @@ public class SpdyFrameDecoder extends ByteToMessageDecoder {
String value = new String(valueBytes, offset, index - offset, "UTF-8");
try {
spdyHeaderBlock.addHeader(name, value);
spdyHeaderBlock.headers().add(name, value);
} catch (IllegalArgumentException e) {
// Name contains NULL or non-ascii characters
spdyHeaderBlock.setInvalid();

View File

@ -289,7 +289,7 @@ public class SpdyFrameEncoder extends MessageToByteEncoder<Object> {
private static ByteBuf encodeHeaderBlock(int version, SpdyHeaderBlock headerFrame)
throws Exception {
Set<String> names = headerFrame.getHeaderNames();
Set<String> names = headerFrame.headers().names();
int numHeaders = names.size();
if (numHeaders == 0) {
return Unpooled.EMPTY_BUFFER;
@ -307,7 +307,7 @@ public class SpdyFrameEncoder extends MessageToByteEncoder<Object> {
int savedIndex = headerBlock.writerIndex();
int valueLength = 0;
writeLengthField(version, headerBlock, valueLength);
for (String value: headerFrame.getHeaders(name)) {
for (String value: headerFrame.headers().getAll(name)) {
byte[] valueBytes = value.getBytes(CharsetUtil.UTF_8);
if (valueBytes.length > 0) {
headerBlock.writeBytes(valueBytes);

View File

@ -29,7 +29,7 @@ public interface SpdyGoAwayFrame extends SpdyControlFrame {
* Sets the Last-good-stream-ID of this frame. The Last-good-stream-ID
* cannot be negative.
*/
void setLastGoodStreamId(int lastGoodStreamId);
SpdyGoAwayFrame setLastGoodStreamId(int lastGoodStreamId);
/**
* Returns the getStatus of this frame.
@ -39,5 +39,5 @@ public interface SpdyGoAwayFrame extends SpdyControlFrame {
/**
* Sets the getStatus of this frame.
*/
void setStatus(SpdySessionStatus status);
SpdyGoAwayFrame setStatus(SpdySessionStatus status);
}

View File

@ -15,9 +15,6 @@
*/
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
@ -36,68 +33,10 @@ public interface SpdyHeaderBlock {
/**
* Marks this header block as invalid.
*/
void setInvalid();
SpdyHeaderBlock 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
* Returns the {@link SpdyHeaders}.
*/
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();
SpdyHeaders headers();
}

View File

@ -19,18 +19,86 @@ import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import java.util.LinkedList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
/**
* Provides the constants for the standard SPDY HTTP header names and commonly
* used utility methods that access a {@link SpdyHeaderBlock}.
* @apiviz.stereotype static
*/
public class SpdyHeaders {
public abstract class SpdyHeaders implements Iterable<Map.Entry<String, String>> {
public static final SpdyHeaders EMPTY_HEADERS = new SpdyHeaders() {
@Override
public List<String> getAll(String name) {
return Collections.emptyList();
}
@Override
public List<Map.Entry<String, String>> entries() {
return Collections.emptyList();
}
@Override
public boolean contains(String name) {
return false;
}
@Override
public boolean isEmpty() {
return true;
}
@Override
public Set<String> names() {
return Collections.emptySet();
}
@Override
public SpdyHeaders add(String name, Object value) {
throw new UnsupportedOperationException("read only");
}
@Override
public SpdyHeaders add(String name, Iterable<?> values) {
throw new UnsupportedOperationException("read only");
}
@Override
public SpdyHeaders set(String name, Object value) {
throw new UnsupportedOperationException("read only");
}
@Override
public SpdyHeaders set(String name, Iterable<?> values) {
throw new UnsupportedOperationException("read only");
}
@Override
public SpdyHeaders remove(String name) {
throw new UnsupportedOperationException("read only");
}
@Override
public SpdyHeaders clear() {
throw new UnsupportedOperationException("read only");
}
@Override
public Iterator<Map.Entry<String, String>> iterator() {
return entries().iterator();
}
@Override
public String get(String name) {
return null;
}
};
/**
* SPDY HTTP header names
@ -102,7 +170,7 @@ public class SpdyHeaders {
* @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);
return block.headers().get(name);
}
/**
@ -114,7 +182,7 @@ public class SpdyHeaders {
* header
*/
public static String getHeader(SpdyHeaderBlock block, String name, String defaultValue) {
String value = block.getHeader(name);
String value = block.headers().get(name);
if (value == null) {
return defaultValue;
}
@ -126,7 +194,7 @@ public class SpdyHeaders {
* 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);
block.headers().set(name, value);
}
/**
@ -134,35 +202,35 @@ public class SpdyHeaders {
* 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);
block.headers().set(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);
block.headers().add(name, value);
}
/**
* Removes the SPDY host header.
*/
public static void removeHost(SpdyHeaderBlock block) {
block.removeHeader(HttpNames.HOST);
block.headers().remove(HttpNames.HOST);
}
/**
* Returns the SPDY host header.
*/
public static String getHost(SpdyHeaderBlock block) {
return block.getHeader(HttpNames.HOST);
return block.headers().get(HttpNames.HOST);
}
/**
* Set the SPDY host header.
*/
public static void setHost(SpdyHeaderBlock block, String host) {
block.setHeader(HttpNames.HOST, host);
block.headers().set(HttpNames.HOST, host);
}
/**
@ -170,9 +238,9 @@ public class SpdyHeaders {
*/
public static void removeMethod(int spdyVersion, SpdyHeaderBlock block) {
if (spdyVersion < 3) {
block.removeHeader(Spdy2HttpNames.METHOD);
block.headers().remove(Spdy2HttpNames.METHOD);
} else {
block.removeHeader(HttpNames.METHOD);
block.headers().remove(HttpNames.METHOD);
}
}
@ -182,9 +250,9 @@ public class SpdyHeaders {
public static HttpMethod getMethod(int spdyVersion, SpdyHeaderBlock block) {
try {
if (spdyVersion < 3) {
return HttpMethod.valueOf(block.getHeader(Spdy2HttpNames.METHOD));
return HttpMethod.valueOf(block.headers().get(Spdy2HttpNames.METHOD));
} else {
return HttpMethod.valueOf(block.getHeader(HttpNames.METHOD));
return HttpMethod.valueOf(block.headers().get(HttpNames.METHOD));
}
} catch (Exception e) {
return null;
@ -196,9 +264,9 @@ public class SpdyHeaders {
*/
public static void setMethod(int spdyVersion, SpdyHeaderBlock block, HttpMethod method) {
if (spdyVersion < 3) {
block.setHeader(Spdy2HttpNames.METHOD, method.name());
block.headers().set(Spdy2HttpNames.METHOD, method.name());
} else {
block.setHeader(HttpNames.METHOD, method.name());
block.headers().set(HttpNames.METHOD, method.name());
}
}
@ -207,9 +275,9 @@ public class SpdyHeaders {
*/
public static void removeScheme(int spdyVersion, SpdyHeaderBlock block) {
if (spdyVersion < 2) {
block.removeHeader(Spdy2HttpNames.SCHEME);
block.headers().remove(Spdy2HttpNames.SCHEME);
} else {
block.removeHeader(HttpNames.SCHEME);
block.headers().remove(HttpNames.SCHEME);
}
}
@ -218,9 +286,9 @@ public class SpdyHeaders {
*/
public static String getScheme(int spdyVersion, SpdyHeaderBlock block) {
if (spdyVersion < 3) {
return block.getHeader(Spdy2HttpNames.SCHEME);
return block.headers().get(Spdy2HttpNames.SCHEME);
} else {
return block.getHeader(HttpNames.SCHEME);
return block.headers().get(HttpNames.SCHEME);
}
}
@ -229,9 +297,9 @@ public class SpdyHeaders {
*/
public static void setScheme(int spdyVersion, SpdyHeaderBlock block, String scheme) {
if (spdyVersion < 3) {
block.setHeader(Spdy2HttpNames.SCHEME, scheme);
block.headers().set(Spdy2HttpNames.SCHEME, scheme);
} else {
block.setHeader(HttpNames.SCHEME, scheme);
block.headers().set(HttpNames.SCHEME, scheme);
}
}
@ -240,9 +308,9 @@ public class SpdyHeaders {
*/
public static void removeStatus(int spdyVersion, SpdyHeaderBlock block) {
if (spdyVersion < 3) {
block.removeHeader(Spdy2HttpNames.STATUS);
block.headers().remove(Spdy2HttpNames.STATUS);
} else {
block.removeHeader(HttpNames.STATUS);
block.headers().remove(HttpNames.STATUS);
}
}
@ -253,9 +321,9 @@ public class SpdyHeaders {
try {
String status;
if (spdyVersion < 3) {
status = block.getHeader(Spdy2HttpNames.STATUS);
status = block.headers().get(Spdy2HttpNames.STATUS);
} else {
status = block.getHeader(HttpNames.STATUS);
status = block.headers().get(HttpNames.STATUS);
}
int space = status.indexOf(' ');
if (space == -1) {
@ -280,9 +348,9 @@ public class SpdyHeaders {
*/
public static void setStatus(int spdyVersion, SpdyHeaderBlock block, HttpResponseStatus status) {
if (spdyVersion < 3) {
block.setHeader(Spdy2HttpNames.STATUS, status.toString());
block.headers().set(Spdy2HttpNames.STATUS, status.toString());
} else {
block.setHeader(HttpNames.STATUS, status.toString());
block.headers().set(HttpNames.STATUS, status.toString());
}
}
@ -291,9 +359,9 @@ public class SpdyHeaders {
*/
public static void removeUrl(int spdyVersion, SpdyHeaderBlock block) {
if (spdyVersion < 3) {
block.removeHeader(Spdy2HttpNames.URL);
block.headers().remove(Spdy2HttpNames.URL);
} else {
block.removeHeader(HttpNames.PATH);
block.headers().remove(HttpNames.PATH);
}
}
@ -302,9 +370,9 @@ public class SpdyHeaders {
*/
public static String getUrl(int spdyVersion, SpdyHeaderBlock block) {
if (spdyVersion < 3) {
return block.getHeader(Spdy2HttpNames.URL);
return block.headers().get(Spdy2HttpNames.URL);
} else {
return block.getHeader(HttpNames.PATH);
return block.headers().get(HttpNames.PATH);
}
}
@ -313,9 +381,9 @@ public class SpdyHeaders {
*/
public static void setUrl(int spdyVersion, SpdyHeaderBlock block, String path) {
if (spdyVersion < 3) {
block.setHeader(Spdy2HttpNames.URL, path);
block.headers().set(Spdy2HttpNames.URL, path);
} else {
block.setHeader(HttpNames.PATH, path);
block.headers().set(HttpNames.PATH, path);
}
}
@ -324,9 +392,9 @@ public class SpdyHeaders {
*/
public static void removeVersion(int spdyVersion, SpdyHeaderBlock block) {
if (spdyVersion < 3) {
block.removeHeader(Spdy2HttpNames.VERSION);
block.headers().remove(Spdy2HttpNames.VERSION);
} else {
block.removeHeader(HttpNames.VERSION);
block.headers().remove(HttpNames.VERSION);
}
}
@ -336,9 +404,9 @@ public class SpdyHeaders {
public static HttpVersion getVersion(int spdyVersion, SpdyHeaderBlock block) {
try {
if (spdyVersion < 3) {
return HttpVersion.valueOf(block.getHeader(Spdy2HttpNames.VERSION));
return HttpVersion.valueOf(block.headers().get(Spdy2HttpNames.VERSION));
} else {
return HttpVersion.valueOf(block.getHeader(HttpNames.VERSION));
return HttpVersion.valueOf(block.headers().get(HttpNames.VERSION));
}
} catch (Exception e) {
return null;
@ -350,295 +418,87 @@ public class SpdyHeaders {
*/
public static void setVersion(int spdyVersion, SpdyHeaderBlock block, HttpVersion httpVersion) {
if (spdyVersion < 3) {
block.setHeader(Spdy2HttpNames.VERSION, httpVersion.text());
block.headers().set(Spdy2HttpNames.VERSION, httpVersion.text());
} else {
block.setHeader(HttpNames.VERSION, httpVersion.text());
block.headers().set(HttpNames.VERSION, httpVersion.text());
}
}
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;
}
@Override
public Iterator<Map.Entry<String, String>> iterator() {
return entries().iterator();
}
private static boolean eq(String name1, String name2) {
int nameLen = name1.length();
if (nameLen != name2.length()) {
return false;
}
/**
* 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
*/
public abstract String get(String name);
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;
}
/**
* 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.
*/
public abstract List<String> getAll(String name);
private static int index(int hash) {
return hash % BUCKET_SIZE;
}
/**
* 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.
*/
public abstract List<Map.Entry<String, String>> entries();
private final HeaderEntry[] entries = new HeaderEntry[BUCKET_SIZE];
private final HeaderEntry head = new HeaderEntry(-1, null, null);
/**
* Returns {@code true} if and only if there is a header with the specified
* header name.
*/
public abstract boolean contains(String name);
SpdyHeaders() {
head.before = head.after = head;
}
/**
* Returns the {@link Set} of all header names that this block contains.
*/
public abstract Set<String> names();
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);
}
/**
* Adds a new header with the specified name and value.
*/
public abstract SpdyHeaders add(String name, Object value);
private void addHeader0(int h, int i, final String name, final String value) {
// Update the hash table.
HeaderEntry e = entries[i];
HeaderEntry newEntry;
entries[i] = newEntry = new HeaderEntry(h, name, value);
newEntry.next = e;
/**
* Adds 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 abstract SpdyHeaders add(String name, Iterable<?> values);
// Update the linked list.
newEntry.addBefore(head);
}
/**
* 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 abstract SpdyHeaders set(String name, Object value);
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);
}
/**
* 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 abstract SpdyHeaders set(String name, Iterable<?> values);
private void removeHeader0(int h, int i, String name) {
HeaderEntry e = entries[i];
if (e == null) {
return;
}
/**
* Removes the header with the specified name.
*/
public abstract SpdyHeaders remove(String name);
for (;;) {
if (e.hash == h && eq(name, e.key)) {
e.remove();
HeaderEntry next = e.next;
if (next != null) {
entries[i] = next;
e = next;
} else {
entries[i] = null;
return;
}
} else {
break;
}
}
/**
* Removes all headers from this block.
*/
public abstract SpdyHeaders clear();
for (;;) {
HeaderEntry 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);
HeaderEntry 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);
HeaderEntry 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>>();
HeaderEntry 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>();
HeaderEntry 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 HeaderEntry implements Map.Entry<String, String> {
final int hash;
final String key;
String value;
HeaderEntry next;
HeaderEntry before, after;
HeaderEntry(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(HeaderEntry e) {
after = e;
before = e.before;
before.after = this;
after.before = this;
}
@Override
public String getKey() {
return key;
}
@Override
public String getValue() {
return value;
}
@Override
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;
}
}
/**
* Checks if no header exists.
*/
public abstract boolean isEmpty();
}

View File

@ -18,26 +18,15 @@ package io.netty.handler.codec.spdy;
/**
* A SPDY Protocol HEADERS Control Frame
*/
public interface SpdyHeadersFrame extends SpdyHeaderBlock, SpdyControlFrame {
public interface SpdyHeadersFrame extends SpdyHeaderBlock, SpdyControlFrame, SpdyStreamFrame {
/**
* Returns the Stream-ID of this frame.
*/
int getStreamId();
@Override
SpdyHeadersFrame setStreamId(int streamID);
/**
* Sets the Stream-ID of this frame. The Stream-ID must be positive.
*/
void setStreamId(int streamID);
@Override
SpdyHeadersFrame setLast(boolean last);
/**
* Returns {@code true} if this frame is the last frame to be transmitted
* on the stream.
*/
boolean isLast();
@Override
SpdyHeadersFrame setInvalid();
/**
* Sets if this frame is the last frame to be transmitted on the stream.
*/
void setLast(boolean last);
}

View File

@ -182,7 +182,7 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<Object> {
return null;
}
for (Map.Entry<String, String> e: spdyHeadersFrame.getHeaders()) {
for (Map.Entry<String, String> e: spdyHeadersFrame.headers().entries()) {
httpMessage.headers().add(e.getKey(), e.getValue());
}
@ -247,7 +247,7 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<Object> {
HttpHeaders.setHost(req, host);
}
for (Map.Entry<String, String> e: requestFrame.getHeaders()) {
for (Map.Entry<String, String> e: requestFrame.headers().entries()) {
req.headers().add(e.getKey(), e.getValue());
}
@ -269,7 +269,7 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<Object> {
SpdyHeaders.removeVersion(spdyVersion, responseFrame);
FullHttpResponse res = new DefaultFullHttpResponse(version, status);
for (Map.Entry<String, String> e: responseFrame.getHeaders()) {
for (Map.Entry<String, String> e: responseFrame.headers().entries()) {
res.headers().add(e.getKey(), e.getValue());
}

View File

@ -175,7 +175,7 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder<Object> {
// 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());
spdyHeadersFrame.headers().add(entry.getKey(), entry.getValue());
}
// Write HEADERS frame and append Data Frame
@ -246,7 +246,7 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder<Object> {
// Transfer the remaining HTTP headers
for (Map.Entry<String, String> entry: httpMessage.headers()) {
spdySynStreamFrame.addHeader(entry.getKey(), entry.getValue());
spdySynStreamFrame.headers().add(entry.getKey(), entry.getValue());
}
currentStreamId = spdySynStreamFrame.getStreamId();
@ -276,7 +276,7 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder<Object> {
// Transfer the remaining HTTP headers
for (Map.Entry<String, String> entry: httpResponse.headers()) {
spdySynReplyFrame.addHeader(entry.getKey(), entry.getValue());
spdySynReplyFrame.headers().add(entry.getKey(), entry.getValue());
}
if (chunked) {

View File

@ -28,5 +28,5 @@ public interface SpdyPingFrame extends SpdyControlFrame {
/**
* Sets the ID of this frame.
*/
void setId(int id);
SpdyPingFrame setId(int id);
}

View File

@ -28,7 +28,7 @@ public interface SpdyRstStreamFrame extends SpdyControlFrame {
/**
* Sets the Stream-ID of this frame. The Stream-ID must be positive.
*/
void setStreamId(int streamID);
SpdyControlFrame setStreamId(int streamID);
/**
* Returns the getStatus of this frame.
@ -38,5 +38,5 @@ public interface SpdyRstStreamFrame extends SpdyControlFrame {
/**
* Sets the getStatus of this frame.
*/
void setStatus(SpdyStreamStatus status);
SpdyControlFrame setStatus(SpdyStreamStatus status);
}

View File

@ -52,7 +52,7 @@ public interface SpdySettingsFrame extends SpdyControlFrame {
* Sets the value of the setting ID.
* The ID must be positive and cannot exceed 16777215.
*/
void setValue(int id, int value);
SpdySettingsFrame setValue(int id, int value);
/**
* Sets the value of the setting ID.
@ -60,13 +60,13 @@ public interface SpdySettingsFrame extends SpdyControlFrame {
* 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);
SpdySettingsFrame setValue(int id, int value, boolean persistVal, boolean persisted);
/**
* Removes the value of the setting ID.
* Removes all persistence information for the setting.
*/
void removeValue(int id);
SpdySettingsFrame removeValue(int id);
/**
* Returns {@code true} if this setting should be persisted.
@ -79,7 +79,7 @@ public interface SpdySettingsFrame extends SpdyControlFrame {
* Sets if this setting should be persisted.
* Has no effect if the setting ID has no value.
*/
void setPersistValue(int id, boolean persistValue);
SpdySettingsFrame setPersistValue(int id, boolean persistValue);
/**
* Returns {@code true} if this setting is persisted.
@ -92,7 +92,7 @@ public interface SpdySettingsFrame extends SpdyControlFrame {
* Sets if this setting is persisted.
* Has no effect if the setting ID has no value.
*/
void setPersisted(int id, boolean persisted);
SpdySettingsFrame setPersisted(int id, boolean persisted);
/**
* Returns {@code true} if previously persisted settings should be cleared.
@ -102,5 +102,5 @@ public interface SpdySettingsFrame extends SpdyControlFrame {
/**
* Sets if previously persisted settings should be cleared.
*/
void setClearPreviouslyPersistedSettings(boolean clear);
SpdySettingsFrame setClearPreviouslyPersistedSettings(boolean clear);
}

View File

@ -0,0 +1,43 @@
/*
* Copyright 2013 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
package io.netty.handler.codec.spdy;
/**
* A frame which is part of a stream
*/
public interface SpdyStreamFrame {
/**
* Returns the Stream-ID of this frame.
*/
int getStreamId();
/**
* Sets the Stream-ID of this frame. The Stream-ID must be positive.
*/
SpdyStreamFrame 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.
*/
SpdyStreamFrame setLast(boolean last);
}

View File

@ -18,26 +18,14 @@ package io.netty.handler.codec.spdy;
/**
* A SPDY Protocol SYN_REPLY Control Frame
*/
public interface SpdySynReplyFrame extends SpdyHeaderBlock, SpdyControlFrame {
public interface SpdySynReplyFrame extends SpdyHeaderBlock, SpdyControlFrame, SpdyStreamFrame {
/**
* Returns the Stream-ID of this frame.
*/
int getStreamId();
@Override
SpdySynReplyFrame setStreamId(int streamID);
/**
* Sets the Stream-ID of this frame. The Stream-ID must be positive.
*/
void setStreamId(int streamID);
@Override
SpdySynReplyFrame setLast(boolean last);
/**
* 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);
@Override
SpdySynReplyFrame setInvalid();
}

View File

@ -18,17 +18,7 @@ package io.netty.handler.codec.spdy;
/**
* A SPDY Protocol SYN_STREAM Control Frame
*/
public interface SpdySynStreamFrame extends SpdyHeaderBlock, SpdyControlFrame {
/**
* 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);
public interface SpdySynStreamFrame extends SpdyHeaderBlock, SpdyControlFrame , SpdyStreamFrame {
/**
* Returns the Associated-To-Stream-ID of this frame.
@ -39,7 +29,7 @@ public interface SpdySynStreamFrame extends SpdyHeaderBlock, SpdyControlFrame {
* Sets the Associated-To-Stream-ID of this frame.
* The Associated-To-Stream-ID cannot be negative.
*/
void setAssociatedToStreamId(int associatedToStreamId);
SpdySynStreamFrame setAssociatedToStreamId(int associatedToStreamId);
/**
* Returns the priority of the stream.
@ -50,18 +40,7 @@ public interface SpdySynStreamFrame extends SpdyHeaderBlock, SpdyControlFrame {
* Sets the priority of the stream.
* The priority must be between 0 and 7 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);
SpdySynStreamFrame setPriority(byte priority);
/**
* Returns {@code true} if the stream created with this frame is to be
@ -73,5 +52,14 @@ public interface SpdySynStreamFrame extends SpdyHeaderBlock, SpdyControlFrame {
* Sets if the stream created with this frame is to be considered
* half-closed to the receiver.
*/
void setUnidirectional(boolean unidirectional);
SpdySynStreamFrame setUnidirectional(boolean unidirectional);
@Override
SpdySynStreamFrame setStreamId(int streamID);
@Override
SpdySynStreamFrame setLast(boolean last);
@Override
SpdySynStreamFrame setInvalid();
}

View File

@ -28,7 +28,7 @@ public interface SpdyWindowUpdateFrame extends SpdyControlFrame {
/**
* Sets the Stream-ID of this frame. The Stream-ID must be positive.
*/
void setStreamId(int streamID);
SpdyWindowUpdateFrame setStreamId(int streamID);
/**
* Returns the Delta-Window-Size of this frame.
@ -39,5 +39,5 @@ public interface SpdyWindowUpdateFrame extends SpdyControlFrame {
* Sets the Delta-Window-Size of this frame.
* The Delta-Window-Size must be positive.
*/
void setDeltaWindowSize(int deltaWindowSize);
SpdyWindowUpdateFrame setDeltaWindowSize(int deltaWindowSize);
}

View File

@ -40,15 +40,15 @@ public class SpdySessionHandlerTest {
}
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);
for (String name: expected.headers().names()) {
List<String> expectedValues = expected.headers().getAll(name);
List<String> receivedValues = received.headers().getAll(name);
assertTrue(receivedValues.containsAll(expectedValues));
receivedValues.removeAll(expectedValues);
assertTrue(receivedValues.isEmpty());
received.removeHeader(name);
received.headers().remove(name);
}
assertTrue(received.getHeaders().isEmpty());
assertTrue(received.headers().entries().isEmpty());
}
private static void assertDataFrame(Object msg, int streamID, boolean last) {
@ -114,7 +114,7 @@ public class SpdySessionHandlerTest {
SpdySynStreamFrame spdySynStreamFrame =
new DefaultSpdySynStreamFrame(localStreamID, 0, (byte) 0);
spdySynStreamFrame.setHeader("Compression", "test");
spdySynStreamFrame.headers().set("Compression", "test");
SpdyDataFrame spdyDataFrame = new DefaultSpdyDataFrame(localStreamID);
spdyDataFrame.setLast(true);
@ -146,8 +146,10 @@ public class SpdySessionHandlerTest {
assertSynReply(sessionHandler.readOutbound(), localStreamID, false, spdySynStreamFrame);
assertNull(sessionHandler.readOutbound());
SpdyHeadersFrame spdyHeadersFrame = new DefaultSpdyHeadersFrame(localStreamID);
spdyHeadersFrame.addHeader("HEADER","test1");
spdyHeadersFrame.addHeader("HEADER","test2");
spdyHeadersFrame.headers().add("HEADER","test1");
spdyHeadersFrame.headers().add("HEADER","test2");
sessionHandler.writeInbound(spdyHeadersFrame);
assertHeaders(sessionHandler.readOutbound(), localStreamID, spdyHeadersFrame);
assertNull(sessionHandler.readOutbound());
@ -159,6 +161,7 @@ public class SpdySessionHandlerTest {
spdySynStreamFrame.setStreamId(localStreamID);
spdySynStreamFrame.setLast(true);
spdySynStreamFrame.setUnidirectional(true);
sessionHandler.writeInbound(spdySynStreamFrame);
assertRstStream(sessionHandler.readOutbound(), localStreamID, SpdyStreamStatus.REFUSED_STREAM);
assertNull(sessionHandler.readOutbound());
@ -175,6 +178,7 @@ public class SpdySessionHandlerTest {
assertNull(sessionHandler.readOutbound());
spdySynStreamFrame.setUnidirectional(false);
// Check if session handler returns PROTOCOL_ERROR if it receives
// multiple SYN_STREAM frames for the same active Stream-ID
sessionHandler.writeInbound(spdySynStreamFrame);
@ -190,6 +194,7 @@ public class SpdySessionHandlerTest {
assertNull(sessionHandler.readOutbound());
spdySynStreamFrame.setStreamId(localStreamID);
// Check if session handler correctly limits the number of
// concurrent streams in the SETTINGS frame
SpdySettingsFrame spdySettingsFrame = new DefaultSpdySettingsFrame();
@ -212,6 +217,7 @@ public class SpdySessionHandlerTest {
assertDataFrame(sessionHandler.readOutbound(), testStreamID, spdyDataFrame.isLast());
assertNull(sessionHandler.readOutbound());
spdyHeadersFrame.setStreamId(testStreamID);
sessionHandler.writeInbound(spdyHeadersFrame);
assertRstStream(sessionHandler.readOutbound(), testStreamID, SpdyStreamStatus.INVALID_STREAM);
assertNull(sessionHandler.readOutbound());
@ -219,6 +225,7 @@ public class SpdySessionHandlerTest {
// Check if session handler returns PROTOCOL_ERROR if it receives
// an invalid HEADERS frame
spdyHeadersFrame.setStreamId(localStreamID);
spdyHeadersFrame.setInvalid();
sessionHandler.writeInbound(spdyHeadersFrame);
assertRstStream(sessionHandler.readOutbound(), localStreamID, SpdyStreamStatus.PROTOCOL_ERROR);
@ -320,8 +327,8 @@ public class SpdySessionHandlerTest {
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());
for (Map.Entry<String, String> entry: spdySynStreamFrame.headers()) {
spdySynReplyFrame.headers().add(entry.getKey(), entry.getValue());
}
ctx.write(spdySynReplyFrame);