Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
1 var framer = exports;
3 var spdy = require('../../../spdy'),
4 Buffer = require('buffer').Buffer,
5 protocol = require('./');
7 //
8 // ### function Framer (deflate, inflate)
9 // #### @deflate {zlib.Deflate} Deflate stream
10 // #### @inflate {zlib.Inflate} Inflate stream
11 // Framer constructor
12 //
13 function Framer(deflate, inflate) {
14 this.version = 3;
15 this.deflate = deflate;
16 this.inflate = inflate;
17 }
18 exports.Framer = Framer;
20 //
21 // ### function execute (header, body, callback)
22 // #### @header {Object} Frame headers
23 // #### @body {Buffer} Frame's body
24 // #### @callback {Function} Continuation callback
25 // Parse frame (decompress data and create streams)
26 //
27 Framer.prototype.execute = function execute(header, body, callback) {
28 // SYN_STREAM or SYN_REPLY
29 if (header.type === 0x01 || header.type === 0x02) {
30 var frame = protocol.parseSynHead(header.type, header.flags, body);
32 body = body.slice(frame._offset);
34 this.inflate(body, function(err, chunks, length) {
35 if (err) return callback(err);
37 var pairs = new Buffer(length);
38 for (var i = 0, offset = 0; i < chunks.length; i++) {
39 chunks[i].copy(pairs, offset);
40 offset += chunks[i].length;
41 }
43 frame.headers = protocol.parseHeaders(pairs);
44 frame.url = frame.headers.path || '';
46 callback(null, frame);
47 });
48 // RST_STREAM
49 } else if (header.type === 0x03) {
50 callback(null, protocol.parseRst(body));
51 // SETTINGS
52 } else if (header.type === 0x04) {
53 callback(null, protocol.parseSettings(body));
54 } else if (header.type === 0x05) {
55 callback(null, { type: 'NOOP' });
56 // PING
57 } else if (header.type === 0x06) {
58 callback(null, { type: 'PING', pingId: body });
59 // GOAWAY
60 } else if (header.type === 0x07) {
61 callback(null, protocol.parseGoaway(body));
62 } else if (header.type === 0x09) {
63 callback(null, protocol.parseWindowUpdate(body));
64 } else {
65 callback(null, { type: 'unknown: ' + header.type, body: body });
66 }
67 };
69 //
70 // internal, converts object into spdy dictionary
71 //
72 function headersToDict(headers, preprocess) {
73 function stringify(value) {
74 if (value !== undefined) {
75 if (Array.isArray(value)) {
76 return value.join('\x00');
77 } else if (typeof value === 'string') {
78 return value;
79 } else {
80 return value.toString();
81 }
82 } else {
83 return '';
84 }
85 }
87 // Lower case of all headers keys
88 var loweredHeaders = {};
89 Object.keys(headers || {}).map(function(key) {
90 loweredHeaders[key.toLowerCase()] = headers[key];
91 });
93 // Allow outer code to add custom headers or remove something
94 if (preprocess) preprocess(loweredHeaders);
96 // Transform object into kv pairs
97 var len = 4,
98 pairs = Object.keys(loweredHeaders).filter(function(key) {
99 var lkey = key.toLowerCase();
100 return lkey !== 'connection' && lkey !== 'keep-alive' &&
101 lkey !== 'proxy-connection' && lkey !== 'transfer-encoding';
102 }).map(function(key) {
103 var klen = Buffer.byteLength(key),
104 value = stringify(loweredHeaders[key]),
105 vlen = Buffer.byteLength(value);
107 len += 8 + klen + vlen;
108 return [klen, key, vlen, value];
109 }),
110 result = new Buffer(len);
112 result.writeUInt32BE(pairs.length, 0, true);
114 var offset = 4;
115 pairs.forEach(function(pair) {
116 // Write key length
117 result.writeUInt32BE(pair[0], offset, true);
118 // Write key
119 result.write(pair[1], offset + 4);
121 offset += pair[0] + 4;
123 // Write value length
124 result.writeUInt32BE(pair[2], offset, true);
125 // Write value
126 result.write(pair[3], offset + 4);
128 offset += pair[2] + 4;
129 });
131 return result;
132 };
134 Framer.prototype._synFrame = function _synFrame(type, id, assoc, priority, dict,
135 callback) {
136 // Compress headers
137 this.deflate(dict, function (err, chunks, size) {
138 if (err) return callback(err);
140 var offset = type === 'SYN_STREAM' ? 18 : 12,
141 total = (type === 'SYN_STREAM' ? 10 : 4) + size,
142 frame = new Buffer(offset + size);;
144 frame.writeUInt16BE(0x8003, 0, true); // Control + Version
145 frame.writeUInt16BE(type === 'SYN_STREAM' ? 1 : 2, 2, true); // type
146 frame.writeUInt32BE(total & 0x00ffffff, 4, true); // No flag support
147 frame.writeUInt32BE(id & 0x7fffffff, 8, true); // Stream-ID
149 if (type === 'SYN_STREAM') {
150 frame[4] = 2;
151 frame.writeUInt32BE(assoc & 0x7fffffff, 12, true); // Stream-ID
152 }
154 frame.writeUInt8(priority & 0x7, 16, true); // Priority
156 for (var i = 0; i < chunks.length; i++) {
157 chunks[i].copy(frame, offset);
158 offset += chunks[i].length;
159 }
161 callback(null, frame);
162 });
163 };
165 //
166 // ### function replyFrame (id, code, reason, headers, callback)
167 // #### @id {Number} Stream ID
168 // #### @code {Number} HTTP Status Code
169 // #### @reason {String} (optional)
170 // #### @headers {Object|Array} (optional) HTTP headers
171 // #### @callback {Function} Continuation function
172 // Sends SYN_REPLY frame
173 //
174 Framer.prototype.replyFrame = function replyFrame(id, code, reason, headers,
175 callback) {
176 var dict = headersToDict(headers, function(headers) {
177 headers[':status'] = code + ' ' + reason;
178 headers[':version'] = 'HTTP/1.1';
179 });
181 this._synFrame('SYN_REPLY', id, null, 0, dict, callback);
182 };
184 //
185 // ### function streamFrame (id, assoc, headers, callback)
186 // #### @id {Number} stream id
187 // #### @assoc {Number} associated stream id
188 // #### @meta {Object} meta headers ( method, scheme, url, version )
189 // #### @headers {Object} stream headers
190 // #### @callback {Function} continuation callback
191 // Create SYN_STREAM frame
192 // (needed for server push and testing)
193 //
194 Framer.prototype.streamFrame = function streamFrame(id, assoc, meta, headers,
195 callback) {
196 var dict = headersToDict(headers, function(headers) {
197 headers[':status'] = 200;
198 headers[':version'] = meta.version || 'HTTP/1.1';
199 headers[':path'] = meta.path;
200 headers[':scheme'] = meta.scheme || 'https';
201 headers[':host'] = meta.host;
202 });
204 this._synFrame('SYN_STREAM', id, assoc, meta.priority, dict, callback);
205 };
207 //
208 // ### function dataFrame (id, fin, data)
209 // #### @id {Number} Stream id
210 // #### @fin {Bool} Is this data frame last frame
211 // #### @data {Buffer} Response data
212 // Sends DATA frame
213 //
214 Framer.prototype.dataFrame = function dataFrame(id, fin, data) {
215 if (!fin && !data.length) return [];
217 var frame = new Buffer(8 + data.length);
219 frame.writeUInt32BE(id & 0x7fffffff, 0, true);
220 frame.writeUInt32BE(data.length & 0x00ffffff, 4, true);
221 frame.writeUInt8(fin ? 0x01 : 0x0, 4, true);
223 if (data.length) data.copy(frame, 8);
225 return frame;
226 };
228 //
229 // ### function pingFrame (id)
230 // #### @id {Buffer} Ping ID
231 // Sends PING frame
232 //
233 Framer.prototype.pingFrame = function pingFrame(id) {
234 var header = new Buffer(12);
236 header.writeUInt32BE(0x80030006, 0, true); // Version and type
237 header.writeUInt32BE(0x00000004, 4, true); // Length
238 id.copy(header, 8, 0, 4); // ID
240 return header;
241 };
243 //
244 // ### function rstFrame (id, code)
245 // #### @id {Number} Stream ID
246 // #### @code {NUmber} RST Code
247 // Sends PING frame
248 //
249 Framer.prototype.rstFrame = function rstFrame(id, code) {
250 var header;
252 if (!(header = Framer.rstCache[code])) {
253 header = new Buffer(16);
255 header.writeUInt32BE(0x80030003, 0, true); // Version and type
256 header.writeUInt32BE(0x00000008, 4, true); // Length
257 header.writeUInt32BE(id & 0x7fffffff, 8, true); // Stream ID
258 header.writeUInt32BE(code, 12, true); // Status Code
260 Framer.rstCache[code] = header;
261 }
263 return header;
264 };
265 Framer.rstCache = {};
267 //
268 // ### function settingsFrame (options)
269 // #### @options {Object} settings frame options
270 // Sends SETTINGS frame with MAX_CONCURRENT_STREAMS and initial window
271 //
272 Framer.prototype.settingsFrame = function settingsFrame(options) {
273 var settings,
274 key = options.maxStreams + ':' + options.windowSize;
276 if (!(settings = Framer.settingsCache[key])) {
277 settings = new Buffer(28);
279 settings.writeUInt32BE(0x80030004, 0, true); // Version and type
280 settings.writeUInt32BE((4 + 8 * 2) & 0x00FFFFFF, 4, true); // length
281 settings.writeUInt32BE(0x00000002, 8, true); // Count of entries
283 settings.writeUInt32BE(0x01000004, 12, true); // Entry ID and Persist flag
284 settings.writeUInt32BE(options.maxStreams & 0x7fffffff, 16, true);
286 settings.writeUInt32BE(0x01000007, 20, true); // Entry ID and Persist flag
287 settings.writeUInt32BE(options.windowSize & 0x7fffffff, 24, true);
289 Framer.settingsCache[key] = settings;
290 }
292 return settings;
293 };
294 Framer.settingsCache = {};
296 //
297 // ### function windowSizeFrame (size)
298 // #### @size {Number} data transfer window size
299 // Sends SETTINGS frame with window size
300 //
301 Framer.prototype.windowSizeFrame = function windowSizeFrame(size) {
302 var settings;
304 if (!(settings = Framer.windowSizeCache[size])) {
305 settings = new Buffer(20);
307 settings.writeUInt32BE(0x80030004, 0, true); // Version and type
308 settings.writeUInt32BE((4 + 8) & 0x00FFFFFF, 4, true); // length
309 settings.writeUInt32BE(0x00000001, 8, true); // Count of entries
311 settings.writeUInt32BE(0x01000007, 12, true); // Entry ID and Persist flag
312 settings.writeUInt32BE(size & 0x7fffffff, 16, true); // Window Size (KB)
314 Framer.windowSizeCache[size] = settings;
315 }
317 return settings;
318 };
319 Framer.windowSizeCache = {};
321 //
322 // ### function windowUpdateFrame (id)
323 // #### @id {Buffer} WindowUpdate ID
324 // Sends WINDOW_UPDATE frame
325 //
326 Framer.prototype.windowUpdateFrame = function windowUpdateFrame(id, delta) {
327 var header = new Buffer(16);
329 header.writeUInt32BE(0x80030009, 0, true); // Version and type
330 header.writeUInt32BE(0x00000008, 4, true); // Length
331 header.writeUInt32BE(id & 0x7fffffff, 8, true); // ID
332 header.writeUInt32BE(delta & 0x7fffffff, 12, true); // delta
334 return header;
335 };