1
2
3
4
5
6
7
8
9
10
11
12
13
14 package org.eclipse.jetty.spdy.parser;
15
16 import java.nio.ByteBuffer;
17
18 import org.eclipse.jetty.spdy.CompressionFactory;
19 import org.eclipse.jetty.spdy.api.Headers;
20 import org.eclipse.jetty.spdy.api.HeadersInfo;
21 import org.eclipse.jetty.spdy.api.SPDY;
22 import org.eclipse.jetty.spdy.frames.ControlFrameType;
23 import org.eclipse.jetty.spdy.frames.HeadersFrame;
24
25 public class HeadersBodyParser extends ControlFrameBodyParser
26 {
27 private final Headers headers = new Headers();
28 private final ControlFrameParser controlFrameParser;
29 private final HeadersBlockParser headersBlockParser;
30 private State state = State.STREAM_ID;
31 private int cursor;
32 private int streamId;
33
34 public HeadersBodyParser(CompressionFactory.Decompressor decompressor, ControlFrameParser controlFrameParser)
35 {
36 this.controlFrameParser = controlFrameParser;
37 this.headersBlockParser = new HeadersHeadersBlockParser(decompressor);
38 }
39
40 @Override
41 public boolean parse(ByteBuffer buffer)
42 {
43 while (buffer.hasRemaining())
44 {
45 switch (state)
46 {
47 case STREAM_ID:
48 {
49 if (buffer.remaining() >= 4)
50 {
51 streamId = buffer.getInt() & 0x7F_FF_FF_FF;
52 state = State.ADDITIONAL;
53 }
54 else
55 {
56 state = State.STREAM_ID_BYTES;
57 cursor = 4;
58 }
59 break;
60 }
61 case STREAM_ID_BYTES:
62 {
63 byte currByte = buffer.get();
64 --cursor;
65 streamId += (currByte & 0xFF) << 8 * cursor;
66 if (cursor == 0)
67 {
68 streamId &= 0x7F_FF_FF_FF;
69 state = State.ADDITIONAL;
70 }
71 break;
72 }
73 case ADDITIONAL:
74 {
75 switch (controlFrameParser.getVersion())
76 {
77 case SPDY.V2:
78 {
79 if (buffer.remaining() >= 2)
80 {
81 buffer.getShort();
82 state = State.HEADERS;
83 }
84 else
85 {
86 state = State.ADDITIONAL_BYTES;
87 cursor = 2;
88 }
89 break;
90 }
91 case SPDY.V3:
92 {
93 state = State.HEADERS;
94 break;
95 }
96 default:
97 {
98 throw new IllegalStateException();
99 }
100 }
101 break;
102 }
103 case ADDITIONAL_BYTES:
104 {
105 assert controlFrameParser.getVersion() == SPDY.V2;
106 buffer.get();
107 --cursor;
108 if (cursor == 0)
109 state = State.HEADERS;
110 break;
111 }
112 case HEADERS:
113 {
114 short version = controlFrameParser.getVersion();
115 int length = controlFrameParser.getLength() - 4;
116 if (version == SPDY.V2)
117 length -= 2;
118 if (headersBlockParser.parse(streamId, version, length, buffer))
119 {
120 byte flags = controlFrameParser.getFlags();
121 if (flags != 0 && flags != HeadersInfo.FLAG_CLOSE && flags != HeadersInfo.FLAG_RESET_COMPRESSION)
122 throw new IllegalArgumentException("Invalid flag " + flags + " for frame " + ControlFrameType.HEADERS);
123
124 HeadersFrame frame = new HeadersFrame(version, flags, streamId, new Headers(headers, true));
125 controlFrameParser.onControlFrame(frame);
126
127 reset();
128 return true;
129 }
130 break;
131 }
132 default:
133 {
134 throw new IllegalStateException();
135 }
136 }
137 }
138 return false;
139 }
140
141 private void reset()
142 {
143 headers.clear();
144 state = State.STREAM_ID;
145 cursor = 0;
146 streamId = 0;
147 }
148
149 private enum State
150 {
151 STREAM_ID, STREAM_ID_BYTES, ADDITIONAL, ADDITIONAL_BYTES, HEADERS
152 }
153
154 private class HeadersHeadersBlockParser extends HeadersBlockParser
155 {
156 public HeadersHeadersBlockParser(CompressionFactory.Decompressor decompressor)
157 {
158 super(decompressor);
159 }
160
161 @Override
162 protected void onHeader(String name, String[] values)
163 {
164 for (String value : values)
165 headers.add(name, value);
166 }
167 }
168 }