1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.jboss.netty.handler.codec.spdy;
17
18 import org.jboss.netty.buffer.ChannelBuffer;
19 import org.jboss.netty.buffer.ChannelBuffers;
20 import org.jboss.netty.channel.ChannelDownstreamHandler;
21 import org.jboss.netty.channel.ChannelEvent;
22 import org.jboss.netty.channel.ChannelHandlerContext;
23 import org.jboss.netty.channel.ChannelStateEvent;
24 import org.jboss.netty.channel.Channels;
25 import org.jboss.netty.channel.MessageEvent;
26
27 import java.nio.ByteOrder;
28 import java.util.Set;
29
30 import static org.jboss.netty.handler.codec.spdy.SpdyCodecUtil.*;
31
32
33
34
35 public class SpdyFrameEncoder implements ChannelDownstreamHandler {
36
37 private final int version;
38 private volatile boolean finished;
39 private final SpdyHeaderBlockCompressor headerBlockCompressor;
40
41
42
43
44
45
46 @Deprecated
47 public SpdyFrameEncoder() {
48 this(2, 6, 15, 8);
49 }
50
51
52
53
54
55
56 public SpdyFrameEncoder(int version) {
57 this(version, 6, 15, 8);
58 }
59
60
61
62
63
64 @Deprecated
65 public SpdyFrameEncoder(int compressionLevel, int windowBits, int memLevel) {
66 this(2, compressionLevel, windowBits, memLevel);
67 }
68
69
70
71
72 public SpdyFrameEncoder(int version, int compressionLevel, int windowBits, int memLevel) {
73 if (version < SPDY_MIN_VERSION || version > SPDY_MAX_VERSION) {
74 throw new IllegalArgumentException(
75 "unknown version: " + version);
76 }
77 this.version = version;
78 headerBlockCompressor = SpdyHeaderBlockCompressor.newInstance(
79 version, compressionLevel, windowBits, memLevel);
80 }
81
82 public void handleDownstream(
83 final ChannelHandlerContext ctx, ChannelEvent evt) throws Exception {
84 if (evt instanceof ChannelStateEvent) {
85 ChannelStateEvent e = (ChannelStateEvent) evt;
86 switch (e.getState()) {
87 case OPEN:
88 case CONNECTED:
89 case BOUND:
90 if (Boolean.FALSE.equals(e.getValue()) || e.getValue() == null) {
91 synchronized (headerBlockCompressor) {
92 finished = true;
93 headerBlockCompressor.end();
94 }
95 }
96 }
97 }
98
99 if (!(evt instanceof MessageEvent)) {
100 ctx.sendDownstream(evt);
101 return;
102 }
103
104 final MessageEvent e = (MessageEvent) evt;
105 Object msg = e.getMessage();
106
107 if (msg instanceof SpdyDataFrame) {
108
109 SpdyDataFrame spdyDataFrame = (SpdyDataFrame) msg;
110 ChannelBuffer data = spdyDataFrame.getData();
111 byte flags = spdyDataFrame.isLast() ? SPDY_DATA_FLAG_FIN : 0;
112 ChannelBuffer header = ChannelBuffers.buffer(
113 ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE);
114 header.writeInt(spdyDataFrame.getStreamId() & 0x7FFFFFFF);
115 header.writeByte(flags);
116 header.writeMedium(data.readableBytes());
117 ChannelBuffer frame = ChannelBuffers.wrappedBuffer(header, data);
118 Channels.write(ctx, e.getFuture(), frame, e.getRemoteAddress());
119 return;
120 }
121
122 if (msg instanceof SpdySynStreamFrame) {
123
124 synchronized (headerBlockCompressor) {
125 SpdySynStreamFrame spdySynStreamFrame = (SpdySynStreamFrame) msg;
126 ChannelBuffer data = compressHeaderBlock(
127 encodeHeaderBlock(version, spdySynStreamFrame));
128 byte flags = spdySynStreamFrame.isLast() ? SPDY_FLAG_FIN : 0;
129 if (spdySynStreamFrame.isUnidirectional()) {
130 flags |= SPDY_FLAG_UNIDIRECTIONAL;
131 }
132 int headerBlockLength = data.readableBytes();
133 int length;
134 if (version < 3) {
135 length = headerBlockLength == 0 ? 12 : 10 + headerBlockLength;
136 } else {
137 length = 10 + headerBlockLength;
138 }
139 ChannelBuffer frame = ChannelBuffers.buffer(
140 ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + 12);
141 frame.writeShort(version | 0x8000);
142 frame.writeShort(SPDY_SYN_STREAM_FRAME);
143 frame.writeByte(flags);
144 frame.writeMedium(length);
145 frame.writeInt(spdySynStreamFrame.getStreamId());
146 frame.writeInt(spdySynStreamFrame.getAssociatedToStreamId());
147 if (version < 3) {
148
149 byte priority = spdySynStreamFrame.getPriority();
150 if (priority > 3) {
151 priority = 3;
152 }
153 frame.writeShort((priority & 0xFF) << 14);
154 } else {
155 frame.writeShort((spdySynStreamFrame.getPriority() & 0xFF) << 13);
156 }
157 if (version < 3 && data.readableBytes() == 0) {
158 frame.writeShort(0);
159 }
160
161 final ChannelBuffer buffer = ChannelBuffers.wrappedBuffer(frame, data);
162 Channels.write(ctx, e.getFuture(), buffer, e.getRemoteAddress());
163 }
164 return;
165 }
166
167 if (msg instanceof SpdySynReplyFrame) {
168
169 synchronized (headerBlockCompressor) {
170 SpdySynReplyFrame spdySynReplyFrame = (SpdySynReplyFrame) msg;
171 ChannelBuffer data = compressHeaderBlock(
172 encodeHeaderBlock(version, spdySynReplyFrame));
173 byte flags = spdySynReplyFrame.isLast() ? SPDY_FLAG_FIN : 0;
174 int headerBlockLength = data.readableBytes();
175 int length;
176 if (version < 3) {
177 length = headerBlockLength == 0 ? 8 : 6 + headerBlockLength;
178 } else {
179 length = 4 + headerBlockLength;
180 }
181 ChannelBuffer frame = ChannelBuffers.buffer(
182 ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + 8);
183 frame.writeShort(version | 0x8000);
184 frame.writeShort(SPDY_SYN_REPLY_FRAME);
185 frame.writeByte(flags);
186 frame.writeMedium(length);
187 frame.writeInt(spdySynReplyFrame.getStreamId());
188 if (version < 3) {
189 if (data.readableBytes() == 0) {
190 frame.writeInt(0);
191 } else {
192 frame.writeShort(0);
193 }
194 }
195
196 final ChannelBuffer buffer = ChannelBuffers.wrappedBuffer(frame, data);
197 Channels.write(ctx, e.getFuture(), buffer, e.getRemoteAddress());
198 }
199 return;
200 }
201
202 if (msg instanceof SpdyRstStreamFrame) {
203
204 SpdyRstStreamFrame spdyRstStreamFrame = (SpdyRstStreamFrame) msg;
205 ChannelBuffer frame = ChannelBuffers.buffer(
206 ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + 8);
207 frame.writeShort(version | 0x8000);
208 frame.writeShort(SPDY_RST_STREAM_FRAME);
209 frame.writeInt(8);
210 frame.writeInt(spdyRstStreamFrame.getStreamId());
211 frame.writeInt(spdyRstStreamFrame.getStatus().getCode());
212 Channels.write(ctx, e.getFuture(), frame, e.getRemoteAddress());
213 return;
214 }
215
216 if (msg instanceof SpdySettingsFrame) {
217
218 SpdySettingsFrame spdySettingsFrame = (SpdySettingsFrame) msg;
219 byte flags = spdySettingsFrame.clearPreviouslyPersistedSettings() ?
220 SPDY_SETTINGS_CLEAR : 0;
221 Set<Integer> IDs = spdySettingsFrame.getIds();
222 int numEntries = IDs.size();
223 int length = 4 + numEntries * 8;
224 ChannelBuffer frame = ChannelBuffers.buffer(
225 ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + length);
226 frame.writeShort(version | 0x8000);
227 frame.writeShort(SPDY_SETTINGS_FRAME);
228 frame.writeByte(flags);
229 frame.writeMedium(length);
230 frame.writeInt(numEntries);
231 for (Integer ID: IDs) {
232 int id = ID.intValue();
233 byte ID_flags = 0;
234 if (spdySettingsFrame.isPersistValue(id)) {
235 ID_flags |= SPDY_SETTINGS_PERSIST_VALUE;
236 }
237 if (spdySettingsFrame.isPersisted(id)) {
238 ID_flags |= SPDY_SETTINGS_PERSISTED;
239 }
240 if (version < 3) {
241
242
243
244 frame.writeByte(id & 0xFF);
245 frame.writeByte(id >> 8 & 0xFF);
246 frame.writeByte(id >> 16 & 0xFF);
247 frame.writeByte(ID_flags);
248 } else {
249 frame.writeByte(ID_flags);
250 frame.writeMedium(id);
251 }
252 frame.writeInt(spdySettingsFrame.getValue(id));
253 }
254 Channels.write(ctx, e.getFuture(), frame, e.getRemoteAddress());
255 return;
256 }
257
258 if (msg instanceof SpdyNoOpFrame) {
259
260 ChannelBuffer frame = ChannelBuffers.buffer(
261 ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE);
262 frame.writeShort(version | 0x8000);
263 frame.writeShort(SPDY_NOOP_FRAME);
264 frame.writeInt(0);
265 Channels.write(ctx, e.getFuture(), frame, e.getRemoteAddress());
266 return;
267 }
268
269 if (msg instanceof SpdyPingFrame) {
270
271 SpdyPingFrame spdyPingFrame = (SpdyPingFrame) msg;
272 ChannelBuffer frame = ChannelBuffers.buffer(
273 ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + 4);
274 frame.writeShort(version | 0x8000);
275 frame.writeShort(SPDY_PING_FRAME);
276 frame.writeInt(4);
277 frame.writeInt(spdyPingFrame.getId());
278 Channels.write(ctx, e.getFuture(), frame, e.getRemoteAddress());
279 return;
280 }
281
282 if (msg instanceof SpdyGoAwayFrame) {
283
284 SpdyGoAwayFrame spdyGoAwayFrame = (SpdyGoAwayFrame) msg;
285 int length = version < 3 ? 4 : 8;
286 ChannelBuffer frame = ChannelBuffers.buffer(
287 ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + length);
288 frame.writeShort(version | 0x8000);
289 frame.writeShort(SPDY_GOAWAY_FRAME);
290 frame.writeInt(length);
291 frame.writeInt(spdyGoAwayFrame.getLastGoodStreamId());
292 if (version >= 3) {
293 frame.writeInt(spdyGoAwayFrame.getStatus().getCode());
294 }
295 Channels.write(ctx, e.getFuture(), frame, e.getRemoteAddress());
296 return;
297 }
298
299 if (msg instanceof SpdyHeadersFrame) {
300
301 synchronized (headerBlockCompressor) {
302 SpdyHeadersFrame spdyHeadersFrame = (SpdyHeadersFrame) msg;
303 ChannelBuffer data = compressHeaderBlock(
304 encodeHeaderBlock(version, spdyHeadersFrame));
305 byte flags = spdyHeadersFrame.isLast() ? SPDY_FLAG_FIN : 0;
306 int headerBlockLength = data.readableBytes();
307 int length;
308 if (version < 3) {
309 length = headerBlockLength == 0 ? 4 : 6 + headerBlockLength;
310 } else {
311 length = 4 + headerBlockLength;
312 }
313 ChannelBuffer frame = ChannelBuffers.buffer(
314 ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + length);
315 frame.writeShort(version | 0x8000);
316 frame.writeShort(SPDY_HEADERS_FRAME);
317 frame.writeByte(flags);
318 frame.writeMedium(length);
319 frame.writeInt(spdyHeadersFrame.getStreamId());
320 if (version < 3 && data.readableBytes() != 0) {
321 frame.writeShort(0);
322 }
323
324 final ChannelBuffer buffer = ChannelBuffers.wrappedBuffer(frame, data);
325 Channels.write(ctx, e.getFuture(), buffer, e.getRemoteAddress());
326 }
327 return;
328 }
329
330 if (msg instanceof SpdyWindowUpdateFrame) {
331 SpdyWindowUpdateFrame spdyWindowUpdateFrame = (SpdyWindowUpdateFrame) msg;
332 ChannelBuffer frame = ChannelBuffers.buffer(
333 ByteOrder.BIG_ENDIAN, SPDY_HEADER_SIZE + 8);
334 frame.writeShort(version | 0x8000);
335 frame.writeShort(SPDY_WINDOW_UPDATE_FRAME);
336 frame.writeInt(8);
337 frame.writeInt(spdyWindowUpdateFrame.getStreamId());
338 frame.writeInt(spdyWindowUpdateFrame.getDeltaWindowSize());
339 Channels.write(ctx, e.getFuture(), frame, e.getRemoteAddress());
340 return;
341 }
342
343
344 ctx.sendDownstream(evt);
345 }
346
347 private static void writeLengthField(int version, ChannelBuffer buffer, int length) {
348 if (version < 3) {
349 buffer.writeShort(length);
350 } else {
351 buffer.writeInt(length);
352 }
353 }
354
355 private static void setLengthField(int version, ChannelBuffer buffer, int writerIndex, int length) {
356 if (version < 3) {
357 buffer.setShort(writerIndex, length);
358 } else {
359 buffer.setInt(writerIndex, length);
360 }
361 }
362
363 private static ChannelBuffer encodeHeaderBlock(int version, SpdyHeaderBlock headerFrame)
364 throws Exception {
365 Set<String> names = headerFrame.getHeaderNames();
366 int numHeaders = names.size();
367 if (numHeaders == 0) {
368 return ChannelBuffers.EMPTY_BUFFER;
369 }
370 if (numHeaders > SPDY_MAX_NV_LENGTH) {
371 throw new IllegalArgumentException(
372 "header block contains too many headers");
373 }
374 ChannelBuffer headerBlock = ChannelBuffers.dynamicBuffer(
375 ByteOrder.BIG_ENDIAN, 256);
376 writeLengthField(version, headerBlock, numHeaders);
377 for (String name: names) {
378 byte[] nameBytes = name.getBytes("UTF-8");
379 writeLengthField(version, headerBlock, nameBytes.length);
380 headerBlock.writeBytes(nameBytes);
381 int savedIndex = headerBlock.writerIndex();
382 int valueLength = 0;
383 writeLengthField(version, headerBlock, valueLength);
384 for (String value: headerFrame.getHeaders(name)) {
385 byte[] valueBytes = value.getBytes("UTF-8");
386 if (valueBytes.length > 0) {
387 headerBlock.writeBytes(valueBytes);
388 headerBlock.writeByte(0);
389 valueLength += valueBytes.length + 1;
390 }
391 }
392 if (valueLength == 0) {
393 if (version < 3) {
394 throw new IllegalArgumentException(
395 "header value cannot be empty: " + name);
396 }
397 } else {
398 valueLength --;
399 }
400 if (valueLength > SPDY_MAX_NV_LENGTH) {
401 throw new IllegalArgumentException(
402 "header exceeds allowable length: " + name);
403 }
404 if (valueLength > 0) {
405 setLengthField(version, headerBlock, savedIndex, valueLength);
406 headerBlock.writerIndex(headerBlock.writerIndex() - 1);
407 }
408 }
409 return headerBlock;
410 }
411
412
413 private ChannelBuffer compressHeaderBlock(ChannelBuffer uncompressed)
414 throws Exception {
415 if (uncompressed.readableBytes() == 0) {
416 return ChannelBuffers.EMPTY_BUFFER;
417 }
418 ChannelBuffer compressed = ChannelBuffers.dynamicBuffer();
419 if (!finished) {
420 headerBlockCompressor.setInput(uncompressed);
421 headerBlockCompressor.encode(compressed);
422 }
423 return compressed;
424 }
425 }