210 lines
5.0 KiB
JavaScript
210 lines
5.0 KiB
JavaScript
var spdy = require('../spdy'),
|
|
utils = spdy.utils,
|
|
http = require('http'),
|
|
Stream = require('stream').Stream,
|
|
res = http.ServerResponse.prototype;
|
|
|
|
//
|
|
// ### function _renderHeaders ()
|
|
// Copy pasted from lib/http.js
|
|
// (added lowercase)
|
|
//
|
|
exports._renderHeaders = function renderHeaders() {
|
|
if (this._header)
|
|
throw new Error("Can't render headers after they are sent to the client.");
|
|
|
|
var keys = Object.keys(this._headerNames);
|
|
for (var i = 0, l = keys.length; i < l; i++) {
|
|
var key = keys[i];
|
|
this._headerNames[key] = this._headerNames[key].toLowerCase();
|
|
}
|
|
|
|
return res._renderHeaders.call(this);
|
|
};
|
|
|
|
//
|
|
// ### function writeHead (statusCode)
|
|
// #### @statusCode {Number} HTTP Status code
|
|
// .writeHead() wrapper
|
|
// (Sorry, copy pasted from lib/http.js)
|
|
//
|
|
exports.writeHead = function writeHead(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;
|
|
|
|
// Date header
|
|
if (this.sendDate === true) {
|
|
if (headers === undefined)
|
|
headers = {};
|
|
if (headers.date === undefined)
|
|
headers.date = new Date().toUTCString();
|
|
}
|
|
|
|
this.socket._lock(function() {
|
|
var socket = this;
|
|
|
|
this._spdyState.framer.replyFrame(
|
|
this._spdyState.id,
|
|
statusCode,
|
|
reasonPhrase,
|
|
headers,
|
|
function (err, frame) {
|
|
if (err) {
|
|
socket._unlock();
|
|
socket.emit('error', err);
|
|
return;
|
|
}
|
|
|
|
socket.connection.cork();
|
|
socket.connection.write(frame);
|
|
utils.nextTick(function() {
|
|
socket.connection.uncork();
|
|
});
|
|
socket._unlock();
|
|
}
|
|
);
|
|
});
|
|
};
|
|
|
|
//
|
|
// ### function end (data, encoding, cb)
|
|
// #### @data {Buffer|String} (optional) data
|
|
// #### @encoding {String} (optional) string encoding
|
|
// #### @cb {Function}
|
|
// Send final data
|
|
//
|
|
exports.end = function end(data, encoding, cb) {
|
|
if (this.socket)
|
|
this.socket._spdyState.ending = true;
|
|
|
|
this.constructor.prototype.end.call(this, data, encoding, cb);
|
|
};
|
|
|
|
//
|
|
// ### 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, priority, callback) {
|
|
var socket = this.socket;
|
|
|
|
if (!callback && typeof priority === 'function') {
|
|
callback = priority;
|
|
priority = null;
|
|
}
|
|
if (!priority && typeof priority !== 'number')
|
|
priority = 7;
|
|
|
|
if (!callback)
|
|
callback = function() {};
|
|
|
|
if (!socket || socket._destroyed) {
|
|
var stub = new Stream();
|
|
var err = Error('Can\'t open push stream, parent socket destroyed');
|
|
utils.nextTick(function() {
|
|
if (stub.listeners('error').length !== 0)
|
|
stub.emit('error', err);
|
|
callback(err);
|
|
});
|
|
return stub;
|
|
}
|
|
|
|
var id = socket.connection._spdyState.pushId += 2,
|
|
scheme = socket._spdyState.scheme,
|
|
host = headers.host || socket._spdyState.host || 'localhost',
|
|
fullUrl = /^\//.test(url) ? scheme + '://' + host + url : url;
|
|
|
|
var stream = new spdy.Stream(socket.connection, {
|
|
type: 'SYN_STREAM',
|
|
id: id,
|
|
associated: socket._spdyState.id,
|
|
priority: priority,
|
|
headers: {}
|
|
});
|
|
|
|
stream.associated = socket;
|
|
socket.connection._addStream(stream);
|
|
|
|
socket._lock(function() {
|
|
this._spdyState.framer.streamFrame(
|
|
id,
|
|
this._spdyState.id,
|
|
{
|
|
method: 'GET',
|
|
path: url,
|
|
url: fullUrl,
|
|
scheme: scheme,
|
|
host: host,
|
|
version: 'HTTP/1.1',
|
|
priority: priority,
|
|
status: 200
|
|
},
|
|
headers,
|
|
function(err, frame) {
|
|
if (err) {
|
|
socket._unlock();
|
|
if (callback)
|
|
callback(err);
|
|
stream.destroy(err);
|
|
return;
|
|
} else {
|
|
socket.connection.cork();
|
|
socket.connection.write(frame);
|
|
utils.nextTick(function() {
|
|
socket.connection.uncork();
|
|
});
|
|
socket._unlock();
|
|
}
|
|
|
|
stream.emit('acknowledge');
|
|
if (callback)
|
|
callback(null, stream);
|
|
}
|
|
);
|
|
});
|
|
|
|
return stream;
|
|
};
|