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; };