testing/xpcshell/node-spdy/lib/spdy/parser.js

Wed, 31 Dec 2014 06:55:46 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:46 +0100
changeset 1
ca08bd8f51b2
permissions
-rw-r--r--

Added tag TORBROWSER_REPLICA for changeset 6474c204b198

michael@0 1 var parser = exports;
michael@0 2
michael@0 3 var spdy = require('../spdy'),
michael@0 4 util = require('util'),
michael@0 5 stream = require('stream'),
michael@0 6 Buffer = require('buffer').Buffer;
michael@0 7
michael@0 8 var legacy = !stream.Duplex;
michael@0 9
michael@0 10 if (legacy) {
michael@0 11 var DuplexStream = stream;
michael@0 12 } else {
michael@0 13 var DuplexStream = stream.Duplex;
michael@0 14 }
michael@0 15
michael@0 16 //
michael@0 17 // ### function Parser (connection)
michael@0 18 // #### @connection {spdy.Connection} connection
michael@0 19 // SPDY protocol frames parser's @constructor
michael@0 20 //
michael@0 21 function Parser(connection) {
michael@0 22 DuplexStream.call(this);
michael@0 23
michael@0 24 this.drained = true;
michael@0 25 this.paused = false;
michael@0 26 this.buffer = [];
michael@0 27 this.buffered = 0;
michael@0 28 this.waiting = 8;
michael@0 29
michael@0 30 this.state = { type: 'frame-head' };
michael@0 31 this.socket = connection.socket;
michael@0 32 this.connection = connection;
michael@0 33 this.framer = null;
michael@0 34
michael@0 35 this.connection = connection;
michael@0 36
michael@0 37 if (legacy) {
michael@0 38 this.readable = this.writable = true;
michael@0 39 }
michael@0 40 }
michael@0 41 util.inherits(Parser, DuplexStream);
michael@0 42
michael@0 43 //
michael@0 44 // ### function create (connection)
michael@0 45 // #### @connection {spdy.Connection} connection
michael@0 46 // @constructor wrapper
michael@0 47 //
michael@0 48 parser.create = function create(connection) {
michael@0 49 return new Parser(connection);
michael@0 50 };
michael@0 51
michael@0 52 //
michael@0 53 // ### function destroy ()
michael@0 54 // Just a stub.
michael@0 55 //
michael@0 56 Parser.prototype.destroy = function destroy() {
michael@0 57 };
michael@0 58
michael@0 59 //
michael@0 60 // ### function _write (data, encoding, cb)
michael@0 61 // #### @data {Buffer} chunk of data
michael@0 62 // #### @encoding {Null} encoding
michael@0 63 // #### @cb {Function} callback
michael@0 64 // Writes or buffers data to parser
michael@0 65 //
michael@0 66 Parser.prototype._write = function write(data, encoding, cb) {
michael@0 67 // Legacy compatibility
michael@0 68 if (!cb) cb = function() {};
michael@0 69
michael@0 70 if (data !== undefined) {
michael@0 71 // Buffer data
michael@0 72 this.buffer.push(data);
michael@0 73 this.buffered += data.length;
michael@0 74 }
michael@0 75
michael@0 76 // Notify caller about state (for piping)
michael@0 77 if (this.paused) return false;
michael@0 78
michael@0 79 // We shall not do anything until we get all expected data
michael@0 80 if (this.buffered < this.waiting) return cb();
michael@0 81
michael@0 82 // Mark parser as not drained
michael@0 83 if (data !== undefined) this.drained = false;
michael@0 84
michael@0 85 var self = this,
michael@0 86 buffer = new Buffer(this.waiting),
michael@0 87 sliced = 0,
michael@0 88 offset = 0;
michael@0 89
michael@0 90 while (this.waiting > offset && sliced < this.buffer.length) {
michael@0 91 var chunk = this.buffer[sliced++],
michael@0 92 overmatched = false;
michael@0 93
michael@0 94 // Copy chunk into `buffer`
michael@0 95 if (chunk.length > this.waiting - offset) {
michael@0 96 chunk.copy(buffer, offset, 0, this.waiting - offset);
michael@0 97
michael@0 98 this.buffer[--sliced] = chunk.slice(this.waiting - offset);
michael@0 99 this.buffered += this.buffer[sliced].length;
michael@0 100
michael@0 101 overmatched = true;
michael@0 102 } else {
michael@0 103 chunk.copy(buffer, offset);
michael@0 104 }
michael@0 105
michael@0 106 // Move offset and decrease amount of buffered data
michael@0 107 offset += chunk.length;
michael@0 108 this.buffered -= chunk.length;
michael@0 109
michael@0 110 if (overmatched) break;
michael@0 111 }
michael@0 112
michael@0 113 // Remove used buffers
michael@0 114 this.buffer = this.buffer.slice(sliced);
michael@0 115
michael@0 116 // Executed parser for buffered data
michael@0 117 this.paused = true;
michael@0 118 this.execute(this.state, buffer, function (err, waiting) {
michael@0 119 // And unpause once execution finished
michael@0 120 self.paused = false;
michael@0 121
michael@0 122 // Propagate errors
michael@0 123 if (err) {
michael@0 124 cb();
michael@0 125 return self.emit('error', err);
michael@0 126 }
michael@0 127
michael@0 128 // Set new `waiting`
michael@0 129 self.waiting = waiting;
michael@0 130
michael@0 131 if (self.waiting <= self.buffered) {
michael@0 132 self._write(undefined, null, cb);
michael@0 133 } else {
michael@0 134 process.nextTick(function() {
michael@0 135 if (self.drained) return;
michael@0 136
michael@0 137 // Mark parser as drained
michael@0 138 self.drained = true;
michael@0 139 self.emit('drain');
michael@0 140 });
michael@0 141
michael@0 142 cb();
michael@0 143 }
michael@0 144 });
michael@0 145 };
michael@0 146
michael@0 147 if (legacy) {
michael@0 148 //
michael@0 149 // ### function write (data, encoding, cb)
michael@0 150 // #### @data {Buffer} chunk of data
michael@0 151 // #### @encoding {Null} encoding
michael@0 152 // #### @cb {Function} callback
michael@0 153 // Legacy method
michael@0 154 //
michael@0 155 Parser.prototype.write = Parser.prototype._write;
michael@0 156
michael@0 157 //
michael@0 158 // ### function end ()
michael@0 159 // Stream's end() implementation
michael@0 160 //
michael@0 161 Parser.prototype.end = function end() {
michael@0 162 this.emit('end');
michael@0 163 };
michael@0 164 }
michael@0 165
michael@0 166 //
michael@0 167 // ### function createFramer (version)
michael@0 168 // #### @version {Number} Protocol version, either 2 or 3
michael@0 169 // Sets framer instance on Parser's instance
michael@0 170 //
michael@0 171 Parser.prototype.createFramer = function createFramer(version) {
michael@0 172 if (spdy.protocol[version]) {
michael@0 173 this.emit('version', version);
michael@0 174
michael@0 175 this.framer = new spdy.protocol[version].Framer(
michael@0 176 spdy.utils.zwrap(this.connection._deflate),
michael@0 177 spdy.utils.zwrap(this.connection._inflate)
michael@0 178 );
michael@0 179
michael@0 180 // Propagate framer to connection
michael@0 181 this.connection._framer = this.framer;
michael@0 182 this.emit('framer', this.framer);
michael@0 183 } else {
michael@0 184 this.emit(
michael@0 185 'error',
michael@0 186 new Error('Unknown protocol version requested: ' + version)
michael@0 187 );
michael@0 188 }
michael@0 189 };
michael@0 190
michael@0 191 //
michael@0 192 // ### function execute (state, data, callback)
michael@0 193 // #### @state {Object} Parser's state
michael@0 194 // #### @data {Buffer} Incoming data
michael@0 195 // #### @callback {Function} continuation callback
michael@0 196 // Parse buffered data
michael@0 197 //
michael@0 198 Parser.prototype.execute = function execute(state, data, callback) {
michael@0 199 if (state.type === 'frame-head') {
michael@0 200 var header = state.header = spdy.protocol.generic.parseHeader(data);
michael@0 201
michael@0 202 // Lazily create framer
michael@0 203 if (!this.framer && header.control) {
michael@0 204 this.createFramer(header.version);
michael@0 205 }
michael@0 206
michael@0 207 state.type = 'frame-body';
michael@0 208 callback(null, header.length);
michael@0 209 } else if (state.type === 'frame-body') {
michael@0 210 var self = this;
michael@0 211
michael@0 212 // Data frame
michael@0 213 if (!state.header.control) {
michael@0 214 return onFrame(null, {
michael@0 215 type: 'DATA',
michael@0 216 id: state.header.id,
michael@0 217 fin: (state.header.flags & 0x01) === 0x01,
michael@0 218 compressed: (state.header.flags & 0x02) === 0x02,
michael@0 219 data: data
michael@0 220 });
michael@0 221 } else {
michael@0 222 // Control frame
michael@0 223 this.framer.execute(state.header, data, onFrame);
michael@0 224 }
michael@0 225
michael@0 226 function onFrame(err, frame) {
michael@0 227 if (err) return callback(err);
michael@0 228
michael@0 229 self.emit('frame', frame);
michael@0 230
michael@0 231 state.type = 'frame-head';
michael@0 232 callback(null, 8);
michael@0 233 };
michael@0 234 }
michael@0 235 };

mercurial