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