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

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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

mercurial