Name: js-handler/node_modules/restify/node_modules/spdy/lib/spdy/parser.js 
1:
var parser = exports;
2:
 
3:
var spdy = require('../spdy'),
4:
    util = require('util'),
5:
    stream = require('stream'),
6:
    Buffer = require('buffer').Buffer;
7:
 
8:
var legacy = !stream.Duplex;
9:
 
10:
if (legacy) {
11:
  var DuplexStream = stream;
12:
} else {
13:
  var DuplexStream = stream.Duplex;
14:
}
15:
 
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);
23:
 
24:
  this.drained = true;
25:
  this.paused = false;
26:
  this.buffer = [];
27:
  this.buffered = 0;
28:
  this.waiting = 8;
29:
 
30:
  this.state = { type: 'frame-head' };
31:
  this.socket = connection.socket;
32:
  this.connection = connection;
33:
  this.framer = null;
34:
 
35:
  this.connection = connection;
36:
 
37:
  if (legacy) {
38:
    this.readable = this.writable = true;
39:
  }
40:
}
41:
util.inherits(Parser, DuplexStream);
42:
 
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:
};
51:
 
52:
//
53:
// ### function destroy ()
54:
// Just a stub.
55:
//
56:
Parser.prototype.destroy = function destroy() {
57:
};
58:
 
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() {};
69:
 
70:
  if (data !== undefined) {
71:
    // Buffer data
72:
    this.buffer.push(data);
73:
    this.buffered += data.length;
74:
  }
75:
 
76:
  // Notify caller about state (for piping)
77:
  if (this.paused) return false;
78:
 
79:
  // We shall not do anything until we get all expected data
80:
  if (this.buffered < this.waiting) return cb();
81:
 
82:
  // Mark parser as not drained
83:
  if (data !== undefined) this.drained = false;
84:
 
85:
  var self = this,
86:
      buffer = new Buffer(this.waiting),
87:
      sliced = 0,
88:
      offset = 0;
89:
 
90:
  while (this.waiting > offset && sliced < this.buffer.length) {
91:
    var chunk = this.buffer[sliced++],
92:
        overmatched = false;
93:
 
94:
    // Copy chunk into `buffer`
95:
    if (chunk.length > this.waiting - offset) {
96:
      chunk.copy(buffer, offset, 0, this.waiting - offset);
97:
 
98:
      this.buffer[--sliced] = chunk.slice(this.waiting - offset);
99:
      this.buffered += this.buffer[sliced].length;
100:
 
101:
      overmatched = true;
102:
    } else {
103:
      chunk.copy(buffer, offset);
104:
    }
105:
 
106:
    // Move offset and decrease amount of buffered data
107:
    offset += chunk.length;
108:
    this.buffered -= chunk.length;
109:
 
110:
    if (overmatched) break;
111:
  }
112:
 
113:
  // Remove used buffers
114:
  this.buffer = this.buffer.slice(sliced);
115:
 
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;
121:
 
122:
    // Propagate errors
123:
    if (err) {
124:
      cb();
125:
      return self.emit('error', err);
126:
    }
127:
 
128:
    // Set new `waiting`
129:
    self.waiting = waiting;
130:
 
131:
    if (self.waiting <= self.buffered) {
132:
      self._write(undefined, null, cb);
133:
    } else {
134:
      process.nextTick(function() {
135:
        if (self.drained) return;
136:
 
137:
        // Mark parser as drained
138:
        self.drained = true;
139:
        self.emit('drain');
140:
      });
141:
 
142:
      cb();
143:
    }
144:
  });
145:
};
146:
 
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;
156:
 
157:
  //
158:
  // ### function end ()
159:
  // Stream's end() implementation
160:
  //
161:
  Parser.prototype.end = function end() {
162:
    this.emit('end');
163:
  };
164:
}
165:
 
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);
174:
 
175:
    this.framer = new spdy.protocol[version].Framer(
176:
      spdy.utils.zwrap(this.connection._deflate),
177:
      spdy.utils.zwrap(this.connection._inflate)
178:
    );
179:
 
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:
};
190:
 
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);
201:
 
202:
    // Lazily create framer
203:
    if (!this.framer && header.control) {
204:
      this.createFramer(header.version);
205:
    }
206:
 
207:
    state.type = 'frame-body';
208:
    callback(null, header.length);
209:
  } else if (state.type === 'frame-body') {
210:
    var self = this;
211:
 
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:
    }
225:
 
226:
    function onFrame(err, frame) {
227:
      if (err) return callback(err);
228:
 
229:
      self.emit('frame', frame);
230:
 
231:
      state.type = 'frame-head';
232:
      callback(null, 8);
233:
    };
234:
  }
235:
};