1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/testing/xpcshell/node-spdy/lib/spdy/parser.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,235 @@ 1.4 +var parser = exports; 1.5 + 1.6 +var spdy = require('../spdy'), 1.7 + util = require('util'), 1.8 + stream = require('stream'), 1.9 + Buffer = require('buffer').Buffer; 1.10 + 1.11 +var legacy = !stream.Duplex; 1.12 + 1.13 +if (legacy) { 1.14 + var DuplexStream = stream; 1.15 +} else { 1.16 + var DuplexStream = stream.Duplex; 1.17 +} 1.18 + 1.19 +// 1.20 +// ### function Parser (connection) 1.21 +// #### @connection {spdy.Connection} connection 1.22 +// SPDY protocol frames parser's @constructor 1.23 +// 1.24 +function Parser(connection) { 1.25 + DuplexStream.call(this); 1.26 + 1.27 + this.drained = true; 1.28 + this.paused = false; 1.29 + this.buffer = []; 1.30 + this.buffered = 0; 1.31 + this.waiting = 8; 1.32 + 1.33 + this.state = { type: 'frame-head' }; 1.34 + this.socket = connection.socket; 1.35 + this.connection = connection; 1.36 + this.framer = null; 1.37 + 1.38 + this.connection = connection; 1.39 + 1.40 + if (legacy) { 1.41 + this.readable = this.writable = true; 1.42 + } 1.43 +} 1.44 +util.inherits(Parser, DuplexStream); 1.45 + 1.46 +// 1.47 +// ### function create (connection) 1.48 +// #### @connection {spdy.Connection} connection 1.49 +// @constructor wrapper 1.50 +// 1.51 +parser.create = function create(connection) { 1.52 + return new Parser(connection); 1.53 +}; 1.54 + 1.55 +// 1.56 +// ### function destroy () 1.57 +// Just a stub. 1.58 +// 1.59 +Parser.prototype.destroy = function destroy() { 1.60 +}; 1.61 + 1.62 +// 1.63 +// ### function _write (data, encoding, cb) 1.64 +// #### @data {Buffer} chunk of data 1.65 +// #### @encoding {Null} encoding 1.66 +// #### @cb {Function} callback 1.67 +// Writes or buffers data to parser 1.68 +// 1.69 +Parser.prototype._write = function write(data, encoding, cb) { 1.70 + // Legacy compatibility 1.71 + if (!cb) cb = function() {}; 1.72 + 1.73 + if (data !== undefined) { 1.74 + // Buffer data 1.75 + this.buffer.push(data); 1.76 + this.buffered += data.length; 1.77 + } 1.78 + 1.79 + // Notify caller about state (for piping) 1.80 + if (this.paused) return false; 1.81 + 1.82 + // We shall not do anything until we get all expected data 1.83 + if (this.buffered < this.waiting) return cb(); 1.84 + 1.85 + // Mark parser as not drained 1.86 + if (data !== undefined) this.drained = false; 1.87 + 1.88 + var self = this, 1.89 + buffer = new Buffer(this.waiting), 1.90 + sliced = 0, 1.91 + offset = 0; 1.92 + 1.93 + while (this.waiting > offset && sliced < this.buffer.length) { 1.94 + var chunk = this.buffer[sliced++], 1.95 + overmatched = false; 1.96 + 1.97 + // Copy chunk into `buffer` 1.98 + if (chunk.length > this.waiting - offset) { 1.99 + chunk.copy(buffer, offset, 0, this.waiting - offset); 1.100 + 1.101 + this.buffer[--sliced] = chunk.slice(this.waiting - offset); 1.102 + this.buffered += this.buffer[sliced].length; 1.103 + 1.104 + overmatched = true; 1.105 + } else { 1.106 + chunk.copy(buffer, offset); 1.107 + } 1.108 + 1.109 + // Move offset and decrease amount of buffered data 1.110 + offset += chunk.length; 1.111 + this.buffered -= chunk.length; 1.112 + 1.113 + if (overmatched) break; 1.114 + } 1.115 + 1.116 + // Remove used buffers 1.117 + this.buffer = this.buffer.slice(sliced); 1.118 + 1.119 + // Executed parser for buffered data 1.120 + this.paused = true; 1.121 + this.execute(this.state, buffer, function (err, waiting) { 1.122 + // And unpause once execution finished 1.123 + self.paused = false; 1.124 + 1.125 + // Propagate errors 1.126 + if (err) { 1.127 + cb(); 1.128 + return self.emit('error', err); 1.129 + } 1.130 + 1.131 + // Set new `waiting` 1.132 + self.waiting = waiting; 1.133 + 1.134 + if (self.waiting <= self.buffered) { 1.135 + self._write(undefined, null, cb); 1.136 + } else { 1.137 + process.nextTick(function() { 1.138 + if (self.drained) return; 1.139 + 1.140 + // Mark parser as drained 1.141 + self.drained = true; 1.142 + self.emit('drain'); 1.143 + }); 1.144 + 1.145 + cb(); 1.146 + } 1.147 + }); 1.148 +}; 1.149 + 1.150 +if (legacy) { 1.151 + // 1.152 + // ### function write (data, encoding, cb) 1.153 + // #### @data {Buffer} chunk of data 1.154 + // #### @encoding {Null} encoding 1.155 + // #### @cb {Function} callback 1.156 + // Legacy method 1.157 + // 1.158 + Parser.prototype.write = Parser.prototype._write; 1.159 + 1.160 + // 1.161 + // ### function end () 1.162 + // Stream's end() implementation 1.163 + // 1.164 + Parser.prototype.end = function end() { 1.165 + this.emit('end'); 1.166 + }; 1.167 +} 1.168 + 1.169 +// 1.170 +// ### function createFramer (version) 1.171 +// #### @version {Number} Protocol version, either 2 or 3 1.172 +// Sets framer instance on Parser's instance 1.173 +// 1.174 +Parser.prototype.createFramer = function createFramer(version) { 1.175 + if (spdy.protocol[version]) { 1.176 + this.emit('version', version); 1.177 + 1.178 + this.framer = new spdy.protocol[version].Framer( 1.179 + spdy.utils.zwrap(this.connection._deflate), 1.180 + spdy.utils.zwrap(this.connection._inflate) 1.181 + ); 1.182 + 1.183 + // Propagate framer to connection 1.184 + this.connection._framer = this.framer; 1.185 + this.emit('framer', this.framer); 1.186 + } else { 1.187 + this.emit( 1.188 + 'error', 1.189 + new Error('Unknown protocol version requested: ' + version) 1.190 + ); 1.191 + } 1.192 +}; 1.193 + 1.194 +// 1.195 +// ### function execute (state, data, callback) 1.196 +// #### @state {Object} Parser's state 1.197 +// #### @data {Buffer} Incoming data 1.198 +// #### @callback {Function} continuation callback 1.199 +// Parse buffered data 1.200 +// 1.201 +Parser.prototype.execute = function execute(state, data, callback) { 1.202 + if (state.type === 'frame-head') { 1.203 + var header = state.header = spdy.protocol.generic.parseHeader(data); 1.204 + 1.205 + // Lazily create framer 1.206 + if (!this.framer && header.control) { 1.207 + this.createFramer(header.version); 1.208 + } 1.209 + 1.210 + state.type = 'frame-body'; 1.211 + callback(null, header.length); 1.212 + } else if (state.type === 'frame-body') { 1.213 + var self = this; 1.214 + 1.215 + // Data frame 1.216 + if (!state.header.control) { 1.217 + return onFrame(null, { 1.218 + type: 'DATA', 1.219 + id: state.header.id, 1.220 + fin: (state.header.flags & 0x01) === 0x01, 1.221 + compressed: (state.header.flags & 0x02) === 0x02, 1.222 + data: data 1.223 + }); 1.224 + } else { 1.225 + // Control frame 1.226 + this.framer.execute(state.header, data, onFrame); 1.227 + } 1.228 + 1.229 + function onFrame(err, frame) { 1.230 + if (err) return callback(err); 1.231 + 1.232 + self.emit('frame', frame); 1.233 + 1.234 + state.type = 'frame-head'; 1.235 + callback(null, 8); 1.236 + }; 1.237 + } 1.238 +};