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,310 @@
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 net = require('net');
23
+ var url = require('url');
24
+ var util = require('util');
25
+ var EventEmitter = require('events').EventEmitter;
26
+ var ClientRequest = require('_http_client').ClientRequest;
27
+ var debug = util.debuglog('http');
28
+
29
+ // New Agent code.
30
+
31
+ // The largest departure from the previous implementation is that
32
+ // an Agent instance holds connections for a variable number of host:ports.
33
+ // Surprisingly, this is still API compatible as far as third parties are
34
+ // concerned. The only code that really notices the difference is the
35
+ // request object.
36
+
37
+ // Another departure is that all code related to HTTP parsing is in
38
+ // ClientRequest.onSocket(). The Agent is now *strictly*
39
+ // concerned with managing a connection pool.
40
+
41
+ function Agent(options) {
42
+ if (!(this instanceof Agent))
43
+ return new Agent(options);
44
+
45
+ EventEmitter.call(this);
46
+
47
+ var self = this;
48
+
49
+ self.defaultPort = 80;
50
+ self.protocol = 'http:';
51
+
52
+ self.options = util._extend({}, options);
53
+
54
+ // don't confuse net and make it think that we're connecting to a pipe
55
+ self.options.path = null;
56
+ self.requests = {};
57
+ self.sockets = {};
58
+ self.freeSockets = {};
59
+ self.keepAliveMsecs = self.options.keepAliveMsecs || 1000;
60
+ self.keepAlive = self.options.keepAlive || false;
61
+ self.maxSockets = self.options.maxSockets || Agent.defaultMaxSockets;
62
+ self.maxFreeSockets = self.options.maxFreeSockets || 256;
63
+
64
+ self.on('free', function(socket, options) {
65
+ var name = self.getName(options);
66
+ debug('agent.on(free)', name);
67
+
68
+ if (!socket.destroyed &&
69
+ self.requests[name] && self.requests[name].length) {
70
+ self.requests[name].shift().onSocket(socket);
71
+ if (self.requests[name].length === 0) {
72
+ // don't leak
73
+ delete self.requests[name];
74
+ }
75
+ } else {
76
+ // If there are no pending requests, then put it in
77
+ // the freeSockets pool, but only if we're allowed to do so.
78
+ var req = socket._httpMessage;
79
+ if (req &&
80
+ req.shouldKeepAlive &&
81
+ !socket.destroyed &&
82
+ self.options.keepAlive) {
83
+ var freeSockets = self.freeSockets[name];
84
+ var freeLen = freeSockets ? freeSockets.length : 0;
85
+ var count = freeLen;
86
+ if (self.sockets[name])
87
+ count += self.sockets[name].length;
88
+
89
+ if (count >= self.maxSockets || freeLen >= self.maxFreeSockets) {
90
+ self.removeSocket(socket, options);
91
+ socket.destroy();
92
+ } else {
93
+ freeSockets = freeSockets || [];
94
+ self.freeSockets[name] = freeSockets;
95
+ socket.setKeepAlive(true, self.keepAliveMsecs);
96
+ socket.unref();
97
+ socket._httpMessage = null;
98
+ self.removeSocket(socket, options);
99
+ freeSockets.push(socket);
100
+ }
101
+ } else {
102
+ self.removeSocket(socket, options);
103
+ socket.destroy();
104
+ }
105
+ }
106
+ });
107
+ }
108
+
109
+ util.inherits(Agent, EventEmitter);
110
+ exports.Agent = Agent;
111
+
112
+ Agent.defaultMaxSockets = Infinity;
113
+
114
+ Agent.prototype.createConnection = net.createConnection;
115
+
116
+ // Get the key for a given set of request options
117
+ Agent.prototype.getName = function(options) {
118
+ var name = '';
119
+
120
+ if (options.host)
121
+ name += options.host;
122
+ else
123
+ name += 'localhost';
124
+
125
+ name += ':';
126
+ if (options.port)
127
+ name += options.port;
128
+ name += ':';
129
+ if (options.localAddress)
130
+ name += options.localAddress;
131
+ name += ':';
132
+ return name;
133
+ };
134
+
135
+ Agent.prototype.addRequest = function(req, options) {
136
+ // Legacy API: addRequest(req, host, port, path)
137
+ if (typeof options === 'string') {
138
+ options = {
139
+ host: options,
140
+ port: arguments[2],
141
+ path: arguments[3]
142
+ };
143
+ }
144
+
145
+ var name = this.getName(options);
146
+ if (!this.sockets[name]) {
147
+ this.sockets[name] = [];
148
+ }
149
+
150
+ var freeLen = this.freeSockets[name] ? this.freeSockets[name].length : 0;
151
+ var sockLen = freeLen + this.sockets[name].length;
152
+
153
+ if (freeLen) {
154
+ // we have a free socket, so use that.
155
+ var socket = this.freeSockets[name].shift();
156
+ debug('have free socket');
157
+
158
+ // don't leak
159
+ if (!this.freeSockets[name].length)
160
+ delete this.freeSockets[name];
161
+
162
+ socket.ref();
163
+ req.onSocket(socket);
164
+ this.sockets[name].push(socket);
165
+ } else if (sockLen < this.maxSockets) {
166
+ debug('call onSocket', sockLen, freeLen);
167
+ // If we are under maxSockets create a new one.
168
+ req.onSocket(this.createSocket(req, options));
169
+ } else {
170
+ debug('wait for socket');
171
+ // We are over limit so we'll add it to the queue.
172
+ if (!this.requests[name]) {
173
+ this.requests[name] = [];
174
+ }
175
+ this.requests[name].push(req);
176
+ }
177
+ };
178
+
179
+ Agent.prototype.createSocket = function(req, options) {
180
+ var self = this;
181
+ options = util._extend({}, options);
182
+ options = util._extend(options, self.options);
183
+
184
+ options.servername = options.host;
185
+ if (req) {
186
+ var hostHeader = req.getHeader('host');
187
+ if (hostHeader) {
188
+ options.servername = hostHeader.replace(/:.*$/, '');
189
+ }
190
+ }
191
+
192
+ var name = self.getName(options);
193
+
194
+ debug('createConnection', name, options);
195
+ options.encoding = null;
196
+ var s = self.createConnection(options);
197
+ if (!self.sockets[name]) {
198
+ self.sockets[name] = [];
199
+ }
200
+ this.sockets[name].push(s);
201
+ debug('sockets', name, this.sockets[name].length);
202
+
203
+ function onFree() {
204
+ self.emit('free', s, options);
205
+ }
206
+ s.on('free', onFree);
207
+
208
+ function onClose(err) {
209
+ debug('CLIENT socket onClose');
210
+ // This is the only place where sockets get removed from the Agent.
211
+ // If you want to remove a socket from the pool, just close it.
212
+ // All socket errors end in a close event anyway.
213
+ self.removeSocket(s, options);
214
+ }
215
+ s.on('close', onClose);
216
+
217
+ function onRemove() {
218
+ // We need this function for cases like HTTP 'upgrade'
219
+ // (defined by WebSockets) where we need to remove a socket from the
220
+ // pool because it'll be locked up indefinitely
221
+ debug('CLIENT socket onRemove');
222
+ self.removeSocket(s, options);
223
+ s.removeListener('close', onClose);
224
+ s.removeListener('free', onFree);
225
+ s.removeListener('agentRemove', onRemove);
226
+ }
227
+ s.on('agentRemove', onRemove);
228
+ return s;
229
+ };
230
+
231
+ Agent.prototype.removeSocket = function(s, options) {
232
+ var name = this.getName(options);
233
+ debug('removeSocket', name, 'destroyed:', s.destroyed);
234
+ var sets = [this.sockets];
235
+
236
+ // If the socket was destroyed, remove it from the free buffers too.
237
+ if (s.destroyed)
238
+ sets.push(this.freeSockets);
239
+
240
+ sets.forEach(function(sockets) {
241
+ if (sockets[name]) {
242
+ var index = sockets[name].indexOf(s);
243
+ if (index !== -1) {
244
+ sockets[name].splice(index, 1);
245
+ // Don't leak
246
+ if (sockets[name].length === 0)
247
+ delete sockets[name];
248
+ }
249
+ }
250
+ });
251
+ if (this.requests[name] && this.requests[name].length) {
252
+ debug('removeSocket, have a request, make a socket');
253
+ var req = this.requests[name][0];
254
+ // If we have pending requests and a socket gets closed make a new one
255
+ this.createSocket(req, options).emit('free');
256
+ }
257
+ };
258
+
259
+ Agent.prototype.destroy = function() {
260
+ var sets = [this.freeSockets, this.sockets];
261
+ sets.forEach(function(set) {
262
+ Object.keys(set).forEach(function(name) {
263
+ set[name].forEach(function(socket) {
264
+ socket.destroy();
265
+ });
266
+ });
267
+ });
268
+ };
269
+
270
+ Agent.prototype.request = function(options, cb) {
271
+ if (util.isString(options)) {
272
+ options = url.parse(options);
273
+ }
274
+ // don't try to do dns lookups of foo.com:8080, just foo.com
275
+ if (options.hostname) {
276
+ options.host = options.hostname;
277
+ }
278
+
279
+ if (options && options.path && / /.test(options.path)) {
280
+ // The actual regex is more like /[^A-Za-z0-9\-._~!$&'()*+,;=/:@]/
281
+ // with an additional rule for ignoring percentage-escaped characters
282
+ // but that's a) hard to capture in a regular expression that performs
283
+ // well, and b) possibly too restrictive for real-world usage. That's
284
+ // why it only scans for spaces because those are guaranteed to create
285
+ // an invalid request.
286
+ throw new TypeError('Request path contains unescaped characters.');
287
+ } else if (options.protocol && options.protocol !== this.protocol) {
288
+ throw new Error('Protocol:' + options.protocol + ' not supported.');
289
+ }
290
+
291
+ options = util._extend({
292
+ agent: this,
293
+ keepAlive: this.keepAlive
294
+ }, options);
295
+
296
+ // if it's false, then make a new one, just like this one.
297
+ if (options.agent === false)
298
+ options.agent = new this.constructor();
299
+
300
+ debug('agent.request', options);
301
+ return new ClientRequest(options, cb);
302
+ };
303
+
304
+ Agent.prototype.get = function(options, cb) {
305
+ var req = this.request(options, cb);
306
+ req.end();
307
+ return req;
308
+ };
309
+
310
+ exports.globalAgent = new Agent();
@@ -0,0 +1,533 @@
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
+
30
+ var httpSocketSetup = common.httpSocketSetup;
31
+ var parsers = common.parsers;
32
+ var freeParser = common.freeParser;
33
+ var debug = common.debug;
34
+
35
+ var IncomingMessage = require('_http_incoming').IncomingMessage;
36
+ var OutgoingMessage = require('_http_outgoing').OutgoingMessage;
37
+
38
+ var agent = require('_http_agent');
39
+ var globalAgent = agent.globalAgent;
40
+
41
+
42
+ function ClientRequest(options, cb) {
43
+ var self = this;
44
+ OutgoingMessage.call(self);
45
+
46
+ options = util._extend({}, options);
47
+
48
+ self.agent = util.isUndefined(options.agent) ? globalAgent : options.agent;
49
+
50
+ var defaultPort = options.defaultPort || self.agent.defaultPort;
51
+
52
+ var port = options.port = options.port || defaultPort;
53
+ var host = options.host = options.hostname || options.host || 'localhost';
54
+
55
+ if (util.isUndefined(options.setHost)) {
56
+ var setHost = true;
57
+ }
58
+
59
+ self.socketPath = options.socketPath;
60
+
61
+ var method = self.method = (options.method || 'GET').toUpperCase();
62
+ self.path = options.path || '/';
63
+ if (cb) {
64
+ self.once('response', cb);
65
+ }
66
+
67
+ if (!util.isArray(options.headers)) {
68
+ if (options.headers) {
69
+ var keys = Object.keys(options.headers);
70
+ for (var i = 0, l = keys.length; i < l; i++) {
71
+ var key = keys[i];
72
+ self.setHeader(key, options.headers[key]);
73
+ }
74
+ }
75
+ if (host && !this.getHeader('host') && setHost) {
76
+ var hostHeader = host;
77
+ if (port && +port !== defaultPort) {
78
+ hostHeader += ':' + port;
79
+ }
80
+ this.setHeader('Host', hostHeader);
81
+ }
82
+ }
83
+
84
+ if (options.auth && !this.getHeader('Authorization')) {
85
+ //basic auth
86
+ this.setHeader('Authorization', 'Basic ' +
87
+ new Buffer(options.auth).toString('base64'));
88
+ }
89
+
90
+ if (method === 'GET' ||
91
+ method === 'HEAD' ||
92
+ method === 'DELETE' ||
93
+ method === 'CONNECT') {
94
+ self.useChunkedEncodingByDefault = false;
95
+ } else {
96
+ self.useChunkedEncodingByDefault = true;
97
+ }
98
+
99
+ if (util.isArray(options.headers)) {
100
+ self._storeHeader(self.method + ' ' + self.path + ' HTTP/1.1\r\n',
101
+ options.headers);
102
+ } else if (self.getHeader('expect')) {
103
+ self._storeHeader(self.method + ' ' + self.path + ' HTTP/1.1\r\n',
104
+ self._renderHeaders());
105
+ }
106
+
107
+ if (self.socketPath) {
108
+ self._last = true;
109
+ self.shouldKeepAlive = false;
110
+ var conn = self.agent.createConnection({ path: self.socketPath });
111
+ self.onSocket(conn);
112
+ } else if (self.agent) {
113
+ // If there is an agent we should default to Connection:keep-alive,
114
+ // but only if the Agent will actually reuse the connection!
115
+ // If it's not a keepAlive agent, and the maxSockets==Infinity, then
116
+ // there's never a case where this socket will actually be reused
117
+ var agent = self.agent;
118
+ if (!agent.keepAlive && !Number.isFinite(agent.maxSockets)) {
119
+ self._last = true;
120
+ self.shouldKeepAlive = false;
121
+ } else {
122
+ self._last = false;
123
+ self.shouldKeepAlive = true;
124
+ }
125
+ self.agent.addRequest(self, options);
126
+ } else {
127
+ // No agent, default to Connection:close.
128
+ self._last = true;
129
+ self.shouldKeepAlive = false;
130
+ if (options.createConnection) {
131
+ var conn = options.createConnection(options);
132
+ } else {
133
+ debug('CLIENT use net.createConnection', options);
134
+ var conn = net.createConnection(options);
135
+ }
136
+ self.onSocket(conn);
137
+ }
138
+
139
+ self._deferToConnect(null, null, function() {
140
+ self._flush();
141
+ self = null;
142
+ });
143
+ }
144
+
145
+ util.inherits(ClientRequest, OutgoingMessage);
146
+
147
+ exports.ClientRequest = ClientRequest;
148
+
149
+ ClientRequest.prototype._finish = function() {
150
+ DTRACE_HTTP_CLIENT_REQUEST(this, this.connection);
151
+ COUNTER_HTTP_CLIENT_REQUEST();
152
+ OutgoingMessage.prototype._finish.call(this);
153
+ };
154
+
155
+ ClientRequest.prototype._implicitHeader = function() {
156
+ this._storeHeader(this.method + ' ' + this.path + ' HTTP/1.1\r\n',
157
+ this._renderHeaders());
158
+ };
159
+
160
+ ClientRequest.prototype.abort = function() {
161
+ // If we're aborting, we don't care about any more response data.
162
+ if (this.res)
163
+ this.res._dump();
164
+ else
165
+ this.once('response', function(res) {
166
+ res._dump();
167
+ });
168
+
169
+ if (this.socket) {
170
+ // in-progress
171
+ this.socket.destroy();
172
+ } else {
173
+ // haven't been assigned a socket yet.
174
+ // this could be more efficient, it could
175
+ // remove itself from the pending requests
176
+ this._deferToConnect('destroy', []);
177
+ }
178
+ };
179
+
180
+
181
+ function createHangUpError() {
182
+ var error = new Error('socket hang up');
183
+ error.code = 'ECONNRESET';
184
+ return error;
185
+ }
186
+
187
+
188
+ function socketCloseListener() {
189
+ var socket = this;
190
+ var req = socket._httpMessage;
191
+ debug('HTTP socket close');
192
+
193
+ // Pull through final chunk, if anything is buffered.
194
+ // the ondata function will handle it properly, and this
195
+ // is a no-op if no final chunk remains.
196
+ socket.read();
197
+
198
+ // NOTE: Its important to get parser here, because it could be freed by
199
+ // the `socketOnData`.
200
+ var parser = socket.parser;
201
+ req.emit('close');
202
+ if (req.res && req.res.readable) {
203
+ // Socket closed before we emitted 'end' below.
204
+ req.res.emit('aborted');
205
+ var res = req.res;
206
+ res.on('end', function() {
207
+ res.emit('close');
208
+ });
209
+ res.push(null);
210
+ } else if (!req.res && !req.socket._hadError) {
211
+ // This socket error fired before we started to
212
+ // receive a response. The error needs to
213
+ // fire on the request.
214
+ req.emit('error', createHangUpError());
215
+ req.socket._hadError = true;
216
+ }
217
+
218
+ // Too bad. That output wasn't getting written.
219
+ // This is pretty terrible that it doesn't raise an error.
220
+ // Fixed better in v0.10
221
+ if (req.output)
222
+ req.output.length = 0;
223
+ if (req.outputEncodings)
224
+ req.outputEncodings.length = 0;
225
+
226
+ if (parser) {
227
+ parser.finish();
228
+ freeParser(parser, req);
229
+ }
230
+ }
231
+
232
+ function socketErrorListener(err) {
233
+ var socket = this;
234
+ var parser = socket.parser;
235
+ var req = socket._httpMessage;
236
+ debug('SOCKET ERROR:', err.message, err.stack);
237
+
238
+ if (req) {
239
+ req.emit('error', err);
240
+ // For Safety. Some additional errors might fire later on
241
+ // and we need to make sure we don't double-fire the error event.
242
+ req.socket._hadError = true;
243
+ }
244
+
245
+ if (parser) {
246
+ parser.finish();
247
+ freeParser(parser, req);
248
+ }
249
+ socket.destroy();
250
+ }
251
+
252
+ function socketOnEnd() {
253
+ var socket = this;
254
+ var req = this._httpMessage;
255
+ var parser = this.parser;
256
+
257
+ if (!req.res && !req.socket._hadError) {
258
+ // If we don't have a response then we know that the socket
259
+ // ended prematurely and we need to emit an error on the request.
260
+ req.emit('error', createHangUpError());
261
+ req.socket._hadError = true;
262
+ }
263
+ if (parser) {
264
+ parser.finish();
265
+ freeParser(parser, req);
266
+ }
267
+ socket.destroy();
268
+ }
269
+
270
+ function socketOnData(d) {
271
+ var socket = this;
272
+ var req = this._httpMessage;
273
+ var parser = this.parser;
274
+
275
+ assert(parser && parser.socket === socket);
276
+
277
+ var ret = parser.execute(d);
278
+ if (ret instanceof Error) {
279
+ debug('parse error');
280
+ freeParser(parser, req);
281
+ socket.destroy();
282
+ req.emit('error', ret);
283
+ req.socket._hadError = true;
284
+ } else if (parser.incoming && parser.incoming.upgrade) {
285
+ // Upgrade or CONNECT
286
+ var bytesParsed = ret;
287
+ var res = parser.incoming;
288
+ req.res = res;
289
+
290
+ socket.removeListener('data', socketOnData);
291
+ socket.removeListener('end', socketOnEnd);
292
+ parser.finish();
293
+
294
+ var bodyHead = d.slice(bytesParsed, d.length);
295
+
296
+ var eventName = req.method === 'CONNECT' ? 'connect' : 'upgrade';
297
+ if (EventEmitter.listenerCount(req, eventName) > 0) {
298
+ req.upgradeOrConnect = true;
299
+
300
+ // detach the socket
301
+ socket.emit('agentRemove');
302
+ socket.removeListener('close', socketCloseListener);
303
+ socket.removeListener('error', socketErrorListener);
304
+
305
+ // TODO(isaacs): Need a way to reset a stream to fresh state
306
+ // IE, not flowing, and not explicitly paused.
307
+ socket._readableState.flowing = null;
308
+
309
+ req.emit(eventName, res, socket, bodyHead);
310
+ req.emit('close');
311
+ } else {
312
+ // Got Upgrade header or CONNECT method, but have no handler.
313
+ socket.destroy();
314
+ }
315
+ freeParser(parser, req);
316
+ } else if (parser.incoming && parser.incoming.complete &&
317
+ // When the status code is 100 (Continue), the server will
318
+ // send a final response after this client sends a request
319
+ // body. So, we must not free the parser.
320
+ parser.incoming.statusCode !== 100) {
321
+ socket.removeListener('data', socketOnData);
322
+ socket.removeListener('end', socketOnEnd);
323
+ freeParser(parser, req);
324
+ }
325
+ }
326
+
327
+
328
+ // client
329
+ function parserOnIncomingClient(res, shouldKeepAlive) {
330
+ var socket = this.socket;
331
+ var req = socket._httpMessage;
332
+
333
+
334
+ // propogate "domain" setting...
335
+ if (req.domain && !res.domain) {
336
+ debug('setting "res.domain"');
337
+ res.domain = req.domain;
338
+ }
339
+
340
+ debug('AGENT incoming response!');
341
+
342
+ if (req.res) {
343
+ // We already have a response object, this means the server
344
+ // sent a double response.
345
+ socket.destroy();
346
+ return;
347
+ }
348
+ req.res = res;
349
+
350
+ // Responses to CONNECT request is handled as Upgrade.
351
+ if (req.method === 'CONNECT') {
352
+ res.upgrade = true;
353
+ return true; // skip body
354
+ }
355
+
356
+ // Responses to HEAD requests are crazy.
357
+ // HEAD responses aren't allowed to have an entity-body
358
+ // but *can* have a content-length which actually corresponds
359
+ // to the content-length of the entity-body had the request
360
+ // been a GET.
361
+ var isHeadResponse = req.method == 'HEAD';
362
+ debug('AGENT isHeadResponse', isHeadResponse);
363
+
364
+ if (res.statusCode == 100) {
365
+ // restart the parser, as this is a continue message.
366
+ delete req.res; // Clear res so that we don't hit double-responses.
367
+ req.emit('continue');
368
+ return true;
369
+ }
370
+
371
+ if (req.shouldKeepAlive && !shouldKeepAlive && !req.upgradeOrConnect) {
372
+ // Server MUST respond with Connection:keep-alive for us to enable it.
373
+ // If we've been upgraded (via WebSockets) we also shouldn't try to
374
+ // keep the connection open.
375
+ req.shouldKeepAlive = false;
376
+ }
377
+
378
+
379
+ DTRACE_HTTP_CLIENT_RESPONSE(socket, req);
380
+ COUNTER_HTTP_CLIENT_RESPONSE();
381
+ req.res = res;
382
+ res.req = req;
383
+
384
+ // add our listener first, so that we guarantee socket cleanup
385
+ res.on('end', responseOnEnd);
386
+ var handled = req.emit('response', res);
387
+
388
+ // If the user did not listen for the 'response' event, then they
389
+ // can't possibly read the data, so we ._dump() it into the void
390
+ // so that the socket doesn't hang there in a paused state.
391
+ if (!handled)
392
+ res._dump();
393
+
394
+ return isHeadResponse;
395
+ }
396
+
397
+ // client
398
+ function responseOnEnd() {
399
+ var res = this;
400
+ var req = res.req;
401
+ var socket = req.socket;
402
+
403
+ if (!req.shouldKeepAlive) {
404
+ if (socket.writable) {
405
+ debug('AGENT socket.destroySoon()');
406
+ socket.destroySoon();
407
+ }
408
+ assert(!socket.writable);
409
+ } else {
410
+ debug('AGENT socket keep-alive');
411
+ if (req.timeoutCb) {
412
+ socket.setTimeout(0, req.timeoutCb);
413
+ req.timeoutCb = null;
414
+ }
415
+ socket.removeListener('close', socketCloseListener);
416
+ socket.removeListener('error', socketErrorListener);
417
+ // Mark this socket as available, AFTER user-added end
418
+ // handlers have a chance to run.
419
+ process.nextTick(function() {
420
+ socket.emit('free');
421
+ });
422
+ }
423
+ }
424
+
425
+ function tickOnSocket(req, socket) {
426
+ var parser = parsers.alloc();
427
+ req.socket = socket;
428
+ req.connection = socket;
429
+ parser.reinitialize(HTTPParser.RESPONSE);
430
+ parser.socket = socket;
431
+ parser.incoming = null;
432
+ req.parser = parser;
433
+
434
+ socket.parser = parser;
435
+ socket._httpMessage = req;
436
+
437
+ // Setup "drain" propogation.
438
+ httpSocketSetup(socket);
439
+
440
+ // Propagate headers limit from request object to parser
441
+ if (util.isNumber(req.maxHeadersCount)) {
442
+ parser.maxHeaderPairs = req.maxHeadersCount << 1;
443
+ } else {
444
+ // Set default value because parser may be reused from FreeList
445
+ parser.maxHeaderPairs = 2000;
446
+ }
447
+
448
+ parser.onIncoming = parserOnIncomingClient;
449
+ socket.on('error', socketErrorListener);
450
+ socket.on('data', socketOnData);
451
+ socket.on('end', socketOnEnd);
452
+ socket.on('close', socketCloseListener);
453
+ req.emit('socket', socket);
454
+ }
455
+
456
+ ClientRequest.prototype.onSocket = function(socket) {
457
+ var req = this;
458
+
459
+ process.nextTick(function() {
460
+ tickOnSocket(req, socket);
461
+ });
462
+ };
463
+
464
+ ClientRequest.prototype._deferToConnect = function(method, arguments_, cb) {
465
+ // This function is for calls that need to happen once the socket is
466
+ // connected and writable. It's an important promisy thing for all the socket
467
+ // calls that happen either now (when a socket is assigned) or
468
+ // in the future (when a socket gets assigned out of the pool and is
469
+ // eventually writable).
470
+ var self = this;
471
+ var onSocket = function() {
472
+ if (self.socket.writable) {
473
+ if (method) {
474
+ self.socket[method].apply(self.socket, arguments_);
475
+ }
476
+ if (cb) { cb(); }
477
+ } else {
478
+ self.socket.once('connect', function() {
479
+ if (method) {
480
+ self.socket[method].apply(self.socket, arguments_);
481
+ }
482
+ if (cb) { cb(); }
483
+ });
484
+ }
485
+ }
486
+ if (!self.socket) {
487
+ self.once('socket', onSocket);
488
+ } else {
489
+ onSocket();
490
+ }
491
+ };
492
+
493
+ ClientRequest.prototype.setTimeout = function(msecs, callback) {
494
+ if (callback) this.once('timeout', callback);
495
+
496
+ var self = this;
497
+ function emitTimeout() {
498
+ self.emit('timeout');
499
+ }
500
+
501
+ if (this.socket && this.socket.writable) {
502
+ if (this.timeoutCb)
503
+ this.socket.setTimeout(0, this.timeoutCb);
504
+ this.timeoutCb = emitTimeout;
505
+ this.socket.setTimeout(msecs, emitTimeout);
506
+ return;
507
+ }
508
+
509
+ // Set timeoutCb so that it'll get cleaned up on request end
510
+ this.timeoutCb = emitTimeout;
511
+ if (this.socket) {
512
+ var sock = this.socket;
513
+ this.socket.once('connect', function() {
514
+ sock.setTimeout(msecs, emitTimeout);
515
+ });
516
+ return;
517
+ }
518
+
519
+ this.once('socket', function(sock) {
520
+ sock.setTimeout(msecs, emitTimeout);
521
+ });
522
+ };
523
+
524
+ ClientRequest.prototype.setNoDelay = function() {
525
+ this._deferToConnect('setNoDelay', arguments);
526
+ };
527
+ ClientRequest.prototype.setSocketKeepAlive = function() {
528
+ this._deferToConnect('setKeepAlive', arguments);
529
+ };
530
+
531
+ ClientRequest.prototype.clearTimeout = function(cb) {
532
+ this.setTimeout(0, cb);
533
+ };