Merge branch 'master' into net2
This commit is contained in:
Коммит
dc01587c6c
21
ChangeLog
21
ChangeLog
|
@ -1,4 +1,23 @@
|
|||
2010.02.03, Version 0.1.27
|
||||
2010.02.09, Version 0.1.28
|
||||
|
||||
* Use Google's jsmin.py which can be used for evil.
|
||||
|
||||
* Add posix.truncate()
|
||||
|
||||
* Throw errors from server.listen()
|
||||
|
||||
* stdio bugfix (test by Mikeal Rogers)
|
||||
|
||||
* Module system refactor (Felix Geisendörfer, Blaine Cook)
|
||||
|
||||
* Add process.setuid(), getuid() (Michael Carter)
|
||||
|
||||
* sys.inspect refactor (Tim Caswell)
|
||||
|
||||
* Multipart library rewrite (isaacs)
|
||||
|
||||
|
||||
2010.02.03, Version 0.1.27, 0cfa789cc530848725a8cb5595224e78ae7b9dd0
|
||||
|
||||
* Implemented __dirname (Felix Geisendörfer)
|
||||
|
||||
|
|
3
LICENSE
3
LICENSE
|
@ -9,9 +9,6 @@ are:
|
|||
This code is copyrighted by Marc Alexander Lehmann. Both are dually
|
||||
licensed under MIT and GPL2.
|
||||
|
||||
- JSMin JavaScript minifier, located at tools/jsmin.py. This code is
|
||||
copyrighted by Douglas Crockford and Baruch Even and has an MIT license.
|
||||
|
||||
- WAF build system, located at tools/waf. Copyrighted Thomas Nagy.
|
||||
Released under an MIT license.
|
||||
|
||||
|
|
205
doc/api.txt
205
doc/api.txt
|
@ -1,7 +1,7 @@
|
|||
NODE(1)
|
||||
=======
|
||||
Ryan Dahl <ry@tinyclouds.org>
|
||||
Version, 0.1.27, 2010.02.03
|
||||
Version, 0.1.28, 2010.02.09
|
||||
|
||||
|
||||
== NAME
|
||||
|
@ -136,6 +136,9 @@ success code 0.
|
|||
+process.cwd()+::
|
||||
Returns the current working directory of the process.
|
||||
|
||||
+process.getuid(), process.setuid(id)+::
|
||||
Gets/sets the user identity of the process. (See setuid(2).)
|
||||
|
||||
+process.chdir(directory)+::
|
||||
Changes the current working directory of the process.
|
||||
|
||||
|
@ -205,8 +208,8 @@ Like +puts()+ but without the trailing new-line.
|
|||
A synchronous output function. Will block the process and
|
||||
output the string immediately to stdout.
|
||||
|
||||
+inspect(object)+ ::
|
||||
Return a string representation of the +object+. (For debugging.)
|
||||
+inspect(object, showHidden)+ ::
|
||||
Return a string representation of the +object+. (For debugging.) If showHidden is true, then the object's non-enumerable properties will be shown too.
|
||||
|
||||
+exec(command)+::
|
||||
Executes the command as a child process, buffers the output and returns it
|
||||
|
@ -653,6 +656,11 @@ sys.puts("stats: " + JSON.stringify(stats));
|
|||
- on success: no parameters.
|
||||
- on error: no parameters.
|
||||
|
||||
+posix.truncate(fd, len)+ ::
|
||||
See ftruncate(2).
|
||||
- on success: no parameters.
|
||||
- on error: no parameters.
|
||||
|
||||
|
||||
+posix.stat(path)+ ::
|
||||
See stat(2).
|
||||
|
@ -1176,73 +1184,158 @@ After emitted no other events will be emitted on the response.
|
|||
|
||||
=== Multipart Parsing
|
||||
|
||||
A library to parse HTTP requests with +multipart/form-data+ is included with
|
||||
A library to parse +multipart+ internet messages is included with
|
||||
Node. To use it, +require("multipart")+.
|
||||
|
||||
+multipart.parse(options)+ ::
|
||||
- on success: Returns an object where each key holds the value of one part of
|
||||
the stream. +options+ can either be an instance of
|
||||
+http.ServerRequest+ or an object containing a "boundary" and
|
||||
an optional "data" key.
|
||||
- on error: Returns an instanceof Error object. Right now only the request
|
||||
content-type / boundary option is checked. The stream data itself
|
||||
is not validated.
|
||||
+multipart.parse(message)+ ::
|
||||
Returns a multipart.Stream wrapper around a streaming message.
|
||||
The message must contain a `headers` member, and may be either an
|
||||
HTTP request object or a JSGI-style request object with either a
|
||||
forEachable or String body.
|
||||
+
|
||||
See the Stream class below.
|
||||
|
||||
+multipart.cat(message)+ ::
|
||||
Returns a promise.
|
||||
- on success: Returns a multipart.Stream object representing the completed
|
||||
message. The body of each part is saved on the `body` member.
|
||||
- on error: Returns an instanceof Error object. This indicates
|
||||
that the message was malformed in some way.
|
||||
+
|
||||
*Note*: This function saves the *entire* message into memory. As such,
|
||||
it is ill-suited to parsing actual incoming messages from an HTTP request!
|
||||
If a user uploads a very large file, then it may cause serious problems.
|
||||
No checking is done to ensure that the file does not overload the memory.
|
||||
Only use multipart.cat with known and trusted input!
|
||||
|
||||
==== +multipart.Stream+
|
||||
|
||||
The multipart.Stream class is a streaming parser wrapped around a message.
|
||||
The Stream also contains the properties described for the +part+ objects below,
|
||||
and is a reference to the top-level message.
|
||||
|
||||
===== Events
|
||||
|
||||
[cols="1,2,10",options="header"]
|
||||
|=========================================================
|
||||
|Event | Parameters | Notes
|
||||
|+"partBegin"+ | +part+ | Emitted when a new part is found in the stream.
|
||||
+part+ is a +part object+, described below.
|
||||
|+"partEnd"+ | +part+ | Emitted when a part is done.
|
||||
|+"body"+ | +chunk+ | Emitted when a chunk of the body is read.
|
||||
|+"complete"+ | | Emitted when the end of the stream is reached.
|
||||
|+"error"+ | +error+ | Emitted when a parse error is encountered. This
|
||||
indicates that the message is malformed.
|
||||
|=========================================================
|
||||
|
||||
===== Properties
|
||||
|
||||
+stream.part+::
|
||||
The current part being processed. This is important, for instance, when responding
|
||||
to the +body+ event.
|
||||
|
||||
+stream.isMultiPart+::
|
||||
True if the stream is a multipart message. Generally this will be true, but non-multipart
|
||||
messages will behave the same as a multipart message with a single part, and +isMultiPart+
|
||||
will be set to +false+.
|
||||
|
||||
+stream.parts+::
|
||||
An array of the parts contained within the message. Each is a +part+ object.
|
||||
|
||||
===== Methods
|
||||
|
||||
+stream.pause+::
|
||||
If the underlying message supports pause and resume, then this will pause the stream.
|
||||
|
||||
+stream.resume+::
|
||||
If the underlying message supports pause and resume, then this will resume the paused stream.
|
||||
|
||||
==== Part Objects
|
||||
|
||||
As it parses the message, the Stream object will create +Part+ objects.
|
||||
|
||||
===== Properties
|
||||
|
||||
+part.parent+::
|
||||
The message that contains this part.
|
||||
|
||||
+part.headers+::
|
||||
The headers object for this message.
|
||||
|
||||
+part.filename+::
|
||||
The filename, if specified in the +content-disposition+ or +content-type+ header.
|
||||
For uploads, downloads, and attachments, this is the intended filename for the
|
||||
attached file.
|
||||
|
||||
+part.name+::
|
||||
The name, if specified in the +content-disposition+ or +content-type+ header. For
|
||||
+multipart/form-data+ messages, this is the name of the field that was posted, and the
|
||||
body specifies the value.
|
||||
|
||||
+part.isMultiPart+::
|
||||
True if this part is a multipart message.
|
||||
|
||||
+part.parts+::
|
||||
Array of children contained within a multipart message, or falsey.
|
||||
|
||||
+part.boundary+::
|
||||
For multipart messages, this is the boundary that separates subparts.
|
||||
|
||||
+part.type+::
|
||||
For multipart messages, this is the multipart type specified in the +content-type+ header.
|
||||
For example, a message with +content-type: multipart/form-data+ will have a +type+
|
||||
property of +form-data+.
|
||||
|
||||
==== Example
|
||||
|
||||
Here is an example for parsing a +multipart/form-data+ request:
|
||||
|
||||
----------------------------------------
|
||||
var multipart = require("multipart");
|
||||
var stream = new multipart.Stream(options);
|
||||
var parts = {};
|
||||
|
||||
stream.addListener("part", function (part) {
|
||||
var buffer = "";
|
||||
|
||||
part.addListener("body", function(chunk) {
|
||||
buffer = buffer + chunk;
|
||||
var multipart = require("multipart"),
|
||||
sys = require("sys"),
|
||||
http = require("http");
|
||||
http.createServer(function (req, res) {
|
||||
var mp = multipart.parse(req),
|
||||
fields = {},
|
||||
name, filename;
|
||||
mp.addListener("error", function (er) {
|
||||
res.sendHeader(400, {"content-type":"text/plain"});
|
||||
res.sendBody("You sent a bad message!\n"+er.message);
|
||||
res.finish();
|
||||
});
|
||||
|
||||
part.addListener("complete", function() {
|
||||
parts[part.name] = buffer;
|
||||
mp.addListener("partBegin", function (part) {
|
||||
name = part.name;
|
||||
filename = part.filename;
|
||||
if (name) fields[name] = "";
|
||||
});
|
||||
});
|
||||
|
||||
stream.addListener("complete", function() {
|
||||
// The parts object now contains all parts and data
|
||||
mp.addListener("body", function (chunk) {
|
||||
if (name) {
|
||||
// just a demo. in reality, you'd probably
|
||||
// want to sniff for base64 encoding, decode,
|
||||
// and write the bytes to a file or something.
|
||||
if (fields[name].length > 1024) return;
|
||||
fields[name] += chunk;
|
||||
}
|
||||
});
|
||||
mp.addListener("complete", function () {
|
||||
var response = "You posted: \n" + sys.inspect(fields);
|
||||
res.sendHeader(200, {
|
||||
"content-type" : "text/plain",
|
||||
"content-length" : response.length
|
||||
});
|
||||
res.sendBody(response);
|
||||
res.finish();
|
||||
})
|
||||
});
|
||||
----------------------------------------
|
||||
|
||||
==== Nested Multipart Messages
|
||||
|
||||
[cols="1,2,10",options="header"]
|
||||
|=========================================================
|
||||
|Event | Parameters | Notes
|
||||
|+"part"+ | +part+ | Emitted when a new part is found in the stream.
|
||||
+part+ is an instance of +multipart.Part+.
|
||||
|+"complete"+ | | Emitted when the end of the stream is reached.
|
||||
|=========================================================
|
||||
|
||||
+stream.bytesTotal+::
|
||||
The amount of bytes this stream is expected to have.
|
||||
|
||||
+stream.bytesReceived+::
|
||||
The amount of bytes received by this stream so far.
|
||||
|
||||
==== +multipart.Part+
|
||||
|
||||
[cols="1,2,10",options="header"]
|
||||
|=========================================================
|
||||
|Event | Parameters | Notes
|
||||
|+"body"+ | +chunk+ | Emitted when a chunk of body is read.
|
||||
|+"complete"+ | | Emitted when the end of the part is reached.
|
||||
|=========================================================
|
||||
|
||||
+part.name+::
|
||||
The field name of this part.
|
||||
|
||||
+part.filename+::
|
||||
The filename of this part. Only set for file uploads.
|
||||
Nested multipart parsing is supported. The +stream.part+ object always refers
|
||||
to the current part. If +part.isMultiPart+ is set, then that part is a
|
||||
multipart message, which contains other parts. You can inspect its +parts+
|
||||
array to see the list of sub-parts, which may also be multipart, and contain
|
||||
sub-parts.
|
||||
|
||||
=== TCP
|
||||
|
||||
|
|
|
@ -97,9 +97,9 @@ server.listen(7000, "localhost");</pre>
|
|||
<a href="http://github.com/ry/node/tree/master">git repo</a>
|
||||
</p>
|
||||
<p>
|
||||
2010.02.03
|
||||
2010.02.09
|
||||
<a
|
||||
href="http://s3.amazonaws.com/four.livejournal/20100203/node-v0.1.27.tar.gz">node-v0.1.27.tar.gz</a>
|
||||
href="http://s3.amazonaws.com/four.livejournal/20100209/node-v0.1.28.tar.gz">node-v0.1.28.tar.gz</a>
|
||||
</p>
|
||||
|
||||
<h2 id="build">Build</h2>
|
||||
|
|
552
lib/multipart.js
552
lib/multipart.js
|
@ -1,194 +1,408 @@
|
|||
var sys = require("sys");
|
||||
var events = require('events');
|
||||
|
||||
exports.parse = function(options) {
|
||||
var promise = new events.Promise();
|
||||
var sys = require("sys"),
|
||||
events = require("events"),
|
||||
wrapExpression = /^[ \t]+/,
|
||||
multipartExpression = new RegExp(
|
||||
"^multipart\/(" +
|
||||
"mixed|rfc822|message|digest|alternative|" +
|
||||
"related|report|signed|encrypted|form-data|" +
|
||||
"x-mixed-replace|byteranges)", "i"),
|
||||
boundaryExpression = /boundary=([^;]+)/i,
|
||||
CR = "\r",
|
||||
LF = "\n",
|
||||
CRLF = CR+LF,
|
||||
MAX_BUFFER_LENGTH = 16 * 1024,
|
||||
|
||||
try {
|
||||
var stream = new exports.Stream(options);
|
||||
} catch (e) {
|
||||
process.nextTick(function() {
|
||||
promise.emitError(e);
|
||||
});
|
||||
return promise;
|
||||
}
|
||||
// parser states.
|
||||
s = 0,
|
||||
S_NEW_PART = s++,
|
||||
S_HEADER = s++,
|
||||
S_BODY = s++;
|
||||
|
||||
var parts = {};
|
||||
stream.addListener('part', function(part) {
|
||||
var name = part.name;
|
||||
var buffer = '';
|
||||
exports.parse = parse;
|
||||
exports.cat = cat;
|
||||
exports.Stream = Stream;
|
||||
|
||||
part.addListener('body', function(chunk) {
|
||||
buffer = buffer + chunk;
|
||||
});
|
||||
|
||||
part.addListener('complete', function() {
|
||||
parts[name] = buffer;
|
||||
});
|
||||
});
|
||||
|
||||
stream.addListener('complete', function() {
|
||||
promise.emitSuccess(parts);
|
||||
});
|
||||
|
||||
return promise;
|
||||
// Parse a streaming message to a stream.
|
||||
// If the message has a "body" and no "addListener", then
|
||||
// just take it in and write() the body.
|
||||
function parse (message) {
|
||||
return new Stream(message);
|
||||
};
|
||||
|
||||
exports.Stream = function(options) {
|
||||
events.EventEmitter.call(this);
|
||||
|
||||
this.init(options);
|
||||
// WARNING: DONT EVER USE THE CAT FUNCTION IN PRODUCTION WEBSITES!!
|
||||
// It works pretty great, and it's a nice test function. But if
|
||||
// you use this function to parse an HTTP request from a live web
|
||||
// site, then you're essentially giving the world permission to
|
||||
// rack up as much memory usage as they can manage. This function
|
||||
// buffers the whole message, which is very convenient, but also
|
||||
// very much the wrong thing to do in most cases.
|
||||
function cat (message) {
|
||||
var p = new (events.Promise),
|
||||
stream = parse(message);
|
||||
stream.files = {};
|
||||
stream.fields = {};
|
||||
stream.addListener("partBegin", function (part) {
|
||||
if (part.filename) stream.files[part.filename] = part;
|
||||
if (part.name) stream.fields[part.name] = part;
|
||||
});
|
||||
stream.addListener("body", function (chunk) {
|
||||
stream.part.body = (stream.part.body || "") + chunk;
|
||||
});
|
||||
stream.addListener("error", function (e) { p.emitError(e) });
|
||||
stream.addListener("complete", function () { p.emitSuccess(stream) });
|
||||
return p;
|
||||
};
|
||||
sys.inherits(exports.Stream, events.EventEmitter);
|
||||
|
||||
|
||||
var proto = exports.Stream.prototype;
|
||||
|
||||
proto.init = function(options) {
|
||||
this.buffer = '';
|
||||
this.bytesReceived = 0;
|
||||
this.bytesTotal = 0;
|
||||
this.part = null;
|
||||
|
||||
if ('headers' in options) {
|
||||
var req = options, contentType = req.headers['content-type'];
|
||||
if (!contentType) {
|
||||
throw new Error('Content-Type header not set');
|
||||
// events:
|
||||
// "partBegin", "partEnd", "body", "complete"
|
||||
// everything emits on the Stream directly.
|
||||
// the stream's "parts" object is a nested collection of the header objects
|
||||
// check the stream's "part" member to know what it's currently chewin on.
|
||||
// this.part.parent refers to that part's containing message (which may be
|
||||
// the stream itself)
|
||||
// child messages inherit their parent's headers
|
||||
// A non-multipart message looks just like a multipart message with a
|
||||
// single part.
|
||||
function Stream (message) {
|
||||
var isMultiPart = multipartHeaders(message, this),
|
||||
w = isMultiPart ? writer(this) : simpleWriter(this),
|
||||
e = ender(this);
|
||||
if (message.addListener) {
|
||||
message.addListener("body", w);
|
||||
message.addListener("complete", e);
|
||||
if (message.pause && message.resume) {
|
||||
this._pause = message;
|
||||
}
|
||||
|
||||
if (!contentType.match(/^multipart\/form-data/i)) {
|
||||
throw new Error('Content-Type is not multipart: "'+contentType+'"');
|
||||
}
|
||||
|
||||
var boundary = contentType.match(/boundary=([^;]+)/i)
|
||||
if (!boundary) {
|
||||
throw new Error('No boundary in Content-Type header: "'+contentType+'"');
|
||||
}
|
||||
|
||||
this.boundary = '--'+boundary[1];
|
||||
this.bytesTotal = req.headers['content-length'];
|
||||
|
||||
} else if (message.body) {
|
||||
var self = this;
|
||||
req
|
||||
.addListener('body', function(chunk) {
|
||||
self.write(chunk);
|
||||
})
|
||||
.addListener('complete', function() {
|
||||
self.emit('complete');
|
||||
});
|
||||
} else {
|
||||
if (!options.boundary) {
|
||||
throw new Error('No boundary option given');
|
||||
if (message.body.pause && message.body.resume) {
|
||||
this._pause = message.body;
|
||||
}
|
||||
|
||||
this.boundary = options.boundary;
|
||||
this.write(options.data || '');
|
||||
if (message.body.addListener) {
|
||||
message.body.addListener("data", w);
|
||||
message.body.addListener("end", e);
|
||||
} if (message.body.forEach) {
|
||||
var p = message.body.forEach(w);
|
||||
if (p && p.addCallback) p.addCallback(e);
|
||||
else e();
|
||||
} else {
|
||||
// just write a string.
|
||||
w(message.body);
|
||||
e();
|
||||
}
|
||||
}
|
||||
};
|
||||
Stream.prototype = {
|
||||
__proto__ : events.EventEmitter.prototype,
|
||||
error : function (ex) {
|
||||
this._error = ex;
|
||||
this.emit("error", ex);
|
||||
},
|
||||
pause : function () {
|
||||
if (this._pause) return this._pause.pause();
|
||||
throw new Error("Unsupported");
|
||||
},
|
||||
resume : function () {
|
||||
if (this._pause) return this._pause.resume();
|
||||
throw new Error("Unsupported");
|
||||
}
|
||||
};
|
||||
|
||||
proto.write = function(chunk) {
|
||||
this.bytesReceived = this.bytesReceived + chunk.length;
|
||||
this.buffer = this.buffer + chunk;
|
||||
// check the headers of the message. If it wants to be multipart,
|
||||
// then we'll be returning true. Regardless, if supplied, then
|
||||
// stream will get a headers object that inherits from message's.
|
||||
// If no stream object is supplied, then this function just inspects
|
||||
// the message's headers for multipartness, and modifies the message
|
||||
// directly. This divergence is so that we can avoid modifying
|
||||
// the original message when we want a wrapper, but still have the
|
||||
// info available when it's one of our own objects.
|
||||
function multipartHeaders (message, stream) {
|
||||
var field, val, contentType, contentDisposition = "";
|
||||
if (stream) stream.headers = {};
|
||||
for (var h in message.headers) if (message.headers.hasOwnProperty(h)) {
|
||||
val = message.headers[h];
|
||||
field = h.toLowerCase();
|
||||
if (stream) stream.headers[field] = val;
|
||||
if (field === "content-type") {
|
||||
contentType = val;
|
||||
} else if (field === "content-disposition") {
|
||||
contentDisposition = val;
|
||||
}
|
||||
}
|
||||
|
||||
while (this.buffer.length) {
|
||||
var offset = this.buffer.indexOf(this.boundary);
|
||||
if (!Array.isArray(contentDisposition)) {
|
||||
contentDisposition = contentDisposition.split(",");
|
||||
}
|
||||
contentDisposition = contentDisposition[contentDisposition.length - 1];
|
||||
|
||||
if (offset === 0) {
|
||||
this.buffer = this.buffer.substr(offset + this.boundary.length + 2);
|
||||
} else if (offset == -1) {
|
||||
if (this.buffer === "\r\n") {
|
||||
this.buffer = '';
|
||||
} else {
|
||||
this.part = (this.part || new Part(this));
|
||||
this.part.write(this.buffer);
|
||||
this.buffer = [];
|
||||
var mutate = (stream || message);
|
||||
|
||||
// Name and filename can come along with either content-disposition
|
||||
// or content-type. Well-behaved agents use CD rather than CT,
|
||||
// but sadly not all agents are well-behaved.
|
||||
[contentDisposition, contentType].forEach(function (h) {
|
||||
if (!h) return;
|
||||
var cd = h.split(/; */);
|
||||
cd.shift();
|
||||
for (var i = 0, l = cd.length; i < l; i ++) {
|
||||
var bit = cd[i].split("="),
|
||||
name = bit.shift(),
|
||||
val = stripQuotes(bit.join("="));
|
||||
if (name === "filename" || name === "name") {
|
||||
mutate[name] = val;
|
||||
}
|
||||
} else if (offset > 0) {
|
||||
this.part = (this.part || new Part(this));
|
||||
this.part.write(this.buffer.substr(0, offset - 2));
|
||||
|
||||
this.part.emit('complete');
|
||||
|
||||
this.part = new Part(this);
|
||||
this.buffer = this.buffer.substr(offset + this.boundary.length + 2);
|
||||
}
|
||||
});
|
||||
|
||||
if (!contentType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// legacy
|
||||
// TODO: Update this when/if jsgi-style headers are supported.
|
||||
// this will keep working, but is less efficient than it could be.
|
||||
if (!Array.isArray(contentType)) {
|
||||
contentType = contentType.split(",");
|
||||
}
|
||||
contentType = contentType[contentType.length-1];
|
||||
|
||||
// make sure it's actually multipart.
|
||||
var mpType = multipartExpression.exec(contentType);
|
||||
if (!mpType) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// make sure we have a boundary.
|
||||
var boundary = boundaryExpression.exec(contentType);
|
||||
if (!boundary) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mutate.type = mpType[1];
|
||||
mutate.boundary = "--" + boundary[1];
|
||||
mutate.isMultiPart = true;
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
function Part(stream) {
|
||||
events.EventEmitter.call(this);
|
||||
|
||||
this.headers = {};
|
||||
this.name = null;
|
||||
this.filename = null;
|
||||
this.buffer = '';
|
||||
this.bytesReceived = 0;
|
||||
|
||||
// Avoids turning Part into a circular JSON object
|
||||
this.getStream = function() {
|
||||
return stream;
|
||||
function simpleWriter (stream) {
|
||||
stream.part = stream;
|
||||
stream.type = false;
|
||||
var started = false;
|
||||
return function (chunk) {
|
||||
if (!started) {
|
||||
stream.emit("partBegin", stream);
|
||||
started = true;
|
||||
}
|
||||
stream.emit("body", chunk);
|
||||
};
|
||||
|
||||
this._headersComplete = false;
|
||||
}
|
||||
sys.inherits(Part, events.EventEmitter);
|
||||
function writer (stream) {
|
||||
var buffer = "",
|
||||
state = S_NEW_PART,
|
||||
part = stream.part = stream;
|
||||
stream.parts = [];
|
||||
stream.parent = stream;
|
||||
return function (chunk) {
|
||||
if (stream._error) return;
|
||||
// write to the buffer, and then process the buffer.
|
||||
buffer += chunk;
|
||||
while (buffer.length > 0) {
|
||||
while (buffer.substr(0, 2) === CRLF) buffer = buffer.substr(2);
|
||||
switch (state) {
|
||||
case S_NEW_PART:
|
||||
// part is a multipart message.
|
||||
// we're either going to start reading a new part, or we're going to
|
||||
// end the current part, depending on whether the boundary has -- at
|
||||
// the end. either way, we expect --boundary right away.
|
||||
var boundary = part.boundary,
|
||||
len = boundary.length,
|
||||
offset = buffer.indexOf(boundary);
|
||||
if (offset === -1) {
|
||||
if (buffer.length > MAX_BUFFER_LENGTH) {
|
||||
return stream.error(new Error(
|
||||
"Malformed: boundary not found at start of message"));
|
||||
}
|
||||
// keep waiting for it.
|
||||
return;
|
||||
}
|
||||
if (offset > 0) {
|
||||
return stream.error(Error("Malformed: data before the boundary"));
|
||||
}
|
||||
if (buffer.length < (len + 2)) {
|
||||
// we'll need to see either -- or CRLF after the boundary.
|
||||
// get it on the next pass.
|
||||
return;
|
||||
}
|
||||
if (buffer.substr(len, 2) === "--") {
|
||||
// this message is done.
|
||||
// chomp off the boundary and crlf and move up
|
||||
if (part !== stream) {
|
||||
// wait to see the crlf, unless this is the top-level message.
|
||||
if (buffer.length < (len + 4)) {
|
||||
return;
|
||||
}
|
||||
if (buffer.substr(len+2, 2) !== CRLF) {
|
||||
return stream.error(new Error(
|
||||
"Malformed: CRLF not found after boundary"));
|
||||
}
|
||||
}
|
||||
buffer = buffer.substr(len + 4);
|
||||
stream.emit("partEnd", part);
|
||||
stream.part = part = part.parent;
|
||||
state = S_NEW_PART;
|
||||
continue;
|
||||
}
|
||||
if (part !== stream) {
|
||||
// wait to see the crlf, unless this is the top-level message.
|
||||
if (buffer.length < (len + 2)) {
|
||||
return;
|
||||
}
|
||||
if (buffer.substr(len, 2) !== CRLF) {
|
||||
return stream.error(new Error(
|
||||
"Malformed: CRLF not found after boundary"));
|
||||
}
|
||||
}
|
||||
// walk past the crlf
|
||||
buffer = buffer.substr(len + 2);
|
||||
// mint a new child part, and start parsing headers.
|
||||
stream.part = part = startPart(part);
|
||||
state = S_HEADER;
|
||||
continue;
|
||||
case S_HEADER:
|
||||
// just grab everything to the double crlf.
|
||||
var headerEnd = buffer.indexOf(CRLF+CRLF);
|
||||
if (headerEnd === -1) {
|
||||
if (buffer.length > MAX_BUFFER_LENGTH) {
|
||||
return stream.error(new Error(
|
||||
"Malformed: header unreasonably long."));
|
||||
}
|
||||
return;
|
||||
}
|
||||
var headerString = buffer.substr(0, headerEnd);
|
||||
// chomp off the header and the empty line.
|
||||
buffer = buffer.substr(headerEnd + 4);
|
||||
try {
|
||||
parseHeaderString(part.headers, headerString);
|
||||
} catch (ex) {
|
||||
return stream.error(ex);
|
||||
}
|
||||
multipartHeaders(part);
|
||||
|
||||
Part.prototype.parsedHeaders = function() {
|
||||
for (var header in this.headers) {
|
||||
var parts = this.headers[header].split(/; ?/), parsedHeader = {};
|
||||
for (var i = 0; i < parts.length; i++) {
|
||||
var pair = parts[i].split('=');
|
||||
if (pair.length < 2) {
|
||||
// let the world know
|
||||
stream.emit("partBegin", part);
|
||||
|
||||
if (part.isMultiPart) {
|
||||
// it has a boundary and we're ready to grab parts out.
|
||||
state = S_NEW_PART;
|
||||
} else {
|
||||
// it doesn't have a boundary, and is about to
|
||||
// start spitting out body bits.
|
||||
state = S_BODY;
|
||||
}
|
||||
continue;
|
||||
case S_BODY:
|
||||
// look for part.parent.boundary
|
||||
var boundary = part.parent.boundary,
|
||||
offset = buffer.indexOf(boundary);
|
||||
if (offset === -1) {
|
||||
// emit and wait for more data, but be careful, because
|
||||
// we might only have half of the boundary so far.
|
||||
// make sure to leave behind the boundary's length, so that we'll
|
||||
// definitely get it next time if it's on its way.
|
||||
var emittable = buffer.length - boundary.length;
|
||||
if (buffer.substr(-1) === CR) emittable -= 1;
|
||||
if (buffer.substr(-2) === CRLF) emittable -= 2;
|
||||
|
||||
if (emittable > 0) {
|
||||
stream.emit("body", buffer.substr(0, emittable));
|
||||
buffer = buffer.substr(emittable);
|
||||
}
|
||||
// haven't seen the boundary, so wait for more bytes.
|
||||
return;
|
||||
}
|
||||
if (offset > 0) {
|
||||
var emit = buffer.substr(0, offset);
|
||||
if (emit.substr(-2) === CRLF) emit = emit.substr(0, emit.length-2);
|
||||
if (emit) stream.emit("body", emit);
|
||||
buffer = buffer.substr(offset);
|
||||
}
|
||||
|
||||
// let em know we're done.
|
||||
stream.emit("partEnd", part);
|
||||
|
||||
// now buffer starts with boundary.
|
||||
if (buffer.substr(boundary.length, 2) === "--") {
|
||||
// message end.
|
||||
// parent ends, look for a new part in the grandparent.
|
||||
stream.part = part = part.parent;
|
||||
stream.emit("partEnd", part);
|
||||
stream.part = part = part.parent;
|
||||
state = S_NEW_PART;
|
||||
buffer = buffer.substr(boundary.length + 4);
|
||||
} else {
|
||||
// another part coming for the parent message.
|
||||
stream.part = part = part.parent;
|
||||
state = S_NEW_PART;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
var key = pair[0].toLowerCase(), val = pair[1] || '';
|
||||
val = stripslashes(val).substr(1);
|
||||
val = val.substr(0, val.length - 1);
|
||||
|
||||
parsedHeader[key] = val;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
if (header == 'content-disposition') {
|
||||
this.name = parsedHeader.name || null;
|
||||
this.filename = parsedHeader.filename || null;
|
||||
function parseHeaderString (headers, string) {
|
||||
var lines = string.split(CRLF),
|
||||
field, value, line;
|
||||
for (var i = 0, l = lines.length; i < l; i ++) {
|
||||
line = lines[i];
|
||||
if (line.match(wrapExpression)) {
|
||||
if (!field) {
|
||||
throw new Error("Malformed. First header starts with whitespace.");
|
||||
}
|
||||
value += line.replace(wrapExpression, " ");
|
||||
continue;
|
||||
} else if (field) {
|
||||
// now that we know it's not wrapping, put it on the headers obj.
|
||||
affixHeader(headers, field, value);
|
||||
}
|
||||
line = line.split(":");
|
||||
field = line.shift().toLowerCase();
|
||||
if (!field) {
|
||||
throw new Error("Malformed: improper field name.");
|
||||
}
|
||||
value = line.join(":").replace(/^\s+/, "");
|
||||
}
|
||||
// now affix the last field.
|
||||
affixHeader(headers, field, value);
|
||||
};
|
||||
|
||||
this.headers[header] = parsedHeader;
|
||||
function affixHeader (headers, field, value) {
|
||||
if (!headers.hasOwnProperty(field)) {
|
||||
headers[field] = value;
|
||||
} else if (Array.isArray(headers[field])) {
|
||||
headers[field].push(value);
|
||||
} else {
|
||||
headers[field] = [headers[field], value];
|
||||
}
|
||||
};
|
||||
|
||||
Part.prototype.write = function(chunk) {
|
||||
if (this._headersComplete) {
|
||||
this.bytesReceived = this.bytesReceived + chunk.length;
|
||||
this.emit('body', chunk);
|
||||
return;
|
||||
}
|
||||
|
||||
this.buffer = this.buffer + chunk;
|
||||
while (this.buffer.length) {
|
||||
var offset = this.buffer.indexOf("\r\n");
|
||||
|
||||
if (offset === 0) {
|
||||
this._headersComplete = true;
|
||||
this.parsedHeaders();
|
||||
this.getStream().emit('part', this);
|
||||
|
||||
this.buffer = this.buffer.substr(2);
|
||||
this.bytesReceived = this.bytesReceived + this.buffer.length;
|
||||
this.emit('body', this.buffer);
|
||||
this.buffer = '';
|
||||
return;
|
||||
} else if (offset > 0) {
|
||||
var header = this.buffer.substr(0, offset).split(/: ?/);
|
||||
this.headers[header[0].toLowerCase()] = header[1];
|
||||
this.buffer = this.buffer.substr(offset+2);
|
||||
} else if (offset === -1) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
function startPart (parent) {
|
||||
var part = {
|
||||
headers : {},
|
||||
parent : parent
|
||||
};
|
||||
parent.parts = parent.parts || [];
|
||||
parent.parts.push(part);
|
||||
return part;
|
||||
};
|
||||
|
||||
function ender (stream) { return function () {
|
||||
if (stream._error) return;
|
||||
if (!stream.isMultiPart) stream.emit("partEnd", stream);
|
||||
stream.emit("complete");
|
||||
}};
|
||||
|
||||
function stripslashes(str) {
|
||||
// + original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
|
||||
// + improved by: Ates Goral (http://magnetiq.com)
|
||||
|
@ -198,20 +412,24 @@ function stripslashes(str) {
|
|||
// + improved by: rezna
|
||||
// + input by: Rick Waldron
|
||||
// + reimplemented by: Brett Zamir (http://brett-zamir.me)
|
||||
// * example 1: stripslashes('Kevin\'s code');
|
||||
// * example 1: stripslashes("Kevin\'s code");
|
||||
// * returns 1: "Kevin's code"
|
||||
// * example 2: stripslashes('Kevin\\\'s code');
|
||||
// * example 2: stripslashes("Kevin\\\'s code");
|
||||
// * returns 2: "Kevin\'s code"
|
||||
return (str+'').replace(/\\(.?)/g, function (s, n1) {
|
||||
return (str+"").replace(/\\(.?)/g, function (s, n1) {
|
||||
switch(n1) {
|
||||
case '\\':
|
||||
return '\\';
|
||||
case '0':
|
||||
return '\0';
|
||||
case '':
|
||||
return '';
|
||||
case "\\":
|
||||
return "\\";
|
||||
case "0":
|
||||
return "\0";
|
||||
case "":
|
||||
return "";
|
||||
default:
|
||||
return n1;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
function stripQuotes (str) {
|
||||
str = stripslashes(str);
|
||||
return str.substr(1, str.length - 2);
|
||||
};
|
||||
|
|
167
lib/sys.js
167
lib/sys.js
|
@ -21,9 +21,99 @@ exports.error = function (x) {
|
|||
* in the best way possible given the different types.
|
||||
*
|
||||
* @param {Object} value The object to print out
|
||||
* @param {Boolean} showHidden Flag that shows hidden (not enumerable) properties of objects.
|
||||
*/
|
||||
exports.inspect = function (value) {
|
||||
return formatter(value, '', []);
|
||||
exports.inspect = function (obj, showHidden) {
|
||||
var seen = [];
|
||||
function format(value) {
|
||||
// Primitive types cannot have properties
|
||||
switch (typeof value) {
|
||||
case 'undefined': return 'undefined';
|
||||
case 'string': return JSON.stringify(value);
|
||||
case 'number': return '' + value;
|
||||
case 'boolean': return '' + value;
|
||||
}
|
||||
// For some reason typeof null is "object", so special case here.
|
||||
if (value === null) {
|
||||
return 'null';
|
||||
}
|
||||
|
||||
// Look up the keys of the object.
|
||||
var keys = showHidden ? Object.getOwnPropertyNames(value).map(function (key) {
|
||||
return '' + key;
|
||||
}) : Object.keys(value);
|
||||
var visible_keys = Object.keys(value);
|
||||
|
||||
// Functions without properties can be shortcutted.
|
||||
if (typeof value === 'function' && keys.length === 0) {
|
||||
if (value instanceof RegExp) {
|
||||
return '' + value;
|
||||
} else {
|
||||
return '[Function]';
|
||||
}
|
||||
}
|
||||
|
||||
var base, type, braces;
|
||||
// Determine the object type
|
||||
if (value instanceof Array) {
|
||||
type = 'Array';
|
||||
braces = ["[", "]"];
|
||||
} else {
|
||||
type = 'Object';
|
||||
braces = ["{", "}"];
|
||||
}
|
||||
|
||||
// Make functions say that they are functions
|
||||
if (typeof value === 'function') {
|
||||
base = (value instanceof RegExp) ? ' ' + value : ' [Function]';
|
||||
} else {
|
||||
base = "";
|
||||
}
|
||||
|
||||
seen.push(value);
|
||||
|
||||
if (keys.length === 0) {
|
||||
return braces[0] + base + braces[1];
|
||||
}
|
||||
|
||||
return braces[0] + base + "\n" + (keys.map(function (key) {
|
||||
var name, str;
|
||||
if (value.__lookupGetter__) {
|
||||
if (value.__lookupGetter__(key)) {
|
||||
if (value.__lookupSetter__(key)) {
|
||||
str = "[Getter/Setter]";
|
||||
} else {
|
||||
str = "[Getter]";
|
||||
}
|
||||
} else {
|
||||
if (value.__lookupSetter__(key)) {
|
||||
str = "[Setter]";
|
||||
}
|
||||
}
|
||||
}
|
||||
if (visible_keys.indexOf(key) < 0) {
|
||||
name = "[" + key + "]";
|
||||
}
|
||||
if (!str) {
|
||||
if (seen.indexOf(value[key]) < 0) {
|
||||
str = format(value[key]);
|
||||
} else {
|
||||
str = '[Circular]';
|
||||
}
|
||||
}
|
||||
if (typeof name === 'undefined') {
|
||||
if (type === 'Array' && key.match(/^\d+$/)) {
|
||||
return str;
|
||||
}
|
||||
name = JSON.stringify('' + key);
|
||||
}
|
||||
|
||||
return name + ": " + str;
|
||||
}).join(",\n")).split("\n").map(function (line) {
|
||||
return ' ' + line;
|
||||
}).join('\n') + "\n" + braces[1];
|
||||
}
|
||||
return format(obj);
|
||||
};
|
||||
|
||||
exports.p = function (x) {
|
||||
|
@ -70,76 +160,3 @@ exports.exec = function (command) {
|
|||
*/
|
||||
exports.inherits = process.inherits;
|
||||
|
||||
/**
|
||||
* A recursive function to format an object - used by inspect.
|
||||
*
|
||||
* @param {Object} value
|
||||
* the value to format
|
||||
* @param {String} indent
|
||||
* the indent level of any nested objects, since they are formatted over
|
||||
* more than one line
|
||||
* @param {Array} parents
|
||||
* contains all objects above the current one in the heirachy, used to
|
||||
* prevent getting stuck in a loop on circular references
|
||||
*/
|
||||
var formatter = function(value, indent, parents) {
|
||||
switch(typeof(value)) {
|
||||
case 'string': return JSON.stringify(value);
|
||||
case 'number': return '' + value;
|
||||
case 'function': return '[Function]';
|
||||
case 'boolean': return '' + value;
|
||||
case 'undefined': return 'undefined';
|
||||
case 'object':
|
||||
if (value == null) return 'null';
|
||||
if (parents.indexOf(value) >= 0) return '[Circular]';
|
||||
parents.push(value);
|
||||
|
||||
if (value instanceof Array && Object.keys(value).length === value.length) {
|
||||
return formatObject(value, indent, parents, '[]', function(x, f) {
|
||||
return f(value[x]);
|
||||
});
|
||||
} else {
|
||||
return formatObject(value, indent, parents, '{}', function(x, f) {
|
||||
var child;
|
||||
if (value.__lookupGetter__(x)) {
|
||||
if (value.__lookupSetter__(x)) {
|
||||
child = "[Getter/Setter]";
|
||||
} else {
|
||||
child = "[Getter]";
|
||||
}
|
||||
} else {
|
||||
if (value.__lookupSetter__(x)) {
|
||||
child = "[Setter]";
|
||||
} else {
|
||||
child = f(value[x]);
|
||||
}
|
||||
}
|
||||
return f(x) + ': ' + child;
|
||||
});
|
||||
}
|
||||
return buffer;
|
||||
default:
|
||||
throw('inspect unimplemented for ' + typeof(value));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper function for formatting either an array or an object, used internally by formatter
|
||||
*/
|
||||
var formatObject = function(obj, indent, parents, parenthesis, entryFormatter) {
|
||||
var buffer = parenthesis[0];
|
||||
var values = [];
|
||||
var x;
|
||||
|
||||
var localFormatter = function(value) {
|
||||
return formatter(value, indent + ' ', parents);
|
||||
};
|
||||
for (x in obj) {
|
||||
values.push(indent + ' ' + entryFormatter(x, localFormatter));
|
||||
}
|
||||
if (values.length > 0) {
|
||||
buffer += "\n" + values.join(",\n") + "\n" + indent;
|
||||
}
|
||||
buffer += parenthesis[1];
|
||||
return buffer;
|
||||
}
|
||||
|
|
39
src/node.cc
39
src/node.cc
|
@ -9,6 +9,8 @@
|
|||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <dlfcn.h> /* dlopen(), dlsym() */
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h> /* setuid, getuid */
|
||||
|
||||
#include <node_buffer.h>
|
||||
#include <node_io_watcher.h>
|
||||
|
@ -468,12 +470,37 @@ static Handle<Value> Umask(const Arguments& args){
|
|||
return scope.Close(Uint32::New(old));
|
||||
}
|
||||
|
||||
|
||||
static Handle<Value> GetUid(const Arguments& args) {
|
||||
HandleScope scope;
|
||||
int uid = getuid();
|
||||
return scope.Close(Integer::New(uid));
|
||||
}
|
||||
|
||||
|
||||
static Handle<Value> SetUid(const Arguments& args) {
|
||||
HandleScope scope;
|
||||
|
||||
if (args.Length() < 1) {
|
||||
return ThrowException(Exception::Error(
|
||||
String::New("setuid requires 1 argument")));
|
||||
}
|
||||
|
||||
Local<Integer> given_uid = args[0]->ToInteger();
|
||||
int uid = given_uid->Int32Value();
|
||||
int result;
|
||||
if ((result = setuid(uid)) != 0) {
|
||||
return ThrowException(Exception::Error(String::New(strerror(errno))));
|
||||
}
|
||||
return Undefined();
|
||||
}
|
||||
|
||||
|
||||
v8::Handle<v8::Value> Exit(const v8::Arguments& args) {
|
||||
int r = 0;
|
||||
if (args.Length() > 0)
|
||||
r = args[0]->IntegerValue();
|
||||
HandleScope scope;
|
||||
fflush(stderr);
|
||||
exit(r);
|
||||
Stdio::Flush();
|
||||
exit(args[0]->IntegerValue());
|
||||
return Undefined();
|
||||
}
|
||||
|
||||
|
@ -958,6 +985,8 @@ static void Load(int argc, char *argv[]) {
|
|||
NODE_SET_METHOD(process, "reallyExit", Exit);
|
||||
NODE_SET_METHOD(process, "chdir", Chdir);
|
||||
NODE_SET_METHOD(process, "cwd", Cwd);
|
||||
NODE_SET_METHOD(process, "getuid", GetUid);
|
||||
NODE_SET_METHOD(process, "setuid", SetUid);
|
||||
NODE_SET_METHOD(process, "umask", Umask);
|
||||
NODE_SET_METHOD(process, "dlopen", DLOpen);
|
||||
NODE_SET_METHOD(process, "kill", Kill);
|
||||
|
@ -1170,6 +1199,8 @@ int main(int argc, char *argv[]) {
|
|||
// so your next reading stop should be node::Load()!
|
||||
node::Load(argc, argv);
|
||||
|
||||
node::Stdio::Flush();
|
||||
|
||||
#ifndef NDEBUG
|
||||
// Clean up.
|
||||
context.Dispose();
|
||||
|
|
10
src/node.js
10
src/node.js
|
@ -571,6 +571,16 @@ var posixModule = createInternalModule("posix", function (exports) {
|
|||
return process.fs.rename(oldPath, newPath);
|
||||
};
|
||||
|
||||
exports.truncate = function (fd, len) {
|
||||
var promise = new events.Promise();
|
||||
process.fs.truncate(fd, len, callback(promise));
|
||||
return promise;
|
||||
};
|
||||
|
||||
exports.truncateSync = function (fd, len) {
|
||||
return process.fs.truncate(fd, len);
|
||||
};
|
||||
|
||||
exports.rmdir = function (path) {
|
||||
var promise = new events.Promise();
|
||||
process.fs.rmdir(path, callback(promise));
|
||||
|
|
|
@ -51,6 +51,7 @@ static int After(eio_req *req) {
|
|||
case EIO_UNLINK:
|
||||
case EIO_RMDIR:
|
||||
case EIO_MKDIR:
|
||||
case EIO_FTRUNCATE:
|
||||
argc = 0;
|
||||
break;
|
||||
|
||||
|
@ -193,6 +194,25 @@ static Handle<Value> Rename(const Arguments& args) {
|
|||
}
|
||||
}
|
||||
|
||||
static Handle<Value> Truncate(const Arguments& args) {
|
||||
HandleScope scope;
|
||||
|
||||
if (args.Length() < 2 || !args[0]->IsInt32()) {
|
||||
return THROW_BAD_ARGS;
|
||||
}
|
||||
|
||||
int fd = args[0]->Int32Value();
|
||||
off_t len = args[1]->Uint32Value();
|
||||
|
||||
if (args[2]->IsFunction()) {
|
||||
ASYNC_CALL(ftruncate, args[2], fd, len)
|
||||
} else {
|
||||
int ret = ftruncate(fd, len);
|
||||
if (ret != 0) return ThrowException(errno_exception(errno));
|
||||
return Undefined();
|
||||
}
|
||||
}
|
||||
|
||||
static Handle<Value> Unlink(const Arguments& args) {
|
||||
HandleScope scope;
|
||||
|
||||
|
@ -403,6 +423,7 @@ void File::Initialize(Handle<Object> target) {
|
|||
NODE_SET_METHOD(target, "open", Open);
|
||||
NODE_SET_METHOD(target, "read", Read);
|
||||
NODE_SET_METHOD(target, "rename", Rename);
|
||||
NODE_SET_METHOD(target, "truncate", Truncate);
|
||||
NODE_SET_METHOD(target, "rmdir", RMDir);
|
||||
NODE_SET_METHOD(target, "mkdir", MKDir);
|
||||
NODE_SET_METHOD(target, "sendfile", SendFile);
|
||||
|
|
|
@ -821,6 +821,14 @@ Handle<Value> Server::Listen(const Arguments& args) {
|
|||
|
||||
if (address_list) freeaddrinfo(address_list);
|
||||
|
||||
if (server->server_.errorno) {
|
||||
Local<Value> e = Exception::Error(
|
||||
String::NewSymbol(strerror(server->server_.errorno)));
|
||||
Local<Object> obj = e->ToObject();
|
||||
obj->Set(String::NewSymbol("errno"), Integer::New(server->server_.errorno));
|
||||
return ThrowException(e);
|
||||
}
|
||||
|
||||
return Undefined();
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
using namespace v8;
|
||||
using namespace node;
|
||||
|
@ -42,6 +44,15 @@ EmitClose (void)
|
|||
emit->Call(stdio, 1, argv);
|
||||
}
|
||||
|
||||
|
||||
static inline Local<Value> errno_exception(int errorno) {
|
||||
Local<Value> e = Exception::Error(String::NewSymbol(strerror(errorno)));
|
||||
Local<Object> obj = e->ToObject();
|
||||
obj->Set(String::NewSymbol("errno"), Integer::New(errorno));
|
||||
return e;
|
||||
}
|
||||
|
||||
|
||||
/* STDERR IS ALWAY SYNC */
|
||||
static Handle<Value>
|
||||
WriteError (const Arguments& args)
|
||||
|
@ -53,8 +64,19 @@ WriteError (const Arguments& args)
|
|||
|
||||
String::Utf8Value msg(args[0]->ToString());
|
||||
|
||||
fprintf(stderr, "%s", *msg);
|
||||
fflush(stderr);
|
||||
ssize_t r;
|
||||
size_t written = 0;
|
||||
while (written < msg.length()) {
|
||||
r = write(STDERR_FILENO, (*msg) + written, msg.length() - written);
|
||||
if (r < 0) {
|
||||
if (errno == EAGAIN || errno == EIO) {
|
||||
usleep(100);
|
||||
continue;
|
||||
}
|
||||
return ThrowException(errno_exception(errno));
|
||||
}
|
||||
written += (size_t)r;
|
||||
}
|
||||
|
||||
return Undefined();
|
||||
}
|
||||
|
@ -197,6 +219,19 @@ Close (const Arguments& args)
|
|||
return Undefined();
|
||||
}
|
||||
|
||||
void Stdio::Flush() {
|
||||
if (stdout_fd >= 0) {
|
||||
close(stdout_fd);
|
||||
stdout_fd = -1;
|
||||
}
|
||||
|
||||
if (stdout_coupling) {
|
||||
coupling_join(stdout_coupling);
|
||||
coupling_destroy(stdout_coupling);
|
||||
stdout_coupling = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Stdio::Initialize (v8::Handle<v8::Object> target)
|
||||
{
|
||||
|
|
|
@ -11,6 +11,7 @@ namespace node {
|
|||
class Stdio {
|
||||
public:
|
||||
static void Initialize (v8::Handle<v8::Object> target);
|
||||
static void Flush ();
|
||||
};
|
||||
|
||||
} // namespace node
|
||||
|
|
|
@ -1,11 +1,721 @@
|
|||
exports.reply = ["--AaB03x\r",
|
||||
"content-disposition: form-data; name=\"reply\"\r",
|
||||
"\r",
|
||||
"yes\r",
|
||||
"--AaB03x\r",
|
||||
"content-disposition: form-data; name=\"fileupload\"; filename=\"dj.jpg\"\r",
|
||||
"Content-Type: image/jpeg\r",
|
||||
"Content-Transfer-Encoding: base64\r",
|
||||
"\r",
|
||||
"/9j/4AAQSkZJRgABAQAAAQABAAD//gA+Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcg\r",
|
||||
"--AaB03x--\r\n"].join("\n");
|
||||
|
||||
// each message contains a header, body, and a list of the parts that are
|
||||
// expected. Any properties in the expected objects will be matched against
|
||||
// the parsed parts.
|
||||
|
||||
var messages = exports.messages = [];
|
||||
|
||||
var bad = exports.badMessages = [];
|
||||
|
||||
var longString = "";
|
||||
for (var i = 0; i < (16*1024); i ++) longString += Math.random();
|
||||
|
||||
// content before the first boundary
|
||||
bad.push({
|
||||
headers : { "Content-Type":"multipart/mixed; boundary=boundary" },
|
||||
body : "blerg\r\n--boundary\r\nblarggghhh"
|
||||
});
|
||||
// no boundary
|
||||
bad.push({
|
||||
headers : { "Content-Type":"multipart/mixed; boundary=boundary" },
|
||||
body : longString
|
||||
});
|
||||
// header unreasonably long.
|
||||
bad.push({
|
||||
headers : { "Content-Type":"multipart/mixed; boundary=boundary" },
|
||||
body : "--boundary\r\ncontent-type: "+longString+"\r\n"+longString
|
||||
});
|
||||
// CRLF not found after boundary
|
||||
bad.push({
|
||||
headers : { "Content-Type":"multipart/mixed; boundary=boundary" },
|
||||
body : "--boundary"+longString
|
||||
});
|
||||
// start first header with whitespace.
|
||||
bad.push({
|
||||
headers : { "Content-Type":"multipart/mixed; boundary=boundary" },
|
||||
body : "--boundary\r\n fail: blahrg\r\n\r\n"+longString
|
||||
});
|
||||
|
||||
// The comments in this first test case tell a story about what the parser is
|
||||
// doing at each step. If you mean to touch the code, it's best to read through
|
||||
// this test case first so that you know what you're getting into.
|
||||
messages.push({
|
||||
expect : [
|
||||
{ type : "mixed", boundary : "--inner1" },
|
||||
{ type : "mixed", boundary : "--inner2" },
|
||||
{ filename : "hello.txt" },
|
||||
{ filename : "hello2.txt" },
|
||||
{ type : "mixed", boundary : "--inner3" },
|
||||
{ filename : "hello3.txt" },
|
||||
{ filename : "hello4.txt" },
|
||||
{ filename : "hello-outer.txt" }
|
||||
],
|
||||
headers : {
|
||||
"Content-Type":"multipart/mixed; boundary=outer"
|
||||
}, body : [
|
||||
// s=new part, part = stream, part.boundary=--outer
|
||||
"--outer",// chomp to here, because it matches the boundary.
|
||||
// mint a new part without a boundary, parent=old part, set state to header
|
||||
"Content-Type: multipart/mixed; boundary=inner1",// move along
|
||||
"", // found the end of the header. chomp to here, parse the headers onto
|
||||
// the current part. Once we do that, we know that the current part
|
||||
// is multipart, and has a boundary of --inner1
|
||||
// s=new part, part = --inner1
|
||||
"--inner1", // chomp to here.
|
||||
// mint a new part without a boundary, parent=--inner1, s=header
|
||||
"Content-type: multipart/mixed; boundary=inner2", // move along
|
||||
"", // again, found the end of the header. chomp to here, parse headers
|
||||
// onto the newly minted part. Then find out that this part has a
|
||||
// boundary of --inner2.
|
||||
// s=new part, part=--inner2
|
||||
"--inner2", // chomp to here.
|
||||
// mint a new part without a boundary, parent=--inner2
|
||||
"Content-type: text/plain", // move along
|
||||
"content-disposition: inline; filename=\"hello.txt\"", // move along
|
||||
"", // chomp to here. found end of header. parse headers
|
||||
// then we know that it's not multipart, so we'll be looking for
|
||||
// the parent's boundary and emitting body bits.
|
||||
// also, we can set part.filename to "hello.txt"
|
||||
// s=body, part=hello.txt
|
||||
"hello, world", // chomp, emit the body, looking for parent-boundary
|
||||
"--inner2", // found parent.boundary. leave it on the buffer, and
|
||||
// set part=part.parent, s=new part
|
||||
// on the next pass, we'll chomp to here, mint a new part
|
||||
// without a boundary, set s=header
|
||||
"content-type: text/plain", // header...
|
||||
"content-disposition: inline; filename=\"hello2.txt\"", // header...
|
||||
"", // chomp to here, parse header onto the current part.
|
||||
// since it's not multipart, we're looking for parent.boundary
|
||||
"hello to the world", // body, looking for parent.boundary=--inner
|
||||
"--inner2--", // found parent.boundary. In this case, we have the
|
||||
// trailing --, indicating that no more parts are coming
|
||||
// for this set. We need to back up to the grandparent,
|
||||
// and then do the new part bit. Chomp off the --inner2--
|
||||
// s=new part, part=part.parent.parent=--inner1
|
||||
"--inner1", // chomp to here, because this is part.boundary
|
||||
// mint a new part without a boundary
|
||||
// s=header, part = (new)
|
||||
"Content-type: multipart/mixed; boundary=inner3", // header...
|
||||
"", // chomp to here, parse headers onto the new part.
|
||||
// it's multipart, so set the boundary=--inner3,
|
||||
// s=new part, part = --inner3
|
||||
"--inner3", // chomp to here. mint a new part with no boundary, parse headers
|
||||
"Content-type: text/plain", // header
|
||||
"content-disposition: inline; filename=\"hello3.txt\"", // header
|
||||
"", // end of header. parse headers onto part, whereupon we find that it is
|
||||
// not multipart, and has a filename of hello3.txt.
|
||||
// s=body, part=hello3.txt, looking for part.parent.boundary=--inner3
|
||||
"hello, free the world", // body...
|
||||
"--inner3", // found parent.boundary, and it's not the end.
|
||||
// s = new part, part = part.parent
|
||||
// next pass:
|
||||
// mint a new part without a boundary, s=header
|
||||
"content-type: text/plain", // header
|
||||
"content-disposition: inline; filename=\"hello4.txt\"", // header
|
||||
"", // chomp to here, parse headers on to boundaryless part.
|
||||
// s=body, part = hello4.txt
|
||||
"hello for the world", // body, looking for part.parent.boundary=--inner3
|
||||
"--inner3--", // found parent.boundary, and it's the end
|
||||
// chomp this off the buffer, part = part.parent.parent=--inner1
|
||||
// s = new part
|
||||
"--inner1", // chomp to here, because part.boundary = --inner1
|
||||
// mint a new boundariless part, s = header
|
||||
"Content-type: text/plain", // header...
|
||||
"content-disposition: inline; filename=\"hello-outer.txt\"", // header...
|
||||
"", // chomp to here, parse headers onto the current part.
|
||||
// has no boundary, so we're gonna go into body mode.
|
||||
// s = body, boundary = parent.boundary = --inner1, part = hello-outer.txt
|
||||
"hello, outer world", // body, looking for parent.boundary=--inner1
|
||||
"--inner1--", // found the parent.boundary, and it's the end.
|
||||
// chomp off the --inner1--, part = part.parent.parent, s = new part
|
||||
"--outer--" // we're looking for a new part, but found the ending.
|
||||
// chomp off the --outer--, part = part.parent, s = new part.
|
||||
].join("\r\n")
|
||||
});
|
||||
|
||||
messages.push({
|
||||
headers : {
|
||||
"Content-Type": "multipart/form-data; boundary=AaB03x",
|
||||
},
|
||||
body : [
|
||||
"--AaB03x",
|
||||
"content-disposition: form-data; name=\"reply\"",
|
||||
"",
|
||||
"yes",
|
||||
"--AaB03x",
|
||||
"content-disposition: form-data; name=\"fileupload\"; filename=\"dj.jpg\"",
|
||||
"Content-Type: image/jpeg",
|
||||
"Content-Transfer-Encoding: base64",
|
||||
"",
|
||||
"/9j/4AAQSkZJRgABAQAAAQABAAD//gA+Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcg",
|
||||
"--AaB03x--", ""
|
||||
].join("\r\n"),
|
||||
expect : [
|
||||
{ name : "reply" },
|
||||
{ name : "fileupload", filename : "dj.jpg" }
|
||||
]
|
||||
});
|
||||
|
||||
// one that's not multipart, just for kicks.
|
||||
// verify that it ducks as a multipart message with one part.
|
||||
messages.push({
|
||||
headers: { "content-type" : "text/plain" },
|
||||
body : "Hello, world!",
|
||||
|
||||
// not much to say about this one, since it's just
|
||||
// validating that a part was created, not that it has
|
||||
// any particular properties.
|
||||
expect : [{}]
|
||||
});
|
||||
|
||||
// An actual email message sent from felixge to isaacs.
|
||||
// Addresses and signatures obscured, but the unicycle pic is preserved for posterity.
|
||||
messages.push({
|
||||
headers: {
|
||||
// TODO: When node's parser supports header-wrapping, these should actually be wrapped,
|
||||
// because that's how they appear in real life.
|
||||
"Delivered-To":"isaacs...@gmail.com",
|
||||
"Received":"by 10.142.240.14 with SMTP id n14cs252101wfh; Wed, 3 Feb 2010 14:24:08 -0800 (PST)",
|
||||
"Received":"by 10.223.4.139 with SMTP id 11mr194455far.61.1265235847416; Wed, 03 Feb 2010 14:24:07 -0800 (PST)",
|
||||
"Return-Path":"<isaacs+caf_=isaacs...=gmail.com@izs.me>",
|
||||
"Received":"from mail-fx0-f219.google.com (mail-fx0-f219.google.com [209.85.220.219]) by mx.google.com with ESMTP id d13si118373fka.17.2010.02.03.14.24.05; Wed, 03 Feb 2010 14:24:06 -0800 (PST)",
|
||||
"Received-SPF":"neutral (google.com: 209.85.220.219 is neither permitted nor denied by best guess record for domain of isaacs+caf_=isaacs...=gmail.com@izs.me) client-ip=209.85.220.219;",
|
||||
"Authentication-Results":"mx.google.com; spf=neutral (google.com: 209.85.220.219 is neither permitted nor denied by best guess record for domain of isaacs+caf_=isaacs...=gmail.com@izs.me) smtp.mail=isaacs+caf_=isaacs...=gmail.com@izs.me; dkim=pass (test mode) header.i=@gmail.com",
|
||||
"Received":"by mail-fx0-f219.google.com with SMTP id 19so626487fxm.25 for <isaacs...@gmail.com>; Wed, 03 Feb 2010 14:24:05 -0800 (PST)",
|
||||
"Received":"by 10.216.91.15 with SMTP id g15mr146196wef.24.1265235845694; Wed, 03 Feb 2010 14:24:05 -0800 (PST)",
|
||||
"X-Forwarded-To":"isaacs...@gmail.com",
|
||||
"X-Forwarded-For":"isaacs@izs.me isaacs...@gmail.com",
|
||||
"Delivered-To":"i@izs.me",
|
||||
"Received":"by 10.216.12.146 with SMTP id 18cs33122wez; Wed, 3 Feb 2010 14:24:00 -0800 (PST)",
|
||||
"Received":"by 10.213.97.28 with SMTP id j28mr2627124ebn.82.1265235838786; Wed, 03 Feb 2010 14:23:58 -0800 (PST)",
|
||||
"Return-Path":"<hai...@gmail.com>",
|
||||
"Received":"from ey-out-2122.google.com (ey-out-2122.google.com [74.125.78.25]) by mx.google.com with ESMTP id 4si11869270ewy.8.2010.02.03.14.23.54; Wed, 03 Feb 2010 14:23:57 -0800 (PST)",
|
||||
"Received-SPF":"pass (google.com: domain of hai...@gmail.com designates 74.125.78.25 as permitted sender) client-ip=74.125.78.25;",
|
||||
"Received":"by ey-out-2122.google.com with SMTP id d26so431288eyd.17 for <i@izs.me>; Wed, 03 Feb 2010 14:23:54 -0800 (PST)",
|
||||
"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:mime-version:sender:received:from:date :x-google-sender-auth:message-id:subject:to:content-type; bh=JXfvYIRzerOieADuqMPGlnlFbIGyPuTssL5icEtSLWw=; b=QDzgOCEbYk8cEdBe+HYx/MJrTWmZyx4qENADOcnnn9Xuk1Q6e/c7b3UsvLf/sMoYrG z96RQhUVOKi9IAzkQhNnOCWDuF1KNxtFnCGhEXMARXBM3qjXe3QmAqXNhJrI0E9bMeme d5aX5GMrz5mIark462cDsTmrFgaYE6JtwASho=",
|
||||
"DomainKey-Signature":"a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=mime-version:sender:from:date:x-google-sender-auth:message-id :subject:to:content-type; b=VYkN8OeNNJyxAseCAPH8u2aBfGmZaFesmieoWEDymQ1DsWg/aXbaWt4JGQlefIfmMK hOXd4EN2/iEix10aWDzKpuUV9gU9Wykm93t3pxD7BCz50Kagwp7NVyDJQLK0H5JSNEU/ IVRp90kKNBsb3v76vPsQydi9awLh/jYrFQVMY=",
|
||||
"MIME-Version":"1.0",
|
||||
"Sender":"hai...@gmail.com",
|
||||
"Received":"by 10.216.86.201 with SMTP id w51mr154937wee.8.1265235834101; Wed, 03 Feb 2010 14:23:54 -0800 (PST)",
|
||||
"From":"Felix Geisendoerfer <f...@debuggable.com>",
|
||||
"Date":"Wed, 3 Feb 2010 23:23:34 +0100",
|
||||
"X-Google-Sender-Auth":"0217977a92fcbed0",
|
||||
"Message-ID":"<56dbc1211002031423g750ba93fs4a2f22ce22431590@mail.gmail.com>",
|
||||
"Subject":"Me on my unicycle",
|
||||
"To":"i@izs.me",
|
||||
"Content-Type":"multipart/mixed; boundary=0016e6d99d0572dfaf047eb9ac2e",
|
||||
},
|
||||
expect : [
|
||||
{ type : "alternative", boundary : "--0016e6d99d0572dfa5047eb9ac2c" },
|
||||
{}, // the first bit, text/plain
|
||||
{}, // the second bit, text/html
|
||||
{ name : "unicycle.jpg", filename : "unicycle.jpg" }
|
||||
],
|
||||
body : [
|
||||
"--0016e6d99d0572dfaf047eb9ac2e", // beginpart->header
|
||||
"Content-Type: multipart/alternative; boundary=0016e6d99d0572dfa5047eb9ac2c", // headers. isMultipart
|
||||
"", // bodybegin->beginpart
|
||||
"--0016e6d99d0572dfa5047eb9ac2c",//header
|
||||
"Content-Type: text/plain; charset=ISO-8859-1",
|
||||
"",//bodybegin->body
|
||||
"*This was 4 years ago, I miss riding my unicycle !*",
|
||||
"",
|
||||
"-- fg",
|
||||
"",
|
||||
"--0016e6d99d0572dfa5047eb9ac2c", //partend->partbegin
|
||||
"Content-Type: text/html; charset=ISO-8859-1",
|
||||
"",
|
||||
"<b><font class=\"Apple-style-span\" color=\"#CC0000\">This was 4 years ago, I miss riding my <span class=\"Apple-style-span\" style=\"background-color: rgb(0, 0, 0);\">unicycle</span> !</font></b><div><br clear=\"all\">-- fg<br><br>",
|
||||
"",
|
||||
"",
|
||||
"</div>",
|
||||
"",
|
||||
"--0016e6d99d0572dfa5047eb9ac2c--",//partend, walk up tree-->partbegin
|
||||
"--0016e6d99d0572dfaf047eb9ac2e",//beginpart->header
|
||||
"Content-Type: image/jpeg; name=\"unicycle.jpg\"",//header
|
||||
"Content-Disposition: attachment; filename=\"unicycle.jpg\"",//header
|
||||
"Content-Transfer-Encoding: base64",//header
|
||||
"X-Attachment-Id: f_g58opqah0",//header
|
||||
"",//bodybegin->body
|
||||
"/9j/4AAQSkZJRgABAQEASABIAAD/4gUoSUNDX1BST0ZJTEUAAQEAAAUYYXBwbAIgAABzY25yUkdC",//bodybodybody
|
||||
"IFhZWiAH0wAHAAEAAAAAAABhY3NwQVBQTAAAAABhcHBsAAAAAAAAAAAAAAAAAAAAAAAA9tYAAQAA",//bodybodybody
|
||||
"AADTLWFwcGwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAty",//bodybodybody
|
||||
"WFlaAAABCAAAABRnWFlaAAABHAAAABRiWFlaAAABMAAAABR3dHB0AAABRAAAABRjaGFkAAABWAAA",//bodybodybody
|
||||
"ACxyVFJDAAABhAAAAA5nVFJDAAABhAAAAA5iVFJDAAABhAAAAA5kZXNjAAABlAAAAD1jcHJ0AAAE",
|
||||
"1AAAAEFkc2NtAAAB1AAAAv5YWVogAAAAAAAAdEsAAD4dAAADy1hZWiAAAAAAAABacwAArKYAABcm",
|
||||
"WFlaIAAAAAAAACgYAAAVVwAAuDNYWVogAAAAAAAA81IAAQAAAAEWz3NmMzIAAAAAAAEMQgAABd7/",
|
||||
"//MmAAAHkgAA/ZH///ui///9owAAA9wAAMBsY3VydgAAAAAAAAABAjMAAGRlc2MAAAAAAAAAE0Nh",
|
||||
"bWVyYSBSR0IgUHJvZmlsZQAAAAAAAAAAAAAAE0NhbWVyYSBSR0IgUHJvZmlsZQAAAABtbHVjAAAA",
|
||||
"AAAAAA8AAAAMZW5VUwAAACQAAAKeZXNFUwAAACwAAAFMZGFESwAAADQAAAHaZGVERQAAACwAAAGY",
|
||||
"ZmlGSQAAACgAAADEZnJGVQAAADwAAALCaXRJVAAAACwAAAJybmxOTAAAACQAAAIObm9OTwAAACAA",
|
||||
"AAF4cHRCUgAAACgAAAJKc3ZTRQAAACoAAADsamFKUAAAABwAAAEWa29LUgAAABgAAAIyemhUVwAA",
|
||||
"ABoAAAEyemhDTgAAABYAAAHEAEsAYQBtAGUAcgBhAG4AIABSAEcAQgAtAHAAcgBvAGYAaQBpAGwA",
|
||||
"aQBSAEcAQgAtAHAAcgBvAGYAaQBsACAAZgD2AHIAIABLAGEAbQBlAHIAYTCrMOEw6QAgAFIARwBC",
|
||||
"ACAw1zDtMNUwoTCkMOtleE9NdvhqXwAgAFIARwBCACCCcl9pY8+P8ABQAGUAcgBmAGkAbAAgAFIA",
|
||||
"RwBCACAAcABhAHIAYQAgAEMA4QBtAGEAcgBhAFIARwBCAC0AawBhAG0AZQByAGEAcAByAG8AZgBp",
|
||||
"AGwAUgBHAEIALQBQAHIAbwBmAGkAbAAgAGYA/AByACAASwBhAG0AZQByAGEAc3b4ZzoAIABSAEcA",
|
||||
"QgAgY8+P8GWHTvYAUgBHAEIALQBiAGUAcwBrAHIAaQB2AGUAbABzAGUAIAB0AGkAbAAgAEsAYQBt",
|
||||
"AGUAcgBhAFIARwBCAC0AcAByAG8AZgBpAGUAbAAgAEMAYQBtAGUAcgBhznS6VLd8ACAAUgBHAEIA",
|
||||
"INUEuFzTDMd8AFAAZQByAGYAaQBsACAAUgBHAEIAIABkAGUAIABDAOIAbQBlAHIAYQBQAHIAbwBm",
|
||||
"AGkAbABvACAAUgBHAEIAIABGAG8AdABvAGMAYQBtAGUAcgBhAEMAYQBtAGUAcgBhACAAUgBHAEIA",
|
||||
"IABQAHIAbwBmAGkAbABlAFAAcgBvAGYAaQBsACAAUgBWAEIAIABkAGUAIABsIBkAYQBwAHAAYQBy",
|
||||
"AGUAaQBsAC0AcABoAG8AdABvAAB0ZXh0AAAAAENvcHlyaWdodCAyMDAzIEFwcGxlIENvbXB1dGVy",
|
||||
"IEluYy4sIGFsbCByaWdodHMgcmVzZXJ2ZWQuAAAAAP/hAIxFeGlmAABNTQAqAAAACAAGAQYAAwAA",
|
||||
"AAEAAgAAARIAAwAAAAEAAQAAARoABQAAAAEAAABWARsABQAAAAEAAABeASgAAwAAAAEAAgAAh2kA",
|
||||
"BAAAAAEAAABmAAAAAAAAAEgAAAABAAAASAAAAAEAAqACAAQAAAABAAAAyKADAAQAAAABAAABWwAA",
|
||||
"AAD/2wBDAAICAgICAQICAgICAgIDAwYEAwMDAwcFBQQGCAcICAgHCAgJCg0LCQkMCggICw8LDA0O",
|
||||
"Dg4OCQsQEQ8OEQ0ODg7/2wBDAQICAgMDAwYEBAYOCQgJDg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4O",
|
||||
"Dg4ODg4ODg4ODg4ODg4ODg4ODg4ODg4ODg7/wAARCAFbAMgDASIAAhEBAxEB/8QAHwAAAQUBAQEB",
|
||||
"AQEAAAAAAAAAAAECAwQFBgcICQoL/8QAtRAAAgEDAwIEAwUFBAQAAAF9AQIDAAQRBRIhMUEGE1Fh",
|
||||
"ByJxFDKBkaEII0KxwRVS0fAkM2JyggkKFhcYGRolJicoKSo0NTY3ODk6Q0RFRkdISUpTVFVWV1hZ",
|
||||
"WmNkZWZnaGlqc3R1dnd4eXqDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXG",
|
||||
"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",
|
||||
"CivqJ00meTzNH//Z",
|
||||
"--0016e6d99d0572dfaf047eb9ac2e--",//partend, then complete
|
||||
""
|
||||
].join("\r\n")
|
||||
});
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
process.mixin(require("../common"));
|
||||
|
||||
var n = parseInt(process.argv[2]);
|
||||
|
||||
var s = "";
|
||||
for (var i = 0; i < n-1; i++) {
|
||||
s += 'c';
|
||||
}
|
||||
|
||||
puts(s); // \n is the nth char.
|
|
@ -0,0 +1,32 @@
|
|||
process.mixin(require("./common"));
|
||||
var events = require('events');
|
||||
|
||||
var callbacks_called = [ ];
|
||||
|
||||
var e = new events.EventEmitter();
|
||||
|
||||
function callback1() {
|
||||
callbacks_called.push("callback1");
|
||||
e.addListener("foo", callback2);
|
||||
e.removeListener("foo", callback1);
|
||||
}
|
||||
|
||||
function callback2() {
|
||||
callbacks_called.push("callback2");
|
||||
e.removeListener("foo", callback2);
|
||||
}
|
||||
|
||||
e.addListener("foo", callback1);
|
||||
assert.equal(1, e.listeners("foo").length);
|
||||
|
||||
e.emit("foo");
|
||||
assert.equal(1, e.listeners("foo").length);
|
||||
assert.deepEqual(["callback1"], callbacks_called);
|
||||
|
||||
e.emit("foo");
|
||||
assert.equal(0, e.listeners("foo").length);
|
||||
assert.deepEqual(["callback1", "callback2"], callbacks_called);
|
||||
|
||||
e.emit("foo");
|
||||
assert.equal(0, e.listeners("foo").length);
|
||||
assert.deepEqual(["callback1", "callback2"], callbacks_called);
|
|
@ -1,129 +1,123 @@
|
|||
process.mixin(require("./common"));
|
||||
http = require("http");
|
||||
|
||||
var
|
||||
var http = require("http"),
|
||||
multipart = require("multipart"),
|
||||
sys = require("sys"),
|
||||
PORT = 8222,
|
||||
|
||||
multipart = require('multipart'),
|
||||
fixture = require('./fixtures/multipart'),
|
||||
|
||||
requests = 0,
|
||||
badRequests = 0,
|
||||
partsReceived = 0,
|
||||
partsComplete = 0,
|
||||
|
||||
respond = function(res, text) {
|
||||
requests++;
|
||||
if (requests == 5) {
|
||||
server.close();
|
||||
fixture = require("./fixtures/multipart"),
|
||||
events = require("events"),
|
||||
testPart = function (expect, part) {
|
||||
if (!expect) {
|
||||
throw new Error("Got more parts than expected: "+
|
||||
JSON.stringify(part.headers));
|
||||
}
|
||||
for (var i in expect) {
|
||||
assert.equal(expect[i], part[i]);
|
||||
}
|
||||
|
||||
res.sendHeader(200, {"Content-Type": "text/plain"});
|
||||
res.sendBody(text);
|
||||
res.finish();
|
||||
};
|
||||
|
||||
var server = http.createServer(function(req, res) {
|
||||
if (req.headers['x-use-simple-api']) {
|
||||
multipart.parse(req)
|
||||
.addCallback(function() {
|
||||
respond(res, 'thanks');
|
||||
})
|
||||
.addErrback(function() {
|
||||
badRequests++;
|
||||
respond(res, 'no thanks');
|
||||
});
|
||||
var emails = fixture.messages.slice(0),
|
||||
chunkSize = 1, // set to minimum to forcibly expose boundary conditions.
|
||||
// in a real scenario, this would be much much bigger.
|
||||
firstPart = new (events.Promise);
|
||||
|
||||
// test streaming messages through directly, as if they were in a file or something.
|
||||
(function testEmails () {
|
||||
var email = emails.pop(),
|
||||
curr = 0;
|
||||
if (!email) {
|
||||
firstPart.emitSuccess();
|
||||
return;
|
||||
}
|
||||
var expect = email.expect;
|
||||
|
||||
var message = new (events.EventEmitter);
|
||||
message.headers = email.headers;
|
||||
|
||||
try {
|
||||
var stream = new multipart.Stream(req);
|
||||
} catch (e) {
|
||||
badRequests++;
|
||||
respond(res, 'no thanks');
|
||||
return;
|
||||
}
|
||||
|
||||
var parts = {};
|
||||
stream.addListener('part', function(part) {
|
||||
partsReceived++;
|
||||
|
||||
var name = part.name;
|
||||
|
||||
if (partsReceived == 1) {
|
||||
assert.equal('reply', name);
|
||||
} else if (partsReceived == 2) {
|
||||
assert.equal('fileupload', name);
|
||||
var mp = multipart.parse(message);
|
||||
mp.addListener("partBegin", function (part) {
|
||||
testPart(email.expect[curr ++], part);
|
||||
});
|
||||
mp.addListener("complete", function () {
|
||||
process.nextTick(testEmails);
|
||||
});
|
||||
// stream it through in chunks.
|
||||
var emailBody = email.body;
|
||||
process.nextTick(function s () {
|
||||
if (emailBody) {
|
||||
message.emit("body", emailBody.substr(0, chunkSize));
|
||||
emailBody = emailBody.substr(chunkSize);
|
||||
process.nextTick(s);
|
||||
} else {
|
||||
message.emit("complete");
|
||||
}
|
||||
|
||||
parts[name] = '';
|
||||
part.addListener('body', function(chunk) {
|
||||
parts[name] += chunk;
|
||||
});
|
||||
part.addListener('complete', function(chunk) {
|
||||
assert.equal(0, part.buffer.length);
|
||||
if (partsReceived == 1) {
|
||||
assert.equal('yes', parts[name]);
|
||||
} else if (partsReceived == 2) {
|
||||
assert.equal(
|
||||
'/9j/4AAQSkZJRgABAQAAAQABAAD//gA+Q1JFQVRPUjogZ2QtanBlZyB2MS4wICh1c2luZyBJSkcg',
|
||||
parts[name]
|
||||
);
|
||||
}
|
||||
partsComplete++;
|
||||
});
|
||||
});
|
||||
})();
|
||||
|
||||
stream.addListener('complete', function() {
|
||||
respond(res, 'thanks');
|
||||
});
|
||||
});
|
||||
// run good HTTP messages test after previous test ends.
|
||||
var secondPart = new (events.Promise),
|
||||
server = http.createServer(function (req, res) {
|
||||
var mp = multipart.parse(req),
|
||||
curr = 0;
|
||||
req.setBodyEncoding("binary");
|
||||
if (req.url !== "/bad") {
|
||||
mp.addListener("partBegin", function (part) {
|
||||
testPart(message.expect[curr ++], part);
|
||||
});
|
||||
}
|
||||
mp.addListener("error", function (er) {
|
||||
res.sendHeader(400, {});
|
||||
res.sendBody("bad");
|
||||
res.finish();
|
||||
});
|
||||
mp.addListener("complete", function () {
|
||||
res.sendHeader(200, {});
|
||||
res.sendBody("ok");
|
||||
res.finish();
|
||||
});
|
||||
}),
|
||||
message,
|
||||
client = http.createClient(PORT);
|
||||
server.listen(PORT);
|
||||
|
||||
var client = http.createClient(PORT);
|
||||
|
||||
var request = client.request('POST', '/', {
|
||||
'Content-Type': 'multipart/form-data; boundary=AaB03x',
|
||||
'Content-Length': fixture.reply.length
|
||||
// could dry these two up a bit.
|
||||
firstPart.addCallback(function testGoodMessages () {
|
||||
var httpMessages = fixture.messages.slice(0);
|
||||
process.nextTick(function testHTTP () {
|
||||
message = httpMessages.pop();
|
||||
if (!message) {
|
||||
secondPart.emitSuccess();
|
||||
return;
|
||||
}
|
||||
var req = client.request("POST", "/", message.headers);
|
||||
req.sendBody(message.body, "binary");
|
||||
req.finish(function (res) {
|
||||
var buff = "";
|
||||
res.addListener("body", function (chunk) { buff += chunk });
|
||||
res.addListener("complete", function () {
|
||||
assert.equal(buff, "ok");
|
||||
process.nextTick(testHTTP);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
request.sendBody(fixture.reply, 'binary');
|
||||
request.finish();
|
||||
|
||||
var simpleRequest = client.request('POST', '/', {
|
||||
'X-Use-Simple-Api': 'yes',
|
||||
'Content-Type': 'multipart/form-data; boundary=AaB03x',
|
||||
'Content-Length': fixture.reply.length
|
||||
});
|
||||
simpleRequest.sendBody(fixture.reply, 'binary');
|
||||
simpleRequest.finish();
|
||||
|
||||
var badRequest = client.request('POST', '/', {
|
||||
'Content-Type': 'invalid!',
|
||||
'Content-Length': fixture.reply.length
|
||||
});
|
||||
badRequest.sendBody(fixture.reply, 'binary');
|
||||
badRequest.finish();
|
||||
|
||||
var simpleBadRequest = client.request('POST', '/', {
|
||||
'X-Use-Simple-Api': 'yes',
|
||||
'Content-Type': 'something',
|
||||
'Content-Length': fixture.reply.length
|
||||
});
|
||||
simpleBadRequest.sendBody(fixture.reply, 'binary');
|
||||
simpleBadRequest.finish();
|
||||
|
||||
var requestWithCharset = client.request('POST', '/', {
|
||||
'X-Use-Simple-Api': 'yes',
|
||||
'Content-Type': 'multipart/form-data; charset=utf-8; boundary=AaB03x',
|
||||
'Content-Length': fixture.reply.length
|
||||
});
|
||||
requestWithCharset.sendBody(fixture.reply, 'binary');
|
||||
requestWithCharset.finish();
|
||||
|
||||
process.addListener('exit', function() {
|
||||
puts("done");
|
||||
assert.equal(2, partsComplete);
|
||||
assert.equal(2, partsReceived);
|
||||
assert.equal(2, badRequests);
|
||||
secondPart.addCallback(function testBadMessages () {
|
||||
var httpMessages = fixture.badMessages.slice(0);
|
||||
process.nextTick(function testHTTP () {
|
||||
message = httpMessages.pop();
|
||||
if (!message) {
|
||||
server.close()
|
||||
return;
|
||||
}
|
||||
var req = client.request("POST", "/bad", message.headers);
|
||||
req.sendBody(message.body, "binary");
|
||||
req.finish(function (res) {
|
||||
var buff = "";
|
||||
res.addListener("body", function (chunk) { buff += chunk });
|
||||
res.addListener("complete", function () {
|
||||
assert.equal(buff, "bad");
|
||||
process.nextTick(testHTTP);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
|
@ -13,6 +13,7 @@ promise.addCallback(function (files) {
|
|||
, 'echo.js'
|
||||
, 'multipart.js'
|
||||
, 'nested-index'
|
||||
, 'print-chars.js'
|
||||
, 'test_ca.pem'
|
||||
, 'test_cert.pem'
|
||||
, 'test_key.pem'
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
process.mixin(require("./common"));
|
||||
|
||||
var sub = path.join(fixturesDir, 'print-chars.js');
|
||||
|
||||
n = 100000;
|
||||
|
||||
var child = process.createChildProcess(process.argv[0], [sub, n]);
|
||||
|
||||
var count = 0;
|
||||
|
||||
child.addListener("error", function (data){
|
||||
if (data) {
|
||||
puts("parent stderr: " + data);
|
||||
assert.ok(false);
|
||||
}
|
||||
});
|
||||
|
||||
child.addListener("output", function (data){
|
||||
if (data) {
|
||||
count += data.length;
|
||||
puts(count);
|
||||
}
|
||||
});
|
||||
|
||||
child.addListener("exit", function (data) {
|
||||
assert.equal(n, count);
|
||||
puts("okay");
|
||||
});
|
|
@ -9,6 +9,7 @@ assert.equal('"hello"', inspect("hello"));
|
|||
assert.equal("[Function]", inspect(function() {}));
|
||||
assert.equal('undefined', inspect(undefined));
|
||||
assert.equal('null', inspect(null));
|
||||
assert.equal('/foo(bar\\n)?/gi', inspect(/foo(bar\n)?/gi));
|
||||
|
||||
assert.equal("\"\\n\\u0001\"", inspect("\n\u0001"));
|
||||
|
||||
|
@ -23,6 +24,24 @@ assert.equal('{\n "a": [Function]\n}', inspect({a: function() {}}));
|
|||
assert.equal('{\n "a": 1,\n "b": 2\n}', inspect({a: 1, b: 2}));
|
||||
assert.equal('{\n "a": {}\n}', inspect({'a': {}}));
|
||||
assert.equal('{\n "a": {\n "b": 2\n }\n}', inspect({'a': {'b': 2}}));
|
||||
assert.equal('[\n 1,\n 2,\n 3,\n [length]: 3\n]', inspect([1,2,3], true));
|
||||
assert.equal("{\n \"visible\": 1\n}",
|
||||
inspect(Object.create({}, {visible:{value:1,enumerable:true},hidden:{value:2}}))
|
||||
);
|
||||
assert.equal("{\n [hidden]: 2,\n \"visible\": 1\n}",
|
||||
inspect(Object.create({}, {visible:{value:1,enumerable:true},hidden:{value:2}}), true)
|
||||
);
|
||||
|
||||
// Objects without prototype
|
||||
assert.equal(
|
||||
"{\n [hidden]: \"secret\",\n \"name\": \"Tim\"\n}",
|
||||
inspect(Object.create(null, {name: {value: "Tim", enumerable: true}, hidden: {value: "secret"}}), true)
|
||||
);
|
||||
assert.equal(
|
||||
"{\n \"name\": \"Tim\"\n}",
|
||||
inspect(Object.create(null, {name: {value: "Tim", enumerable: true}, hidden: {value: "secret"}}))
|
||||
);
|
||||
|
||||
|
||||
// Dynamic properties
|
||||
assert.equal(
|
||||
|
@ -35,12 +54,28 @@ value['a'] = value;
|
|||
assert.equal('{\n "a": [Circular]\n}', inspect(value));
|
||||
value = Object.create([]);
|
||||
value.push(1);
|
||||
assert.equal('{\n "0": 1,\n "length": 1\n}', inspect(value));
|
||||
assert.equal("[\n 1,\n \"length\": 1\n]", inspect(value));
|
||||
|
||||
// Array with dynamic properties
|
||||
value = [1,2,3];
|
||||
value.__defineGetter__('growingLength', function () { this.push(true); return this.length; });
|
||||
assert.equal(
|
||||
"{\n \"0\": 1,\n \"1\": 2,\n \"2\": 3,\n \"growingLength\": [Getter]\n}",
|
||||
"[\n 1,\n 2,\n 3,\n \"growingLength\": [Getter]\n]",
|
||||
inspect(value)
|
||||
);
|
||||
);
|
||||
|
||||
// Function with properties
|
||||
value = function () {};
|
||||
value.aprop = 42;
|
||||
assert.equal(
|
||||
"{ [Function]\n \"aprop\": 42\n}",
|
||||
inspect(value)
|
||||
);
|
||||
|
||||
// Regular expressions with properties
|
||||
value = /123/ig;
|
||||
value.aprop = 42;
|
||||
assert.equal(
|
||||
"{ /123/gi\n \"aprop\": 42\n}",
|
||||
inspect(value)
|
||||
);
|
||||
|
|
|
@ -49,7 +49,8 @@ def CompressScript(lines, do_jsmin):
|
|||
# If we're not expecting this code to be user visible, we can run it through
|
||||
# a more aggressive minifier.
|
||||
if do_jsmin:
|
||||
return jsmin.jsmin(lines)
|
||||
minifier = JavaScriptMinifier()
|
||||
return minifier.JSMinify(lines)
|
||||
|
||||
# Remove stuff from the source that we don't want to appear when
|
||||
# people print the source code using Function.prototype.toString().
|
||||
|
|
218
tools/jsmin.py
218
tools/jsmin.py
|
@ -1,218 +0,0 @@
|
|||
#!/usr/bin/python
|
||||
|
||||
# This code is original from jsmin by Douglas Crockford, it was translated to
|
||||
# Python by Baruch Even. The original code had the following copyright and
|
||||
# license.
|
||||
#
|
||||
# /* jsmin.c
|
||||
# 2007-05-22
|
||||
#
|
||||
# Copyright (c) 2002 Douglas Crockford (www.crockford.com)
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
# this software and associated documentation files (the "Software"), to deal in
|
||||
# the Software without restriction, including without limitation the rights to
|
||||
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
# of the Software, and to permit persons to whom the Software is furnished to do
|
||||
# so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# The Software shall be used for Good, not Evil.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
# */
|
||||
|
||||
from StringIO import StringIO
|
||||
|
||||
def jsmin(js):
|
||||
ins = StringIO(js)
|
||||
outs = StringIO()
|
||||
JavascriptMinify().minify(ins, outs)
|
||||
str = outs.getvalue()
|
||||
if len(str) > 0 and str[0] == '\n':
|
||||
str = str[1:]
|
||||
return str
|
||||
|
||||
def isAlphanum(c):
|
||||
"""return true if the character is a letter, digit, underscore,
|
||||
dollar sign, or non-ASCII character.
|
||||
"""
|
||||
return ((c >= 'a' and c <= 'z') or (c >= '0' and c <= '9') or
|
||||
(c >= 'A' and c <= 'Z') or c == '_' or c == '$' or c == '\\' or (c is not None and ord(c) > 126));
|
||||
|
||||
class UnterminatedComment(Exception):
|
||||
pass
|
||||
|
||||
class UnterminatedStringLiteral(Exception):
|
||||
pass
|
||||
|
||||
class UnterminatedRegularExpression(Exception):
|
||||
pass
|
||||
|
||||
class JavascriptMinify(object):
|
||||
|
||||
def _outA(self):
|
||||
self.outstream.write(self.theA)
|
||||
def _outB(self):
|
||||
self.outstream.write(self.theB)
|
||||
|
||||
def _get(self):
|
||||
"""return the next character from stdin. Watch out for lookahead. If
|
||||
the character is a control character, translate it to a space or
|
||||
linefeed.
|
||||
"""
|
||||
c = self.theLookahead
|
||||
self.theLookahead = None
|
||||
if c == None:
|
||||
c = self.instream.read(1)
|
||||
if c >= ' ' or c == '\n':
|
||||
return c
|
||||
if c == '': # EOF
|
||||
return '\000'
|
||||
if c == '\r':
|
||||
return '\n'
|
||||
return ' '
|
||||
|
||||
def _peek(self):
|
||||
self.theLookahead = self._get()
|
||||
return self.theLookahead
|
||||
|
||||
def _next(self):
|
||||
"""get the next character, excluding comments. peek() is used to see
|
||||
if an unescaped '/' is followed by a '/' or '*'.
|
||||
"""
|
||||
c = self._get()
|
||||
if c == '/' and self.theA != '\\':
|
||||
p = self._peek()
|
||||
if p == '/':
|
||||
c = self._get()
|
||||
while c > '\n':
|
||||
c = self._get()
|
||||
return c
|
||||
if p == '*':
|
||||
c = self._get()
|
||||
while 1:
|
||||
c = self._get()
|
||||
if c == '*':
|
||||
if self._peek() == '/':
|
||||
self._get()
|
||||
return ' '
|
||||
if c == '\000':
|
||||
raise UnterminatedComment()
|
||||
|
||||
return c
|
||||
|
||||
def _action(self, action):
|
||||
"""do something! What you do is determined by the argument:
|
||||
1 Output A. Copy B to A. Get the next B.
|
||||
2 Copy B to A. Get the next B. (Delete A).
|
||||
3 Get the next B. (Delete B).
|
||||
action treats a string as a single character. Wow!
|
||||
action recognizes a regular expression if it is preceded by ( or , or =.
|
||||
"""
|
||||
if action <= 1:
|
||||
self._outA()
|
||||
|
||||
if action <= 2:
|
||||
self.theA = self.theB
|
||||
if self.theA == "'" or self.theA == '"':
|
||||
while 1:
|
||||
self._outA()
|
||||
self.theA = self._get()
|
||||
if self.theA == self.theB:
|
||||
break
|
||||
if self.theA <= '\n':
|
||||
raise UnterminatedStringLiteral()
|
||||
if self.theA == '\\':
|
||||
self._outA()
|
||||
self.theA = self._get()
|
||||
|
||||
|
||||
if action <= 3:
|
||||
self.theB = self._next()
|
||||
if self.theB == '/' and (self.theA == '(' or self.theA == ',' or
|
||||
self.theA == '=' or self.theA == ':' or
|
||||
self.theA == '[' or self.theA == '?' or
|
||||
self.theA == '!' or self.theA == '&' or
|
||||
self.theA == '|' or self.theA == ';' or
|
||||
self.theA == '{' or self.theA == '}' or
|
||||
self.theA == '\n'):
|
||||
self._outA()
|
||||
self._outB()
|
||||
while 1:
|
||||
self.theA = self._get()
|
||||
if self.theA == '/':
|
||||
break
|
||||
elif self.theA == '\\':
|
||||
self._outA()
|
||||
self.theA = self._get()
|
||||
elif self.theA <= '\n':
|
||||
raise UnterminatedRegularExpression()
|
||||
self._outA()
|
||||
self.theB = self._next()
|
||||
|
||||
|
||||
def _jsmin(self):
|
||||
"""Copy the input to the output, deleting the characters which are
|
||||
insignificant to JavaScript. Comments will be removed. Tabs will be
|
||||
replaced with spaces. Carriage returns will be replaced with linefeeds.
|
||||
Most spaces and linefeeds will be removed.
|
||||
"""
|
||||
self.theA = '\n'
|
||||
self._action(3)
|
||||
|
||||
while self.theA != '\000':
|
||||
if self.theA == ' ':
|
||||
if isAlphanum(self.theB):
|
||||
self._action(1)
|
||||
else:
|
||||
self._action(2)
|
||||
elif self.theA == '\n':
|
||||
if self.theB in ['{', '[', '(', '+', '-']:
|
||||
self._action(1)
|
||||
elif self.theB == ' ':
|
||||
self._action(3)
|
||||
else:
|
||||
if isAlphanum(self.theB):
|
||||
self._action(1)
|
||||
else:
|
||||
self._action(2)
|
||||
else:
|
||||
if self.theB == ' ':
|
||||
if isAlphanum(self.theA):
|
||||
self._action(1)
|
||||
else:
|
||||
self._action(3)
|
||||
elif self.theB == '\n':
|
||||
if self.theA in ['}', ']', ')', '+', '-', '"', '\'']:
|
||||
self._action(1)
|
||||
else:
|
||||
if isAlphanum(self.theA):
|
||||
self._action(1)
|
||||
else:
|
||||
self._action(3)
|
||||
else:
|
||||
self._action(1)
|
||||
|
||||
def minify(self, instream, outstream):
|
||||
self.instream = instream
|
||||
self.outstream = outstream
|
||||
self.theA = '\n'
|
||||
self.theB = None
|
||||
self.theLookahead = None
|
||||
|
||||
self._jsmin()
|
||||
self.instream.close()
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
jsm = JavascriptMinify()
|
||||
jsm.minify(sys.stdin, sys.stdout)
|
|
@ -0,0 +1 @@
|
|||
../deps/v8/tools/jsmin.py
|
8
wscript
8
wscript
|
@ -7,7 +7,7 @@ from os.path import join, dirname, abspath
|
|||
from logging import fatal
|
||||
|
||||
cwd = os.getcwd()
|
||||
VERSION="0.1.27"
|
||||
VERSION="0.1.28"
|
||||
APPNAME="node.js"
|
||||
|
||||
import js2c
|
||||
|
@ -91,7 +91,7 @@ def conf_subproject (conf, subdir, command=None):
|
|||
copytree(src, default_tgt, True)
|
||||
|
||||
if command:
|
||||
if os.system("cd %s && %s" % (default_tgt, command)) != 0:
|
||||
if os.system("cd \"%s\" && %s" % (default_tgt, command)) != 0:
|
||||
conf.fatal("Configuring %s failed." % (subdir))
|
||||
|
||||
debug_tgt = join(conf.blddir, "debug", subdir)
|
||||
|
@ -191,7 +191,7 @@ def build_udns(bld):
|
|||
|
||||
static_lib = bld.env["staticlib_PATTERN"] % "udns"
|
||||
|
||||
rule = 'cd %s && make'
|
||||
rule = 'cd "%s" && make'
|
||||
|
||||
default = bld.new_task_gen(
|
||||
target= join("deps/udns", static_lib),
|
||||
|
@ -234,7 +234,7 @@ def v8_cmd(bld, variant):
|
|||
else:
|
||||
mode = "debug"
|
||||
|
||||
cmd_R = 'python %s -C %s -Y %s visibility=default mode=%s %s library=static snapshot=on'
|
||||
cmd_R = 'python "%s" -C "%s" -Y "%s" visibility=default mode=%s %s library=static snapshot=on'
|
||||
|
||||
cmd = cmd_R % ( scons
|
||||
, bld.srcnode.abspath(bld.env_of_name(variant))
|
||||
|
|
Загрузка…
Ссылка в новой задаче