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 continueExpression = common.continueExpression;
32 var debug = common.debug;
35 var connectionExpression = /Connection/i;
36 var transferEncodingExpression = /Transfer-Encoding/i;
37 var closeExpression = /close/i;
38 var contentLengthExpression = /Content-Length/i;
39 var dateExpression = /Date/i;
40 var expectExpression = /Expect/i;
47 dateCache = d.toUTCString();
48 timers.enroll(utcDate, 1000 - d.getMilliseconds());
49 timers._unrefActive(utcDate);
53 utcDate._onTimeout = function() {
54 dateCache = undefined;
58 function OutgoingMessage() {
62 this.outputEncodings = [];
63 this.outputCallbacks = [];
68 this.chunkedEncoding = false;
69 this.shouldKeepAlive = true;
70 this.useChunkedEncodingByDefault = true;
71 this.sendDate = false;
76 this.finished = false;
77 this._hangupClose = false;
80 this.connection = null;
82 util.inherits(OutgoingMessage, Stream);
85 exports.OutgoingMessage = OutgoingMessage;
88 OutgoingMessage.prototype.setTimeout = function(msecs, callback) {
90 this.on('timeout', callback);
92 this.once('socket', function(socket) {
93 socket.setTimeout(msecs);
96 this.socket.setTimeout(msecs);
100 // It's possible that the socket will be destroyed, and removed from
101 // any messages, before ever calling this. In that case, just skip
102 // it, since something else is destroying this connection anyway.
103 OutgoingMessage.prototype.destroy = function(error) {
105 this.socket.destroy(error);
107 this.once('socket', function(socket) {
108 socket.destroy(error);
113 // This abstract either writing directly to the socket or buffering it.
114 OutgoingMessage.prototype._send = function(data, encoding, callback) {
115 // This is a shameful hack to get the headers and first body chunk onto
116 // the same packet. Future versions of Node are going to take care of
117 // this at a lower level and in a more general way.
118 if (!this._headerSent) {
119 if (util.isString(data) &&
120 encoding !== 'hex' &&
121 encoding !== 'base64') {
122 data = this._header + data;
124 this.output.unshift(this._header);
125 this.outputEncodings.unshift('ascii');
126 this.outputCallbacks.unshift(null);
128 this._headerSent = true;
130 return this._writeRaw(data, encoding, callback);
134 OutgoingMessage.prototype._writeRaw = function(data, encoding, callback) {
135 if (util.isFunction(encoding)) {
140 if (data.length === 0) {
141 if (util.isFunction(callback))
142 process.nextTick(callback);
146 if (this.connection &&
147 this.connection._httpMessage === this &&
148 this.connection.writable &&
149 !this.connection.destroyed) {
150 // There might be pending data in the this.output buffer.
151 while (this.output.length) {
152 if (!this.connection.writable) {
153 this._buffer(data, encoding, callback);
156 var c = this.output.shift();
157 var e = this.outputEncodings.shift();
158 var cb = this.outputCallbacks.shift();
159 this.connection.write(c, e, cb);
162 // Directly write to socket.
163 return this.connection.write(data, encoding, callback);
164 } else if (this.connection && this.connection.destroyed) {
165 // The socket was destroyed. If we're still trying to write to it,
166 // then we haven't gotten the 'close' event yet.
169 // buffer, as long as we're not destroyed.
170 this._buffer(data, encoding, callback);
176 OutgoingMessage.prototype._buffer = function(data, encoding, callback) {
177 this.output.push(data);
178 this.outputEncodings.push(encoding);
179 this.outputCallbacks.push(callback);
184 OutgoingMessage.prototype._storeHeader = function(firstLine, headers) {
185 // firstLine in the case of request is: 'GET /index.html HTTP/1.1\r\n'
186 // in the case of response it is: 'HTTP/1.1 200 OK\r\n'
188 sentConnectionHeader: false,
189 sentContentLengthHeader: false,
190 sentTransferEncodingHeader: false,
191 sentDateHeader: false,
193 messageHeader: firstLine
200 var keys = Object.keys(headers);
201 var isArray = util.isArray(headers);
204 for (var i = 0, l = keys.length; i < l; i++) {
207 field = headers[key][0];
208 value = headers[key][1];
211 value = headers[key];
214 if (util.isArray(value)) {
215 for (var j = 0; j < value.length; j++) {
216 storeHeader(this, state, field, value[j]);
219 storeHeader(this, state, field, value);
225 if (this.sendDate == true && state.sentDateHeader == false) {
226 state.messageHeader += 'Date: ' + utcDate() + CRLF;
229 // Force the connection to close when the response is a 204 No Content or
230 // a 304 Not Modified and the user has set a "Transfer-Encoding: chunked"
233 // RFC 2616 mandates that 204 and 304 responses MUST NOT have a body but
234 // node.js used to send out a zero chunk anyway to accommodate clients
235 // that don't have special handling for those responses.
237 // It was pointed out that this might confuse reverse proxies to the point
238 // of creating security liabilities, so suppress the zero chunk and force
239 // the connection to close.
240 var statusCode = this.statusCode;
241 if ((statusCode == 204 || statusCode === 304) &&
242 this.chunkedEncoding === true) {
243 debug(statusCode + ' response should not use chunked encoding,' +
244 ' closing connection.');
245 this.chunkedEncoding = false;
246 this.shouldKeepAlive = false;
250 if (state.sentConnectionHeader === false) {
251 var shouldSendKeepAlive = this.shouldKeepAlive &&
252 (state.sentContentLengthHeader ||
253 this.useChunkedEncodingByDefault ||
255 if (shouldSendKeepAlive) {
256 state.messageHeader += 'Connection: keep-alive\r\n';
259 state.messageHeader += 'Connection: close\r\n';
263 if (state.sentContentLengthHeader == false &&
264 state.sentTransferEncodingHeader == false) {
266 if (this.useChunkedEncodingByDefault) {
267 state.messageHeader += 'Transfer-Encoding: chunked\r\n';
268 this.chunkedEncoding = true;
273 // Make sure we don't end the 0\r\n\r\n at the end of the message.
274 this.chunkedEncoding = false;
278 this._header = state.messageHeader + CRLF;
279 this._headerSent = false;
281 // wait until the first body chunk, or close(), is sent to flush,
282 // UNLESS we're sending Expect: 100-continue.
283 if (state.sentExpect) this._send('');
286 function storeHeader(self, state, field, value) {
287 // Protect against response splitting. The if statement is there to
288 // minimize the performance impact in the common case.
289 if (/[\r\n]/.test(value))
290 value = value.replace(/[\r\n]+[ \t]*/g, '');
292 state.messageHeader += field + ': ' + value + CRLF;
294 if (connectionExpression.test(field)) {
295 state.sentConnectionHeader = true;
296 if (closeExpression.test(value)) {
299 self.shouldKeepAlive = true;
302 } else if (transferEncodingExpression.test(field)) {
303 state.sentTransferEncodingHeader = true;
304 if (chunkExpression.test(value)) self.chunkedEncoding = true;
306 } else if (contentLengthExpression.test(field)) {
307 state.sentContentLengthHeader = true;
308 } else if (dateExpression.test(field)) {
309 state.sentDateHeader = true;
310 } else if (expectExpression.test(field)) {
311 state.sentExpect = true;
316 OutgoingMessage.prototype.setHeader = function(name, value) {
317 if (arguments.length < 2) {
318 throw new Error('`name` and `value` are required for setHeader().');
322 throw new Error('Can\'t set headers after they are sent.');
325 var key = name.toLowerCase();
326 this._headers = this._headers || {};
327 this._headerNames = this._headerNames || {};
328 this._headers[key] = value;
329 this._headerNames[key] = name;
333 OutgoingMessage.prototype.getHeader = function(name) {
334 if (arguments.length < 1) {
335 throw new Error('`name` is required for getHeader().');
338 if (!this._headers) return;
340 var key = name.toLowerCase();
341 return this._headers[key];
345 OutgoingMessage.prototype.removeHeader = function(name) {
346 if (arguments.length < 1) {
347 throw new Error('`name` is required for removeHeader().');
351 throw new Error('Can\'t remove headers after they are sent.');
354 if (!this._headers) return;
356 var key = name.toLowerCase();
357 delete this._headers[key];
358 delete this._headerNames[key];
362 OutgoingMessage.prototype._renderHeaders = function() {
364 throw new Error('Can\'t render headers after they are sent to the client.');
367 if (!this._headers) return {};
370 var keys = Object.keys(this._headers);
371 for (var i = 0, l = keys.length; i < l; i++) {
373 headers[this._headerNames[key]] = this._headers[key];
379 Object.defineProperty(OutgoingMessage.prototype, 'headersSent', {
382 get: function() { return !!this._header; }
386 OutgoingMessage.prototype.write = function(chunk, encoding, callback) {
388 this._implicitHeader();
391 if (!this._hasBody) {
392 debug('This type of response MUST NOT have a body. ' +
393 'Ignoring write() calls.');
397 if (!util.isString(chunk) && !util.isBuffer(chunk)) {
398 throw new TypeError('first argument must be a string or Buffer');
402 // If we get an empty string or buffer, then just do nothing, and
403 // signal the user to keep writing.
404 if (chunk.length === 0) return true;
407 if (this.chunkedEncoding) {
408 if (util.isString(chunk) &&
409 encoding !== 'hex' &&
410 encoding !== 'base64' &&
411 encoding !== 'binary') {
412 len = Buffer.byteLength(chunk, encoding);
413 chunk = len.toString(16) + CRLF + chunk + CRLF;
414 ret = this._send(chunk, encoding, callback);
416 // buffer, or a non-toString-friendly encoding
417 if (util.isString(chunk))
418 len = Buffer.byteLength(chunk, encoding);
423 this.connection.cork();
424 this._send(len.toString(16), 'ascii', null);
425 this._send(crlf_buf, null, null);
426 this._send(chunk, encoding, null);
427 ret = this._send(crlf_buf, null, callback);
429 this.connection.uncork();
432 ret = this._send(chunk, encoding, callback);
435 debug('write ret = ' + ret);
440 OutgoingMessage.prototype.addTrailers = function(headers) {
442 var keys = Object.keys(headers);
443 var isArray = util.isArray(headers);
445 for (var i = 0, l = keys.length; i < l; i++) {
448 field = headers[key][0];
449 value = headers[key][1];
452 value = headers[key];
455 this._trailer += field + ': ' + value + CRLF;
460 var zero_chunk_buf = new Buffer('\r\n0\r\n');
461 var crlf_buf = new Buffer('\r\n');
464 OutgoingMessage.prototype.end = function(data, encoding, callback) {
465 if (util.isFunction(data)) {
468 } else if (util.isFunction(encoding)) {
473 if (data && !util.isString(data) && !util.isBuffer(data)) {
474 throw new TypeError('first argument must be a string or Buffer');
481 this._implicitHeader();
484 if (data && !this._hasBody) {
485 debug('This type of response MUST NOT have a body. ' +
486 'Ignoring data passed to end().');
490 if (this.connection && data)
491 this.connection.cork();
495 // Normal body write.
496 ret = this.write(data, encoding);
499 if (this.chunkedEncoding) {
500 ret = this._send('0\r\n' + this._trailer + '\r\n', 'ascii', callback);
502 // Force a flush, HACK.
503 ret = this._send('', 'ascii', callback);
506 if (this.connection && data)
507 this.connection.uncork();
509 this.finished = true;
511 // There is the first message on the outgoing queue, and we've sent
512 // everything to the socket.
513 debug('outgoing message end.');
514 if (this.output.length === 0 && this.connection._httpMessage === this) {
522 OutgoingMessage.prototype._finish = function() {
523 assert(this.connection);
528 OutgoingMessage.prototype._flush = function() {
529 // This logic is probably a bit confusing. Let me explain a bit:
531 // In both HTTP servers and clients it is possible to queue up several
532 // outgoing messages. This is easiest to imagine in the case of a client.
533 // Take the following situation:
535 // req1 = client.request('GET', '/');
536 // req2 = client.request('POST', '/');
538 // When the user does
540 // req2.write('hello world\n');
542 // it's possible that the first request has not been completely flushed to
543 // the socket yet. Thus the outgoing messages need to be prepared to queue
544 // up data internally before sending it on further to the socket's queue.
546 // This function, outgoingFlush(), is called by both the Server and Client
547 // to attempt to flush any pending messages out to the socket.
549 if (!this.socket) return;
552 while (this.output.length) {
554 if (!this.socket.writable) return; // XXX Necessary?
556 var data = this.output.shift();
557 var encoding = this.outputEncodings.shift();
558 var cb = this.outputCallbacks.shift();
560 ret = this.socket.write(data, encoding, cb);
564 // This is a queue to the server or client to bring in the next this.
567 // This is necessary to prevent https from breaking