Browse files

Support for urlencoded forms

  • Loading branch information...
1 parent ace4b09 commit 132d716fc4bfbf5df765ab586ad04c2825582220 @felixge committed Jun 13, 2010
View
3 Readme.md
@@ -14,9 +14,8 @@ A node.js module for dealing with web forms.
### Todo
-* Support for form/urlencoded
* Limit buffer size for fields
-* formidable.OutgoingForm
+* Implement QuerystringParser the same way as MultipartParser
## Installation
View
43 example/post.js
@@ -0,0 +1,43 @@
+require('../test/common');
+var http = require('http')
+ , sys = require('sys')
+ , formidable = require('formidable')
+ , server;
+
+server = http.createServer(function(req, res) {
+ if (req.url == '/') {
+ res.writeHead(200, {'content-type': 'text/html'});
+ res.end
+ ( '<form action="/post" method="post">'
+ + '<input type="text" name="title"><br>'
+ + '<input type="text" name="data[foo][]"><br>'
+ + '<input type="submit" value="Submit">'
+ + '</form>'
+ )
+ } else if (req.url == '/post') {
+ var form = new formidable.IncomingForm()
+ , fields = [];
+
+ form
+ .addListener('error', function(err) {
+ res.writeHead(200, {'content-type': 'text/plain'});
+ res.end('error:\n\n'+sys.inspect(err));
+ })
+ .addListener('field', function(field, value) {
+ p([field, value]);
+ fields.push([field, value]);
+ })
+ .addListener('end', function() {
+ puts('-> post done');
+ res.writeHead(200, {'content-type': 'text/plain'});
+ res.end('received fields:\n\n '+sys.inspect(fields));
+ });
+ form.parse(req);
+ } else {
+ res.writeHead(404, {'content-type': 'text/plain'});
+ res.end('404');
+ }
+});
+server.listen(TEST_PORT);
+
+sys.puts('listening on http://localhost:'+TEST_PORT+'/');
View
27 lib/formidable/incoming_form.js
@@ -4,6 +4,7 @@ var sys = require('sys')
, path = require('path')
, WriteStream = require('fs').WriteStream
, MultipartParser = require('./multipart_parser').MultipartParser
+ , QuerystringParser = require('./querystring_parser').QuerystringParser
, Utf8Decoder = require('utf8decoder').Utf8Decoder
, EventEmitter = require('events').EventEmitter;
@@ -47,6 +48,16 @@ IncomingForm.prototype.parse = function(req, cb) {
})
.addListener('data', function(buffer) {
self.write(buffer);
+ })
+ .addListener('end', function() {
+ if (self.error) {
+ return;
+ }
+
+ var err = self._parser.end();
+ if (err) {
+ self._error(err);
+ }
});
if (cb) {
@@ -170,7 +181,7 @@ IncomingForm.prototype._parseContentType = function() {
}
if (this.headers['content-type'].match(/urlencoded/i)) {
- this.type = 'urlencoded';
+ this._initUrlencoded();
return;
}
@@ -281,6 +292,20 @@ IncomingForm.prototype._initMultipart = function(boundary) {
IncomingForm.prototype._initUrlencoded = function() {
this.type = 'urlencoded';
+
+ var parser = new QuerystringParser()
+ , self = this;
+
+ parser.onField = function(key, val) {
+ self.emit('field', key, val);
+ };
+
+ parser.onEnd = function() {
+ self.ended = true;
+ self._maybeEnd();
+ };
+
+ this._parser = parser;
};
IncomingForm.prototype._uploadPath = function() {
View
6 lib/formidable/multipart_parser.js
@@ -292,4 +292,10 @@ MultipartParser.prototype.write = function(buffer) {
this.flags = flags;
return len;
+};
+
+MultipartParser.prototype.end = function() {
+ if (this.state != S.END) {
+ return new Error('MultipartParser.end(): stream ended unexpectedly');
+ }
};
View
66 test/simple/test-incoming-form.js
@@ -1,5 +1,6 @@
require('../common');
var MultipartParserStub = GENTLY.stub('./multipart_parser', 'MultipartParser')
+ , QuerystringParserStub = GENTLY.stub('./querystring_parser', 'QuerystringParser')
, EventEmitterStub = GENTLY.stub('events', 'EventEmitter')
, WriteStreamStub = GENTLY.stub('fs', 'WriteStream');
@@ -43,8 +44,8 @@ test(function parse() {
assert.strictEqual(headers, REQ.headers);
});
- var events = ['error', 'data'];
- gently.expect(REQ, 'addListener', 2, function(event, fn) {
+ var events = ['error', 'data', 'end'];
+ gently.expect(REQ, 'addListener', events.length, function(event, fn) {
assert.equal(event, events.shift());
emit[event] = fn;
return this;
@@ -77,15 +78,43 @@ test(function parse() {
});
emit.data(BUFFER);
})();
+
+ (function testEmitEnd() {
+ form._parser = {};
+
+ (function testWithError() {
+ var ERR = new Error('haha');
+ gently.expect(form._parser, 'end', function() {
+ return ERR;
+ });
+
+ gently.expect(form, '_error', function(err) {
+ assert.strictEqual(err, ERR);
+ });
+
+ emit.end();
+ })();
+
+ (function testWithoutError() {
+ gently.expect(form._parser, 'end');
+ emit.end();
+ })();
+
+ (function testAfterError() {
+ form.error = true;
+ emit.end();
+ })();
+ })();
+
(function testWithCallback() {
gently.expect(EventEmitterStub, 'call');
var form = new IncomingForm()
, REQ = {headers: {}}
, parseCalled = 0;
gently.expect(form, 'writeHeaders');
- gently.expect(REQ, 'addListener', 2, function() {
+ gently.expect(REQ, 'addListener', 3, function() {
return this;
});
@@ -114,7 +143,7 @@ test(function parse() {
}));
gently.expect(form, 'writeHeaders');
- gently.expect(REQ, 'addListener', 2, function() {
+ gently.expect(REQ, 'addListener', 3, function() {
return this;
});
@@ -209,13 +238,13 @@ test(function parseContentType() {
var HEADERS = {};
form.headers = {'content-type': 'application/x-www-form-urlencoded'};
+ gently.expect(form, '_initUrlencoded');
form._parseContentType();
- assert.strictEqual(form.type, 'urlencoded');
// accept anything that has 'urlencoded' in it
form.headers = {'content-type': 'broken-client/urlencoded-stupid'};
+ gently.expect(form, '_initUrlencoded');
form._parseContentType();
- assert.strictEqual(form.type, 'urlencoded');
var BOUNDARY = '---------------------------57814261102167618332366269';
form.headers = {'content-type': 'multipart/form-data; boundary='+BOUNDARY};
@@ -370,8 +399,33 @@ test(function _initMultipart() {
});
test(function _initUrlencoded() {
+ var PARSER;
+
+ gently.expect(QuerystringParserStub, 'new', function() {
+ PARSER = this;
+ });
+
form._initUrlencoded();
assert.equal(form.type, 'urlencoded');
+ assert.strictEqual(form._parser, PARSER);
+
+ (function testOnField() {
+ var KEY = 'KEY', VAL = 'VAL';
+ gently.expect(form, 'emit', function(field, key, val) {
+ assert.equal(field, 'field');
+ assert.equal(key, KEY);
+ assert.equal(val, VAL);
+ });
+
+ PARSER.onField(KEY, VAL);
+ })();
+
+ (function testOnEnd() {
+ gently.expect(form, '_maybeEnd');
+
+ PARSER.onEnd();
+ assert.equal(form.ended, true);
+ })();
});
test(function _error() {
View
11 test/simple/test-multipart-parser.js
@@ -36,4 +36,15 @@ test(function parserError() {
parser.initWithBoundary(boundary);
buffer.write('--ad', 'ascii', 0);
assert.equal(parser.write(buffer), 3);
+});
+
+test(function end() {
+ (function testError() {
+ assert.equal(parser.end().message, 'MultipartParser.end(): stream ended unexpectedly');
+ })();
+
+ (function testRegular() {
+ parser.state = multipartParser.END;
+ assert.strictEqual(parser.end(), undefined);
+ })();
});

0 comments on commit 132d716

Please sign in to comment.