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