a252c114bf84a37a324e1e5185092fc90a4bf5b8
[platform/upstream/nodejs.git] / lib / _http_incoming.js
1 // Copyright Joyent, Inc. and other Node contributors.
2 //
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:
10 //
11 // The above copyright notice and this permission notice shall be included
12 // in all copies or substantial portions of the Software.
13 //
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.
21
22 'use strict';
23
24 var util = require('util');
25 var Stream = require('stream');
26
27 function readStart(socket) {
28   if (socket && !socket._paused && socket.readable)
29     socket.resume();
30 }
31 exports.readStart = readStart;
32
33 function readStop(socket) {
34   if (socket)
35     socket.pause();
36 }
37 exports.readStop = readStop;
38
39
40 /* Abstract base class for ServerRequest and ClientResponse. */
41 function IncomingMessage(socket) {
42   Stream.Readable.call(this);
43
44   // XXX This implementation is kind of all over the place
45   // When the parser emits body chunks, they go in this list.
46   // _read() pulls them out, and when it finds EOF, it ends.
47
48   this.socket = socket;
49   this.connection = socket;
50
51   this.httpVersionMajor = null;
52   this.httpVersionMinor = null;
53   this.httpVersion = null;
54   this.complete = false;
55   this.headers = {};
56   this.rawHeaders = [];
57   this.trailers = {};
58   this.rawTrailers = [];
59
60   this.readable = true;
61
62   this._pendings = [];
63   this._pendingIndex = 0;
64   this.upgrade = null;
65
66   // request (server) only
67   this.url = '';
68   this.method = null;
69
70   // response (client) only
71   this.statusCode = null;
72   this.statusMessage = null;
73   this.client = this.socket;
74
75   // flag for backwards compatibility grossness.
76   this._consuming = false;
77
78   // flag for when we decide that this message cannot possibly be
79   // read by the user, so there's no point continuing to handle it.
80   this._dumped = false;
81 }
82 util.inherits(IncomingMessage, Stream.Readable);
83
84
85 exports.IncomingMessage = IncomingMessage;
86
87
88 IncomingMessage.prototype.setTimeout = function(msecs, callback) {
89   if (callback)
90     this.on('timeout', callback);
91   this.socket.setTimeout(msecs);
92 };
93
94
95 IncomingMessage.prototype.read = function(n) {
96   this._consuming = true;
97   this.read = Stream.Readable.prototype.read;
98   return this.read(n);
99 };
100
101
102 IncomingMessage.prototype._read = function(n) {
103   // We actually do almost nothing here, because the parserOnBody
104   // function fills up our internal buffer directly.  However, we
105   // do need to unpause the underlying socket so that it flows.
106   if (this.socket.readable)
107     readStart(this.socket);
108 };
109
110
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 IncomingMessage.prototype.destroy = function(error) {
115   if (this.socket)
116     this.socket.destroy(error);
117 };
118
119
120 IncomingMessage.prototype._addHeaderLines = function(headers, n) {
121   if (headers && headers.length) {
122     var raw, dest;
123     if (this.complete) {
124       raw = this.rawTrailers;
125       dest = this.trailers;
126     } else {
127       raw = this.rawHeaders;
128       dest = this.headers;
129     }
130
131     for (var i = 0; i < n; i += 2) {
132       var k = headers[i];
133       var v = headers[i + 1];
134       raw.push(k);
135       raw.push(v);
136       this._addHeaderLine(k, v, dest);
137     }
138   }
139 };
140
141
142 // Add the given (field, value) pair to the message
143 //
144 // Per RFC2616, section 4.2 it is acceptable to join multiple instances of the
145 // same header with a ', ' if the header in question supports specification of
146 // multiple values this way. If not, we declare the first instance the winner
147 // and drop the second. Extended header fields (those beginning with 'x-') are
148 // always joined.
149 IncomingMessage.prototype._addHeaderLine = function(field, value, dest) {
150   field = field.toLowerCase();
151   switch (field) {
152     // Array headers:
153     case 'set-cookie':
154       if (!util.isUndefined(dest[field])) {
155         dest[field].push(value);
156       } else {
157         dest[field] = [value];
158       }
159       break;
160
161     // list is taken from:
162     // https://mxr.mozilla.org/mozilla/source/netwerk/protocol/http/src/nsHttpHeaderArray.cpp
163     case 'content-type':
164     case 'content-length':
165     case 'user-agent':
166     case 'referer':
167     case 'host':
168     case 'authorization':
169     case 'proxy-authorization':
170     case 'if-modified-since':
171     case 'if-unmodified-since':
172     case 'from':
173     case 'location':
174     case 'max-forwards':
175       // drop duplicates
176       if (util.isUndefined(dest[field]))
177         dest[field] = value;
178       break;
179
180     default:
181       // make comma-separated list
182       if (!util.isUndefined(dest[field]))
183         dest[field] += ', ' + value;
184       else {
185         dest[field] = value;
186       }
187   }
188 };
189
190
191 // Call this instead of resume() if we want to just
192 // dump all the data to /dev/null
193 IncomingMessage.prototype._dump = function() {
194   if (!this._dumped) {
195     this._dumped = true;
196     this.resume();
197   }
198 };