SPDY: refactor SpdyHttpDecoder to allow subclasses to access messageMap

This commit is contained in:
Jeff Pinner 2013-05-02 17:17:36 -04:00 committed by Norman Maurer
parent a00f9f0b70
commit 79648026ff

View File

@ -45,7 +45,7 @@ public class SpdyHttpDecoder extends OneToOneDecoder {
private final int spdyVersion;
private final int maxContentLength;
private final Map<Integer, HttpMessage> messageMap = new HashMap<Integer, HttpMessage>();
private final Map<Integer, HttpMessage> messageMap;
/**
* Creates a new instance for the SPDY/2 protocol
@ -68,6 +68,19 @@ public class SpdyHttpDecoder extends OneToOneDecoder {
* a {@link TooLongFrameException} will be raised.
*/
public SpdyHttpDecoder(int version, int maxContentLength) {
this(version, maxContentLength, new HashMap<Integer, HttpMessage>());
}
/**
* Creates a new instance with the specified parameters.
*
* @param version the protocol version
* @param maxContentLength the maximum length of the message content.
* If the length of the message content exceeds this value,
* a {@link TooLongFrameException} will be raised.
* @param messageMap the {@link Map} used to hold partially received messages.
*/
protected SpdyHttpDecoder(int version, int maxContentLength, Map<Integer, HttpMessage> messageMap) {
if (version < SPDY_MIN_VERSION || version > SPDY_MAX_VERSION) {
throw new IllegalArgumentException(
"unsupported version: " + version);
@ -78,6 +91,19 @@ public class SpdyHttpDecoder extends OneToOneDecoder {
}
spdyVersion = version;
this.maxContentLength = maxContentLength;
this.messageMap = messageMap;
}
protected HttpMessage putMessage(int streamId, HttpMessage message) {
return messageMap.put(streamId, message);
}
protected HttpMessage getMessage(int streamId) {
return messageMap.get(streamId);
}
protected HttpMessage removeMessage(int streamId) {
return messageMap.remove(streamId);
}
@Override
@ -88,17 +114,17 @@ public class SpdyHttpDecoder extends OneToOneDecoder {
// HTTP requests/responses are mapped one-to-one to SPDY streams.
SpdySynStreamFrame spdySynStreamFrame = (SpdySynStreamFrame) msg;
int streamID = spdySynStreamFrame.getStreamId();
int streamId = spdySynStreamFrame.getStreamId();
if (isServerId(streamID)) {
if (isServerId(streamId)) {
// SYN_STREAM frames initiated by the server are pushed resources
int associatedToStreamID = spdySynStreamFrame.getAssociatedToStreamId();
int associatedToStreamId = spdySynStreamFrame.getAssociatedToStreamId();
// If a client receives a SYN_STREAM with an Associated-To-Stream-ID of 0
// it must reply with a RST_STREAM with error code INVALID_STREAM
if (associatedToStreamID == 0) {
if (associatedToStreamId == 0) {
SpdyRstStreamFrame spdyRstStreamFrame =
new DefaultSpdyRstStreamFrame(streamID, SpdyStreamStatus.INVALID_STREAM);
new DefaultSpdyRstStreamFrame(streamId, SpdyStreamStatus.INVALID_STREAM);
Channels.write(ctx, Channels.future(channel), spdyRstStreamFrame);
}
@ -108,7 +134,7 @@ public class SpdyHttpDecoder extends OneToOneDecoder {
// it must reply with a RST_STREAM with error code PROTOCOL_ERROR
if (URL == null) {
SpdyRstStreamFrame spdyRstStreamFrame =
new DefaultSpdyRstStreamFrame(streamID, SpdyStreamStatus.PROTOCOL_ERROR);
new DefaultSpdyRstStreamFrame(streamId, SpdyStreamStatus.PROTOCOL_ERROR);
Channels.write(ctx, Channels.future(channel), spdyRstStreamFrame);
}
@ -116,8 +142,8 @@ public class SpdyHttpDecoder extends OneToOneDecoder {
HttpResponse httpResponse = createHttpResponse(spdyVersion, spdySynStreamFrame);
// Set the Stream-ID, Associated-To-Stream-ID, Priority, and URL as headers
SpdyHttpHeaders.setStreamId(httpResponse, streamID);
SpdyHttpHeaders.setAssociatedToStreamId(httpResponse, associatedToStreamID);
SpdyHttpHeaders.setStreamId(httpResponse, streamId);
SpdyHttpHeaders.setAssociatedToStreamId(httpResponse, associatedToStreamId);
SpdyHttpHeaders.setPriority(httpResponse, spdySynStreamFrame.getPriority());
SpdyHttpHeaders.setUrl(httpResponse, URL);
@ -126,11 +152,11 @@ public class SpdyHttpDecoder extends OneToOneDecoder {
return httpResponse;
} else {
// Response body will follow in a series of Data Frames
messageMap.put(streamID, httpResponse);
putMessage(streamId, httpResponse);
}
} catch (Exception e) {
SpdyRstStreamFrame spdyRstStreamFrame =
new DefaultSpdyRstStreamFrame(streamID, SpdyStreamStatus.PROTOCOL_ERROR);
new DefaultSpdyRstStreamFrame(streamId, SpdyStreamStatus.PROTOCOL_ERROR);
Channels.write(ctx, Channels.future(channel), spdyRstStreamFrame);
}
} else {
@ -139,19 +165,19 @@ public class SpdyHttpDecoder extends OneToOneDecoder {
HttpRequest httpRequest = createHttpRequest(spdyVersion, spdySynStreamFrame);
// Set the Stream-ID as a header
SpdyHttpHeaders.setStreamId(httpRequest, streamID);
SpdyHttpHeaders.setStreamId(httpRequest, streamId);
if (spdySynStreamFrame.isLast()) {
return httpRequest;
} else {
// Request body will follow in a series of Data Frames
messageMap.put(streamID, httpRequest);
putMessage(streamId, httpRequest);
}
} catch (Exception e) {
// If a client sends a SYN_STREAM without all of the method, url (host and path),
// scheme, and version headers the server must reply with a HTTP 400 BAD REQUEST reply.
// Also sends HTTP 400 BAD REQUEST reply if header name/value pairs are invalid
SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamID);
SpdySynReplyFrame spdySynReplyFrame = new DefaultSpdySynReplyFrame(streamId);
spdySynReplyFrame.setLast(true);
SpdyHeaders.setStatus(spdyVersion, spdySynReplyFrame, HttpResponseStatus.BAD_REQUEST);
SpdyHeaders.setVersion(spdyVersion, spdySynReplyFrame, HttpVersion.HTTP_1_0);
@ -162,37 +188,36 @@ public class SpdyHttpDecoder extends OneToOneDecoder {
} else if (msg instanceof SpdySynReplyFrame) {
SpdySynReplyFrame spdySynReplyFrame = (SpdySynReplyFrame) msg;
int streamID = spdySynReplyFrame.getStreamId();
int streamId = spdySynReplyFrame.getStreamId();
try {
HttpResponse httpResponse = createHttpResponse(spdyVersion, spdySynReplyFrame);
// Set the Stream-ID as a header
SpdyHttpHeaders.setStreamId(httpResponse, streamID);
SpdyHttpHeaders.setStreamId(httpResponse, streamId);
if (spdySynReplyFrame.isLast()) {
HttpHeaders.setContentLength(httpResponse, 0);
return httpResponse;
} else {
// Response body will follow in a series of Data Frames
messageMap.put(streamID, httpResponse);
putMessage(streamId, httpResponse);
}
} catch (Exception e) {
// If a client receives a SYN_REPLY without valid status and version headers
// the client must reply with a RST_STREAM frame indicating a PROTOCOL_ERROR
SpdyRstStreamFrame spdyRstStreamFrame =
new DefaultSpdyRstStreamFrame(streamID, SpdyStreamStatus.PROTOCOL_ERROR);
new DefaultSpdyRstStreamFrame(streamId, SpdyStreamStatus.PROTOCOL_ERROR);
Channels.write(ctx, Channels.future(channel), spdyRstStreamFrame);
}
} else if (msg instanceof SpdyHeadersFrame) {
SpdyHeadersFrame spdyHeadersFrame = (SpdyHeadersFrame) msg;
Integer streamID = spdyHeadersFrame.getStreamId();
HttpMessage httpMessage = messageMap.get(streamID);
Integer streamId = spdyHeadersFrame.getStreamId();
HttpMessage httpMessage = getMessage(streamId);
// If message is not in map discard HEADERS frame.
// SpdySessionHandler should prevent this from happening.
if (httpMessage == null) {
return null;
}
@ -201,21 +226,25 @@ public class SpdyHttpDecoder extends OneToOneDecoder {
httpMessage.addHeader(e.getKey(), e.getValue());
}
if (spdyHeadersFrame.isLast()) {
removeMessage(streamId);
return httpMessage;
}
} else if (msg instanceof SpdyDataFrame) {
SpdyDataFrame spdyDataFrame = (SpdyDataFrame) msg;
Integer streamID = spdyDataFrame.getStreamId();
HttpMessage httpMessage = messageMap.get(streamID);
Integer streamId = spdyDataFrame.getStreamId();
HttpMessage httpMessage = getMessage(streamId);
// If message is not in map discard Data Frame.
// SpdySessionHandler should prevent this from happening.
if (httpMessage == null) {
return null;
}
ChannelBuffer content = httpMessage.getContent();
if (content.readableBytes() > maxContentLength - spdyDataFrame.getData().readableBytes()) {
messageMap.remove(streamID);
removeMessage(streamId);
throw new TooLongFrameException(
"HTTP content length exceeded " + maxContentLength + " bytes.");
}
@ -230,15 +259,15 @@ public class SpdyHttpDecoder extends OneToOneDecoder {
if (spdyDataFrame.isLast()) {
HttpHeaders.setContentLength(httpMessage, content.readableBytes());
messageMap.remove(streamID);
removeMessage(streamId);
return httpMessage;
}
} else if (msg instanceof SpdyRstStreamFrame) {
SpdyRstStreamFrame spdyRstStreamFrame = (SpdyRstStreamFrame) msg;
Integer streamID = spdyRstStreamFrame.getStreamId();
messageMap.remove(streamID);
Integer streamId = spdyRstStreamFrame.getStreamId();
removeMessage(streamId);
}
return null;