1 // Copyright Joyent, Inc. and other Node contributors.
3 // Permission is hereby granted, free of charge, to any person obtaining a
4 // copy of this software and associated documentation files (the
5 // "Software"), to deal in the Software without restriction, including
6 // without limitation the rights to use, copy, modify, merge, publish,
7 // distribute, sublicense, and/or sell copies of the Software, and to permit
8 // persons to whom the Software is furnished to do so, subject to the
9 // following conditions:
11 // The above copyright notice and this permission notice shall be included
12 // in all copies or substantial portions of the Software.
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17 // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18 // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20 // USE OR OTHER DEALINGS IN THE SOFTWARE.
22 var assert = require('assert').ok;
23 var Stream = require('stream');
24 var timers = require('timers');
25 var util = require('util');
27 var common = require('_http_common');
29 var CRLF = common.CRLF;
30 var chunkExpression = common.chunkExpression;
31 var debug = common.debug;
34 var connectionExpression = /Connection/i;
35 var transferEncodingExpression = /Transfer-Encoding/i;
36 var closeExpression = /close/i;
37 var contentLengthExpression = /Content-Length/i;
38 var dateExpression = /Date/i;
39 var expectExpression = /Expect/i;
41 var automaticHeaders = {
43 'content-length': true,
44 'transfer-encoding': true,
53 dateCache = d.toUTCString();
54 timers.enroll(utcDate, 1000 - d.getMilliseconds());
55 timers._unrefActive(utcDate);
59 utcDate._onTimeout = function() {
60 dateCache = undefined;
64 function OutgoingMessage() {
68 this.outputEncodings = [];
69 this.outputCallbacks = [];
74 this.chunkedEncoding = false;
75 this.shouldKeepAlive = true;
76 this.useChunkedEncodingByDefault = true;
77 this.sendDate = false;
78 this._removedHeader = {};
83 this.finished = false;
84 this._hangupClose = false;
85 this._headerSent = false;
88 this.connection = null;
91 this._headerNames = {};
93 util.inherits(OutgoingMessage, Stream);
96 exports.OutgoingMessage = OutgoingMessage;
99 OutgoingMessage.prototype.setTimeout = function(msecs, callback) {
101 this.on('timeout', callback);
103 this.once('socket', function(socket) {
104 socket.setTimeout(msecs);
107 this.socket.setTimeout(msecs);
111 // It's possible that the socket will be destroyed, and removed from
112 // any messages, before ever calling this. In that case, just skip
113 // it, since something else is destroying this connection anyway.
114 OutgoingMessage.prototype.destroy = function(error) {
116 this.socket.destroy(error);
118 this.once('socket', function(socket) {
119 socket.destroy(error);
124 // This abstract either writing directly to the socket or buffering it.
125 OutgoingMessage.prototype._send = function(data, encoding, callback) {
126 // This is a shameful hack to get the headers and first body chunk onto
127 // the same packet. Future versions of Node are going to take care of
128 // this at a lower level and in a more general way.
129 if (!this._headerSent) {
130 if (util.isString(data) &&
131 encoding !== 'hex' &&
132 encoding !== 'base64') {
133 data = this._header + data;
135 this.output.unshift(this._header);
136 this.outputEncodings.unshift('binary');
137 this.outputCallbacks.unshift(null);
139 this._headerSent = true;
141 return this._writeRaw(data, encoding, callback);
145 OutgoingMessage.prototype._writeRaw = function(data, encoding, callback) {
146 if (util.isFunction(encoding)) {
151 if (data.length === 0) {
152 if (util.isFunction(callback))
153 process.nextTick(callback);
157 if (this.connection &&
158 this.connection._httpMessage === this &&
159 this.connection.writable &&
160 !this.connection.destroyed) {
161 // There might be pending data in the this.output buffer.
162 while (this.output.length) {
163 if (!this.connection.writable) {
164 this._buffer(data, encoding, callback);
167 var c = this.output.shift();
168 var e = this.outputEncodings.shift();
169 var cb = this.outputCallbacks.shift();
170 this.connection.write(c, e, cb);
173 // Directly write to socket.
174 return this.connection.write(data, encoding, callback);
175 } else if (this.connection && this.connection.destroyed) {
176 // The socket was destroyed. If we're still trying to write to it,
177 // then we haven't gotten the 'close' event yet.
180 // buffer, as long as we're not destroyed.
181 this._buffer(data, encoding, callback);
187 OutgoingMessage.prototype._buffer = function(data, encoding, callback) {
188 this.output.push(data);
189 this.outputEncodings.push(encoding);
190 this.outputCallbacks.push(callback);
195 OutgoingMessage.prototype._storeHeader = function(firstLine, headers) {
196 // firstLine in the case of request is: 'GET /index.html HTTP/1.1\r\n'
197 // in the case of response it is: 'HTTP/1.1 200 OK\r\n'
199 sentConnectionHeader: false,
200 sentContentLengthHeader: false,
201 sentTransferEncodingHeader: false,
202 sentDateHeader: false,
204 messageHeader: firstLine
210 var keys = Object.keys(headers);
211 var isArray = util.isArray(headers);
214 for (var i = 0, l = keys.length; i < l; i++) {
217 field = headers[key][0];
218 value = headers[key][1];
221 value = headers[key];
224 if (util.isArray(value)) {
225 for (var j = 0; j < value.length; j++) {
226 storeHeader(this, state, field, value[j]);
229 storeHeader(this, state, field, value);
235 if (this.sendDate === true && state.sentDateHeader === false) {
236 state.messageHeader += 'Date: ' + utcDate() + CRLF;
239 // Force the connection to close when the response is a 204 No Content or
240 // a 304 Not Modified and the user has set a "Transfer-Encoding: chunked"
243 // RFC 2616 mandates that 204 and 304 responses MUST NOT have a body but
244 // node.js used to send out a zero chunk anyway to accommodate clients
245 // that don't have special handling for those responses.
247 // It was pointed out that this might confuse reverse proxies to the point
248 // of creating security liabilities, so suppress the zero chunk and force
249 // the connection to close.
250 var statusCode = this.statusCode;
251 if ((statusCode === 204 || statusCode === 304) &&
252 this.chunkedEncoding === true) {
253 debug(statusCode + ' response should not use chunked encoding,' +
254 ' closing connection.');
255 this.chunkedEncoding = false;
256 this.shouldKeepAlive = false;
260 if (this._removedHeader.connection) {
262 this.shouldKeepAlive = false;
263 } else if (state.sentConnectionHeader === false) {
264 var shouldSendKeepAlive = this.shouldKeepAlive &&
265 (state.sentContentLengthHeader ||
266 this.useChunkedEncodingByDefault ||
268 if (shouldSendKeepAlive) {
269 state.messageHeader += 'Connection: keep-alive\r\n';
272 state.messageHeader += 'Connection: close\r\n';
276 if (state.sentContentLengthHeader === false &&
277 state.sentTransferEncodingHeader === false) {
278 if (this._hasBody && !this._removedHeader['transfer-encoding']) {
279 if (this.useChunkedEncodingByDefault) {
280 state.messageHeader += 'Transfer-Encoding: chunked\r\n';
281 this.chunkedEncoding = true;
286 // Make sure we don't end the 0\r\n\r\n at the end of the message.
287 this.chunkedEncoding = false;
291 this._header = state.messageHeader + CRLF;
292 this._headerSent = false;
294 // wait until the first body chunk, or close(), is sent to flush,
295 // UNLESS we're sending Expect: 100-continue.
296 if (state.sentExpect) this._send('');
299 function storeHeader(self, state, field, value) {
300 // Protect against response splitting. The if statement is there to
301 // minimize the performance impact in the common case.
302 if (/[\r\n]/.test(value))
303 value = value.replace(/[\r\n]+[ \t]*/g, '');
305 state.messageHeader += field + ': ' + value + CRLF;
307 if (connectionExpression.test(field)) {
308 state.sentConnectionHeader = true;
309 if (closeExpression.test(value)) {
312 self.shouldKeepAlive = true;
315 } else if (transferEncodingExpression.test(field)) {
316 state.sentTransferEncodingHeader = true;
317 if (chunkExpression.test(value)) self.chunkedEncoding = true;
319 } else if (contentLengthExpression.test(field)) {
320 state.sentContentLengthHeader = true;
321 } else if (dateExpression.test(field)) {
322 state.sentDateHeader = true;
323 } else if (expectExpression.test(field)) {
324 state.sentExpect = true;
329 OutgoingMessage.prototype.setHeader = function(name, value) {
330 if (typeof name !== 'string')
331 throw new TypeError('"name" should be a string');
332 if (value === undefined)
333 throw new Error('"name" and "value" are required for setHeader().');
335 throw new Error('Can\'t set headers after they are sent.');
337 if (this._headers === null)
340 var key = name.toLowerCase();
341 this._headers[key] = value;
342 this._headerNames[key] = name;
344 if (automaticHeaders[key])
345 this._removedHeader[key] = false;
349 OutgoingMessage.prototype.getHeader = function(name) {
350 if (arguments.length < 1) {
351 throw new Error('`name` is required for getHeader().');
354 if (!this._headers) return;
356 var key = name.toLowerCase();
357 return this._headers[key];
361 OutgoingMessage.prototype.removeHeader = function(name) {
362 if (arguments.length < 1) {
363 throw new Error('`name` is required for removeHeader().');
367 throw new Error('Can\'t remove headers after they are sent.');
370 var key = name.toLowerCase();
373 this.sendDate = false;
374 else if (automaticHeaders[key])
375 this._removedHeader[key] = true;
378 delete this._headers[key];
379 delete this._headerNames[key];
384 OutgoingMessage.prototype._renderHeaders = function() {
386 throw new Error('Can\'t render headers after they are sent to the client.');
389 if (!this._headers) return {};
392 var keys = Object.keys(this._headers);
394 for (var i = 0, l = keys.length; i < l; i++) {
396 headers[this._headerNames[key]] = this._headers[key];
402 Object.defineProperty(OutgoingMessage.prototype, 'headersSent', {
405 get: function() { return !!this._header; }
409 OutgoingMessage.prototype.write = function(chunk, encoding, callback) {
413 var err = new Error('write after end');
414 process.nextTick(function() {
415 self.emit('error', err);
416 if (callback) callback(err);
423 this._implicitHeader();
426 if (!this._hasBody) {
427 debug('This type of response MUST NOT have a body. ' +
428 'Ignoring write() calls.');
432 if (!util.isString(chunk) && !util.isBuffer(chunk)) {
433 throw new TypeError('first argument must be a string or Buffer');
437 // If we get an empty string or buffer, then just do nothing, and
438 // signal the user to keep writing.
439 if (chunk.length === 0) return true;
442 if (this.chunkedEncoding) {
443 if (util.isString(chunk) &&
444 encoding !== 'hex' &&
445 encoding !== 'base64' &&
446 encoding !== 'binary') {
447 len = Buffer.byteLength(chunk, encoding);
448 chunk = len.toString(16) + CRLF + chunk + CRLF;
449 ret = this._send(chunk, encoding, callback);
451 // buffer, or a non-toString-friendly encoding
452 if (util.isString(chunk))
453 len = Buffer.byteLength(chunk, encoding);
457 if (this.connection && !this.connection.corked) {
458 this.connection.cork();
459 var conn = this.connection;
460 process.nextTick(function connectionCork() {
465 this._send(len.toString(16), 'binary', null);
466 this._send(crlf_buf, null, null);
467 this._send(chunk, encoding, null);
468 ret = this._send(crlf_buf, null, callback);
471 ret = this._send(chunk, encoding, callback);
474 debug('write ret = ' + ret);
479 OutgoingMessage.prototype.addTrailers = function(headers) {
481 var keys = Object.keys(headers);
482 var isArray = util.isArray(headers);
484 for (var i = 0, l = keys.length; i < l; i++) {
487 field = headers[key][0];
488 value = headers[key][1];
491 value = headers[key];
494 this._trailer += field + ': ' + value + CRLF;
499 var crlf_buf = new Buffer('\r\n');
502 OutgoingMessage.prototype.end = function(data, encoding, callback) {
503 if (util.isFunction(data)) {
506 } else if (util.isFunction(encoding)) {
511 if (data && !util.isString(data) && !util.isBuffer(data)) {
512 throw new TypeError('first argument must be a string or Buffer');
524 if (util.isFunction(callback))
525 this.once('finish', callback);
529 this._implicitHeader();
532 if (data && !this._hasBody) {
533 debug('This type of response MUST NOT have a body. ' +
534 'Ignoring data passed to end().');
538 if (this.connection && data)
539 this.connection.cork();
543 // Normal body write.
544 ret = this.write(data, encoding);
547 if (this._hasBody && this.chunkedEncoding) {
548 ret = this._send('0\r\n' + this._trailer + '\r\n', 'binary', finish);
550 // Force a flush, HACK.
551 ret = this._send('', 'binary', finish);
554 if (this.connection && data)
555 this.connection.uncork();
557 this.finished = true;
559 // There is the first message on the outgoing queue, and we've sent
560 // everything to the socket.
561 debug('outgoing message end.');
562 if (this.output.length === 0 && this.connection._httpMessage === this) {
570 OutgoingMessage.prototype._finish = function() {
571 assert(this.connection);
572 this.emit('prefinish');
576 // This logic is probably a bit confusing. Let me explain a bit:
578 // In both HTTP servers and clients it is possible to queue up several
579 // outgoing messages. This is easiest to imagine in the case of a client.
580 // Take the following situation:
582 // req1 = client.request('GET', '/');
583 // req2 = client.request('POST', '/');
585 // When the user does
587 // req2.write('hello world\n');
589 // it's possible that the first request has not been completely flushed to
590 // the socket yet. Thus the outgoing messages need to be prepared to queue
591 // up data internally before sending it on further to the socket's queue.
593 // This function, outgoingFlush(), is called by both the Server and Client
594 // to attempt to flush any pending messages out to the socket.
595 OutgoingMessage.prototype._flush = function() {
596 if (this.socket && this.socket.writable) {
598 while (this.output.length) {
599 var data = this.output.shift();
600 var encoding = this.outputEncodings.shift();
601 var cb = this.outputCallbacks.shift();
602 ret = this.socket.write(data, encoding, cb);
606 // This is a queue to the server or client to bring in the next this.
609 // This is necessary to prevent https from breaking
616 OutgoingMessage.prototype.flush = function() {
618 // Force-flush the headers.
619 this._implicitHeader();