3 const assert = require('assert').ok;
4 const Stream = require('stream');
5 const timers = require('timers');
6 const util = require('util');
7 const internalUtil = require('internal/util');
8 const Buffer = require('buffer').Buffer;
9 const common = require('_http_common');
11 const CRLF = common.CRLF;
12 const chunkExpression = common.chunkExpression;
13 const debug = common.debug;
15 const connectionExpression = /^Connection$/i;
16 const transferEncodingExpression = /^Transfer-Encoding$/i;
17 const closeExpression = /close/i;
18 const contentLengthExpression = /^Content-Length$/i;
19 const dateExpression = /^Date$/i;
20 const expectExpression = /^Expect$/i;
21 const trailerExpression = /^Trailer$/i;
23 const automaticHeaders = {
25 'content-length': true,
26 'transfer-encoding': true,
35 dateCache = d.toUTCString();
36 timers.enroll(utcDate, 1000 - d.getMilliseconds());
37 timers._unrefActive(utcDate);
41 utcDate._onTimeout = function() {
42 dateCache = undefined;
46 function OutgoingMessage() {
49 // Queue that holds all currently pending data, until the response will be
50 // assigned to the socket (until it will its turn in the HTTP pipeline).
52 this.outputEncodings = [];
53 this.outputCallbacks = [];
55 // `outputSize` is an approximate measure of how much data is queued on this
56 // response. `_onPendingData` will be invoked to update similar global
57 // per-connection counter. That counter will be used to pause/unpause the
58 // TCP socket and HTTP Parser and thus handle the backpressure.
64 this.chunkedEncoding = false;
65 this.shouldKeepAlive = true;
66 this.useChunkedEncodingByDefault = true;
67 this.sendDate = false;
68 this._removedHeader = {};
70 this._contentLength = null;
74 this.finished = false;
75 this._headerSent = false;
78 this.connection = null;
81 this._headerNames = {};
83 this._onPendingData = null;
85 util.inherits(OutgoingMessage, Stream);
88 exports.OutgoingMessage = OutgoingMessage;
91 OutgoingMessage.prototype.setTimeout = function(msecs, callback) {
93 this.on('timeout', callback);
96 this.once('socket', function(socket) {
97 socket.setTimeout(msecs);
100 this.socket.setTimeout(msecs);
106 // It's possible that the socket will be destroyed, and removed from
107 // any messages, before ever calling this. In that case, just skip
108 // it, since something else is destroying this connection anyway.
109 OutgoingMessage.prototype.destroy = function(error) {
111 this.socket.destroy(error);
113 this.once('socket', function(socket) {
114 socket.destroy(error);
119 // This abstract either writing directly to the socket or buffering it.
120 OutgoingMessage.prototype._send = function(data, encoding, callback) {
121 // This is a shameful hack to get the headers and first body chunk onto
122 // the same packet. Future versions of Node are going to take care of
123 // this at a lower level and in a more general way.
124 if (!this._headerSent) {
125 if (typeof data === 'string' &&
126 encoding !== 'hex' &&
127 encoding !== 'base64') {
128 data = this._header + data;
130 this.output.unshift(this._header);
131 this.outputEncodings.unshift('binary');
132 this.outputCallbacks.unshift(null);
133 this.outputSize += this._header.length;
134 if (typeof this._onPendingData === 'function')
135 this._onPendingData(this._header.length);
137 this._headerSent = true;
139 return this._writeRaw(data, encoding, callback);
143 OutgoingMessage.prototype._writeRaw = function(data, encoding, callback) {
144 if (typeof encoding === 'function') {
149 var connection = this.connection;
151 connection._httpMessage === this &&
152 connection.writable &&
153 !connection.destroyed) {
154 // There might be pending data in the this.output buffer.
155 var outputLength = this.output.length;
156 if (outputLength > 0) {
157 this._flushOutput(connection);
158 } else if (data.length === 0) {
159 if (typeof callback === 'function')
160 process.nextTick(callback);
164 // Directly write to socket.
165 return connection.write(data, encoding, callback);
166 } else if (connection && connection.destroyed) {
167 // The socket was destroyed. If we're still trying to write to it,
168 // then we haven't gotten the 'close' event yet.
171 // buffer, as long as we're not destroyed.
172 return this._buffer(data, encoding, callback);
177 OutgoingMessage.prototype._buffer = function(data, encoding, callback) {
178 this.output.push(data);
179 this.outputEncodings.push(encoding);
180 this.outputCallbacks.push(callback);
181 this.outputSize += data.length;
182 if (typeof this._onPendingData === 'function')
183 this._onPendingData(data.length);
188 OutgoingMessage.prototype._storeHeader = function(firstLine, headers) {
189 // firstLine in the case of request is: 'GET /index.html HTTP/1.1\r\n'
190 // in the case of response it is: 'HTTP/1.1 200 OK\r\n'
192 sentConnectionHeader: false,
193 sentContentLengthHeader: false,
194 sentTransferEncodingHeader: false,
195 sentDateHeader: false,
198 messageHeader: firstLine
202 var keys = Object.keys(headers);
203 var isArray = Array.isArray(headers);
206 for (var i = 0, l = keys.length; i < l; i++) {
209 field = headers[key][0];
210 value = headers[key][1];
213 value = headers[key];
216 if (Array.isArray(value)) {
217 for (var j = 0; j < value.length; j++) {
218 storeHeader(this, state, field, value[j]);
221 storeHeader(this, state, field, value);
227 if (this.sendDate === true && state.sentDateHeader === false) {
228 state.messageHeader += 'Date: ' + utcDate() + CRLF;
231 // Force the connection to close when the response is a 204 No Content or
232 // a 304 Not Modified and the user has set a "Transfer-Encoding: chunked"
235 // RFC 2616 mandates that 204 and 304 responses MUST NOT have a body but
236 // node.js used to send out a zero chunk anyway to accommodate clients
237 // that don't have special handling for those responses.
239 // It was pointed out that this might confuse reverse proxies to the point
240 // of creating security liabilities, so suppress the zero chunk and force
241 // the connection to close.
242 var statusCode = this.statusCode;
243 if ((statusCode === 204 || statusCode === 304) &&
244 this.chunkedEncoding === true) {
245 debug(statusCode + ' response should not use chunked encoding,' +
246 ' closing connection.');
247 this.chunkedEncoding = false;
248 this.shouldKeepAlive = false;
252 if (this._removedHeader.connection) {
254 this.shouldKeepAlive = false;
255 } else if (state.sentConnectionHeader === false) {
256 var shouldSendKeepAlive = this.shouldKeepAlive &&
257 (state.sentContentLengthHeader ||
258 this.useChunkedEncodingByDefault ||
260 if (shouldSendKeepAlive) {
261 state.messageHeader += 'Connection: keep-alive\r\n';
264 state.messageHeader += 'Connection: close\r\n';
268 if (state.sentContentLengthHeader === false &&
269 state.sentTransferEncodingHeader === false) {
270 if (!this._hasBody) {
271 // Make sure we don't end the 0\r\n\r\n at the end of the message.
272 this.chunkedEncoding = false;
273 } else if (!this.useChunkedEncodingByDefault) {
276 if (!state.sentTrailer &&
277 !this._removedHeader['content-length'] &&
278 typeof this._contentLength === 'number') {
279 state.messageHeader += 'Content-Length: ' + this._contentLength +
281 } else if (!this._removedHeader['transfer-encoding']) {
282 state.messageHeader += 'Transfer-Encoding: chunked\r\n';
283 this.chunkedEncoding = true;
285 // We should only be able to get here if both Content-Length and
286 // Transfer-Encoding are removed by the user.
287 // See: test/parallel/test-http-remove-header-stays-removed.js
288 debug('Both Content-Length and Transfer-Encoding are removed');
293 this._header = state.messageHeader + CRLF;
294 this._headerSent = false;
296 // wait until the first body chunk, or close(), is sent to flush,
297 // UNLESS we're sending Expect: 100-continue.
298 if (state.sentExpect) this._send('');
301 function storeHeader(self, state, field, value) {
302 value = escapeHeaderValue(value);
303 state.messageHeader += field + ': ' + value + CRLF;
305 if (connectionExpression.test(field)) {
306 state.sentConnectionHeader = true;
307 if (closeExpression.test(value)) {
310 self.shouldKeepAlive = true;
313 } else if (transferEncodingExpression.test(field)) {
314 state.sentTransferEncodingHeader = true;
315 if (chunkExpression.test(value)) self.chunkedEncoding = true;
317 } else if (contentLengthExpression.test(field)) {
318 state.sentContentLengthHeader = true;
319 } else if (dateExpression.test(field)) {
320 state.sentDateHeader = true;
321 } else if (expectExpression.test(field)) {
322 state.sentExpect = true;
323 } else if (trailerExpression.test(field)) {
324 state.sentTrailer = true;
329 OutgoingMessage.prototype.setHeader = function(name, value) {
330 if (typeof name !== 'string')
331 throw new TypeError('`name` should be a string in setHeader(name, value).');
332 if (value === undefined)
333 throw new Error('`value` required in setHeader("' + name + '", value).');
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(name).');
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(name).');
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 var headersMap = this._headers;
390 if (!headersMap) return {};
393 var keys = Object.keys(headersMap);
394 var headerNames = this._headerNames;
396 for (var i = 0, l = keys.length; i < l; i++) {
398 headers[headerNames[key]] = headersMap[key];
404 Object.defineProperty(OutgoingMessage.prototype, 'headersSent', {
407 get: function() { return !!this._header; }
411 OutgoingMessage.prototype.write = function(chunk, encoding, callback) {
413 var err = new Error('write after end');
414 process.nextTick(writeAfterEndNT, this, err, callback);
420 this._implicitHeader();
423 if (!this._hasBody) {
424 debug('This type of response MUST NOT have a body. ' +
425 'Ignoring write() calls.');
429 if (typeof chunk !== 'string' && !(chunk instanceof Buffer)) {
430 throw new TypeError('first argument must be a string or Buffer');
434 // If we get an empty string or buffer, then just do nothing, and
435 // signal the user to keep writing.
436 if (chunk.length === 0) return true;
439 if (this.chunkedEncoding) {
440 if (typeof chunk === 'string' &&
441 encoding !== 'hex' &&
442 encoding !== 'base64' &&
443 encoding !== 'binary') {
444 len = Buffer.byteLength(chunk, encoding);
445 chunk = len.toString(16) + CRLF + chunk + CRLF;
446 ret = this._send(chunk, encoding, callback);
448 // buffer, or a non-toString-friendly encoding
449 if (typeof chunk === 'string')
450 len = Buffer.byteLength(chunk, encoding);
454 if (this.connection && !this.connection.corked) {
455 this.connection.cork();
456 process.nextTick(connectionCorkNT, this.connection);
458 this._send(len.toString(16), 'binary', null);
459 this._send(crlf_buf, null, null);
460 this._send(chunk, encoding, null);
461 ret = this._send(crlf_buf, null, callback);
464 ret = this._send(chunk, encoding, callback);
467 debug('write ret = ' + ret);
472 function writeAfterEndNT(self, err, callback) {
473 self.emit('error', err);
474 if (callback) callback(err);
478 function connectionCorkNT(conn) {
484 function escapeHeaderValue(value) {
485 // Protect against response splitting. The regex test is there to
486 // minimize the performance impact in the common case.
487 return /[\r\n]/.test(value) ? value.replace(/[\r\n]+[ \t]*/g, '') : value;
491 OutgoingMessage.prototype.addTrailers = function(headers) {
493 var keys = Object.keys(headers);
494 var isArray = Array.isArray(headers);
496 for (var i = 0, l = keys.length; i < l; i++) {
499 field = headers[key][0];
500 value = headers[key][1];
503 value = headers[key];
506 this._trailer += field + ': ' + escapeHeaderValue(value) + CRLF;
511 const crlf_buf = new Buffer('\r\n');
514 OutgoingMessage.prototype.end = function(data, encoding, callback) {
515 if (typeof data === 'function') {
518 } else if (typeof encoding === 'function') {
523 if (data && typeof data !== 'string' && !(data instanceof Buffer)) {
524 throw new TypeError('first argument must be a string or Buffer');
536 if (typeof callback === 'function')
537 this.once('finish', callback);
541 if (typeof data === 'string')
542 this._contentLength = Buffer.byteLength(data, encoding);
544 this._contentLength = data.length;
546 this._contentLength = 0;
548 this._implicitHeader();
551 if (data && !this._hasBody) {
552 debug('This type of response MUST NOT have a body. ' +
553 'Ignoring data passed to end().');
557 if (this.connection && data)
558 this.connection.cork();
562 // Normal body write.
563 ret = this.write(data, encoding);
566 if (this._hasBody && this.chunkedEncoding) {
567 ret = this._send('0\r\n' + this._trailer + '\r\n', 'binary', finish);
569 // Force a flush, HACK.
570 ret = this._send('', 'binary', finish);
573 if (this.connection && data)
574 this.connection.uncork();
576 this.finished = true;
578 // There is the first message on the outgoing queue, and we've sent
579 // everything to the socket.
580 debug('outgoing message end.');
581 if (this.output.length === 0 &&
583 this.connection._httpMessage === this) {
591 OutgoingMessage.prototype._finish = function() {
592 assert(this.connection);
593 this.emit('prefinish');
597 // This logic is probably a bit confusing. Let me explain a bit:
599 // In both HTTP servers and clients it is possible to queue up several
600 // outgoing messages. This is easiest to imagine in the case of a client.
601 // Take the following situation:
603 // req1 = client.request('GET', '/');
604 // req2 = client.request('POST', '/');
606 // When the user does
608 // req2.write('hello world\n');
610 // it's possible that the first request has not been completely flushed to
611 // the socket yet. Thus the outgoing messages need to be prepared to queue
612 // up data internally before sending it on further to the socket's queue.
614 // This function, outgoingFlush(), is called by both the Server and Client
615 // to attempt to flush any pending messages out to the socket.
616 OutgoingMessage.prototype._flush = function() {
617 var socket = this.socket;
620 if (socket && socket.writable) {
621 // There might be remaining data in this.output; write it out
622 ret = this._flushOutput(socket);
625 // This is a queue to the server or client to bring in the next this.
628 // This is necessary to prevent https from breaking
634 OutgoingMessage.prototype._flushOutput = function _flushOutput(socket) {
636 var outputLength = this.output.length;
637 if (outputLength <= 0)
640 var output = this.output;
641 var outputEncodings = this.outputEncodings;
642 var outputCallbacks = this.outputCallbacks;
644 for (var i = 0; i < outputLength; i++) {
645 ret = socket.write(output[i], outputEncodings[i],
651 this.outputEncodings = [];
652 this.outputCallbacks = [];
653 if (typeof this._onPendingData === 'function')
654 this._onPendingData(-this.outputSize);
661 OutgoingMessage.prototype.flushHeaders = function() {
663 this._implicitHeader();
666 // Force-flush the headers.
670 OutgoingMessage.prototype.flush = internalUtil.deprecate(function() {
672 }, 'OutgoingMessage.flush is deprecated. Use flushHeaders instead.');