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,222 @@
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 FreeList = require('freelist').FreeList;
23
+ var HTTPParser = process.binding('http_parser').HTTPParser;
24
+
25
+ var incoming = require('_http_incoming');
26
+ var IncomingMessage = incoming.IncomingMessage;
27
+ var readStart = incoming.readStart;
28
+ var readStop = incoming.readStop;
29
+
30
+ var isNumber = require('util').isNumber;
31
+ var debug = require('util').debuglog('http');
32
+ exports.debug = debug;
33
+
34
+ exports.CRLF = '\r\n';
35
+ exports.chunkExpression = /chunk/i;
36
+ exports.continueExpression = /100-continue/i;
37
+ exports.methods = HTTPParser.methods;
38
+
39
+ var kOnHeaders = HTTPParser.kOnHeaders | 0;
40
+ var kOnHeadersComplete = HTTPParser.kOnHeadersComplete | 0;
41
+ var kOnBody = HTTPParser.kOnBody | 0;
42
+ var kOnMessageComplete = HTTPParser.kOnMessageComplete | 0;
43
+
44
+ // Only called in the slow case where slow means
45
+ // that the request headers were either fragmented
46
+ // across multiple TCP packets or too large to be
47
+ // processed in a single run. This method is also
48
+ // called to process trailing HTTP headers.
49
+ function parserOnHeaders(headers, url) {
50
+ // Once we exceeded headers limit - stop collecting them
51
+ if (this.maxHeaderPairs <= 0 ||
52
+ this._headers.length < this.maxHeaderPairs) {
53
+ this._headers = this._headers.concat(headers);
54
+ }
55
+ this._url += url;
56
+ }
57
+
58
+ // info.headers and info.url are set only if .onHeaders()
59
+ // has not been called for this request.
60
+ //
61
+ // info.url is not set for response parsers but that's not
62
+ // applicable here since all our parsers are request parsers.
63
+ function parserOnHeadersComplete(info) {
64
+ debug('parserOnHeadersComplete', info);
65
+ var parser = this;
66
+ var headers = info.headers;
67
+ var url = info.url;
68
+
69
+ if (!headers) {
70
+ headers = parser._headers;
71
+ parser._headers = [];
72
+ }
73
+
74
+ if (!url) {
75
+ url = parser._url;
76
+ parser._url = '';
77
+ }
78
+
79
+ parser.incoming = new IncomingMessage(parser.socket);
80
+ parser.incoming.httpVersionMajor = info.versionMajor;
81
+ parser.incoming.httpVersionMinor = info.versionMinor;
82
+ parser.incoming.httpVersion = info.versionMajor + '.' + info.versionMinor;
83
+ parser.incoming.url = url;
84
+
85
+ var n = headers.length;
86
+
87
+ // If parser.maxHeaderPairs <= 0 - assume that there're no limit
88
+ if (parser.maxHeaderPairs > 0) {
89
+ n = Math.min(n, parser.maxHeaderPairs);
90
+ }
91
+
92
+ parser.incoming._addHeaderLines(headers, n);
93
+
94
+ if (isNumber(info.method)) {
95
+ // server only
96
+ parser.incoming.method = HTTPParser.methods[info.method];
97
+ } else {
98
+ // client only
99
+ parser.incoming.statusCode = info.statusCode;
100
+ parser.incoming.statusMessage = info.statusMessage;
101
+ }
102
+
103
+ parser.incoming.upgrade = info.upgrade;
104
+
105
+ var skipBody = false; // response to HEAD or CONNECT
106
+
107
+ if (!info.upgrade) {
108
+ // For upgraded connections and CONNECT method request,
109
+ // we'll emit this after parser.execute
110
+ // so that we can capture the first part of the new protocol
111
+ skipBody = parser.onIncoming(parser.incoming, info.shouldKeepAlive);
112
+ }
113
+
114
+ return skipBody;
115
+ }
116
+
117
+ // XXX This is a mess.
118
+ // TODO: http.Parser should be a Writable emits request/response events.
119
+ function parserOnBody(b, start, len) {
120
+ var parser = this;
121
+ var stream = parser.incoming;
122
+
123
+ // if the stream has already been removed, then drop it.
124
+ if (!stream)
125
+ return;
126
+
127
+ var socket = stream.socket;
128
+
129
+ // pretend this was the result of a stream._read call.
130
+ if (len > 0 && !stream._dumped) {
131
+ var slice = b.slice(start, start + len);
132
+ var ret = stream.push(slice);
133
+ if (!ret)
134
+ readStop(socket);
135
+ }
136
+ }
137
+
138
+ function parserOnMessageComplete() {
139
+ var parser = this;
140
+ var stream = parser.incoming;
141
+
142
+ if (stream) {
143
+ stream.complete = true;
144
+ // Emit any trailing headers.
145
+ var headers = parser._headers;
146
+ if (headers) {
147
+ parser.incoming._addHeaderLines(headers, headers.length);
148
+ parser._headers = [];
149
+ parser._url = '';
150
+ }
151
+
152
+ if (!stream.upgrade)
153
+ // For upgraded connections, also emit this after parser.execute
154
+ stream.push(null);
155
+ }
156
+
157
+ if (stream && !parser.incoming._pendings.length) {
158
+ // For emit end event
159
+ stream.push(null);
160
+ }
161
+
162
+ // force to read the next incoming message
163
+ readStart(parser.socket);
164
+ }
165
+
166
+
167
+ var parsers = new FreeList('parsers', 1000, function() {
168
+ var parser = new HTTPParser(HTTPParser.REQUEST);
169
+
170
+ parser._headers = [];
171
+ parser._url = '';
172
+
173
+ // Only called in the slow case where slow means
174
+ // that the request headers were either fragmented
175
+ // across multiple TCP packets or too large to be
176
+ // processed in a single run. This method is also
177
+ // called to process trailing HTTP headers.
178
+ parser[kOnHeaders] = parserOnHeaders;
179
+ parser[kOnHeadersComplete] = parserOnHeadersComplete;
180
+ parser[kOnBody] = parserOnBody;
181
+ parser[kOnMessageComplete] = parserOnMessageComplete;
182
+
183
+ return parser;
184
+ });
185
+ exports.parsers = parsers;
186
+
187
+
188
+ // Free the parser and also break any links that it
189
+ // might have to any other things.
190
+ // TODO: All parser data should be attached to a
191
+ // single object, so that it can be easily cleaned
192
+ // up by doing `parser.data = {}`, which should
193
+ // be done in FreeList.free. `parsers.free(parser)`
194
+ // should be all that is needed.
195
+ function freeParser(parser, req) {
196
+ if (parser) {
197
+ parser._headers = [];
198
+ parser.onIncoming = null;
199
+ if (parser.socket)
200
+ parser.socket.parser = null;
201
+ parser.socket = null;
202
+ parser.incoming = null;
203
+ parsers.free(parser);
204
+ parser = null;
205
+ }
206
+ if (req) {
207
+ req.parser = null;
208
+ }
209
+ }
210
+ exports.freeParser = freeParser;
211
+
212
+
213
+ function ondrain() {
214
+ if (this._httpMessage) this._httpMessage.emit('drain');
215
+ }
216
+
217
+
218
+ function httpSocketSetup(socket) {
219
+ socket.removeListener('drain', ondrain);
220
+ socket.on('drain', ondrain);
221
+ }
222
+ exports.httpSocketSetup = httpSocketSetup;
@@ -0,0 +1,194 @@
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 Stream = require('stream');
24
+
25
+ function readStart(socket) {
26
+ if (socket && !socket._paused && socket.readable)
27
+ socket.resume();
28
+ }
29
+ exports.readStart = readStart;
30
+
31
+ function readStop(socket) {
32
+ if (socket)
33
+ socket.pause();
34
+ }
35
+ exports.readStop = readStop;
36
+
37
+
38
+ /* Abstract base class for ServerRequest and ClientResponse. */
39
+ function IncomingMessage(socket) {
40
+ Stream.Readable.call(this);
41
+
42
+ // XXX This implementation is kind of all over the place
43
+ // When the parser emits body chunks, they go in this list.
44
+ // _read() pulls them out, and when it finds EOF, it ends.
45
+
46
+ this.socket = socket;
47
+ this.connection = socket;
48
+
49
+ this.httpVersion = null;
50
+ this.complete = false;
51
+ this.headers = {};
52
+ this.rawHeaders = [];
53
+ this.trailers = {};
54
+ this.rawTrailers = [];
55
+
56
+ this.readable = true;
57
+
58
+ this._pendings = [];
59
+ this._pendingIndex = 0;
60
+
61
+ // request (server) only
62
+ this.url = '';
63
+ this.method = null;
64
+
65
+ // response (client) only
66
+ this.statusCode = null;
67
+ this.statusMessage = null;
68
+ this.client = this.socket;
69
+
70
+ // flag for backwards compatibility grossness.
71
+ this._consuming = false;
72
+
73
+ // flag for when we decide that this message cannot possibly be
74
+ // read by the user, so there's no point continuing to handle it.
75
+ this._dumped = false;
76
+ }
77
+ util.inherits(IncomingMessage, Stream.Readable);
78
+
79
+
80
+ exports.IncomingMessage = IncomingMessage;
81
+
82
+
83
+ IncomingMessage.prototype.setTimeout = function(msecs, callback) {
84
+ if (callback)
85
+ this.on('timeout', callback);
86
+ this.socket.setTimeout(msecs);
87
+ };
88
+
89
+
90
+ IncomingMessage.prototype.read = function(n) {
91
+ this._consuming = true;
92
+ this.read = Stream.Readable.prototype.read;
93
+ return this.read(n);
94
+ };
95
+
96
+
97
+ IncomingMessage.prototype._read = function(n) {
98
+ // We actually do almost nothing here, because the parserOnBody
99
+ // function fills up our internal buffer directly. However, we
100
+ // do need to unpause the underlying socket so that it flows.
101
+ if (!this.socket.readable)
102
+ this.push(null);
103
+ else
104
+ readStart(this.socket);
105
+ };
106
+
107
+
108
+ // It's possible that the socket will be destroyed, and removed from
109
+ // any messages, before ever calling this. In that case, just skip
110
+ // it, since something else is destroying this connection anyway.
111
+ IncomingMessage.prototype.destroy = function(error) {
112
+ if (this.socket)
113
+ this.socket.destroy(error);
114
+ };
115
+
116
+
117
+ IncomingMessage.prototype._addHeaderLines = function(headers, n) {
118
+ if (headers && headers.length) {
119
+ var raw, dest;
120
+ if (this.complete) {
121
+ raw = this.rawTrailers;
122
+ dest = this.trailers;
123
+ } else {
124
+ raw = this.rawHeaders;
125
+ dest = this.headers;
126
+ }
127
+ raw.push.apply(raw, headers);
128
+
129
+ for (var i = 0; i < n; i += 2) {
130
+ var k = headers[i];
131
+ var v = headers[i + 1];
132
+ this._addHeaderLine(k, v, dest);
133
+ }
134
+ }
135
+ };
136
+
137
+
138
+ // Add the given (field, value) pair to the message
139
+ //
140
+ // Per RFC2616, section 4.2 it is acceptable to join multiple instances of the
141
+ // same header with a ', ' if the header in question supports specification of
142
+ // multiple values this way. If not, we declare the first instance the winner
143
+ // and drop the second. Extended header fields (those beginning with 'x-') are
144
+ // always joined.
145
+ IncomingMessage.prototype._addHeaderLine = function(field, value, dest) {
146
+ field = field.toLowerCase();
147
+ switch (field) {
148
+ // Array headers:
149
+ case 'set-cookie':
150
+ if (!util.isUndefined(dest[field])) {
151
+ dest[field].push(value);
152
+ } else {
153
+ dest[field] = [value];
154
+ }
155
+ break;
156
+
157
+ // list is taken from:
158
+ // https://mxr.mozilla.org/mozilla/source/netwerk/protocol/http/src/nsHttpHeaderArray.cpp
159
+ case 'content-type':
160
+ case 'content-length':
161
+ case 'user-agent':
162
+ case 'referer':
163
+ case 'host':
164
+ case 'authorization':
165
+ case 'proxy-authorization':
166
+ case 'if-modified-since':
167
+ case 'if-unmodified-since':
168
+ case 'from':
169
+ case 'location':
170
+ case 'max-forwards':
171
+ // drop duplicates
172
+ if (util.isUndefined(dest[field]))
173
+ dest[field] = value;
174
+ break;
175
+
176
+ default:
177
+ // make comma-separated list
178
+ if (!util.isUndefined(dest[field]))
179
+ dest[field] += ', ' + value;
180
+ else {
181
+ dest[field] = value;
182
+ }
183
+ }
184
+ };
185
+
186
+
187
+ // Call this instead of resume() if we want to just
188
+ // dump all the data to /dev/null
189
+ IncomingMessage.prototype._dump = function() {
190
+ if (!this._dumped) {
191
+ this._dumped = true;
192
+ this.resume();
193
+ }
194
+ };
@@ -0,0 +1,597 @@
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 assert = require('assert').ok;
23
+ var Stream = require('stream');
24
+ var timers = require('timers');
25
+ var util = require('util');
26
+
27
+ var common = require('_http_common');
28
+
29
+ var CRLF = common.CRLF;
30
+ var chunkExpression = common.chunkExpression;
31
+ var debug = common.debug;
32
+
33
+
34
+ var connectionExpression = /Connection/i;
35
+ var transferEncodingExpression = /Transfer-Encoding/i;
36
+ var closeExpression = /close/i;
37
+ var contentLengthExpression = /Content-Length/i;
38
+ var dateExpression = /Date/i;
39
+ var expectExpression = /Expect/i;
40
+
41
+ var automaticHeaders = {
42
+ connection: true,
43
+ 'content-length': true,
44
+ 'transfer-encoding': true,
45
+ date: true
46
+ };
47
+
48
+
49
+ var dateCache;
50
+ function utcDate() {
51
+ if (!dateCache) {
52
+ var d = new Date();
53
+ dateCache = d.toUTCString();
54
+ timers.enroll(utcDate, 1000 - d.getMilliseconds());
55
+ timers._unrefActive(utcDate);
56
+ }
57
+ return dateCache;
58
+ }
59
+ utcDate._onTimeout = function() {
60
+ dateCache = undefined;
61
+ };
62
+
63
+
64
+ function OutgoingMessage() {
65
+ Stream.call(this);
66
+
67
+ this.output = [];
68
+ this.outputEncodings = [];
69
+ this.outputCallbacks = [];
70
+
71
+ this.writable = true;
72
+
73
+ this._last = false;
74
+ this.chunkedEncoding = false;
75
+ this.shouldKeepAlive = true;
76
+ this.useChunkedEncodingByDefault = true;
77
+ this.sendDate = false;
78
+ this._removedHeader = {};
79
+
80
+ this._hasBody = true;
81
+ this._trailer = '';
82
+
83
+ this.finished = false;
84
+ this._hangupClose = false;
85
+
86
+ this.socket = null;
87
+ this.connection = null;
88
+ }
89
+ util.inherits(OutgoingMessage, Stream);
90
+
91
+
92
+ exports.OutgoingMessage = OutgoingMessage;
93
+
94
+
95
+ OutgoingMessage.prototype.setTimeout = function(msecs, callback) {
96
+ if (callback)
97
+ this.on('timeout', callback);
98
+ if (!this.socket) {
99
+ this.once('socket', function(socket) {
100
+ socket.setTimeout(msecs);
101
+ });
102
+ } else
103
+ this.socket.setTimeout(msecs);
104
+ };
105
+
106
+
107
+ // It's possible that the socket will be destroyed, and removed from
108
+ // any messages, before ever calling this. In that case, just skip
109
+ // it, since something else is destroying this connection anyway.
110
+ OutgoingMessage.prototype.destroy = function(error) {
111
+ if (this.socket)
112
+ this.socket.destroy(error);
113
+ else
114
+ this.once('socket', function(socket) {
115
+ socket.destroy(error);
116
+ });
117
+ };
118
+
119
+
120
+ // This abstract either writing directly to the socket or buffering it.
121
+ OutgoingMessage.prototype._send = function(data, encoding, callback) {
122
+ // This is a shameful hack to get the headers and first body chunk onto
123
+ // the same packet. Future versions of Node are going to take care of
124
+ // this at a lower level and in a more general way.
125
+ if (!this._headerSent) {
126
+ if (util.isString(data) &&
127
+ encoding !== 'hex' &&
128
+ encoding !== 'base64') {
129
+ data = this._header + data;
130
+ } else {
131
+ this.output.unshift(this._header);
132
+ this.outputEncodings.unshift('binary');
133
+ this.outputCallbacks.unshift(null);
134
+ }
135
+ this._headerSent = true;
136
+ }
137
+ return this._writeRaw(data, encoding, callback);
138
+ };
139
+
140
+
141
+ OutgoingMessage.prototype._writeRaw = function(data, encoding, callback) {
142
+ if (util.isFunction(encoding)) {
143
+ callback = encoding;
144
+ encoding = null;
145
+ }
146
+
147
+ if (data.length === 0) {
148
+ if (util.isFunction(callback))
149
+ process.nextTick(callback);
150
+ return true;
151
+ }
152
+
153
+ if (this.connection &&
154
+ this.connection._httpMessage === this &&
155
+ this.connection.writable &&
156
+ !this.connection.destroyed) {
157
+ // There might be pending data in the this.output buffer.
158
+ while (this.output.length) {
159
+ if (!this.connection.writable) {
160
+ this._buffer(data, encoding, callback);
161
+ return false;
162
+ }
163
+ var c = this.output.shift();
164
+ var e = this.outputEncodings.shift();
165
+ var cb = this.outputCallbacks.shift();
166
+ this.connection.write(c, e, cb);
167
+ }
168
+
169
+ // Directly write to socket.
170
+ return this.connection.write(data, encoding, callback);
171
+ } else if (this.connection && this.connection.destroyed) {
172
+ // The socket was destroyed. If we're still trying to write to it,
173
+ // then we haven't gotten the 'close' event yet.
174
+ return false;
175
+ } else {
176
+ // buffer, as long as we're not destroyed.
177
+ this._buffer(data, encoding, callback);
178
+ return false;
179
+ }
180
+ };
181
+
182
+
183
+ OutgoingMessage.prototype._buffer = function(data, encoding, callback) {
184
+ this.output.push(data);
185
+ this.outputEncodings.push(encoding);
186
+ this.outputCallbacks.push(callback);
187
+ return false;
188
+ };
189
+
190
+
191
+ OutgoingMessage.prototype._storeHeader = function(firstLine, headers) {
192
+ // firstLine in the case of request is: 'GET /index.html HTTP/1.1\r\n'
193
+ // in the case of response it is: 'HTTP/1.1 200 OK\r\n'
194
+ var state = {
195
+ sentConnectionHeader: false,
196
+ sentContentLengthHeader: false,
197
+ sentTransferEncodingHeader: false,
198
+ sentDateHeader: false,
199
+ sentExpect: false,
200
+ messageHeader: firstLine
201
+ };
202
+
203
+ var field, value;
204
+
205
+ if (headers) {
206
+ var keys = Object.keys(headers);
207
+ var isArray = util.isArray(headers);
208
+ var field, value;
209
+
210
+ for (var i = 0, l = keys.length; i < l; i++) {
211
+ var key = keys[i];
212
+ if (isArray) {
213
+ field = headers[key][0];
214
+ value = headers[key][1];
215
+ } else {
216
+ field = key;
217
+ value = headers[key];
218
+ }
219
+
220
+ if (util.isArray(value)) {
221
+ for (var j = 0; j < value.length; j++) {
222
+ storeHeader(this, state, field, value[j]);
223
+ }
224
+ } else {
225
+ storeHeader(this, state, field, value);
226
+ }
227
+ }
228
+ }
229
+
230
+ // Date header
231
+ if (this.sendDate == true && state.sentDateHeader == false) {
232
+ state.messageHeader += 'Date: ' + utcDate() + CRLF;
233
+ }
234
+
235
+ // Force the connection to close when the response is a 204 No Content or
236
+ // a 304 Not Modified and the user has set a "Transfer-Encoding: chunked"
237
+ // header.
238
+ //
239
+ // RFC 2616 mandates that 204 and 304 responses MUST NOT have a body but
240
+ // node.js used to send out a zero chunk anyway to accommodate clients
241
+ // that don't have special handling for those responses.
242
+ //
243
+ // It was pointed out that this might confuse reverse proxies to the point
244
+ // of creating security liabilities, so suppress the zero chunk and force
245
+ // the connection to close.
246
+ var statusCode = this.statusCode;
247
+ if ((statusCode == 204 || statusCode === 304) &&
248
+ this.chunkedEncoding === true) {
249
+ debug(statusCode + ' response should not use chunked encoding,' +
250
+ ' closing connection.');
251
+ this.chunkedEncoding = false;
252
+ this.shouldKeepAlive = false;
253
+ }
254
+
255
+ // keep-alive logic
256
+ if (this._removedHeader.connection) {
257
+ this._last = true;
258
+ this.shouldKeepAlive = false;
259
+ } else if (state.sentConnectionHeader === false) {
260
+ var shouldSendKeepAlive = this.shouldKeepAlive &&
261
+ (state.sentContentLengthHeader ||
262
+ this.useChunkedEncodingByDefault ||
263
+ this.agent);
264
+ if (shouldSendKeepAlive) {
265
+ state.messageHeader += 'Connection: keep-alive\r\n';
266
+ } else {
267
+ this._last = true;
268
+ state.messageHeader += 'Connection: close\r\n';
269
+ }
270
+ }
271
+
272
+ if (state.sentContentLengthHeader == false &&
273
+ state.sentTransferEncodingHeader == false) {
274
+ if (this._hasBody && !this._removedHeader['transfer-encoding']) {
275
+ if (this.useChunkedEncodingByDefault) {
276
+ state.messageHeader += 'Transfer-Encoding: chunked\r\n';
277
+ this.chunkedEncoding = true;
278
+ } else {
279
+ this._last = true;
280
+ }
281
+ } else {
282
+ // Make sure we don't end the 0\r\n\r\n at the end of the message.
283
+ this.chunkedEncoding = false;
284
+ }
285
+ }
286
+
287
+ this._header = state.messageHeader + CRLF;
288
+ this._headerSent = false;
289
+
290
+ // wait until the first body chunk, or close(), is sent to flush,
291
+ // UNLESS we're sending Expect: 100-continue.
292
+ if (state.sentExpect) this._send('');
293
+ };
294
+
295
+ function storeHeader(self, state, field, value) {
296
+ // Protect against response splitting. The if statement is there to
297
+ // minimize the performance impact in the common case.
298
+ if (/[\r\n]/.test(value))
299
+ value = value.replace(/[\r\n]+[ \t]*/g, '');
300
+
301
+ state.messageHeader += field + ': ' + value + CRLF;
302
+
303
+ if (connectionExpression.test(field)) {
304
+ state.sentConnectionHeader = true;
305
+ if (closeExpression.test(value)) {
306
+ self._last = true;
307
+ } else {
308
+ self.shouldKeepAlive = true;
309
+ }
310
+
311
+ } else if (transferEncodingExpression.test(field)) {
312
+ state.sentTransferEncodingHeader = true;
313
+ if (chunkExpression.test(value)) self.chunkedEncoding = true;
314
+
315
+ } else if (contentLengthExpression.test(field)) {
316
+ state.sentContentLengthHeader = true;
317
+ } else if (dateExpression.test(field)) {
318
+ state.sentDateHeader = true;
319
+ } else if (expectExpression.test(field)) {
320
+ state.sentExpect = true;
321
+ }
322
+ }
323
+
324
+
325
+ OutgoingMessage.prototype.setHeader = function(name, value) {
326
+ if (arguments.length < 2) {
327
+ throw new Error('`name` and `value` are required for setHeader().');
328
+ }
329
+
330
+ if (this._header) {
331
+ throw new Error('Can\'t set headers after they are sent.');
332
+ }
333
+
334
+ var key = name.toLowerCase();
335
+ this._headers = this._headers || {};
336
+ this._headerNames = this._headerNames || {};
337
+ this._headers[key] = value;
338
+ this._headerNames[key] = name;
339
+
340
+ if (automaticHeaders[key]) {
341
+ this._removedHeader[key] = false;
342
+ }
343
+ };
344
+
345
+
346
+ OutgoingMessage.prototype.getHeader = function(name) {
347
+ if (arguments.length < 1) {
348
+ throw new Error('`name` is required for getHeader().');
349
+ }
350
+
351
+ if (!this._headers) return;
352
+
353
+ var key = name.toLowerCase();
354
+ return this._headers[key];
355
+ };
356
+
357
+
358
+ OutgoingMessage.prototype.removeHeader = function(name) {
359
+ if (arguments.length < 1) {
360
+ throw new Error('`name` is required for removeHeader().');
361
+ }
362
+
363
+ if (this._header) {
364
+ throw new Error('Can\'t remove headers after they are sent.');
365
+ }
366
+
367
+ var key = name.toLowerCase();
368
+
369
+ if (key === 'date')
370
+ this.sendDate = false;
371
+ else if (automaticHeaders[key])
372
+ this._removedHeader[key] = true;
373
+
374
+ if (this._headers) {
375
+ delete this._headers[key];
376
+ delete this._headerNames[key];
377
+ }
378
+ };
379
+
380
+
381
+ OutgoingMessage.prototype._renderHeaders = function() {
382
+ if (this._header) {
383
+ throw new Error('Can\'t render headers after they are sent to the client.');
384
+ }
385
+
386
+ if (!this._headers) return {};
387
+
388
+ var headers = {};
389
+ var keys = Object.keys(this._headers);
390
+ for (var i = 0, l = keys.length; i < l; i++) {
391
+ var key = keys[i];
392
+ headers[this._headerNames[key]] = this._headers[key];
393
+ }
394
+ return headers;
395
+ };
396
+
397
+
398
+ Object.defineProperty(OutgoingMessage.prototype, 'headersSent', {
399
+ configurable: true,
400
+ enumerable: true,
401
+ get: function() { return !!this._header; }
402
+ });
403
+
404
+
405
+ OutgoingMessage.prototype.write = function(chunk, encoding, callback) {
406
+ if (!this._header) {
407
+ this._implicitHeader();
408
+ }
409
+
410
+ if (!this._hasBody) {
411
+ debug('This type of response MUST NOT have a body. ' +
412
+ 'Ignoring write() calls.');
413
+ return true;
414
+ }
415
+
416
+ if (!util.isString(chunk) && !util.isBuffer(chunk)) {
417
+ throw new TypeError('first argument must be a string or Buffer');
418
+ }
419
+
420
+
421
+ // If we get an empty string or buffer, then just do nothing, and
422
+ // signal the user to keep writing.
423
+ if (chunk.length === 0) return true;
424
+
425
+ var len, ret;
426
+ if (this.chunkedEncoding) {
427
+ if (util.isString(chunk) &&
428
+ encoding !== 'hex' &&
429
+ encoding !== 'base64' &&
430
+ encoding !== 'binary') {
431
+ len = Buffer.byteLength(chunk, encoding);
432
+ chunk = len.toString(16) + CRLF + chunk + CRLF;
433
+ ret = this._send(chunk, encoding, callback);
434
+ } else {
435
+ // buffer, or a non-toString-friendly encoding
436
+ if (util.isString(chunk))
437
+ len = Buffer.byteLength(chunk, encoding);
438
+ else
439
+ len = chunk.length;
440
+
441
+ if (this.connection && !this.connection.corked) {
442
+ this.connection.cork();
443
+ var conn = this.connection;
444
+ process.nextTick(function connectionCork() {
445
+ if (conn)
446
+ conn.uncork();
447
+ });
448
+ }
449
+ this._send(len.toString(16), 'binary', null);
450
+ this._send(crlf_buf, null, null);
451
+ this._send(chunk, encoding, null);
452
+ ret = this._send(crlf_buf, null, callback);
453
+ }
454
+ } else {
455
+ ret = this._send(chunk, encoding, callback);
456
+ }
457
+
458
+ debug('write ret = ' + ret);
459
+ return ret;
460
+ };
461
+
462
+
463
+ OutgoingMessage.prototype.addTrailers = function(headers) {
464
+ this._trailer = '';
465
+ var keys = Object.keys(headers);
466
+ var isArray = util.isArray(headers);
467
+ var field, value;
468
+ for (var i = 0, l = keys.length; i < l; i++) {
469
+ var key = keys[i];
470
+ if (isArray) {
471
+ field = headers[key][0];
472
+ value = headers[key][1];
473
+ } else {
474
+ field = key;
475
+ value = headers[key];
476
+ }
477
+
478
+ this._trailer += field + ': ' + value + CRLF;
479
+ }
480
+ };
481
+
482
+
483
+ var crlf_buf = new Buffer('\r\n');
484
+
485
+
486
+ OutgoingMessage.prototype.end = function(data, encoding, callback) {
487
+ if (util.isFunction(data)) {
488
+ callback = data;
489
+ data = null;
490
+ } else if (util.isFunction(encoding)) {
491
+ callback = encoding;
492
+ encoding = null;
493
+ }
494
+
495
+ if (data && !util.isString(data) && !util.isBuffer(data)) {
496
+ throw new TypeError('first argument must be a string or Buffer');
497
+ }
498
+
499
+ if (this.finished) {
500
+ return false;
501
+ }
502
+
503
+ var self = this;
504
+ function finish() {
505
+ self.emit('finish');
506
+ }
507
+
508
+ if (util.isFunction(callback))
509
+ this.once('finish', callback);
510
+
511
+
512
+ if (!this._header) {
513
+ this._implicitHeader();
514
+ }
515
+
516
+ if (data && !this._hasBody) {
517
+ debug('This type of response MUST NOT have a body. ' +
518
+ 'Ignoring data passed to end().');
519
+ data = null;
520
+ }
521
+
522
+ if (this.connection && data)
523
+ this.connection.cork();
524
+
525
+ var ret;
526
+ if (data) {
527
+ // Normal body write.
528
+ ret = this.write(data, encoding);
529
+ }
530
+
531
+ if (this.chunkedEncoding) {
532
+ ret = this._send('0\r\n' + this._trailer + '\r\n', 'binary', finish);
533
+ } else {
534
+ // Force a flush, HACK.
535
+ ret = this._send('', 'binary', finish);
536
+ }
537
+
538
+ if (this.connection && data)
539
+ this.connection.uncork();
540
+
541
+ this.finished = true;
542
+
543
+ // There is the first message on the outgoing queue, and we've sent
544
+ // everything to the socket.
545
+ debug('outgoing message end.');
546
+ if (this.output.length === 0 && this.connection._httpMessage === this) {
547
+ this._finish();
548
+ }
549
+
550
+ return ret;
551
+ };
552
+
553
+
554
+ OutgoingMessage.prototype._finish = function() {
555
+ assert(this.connection);
556
+ this.emit('prefinish');
557
+ };
558
+
559
+
560
+ // This logic is probably a bit confusing. Let me explain a bit:
561
+ //
562
+ // In both HTTP servers and clients it is possible to queue up several
563
+ // outgoing messages. This is easiest to imagine in the case of a client.
564
+ // Take the following situation:
565
+ //
566
+ // req1 = client.request('GET', '/');
567
+ // req2 = client.request('POST', '/');
568
+ //
569
+ // When the user does
570
+ //
571
+ // req2.write('hello world\n');
572
+ //
573
+ // it's possible that the first request has not been completely flushed to
574
+ // the socket yet. Thus the outgoing messages need to be prepared to queue
575
+ // up data internally before sending it on further to the socket's queue.
576
+ //
577
+ // This function, outgoingFlush(), is called by both the Server and Client
578
+ // to attempt to flush any pending messages out to the socket.
579
+ OutgoingMessage.prototype._flush = function() {
580
+ if (this.socket && this.socket.writable) {
581
+ var ret;
582
+ while (this.output.length) {
583
+ var data = this.output.shift();
584
+ var encoding = this.outputEncodings.shift();
585
+ var cb = this.outputCallbacks.shift();
586
+ ret = this.socket.write(data, encoding, cb);
587
+ }
588
+
589
+ if (this.finished) {
590
+ // This is a queue to the server or client to bring in the next this.
591
+ this._finish();
592
+ } else if (ret) {
593
+ // This is necessary to prevent https from breaking
594
+ this.emit('drain');
595
+ }
596
+ }
597
+ };