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

View File

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

View File

@ -17,9 +17,7 @@ package io.netty.handler.codec.spdy;
import io.netty.util.internal.StringUtil; import io.netty.util.internal.StringUtil;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
/** /**
* The default {@link SpdyHeaderBlock} implementation. * The default {@link SpdyHeaderBlock} implementation.
@ -27,7 +25,7 @@ import java.util.Set;
public class DefaultSpdyHeaderBlock implements SpdyHeaderBlock { public class DefaultSpdyHeaderBlock implements SpdyHeaderBlock {
private boolean invalid; private boolean invalid;
private final SpdyHeaders headers = new SpdyHeaders(); private final SpdyHeaders headers = new DefaultSpdyHeaders();
/** /**
* Creates a new instance. * Creates a new instance.
@ -41,62 +39,18 @@ public class DefaultSpdyHeaderBlock implements SpdyHeaderBlock {
} }
@Override @Override
public void setInvalid() { public SpdyHeaderBlock setInvalid() {
invalid = true; invalid = true;
return this;
} }
@Override @Override
public void addHeader(final String name, final Object value) { public SpdyHeaders headers() {
headers.addHeader(name, value); return headers;
}
@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();
} }
protected void appendHeaders(StringBuilder buf) { protected void appendHeaders(StringBuilder buf) {
for (Map.Entry<String, String> e: getHeaders()) { for (Map.Entry<String, String> e: headers().entries()) {
buf.append(" "); buf.append(" ");
buf.append(e.getKey()); buf.append(e.getKey());
buf.append(": "); 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 @Override
public void setStreamId(int streamId) { public SpdyHeadersFrame setStreamId(int streamId) {
if (streamId <= 0) { if (streamId <= 0) {
throw new IllegalArgumentException( throw new IllegalArgumentException(
"Stream-ID must be positive: " + streamId); "Stream-ID must be positive: " + streamId);
} }
this.streamId = streamId; this.streamId = streamId;
return this;
} }
@Override @Override
@ -56,8 +57,15 @@ public class DefaultSpdyHeadersFrame extends DefaultSpdyHeaderBlock
} }
@Override @Override
public void setLast(boolean last) { public SpdyHeadersFrame setLast(boolean last) {
this.last = last; this.last = last;
return this;
}
@Override
public SpdyHeadersFrame setInvalid() {
super.setInvalid();
return this;
} }
@Override @Override

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -289,7 +289,7 @@ public class SpdyFrameEncoder extends MessageToByteEncoder<Object> {
private static ByteBuf encodeHeaderBlock(int version, SpdyHeaderBlock headerFrame) private static ByteBuf encodeHeaderBlock(int version, SpdyHeaderBlock headerFrame)
throws Exception { throws Exception {
Set<String> names = headerFrame.getHeaderNames(); Set<String> names = headerFrame.headers().names();
int numHeaders = names.size(); int numHeaders = names.size();
if (numHeaders == 0) { if (numHeaders == 0) {
return Unpooled.EMPTY_BUFFER; return Unpooled.EMPTY_BUFFER;
@ -307,7 +307,7 @@ public class SpdyFrameEncoder extends MessageToByteEncoder<Object> {
int savedIndex = headerBlock.writerIndex(); int savedIndex = headerBlock.writerIndex();
int valueLength = 0; int valueLength = 0;
writeLengthField(version, headerBlock, valueLength); writeLengthField(version, headerBlock, valueLength);
for (String value: headerFrame.getHeaders(name)) { for (String value: headerFrame.headers().getAll(name)) {
byte[] valueBytes = value.getBytes(CharsetUtil.UTF_8); byte[] valueBytes = value.getBytes(CharsetUtil.UTF_8);
if (valueBytes.length > 0) { if (valueBytes.length > 0) {
headerBlock.writeBytes(valueBytes); 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 * Sets the Last-good-stream-ID of this frame. The Last-good-stream-ID
* cannot be negative. * cannot be negative.
*/ */
void setLastGoodStreamId(int lastGoodStreamId); SpdyGoAwayFrame setLastGoodStreamId(int lastGoodStreamId);
/** /**
* Returns the getStatus of this frame. * Returns the getStatus of this frame.
@ -39,5 +39,5 @@ public interface SpdyGoAwayFrame extends SpdyControlFrame {
/** /**
* Sets the getStatus of this frame. * 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; 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 * A SPDY Name/Value Header Block which provides common properties for
@ -36,68 +33,10 @@ public interface SpdyHeaderBlock {
/** /**
* Marks this header block as invalid. * Marks this header block as invalid.
*/ */
void setInvalid(); SpdyHeaderBlock setInvalid();
/** /**
* Returns the header value with the specified header name. If there is * Returns the {@link SpdyHeaders}.
* 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); SpdyHeaders headers();
/**
* 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

@ -19,18 +19,86 @@ import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion; 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.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.TreeSet;
/** /**
* Provides the constants for the standard SPDY HTTP header names and commonly * Provides the constants for the standard SPDY HTTP header names and commonly
* used utility methods that access a {@link SpdyHeaderBlock}. * used utility methods that access a {@link SpdyHeaderBlock}.
* @apiviz.stereotype static * @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 * SPDY HTTP header names
@ -102,7 +170,7 @@ public class SpdyHeaders {
* @return the header value or {@code null} if there is no such header * @return the header value or {@code null} if there is no such header
*/ */
public static String getHeader(SpdyHeaderBlock block, String name) { 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 * header
*/ */
public static String getHeader(SpdyHeaderBlock block, String name, String defaultValue) { public static String getHeader(SpdyHeaderBlock block, String name, String defaultValue) {
String value = block.getHeader(name); String value = block.headers().get(name);
if (value == null) { if (value == null) {
return defaultValue; return defaultValue;
} }
@ -126,7 +194,7 @@ public class SpdyHeaders {
* existing header with the same name, the existing header is removed. * existing header with the same name, the existing header is removed.
*/ */
public static void setHeader(SpdyHeaderBlock block, String name, Object value) { 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. * existing header with the same name, the existing header is removed.
*/ */
public static void setHeader(SpdyHeaderBlock block, String name, Iterable<?> values) { 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. * Adds a new header with the specified name and value.
*/ */
public static void addHeader(SpdyHeaderBlock block, String name, Object 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. * Removes the SPDY host header.
*/ */
public static void removeHost(SpdyHeaderBlock block) { public static void removeHost(SpdyHeaderBlock block) {
block.removeHeader(HttpNames.HOST); block.headers().remove(HttpNames.HOST);
} }
/** /**
* Returns the SPDY host header. * Returns the SPDY host header.
*/ */
public static String getHost(SpdyHeaderBlock block) { public static String getHost(SpdyHeaderBlock block) {
return block.getHeader(HttpNames.HOST); return block.headers().get(HttpNames.HOST);
} }
/** /**
* Set the SPDY host header. * Set the SPDY host header.
*/ */
public static void setHost(SpdyHeaderBlock block, String host) { 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) { public static void removeMethod(int spdyVersion, SpdyHeaderBlock block) {
if (spdyVersion < 3) { if (spdyVersion < 3) {
block.removeHeader(Spdy2HttpNames.METHOD); block.headers().remove(Spdy2HttpNames.METHOD);
} else { } 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) { public static HttpMethod getMethod(int spdyVersion, SpdyHeaderBlock block) {
try { try {
if (spdyVersion < 3) { if (spdyVersion < 3) {
return HttpMethod.valueOf(block.getHeader(Spdy2HttpNames.METHOD)); return HttpMethod.valueOf(block.headers().get(Spdy2HttpNames.METHOD));
} else { } else {
return HttpMethod.valueOf(block.getHeader(HttpNames.METHOD)); return HttpMethod.valueOf(block.headers().get(HttpNames.METHOD));
} }
} catch (Exception e) { } catch (Exception e) {
return null; return null;
@ -196,9 +264,9 @@ public class SpdyHeaders {
*/ */
public static void setMethod(int spdyVersion, SpdyHeaderBlock block, HttpMethod method) { public static void setMethod(int spdyVersion, SpdyHeaderBlock block, HttpMethod method) {
if (spdyVersion < 3) { if (spdyVersion < 3) {
block.setHeader(Spdy2HttpNames.METHOD, method.name()); block.headers().set(Spdy2HttpNames.METHOD, method.name());
} else { } 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) { public static void removeScheme(int spdyVersion, SpdyHeaderBlock block) {
if (spdyVersion < 2) { if (spdyVersion < 2) {
block.removeHeader(Spdy2HttpNames.SCHEME); block.headers().remove(Spdy2HttpNames.SCHEME);
} else { } 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) { public static String getScheme(int spdyVersion, SpdyHeaderBlock block) {
if (spdyVersion < 3) { if (spdyVersion < 3) {
return block.getHeader(Spdy2HttpNames.SCHEME); return block.headers().get(Spdy2HttpNames.SCHEME);
} else { } 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) { public static void setScheme(int spdyVersion, SpdyHeaderBlock block, String scheme) {
if (spdyVersion < 3) { if (spdyVersion < 3) {
block.setHeader(Spdy2HttpNames.SCHEME, scheme); block.headers().set(Spdy2HttpNames.SCHEME, scheme);
} else { } 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) { public static void removeStatus(int spdyVersion, SpdyHeaderBlock block) {
if (spdyVersion < 3) { if (spdyVersion < 3) {
block.removeHeader(Spdy2HttpNames.STATUS); block.headers().remove(Spdy2HttpNames.STATUS);
} else { } else {
block.removeHeader(HttpNames.STATUS); block.headers().remove(HttpNames.STATUS);
} }
} }
@ -253,9 +321,9 @@ public class SpdyHeaders {
try { try {
String status; String status;
if (spdyVersion < 3) { if (spdyVersion < 3) {
status = block.getHeader(Spdy2HttpNames.STATUS); status = block.headers().get(Spdy2HttpNames.STATUS);
} else { } else {
status = block.getHeader(HttpNames.STATUS); status = block.headers().get(HttpNames.STATUS);
} }
int space = status.indexOf(' '); int space = status.indexOf(' ');
if (space == -1) { if (space == -1) {
@ -280,9 +348,9 @@ public class SpdyHeaders {
*/ */
public static void setStatus(int spdyVersion, SpdyHeaderBlock block, HttpResponseStatus status) { public static void setStatus(int spdyVersion, SpdyHeaderBlock block, HttpResponseStatus status) {
if (spdyVersion < 3) { if (spdyVersion < 3) {
block.setHeader(Spdy2HttpNames.STATUS, status.toString()); block.headers().set(Spdy2HttpNames.STATUS, status.toString());
} else { } 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) { public static void removeUrl(int spdyVersion, SpdyHeaderBlock block) {
if (spdyVersion < 3) { if (spdyVersion < 3) {
block.removeHeader(Spdy2HttpNames.URL); block.headers().remove(Spdy2HttpNames.URL);
} else { } 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) { public static String getUrl(int spdyVersion, SpdyHeaderBlock block) {
if (spdyVersion < 3) { if (spdyVersion < 3) {
return block.getHeader(Spdy2HttpNames.URL); return block.headers().get(Spdy2HttpNames.URL);
} else { } 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) { public static void setUrl(int spdyVersion, SpdyHeaderBlock block, String path) {
if (spdyVersion < 3) { if (spdyVersion < 3) {
block.setHeader(Spdy2HttpNames.URL, path); block.headers().set(Spdy2HttpNames.URL, path);
} else { } 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) { public static void removeVersion(int spdyVersion, SpdyHeaderBlock block) {
if (spdyVersion < 3) { if (spdyVersion < 3) {
block.removeHeader(Spdy2HttpNames.VERSION); block.headers().remove(Spdy2HttpNames.VERSION);
} else { } 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) { public static HttpVersion getVersion(int spdyVersion, SpdyHeaderBlock block) {
try { try {
if (spdyVersion < 3) { if (spdyVersion < 3) {
return HttpVersion.valueOf(block.getHeader(Spdy2HttpNames.VERSION)); return HttpVersion.valueOf(block.headers().get(Spdy2HttpNames.VERSION));
} else { } else {
return HttpVersion.valueOf(block.getHeader(HttpNames.VERSION)); return HttpVersion.valueOf(block.headers().get(HttpNames.VERSION));
} }
} catch (Exception e) { } catch (Exception e) {
return null; return null;
@ -350,295 +418,87 @@ public class SpdyHeaders {
*/ */
public static void setVersion(int spdyVersion, SpdyHeaderBlock block, HttpVersion httpVersion) { public static void setVersion(int spdyVersion, SpdyHeaderBlock block, HttpVersion httpVersion) {
if (spdyVersion < 3) { if (spdyVersion < 3) {
block.setHeader(Spdy2HttpNames.VERSION, httpVersion.text()); block.headers().set(Spdy2HttpNames.VERSION, httpVersion.text());
} else { } else {
block.setHeader(HttpNames.VERSION, httpVersion.text()); block.headers().set(HttpNames.VERSION, httpVersion.text());
} }
} }
@Override
private static final int BUCKET_SIZE = 17; public Iterator<Map.Entry<String, String>> iterator() {
return entries().iterator();
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(); * Returns the header value with the specified header name. If there is
if (nameLen != name2.length()) { * more than one header value for the specified header name, the first
return false; * 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); * Returns the header values with the specified header name.
char c2 = name2.charAt(i); *
if (c1 != c2) { * @return the {@link List} of header values. An empty list if there is no
if (c1 >= 'A' && c1 <= 'Z') { * such header.
c1 += 32; */
} public abstract List<String> getAll(String name);
if (c2 >= 'A' && c2 <= 'Z') {
c2 += 32;
}
if (c1 != c2) {
return false;
}
}
}
return true;
}
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(); * Adds a new header with the specified name and value.
SpdyCodecUtil.validateHeaderName(lowerCaseName); */
String strVal = toString(value); public abstract SpdyHeaders add(String name, Object 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. * Adds a new header with the specified name and values. If there is an
HeaderEntry e = entries[i]; * existing header with the same name, the existing header is removed.
HeaderEntry newEntry; */
entries[i] = newEntry = new HeaderEntry(h, name, value); public abstract SpdyHeaders add(String name, Iterable<?> values);
newEntry.next = e;
// 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) { * Sets a new header with the specified name and values. If there is an
throw new NullPointerException("name"); * existing header with the same name, the existing header is removed.
} */
String lowerCaseName = name.toLowerCase(); public abstract SpdyHeaders set(String name, Iterable<?> values);
int h = hash(lowerCaseName);
int i = index(h);
removeHeader0(h, i, lowerCaseName);
}
private void removeHeader0(int h, int i, String name) { /**
HeaderEntry e = entries[i]; * Removes the header with the specified name.
if (e == null) { */
return; public abstract SpdyHeaders remove(String name);
}
for (;;) { /**
if (e.hash == h && eq(name, e.key)) { * Removes all headers from this block.
e.remove(); */
HeaderEntry next = e.next; public abstract SpdyHeaders clear();
if (next != null) {
entries[i] = next;
e = next;
} else {
entries[i] = null;
return;
}
} else {
break;
}
}
for (;;) { /**
HeaderEntry next = e.next; * Checks if no header exists.
if (next == null) { */
break; public abstract boolean isEmpty();
}
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;
}
}
} }

View File

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

@ -182,7 +182,7 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<Object> {
return null; 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()); httpMessage.headers().add(e.getKey(), e.getValue());
} }
@ -247,7 +247,7 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<Object> {
HttpHeaders.setHost(req, host); 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()); req.headers().add(e.getKey(), e.getValue());
} }
@ -269,7 +269,7 @@ public class SpdyHttpDecoder extends MessageToMessageDecoder<Object> {
SpdyHeaders.removeVersion(spdyVersion, responseFrame); SpdyHeaders.removeVersion(spdyVersion, responseFrame);
FullHttpResponse res = new DefaultFullHttpResponse(version, status); 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()); 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 // Create SPDY HEADERS frame out of trailers
SpdyHeadersFrame spdyHeadersFrame = new DefaultSpdyHeadersFrame(currentStreamId); SpdyHeadersFrame spdyHeadersFrame = new DefaultSpdyHeadersFrame(currentStreamId);
for (Map.Entry<String, String> entry: trailers) { 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 // Write HEADERS frame and append Data Frame
@ -246,7 +246,7 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder<Object> {
// Transfer the remaining HTTP headers // Transfer the remaining HTTP headers
for (Map.Entry<String, String> entry: httpMessage.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(); currentStreamId = spdySynStreamFrame.getStreamId();
@ -276,7 +276,7 @@ public class SpdyHttpEncoder extends MessageToMessageEncoder<Object> {
// Transfer the remaining HTTP headers // Transfer the remaining HTTP headers
for (Map.Entry<String, String> entry: httpResponse.headers()) { for (Map.Entry<String, String> entry: httpResponse.headers()) {
spdySynReplyFrame.addHeader(entry.getKey(), entry.getValue()); spdySynReplyFrame.headers().add(entry.getKey(), entry.getValue());
} }
if (chunked) { if (chunked) {

View File

@ -28,5 +28,5 @@ public interface SpdyPingFrame extends SpdyControlFrame {
/** /**
* Sets the ID of this frame. * 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. * 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. * Returns the getStatus of this frame.
@ -38,5 +38,5 @@ public interface SpdyRstStreamFrame extends SpdyControlFrame {
/** /**
* Sets the getStatus of this frame. * 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. * Sets the value of the setting ID.
* The ID must be positive and cannot exceed 16777215. * 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. * 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). * Sets if the setting is persisted (should only be set by the client).
* The ID must be positive and cannot exceed 16777215. * 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 the value of the setting ID.
* Removes all persistence information for the setting. * Removes all persistence information for the setting.
*/ */
void removeValue(int id); SpdySettingsFrame removeValue(int id);
/** /**
* Returns {@code true} if this setting should be persisted. * 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. * Sets if this setting should be persisted.
* Has no effect if the setting ID has no value. * 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. * Returns {@code true} if this setting is persisted.
@ -92,7 +92,7 @@ public interface SpdySettingsFrame extends SpdyControlFrame {
* Sets if this setting is persisted. * Sets if this setting is persisted.
* Has no effect if the setting ID has no value. * 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. * 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. * 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 * A SPDY Protocol SYN_REPLY Control Frame
*/ */
public interface SpdySynReplyFrame extends SpdyHeaderBlock, SpdyControlFrame { public interface SpdySynReplyFrame extends SpdyHeaderBlock, SpdyControlFrame, SpdyStreamFrame {
/** @Override
* Returns the Stream-ID of this frame. SpdySynReplyFrame setStreamId(int streamID);
*/
int getStreamId();
/** @Override
* Sets the Stream-ID of this frame. The Stream-ID must be positive. SpdySynReplyFrame setLast(boolean last);
*/
void setStreamId(int streamID);
/** @Override
* Returns {@code true} if this frame is the last frame to be transmitted SpdySynReplyFrame setInvalid();
* 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

@ -18,17 +18,7 @@ package io.netty.handler.codec.spdy;
/** /**
* A SPDY Protocol SYN_STREAM Control Frame * A SPDY Protocol SYN_STREAM Control Frame
*/ */
public interface SpdySynStreamFrame extends SpdyHeaderBlock, SpdyControlFrame { public interface SpdySynStreamFrame extends SpdyHeaderBlock, SpdyControlFrame , SpdyStreamFrame {
/**
* 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. * 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. * Sets the Associated-To-Stream-ID of this frame.
* The Associated-To-Stream-ID cannot be negative. * The Associated-To-Stream-ID cannot be negative.
*/ */
void setAssociatedToStreamId(int associatedToStreamId); SpdySynStreamFrame setAssociatedToStreamId(int associatedToStreamId);
/** /**
* Returns the priority of the stream. * Returns the priority of the stream.
@ -50,18 +40,7 @@ public interface SpdySynStreamFrame extends SpdyHeaderBlock, SpdyControlFrame {
* Sets the priority of the stream. * Sets the priority of the stream.
* The priority must be between 0 and 7 inclusive. * The priority must be between 0 and 7 inclusive.
*/ */
void setPriority(byte priority); SpdySynStreamFrame 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 * 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 * Sets if the stream created with this frame is to be considered
* half-closed to the receiver. * 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. * 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. * 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. * Sets the Delta-Window-Size of this frame.
* The Delta-Window-Size must be positive. * 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) { private static void assertHeaderBlock(SpdyHeaderBlock received, SpdyHeaderBlock expected) {
for (String name: expected.getHeaderNames()) { for (String name: expected.headers().names()) {
List<String> expectedValues = expected.getHeaders(name); List<String> expectedValues = expected.headers().getAll(name);
List<String> receivedValues = received.getHeaders(name); List<String> receivedValues = received.headers().getAll(name);
assertTrue(receivedValues.containsAll(expectedValues)); assertTrue(receivedValues.containsAll(expectedValues));
receivedValues.removeAll(expectedValues); receivedValues.removeAll(expectedValues);
assertTrue(receivedValues.isEmpty()); 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) { private static void assertDataFrame(Object msg, int streamID, boolean last) {
@ -114,7 +114,7 @@ public class SpdySessionHandlerTest {
SpdySynStreamFrame spdySynStreamFrame = SpdySynStreamFrame spdySynStreamFrame =
new DefaultSpdySynStreamFrame(localStreamID, 0, (byte) 0); new DefaultSpdySynStreamFrame(localStreamID, 0, (byte) 0);
spdySynStreamFrame.setHeader("Compression", "test"); spdySynStreamFrame.headers().set("Compression", "test");
SpdyDataFrame spdyDataFrame = new DefaultSpdyDataFrame(localStreamID); SpdyDataFrame spdyDataFrame = new DefaultSpdyDataFrame(localStreamID);
spdyDataFrame.setLast(true); spdyDataFrame.setLast(true);
@ -146,8 +146,10 @@ public class SpdySessionHandlerTest {
assertSynReply(sessionHandler.readOutbound(), localStreamID, false, spdySynStreamFrame); assertSynReply(sessionHandler.readOutbound(), localStreamID, false, spdySynStreamFrame);
assertNull(sessionHandler.readOutbound()); assertNull(sessionHandler.readOutbound());
SpdyHeadersFrame spdyHeadersFrame = new DefaultSpdyHeadersFrame(localStreamID); 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); sessionHandler.writeInbound(spdyHeadersFrame);
assertHeaders(sessionHandler.readOutbound(), localStreamID, spdyHeadersFrame); assertHeaders(sessionHandler.readOutbound(), localStreamID, spdyHeadersFrame);
assertNull(sessionHandler.readOutbound()); assertNull(sessionHandler.readOutbound());
@ -159,6 +161,7 @@ public class SpdySessionHandlerTest {
spdySynStreamFrame.setStreamId(localStreamID); spdySynStreamFrame.setStreamId(localStreamID);
spdySynStreamFrame.setLast(true); spdySynStreamFrame.setLast(true);
spdySynStreamFrame.setUnidirectional(true); spdySynStreamFrame.setUnidirectional(true);
sessionHandler.writeInbound(spdySynStreamFrame); sessionHandler.writeInbound(spdySynStreamFrame);
assertRstStream(sessionHandler.readOutbound(), localStreamID, SpdyStreamStatus.REFUSED_STREAM); assertRstStream(sessionHandler.readOutbound(), localStreamID, SpdyStreamStatus.REFUSED_STREAM);
assertNull(sessionHandler.readOutbound()); assertNull(sessionHandler.readOutbound());
@ -175,6 +178,7 @@ public class SpdySessionHandlerTest {
assertNull(sessionHandler.readOutbound()); assertNull(sessionHandler.readOutbound());
spdySynStreamFrame.setUnidirectional(false); spdySynStreamFrame.setUnidirectional(false);
// Check if session handler returns PROTOCOL_ERROR if it receives // Check if session handler returns PROTOCOL_ERROR if it receives
// multiple SYN_STREAM frames for the same active Stream-ID // multiple SYN_STREAM frames for the same active Stream-ID
sessionHandler.writeInbound(spdySynStreamFrame); sessionHandler.writeInbound(spdySynStreamFrame);
@ -190,6 +194,7 @@ public class SpdySessionHandlerTest {
assertNull(sessionHandler.readOutbound()); assertNull(sessionHandler.readOutbound());
spdySynStreamFrame.setStreamId(localStreamID); spdySynStreamFrame.setStreamId(localStreamID);
// Check if session handler correctly limits the number of // Check if session handler correctly limits the number of
// concurrent streams in the SETTINGS frame // concurrent streams in the SETTINGS frame
SpdySettingsFrame spdySettingsFrame = new DefaultSpdySettingsFrame(); SpdySettingsFrame spdySettingsFrame = new DefaultSpdySettingsFrame();
@ -212,6 +217,7 @@ public class SpdySessionHandlerTest {
assertDataFrame(sessionHandler.readOutbound(), testStreamID, spdyDataFrame.isLast()); assertDataFrame(sessionHandler.readOutbound(), testStreamID, spdyDataFrame.isLast());
assertNull(sessionHandler.readOutbound()); assertNull(sessionHandler.readOutbound());
spdyHeadersFrame.setStreamId(testStreamID); spdyHeadersFrame.setStreamId(testStreamID);
sessionHandler.writeInbound(spdyHeadersFrame); sessionHandler.writeInbound(spdyHeadersFrame);
assertRstStream(sessionHandler.readOutbound(), testStreamID, SpdyStreamStatus.INVALID_STREAM); assertRstStream(sessionHandler.readOutbound(), testStreamID, SpdyStreamStatus.INVALID_STREAM);
assertNull(sessionHandler.readOutbound()); assertNull(sessionHandler.readOutbound());
@ -219,6 +225,7 @@ public class SpdySessionHandlerTest {
// Check if session handler returns PROTOCOL_ERROR if it receives // Check if session handler returns PROTOCOL_ERROR if it receives
// an invalid HEADERS frame // an invalid HEADERS frame
spdyHeadersFrame.setStreamId(localStreamID); spdyHeadersFrame.setStreamId(localStreamID);
spdyHeadersFrame.setInvalid(); spdyHeadersFrame.setInvalid();
sessionHandler.writeInbound(spdyHeadersFrame); sessionHandler.writeInbound(spdyHeadersFrame);
assertRstStream(sessionHandler.readOutbound(), localStreamID, SpdyStreamStatus.PROTOCOL_ERROR); assertRstStream(sessionHandler.readOutbound(), localStreamID, SpdyStreamStatus.PROTOCOL_ERROR);
@ -320,8 +327,8 @@ public class SpdySessionHandlerTest {
int streamID = spdySynStreamFrame.getStreamId(); int streamID = spdySynStreamFrame.getStreamId();
SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamID); SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamID);
spdySynReplyFrame.setLast(spdySynStreamFrame.isLast()); spdySynReplyFrame.setLast(spdySynStreamFrame.isLast());
for (Map.Entry<String, String> entry: spdySynStreamFrame.getHeaders()) { for (Map.Entry<String, String> entry: spdySynStreamFrame.headers()) {
spdySynReplyFrame.addHeader(entry.getKey(), entry.getValue()); spdySynReplyFrame.headers().add(entry.getKey(), entry.getValue());
} }
ctx.write(spdySynReplyFrame); ctx.write(spdySynReplyFrame);