doc: improvements to debugger.markdown copy
[platform/upstream/nodejs.git] / lib / _http_common.js
1 'use strict';
2
3 const FreeList = require('internal/freelist').FreeList;
4 const HTTPParser = process.binding('http_parser').HTTPParser;
5
6 const incoming = require('_http_incoming');
7 const IncomingMessage = incoming.IncomingMessage;
8 const readStart = incoming.readStart;
9 const readStop = incoming.readStop;
10
11 const debug = require('util').debuglog('http');
12 exports.debug = debug;
13
14 exports.CRLF = '\r\n';
15 exports.chunkExpression = /chunk/i;
16 exports.continueExpression = /100-continue/i;
17 exports.methods = HTTPParser.methods;
18
19 const kOnHeaders = HTTPParser.kOnHeaders | 0;
20 const kOnHeadersComplete = HTTPParser.kOnHeadersComplete | 0;
21 const kOnBody = HTTPParser.kOnBody | 0;
22 const kOnMessageComplete = HTTPParser.kOnMessageComplete | 0;
23
24 // Only called in the slow case where slow means
25 // that the request headers were either fragmented
26 // across multiple TCP packets or too large to be
27 // processed in a single run. This method is also
28 // called to process trailing HTTP headers.
29 function parserOnHeaders(headers, url) {
30   // Once we exceeded headers limit - stop collecting them
31   if (this.maxHeaderPairs <= 0 ||
32       this._headers.length < this.maxHeaderPairs) {
33     this._headers = this._headers.concat(headers);
34   }
35   this._url += url;
36 }
37
38 // `headers` and `url` are set only if .onHeaders() has not been called for
39 // this request.
40 // `url` is not set for response parsers but that's not applicable here since
41 // all our parsers are request parsers.
42 function parserOnHeadersComplete(versionMajor, versionMinor, headers, method,
43                                  url, statusCode, statusMessage, upgrade,
44                                  shouldKeepAlive) {
45   var parser = this;
46
47   if (!headers) {
48     headers = parser._headers;
49     parser._headers = [];
50   }
51
52   if (!url) {
53     url = parser._url;
54     parser._url = '';
55   }
56
57   parser.incoming = new IncomingMessage(parser.socket);
58   parser.incoming.httpVersionMajor = versionMajor;
59   parser.incoming.httpVersionMinor = versionMinor;
60   parser.incoming.httpVersion = versionMajor + '.' + versionMinor;
61   parser.incoming.url = url;
62
63   var n = headers.length;
64
65   // If parser.maxHeaderPairs <= 0 assume that there's no limit.
66   if (parser.maxHeaderPairs > 0)
67     n = Math.min(n, parser.maxHeaderPairs);
68
69   parser.incoming._addHeaderLines(headers, n);
70
71   if (typeof method === 'number') {
72     // server only
73     parser.incoming.method = HTTPParser.methods[method];
74   } else {
75     // client only
76     parser.incoming.statusCode = statusCode;
77     parser.incoming.statusMessage = statusMessage;
78   }
79
80   parser.incoming.upgrade = upgrade;
81
82   var skipBody = false; // response to HEAD or CONNECT
83
84   if (!upgrade) {
85     // For upgraded connections and CONNECT method request, we'll emit this
86     // after parser.execute so that we can capture the first part of the new
87     // protocol.
88     skipBody = parser.onIncoming(parser.incoming, shouldKeepAlive);
89   }
90
91   return skipBody;
92 }
93
94 // XXX This is a mess.
95 // TODO: http.Parser should be a Writable emits request/response events.
96 function parserOnBody(b, start, len) {
97   var parser = this;
98   var stream = parser.incoming;
99
100   // if the stream has already been removed, then drop it.
101   if (!stream)
102     return;
103
104   var socket = stream.socket;
105
106   // pretend this was the result of a stream._read call.
107   if (len > 0 && !stream._dumped) {
108     var slice = b.slice(start, start + len);
109     var ret = stream.push(slice);
110     if (!ret)
111       readStop(socket);
112   }
113 }
114
115 function parserOnMessageComplete() {
116   var parser = this;
117   var stream = parser.incoming;
118
119   if (stream) {
120     stream.complete = true;
121     // Emit any trailing headers.
122     var headers = parser._headers;
123     if (headers) {
124       parser.incoming._addHeaderLines(headers, headers.length);
125       parser._headers = [];
126       parser._url = '';
127     }
128
129     // For emit end event
130     stream.push(null);
131   }
132
133   // force to read the next incoming message
134   readStart(parser.socket);
135 }
136
137
138 var parsers = new FreeList('parsers', 1000, function() {
139   var parser = new HTTPParser(HTTPParser.REQUEST);
140
141   parser._headers = [];
142   parser._url = '';
143   parser._consumed = false;
144
145   // Only called in the slow case where slow means
146   // that the request headers were either fragmented
147   // across multiple TCP packets or too large to be
148   // processed in a single run. This method is also
149   // called to process trailing HTTP headers.
150   parser[kOnHeaders] = parserOnHeaders;
151   parser[kOnHeadersComplete] = parserOnHeadersComplete;
152   parser[kOnBody] = parserOnBody;
153   parser[kOnMessageComplete] = parserOnMessageComplete;
154
155   return parser;
156 });
157 exports.parsers = parsers;
158
159
160 // Free the parser and also break any links that it
161 // might have to any other things.
162 // TODO: All parser data should be attached to a
163 // single object, so that it can be easily cleaned
164 // up by doing `parser.data = {}`, which should
165 // be done in FreeList.free.  `parsers.free(parser)`
166 // should be all that is needed.
167 function freeParser(parser, req, socket) {
168   if (parser) {
169     parser._headers = [];
170     parser.onIncoming = null;
171     if (parser._consumed)
172       parser.unconsume();
173     parser._consumed = false;
174     if (parser.socket)
175       parser.socket.parser = null;
176     parser.socket = null;
177     parser.incoming = null;
178     if (parsers.free(parser) === false)
179       parser.close();
180     parser = null;
181   }
182   if (req) {
183     req.parser = null;
184   }
185   if (socket) {
186     socket.parser = null;
187   }
188 }
189 exports.freeParser = freeParser;
190
191
192 function ondrain() {
193   if (this._httpMessage) this._httpMessage.emit('drain');
194 }
195
196
197 function httpSocketSetup(socket) {
198   socket.removeListener('drain', ondrain);
199   socket.on('drain', ondrain);
200 }
201 exports.httpSocketSetup = httpSocketSetup;