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;
87 this.connection = null;
89 util.inherits(OutgoingMessage, Stream);
92 exports.OutgoingMessage = OutgoingMessage;
95 OutgoingMessage.prototype.setTimeout = function(msecs, callback) {
97 this.on('timeout', callback);
99 this.once('socket', function(socket) {
100 socket.setTimeout(msecs);
103 this.socket.setTimeout(msecs);
107 // It's possible that the socket will be destroyed, and removed from
108 // any messages, before ever calling this. In that case, just skip
109 // it, since something else is destroying this connection anyway.
110 OutgoingMessage.prototype.destroy = function(error) {
112 this.socket.destroy(error);
114 this.once('socket', function(socket) {
115 socket.destroy(error);
120 // This abstract either writing directly to the socket or buffering it.
121 OutgoingMessage.prototype._send = function(data, encoding, callback) {
122 // This is a shameful hack to get the headers and first body chunk onto
123 // the same packet. Future versions of Node are going to take care of
124 // this at a lower level and in a more general way.
125 if (!this._headerSent) {
126 if (util.isString(data) &&
127 encoding !== 'hex' &&
128 encoding !== 'base64') {
129 data = this._header + data;
131 this.output.unshift(this._header);
132 this.outputEncodings.unshift('binary');
133 this.outputCallbacks.unshift(null);
135 this._headerSent = true;
137 return this._writeRaw(data, encoding, callback);
141 OutgoingMessage.prototype._writeRaw = function(data, encoding, callback) {
142 if (util.isFunction(encoding)) {
147 if (data.length === 0) {
148 if (util.isFunction(callback))
149 process.nextTick(callback);
153 if (this.connection &&
154 this.connection._httpMessage === this &&
155 this.connection.writable &&
156 !this.connection.destroyed) {
157 // There might be pending data in the this.output buffer.
158 while (this.output.length) {
159 if (!this.connection.writable) {
160 this._buffer(data, encoding, callback);
163 var c = this.output.shift();
164 var e = this.outputEncodings.shift();
165 var cb = this.outputCallbacks.shift();
166 this.connection.write(c, e, cb);
169 // Directly write to socket.
170 return this.connection.write(data, encoding, callback);
171 } else if (this.connection && this.connection.destroyed) {
172 // The socket was destroyed. If we're still trying to write to it,
173 // then we haven't gotten the 'close' event yet.
176 // buffer, as long as we're not destroyed.
177 this._buffer(data, encoding, callback);
183 OutgoingMessage.prototype._buffer = function(data, encoding, callback) {
184 this.output.push(data);
185 this.outputEncodings.push(encoding);
186 this.outputCallbacks.push(callback);
191 OutgoingMessage.prototype._storeHeader = function(firstLine, headers) {
192 // firstLine in the case of request is: 'GET /index.html HTTP/1.1\r\n'
193 // in the case of response it is: 'HTTP/1.1 200 OK\r\n'
195 sentConnectionHeader: false,
196 sentContentLengthHeader: false,
197 sentTransferEncodingHeader: false,
198 sentDateHeader: false,
200 messageHeader: firstLine
206 var keys = Object.keys(headers);
207 var isArray = util.isArray(headers);
210 for (var i = 0, l = keys.length; i < l; i++) {
213 field = headers[key][0];
214 value = headers[key][1];
217 value = headers[key];
220 if (util.isArray(value)) {
221 for (var j = 0; j < value.length; j++) {
222 storeHeader(this, state, field, value[j]);
225 storeHeader(this, state, field, value);
231 if (this.sendDate == true && state.sentDateHeader == false) {
232 state.messageHeader += 'Date: ' + utcDate() + CRLF;
235 // Force the connection to close when the response is a 204 No Content or
236 // a 304 Not Modified and the user has set a "Transfer-Encoding: chunked"
239 // RFC 2616 mandates that 204 and 304 responses MUST NOT have a body but
240 // node.js used to send out a zero chunk anyway to accommodate clients
241 // that don't have special handling for those responses.
243 // It was pointed out that this might confuse reverse proxies to the point
244 // of creating security liabilities, so suppress the zero chunk and force
245 // the connection to close.
246 var statusCode = this.statusCode;
247 if ((statusCode == 204 || statusCode === 304) &&
248 this.chunkedEncoding === true) {
249 debug(statusCode + ' response should not use chunked encoding,' +
250 ' closing connection.');
251 this.chunkedEncoding = false;
252 this.shouldKeepAlive = false;
256 if (this._removedHeader.connection) {
258 this.shouldKeepAlive = false;
259 } else if (state.sentConnectionHeader === false) {
260 var shouldSendKeepAlive = this.shouldKeepAlive &&
261 (state.sentContentLengthHeader ||
262 this.useChunkedEncodingByDefault ||
264 if (shouldSendKeepAlive) {
265 state.messageHeader += 'Connection: keep-alive\r\n';
268 state.messageHeader += 'Connection: close\r\n';
272 if (state.sentContentLengthHeader == false &&
273 state.sentTransferEncodingHeader == false) {
274 if (this._hasBody && !this._removedHeader['transfer-encoding']) {
275 if (this.useChunkedEncodingByDefault) {
276 state.messageHeader += 'Transfer-Encoding: chunked\r\n';
277 this.chunkedEncoding = true;
282 // Make sure we don't end the 0\r\n\r\n at the end of the message.
283 this.chunkedEncoding = false;
287 this._header = state.messageHeader + CRLF;
288 this._headerSent = false;
290 // wait until the first body chunk, or close(), is sent to flush,
291 // UNLESS we're sending Expect: 100-continue.
292 if (state.sentExpect) this._send('');
295 function storeHeader(self, state, field, value) {
296 // Protect against response splitting. The if statement is there to
297 // minimize the performance impact in the common case.
298 if (/[\r\n]/.test(value))
299 value = value.replace(/[\r\n]+[ \t]*/g, '');
301 state.messageHeader += field + ': ' + value + CRLF;
303 if (connectionExpression.test(field)) {
304 state.sentConnectionHeader = true;
305 if (closeExpression.test(value)) {
308 self.shouldKeepAlive = true;
311 } else if (transferEncodingExpression.test(field)) {
312 state.sentTransferEncodingHeader = true;
313 if (chunkExpression.test(value)) self.chunkedEncoding = true;
315 } else if (contentLengthExpression.test(field)) {
316 state.sentContentLengthHeader = true;
317 } else if (dateExpression.test(field)) {
318 state.sentDateHeader = true;
319 } else if (expectExpression.test(field)) {
320 state.sentExpect = true;
325 OutgoingMessage.prototype.setHeader = function(name, value) {
326 if (arguments.length < 2) {
327 throw new Error('`name` and `value` are required for setHeader().');
331 throw new Error('Can\'t set headers after they are sent.');
334 var key = name.toLowerCase();
335 this._headers = this._headers || {};
336 this._headerNames = this._headerNames || {};
337 this._headers[key] = value;
338 this._headerNames[key] = name;
340 if (automaticHeaders[key]) {
341 this._removedHeader[key] = false;
346 OutgoingMessage.prototype.getHeader = function(name) {
347 if (arguments.length < 1) {
348 throw new Error('`name` is required for getHeader().');
351 if (!this._headers) return;
353 var key = name.toLowerCase();
354 return this._headers[key];
358 OutgoingMessage.prototype.removeHeader = function(name) {
359 if (arguments.length < 1) {
360 throw new Error('`name` is required for removeHeader().');
364 throw new Error('Can\'t remove headers after they are sent.');
367 var key = name.toLowerCase();
370 this.sendDate = false;
371 else if (automaticHeaders[key])
372 this._removedHeader[key] = true;
375 delete this._headers[key];
376 delete this._headerNames[key];
381 OutgoingMessage.prototype._renderHeaders = function() {
383 throw new Error('Can\'t render headers after they are sent to the client.');
386 if (!this._headers) return {};
389 var keys = Object.keys(this._headers);
390 for (var i = 0, l = keys.length; i < l; i++) {
392 headers[this._headerNames[key]] = this._headers[key];
398 Object.defineProperty(OutgoingMessage.prototype, 'headersSent', {
401 get: function() { return !!this._header; }
405 OutgoingMessage.prototype.write = function(chunk, encoding, callback) {
407 this._implicitHeader();
410 if (!this._hasBody) {
411 debug('This type of response MUST NOT have a body. ' +
412 'Ignoring write() calls.');
416 if (!util.isString(chunk) && !util.isBuffer(chunk)) {
417 throw new TypeError('first argument must be a string or Buffer');
421 // If we get an empty string or buffer, then just do nothing, and
422 // signal the user to keep writing.
423 if (chunk.length === 0) return true;
426 if (this.chunkedEncoding) {
427 if (util.isString(chunk) &&
428 encoding !== 'hex' &&
429 encoding !== 'base64' &&
430 encoding !== 'binary') {
431 len = Buffer.byteLength(chunk, encoding);
432 chunk = len.toString(16) + CRLF + chunk + CRLF;
433 ret = this._send(chunk, encoding, callback);
435 // buffer, or a non-toString-friendly encoding
436 if (util.isString(chunk))
437 len = Buffer.byteLength(chunk, encoding);
442 this.connection.cork();
443 this._send(len.toString(16), 'binary', null);
444 this._send(crlf_buf, null, null);
445 this._send(chunk, encoding, null);
446 ret = this._send(crlf_buf, null, callback);
448 this.connection.uncork();
451 ret = this._send(chunk, encoding, callback);
454 debug('write ret = ' + ret);
459 OutgoingMessage.prototype.addTrailers = function(headers) {
461 var keys = Object.keys(headers);
462 var isArray = util.isArray(headers);
464 for (var i = 0, l = keys.length; i < l; i++) {
467 field = headers[key][0];
468 value = headers[key][1];
471 value = headers[key];
474 this._trailer += field + ': ' + value + CRLF;
479 var crlf_buf = new Buffer('\r\n');
482 OutgoingMessage.prototype.end = function(data, encoding, callback) {
483 if (util.isFunction(data)) {
486 } else if (util.isFunction(encoding)) {
491 if (data && !util.isString(data) && !util.isBuffer(data)) {
492 throw new TypeError('first argument must be a string or Buffer');
504 if (util.isFunction(callback))
505 this.once('finish', callback);
509 this._implicitHeader();
512 if (data && !this._hasBody) {
513 debug('This type of response MUST NOT have a body. ' +
514 'Ignoring data passed to end().');
518 if (this.connection && data)
519 this.connection.cork();
523 // Normal body write.
524 ret = this.write(data, encoding);
527 if (this.chunkedEncoding) {
528 ret = this._send('0\r\n' + this._trailer + '\r\n', 'binary', finish);
530 // Force a flush, HACK.
531 ret = this._send('', 'binary', finish);
534 if (this.connection && data)
535 this.connection.uncork();
537 this.finished = true;
539 // There is the first message on the outgoing queue, and we've sent
540 // everything to the socket.
541 debug('outgoing message end.');
542 if (this.output.length === 0 && this.connection._httpMessage === this) {
550 OutgoingMessage.prototype._finish = function() {
551 assert(this.connection);
552 this.emit('prefinish');
556 // This logic is probably a bit confusing. Let me explain a bit:
558 // In both HTTP servers and clients it is possible to queue up several
559 // outgoing messages. This is easiest to imagine in the case of a client.
560 // Take the following situation:
562 // req1 = client.request('GET', '/');
563 // req2 = client.request('POST', '/');
565 // When the user does
567 // req2.write('hello world\n');
569 // it's possible that the first request has not been completely flushed to
570 // the socket yet. Thus the outgoing messages need to be prepared to queue
571 // up data internally before sending it on further to the socket's queue.
573 // This function, outgoingFlush(), is called by both the Server and Client
574 // to attempt to flush any pending messages out to the socket.
575 OutgoingMessage.prototype._flush = function() {
576 if (this.socket && this.socket.writable) {
578 while (this.output.length) {
579 var data = this.output.shift();
580 var encoding = this.outputEncodings.shift();
581 var cb = this.outputCallbacks.shift();
582 ret = this.socket.write(data, encoding, cb);
586 // This is a queue to the server or client to bring in the next this.
589 // This is necessary to prevent https from breaking