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