macgyver 0.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+ };