macgyver 0.0.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/README.md +45 -0
- data/Rakefile +1 -0
- data/assets/MacGap.app/Contents/Frameworks/Growl.framework/Growl +0 -0
- data/assets/MacGap.app/Contents/Frameworks/Growl.framework/Versions/A/Growl +0 -0
- data/assets/MacGap.app/Contents/Frameworks/Growl.framework/Versions/A/Headers/Growl.h +5 -0
- data/assets/MacGap.app/Contents/Frameworks/Growl.framework/Versions/A/Headers/GrowlApplicationBridge.h +551 -0
- data/assets/MacGap.app/Contents/Frameworks/Growl.framework/Versions/A/Headers/GrowlDefines.h +341 -0
- data/assets/MacGap.app/Contents/Frameworks/Growl.framework/Versions/A/Resources/Info.plist +40 -0
- data/assets/MacGap.app/Contents/Frameworks/Growl.framework/Versions/A/_CodeSignature/CodeResources +34 -0
- data/assets/MacGap.app/Contents/Info.plist +48 -0
- data/assets/MacGap.app/Contents/MacOS/MacGap +0 -0
- data/assets/MacGap.app/Contents/PkgInfo +1 -0
- data/assets/MacGap.app/Contents/Resources/_debugger.js +1718 -0
- data/assets/MacGap.app/Contents/Resources/_http_agent.js +310 -0
- data/assets/MacGap.app/Contents/Resources/_http_client.js +533 -0
- data/assets/MacGap.app/Contents/Resources/_http_common.js +222 -0
- data/assets/MacGap.app/Contents/Resources/_http_incoming.js +194 -0
- data/assets/MacGap.app/Contents/Resources/_http_outgoing.js +597 -0
- data/assets/MacGap.app/Contents/Resources/_http_server.js +510 -0
- data/assets/MacGap.app/Contents/Resources/_linklist.js +76 -0
- data/assets/MacGap.app/Contents/Resources/_stream_duplex.js +69 -0
- data/assets/MacGap.app/Contents/Resources/_stream_passthrough.js +41 -0
- data/assets/MacGap.app/Contents/Resources/_stream_readable.js +900 -0
- data/assets/MacGap.app/Contents/Resources/_stream_transform.js +204 -0
- data/assets/MacGap.app/Contents/Resources/_stream_writable.js +456 -0
- data/assets/MacGap.app/Contents/Resources/_tls_legacy.js +887 -0
- data/assets/MacGap.app/Contents/Resources/_tls_wrap.js +831 -0
- data/assets/MacGap.app/Contents/Resources/application.icns +0 -0
- data/assets/MacGap.app/Contents/Resources/assert.js +326 -0
- data/assets/MacGap.app/Contents/Resources/buffer.js +724 -0
- data/assets/MacGap.app/Contents/Resources/child_process.js +1107 -0
- data/assets/MacGap.app/Contents/Resources/cluster.js +613 -0
- data/assets/MacGap.app/Contents/Resources/console.js +108 -0
- data/assets/MacGap.app/Contents/Resources/constants.js +22 -0
- data/assets/MacGap.app/Contents/Resources/crypto.js +691 -0
- data/assets/MacGap.app/Contents/Resources/dgram.js +459 -0
- data/assets/MacGap.app/Contents/Resources/dns.js +274 -0
- data/assets/MacGap.app/Contents/Resources/domain.js +292 -0
- data/assets/MacGap.app/Contents/Resources/en.lproj/Credits.rtf +29 -0
- data/assets/MacGap.app/Contents/Resources/en.lproj/InfoPlist.strings +0 -0
- data/assets/MacGap.app/Contents/Resources/en.lproj/MainMenu.nib +0 -0
- data/assets/MacGap.app/Contents/Resources/en.lproj/Window.nib +0 -0
- data/assets/MacGap.app/Contents/Resources/events.js +312 -0
- data/assets/MacGap.app/Contents/Resources/freelist.js +43 -0
- data/assets/MacGap.app/Contents/Resources/fs.js +1732 -0
- data/assets/MacGap.app/Contents/Resources/http.js +119 -0
- data/assets/MacGap.app/Contents/Resources/https.js +134 -0
- data/assets/MacGap.app/Contents/Resources/module.js +529 -0
- data/assets/MacGap.app/Contents/Resources/net.js +1378 -0
- data/assets/MacGap.app/Contents/Resources/nodelike.js +195 -0
- data/assets/MacGap.app/Contents/Resources/os.js +64 -0
- data/assets/MacGap.app/Contents/Resources/path.js +517 -0
- data/assets/MacGap.app/Contents/Resources/public/index.html +38 -0
- data/assets/MacGap.app/Contents/Resources/punycode.js +507 -0
- data/assets/MacGap.app/Contents/Resources/querystring.js +206 -0
- data/assets/MacGap.app/Contents/Resources/readline.js +1311 -0
- data/assets/MacGap.app/Contents/Resources/repl.js +945 -0
- data/assets/MacGap.app/Contents/Resources/smalloc.js +90 -0
- data/assets/MacGap.app/Contents/Resources/stream.js +127 -0
- data/assets/MacGap.app/Contents/Resources/string_decoder.js +189 -0
- data/assets/MacGap.app/Contents/Resources/sys.js +24 -0
- data/assets/MacGap.app/Contents/Resources/timers.js +568 -0
- data/assets/MacGap.app/Contents/Resources/tls.js +220 -0
- data/assets/MacGap.app/Contents/Resources/tty.js +129 -0
- data/assets/MacGap.app/Contents/Resources/url.js +693 -0
- data/assets/MacGap.app/Contents/Resources/util.js +688 -0
- data/assets/MacGap.app/Contents/Resources/vm.js +73 -0
- data/assets/MacGap.app/Contents/Resources/zlib.js +524 -0
- data/assets/index.html +38 -0
- data/bin/macgyver +104 -0
- data/macgyver.gemspec +19 -0
- data/test/public/index.html +27 -0
- metadata +121 -0
@@ -0,0 +1,510 @@
|
|
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
|
+
var util = require('util');
|
23
|
+
var net = require('net');
|
24
|
+
var EventEmitter = require('events').EventEmitter;
|
25
|
+
var HTTPParser = process.binding('http_parser').HTTPParser;
|
26
|
+
var assert = require('assert').ok;
|
27
|
+
|
28
|
+
var common = require('_http_common');
|
29
|
+
var parsers = common.parsers;
|
30
|
+
var freeParser = common.freeParser;
|
31
|
+
var debug = common.debug;
|
32
|
+
var CRLF = common.CRLF;
|
33
|
+
var continueExpression = common.continueExpression;
|
34
|
+
var chunkExpression = common.chunkExpression;
|
35
|
+
var httpSocketSetup = common.httpSocketSetup;
|
36
|
+
|
37
|
+
var OutgoingMessage = require('_http_outgoing').OutgoingMessage;
|
38
|
+
|
39
|
+
|
40
|
+
var STATUS_CODES = exports.STATUS_CODES = {
|
41
|
+
100 : 'Continue',
|
42
|
+
101 : 'Switching Protocols',
|
43
|
+
102 : 'Processing', // RFC 2518, obsoleted by RFC 4918
|
44
|
+
200 : 'OK',
|
45
|
+
201 : 'Created',
|
46
|
+
202 : 'Accepted',
|
47
|
+
203 : 'Non-Authoritative Information',
|
48
|
+
204 : 'No Content',
|
49
|
+
205 : 'Reset Content',
|
50
|
+
206 : 'Partial Content',
|
51
|
+
207 : 'Multi-Status', // RFC 4918
|
52
|
+
300 : 'Multiple Choices',
|
53
|
+
301 : 'Moved Permanently',
|
54
|
+
302 : 'Moved Temporarily',
|
55
|
+
303 : 'See Other',
|
56
|
+
304 : 'Not Modified',
|
57
|
+
305 : 'Use Proxy',
|
58
|
+
307 : 'Temporary Redirect',
|
59
|
+
400 : 'Bad Request',
|
60
|
+
401 : 'Unauthorized',
|
61
|
+
402 : 'Payment Required',
|
62
|
+
403 : 'Forbidden',
|
63
|
+
404 : 'Not Found',
|
64
|
+
405 : 'Method Not Allowed',
|
65
|
+
406 : 'Not Acceptable',
|
66
|
+
407 : 'Proxy Authentication Required',
|
67
|
+
408 : 'Request Time-out',
|
68
|
+
409 : 'Conflict',
|
69
|
+
410 : 'Gone',
|
70
|
+
411 : 'Length Required',
|
71
|
+
412 : 'Precondition Failed',
|
72
|
+
413 : 'Request Entity Too Large',
|
73
|
+
414 : 'Request-URI Too Large',
|
74
|
+
415 : 'Unsupported Media Type',
|
75
|
+
416 : 'Requested Range Not Satisfiable',
|
76
|
+
417 : 'Expectation Failed',
|
77
|
+
418 : 'I\'m a teapot', // RFC 2324
|
78
|
+
422 : 'Unprocessable Entity', // RFC 4918
|
79
|
+
423 : 'Locked', // RFC 4918
|
80
|
+
424 : 'Failed Dependency', // RFC 4918
|
81
|
+
425 : 'Unordered Collection', // RFC 4918
|
82
|
+
426 : 'Upgrade Required', // RFC 2817
|
83
|
+
428 : 'Precondition Required', // RFC 6585
|
84
|
+
429 : 'Too Many Requests', // RFC 6585
|
85
|
+
431 : 'Request Header Fields Too Large',// RFC 6585
|
86
|
+
500 : 'Internal Server Error',
|
87
|
+
501 : 'Not Implemented',
|
88
|
+
502 : 'Bad Gateway',
|
89
|
+
503 : 'Service Unavailable',
|
90
|
+
504 : 'Gateway Time-out',
|
91
|
+
505 : 'HTTP Version Not Supported',
|
92
|
+
506 : 'Variant Also Negotiates', // RFC 2295
|
93
|
+
507 : 'Insufficient Storage', // RFC 4918
|
94
|
+
509 : 'Bandwidth Limit Exceeded',
|
95
|
+
510 : 'Not Extended', // RFC 2774
|
96
|
+
511 : 'Network Authentication Required' // RFC 6585
|
97
|
+
};
|
98
|
+
|
99
|
+
|
100
|
+
function ServerResponse(req) {
|
101
|
+
OutgoingMessage.call(this);
|
102
|
+
|
103
|
+
if (req.method === 'HEAD') this._hasBody = false;
|
104
|
+
|
105
|
+
this.sendDate = true;
|
106
|
+
|
107
|
+
if (req.httpVersionMajor < 1 || req.httpVersionMinor < 1) {
|
108
|
+
this.useChunkedEncodingByDefault = chunkExpression.test(req.headers.te);
|
109
|
+
this.shouldKeepAlive = false;
|
110
|
+
}
|
111
|
+
}
|
112
|
+
util.inherits(ServerResponse, OutgoingMessage);
|
113
|
+
|
114
|
+
ServerResponse.prototype._finish = function() {
|
115
|
+
DTRACE_HTTP_SERVER_RESPONSE(this.connection);
|
116
|
+
COUNTER_HTTP_SERVER_RESPONSE();
|
117
|
+
OutgoingMessage.prototype._finish.call(this);
|
118
|
+
};
|
119
|
+
|
120
|
+
|
121
|
+
|
122
|
+
exports.ServerResponse = ServerResponse;
|
123
|
+
|
124
|
+
ServerResponse.prototype.statusCode = 200;
|
125
|
+
ServerResponse.prototype.statusMessage = undefined;
|
126
|
+
|
127
|
+
function onServerResponseClose() {
|
128
|
+
// EventEmitter.emit makes a copy of the 'close' listeners array before
|
129
|
+
// calling the listeners. detachSocket() unregisters onServerResponseClose
|
130
|
+
// but if detachSocket() is called, directly or indirectly, by a 'close'
|
131
|
+
// listener, onServerResponseClose is still in that copy of the listeners
|
132
|
+
// array. That is, in the example below, b still gets called even though
|
133
|
+
// it's been removed by a:
|
134
|
+
//
|
135
|
+
// var obj = new events.EventEmitter;
|
136
|
+
// obj.on('event', a);
|
137
|
+
// obj.on('event', b);
|
138
|
+
// function a() { obj.removeListener('event', b) }
|
139
|
+
// function b() { throw "BAM!" }
|
140
|
+
// obj.emit('event'); // throws
|
141
|
+
//
|
142
|
+
// Ergo, we need to deal with stale 'close' events and handle the case
|
143
|
+
// where the ServerResponse object has already been deconstructed.
|
144
|
+
// Fortunately, that requires only a single if check. :-)
|
145
|
+
if (this._httpMessage) this._httpMessage.emit('close');
|
146
|
+
}
|
147
|
+
|
148
|
+
ServerResponse.prototype.assignSocket = function(socket) {
|
149
|
+
assert(!socket._httpMessage);
|
150
|
+
socket._httpMessage = this;
|
151
|
+
socket.on('close', onServerResponseClose);
|
152
|
+
this.socket = socket;
|
153
|
+
this.connection = socket;
|
154
|
+
this.emit('socket', socket);
|
155
|
+
this._flush();
|
156
|
+
};
|
157
|
+
|
158
|
+
ServerResponse.prototype.detachSocket = function(socket) {
|
159
|
+
assert(socket._httpMessage == this);
|
160
|
+
socket.removeListener('close', onServerResponseClose);
|
161
|
+
socket._httpMessage = null;
|
162
|
+
this.socket = this.connection = null;
|
163
|
+
};
|
164
|
+
|
165
|
+
ServerResponse.prototype.writeContinue = function(cb) {
|
166
|
+
this._writeRaw('HTTP/1.1 100 Continue' + CRLF + CRLF, 'ascii', cb);
|
167
|
+
this._sent100 = true;
|
168
|
+
};
|
169
|
+
|
170
|
+
ServerResponse.prototype._implicitHeader = function() {
|
171
|
+
this.writeHead(this.statusCode);
|
172
|
+
};
|
173
|
+
|
174
|
+
ServerResponse.prototype.writeHead = function(statusCode) {
|
175
|
+
var headers, headerIndex;
|
176
|
+
|
177
|
+
if (util.isString(arguments[1])) {
|
178
|
+
this.statusMessage = arguments[1];
|
179
|
+
headerIndex = 2;
|
180
|
+
} else {
|
181
|
+
this.statusMessage =
|
182
|
+
this.statusMessage || STATUS_CODES[statusCode] || 'unknown';
|
183
|
+
headerIndex = 1;
|
184
|
+
}
|
185
|
+
this.statusCode = statusCode;
|
186
|
+
|
187
|
+
var obj = arguments[headerIndex];
|
188
|
+
|
189
|
+
if (obj && this._headers) {
|
190
|
+
// Slow-case: when progressive API and header fields are passed.
|
191
|
+
headers = this._renderHeaders();
|
192
|
+
|
193
|
+
if (util.isArray(obj)) {
|
194
|
+
// handle array case
|
195
|
+
// TODO: remove when array is no longer accepted
|
196
|
+
var field;
|
197
|
+
for (var i = 0, len = obj.length; i < len; ++i) {
|
198
|
+
field = obj[i][0];
|
199
|
+
if (!util.isUndefined(headers[field])) {
|
200
|
+
obj.push([field, headers[field]]);
|
201
|
+
}
|
202
|
+
}
|
203
|
+
headers = obj;
|
204
|
+
|
205
|
+
} else {
|
206
|
+
// handle object case
|
207
|
+
var keys = Object.keys(obj);
|
208
|
+
for (var i = 0; i < keys.length; i++) {
|
209
|
+
var k = keys[i];
|
210
|
+
if (k) headers[k] = obj[k];
|
211
|
+
}
|
212
|
+
}
|
213
|
+
} else if (this._headers) {
|
214
|
+
// only progressive api is used
|
215
|
+
headers = this._renderHeaders();
|
216
|
+
} else {
|
217
|
+
// only writeHead() called
|
218
|
+
headers = obj;
|
219
|
+
}
|
220
|
+
|
221
|
+
var statusLine = 'HTTP/1.1 ' + statusCode.toString() + ' ' +
|
222
|
+
this.statusMessage + CRLF;
|
223
|
+
|
224
|
+
if (statusCode === 204 || statusCode === 304 ||
|
225
|
+
(100 <= statusCode && statusCode <= 199)) {
|
226
|
+
// RFC 2616, 10.2.5:
|
227
|
+
// The 204 response MUST NOT include a message-body, and thus is always
|
228
|
+
// terminated by the first empty line after the header fields.
|
229
|
+
// RFC 2616, 10.3.5:
|
230
|
+
// The 304 response MUST NOT contain a message-body, and thus is always
|
231
|
+
// terminated by the first empty line after the header fields.
|
232
|
+
// RFC 2616, 10.1 Informational 1xx:
|
233
|
+
// This class of status code indicates a provisional response,
|
234
|
+
// consisting only of the Status-Line and optional headers, and is
|
235
|
+
// terminated by an empty line.
|
236
|
+
this._hasBody = false;
|
237
|
+
}
|
238
|
+
|
239
|
+
// don't keep alive connections where the client expects 100 Continue
|
240
|
+
// but we sent a final status; they may put extra bytes on the wire.
|
241
|
+
if (this._expect_continue && !this._sent100) {
|
242
|
+
this.shouldKeepAlive = false;
|
243
|
+
}
|
244
|
+
|
245
|
+
this._storeHeader(statusLine, headers);
|
246
|
+
};
|
247
|
+
|
248
|
+
ServerResponse.prototype.writeHeader = function() {
|
249
|
+
this.writeHead.apply(this, arguments);
|
250
|
+
};
|
251
|
+
|
252
|
+
|
253
|
+
function Server(requestListener) {
|
254
|
+
if (!(this instanceof Server)) return new Server(requestListener);
|
255
|
+
net.Server.call(this, { allowHalfOpen: true });
|
256
|
+
|
257
|
+
if (requestListener) {
|
258
|
+
this.addListener('request', requestListener);
|
259
|
+
}
|
260
|
+
|
261
|
+
// Similar option to this. Too lazy to write my own docs.
|
262
|
+
// http://www.squid-cache.org/Doc/config/half_closed_clients/
|
263
|
+
// http://wiki.squid-cache.org/SquidFaq/InnerWorkings#What_is_a_half-closed_filedescriptor.3F
|
264
|
+
this.httpAllowHalfOpen = false;
|
265
|
+
|
266
|
+
this.addListener('connection', connectionListener);
|
267
|
+
|
268
|
+
this.addListener('clientError', function(err, conn) {
|
269
|
+
conn.destroy(err);
|
270
|
+
});
|
271
|
+
|
272
|
+
this.timeout = 2 * 60 * 1000;
|
273
|
+
}
|
274
|
+
util.inherits(Server, net.Server);
|
275
|
+
|
276
|
+
|
277
|
+
Server.prototype.setTimeout = function(msecs, callback) {
|
278
|
+
this.timeout = msecs;
|
279
|
+
if (callback)
|
280
|
+
this.on('timeout', callback);
|
281
|
+
};
|
282
|
+
|
283
|
+
|
284
|
+
exports.Server = Server;
|
285
|
+
|
286
|
+
|
287
|
+
function connectionListener(socket) {
|
288
|
+
var self = this;
|
289
|
+
var outgoing = [];
|
290
|
+
var incoming = [];
|
291
|
+
|
292
|
+
function abortIncoming() {
|
293
|
+
while (incoming.length) {
|
294
|
+
var req = incoming.shift();
|
295
|
+
req.emit('aborted');
|
296
|
+
req.emit('close');
|
297
|
+
}
|
298
|
+
// abort socket._httpMessage ?
|
299
|
+
}
|
300
|
+
|
301
|
+
function serverSocketCloseListener() {
|
302
|
+
debug('server socket close');
|
303
|
+
// mark this parser as reusable
|
304
|
+
if (this.parser)
|
305
|
+
freeParser(this.parser);
|
306
|
+
|
307
|
+
abortIncoming();
|
308
|
+
}
|
309
|
+
|
310
|
+
debug('SERVER new http connection');
|
311
|
+
|
312
|
+
httpSocketSetup(socket);
|
313
|
+
|
314
|
+
// If the user has added a listener to the server,
|
315
|
+
// request, or response, then it's their responsibility.
|
316
|
+
// otherwise, destroy on timeout by default
|
317
|
+
if (self.timeout)
|
318
|
+
socket.setTimeout(self.timeout);
|
319
|
+
socket.on('timeout', function() {
|
320
|
+
var req = socket.parser && socket.parser.incoming;
|
321
|
+
var reqTimeout = req && !req.complete && req.emit('timeout', socket);
|
322
|
+
var res = socket._httpMessage;
|
323
|
+
var resTimeout = res && res.emit('timeout', socket);
|
324
|
+
var serverTimeout = self.emit('timeout', socket);
|
325
|
+
|
326
|
+
if (!reqTimeout && !resTimeout && !serverTimeout)
|
327
|
+
socket.destroy();
|
328
|
+
});
|
329
|
+
|
330
|
+
var parser = parsers.alloc();
|
331
|
+
parser.reinitialize(HTTPParser.REQUEST);
|
332
|
+
parser.socket = socket;
|
333
|
+
socket.parser = parser;
|
334
|
+
parser.incoming = null;
|
335
|
+
|
336
|
+
// Propagate headers limit from server instance to parser
|
337
|
+
if (util.isNumber(this.maxHeadersCount)) {
|
338
|
+
parser.maxHeaderPairs = this.maxHeadersCount << 1;
|
339
|
+
} else {
|
340
|
+
// Set default value because parser may be reused from FreeList
|
341
|
+
parser.maxHeaderPairs = 2000;
|
342
|
+
}
|
343
|
+
|
344
|
+
socket.addListener('error', socketOnError);
|
345
|
+
socket.addListener('close', serverSocketCloseListener);
|
346
|
+
parser.onIncoming = parserOnIncoming;
|
347
|
+
socket.on('end', socketOnEnd);
|
348
|
+
socket.on('data', socketOnData);
|
349
|
+
|
350
|
+
// TODO(isaacs): Move all these functions out of here
|
351
|
+
function socketOnError(e) {
|
352
|
+
self.emit('clientError', e, this);
|
353
|
+
}
|
354
|
+
|
355
|
+
function socketOnData(d) {
|
356
|
+
assert(!socket._paused);
|
357
|
+
debug('SERVER socketOnData %d', d.length);
|
358
|
+
var ret = parser.execute(d);
|
359
|
+
if (ret instanceof Error) {
|
360
|
+
debug('parse error');
|
361
|
+
socket.destroy(ret);
|
362
|
+
} else if (parser.incoming && parser.incoming.upgrade) {
|
363
|
+
// Upgrade or CONNECT
|
364
|
+
var bytesParsed = ret;
|
365
|
+
var req = parser.incoming;
|
366
|
+
debug('SERVER upgrade or connect', req.method);
|
367
|
+
|
368
|
+
socket.removeListener('data', socketOnData);
|
369
|
+
socket.removeListener('end', socketOnEnd);
|
370
|
+
socket.removeListener('close', serverSocketCloseListener);
|
371
|
+
parser.finish();
|
372
|
+
freeParser(parser, req);
|
373
|
+
|
374
|
+
var eventName = req.method === 'CONNECT' ? 'connect' : 'upgrade';
|
375
|
+
if (EventEmitter.listenerCount(self, eventName) > 0) {
|
376
|
+
debug('SERVER have listener for %s', eventName);
|
377
|
+
var bodyHead = d.slice(bytesParsed, d.length);
|
378
|
+
|
379
|
+
// TODO(isaacs): Need a way to reset a stream to fresh state
|
380
|
+
// IE, not flowing, and not explicitly paused.
|
381
|
+
socket._readableState.flowing = null;
|
382
|
+
self.emit(eventName, req, socket, bodyHead);
|
383
|
+
} else {
|
384
|
+
// Got upgrade header or CONNECT method, but have no handler.
|
385
|
+
socket.destroy();
|
386
|
+
}
|
387
|
+
}
|
388
|
+
|
389
|
+
if (socket._paused) {
|
390
|
+
// onIncoming paused the socket, we should pause the parser as well
|
391
|
+
debug('pause parser');
|
392
|
+
socket.parser.pause();
|
393
|
+
}
|
394
|
+
}
|
395
|
+
|
396
|
+
function socketOnEnd() {
|
397
|
+
var socket = this;
|
398
|
+
var ret = parser.finish();
|
399
|
+
|
400
|
+
if (ret instanceof Error) {
|
401
|
+
debug('parse error');
|
402
|
+
socket.destroy(ret);
|
403
|
+
return;
|
404
|
+
}
|
405
|
+
|
406
|
+
if (!self.httpAllowHalfOpen) {
|
407
|
+
abortIncoming();
|
408
|
+
if (socket.writable) socket.end();
|
409
|
+
} else if (outgoing.length) {
|
410
|
+
outgoing[outgoing.length - 1]._last = true;
|
411
|
+
} else if (socket._httpMessage) {
|
412
|
+
socket._httpMessage._last = true;
|
413
|
+
} else {
|
414
|
+
if (socket.writable) socket.end();
|
415
|
+
}
|
416
|
+
}
|
417
|
+
|
418
|
+
|
419
|
+
// The following callback is issued after the headers have been read on a
|
420
|
+
// new message. In this callback we setup the response object and pass it
|
421
|
+
// to the user.
|
422
|
+
|
423
|
+
socket._paused = false;
|
424
|
+
function socketOnDrain() {
|
425
|
+
// If we previously paused, then start reading again.
|
426
|
+
if (socket._paused) {
|
427
|
+
socket._paused = false;
|
428
|
+
socket.parser.resume();
|
429
|
+
socket.resume();
|
430
|
+
}
|
431
|
+
}
|
432
|
+
socket.on('drain', socketOnDrain);
|
433
|
+
|
434
|
+
function parserOnIncoming(req, shouldKeepAlive) {
|
435
|
+
incoming.push(req);
|
436
|
+
|
437
|
+
// If the writable end isn't consuming, then stop reading
|
438
|
+
// so that we don't become overwhelmed by a flood of
|
439
|
+
// pipelined requests that may never be resolved.
|
440
|
+
if (!socket._paused) {
|
441
|
+
var needPause = socket._writableState.needDrain;
|
442
|
+
if (needPause) {
|
443
|
+
socket._paused = true;
|
444
|
+
// We also need to pause the parser, but don't do that until after
|
445
|
+
// the call to execute, because we may still be processing the last
|
446
|
+
// chunk.
|
447
|
+
socket.pause();
|
448
|
+
}
|
449
|
+
}
|
450
|
+
|
451
|
+
var res = new ServerResponse(req);
|
452
|
+
|
453
|
+
res.shouldKeepAlive = shouldKeepAlive;
|
454
|
+
DTRACE_HTTP_SERVER_REQUEST(req, socket);
|
455
|
+
COUNTER_HTTP_SERVER_REQUEST();
|
456
|
+
|
457
|
+
if (socket._httpMessage) {
|
458
|
+
// There are already pending outgoing res, append.
|
459
|
+
outgoing.push(res);
|
460
|
+
} else {
|
461
|
+
res.assignSocket(socket);
|
462
|
+
}
|
463
|
+
|
464
|
+
// When we're finished writing the response, check if this is the last
|
465
|
+
// respose, if so destroy the socket.
|
466
|
+
res.on('prefinish', resOnFinish);
|
467
|
+
function resOnFinish() {
|
468
|
+
// Usually the first incoming element should be our request. it may
|
469
|
+
// be that in the case abortIncoming() was called that the incoming
|
470
|
+
// array will be empty.
|
471
|
+
assert(incoming.length == 0 || incoming[0] === req);
|
472
|
+
|
473
|
+
incoming.shift();
|
474
|
+
|
475
|
+
// if the user never called req.read(), and didn't pipe() or
|
476
|
+
// .resume() or .on('data'), then we call req._dump() so that the
|
477
|
+
// bytes will be pulled off the wire.
|
478
|
+
if (!req._consuming)
|
479
|
+
req._dump();
|
480
|
+
|
481
|
+
res.detachSocket(socket);
|
482
|
+
|
483
|
+
if (res._last) {
|
484
|
+
socket.destroySoon();
|
485
|
+
} else {
|
486
|
+
// start sending the next message
|
487
|
+
var m = outgoing.shift();
|
488
|
+
if (m) {
|
489
|
+
m.assignSocket(socket);
|
490
|
+
}
|
491
|
+
}
|
492
|
+
}
|
493
|
+
|
494
|
+
if (!util.isUndefined(req.headers.expect) &&
|
495
|
+
(req.httpVersionMajor == 1 && req.httpVersionMinor == 1) &&
|
496
|
+
continueExpression.test(req.headers['expect'])) {
|
497
|
+
res._expect_continue = true;
|
498
|
+
if (EventEmitter.listenerCount(self, 'checkContinue') > 0) {
|
499
|
+
self.emit('checkContinue', req, res);
|
500
|
+
} else {
|
501
|
+
res.writeContinue();
|
502
|
+
self.emit('request', req, res);
|
503
|
+
}
|
504
|
+
} else {
|
505
|
+
self.emit('request', req, res);
|
506
|
+
}
|
507
|
+
return false; // Not a HEAD response. (Not even a response!)
|
508
|
+
}
|
509
|
+
}
|
510
|
+
exports._connectionListener = connectionListener;
|