|
1 // The framer consists of two [Transform Stream][1] subclasses that operate in [object mode][2]: |
|
2 // the Serializer and the Deserializer |
|
3 // [1]: http://nodejs.org/api/stream.html#stream_class_stream_transform |
|
4 // [2]: http://nodejs.org/api/stream.html#stream_new_stream_readable_options |
|
5 var assert = require('assert'); |
|
6 |
|
7 var Transform = require('stream').Transform; |
|
8 |
|
9 exports.Serializer = Serializer; |
|
10 exports.Deserializer = Deserializer; |
|
11 |
|
12 var logData = Boolean(process.env.HTTP2_LOG_DATA); |
|
13 |
|
14 var MAX_PAYLOAD_SIZE = 16383; |
|
15 |
|
16 // Serializer |
|
17 // ---------- |
|
18 // |
|
19 // Frame Objects |
|
20 // * * * * * * * --+--------------------------- |
|
21 // | | |
|
22 // v v Buffers |
|
23 // [] -----> Payload Ser. --[buffers]--> Header Ser. --> * * * * |
|
24 // empty adds payload adds header |
|
25 // array buffers buffer |
|
26 |
|
27 function Serializer(log, sizeLimit) { |
|
28 this._log = log.child({ component: 'serializer' }); |
|
29 this._sizeLimit = sizeLimit || MAX_PAYLOAD_SIZE; |
|
30 Transform.call(this, { objectMode: true }); |
|
31 } |
|
32 Serializer.prototype = Object.create(Transform.prototype, { constructor: { value: Serializer } }); |
|
33 |
|
34 // When there's an incoming frame object, it first generates the frame type specific part of the |
|
35 // frame (payload), and then then adds the header part which holds fields that are common to all |
|
36 // frame types (like the length of the payload). |
|
37 Serializer.prototype._transform = function _transform(frame, encoding, done) { |
|
38 this._log.trace({ frame: frame }, 'Outgoing frame'); |
|
39 |
|
40 assert(frame.type in Serializer, 'Unknown frame type: ' + frame.type); |
|
41 |
|
42 var buffers = []; |
|
43 Serializer[frame.type](frame, buffers); |
|
44 Serializer.commonHeader(frame, buffers); |
|
45 |
|
46 assert(buffers[0].readUInt16BE(0) <= this._sizeLimit, 'Frame too large!'); |
|
47 |
|
48 for (var i = 0; i < buffers.length; i++) { |
|
49 if (logData) { |
|
50 this._log.trace({ data: buffers[i] }, 'Outgoing data'); |
|
51 } |
|
52 this.push(buffers[i]); |
|
53 } |
|
54 |
|
55 done(); |
|
56 }; |
|
57 |
|
58 // Deserializer |
|
59 // ------------ |
|
60 // |
|
61 // Buffers |
|
62 // * * * * --------+------------------------- |
|
63 // | | |
|
64 // v v Frame Objects |
|
65 // {} -----> Header Des. --{frame}--> Payload Des. --> * * * * * * * |
|
66 // empty adds parsed adds parsed |
|
67 // object header properties payload properties |
|
68 |
|
69 function Deserializer(log, sizeLimit, role) { |
|
70 this._role = role; |
|
71 this._log = log.child({ component: 'deserializer' }); |
|
72 this._sizeLimit = sizeLimit || MAX_PAYLOAD_SIZE; |
|
73 Transform.call(this, { objectMode: true }); |
|
74 this._next(COMMON_HEADER_SIZE); |
|
75 } |
|
76 Deserializer.prototype = Object.create(Transform.prototype, { constructor: { value: Deserializer } }); |
|
77 |
|
78 // The Deserializer is stateful, and it's two main alternating states are: *waiting for header* and |
|
79 // *waiting for payload*. The state is stored in the boolean property `_waitingForHeader`. |
|
80 // |
|
81 // When entering a new state, a `_buffer` is created that will hold the accumulated data (header or |
|
82 // payload). The `_cursor` is used to track the progress. |
|
83 Deserializer.prototype._next = function(size) { |
|
84 this._cursor = 0; |
|
85 this._buffer = new Buffer(size); |
|
86 this._waitingForHeader = !this._waitingForHeader; |
|
87 if (this._waitingForHeader) { |
|
88 this._frame = {}; |
|
89 } |
|
90 }; |
|
91 |
|
92 // Parsing an incoming buffer is an iterative process because it can hold multiple frames if it's |
|
93 // large enough. A `cursor` is used to track the progress in parsing the incoming `chunk`. |
|
94 Deserializer.prototype._transform = function _transform(chunk, encoding, done) { |
|
95 var cursor = 0; |
|
96 |
|
97 if (logData) { |
|
98 this._log.trace({ data: chunk }, 'Incoming data'); |
|
99 } |
|
100 |
|
101 while(cursor < chunk.length) { |
|
102 // The content of an incoming buffer is first copied to `_buffer`. If it can't hold the full |
|
103 // chunk, then only a part of it is copied. |
|
104 var toCopy = Math.min(chunk.length - cursor, this._buffer.length - this._cursor); |
|
105 chunk.copy(this._buffer, this._cursor, cursor, cursor + toCopy); |
|
106 this._cursor += toCopy; |
|
107 cursor += toCopy; |
|
108 |
|
109 // When `_buffer` is full, it's content gets parsed either as header or payload depending on |
|
110 // the actual state. |
|
111 |
|
112 // If it's header then the parsed data is stored in a temporary variable and then the |
|
113 // deserializer waits for the specified length payload. |
|
114 if ((this._cursor === this._buffer.length) && this._waitingForHeader) { |
|
115 var payloadSize = Deserializer.commonHeader(this._buffer, this._frame); |
|
116 if (payloadSize <= this._sizeLimit) { |
|
117 this._next(payloadSize); |
|
118 } else { |
|
119 this.emit('error', 'FRAME_SIZE_ERROR'); |
|
120 return; |
|
121 } |
|
122 } |
|
123 |
|
124 // If it's payload then the the frame object is finalized and then gets pushed out. |
|
125 // Unknown frame types are ignored. |
|
126 // |
|
127 // Note: If we just finished the parsing of a header and the payload length is 0, this branch |
|
128 // will also run. |
|
129 if ((this._cursor === this._buffer.length) && !this._waitingForHeader) { |
|
130 if (this._frame.type) { |
|
131 var error = Deserializer[this._frame.type](this._buffer, this._frame, this._role); |
|
132 if (error) { |
|
133 this._log.error('Incoming frame parsing error: ' + error); |
|
134 this.emit('error', 'PROTOCOL_ERROR'); |
|
135 } else { |
|
136 this._log.trace({ frame: this._frame }, 'Incoming frame'); |
|
137 this.push(this._frame); |
|
138 } |
|
139 } else { |
|
140 this._log.error('Unknown type incoming frame'); |
|
141 this.emit('error', 'PROTOCOL_ERROR'); |
|
142 } |
|
143 this._next(COMMON_HEADER_SIZE); |
|
144 } |
|
145 } |
|
146 |
|
147 done(); |
|
148 }; |
|
149 |
|
150 // [Frame Header](http://tools.ietf.org/html/draft-ietf-httpbis-http2-10#section-4.1) |
|
151 // -------------------------------------------------------------- |
|
152 // |
|
153 // HTTP/2.0 frames share a common base format consisting of an 8-byte header followed by 0 to 65535 |
|
154 // bytes of data. |
|
155 // |
|
156 // Additional size limits can be set by specific application uses. HTTP limits the frame size to |
|
157 // 16,383 octets. |
|
158 // |
|
159 // 0 1 2 3 |
|
160 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 |
|
161 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
|
162 // | R | Length (14) | Type (8) | Flags (8) | |
|
163 // +-+-+---------------------------+---------------+---------------+ |
|
164 // |R| Stream Identifier (31) | |
|
165 // +-+-------------------------------------------------------------+ |
|
166 // | Frame Data (0...) ... |
|
167 // +---------------------------------------------------------------+ |
|
168 // |
|
169 // The fields of the frame header are defined as: |
|
170 // |
|
171 // * R: |
|
172 // A reserved 2-bit field. The semantics of these bits are undefined and the bits MUST remain |
|
173 // unset (0) when sending and MUST be ignored when receiving. |
|
174 // |
|
175 // * Length: |
|
176 // The length of the frame data expressed as an unsigned 14-bit integer. The 8 bytes of the frame |
|
177 // header are not included in this value. |
|
178 // |
|
179 // * Type: |
|
180 // The 8-bit type of the frame. The frame type determines how the remainder of the frame header |
|
181 // and data are interpreted. Implementations MUST ignore unsupported and unrecognized frame types. |
|
182 // |
|
183 // * Flags: |
|
184 // An 8-bit field reserved for frame-type specific boolean flags. |
|
185 // |
|
186 // Flags are assigned semantics specific to the indicated frame type. Flags that have no defined |
|
187 // semantics for a particular frame type MUST be ignored, and MUST be left unset (0) when sending. |
|
188 // |
|
189 // * R: |
|
190 // A reserved 1-bit field. The semantics of this bit are undefined and the bit MUST remain unset |
|
191 // (0) when sending and MUST be ignored when receiving. |
|
192 // |
|
193 // * Stream Identifier: |
|
194 // A 31-bit stream identifier. The value 0 is reserved for frames that are associated with the |
|
195 // connection as a whole as opposed to an individual stream. |
|
196 // |
|
197 // The structure and content of the remaining frame data is dependent entirely on the frame type. |
|
198 |
|
199 var COMMON_HEADER_SIZE = 8; |
|
200 |
|
201 var frameTypes = []; |
|
202 |
|
203 var frameFlags = {}; |
|
204 |
|
205 var genericAttributes = ['type', 'flags', 'stream']; |
|
206 |
|
207 var typeSpecificAttributes = {}; |
|
208 |
|
209 Serializer.commonHeader = function writeCommonHeader(frame, buffers) { |
|
210 var headerBuffer = new Buffer(COMMON_HEADER_SIZE); |
|
211 |
|
212 var size = 0; |
|
213 for (var i = 0; i < buffers.length; i++) { |
|
214 size += buffers[i].length; |
|
215 } |
|
216 headerBuffer.writeUInt16BE(size, 0); |
|
217 |
|
218 var typeId = frameTypes.indexOf(frame.type); // If we are here then the type is valid for sure |
|
219 headerBuffer.writeUInt8(typeId, 2); |
|
220 |
|
221 var flagByte = 0; |
|
222 for (var flag in frame.flags) { |
|
223 var position = frameFlags[frame.type].indexOf(flag); |
|
224 assert(position !== -1, 'Unknown flag for frame type ' + frame.type + ': ' + flag); |
|
225 if (frame.flags[flag]) { |
|
226 flagByte |= (1 << position); |
|
227 } |
|
228 } |
|
229 headerBuffer.writeUInt8(flagByte, 3); |
|
230 |
|
231 assert((0 <= frame.stream) && (frame.stream < 0x7fffffff), frame.stream); |
|
232 headerBuffer.writeUInt32BE(frame.stream || 0, 4); |
|
233 |
|
234 buffers.unshift(headerBuffer); |
|
235 }; |
|
236 |
|
237 Deserializer.commonHeader = function readCommonHeader(buffer, frame) { |
|
238 var length = buffer.readUInt16BE(0); |
|
239 |
|
240 frame.type = frameTypes[buffer.readUInt8(2)]; |
|
241 |
|
242 frame.flags = {}; |
|
243 var flagByte = buffer.readUInt8(3); |
|
244 var definedFlags = frameFlags[frame.type]; |
|
245 for (var i = 0; i < definedFlags.length; i++) { |
|
246 frame.flags[definedFlags[i]] = Boolean(flagByte & (1 << i)); |
|
247 } |
|
248 |
|
249 frame.stream = buffer.readUInt32BE(4) & 0x7fffffff; |
|
250 |
|
251 return length; |
|
252 }; |
|
253 |
|
254 // Frame types |
|
255 // =========== |
|
256 |
|
257 // Every frame type is registered in the following places: |
|
258 // |
|
259 // * `frameTypes`: a register of frame type codes (used by `commonHeader()`) |
|
260 // * `frameFlags`: a register of valid flags for frame types (used by `commonHeader()`) |
|
261 // * `typeSpecificAttributes`: a register of frame specific frame object attributes (used by |
|
262 // logging code and also serves as documentation for frame objects) |
|
263 |
|
264 // [DATA Frames](http://tools.ietf.org/html/draft-ietf-httpbis-http2-10#section-6.1) |
|
265 // ------------------------------------------------------------ |
|
266 // |
|
267 // DATA frames (type=0x0) convey arbitrary, variable-length sequences of octets associated with a |
|
268 // stream. |
|
269 // |
|
270 // The DATA frame defines the following flags: |
|
271 // |
|
272 // * END_STREAM (0x1): |
|
273 // Bit 1 being set indicates that this frame is the last that the endpoint will send for the |
|
274 // identified stream. |
|
275 // * END_SEGMENT (0x2): |
|
276 // Bit 2 being set indicates that this frame is the last for the current segment. Intermediaries |
|
277 // MUST NOT coalesce frames across a segment boundary and MUST preserve segment boundaries when |
|
278 // forwarding frames. |
|
279 // * PAD_LOW (0x10): |
|
280 // Bit 5 being set indicates that the Pad Low field is present. |
|
281 // * PAD_HIGH (0x20): |
|
282 // Bit 6 being set indicates that the Pad High field is present. This bit MUST NOT be set unless |
|
283 // the PAD_LOW flag is also set. Endpoints that receive a frame with PAD_HIGH set and PAD_LOW |
|
284 // cleared MUST treat this as a connection error of type PROTOCOL_ERROR. |
|
285 |
|
286 frameTypes[0x0] = 'DATA'; |
|
287 |
|
288 frameFlags.DATA = ['END_STREAM', 'END_SEGMENT', 'RESERVED4', 'RESERVED8', 'PAD_LOW', 'PAD_HIGH']; |
|
289 |
|
290 typeSpecificAttributes.DATA = ['data']; |
|
291 |
|
292 Serializer.DATA = function writeData(frame, buffers) { |
|
293 buffers.push(frame.data); |
|
294 }; |
|
295 |
|
296 Deserializer.DATA = function readData(buffer, frame) { |
|
297 var dataOffset = 0; |
|
298 var paddingLength = 0; |
|
299 if (frame.flags.PAD_LOW) { |
|
300 if (frame.flags.PAD_HIGH) { |
|
301 paddingLength = (buffer.readUInt8(dataOffset) & 0xff) * 256; |
|
302 dataOffset += 1; |
|
303 } |
|
304 paddingLength += (buffer.readUInt8(dataOffset) & 0xff); |
|
305 dataOffset += 1; |
|
306 } else if (frame.flags.PAD_HIGH) { |
|
307 return 'DATA frame got PAD_HIGH without PAD_LOW'; |
|
308 } |
|
309 if (paddingLength) { |
|
310 frame.data = buffer.slice(dataOffset, -1 * paddingLength); |
|
311 } else { |
|
312 frame.data = buffer.slice(dataOffset); |
|
313 } |
|
314 }; |
|
315 |
|
316 // [HEADERS](http://tools.ietf.org/html/draft-ietf-httpbis-http2-10#section-6.2) |
|
317 // -------------------------------------------------------------- |
|
318 // |
|
319 // The HEADERS frame (type=0x1) allows the sender to create a stream. |
|
320 // |
|
321 // The HEADERS frame defines the following flags: |
|
322 // |
|
323 // * END_STREAM (0x1): |
|
324 // Bit 1 being set indicates that this frame is the last that the endpoint will send for the |
|
325 // identified stream. |
|
326 // * END_SEGMENT (0x2): |
|
327 // Bit 2 being set indicates that this frame is the last for the current segment. Intermediaries |
|
328 // MUST NOT coalesce frames across a segment boundary and MUST preserve segment boundaries when |
|
329 // forwarding frames. |
|
330 // * END_HEADERS (0x4): |
|
331 // The END_HEADERS bit indicates that this frame contains the entire payload necessary to provide |
|
332 // a complete set of headers. |
|
333 // * PRIORITY (0x8): |
|
334 // Bit 4 being set indicates that the first four octets of this frame contain a single reserved |
|
335 // bit and a 31-bit priority. |
|
336 // * PAD_LOW (0x10): |
|
337 // Bit 5 being set indicates that the Pad Low field is present. |
|
338 // * PAD_HIGH (0x20): |
|
339 // Bit 6 being set indicates that the Pad High field is present. This bit MUST NOT be set unless |
|
340 // the PAD_LOW flag is also set. Endpoints that receive a frame with PAD_HIGH set and PAD_LOW |
|
341 // cleared MUST treat this as a connection error of type PROTOCOL_ERROR. |
|
342 |
|
343 frameTypes[0x1] = 'HEADERS'; |
|
344 |
|
345 frameFlags.HEADERS = ['END_STREAM', 'END_SEGMENT', 'END_HEADERS', 'PRIORITY', 'PAD_LOW', 'PAD_HIGH']; |
|
346 |
|
347 typeSpecificAttributes.HEADERS = ['priority', 'headers', 'data']; |
|
348 |
|
349 // 0 1 2 3 |
|
350 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 |
|
351 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
|
352 // |X| (Optional) Priority (31) | |
|
353 // +-+-------------------------------------------------------------+ |
|
354 // | Header Block (*) ... |
|
355 // +---------------------------------------------------------------+ |
|
356 // |
|
357 // The payload of a HEADERS frame contains a Headers Block |
|
358 |
|
359 Serializer.HEADERS = function writeHeadersPriority(frame, buffers) { |
|
360 if (frame.flags.PRIORITY) { |
|
361 var buffer = new Buffer(4); |
|
362 assert((0 <= frame.priority) && (frame.priority <= 0xffffffff), frame.priority); |
|
363 buffer.writeUInt32BE(frame.priority, 0); |
|
364 buffers.push(buffer); |
|
365 } |
|
366 buffers.push(frame.data); |
|
367 }; |
|
368 |
|
369 Deserializer.HEADERS = function readHeadersPriority(buffer, frame) { |
|
370 var dataOffset = 0; |
|
371 var paddingLength = 0; |
|
372 if (frame.flags.PAD_LOW) { |
|
373 if (frame.flags.PAD_HIGH) { |
|
374 paddingLength = (buffer.readUInt8(dataOffset) & 0xff) * 256; |
|
375 dataOffset += 1; |
|
376 } |
|
377 paddingLength += (buffer.readUInt8(dataOffset) & 0xff); |
|
378 dataOffset += 1; |
|
379 } else if (frame.flags.PAD_HIGH) { |
|
380 return 'HEADERS frame got PAD_HIGH without PAD_LOW'; |
|
381 } |
|
382 if (frame.flags.PRIORITY) { |
|
383 frame.priority = buffer.readUInt32BE(dataOffset) & 0x7fffffff; |
|
384 dataOffset += 4; |
|
385 } |
|
386 if (paddingLength) { |
|
387 frame.data = buffer.slice(dataOffset, -1 * paddingLength); |
|
388 } else { |
|
389 frame.data = buffer.slice(dataOffset); |
|
390 } |
|
391 }; |
|
392 |
|
393 // [PRIORITY](http://tools.ietf.org/html/draft-ietf-httpbis-http2-10#section-6.3) |
|
394 // ------------------------------------------------------- |
|
395 // |
|
396 // The PRIORITY frame (type=0x2) specifies the sender-advised priority of a stream. |
|
397 // |
|
398 // The PRIORITY frame does not define any flags. |
|
399 |
|
400 frameTypes[0x2] = 'PRIORITY'; |
|
401 |
|
402 frameFlags.PRIORITY = []; |
|
403 |
|
404 typeSpecificAttributes.PRIORITY = ['priority']; |
|
405 |
|
406 // 0 1 2 3 |
|
407 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 |
|
408 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
|
409 // |X| Priority (31) | |
|
410 // +-+-------------------------------------------------------------+ |
|
411 // |
|
412 // The payload of a PRIORITY frame contains a single reserved bit and a 31-bit priority. |
|
413 |
|
414 Serializer.PRIORITY = function writePriority(frame, buffers) { |
|
415 var buffer = new Buffer(4); |
|
416 buffer.writeUInt32BE(frame.priority, 0); |
|
417 buffers.push(buffer); |
|
418 }; |
|
419 |
|
420 Deserializer.PRIORITY = function readPriority(buffer, frame) { |
|
421 frame.priority = buffer.readUInt32BE(0); |
|
422 }; |
|
423 |
|
424 // [RST_STREAM](http://tools.ietf.org/html/draft-ietf-httpbis-http2-10#section-6.4) |
|
425 // ----------------------------------------------------------- |
|
426 // |
|
427 // The RST_STREAM frame (type=0x3) allows for abnormal termination of a stream. |
|
428 // |
|
429 // No type-flags are defined. |
|
430 |
|
431 frameTypes[0x3] = 'RST_STREAM'; |
|
432 |
|
433 frameFlags.RST_STREAM = []; |
|
434 |
|
435 typeSpecificAttributes.RST_STREAM = ['error']; |
|
436 |
|
437 // 0 1 2 3 |
|
438 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 |
|
439 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
|
440 // | Error Code (32) | |
|
441 // +---------------------------------------------------------------+ |
|
442 // |
|
443 // The RST_STREAM frame contains a single unsigned, 32-bit integer identifying the error |
|
444 // code (see Error Codes). The error code indicates why the stream is being terminated. |
|
445 |
|
446 Serializer.RST_STREAM = function writeRstStream(frame, buffers) { |
|
447 var buffer = new Buffer(4); |
|
448 var code = errorCodes.indexOf(frame.error); |
|
449 assert((0 <= code) && (code <= 0xffffffff), code); |
|
450 buffer.writeUInt32BE(code, 0); |
|
451 buffers.push(buffer); |
|
452 }; |
|
453 |
|
454 Deserializer.RST_STREAM = function readRstStream(buffer, frame) { |
|
455 frame.error = errorCodes[buffer.readUInt32BE(0)]; |
|
456 }; |
|
457 |
|
458 // [SETTINGS](http://tools.ietf.org/html/draft-ietf-httpbis-http2-10#section-6.5) |
|
459 // ------------------------------------------------------- |
|
460 // |
|
461 // The SETTINGS frame (type=0x4) conveys configuration parameters that affect how endpoints |
|
462 // communicate. |
|
463 // |
|
464 // The SETTINGS frame defines the following flag: |
|
465 |
|
466 // * ACK (0x1): |
|
467 // Bit 1 being set indicates that this frame acknowledges receipt and application of the peer's |
|
468 // SETTINGS frame. |
|
469 frameTypes[0x4] = 'SETTINGS'; |
|
470 |
|
471 frameFlags.SETTINGS = ['ACK']; |
|
472 |
|
473 typeSpecificAttributes.SETTINGS = ['settings']; |
|
474 |
|
475 // The payload of a SETTINGS frame consists of zero or more settings. Each setting consists of an |
|
476 // 8-bit identifier, and an unsigned 32-bit value. |
|
477 // |
|
478 // 0 1 2 3 |
|
479 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 |
|
480 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
|
481 // | Identifier(8) | Value (32) | |
|
482 // +-----------------+---------------------------------------------+ |
|
483 // ...Value | |
|
484 // +-----------------+ |
|
485 // |
|
486 // Each setting in a SETTINGS frame replaces the existing value for that setting. Settings are |
|
487 // processed in the order in which they appear, and a receiver of a SETTINGS frame does not need to |
|
488 // maintain any state other than the current value of settings. Therefore, the value of a setting |
|
489 // is the last value that is seen by a receiver. This permits the inclusion of the same settings |
|
490 // multiple times in the same SETTINGS frame, though doing so does nothing other than waste |
|
491 // connection capacity. |
|
492 |
|
493 Serializer.SETTINGS = function writeSettings(frame, buffers) { |
|
494 var settings = [], settingsLeft = Object.keys(frame.settings); |
|
495 definedSettings.forEach(function(setting, id) { |
|
496 if (setting.name in frame.settings) { |
|
497 settingsLeft.splice(settingsLeft.indexOf(setting.name), 1); |
|
498 var value = frame.settings[setting.name]; |
|
499 settings.push({ id: id, value: setting.flag ? Boolean(value) : value }); |
|
500 } |
|
501 }); |
|
502 assert(settingsLeft.length === 0, 'Unknown settings: ' + settingsLeft.join(', ')); |
|
503 |
|
504 var buffer = new Buffer(settings.length * 5); |
|
505 for (var i = 0; i < settings.length; i++) { |
|
506 buffer.writeUInt8(settings[i].id & 0xff, i*5); |
|
507 buffer.writeUInt32BE(settings[i].value, i*5 + 1); |
|
508 } |
|
509 |
|
510 buffers.push(buffer); |
|
511 }; |
|
512 |
|
513 Deserializer.SETTINGS = function readSettings(buffer, frame, role) { |
|
514 frame.settings = {}; |
|
515 |
|
516 if (buffer.length % 5 !== 0) { |
|
517 return 'Invalid SETTINGS frame'; |
|
518 } |
|
519 for (var i = 0; i < buffer.length / 5; i++) { |
|
520 var id = buffer.readUInt8(i*5) & 0xff; |
|
521 var setting = definedSettings[id]; |
|
522 if (setting) { |
|
523 if (role == 'CLIENT' && setting.name == 'SETTINGS_ENABLE_PUSH') { |
|
524 return 'SETTINGS frame on client got SETTINGS_ENABLE_PUSH'; |
|
525 } |
|
526 var value = buffer.readUInt32BE(i*5 + 1); |
|
527 frame.settings[setting.name] = setting.flag ? Boolean(value & 0x1) : value; |
|
528 } else { |
|
529 /* Unknown setting, protocol error */ |
|
530 return 'SETTINGS frame got unknown setting type'; |
|
531 } |
|
532 } |
|
533 }; |
|
534 |
|
535 // The following settings are defined: |
|
536 var definedSettings = []; |
|
537 |
|
538 // * SETTINGS_HEADER_TABLE_SIZE (1): |
|
539 // Allows the sender to inform the remote endpoint of the size of the header compression table |
|
540 // used to decode header blocks. |
|
541 definedSettings[1] = { name: 'SETTINGS_HEADER_TABLE_SIZE', flag: false }; |
|
542 |
|
543 // * SETTINGS_ENABLE_PUSH (2): |
|
544 // This setting can be use to disable server push. An endpoint MUST NOT send a PUSH_PROMISE frame |
|
545 // if it receives this setting set to a value of 0. The default value is 1, which indicates that |
|
546 // push is permitted. |
|
547 definedSettings[2] = { name: 'SETTINGS_ENABLE_PUSH', flag: true }; |
|
548 |
|
549 // * SETTINGS_MAX_CONCURRENT_STREAMS (4): |
|
550 // indicates the maximum number of concurrent streams that the sender will allow. |
|
551 definedSettings[3] = { name: 'SETTINGS_MAX_CONCURRENT_STREAMS', flag: false }; |
|
552 |
|
553 // * SETTINGS_INITIAL_WINDOW_SIZE (7): |
|
554 // indicates the sender's initial stream window size (in bytes) for new streams. |
|
555 definedSettings[4] = { name: 'SETTINGS_INITIAL_WINDOW_SIZE', flag: false }; |
|
556 |
|
557 // [PUSH_PROMISE](http://tools.ietf.org/html/draft-ietf-httpbis-http2-10#section-6.6) |
|
558 // --------------------------------------------------------------- |
|
559 // |
|
560 // The PUSH_PROMISE frame (type=0x5) is used to notify the peer endpoint in advance of streams the |
|
561 // sender intends to initiate. |
|
562 // |
|
563 // The PUSH_PROMISE frame defines the following flags: |
|
564 // |
|
565 // * END_PUSH_PROMISE (0x4): |
|
566 // The END_PUSH_PROMISE bit indicates that this frame contains the entire payload necessary to |
|
567 // provide a complete set of headers. |
|
568 |
|
569 frameTypes[0x5] = 'PUSH_PROMISE'; |
|
570 |
|
571 frameFlags.PUSH_PROMISE = ['RESERVED1', 'RESERVED2', 'END_PUSH_PROMISE']; |
|
572 |
|
573 typeSpecificAttributes.PUSH_PROMISE = ['promised_stream', 'headers', 'data']; |
|
574 |
|
575 // 0 1 2 3 |
|
576 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 |
|
577 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
|
578 // |X| Promised-Stream-ID (31) | |
|
579 // +-+-------------------------------------------------------------+ |
|
580 // | Header Block (*) ... |
|
581 // +---------------------------------------------------------------+ |
|
582 // |
|
583 // The PUSH_PROMISE frame includes the unsigned 31-bit identifier of |
|
584 // the stream the endpoint plans to create along with a minimal set of headers that provide |
|
585 // additional context for the stream. |
|
586 |
|
587 Serializer.PUSH_PROMISE = function writePushPromise(frame, buffers) { |
|
588 var buffer = new Buffer(4); |
|
589 |
|
590 var promised_stream = frame.promised_stream; |
|
591 assert((0 <= promised_stream) && (promised_stream <= 0x7fffffff), promised_stream); |
|
592 buffer.writeUInt32BE(promised_stream, 0); |
|
593 |
|
594 buffers.push(buffer); |
|
595 buffers.push(frame.data); |
|
596 }; |
|
597 |
|
598 Deserializer.PUSH_PROMISE = function readPushPromise(buffer, frame) { |
|
599 frame.promised_stream = buffer.readUInt32BE(0) & 0x7fffffff; |
|
600 frame.data = buffer.slice(4); |
|
601 }; |
|
602 |
|
603 // [PING](http://tools.ietf.org/html/draft-ietf-httpbis-http2-10#section-6.7) |
|
604 // ----------------------------------------------- |
|
605 // |
|
606 // The PING frame (type=0x6) is a mechanism for measuring a minimal round-trip time from the |
|
607 // sender, as well as determining whether an idle connection is still functional. |
|
608 // |
|
609 // The PING frame defines one type-specific flag: |
|
610 // |
|
611 // * ACK (0x1): |
|
612 // Bit 1 being set indicates that this PING frame is a PING response. |
|
613 |
|
614 frameTypes[0x6] = 'PING'; |
|
615 |
|
616 frameFlags.PING = ['ACK']; |
|
617 |
|
618 typeSpecificAttributes.PING = ['data']; |
|
619 |
|
620 // In addition to the frame header, PING frames MUST contain 8 additional octets of opaque data. |
|
621 |
|
622 Serializer.PING = function writePing(frame, buffers) { |
|
623 buffers.push(frame.data); |
|
624 }; |
|
625 |
|
626 Deserializer.PING = function readPing(buffer, frame) { |
|
627 if (buffer.length !== 8) { |
|
628 return 'Invalid size PING frame'; |
|
629 } |
|
630 frame.data = buffer; |
|
631 }; |
|
632 |
|
633 // [GOAWAY](http://tools.ietf.org/html/draft-ietf-httpbis-http2-10#section-6.8) |
|
634 // --------------------------------------------------- |
|
635 // |
|
636 // The GOAWAY frame (type=0x7) informs the remote peer to stop creating streams on this connection. |
|
637 // |
|
638 // The GOAWAY frame does not define any flags. |
|
639 |
|
640 frameTypes[0x7] = 'GOAWAY'; |
|
641 |
|
642 frameFlags.GOAWAY = []; |
|
643 |
|
644 typeSpecificAttributes.GOAWAY = ['last_stream', 'error']; |
|
645 |
|
646 // 0 1 2 3 |
|
647 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 |
|
648 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |
|
649 // |X| Last-Stream-ID (31) | |
|
650 // +-+-------------------------------------------------------------+ |
|
651 // | Error Code (32) | |
|
652 // +---------------------------------------------------------------+ |
|
653 // |
|
654 // The last stream identifier in the GOAWAY frame contains the highest numbered stream identifier |
|
655 // for which the sender of the GOAWAY frame has received frames on and might have taken some action |
|
656 // on. |
|
657 // |
|
658 // The GOAWAY frame also contains a 32-bit error code (see Error Codes) that contains the reason for |
|
659 // closing the connection. |
|
660 |
|
661 Serializer.GOAWAY = function writeGoaway(frame, buffers) { |
|
662 var buffer = new Buffer(8); |
|
663 |
|
664 var last_stream = frame.last_stream; |
|
665 assert((0 <= last_stream) && (last_stream <= 0x7fffffff), last_stream); |
|
666 buffer.writeUInt32BE(last_stream, 0); |
|
667 |
|
668 var code = errorCodes.indexOf(frame.error); |
|
669 assert((0 <= code) && (code <= 0xffffffff), code); |
|
670 buffer.writeUInt32BE(code, 4); |
|
671 |
|
672 buffers.push(buffer); |
|
673 }; |
|
674 |
|
675 Deserializer.GOAWAY = function readGoaway(buffer, frame) { |
|
676 frame.last_stream = buffer.readUInt32BE(0) & 0x7fffffff; |
|
677 frame.error = errorCodes[buffer.readUInt32BE(4)]; |
|
678 }; |
|
679 |
|
680 // [WINDOW_UPDATE](http://tools.ietf.org/html/draft-ietf-httpbis-http2-10#section-6.9) |
|
681 // ----------------------------------------------------------------- |
|
682 // |
|
683 // The WINDOW_UPDATE frame (type=0x8) is used to implement flow control. |
|
684 // |
|
685 // The WINDOW_UPDATE frame does not define any flags. |
|
686 |
|
687 frameTypes[0x8] = 'WINDOW_UPDATE'; |
|
688 |
|
689 frameFlags.WINDOW_UPDATE = []; |
|
690 |
|
691 typeSpecificAttributes.WINDOW_UPDATE = ['window_size']; |
|
692 |
|
693 // The payload of a WINDOW_UPDATE frame is a 32-bit value indicating the additional number of bytes |
|
694 // that the sender can transmit in addition to the existing flow control window. The legal range |
|
695 // for this field is 1 to 2^31 - 1 (0x7fffffff) bytes; the most significant bit of this value is |
|
696 // reserved. |
|
697 |
|
698 Serializer.WINDOW_UPDATE = function writeWindowUpdate(frame, buffers) { |
|
699 var buffer = new Buffer(4); |
|
700 |
|
701 var window_size = frame.window_size; |
|
702 assert((0 <= window_size) && (window_size <= 0x7fffffff), window_size); |
|
703 buffer.writeUInt32BE(window_size, 0); |
|
704 |
|
705 buffers.push(buffer); |
|
706 }; |
|
707 |
|
708 Deserializer.WINDOW_UPDATE = function readWindowUpdate(buffer, frame) { |
|
709 frame.window_size = buffer.readUInt32BE(0) & 0x7fffffff; |
|
710 }; |
|
711 |
|
712 // [CONTINUATION](http://tools.ietf.org/html/draft-ietf-httpbis-http2-10#section-6.10) |
|
713 // ------------------------------------------------------------ |
|
714 // |
|
715 // The CONTINUATION frame (type=0xA) is used to continue a sequence of header block fragments. |
|
716 // |
|
717 // The CONTINUATION frame defines the following flag: |
|
718 // |
|
719 // * END_HEADERS (0x4): |
|
720 // The END_HEADERS bit indicates that this frame ends the sequence of header block fragments |
|
721 // necessary to provide a complete set of headers. |
|
722 // * PAD_LOW (0x10): |
|
723 // Bit 5 being set indicates that the Pad Low field is present. |
|
724 // * PAD_HIGH (0x20): |
|
725 // Bit 6 being set indicates that the Pad High field is present. This bit MUST NOT be set unless |
|
726 // the PAD_LOW flag is also set. Endpoints that receive a frame with PAD_HIGH set and PAD_LOW |
|
727 // cleared MUST treat this as a connection error of type PROTOCOL_ERROR. |
|
728 |
|
729 frameTypes[0x9] = 'CONTINUATION'; |
|
730 |
|
731 frameFlags.CONTINUATION = ['RESERVED1', 'RESERVED2', 'END_HEADERS', 'RESERVED8', 'PAD_LOW', 'PAD_HIGH']; |
|
732 |
|
733 typeSpecificAttributes.CONTINUATION = ['headers', 'data']; |
|
734 |
|
735 Serializer.CONTINUATION = function writeContinuation(frame, buffers) { |
|
736 buffers.push(frame.data); |
|
737 }; |
|
738 |
|
739 Deserializer.CONTINUATION = function readContinuation(buffer, frame) { |
|
740 var dataOffset = 0; |
|
741 var paddingLength = 0; |
|
742 if (frame.flags.PAD_LOW) { |
|
743 if (frame.flags.PAD_HIGH) { |
|
744 paddingLength = (buffer.readUInt8(dataOffset) & 0xff) * 256; |
|
745 dataOffset += 1; |
|
746 } |
|
747 paddingLength += (buffer.readUInt8(dataOffset) & 0xff); |
|
748 dataOffset += 1; |
|
749 } else if (frame.flags.PAD_HIGH) { |
|
750 return 'CONTINUATION frame got PAD_HIGH without PAD_LOW'; |
|
751 } |
|
752 if (paddingLength) { |
|
753 frame.data = buffer.slice(dataOffset, -1 * paddingLength); |
|
754 } else { |
|
755 frame.data = buffer.slice(dataOffset); |
|
756 } |
|
757 }; |
|
758 |
|
759 // [Error Codes](http://tools.ietf.org/html/draft-ietf-httpbis-http2-10#section-7) |
|
760 // ------------------------------------------------------------ |
|
761 |
|
762 var errorCodes = [ |
|
763 'NO_ERROR', |
|
764 'PROTOCOL_ERROR', |
|
765 'INTERNAL_ERROR', |
|
766 'FLOW_CONTROL_ERROR', |
|
767 'SETTINGS_TIMEOUT', |
|
768 'STREAM_CLOSED', |
|
769 'FRAME_SIZE_ERROR', |
|
770 'REFUSED_STREAM', |
|
771 'CANCEL', |
|
772 'COMPRESSION_ERROR', |
|
773 'CONNECT_ERROR' |
|
774 ]; |
|
775 errorCodes[420] = 'ENHANCE_YOUR_CALM'; |
|
776 |
|
777 // Logging |
|
778 // ------- |
|
779 |
|
780 // [Bunyan serializers](https://github.com/trentm/node-bunyan#serializers) to improve logging output |
|
781 // for debug messages emitted in this component. |
|
782 exports.serializers = {}; |
|
783 |
|
784 // * `frame` serializer: it transforms data attributes from Buffers to hex strings and filters out |
|
785 // flags that are not present. |
|
786 var frameCounter = 0; |
|
787 exports.serializers.frame = function(frame) { |
|
788 if (!frame) { |
|
789 return null; |
|
790 } |
|
791 |
|
792 if ('id' in frame) { |
|
793 return frame.id; |
|
794 } |
|
795 |
|
796 frame.id = frameCounter; |
|
797 frameCounter += 1; |
|
798 |
|
799 var logEntry = { id: frame.id }; |
|
800 genericAttributes.concat(typeSpecificAttributes[frame.type]).forEach(function(name) { |
|
801 logEntry[name] = frame[name]; |
|
802 }); |
|
803 |
|
804 if (frame.data instanceof Buffer) { |
|
805 if (logEntry.data.length > 50) { |
|
806 logEntry.data = frame.data.slice(0, 47).toString('hex') + '...'; |
|
807 } else { |
|
808 logEntry.data = frame.data.toString('hex'); |
|
809 } |
|
810 |
|
811 if (!('length' in logEntry)) { |
|
812 logEntry.length = frame.data.length; |
|
813 } |
|
814 } |
|
815 |
|
816 if (frame.promised_stream instanceof Object) { |
|
817 logEntry.promised_stream = 'stream-' + frame.promised_stream.id; |
|
818 } |
|
819 |
|
820 logEntry.flags = Object.keys(frame.flags || {}).filter(function(name) { |
|
821 return frame.flags[name] === true; |
|
822 }); |
|
823 |
|
824 return logEntry; |
|
825 }; |
|
826 |
|
827 // * `data` serializer: it simply transforms a buffer to a hex string. |
|
828 exports.serializers.data = function(data) { |
|
829 return data.toString('hex'); |
|
830 }; |