151 lines
3.7 KiB
JavaScript
151 lines
3.7 KiB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
var spdy = require('../spdy'),
|
|
http = require('http');
|
|
|
|
//
|
|
// ### function _renderHeaders ()
|
|
// Copy pasted from lib/http.js
|
|
// (added lowercase)
|
|
//
|
|
exports._renderHeaders = function() {
|
|
if (this._header) {
|
|
throw new Error("Can't render headers after they are sent to the client.");
|
|
}
|
|
|
|
if (!this._headers) return {};
|
|
|
|
var headers = {};
|
|
var keys = Object.keys(this._headers);
|
|
for (var i = 0, l = keys.length; i < l; i++) {
|
|
var key = keys[i];
|
|
headers[(this._headerNames[key] || '').toLowerCase()] = this._headers[key];
|
|
}
|
|
return headers;
|
|
};
|
|
|
|
//
|
|
// ### function writeHead (statusCode)
|
|
// #### @statusCode {Number} HTTP Status code
|
|
// .writeHead() wrapper
|
|
// (Sorry, copy pasted from lib/http.js)
|
|
//
|
|
exports.writeHead = function(statusCode) {
|
|
if (this._headerSent) return;
|
|
this._headerSent = true;
|
|
|
|
var reasonPhrase, headers, headerIndex;
|
|
|
|
if (typeof arguments[1] == 'string') {
|
|
reasonPhrase = arguments[1];
|
|
headerIndex = 2;
|
|
} else {
|
|
reasonPhrase = http.STATUS_CODES[statusCode] || 'unknown';
|
|
headerIndex = 1;
|
|
}
|
|
this.statusCode = statusCode;
|
|
|
|
var obj = arguments[headerIndex];
|
|
|
|
if (obj && this._headers) {
|
|
// Slow-case: when progressive API and header fields are passed.
|
|
headers = this._renderHeaders();
|
|
|
|
// handle object case
|
|
var keys = Object.keys(obj);
|
|
for (var i = 0; i < keys.length; i++) {
|
|
var k = keys[i];
|
|
if (k) headers[k] = obj[k];
|
|
}
|
|
} else if (this._headers) {
|
|
// only progressive api is used
|
|
headers = this._renderHeaders();
|
|
} else {
|
|
// only writeHead() called
|
|
headers = obj;
|
|
}
|
|
|
|
// cleanup
|
|
this._header = '';
|
|
|
|
// Do not send data to new connections after GOAWAY
|
|
if (this.socket.isGoaway()) return;
|
|
|
|
this.socket.lock(function() {
|
|
var socket = this;
|
|
|
|
this.framer.replyFrame(
|
|
this.id,
|
|
statusCode,
|
|
reasonPhrase,
|
|
headers,
|
|
function (err, frame) {
|
|
// TODO: Handle err
|
|
socket.connection.write(frame);
|
|
socket.unlock();
|
|
}
|
|
);
|
|
});
|
|
};
|
|
|
|
//
|
|
// ### function push (url, headers, callback)
|
|
// #### @url {String} absolute or relative url (from root anyway)
|
|
// #### @headers {Object} response headers
|
|
// #### @callbacks {Function} continuation that will receive stream object
|
|
// Initiates push stream
|
|
//
|
|
exports.push = function push(url, headers, callback) {
|
|
if (this.socket._destroyed) {
|
|
return callback(Error('Can\'t open push stream, parent socket destroyed'));
|
|
}
|
|
|
|
this.socket.lock(function() {
|
|
var socket = this,
|
|
id = socket.connection.pushId += 2,
|
|
fullUrl = /^\//.test(url) ?
|
|
this.frame.headers.scheme + '://' +
|
|
(this.frame.headers.host || 'localhost') +
|
|
url
|
|
:
|
|
url;
|
|
|
|
this.framer.streamFrame(
|
|
id,
|
|
this.id,
|
|
{
|
|
method: 'GET',
|
|
url: fullUrl,
|
|
schema: 'https',
|
|
version: 'HTTP/1.1'
|
|
},
|
|
headers,
|
|
function(err, frame) {
|
|
if (err) {
|
|
socket.unlock();
|
|
callback(err);
|
|
} else {
|
|
socket.connection.write(frame);
|
|
socket.unlock();
|
|
|
|
var stream = new spdy.server.Stream(socket.connection, {
|
|
type: 'SYN_STREAM',
|
|
push: true,
|
|
id: id,
|
|
assoc: socket.id,
|
|
priority: 0,
|
|
headers: {}
|
|
});
|
|
|
|
socket.connection.streams[id] = stream;
|
|
socket.pushes.push(stream);
|
|
|
|
callback(null, stream);
|
|
}
|
|
}
|
|
);
|
|
});
|
|
};
|