Browse Source

Remove multipart library

Too messy, unmaintainable. Pull it out of the history if you need it.
v0.7.4-release
Ryan Dahl 15 years ago
parent
commit
94644d743c
  1. 170
      doc/api.markdown
  2. 437
      lib/multipart.js
  3. 1
      src/node.cc
  4. 721
      test/fixtures/multipart.js
  5. 140
      test/pummel/test-multipart.js

170
doc/api.markdown

@ -1483,176 +1483,6 @@ Resumes a paused response.
A reference to the `http.Client` that this response belongs to.
## Multipart Parsing
A library to parse `multipart` internet messages is included with
Node. To use it, `require("multipart")`.
### 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, callback)
On success, `callback` is called with `(null, stream)` where `stream` is a
`multipart.Stream` object representing the completed message. The body of
each part is saved on the `body` member.
On error, `callback` is called with `(err)` where `err` is an instanceof
the `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.
This is an EventEmitter with the following events:
- **`"partBegin"`** - `callback(part)`:
Emitted when a new part is found in the stream. `part` is a `part object`, described below.
- **`"partEnd"`** - `callback(part)`:
Emitted when a part is done.
- **`"body"`** - `callback(chunk)`:
Emitted when a chunk of the body is read.
- **`"complete"`** - `callback()`:
Emitted when the end of the stream is reached.
- **`"error"`** - `callback(error)`:
Emitted when a parse error is encountered. This indicates that the message is malformed.
### 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.
### 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.
### multipart.Part
As it parses the message, the Stream object will create `Part` objects.
### 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"),
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.writeHead(400, {"content-type":"text/plain"});
res.write("You sent a bad message!\n"+er.message);
res.close();
});
mp.addListener("partBegin", function (part) {
name = part.name;
filename = part.filename;
if (name) fields[name] = "";
});
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.writeHead(200, {
"content-type" : "text/plain",
"content-length" : response.length
});
res.write(response);
res.close();
})
});
### Nested Multipart Messages
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

437
lib/multipart.js

@ -1,437 +0,0 @@
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,
// parser states.
s = 0,
S_NEW_PART = s++,
S_HEADER = s++,
S_BODY = s++;
exports.parse = parse;
exports.cat = cat;
exports.Stream = Stream;
// 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);
};
// 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, callback) {
var 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)
if (callback) callback(e);
});
stream.addListener("complete", function () {
if (callback) callback(null, stream);
});
};
// 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("data", w);
message.addListener("end", e);
if (message.pause && message.resume) {
this._pause = message;
}
} else if (message.body) {
var self = this;
if (message.body.pause && message.body.resume) {
this._pause = message.body;
}
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");
}
};
// 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;
}
}
if (!Array.isArray(contentDisposition)) {
contentDisposition = contentDisposition.split(",");
}
contentDisposition = contentDisposition[contentDisposition.length - 1];
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;
}
}
});
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 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);
};
}
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);
// 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;
}
}
};
};
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);
};
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];
}
};
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)
// + fixed by: Mick@el
// + improved by: marrtins
// + bugfixed by: Onno Marsman
// + improved by: rezna
// + input by: Rick Waldron
// + reimplemented by: Brett Zamir (http://brett-zamir.me)
// * example 1: stripslashes("Kevin\'s code");
// * returns 1: "Kevin's code"
// * example 2: stripslashes("Kevin\\\'s code");
// * returns 2: "Kevin\'s code"
return (str+"").replace(/\\(.?)/g, function (s, n1) {
switch(n1) {
case "\\":
return "\\";
case "0":
return "\0";
case "":
return "";
default:
return n1;
}
});
};
function stripQuotes (str) {
str = stripslashes(str);
return str.substr(1, str.length - 2);
};

1
src/node.cc

@ -1199,7 +1199,6 @@ static Handle<Value> Binding(const Arguments& args) {
exports->Set(String::New("http_old"), String::New(native_http_old));
exports->Set(String::New("ini"), String::New(native_ini));
exports->Set(String::New("mjsunit"), String::New(native_mjsunit));
exports->Set(String::New("multipart"), String::New(native_multipart));
exports->Set(String::New("net"), String::New(native_net));
exports->Set(String::New("posix"), String::New(native_posix));
exports->Set(String::New("querystring"), String::New(native_querystring));

721
test/fixtures/multipart.js

@ -1,721 +0,0 @@
// 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",
"x8jJytLT1NXW19jZ2uHi4+Tl5ufo6erx8vP09fb3+Pn6/8QAHwEAAwEBAQEBAQEBAQAAAAAAAAEC",
"AwQFBgcICQoL/8QAtREAAgECBAQDBAcFBAQAAQJ3AAECAxEEBSExBhJBUQdhcRMiMoEIFEKRobHB",
"CSMzUvAVYnLRChYkNOEl8RcYGRomJygpKjU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0",
"dXZ3eHl6goOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX",
"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",
"CivqJ00meTzNH//Z",
"--0016e6d99d0572dfaf047eb9ac2e--",//partend, then complete
""
].join("\r\n")
});

140
test/pummel/test-multipart.js

@ -1,140 +0,0 @@
require("../common");
var http = require("http"),
multipart = require("multipart"),
sys = require("sys"),
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]);
}
};
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.
// test streaming messages through directly, as if they were in a file or something.
sys.puts("test "+emails.length+" emails");
(function testEmails () {
var email = emails.pop(),
curr = 0;
if (!email) {
sys.puts("done testing emails");
testGoodMessages();
return;
}
sys.puts("testing email "+emails.length);
var expect = email.expect;
var message = new (events.EventEmitter);
message.headers = email.headers;
var mp = multipart.parse(message);
mp.addListener("partBegin", function (part) {
sys.puts(">> testing part #"+curr);
testPart(email.expect[curr ++], part);
});
mp.addListener("complete", function () {
sys.puts("done with email "+emails.length);
process.nextTick(testEmails);
});
// stream it through in chunks.
var emailBody = email.body;
process.nextTick(function s () {
if (emailBody) {
message.emit("data", emailBody.substr(0, chunkSize));
emailBody = emailBody.substr(chunkSize);
process.nextTick(s);
} else {
message.emit("end");
}
});
})();
// run good HTTP messages test after previous test ends.
var server = http.createServer(function (req, res) {
sys.puts("HTTP mp request");
var mp = multipart.parse(req),
curr = 0;
req.setBodyEncoding("binary");
if (req.url !== "/bad") {
sys.puts("expected to be good");
mp.addListener("partBegin", function (part) {
sys.puts(">> testing part #"+curr);
testPart(message.expect[curr ++], part);
});
} else {
sys.puts("expected to be bad");
}
mp.addListener("error", function (er) {
sys.puts("!! error occurred");
res.writeHead(400, {});
res.write("bad");
res.close();
});
mp.addListener("complete", function () {
res.writeHead(200, {});
res.write("ok");
res.close();
});
}),
message,
client = http.createClient(PORT);
server.listen(PORT);
// could dry these two up a bit.
function testGoodMessages () {
var httpMessages = fixture.messages.slice(0);
sys.puts("testing "+httpMessages.length+" good messages");
(function testHTTP () {
message = httpMessages.pop();
if (!message) {
testBadMessages();
return;
}
sys.puts("test message "+httpMessages.length);
var req = client.request("POST", "/", message.headers);
req.write(message.body, "binary");
req.addListener('response', function (res) {
var buff = "";
res.addListener("data", function (chunk) { buff += chunk });
res.addListener("end", function () {
assert.equal(buff, "ok");
process.nextTick(testHTTP);
});
});
req.close();
})();
}
function testBadMessages () {
var httpMessages = fixture.badMessages.slice(0);
sys.puts("testing "+httpMessages.length+" bad messages");
(function testHTTP () {
message = httpMessages.pop();
if (!message) {
server.close()
return;
}
sys.puts("test message "+httpMessages.length);
var req = client.request("POST", "/bad", message.headers);
req.write(message.body, "binary");
req.addListener('response', function (res) {
var buff = "";
res.addListener("data", function (chunk) { buff += chunk });
res.addListener("end", function () {
assert.equal(buff, "bad");
process.nextTick(testHTTP);
});
});
req.close();
})();
}
Loading…
Cancel
Save