macgyver 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +5 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE +20 -0
  5. data/README.md +45 -0
  6. data/Rakefile +1 -0
  7. data/assets/MacGap.app/Contents/Frameworks/Growl.framework/Growl +0 -0
  8. data/assets/MacGap.app/Contents/Frameworks/Growl.framework/Versions/A/Growl +0 -0
  9. data/assets/MacGap.app/Contents/Frameworks/Growl.framework/Versions/A/Headers/Growl.h +5 -0
  10. data/assets/MacGap.app/Contents/Frameworks/Growl.framework/Versions/A/Headers/GrowlApplicationBridge.h +551 -0
  11. data/assets/MacGap.app/Contents/Frameworks/Growl.framework/Versions/A/Headers/GrowlDefines.h +341 -0
  12. data/assets/MacGap.app/Contents/Frameworks/Growl.framework/Versions/A/Resources/Info.plist +40 -0
  13. data/assets/MacGap.app/Contents/Frameworks/Growl.framework/Versions/A/_CodeSignature/CodeResources +34 -0
  14. data/assets/MacGap.app/Contents/Info.plist +48 -0
  15. data/assets/MacGap.app/Contents/MacOS/MacGap +0 -0
  16. data/assets/MacGap.app/Contents/PkgInfo +1 -0
  17. data/assets/MacGap.app/Contents/Resources/_debugger.js +1718 -0
  18. data/assets/MacGap.app/Contents/Resources/_http_agent.js +310 -0
  19. data/assets/MacGap.app/Contents/Resources/_http_client.js +533 -0
  20. data/assets/MacGap.app/Contents/Resources/_http_common.js +222 -0
  21. data/assets/MacGap.app/Contents/Resources/_http_incoming.js +194 -0
  22. data/assets/MacGap.app/Contents/Resources/_http_outgoing.js +597 -0
  23. data/assets/MacGap.app/Contents/Resources/_http_server.js +510 -0
  24. data/assets/MacGap.app/Contents/Resources/_linklist.js +76 -0
  25. data/assets/MacGap.app/Contents/Resources/_stream_duplex.js +69 -0
  26. data/assets/MacGap.app/Contents/Resources/_stream_passthrough.js +41 -0
  27. data/assets/MacGap.app/Contents/Resources/_stream_readable.js +900 -0
  28. data/assets/MacGap.app/Contents/Resources/_stream_transform.js +204 -0
  29. data/assets/MacGap.app/Contents/Resources/_stream_writable.js +456 -0
  30. data/assets/MacGap.app/Contents/Resources/_tls_legacy.js +887 -0
  31. data/assets/MacGap.app/Contents/Resources/_tls_wrap.js +831 -0
  32. data/assets/MacGap.app/Contents/Resources/application.icns +0 -0
  33. data/assets/MacGap.app/Contents/Resources/assert.js +326 -0
  34. data/assets/MacGap.app/Contents/Resources/buffer.js +724 -0
  35. data/assets/MacGap.app/Contents/Resources/child_process.js +1107 -0
  36. data/assets/MacGap.app/Contents/Resources/cluster.js +613 -0
  37. data/assets/MacGap.app/Contents/Resources/console.js +108 -0
  38. data/assets/MacGap.app/Contents/Resources/constants.js +22 -0
  39. data/assets/MacGap.app/Contents/Resources/crypto.js +691 -0
  40. data/assets/MacGap.app/Contents/Resources/dgram.js +459 -0
  41. data/assets/MacGap.app/Contents/Resources/dns.js +274 -0
  42. data/assets/MacGap.app/Contents/Resources/domain.js +292 -0
  43. data/assets/MacGap.app/Contents/Resources/en.lproj/Credits.rtf +29 -0
  44. data/assets/MacGap.app/Contents/Resources/en.lproj/InfoPlist.strings +0 -0
  45. data/assets/MacGap.app/Contents/Resources/en.lproj/MainMenu.nib +0 -0
  46. data/assets/MacGap.app/Contents/Resources/en.lproj/Window.nib +0 -0
  47. data/assets/MacGap.app/Contents/Resources/events.js +312 -0
  48. data/assets/MacGap.app/Contents/Resources/freelist.js +43 -0
  49. data/assets/MacGap.app/Contents/Resources/fs.js +1732 -0
  50. data/assets/MacGap.app/Contents/Resources/http.js +119 -0
  51. data/assets/MacGap.app/Contents/Resources/https.js +134 -0
  52. data/assets/MacGap.app/Contents/Resources/module.js +529 -0
  53. data/assets/MacGap.app/Contents/Resources/net.js +1378 -0
  54. data/assets/MacGap.app/Contents/Resources/nodelike.js +195 -0
  55. data/assets/MacGap.app/Contents/Resources/os.js +64 -0
  56. data/assets/MacGap.app/Contents/Resources/path.js +517 -0
  57. data/assets/MacGap.app/Contents/Resources/public/index.html +38 -0
  58. data/assets/MacGap.app/Contents/Resources/punycode.js +507 -0
  59. data/assets/MacGap.app/Contents/Resources/querystring.js +206 -0
  60. data/assets/MacGap.app/Contents/Resources/readline.js +1311 -0
  61. data/assets/MacGap.app/Contents/Resources/repl.js +945 -0
  62. data/assets/MacGap.app/Contents/Resources/smalloc.js +90 -0
  63. data/assets/MacGap.app/Contents/Resources/stream.js +127 -0
  64. data/assets/MacGap.app/Contents/Resources/string_decoder.js +189 -0
  65. data/assets/MacGap.app/Contents/Resources/sys.js +24 -0
  66. data/assets/MacGap.app/Contents/Resources/timers.js +568 -0
  67. data/assets/MacGap.app/Contents/Resources/tls.js +220 -0
  68. data/assets/MacGap.app/Contents/Resources/tty.js +129 -0
  69. data/assets/MacGap.app/Contents/Resources/url.js +693 -0
  70. data/assets/MacGap.app/Contents/Resources/util.js +688 -0
  71. data/assets/MacGap.app/Contents/Resources/vm.js +73 -0
  72. data/assets/MacGap.app/Contents/Resources/zlib.js +524 -0
  73. data/assets/index.html +38 -0
  74. data/bin/macgyver +104 -0
  75. data/macgyver.gemspec +19 -0
  76. data/test/public/index.html +27 -0
  77. 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;