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 import java.nio.charset.StandardCharsets;
23 import java.util.zip.ZipException;
24
25 import org.eclipse.jetty.spdy.CompressionDictionary;
26 import org.eclipse.jetty.spdy.CompressionFactory;
27 import org.eclipse.jetty.spdy.SessionException;
28 import org.eclipse.jetty.spdy.StreamException;
29 import org.eclipse.jetty.spdy.api.SPDY;
30 import org.eclipse.jetty.spdy.api.SessionStatus;
31 import org.eclipse.jetty.spdy.api.StreamStatus;
32
33 public abstract class HeadersBlockParser
34 {
35 private final CompressionFactory.Decompressor decompressor;
36 private byte[] data;
37 private boolean needsDictionary = true;
38
39 protected HeadersBlockParser(CompressionFactory.Decompressor decompressor)
40 {
41 this.decompressor = decompressor;
42 }
43
44 public boolean parse(int streamId, short version, int length, ByteBuffer buffer)
45 {
46
47
48
49
50
51
52 boolean accumulated = accumulate(length, buffer);
53 if (!accumulated)
54 return false;
55
56 byte[] compressedHeaders = data;
57 data = null;
58 ByteBuffer decompressedHeaders = decompress(version, compressedHeaders);
59
60
61
62 int count = readCount(version, decompressedHeaders);
63 for (int i = 0; i < count; ++i)
64 {
65 int nameLength = readNameLength(version, decompressedHeaders);
66 if (nameLength == 0)
67 throw new StreamException(streamId, StreamStatus.PROTOCOL_ERROR, "Invalid header name length");
68 byte[] nameBytes = new byte[nameLength];
69 decompressedHeaders.get(nameBytes);
70 String name = new String(nameBytes, StandardCharsets.ISO_8859_1);
71
72 int valueLength = readValueLength(version, decompressedHeaders);
73 if (valueLength == 0)
74 throw new StreamException(streamId, StreamStatus.PROTOCOL_ERROR, "Invalid header value length");
75 byte[] valueBytes = new byte[valueLength];
76 decompressedHeaders.get(valueBytes);
77 String value = new String(valueBytes, StandardCharsets.ISO_8859_1);
78
79 String[] values = value.split("\u0000");
80
81 for (String v : values)
82 if (v.length() == 0)
83 throw new StreamException(streamId, StreamStatus.PROTOCOL_ERROR, "Invalid multi valued header");
84
85 onHeader(name, values);
86 }
87
88 return true;
89 }
90
91 private boolean accumulate(int length, ByteBuffer buffer)
92 {
93 int remaining = buffer.remaining();
94 if (data == null)
95 {
96 if (remaining < length)
97 {
98 data = new byte[remaining];
99 buffer.get(data);
100 return false;
101 }
102 else
103 {
104 data = new byte[length];
105 buffer.get(data);
106 return true;
107 }
108 }
109 else
110 {
111 int accumulated = data.length;
112 int needed = length - accumulated;
113 if (remaining < needed)
114 {
115 byte[] local = new byte[accumulated + remaining];
116 System.arraycopy(data, 0, local, 0, accumulated);
117 buffer.get(local, accumulated, remaining);
118 data = local;
119 return false;
120 }
121 else
122 {
123 byte[] local = new byte[length];
124 System.arraycopy(data, 0, local, 0, accumulated);
125 buffer.get(local, accumulated, needed);
126 data = local;
127 return true;
128 }
129 }
130 }
131
132 private int readCount(int version, ByteBuffer buffer)
133 {
134 switch (version)
135 {
136 case SPDY.V2:
137 return buffer.getShort();
138 case SPDY.V3:
139 return buffer.getInt();
140 default:
141 throw new IllegalStateException();
142 }
143 }
144
145 private int readNameLength(int version, ByteBuffer buffer)
146 {
147 return readCount(version, buffer);
148 }
149
150 private int readValueLength(int version, ByteBuffer buffer)
151 {
152 return readCount(version, buffer);
153 }
154
155 protected abstract void onHeader(String name, String[] values);
156
157 private ByteBuffer decompress(short version, byte[] compressed)
158 {
159
160
161
162
163 try
164 {
165 byte[] decompressed = null;
166 byte[] buffer = new byte[compressed.length * 2];
167 decompressor.setInput(compressed);
168
169 while (true)
170 {
171 int count = decompressor.decompress(buffer);
172 if (count == 0)
173 {
174 if (decompressed != null)
175 {
176 return ByteBuffer.wrap(decompressed);
177 }
178 else if (needsDictionary)
179 {
180 decompressor.setDictionary(CompressionDictionary.get(version));
181 needsDictionary = false;
182 }
183 else
184 {
185 throw new IllegalStateException();
186 }
187 }
188 else
189 {
190 if (count < buffer.length)
191 {
192 if (decompressed == null)
193 {
194
195 return ByteBuffer.wrap(buffer, 0, count);
196 }
197 else
198 {
199
200 byte[] result = new byte[decompressed.length + count];
201 System.arraycopy(decompressed, 0, result, 0, decompressed.length);
202 System.arraycopy(buffer, 0, result, decompressed.length, count);
203 return ByteBuffer.wrap(result);
204 }
205 }
206 else
207 {
208 if (decompressed == null)
209 {
210 decompressed = buffer;
211 buffer = new byte[buffer.length];
212 }
213 else
214 {
215 byte[] result = new byte[decompressed.length + buffer.length];
216 System.arraycopy(decompressed, 0, result, 0, decompressed.length);
217 System.arraycopy(buffer, 0, result, decompressed.length, buffer.length);
218 decompressed = result;
219 }
220 }
221 }
222 }
223 }
224 catch (ZipException x)
225 {
226
227
228 throw new SessionException(SessionStatus.PROTOCOL_ERROR, x);
229 }
230 }
231 }