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