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