testing/xpcshell/node-http2/node_modules/http2-protocol/lib/framer.js

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:b30f2316c8e7
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 };

mercurial