Express
Esse commit está contido em:
+2
-2
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/nodemon
|
||||
|
||||
var express = require('express'),
|
||||
app = express(),
|
||||
app = module.exports = express.createServer(),
|
||||
routes = require('./routes'),
|
||||
arDrone = require('ar-drone'),
|
||||
client = arDrone.createClient();
|
||||
@@ -9,7 +9,7 @@ client = arDrone.createClient();
|
||||
app.set('views', __dirname + '/views');
|
||||
app.set('view engine', 'ejs');
|
||||
app.use(app.router);
|
||||
app.use(express.static(__dirname + 'public'));
|
||||
app.use(express.static(__dirname + '/public'));
|
||||
|
||||
app.get('/', routes.index);
|
||||
|
||||
|
||||
+6
@@ -1,4 +1,10 @@
|
||||
|
||||
3.4.0 / 2013-09-07
|
||||
==================
|
||||
|
||||
* add res.vary(). Closes #1682
|
||||
* update connect
|
||||
|
||||
3.3.8 / 2013-09-02
|
||||
==================
|
||||
|
||||
|
||||
+40
-2
@@ -237,7 +237,7 @@ res.jsonp = function(obj){
|
||||
|
||||
// jsonp
|
||||
if (callback) {
|
||||
if (callback instanceof Array) callback = callback[0];
|
||||
if (Array.isArray(callback)) callback = callback[0];
|
||||
this.set('Content-Type', 'text/javascript');
|
||||
var cb = callback.replace(/[^\[\]\w$.]/g, '');
|
||||
body = cb + ' && ' + cb + '(' + body + ');';
|
||||
@@ -463,7 +463,7 @@ res.format = function(obj){
|
||||
|
||||
var key = req.accepts(keys);
|
||||
|
||||
this.set('Vary', 'Accept');
|
||||
this.vary("Accept");
|
||||
|
||||
if (key) {
|
||||
this.set('Content-Type', normalizeType(key).value);
|
||||
@@ -720,6 +720,44 @@ res.redirect = function(url){
|
||||
this.end(head ? null : body);
|
||||
};
|
||||
|
||||
/**
|
||||
* Add `field` to Vary. If already present in the Vary set, then
|
||||
* this call is simply ignored.
|
||||
*
|
||||
* @param {Array|String} field
|
||||
* @param {ServerResponse} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
res.vary = function(field){
|
||||
var self = this;
|
||||
|
||||
// nothing
|
||||
if (!field) return this;
|
||||
|
||||
// array
|
||||
if (Array.isArray(field)) {
|
||||
field.forEach(function(field){
|
||||
self.vary(field);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
var vary = this.get('Vary');
|
||||
|
||||
// append
|
||||
if (vary) {
|
||||
vary = vary.split(/ *, */);
|
||||
if (!~vary.indexOf(field)) vary.push(field);
|
||||
this.set('Vary', vary.join(', '));
|
||||
return this;
|
||||
}
|
||||
|
||||
// set
|
||||
this.set('Vary', field);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Render `view` with the given `options` and optional callback `fn`.
|
||||
* When a callback function is given a response will _not_ be made
|
||||
|
||||
+1
@@ -54,6 +54,7 @@ exports.locals = function(obj){
|
||||
exports.isAbsolute = function(path){
|
||||
if ('/' == path[0]) return true;
|
||||
if (':' == path[1] && '\\' == path[2]) return true;
|
||||
if ('\\\\' == path.substring(0, 2)) return true; // Microsoft Azure absolute path
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
+1
-2
@@ -1,4 +1,3 @@
|
||||
|
||||
/*!
|
||||
* Connect - basicAuth
|
||||
* Copyright(c) 2010 Sencha Inc.
|
||||
@@ -30,7 +29,7 @@ var utils = require('../utils')
|
||||
*
|
||||
* connect()
|
||||
* .use(connect.basicAuth(function(user, pass){
|
||||
* return 'tj' == user & 'wahoo' == pass;
|
||||
* return 'tj' == user && 'wahoo' == pass;
|
||||
* }))
|
||||
*
|
||||
* Async callback verification, accepting `fn(err, user)`.
|
||||
|
||||
+1
-1
@@ -67,7 +67,7 @@ module.exports = function csrf(options) {
|
||||
Object.defineProperty(req.session, '_csrf', {
|
||||
configurable: true,
|
||||
get: function() {
|
||||
console.warn('req.session._csrf is deprecated, use req.csrfToken([callback]) instead');
|
||||
console.warn('req.session._csrf is deprecated, use req.csrfToken() instead');
|
||||
return req.csrfToken();
|
||||
}
|
||||
});
|
||||
|
||||
+2
-12
@@ -13,14 +13,6 @@
|
||||
var utils = require('../utils')
|
||||
, _limit = require('./limit');
|
||||
|
||||
/**
|
||||
* noop middleware.
|
||||
*/
|
||||
|
||||
function noop(req, res, next) {
|
||||
next();
|
||||
}
|
||||
|
||||
/**
|
||||
* JSON:
|
||||
*
|
||||
@@ -31,7 +23,7 @@ function noop(req, res, next) {
|
||||
*
|
||||
* - `strict` when `false` anything `JSON.parse()` accepts will be parsed
|
||||
* - `reviver` used as the second "reviver" argument for JSON.parse
|
||||
* - `limit` byte limit disabled by default
|
||||
* - `limit` byte limit [1mb]
|
||||
*
|
||||
* @param {Object} options
|
||||
* @return {Function}
|
||||
@@ -42,9 +34,7 @@ exports = module.exports = function(options){
|
||||
var options = options || {}
|
||||
, strict = options.strict !== false;
|
||||
|
||||
var limit = options.limit
|
||||
? _limit(options.limit)
|
||||
: noop;
|
||||
var limit = _limit(options.limit || '1mb');
|
||||
|
||||
return function json(req, res, next) {
|
||||
if (req._body) return next();
|
||||
|
||||
+43
-19
@@ -9,19 +9,11 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var formidable = require('formidable')
|
||||
var multiparty = require('multiparty')
|
||||
, _limit = require('./limit')
|
||||
, utils = require('../utils')
|
||||
, qs = require('qs');
|
||||
|
||||
/**
|
||||
* noop middleware.
|
||||
*/
|
||||
|
||||
function noop(req, res, next) {
|
||||
next();
|
||||
}
|
||||
|
||||
/**
|
||||
* Multipart:
|
||||
*
|
||||
@@ -31,18 +23,49 @@ function noop(req, res, next) {
|
||||
*
|
||||
* Configuration:
|
||||
*
|
||||
* The options passed are merged with [formidable](https://github.com/felixge/node-formidable)'s
|
||||
* `IncomingForm` object, allowing you to configure the upload directory,
|
||||
* The options passed are merged with [multiparty](https://github.com/superjoe30/node-multiparty)'s
|
||||
* `Form` object, allowing you to configure the upload directory,
|
||||
* size limits, etc. For example if you wish to change the upload dir do the following.
|
||||
*
|
||||
* app.use(connect.multipart({ uploadDir: path }));
|
||||
*
|
||||
* Options:
|
||||
*
|
||||
* - `limit` byte limit defaulting to none
|
||||
* - `defer` defers processing and exposes the Formidable form object as `req.form`.
|
||||
* - `limit` byte limit defaulting to [100mb]
|
||||
* - `defer` defers processing and exposes the multiparty form object as `req.form`.
|
||||
* `next()` is called without waiting for the form's "end" event.
|
||||
* This option is useful if you need to bind to the "progress" event, for example.
|
||||
* This option is useful if you need to bind to the "progress" or "part" events, for example.
|
||||
*
|
||||
* Temporary Files:
|
||||
*
|
||||
* By default temporary files are used, stored in `os.tmpDir()`. These
|
||||
* are not automatically garbage collected, you are in charge of moving them
|
||||
* or deleting them. When `defer` is not used and these files are created you
|
||||
* may refernce them via the `req.files` object.
|
||||
*
|
||||
* req.files.images.forEach(function(file){
|
||||
* console.log(' uploaded : %s %skb : %s', file.originalFilename, file.size / 1024 | 0, file.path);
|
||||
* });
|
||||
*
|
||||
* It is highly recommended to monitor and clean up tempfiles in any production
|
||||
* environment, you may use tools like [reap](https://github.com/visionmedia/reap)
|
||||
* to do so.
|
||||
*
|
||||
* Streaming:
|
||||
*
|
||||
* When `defer` is used files are _not_ streamed to tmpfiles, you may
|
||||
* access them via the "part" events and stream them accordingly:
|
||||
*
|
||||
* req.form.on('part', function(part){
|
||||
* // transfer to s3 etc
|
||||
* console.log('upload %s %s', part.name, part.filename);
|
||||
* var out = fs.createWriteStream('/tmp/' + part.filename);
|
||||
* part.pipe(out);
|
||||
* });
|
||||
*
|
||||
* req.form.on('close', function(){
|
||||
* res.end('uploaded!');
|
||||
* });
|
||||
*
|
||||
* @param {Object} options
|
||||
* @return {Function}
|
||||
@@ -52,9 +75,7 @@ function noop(req, res, next) {
|
||||
exports = module.exports = function(options){
|
||||
options = options || {};
|
||||
|
||||
var limit = options.limit
|
||||
? _limit(options.limit)
|
||||
: noop;
|
||||
var limit = _limit(options.limit || '100mb');
|
||||
|
||||
return function multipart(req, res, next) {
|
||||
if (req._body) return next();
|
||||
@@ -76,7 +97,7 @@ exports = module.exports = function(options){
|
||||
limit(req, res, function(err){
|
||||
if (err) return next(err);
|
||||
|
||||
var form = new formidable.IncomingForm
|
||||
var form = new multiparty.Form
|
||||
, data = {}
|
||||
, files = {}
|
||||
, done;
|
||||
@@ -99,9 +120,12 @@ exports = module.exports = function(options){
|
||||
ondata(name, val, data);
|
||||
});
|
||||
|
||||
if (!options.defer) {
|
||||
form.on('file', function(name, val){
|
||||
val.name = val.originalFilename;
|
||||
ondata(name, val, files);
|
||||
});
|
||||
}
|
||||
|
||||
form.on('error', function(err){
|
||||
if (!options.defer) {
|
||||
@@ -111,7 +135,7 @@ exports = module.exports = function(options){
|
||||
done = true;
|
||||
});
|
||||
|
||||
form.on('end', function(){
|
||||
form.on('close', function(){
|
||||
if (done) return;
|
||||
try {
|
||||
req.body = qs.parse(data);
|
||||
|
||||
+2
-4
@@ -30,7 +30,7 @@ function noop(req, res, next) {
|
||||
*
|
||||
* Options:
|
||||
*
|
||||
* - `limit` byte limit disabled by default
|
||||
* - `limit` byte limit [1mb]
|
||||
*
|
||||
* @param {Object} options
|
||||
* @return {Function}
|
||||
@@ -40,9 +40,7 @@ function noop(req, res, next) {
|
||||
exports = module.exports = function(options){
|
||||
options = options || {};
|
||||
|
||||
var limit = options.limit
|
||||
? _limit(options.limit)
|
||||
: noop;
|
||||
var limit = _limit(options.limit || '1mb');
|
||||
|
||||
return function urlencoded(req, res, next) {
|
||||
if (req._body) return next();
|
||||
|
||||
-4
@@ -1,4 +0,0 @@
|
||||
/test/tmp/
|
||||
*.upload
|
||||
*.un~
|
||||
*.http
|
||||
-419
@@ -1,419 +0,0 @@
|
||||
# Formidable
|
||||
|
||||
[](http://travis-ci.org/felixge/node-formidable)
|
||||
|
||||
## Purpose
|
||||
|
||||
A node.js module for parsing form data, especially file uploads.
|
||||
|
||||
## Current status
|
||||
|
||||
This module was developed for [Transloadit](http://transloadit.com/), a service focused on uploading
|
||||
and encoding images and videos. It has been battle-tested against hundreds of GB of file uploads from
|
||||
a large variety of clients and is considered production-ready.
|
||||
|
||||
## Features
|
||||
|
||||
* Fast (~500mb/sec), non-buffering multipart parser
|
||||
* Automatically writing file uploads to disk
|
||||
* Low memory footprint
|
||||
* Graceful error handling
|
||||
* Very high test coverage
|
||||
|
||||
## Installation
|
||||
|
||||
Via [npm](http://github.com/isaacs/npm):
|
||||
```
|
||||
npm install formidable@latest
|
||||
```
|
||||
Manually:
|
||||
```
|
||||
git clone git://github.com/felixge/node-formidable.git formidable
|
||||
vim my.js
|
||||
# var formidable = require('./formidable');
|
||||
```
|
||||
|
||||
Note: Formidable requires [gently](http://github.com/felixge/node-gently) to run the unit tests, but you won't need it for just using the library.
|
||||
|
||||
## Example
|
||||
|
||||
Parse an incoming file upload.
|
||||
```javascript
|
||||
var formidable = require('formidable'),
|
||||
http = require('http'),
|
||||
util = require('util');
|
||||
|
||||
http.createServer(function(req, res) {
|
||||
if (req.url == '/upload' && req.method.toLowerCase() == 'post') {
|
||||
// parse a file upload
|
||||
var form = new formidable.IncomingForm();
|
||||
|
||||
form.parse(req, function(err, fields, files) {
|
||||
res.writeHead(200, {'content-type': 'text/plain'});
|
||||
res.write('received upload:\n\n');
|
||||
res.end(util.inspect({fields: fields, files: files}));
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// show a file upload form
|
||||
res.writeHead(200, {'content-type': 'text/html'});
|
||||
res.end(
|
||||
'<form action="/upload" enctype="multipart/form-data" method="post">'+
|
||||
'<input type="text" name="title"><br>'+
|
||||
'<input type="file" name="upload" multiple="multiple"><br>'+
|
||||
'<input type="submit" value="Upload">'+
|
||||
'</form>'
|
||||
);
|
||||
}).listen(8080);
|
||||
```
|
||||
## API
|
||||
|
||||
### Formidable.IncomingForm
|
||||
```javascript
|
||||
var form = new formidable.IncomingForm()
|
||||
```
|
||||
Creates a new incoming form.
|
||||
|
||||
```javascript
|
||||
form.encoding = 'utf-8';
|
||||
```
|
||||
Sets encoding for incoming form fields.
|
||||
|
||||
```javascript
|
||||
form.uploadDir = process.env.TMP || process.env.TMPDIR || process.env.TEMP || '/tmp' || process.cwd();
|
||||
```
|
||||
The directory for placing file uploads in. You can move them later on using
|
||||
`fs.rename()`. The default directory is picked at module load time depending on
|
||||
the first existing directory from those listed above.
|
||||
|
||||
```javascript
|
||||
form.keepExtensions = false;
|
||||
```
|
||||
If you want the files written to `form.uploadDir` to include the extensions of the original files, set this property to `true`.
|
||||
|
||||
```javascript
|
||||
form.type
|
||||
```
|
||||
Either 'multipart' or 'urlencoded' depending on the incoming request.
|
||||
|
||||
```javascript
|
||||
form.maxFieldsSize = 2 * 1024 * 1024;
|
||||
```
|
||||
Limits the amount of memory a field (not file) can allocate in bytes.
|
||||
If this value is exceeded, an `'error'` event is emitted. The default
|
||||
size is 2MB.
|
||||
|
||||
```javascript
|
||||
form.maxFields = 0;
|
||||
```
|
||||
Limits the number of fields that the querystring parser will decode. Defaults
|
||||
to 0 (unlimited).
|
||||
|
||||
```javascript
|
||||
form.hash = false;
|
||||
```
|
||||
If you want checksums calculated for incoming files, set this to either `'sha1'` or `'md5'`.
|
||||
|
||||
```javascript
|
||||
form.bytesReceived
|
||||
```
|
||||
The amount of bytes received for this form so far.
|
||||
|
||||
```javascript
|
||||
form.bytesExpected
|
||||
```
|
||||
The expected number of bytes in this form.
|
||||
|
||||
```javascript
|
||||
form.parse(request, [cb]);
|
||||
```
|
||||
Parses an incoming node.js `request` containing form data. If `cb` is provided, all fields an files are collected and passed to the callback:
|
||||
|
||||
|
||||
```javascript
|
||||
form.parse(req, function(err, fields, files) {
|
||||
// ...
|
||||
});
|
||||
|
||||
form.onPart(part);
|
||||
```
|
||||
You may overwrite this method if you are interested in directly accessing the multipart stream. Doing so will disable any `'field'` / `'file'` events processing which would occur otherwise, making you fully responsible for handling the processing.
|
||||
|
||||
```javascript
|
||||
form.onPart = function(part) {
|
||||
part.addListener('data', function() {
|
||||
// ...
|
||||
});
|
||||
}
|
||||
```
|
||||
If you want to use formidable to only handle certain parts for you, you can do so:
|
||||
```javascript
|
||||
form.onPart = function(part) {
|
||||
if (!part.filename) {
|
||||
// let formidable handle all non-file parts
|
||||
form.handlePart(part);
|
||||
}
|
||||
}
|
||||
```
|
||||
Check the code in this method for further inspiration.
|
||||
|
||||
|
||||
### Formidable.File
|
||||
```javascript
|
||||
file.size = 0
|
||||
```
|
||||
The size of the uploaded file in bytes. If the file is still being uploaded (see `'fileBegin'` event), this property says how many bytes of the file have been written to disk yet.
|
||||
```javascript
|
||||
file.path = null
|
||||
```
|
||||
The path this file is being written to. You can modify this in the `'fileBegin'` event in
|
||||
case you are unhappy with the way formidable generates a temporary path for your files.
|
||||
```javascript
|
||||
file.name = null
|
||||
```
|
||||
The name this file had according to the uploading client.
|
||||
```javascript
|
||||
file.type = null
|
||||
```
|
||||
The mime type of this file, according to the uploading client.
|
||||
```javascript
|
||||
file.lastModifiedDate = null
|
||||
```
|
||||
A date object (or `null`) containing the time this file was last written to. Mostly
|
||||
here for compatibility with the [W3C File API Draft](http://dev.w3.org/2006/webapi/FileAPI/).
|
||||
```javascript
|
||||
file.hash = null
|
||||
```
|
||||
If hash calculation was set, you can read the hex digest out of this var.
|
||||
|
||||
#### Formidable.File#toJSON()
|
||||
|
||||
This method returns a JSON-representation of the file, allowing you to
|
||||
`JSON.stringify()` the file which is useful for logging and responding
|
||||
to requests.
|
||||
|
||||
### Events
|
||||
|
||||
|
||||
#### 'progress'
|
||||
```javascript
|
||||
form.on('progress', function(bytesReceived, bytesExpected) {
|
||||
});
|
||||
```
|
||||
Emitted after each incoming chunk of data that has been parsed. Can be used to roll your own progress bar.
|
||||
|
||||
|
||||
|
||||
#### 'field'
|
||||
```javascript
|
||||
form.on('field', function(name, value) {
|
||||
});
|
||||
```
|
||||
|
||||
#### 'fileBegin'
|
||||
|
||||
Emitted whenever a field / value pair has been received.
|
||||
```javascript
|
||||
form.on('fileBegin', function(name, file) {
|
||||
});
|
||||
```
|
||||
|
||||
#### 'file'
|
||||
|
||||
Emitted whenever a new file is detected in the upload stream. Use this even if
|
||||
you want to stream the file to somewhere else while buffering the upload on
|
||||
the file system.
|
||||
|
||||
Emitted whenever a field / file pair has been received. `file` is an instance of `File`.
|
||||
```javascript
|
||||
form.on('file', function(name, file) {
|
||||
});
|
||||
```
|
||||
|
||||
#### 'error'
|
||||
|
||||
Emitted when there is an error processing the incoming form. A request that experiences an error is automatically paused, you will have to manually call `request.resume()` if you want the request to continue firing `'data'` events.
|
||||
```javascript
|
||||
form.on('error', function(err) {
|
||||
});
|
||||
```
|
||||
|
||||
#### 'aborted'
|
||||
|
||||
|
||||
Emitted when the request was aborted by the user. Right now this can be due to a 'timeout' or 'close' event on the socket. In the future there will be a separate 'timeout' event (needs a change in the node core).
|
||||
```javascript
|
||||
form.on('aborted', function() {
|
||||
});
|
||||
```
|
||||
|
||||
##### 'end'
|
||||
```javascript
|
||||
form.on('end', function() {
|
||||
});
|
||||
```
|
||||
Emitted when the entire request has been received, and all contained files have finished flushing to disk. This is a great place for you to send your response.
|
||||
|
||||
|
||||
|
||||
## Changelog
|
||||
|
||||
### v1.0.14
|
||||
|
||||
* Add failing hash tests. (Ben Trask)
|
||||
* Enable hash calculation again (Eugene Girshov)
|
||||
* Test for immediate data events (Tim Smart)
|
||||
* Re-arrange IncomingForm#parse (Tim Smart)
|
||||
|
||||
### v1.0.13
|
||||
|
||||
* Only update hash if update method exists (Sven Lito)
|
||||
* According to travis v0.10 needs to go quoted (Sven Lito)
|
||||
* Bumping build node versions (Sven Lito)
|
||||
* Additional fix for empty requests (Eugene Girshov)
|
||||
* Change the default to 1000, to match the new Node behaviour. (OrangeDog)
|
||||
* Add ability to control maxKeys in the querystring parser. (OrangeDog)
|
||||
* Adjust test case to work with node 0.9.x (Eugene Girshov)
|
||||
* Update package.json (Sven Lito)
|
||||
* Path adjustment according to eb4468b (Markus Ast)
|
||||
|
||||
### v1.0.12
|
||||
|
||||
* Emit error on aborted connections (Eugene Girshov)
|
||||
* Add support for empty requests (Eugene Girshov)
|
||||
* Fix name/filename handling in Content-Disposition (jesperp)
|
||||
* Tolerate malformed closing boundary in multipart (Eugene Girshov)
|
||||
* Ignore preamble in multipart messages (Eugene Girshov)
|
||||
* Add support for application/json (Mike Frey, Carlos Rodriguez)
|
||||
* Add support for Base64 encoding (Elmer Bulthuis)
|
||||
* Add File#toJSON (TJ Holowaychuk)
|
||||
* Remove support for Node.js 0.4 & 0.6 (Andrew Kelley)
|
||||
* Documentation improvements (Sven Lito, Andre Azevedo)
|
||||
* Add support for application/octet-stream (Ion Lupascu, Chris Scribner)
|
||||
* Use os.tmpDir() to get tmp directory (Andrew Kelley)
|
||||
* Improve package.json (Andrew Kelley, Sven Lito)
|
||||
* Fix benchmark script (Andrew Kelley)
|
||||
* Fix scope issue in incoming_forms (Sven Lito)
|
||||
* Fix file handle leak on error (OrangeDog)
|
||||
|
||||
### v1.0.11
|
||||
|
||||
* Calculate checksums for incoming files (sreuter)
|
||||
* Add definition parameters to "IncomingForm" as an argument (Math-)
|
||||
|
||||
### v1.0.10
|
||||
|
||||
* Make parts to be proper Streams (Matt Robenolt)
|
||||
|
||||
### v1.0.9
|
||||
|
||||
* Emit progress when content length header parsed (Tim Koschützki)
|
||||
* Fix Readme syntax due to GitHub changes (goob)
|
||||
* Replace references to old 'sys' module in Readme with 'util' (Peter Sugihara)
|
||||
|
||||
### v1.0.8
|
||||
|
||||
* Strip potentially unsafe characters when using `keepExtensions: true`.
|
||||
* Switch to utest / urun for testing
|
||||
* Add travis build
|
||||
|
||||
### v1.0.7
|
||||
|
||||
* Remove file from package that was causing problems when installing on windows. (#102)
|
||||
* Fix typos in Readme (Jason Davies).
|
||||
|
||||
### v1.0.6
|
||||
|
||||
* Do not default to the default to the field name for file uploads where
|
||||
filename="".
|
||||
|
||||
### v1.0.5
|
||||
|
||||
* Support filename="" in multipart parts
|
||||
* Explain unexpected end() errors in parser better
|
||||
|
||||
**Note:** Starting with this version, formidable emits 'file' events for empty
|
||||
file input fields. Previously those were incorrectly emitted as regular file
|
||||
input fields with value = "".
|
||||
|
||||
### v1.0.4
|
||||
|
||||
* Detect a good default tmp directory regardless of platform. (#88)
|
||||
|
||||
### v1.0.3
|
||||
|
||||
* Fix problems with utf8 characters (#84) / semicolons in filenames (#58)
|
||||
* Small performance improvements
|
||||
* New test suite and fixture system
|
||||
|
||||
### v1.0.2
|
||||
|
||||
* Exclude node\_modules folder from git
|
||||
* Implement new `'aborted'` event
|
||||
* Fix files in example folder to work with recent node versions
|
||||
* Make gently a devDependency
|
||||
|
||||
[See Commits](https://github.com/felixge/node-formidable/compare/v1.0.1...v1.0.2)
|
||||
|
||||
### v1.0.1
|
||||
|
||||
* Fix package.json to refer to proper main directory. (#68, Dean Landolt)
|
||||
|
||||
[See Commits](https://github.com/felixge/node-formidable/compare/v1.0.0...v1.0.1)
|
||||
|
||||
### v1.0.0
|
||||
|
||||
* Add support for multipart boundaries that are quoted strings. (Jeff Craig)
|
||||
|
||||
This marks the beginning of development on version 2.0 which will include
|
||||
several architectural improvements.
|
||||
|
||||
[See Commits](https://github.com/felixge/node-formidable/compare/v0.9.11...v1.0.0)
|
||||
|
||||
### v0.9.11
|
||||
|
||||
* Emit `'progress'` event when receiving data, regardless of parsing it. (Tim Koschützki)
|
||||
* Use [W3C FileAPI Draft](http://dev.w3.org/2006/webapi/FileAPI/) properties for File class
|
||||
|
||||
**Important:** The old property names of the File class will be removed in a
|
||||
future release.
|
||||
|
||||
[See Commits](https://github.com/felixge/node-formidable/compare/v0.9.10...v0.9.11)
|
||||
|
||||
### Older releases
|
||||
|
||||
These releases were done before starting to maintain the above Changelog:
|
||||
|
||||
* [v0.9.10](https://github.com/felixge/node-formidable/compare/v0.9.9...v0.9.10)
|
||||
* [v0.9.9](https://github.com/felixge/node-formidable/compare/v0.9.8...v0.9.9)
|
||||
* [v0.9.8](https://github.com/felixge/node-formidable/compare/v0.9.7...v0.9.8)
|
||||
* [v0.9.7](https://github.com/felixge/node-formidable/compare/v0.9.6...v0.9.7)
|
||||
* [v0.9.6](https://github.com/felixge/node-formidable/compare/v0.9.5...v0.9.6)
|
||||
* [v0.9.5](https://github.com/felixge/node-formidable/compare/v0.9.4...v0.9.5)
|
||||
* [v0.9.4](https://github.com/felixge/node-formidable/compare/v0.9.3...v0.9.4)
|
||||
* [v0.9.3](https://github.com/felixge/node-formidable/compare/v0.9.2...v0.9.3)
|
||||
* [v0.9.2](https://github.com/felixge/node-formidable/compare/v0.9.1...v0.9.2)
|
||||
* [v0.9.1](https://github.com/felixge/node-formidable/compare/v0.9.0...v0.9.1)
|
||||
* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0)
|
||||
* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0)
|
||||
* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0)
|
||||
* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0)
|
||||
* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0)
|
||||
* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0)
|
||||
* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0)
|
||||
* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0)
|
||||
* [v0.1.0](https://github.com/felixge/node-formidable/commits/v0.1.0)
|
||||
|
||||
## License
|
||||
|
||||
Formidable is licensed under the MIT license.
|
||||
|
||||
## Ports
|
||||
|
||||
* [multipart-parser](http://github.com/FooBarWidget/multipart-parser): a C++ parser based on formidable
|
||||
|
||||
## Credits
|
||||
|
||||
* [Ryan Dahl](http://twitter.com/ryah) for his work on [http-parser](http://github.com/ry/http-parser) which heavily inspired multipart_parser.js
|
||||
gerado
externo
-71
@@ -1,71 +0,0 @@
|
||||
var assert = require('assert');
|
||||
require('../test/common');
|
||||
var multipartParser = require('../lib/multipart_parser'),
|
||||
MultipartParser = multipartParser.MultipartParser,
|
||||
parser = new MultipartParser(),
|
||||
Buffer = require('buffer').Buffer,
|
||||
boundary = '-----------------------------168072824752491622650073',
|
||||
mb = 100,
|
||||
buffer = createMultipartBuffer(boundary, mb * 1024 * 1024),
|
||||
callbacks =
|
||||
{ partBegin: -1,
|
||||
partEnd: -1,
|
||||
headerField: -1,
|
||||
headerValue: -1,
|
||||
partData: -1,
|
||||
end: -1,
|
||||
};
|
||||
|
||||
|
||||
parser.initWithBoundary(boundary);
|
||||
parser.onHeaderField = function() {
|
||||
callbacks.headerField++;
|
||||
};
|
||||
|
||||
parser.onHeaderValue = function() {
|
||||
callbacks.headerValue++;
|
||||
};
|
||||
|
||||
parser.onPartBegin = function() {
|
||||
callbacks.partBegin++;
|
||||
};
|
||||
|
||||
parser.onPartData = function() {
|
||||
callbacks.partData++;
|
||||
};
|
||||
|
||||
parser.onPartEnd = function() {
|
||||
callbacks.partEnd++;
|
||||
};
|
||||
|
||||
parser.onEnd = function() {
|
||||
callbacks.end++;
|
||||
};
|
||||
|
||||
var start = +new Date(),
|
||||
nparsed = parser.write(buffer),
|
||||
duration = +new Date - start,
|
||||
mbPerSec = (mb / (duration / 1000)).toFixed(2);
|
||||
|
||||
console.log(mbPerSec+' mb/sec');
|
||||
|
||||
assert.equal(nparsed, buffer.length);
|
||||
|
||||
function createMultipartBuffer(boundary, size) {
|
||||
var head =
|
||||
'--'+boundary+'\r\n'
|
||||
+ 'content-disposition: form-data; name="field1"\r\n'
|
||||
+ '\r\n'
|
||||
, tail = '\r\n--'+boundary+'--\r\n'
|
||||
, buffer = new Buffer(size);
|
||||
|
||||
buffer.write(head, 'ascii', 0);
|
||||
buffer.write(tail, 'ascii', buffer.length - tail.length);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
process.on('exit', function() {
|
||||
for (var k in callbacks) {
|
||||
assert.equal(0, callbacks[k], k+' count off by '+callbacks[k]);
|
||||
}
|
||||
});
|
||||
-67
@@ -1,67 +0,0 @@
|
||||
var common = require('../test/common'),
|
||||
http = require('http'),
|
||||
util = require('util'),
|
||||
formidable = common.formidable,
|
||||
Buffer = require('buffer').Buffer,
|
||||
port = common.port,
|
||||
server;
|
||||
|
||||
server = http.createServer(function(req, res) {
|
||||
if (req.method !== 'POST') {
|
||||
res.writeHead(200, {'content-type': 'text/plain'})
|
||||
res.end('Please POST a JSON payload to http://localhost:'+port+'/')
|
||||
return;
|
||||
}
|
||||
|
||||
var form = new formidable.IncomingForm(),
|
||||
fields = {};
|
||||
|
||||
form
|
||||
.on('error', function(err) {
|
||||
res.writeHead(500, {'content-type': 'text/plain'});
|
||||
res.end('error:\n\n'+util.inspect(err));
|
||||
console.error(err);
|
||||
})
|
||||
.on('field', function(field, value) {
|
||||
console.log(field, value);
|
||||
fields[field] = value;
|
||||
})
|
||||
.on('end', function() {
|
||||
console.log('-> post done');
|
||||
res.writeHead(200, {'content-type': 'text/plain'});
|
||||
res.end('received fields:\n\n '+util.inspect(fields));
|
||||
});
|
||||
form.parse(req);
|
||||
});
|
||||
server.listen(port);
|
||||
|
||||
console.log('listening on http://localhost:'+port+'/');
|
||||
|
||||
|
||||
var request = http.request({
|
||||
host: 'localhost',
|
||||
path: '/',
|
||||
port: port,
|
||||
method: 'POST',
|
||||
headers: { 'content-type':'application/json', 'content-length':48 }
|
||||
}, function(response) {
|
||||
var data = '';
|
||||
console.log('\nServer responded with:');
|
||||
console.log('Status:', response.statusCode);
|
||||
response.pipe(process.stdout);
|
||||
response.on('end', function() {
|
||||
console.log('\n')
|
||||
process.exit();
|
||||
});
|
||||
// response.on('data', function(chunk) {
|
||||
// data += chunk.toString('utf8');
|
||||
// });
|
||||
// response.on('end', function() {
|
||||
// console.log('Response Data:')
|
||||
// console.log(data);
|
||||
// process.exit();
|
||||
// });
|
||||
})
|
||||
|
||||
request.write('{"numbers":[1,2,3,4,5],"nested":{"key":"value"}}');
|
||||
request.end();
|
||||
-43
@@ -1,43 +0,0 @@
|
||||
require('../test/common');
|
||||
var http = require('http'),
|
||||
util = require('util'),
|
||||
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
|
||||
.on('error', function(err) {
|
||||
res.writeHead(200, {'content-type': 'text/plain'});
|
||||
res.end('error:\n\n'+util.inspect(err));
|
||||
})
|
||||
.on('field', function(field, value) {
|
||||
console.log(field, value);
|
||||
fields.push([field, value]);
|
||||
})
|
||||
.on('end', function() {
|
||||
console.log('-> post done');
|
||||
res.writeHead(200, {'content-type': 'text/plain'});
|
||||
res.end('received fields:\n\n '+util.inspect(fields));
|
||||
});
|
||||
form.parse(req);
|
||||
} else {
|
||||
res.writeHead(404, {'content-type': 'text/plain'});
|
||||
res.end('404');
|
||||
}
|
||||
});
|
||||
server.listen(TEST_PORT);
|
||||
|
||||
console.log('listening on http://localhost:'+TEST_PORT+'/');
|
||||
-48
@@ -1,48 +0,0 @@
|
||||
require('../test/common');
|
||||
var http = require('http'),
|
||||
util = require('util'),
|
||||
formidable = require('formidable'),
|
||||
server;
|
||||
|
||||
server = http.createServer(function(req, res) {
|
||||
if (req.url == '/') {
|
||||
res.writeHead(200, {'content-type': 'text/html'});
|
||||
res.end(
|
||||
'<form action="/upload" enctype="multipart/form-data" method="post">'+
|
||||
'<input type="text" name="title"><br>'+
|
||||
'<input type="file" name="upload" multiple="multiple"><br>'+
|
||||
'<input type="submit" value="Upload">'+
|
||||
'</form>'
|
||||
);
|
||||
} else if (req.url == '/upload') {
|
||||
var form = new formidable.IncomingForm(),
|
||||
files = [],
|
||||
fields = [];
|
||||
|
||||
form.uploadDir = TEST_TMP;
|
||||
|
||||
form
|
||||
.on('field', function(field, value) {
|
||||
console.log(field, value);
|
||||
fields.push([field, value]);
|
||||
})
|
||||
.on('file', function(field, file) {
|
||||
console.log(field, file);
|
||||
files.push([field, file]);
|
||||
})
|
||||
.on('end', function() {
|
||||
console.log('-> upload done');
|
||||
res.writeHead(200, {'content-type': 'text/plain'});
|
||||
res.write('received fields:\n\n '+util.inspect(fields));
|
||||
res.write('\n\n');
|
||||
res.end('received files:\n\n '+util.inspect(files));
|
||||
});
|
||||
form.parse(req);
|
||||
} else {
|
||||
res.writeHead(404, {'content-type': 'text/plain'});
|
||||
res.end('404');
|
||||
}
|
||||
});
|
||||
server.listen(TEST_PORT);
|
||||
|
||||
console.log('listening on http://localhost:'+TEST_PORT+'/');
|
||||
-1
@@ -1 +0,0 @@
|
||||
module.exports = require('./lib');
|
||||
-72
@@ -1,72 +0,0 @@
|
||||
if (global.GENTLY) require = GENTLY.hijack(require);
|
||||
|
||||
var util = require('util'),
|
||||
WriteStream = require('fs').WriteStream,
|
||||
EventEmitter = require('events').EventEmitter,
|
||||
crypto = require('crypto');
|
||||
|
||||
function File(properties) {
|
||||
EventEmitter.call(this);
|
||||
|
||||
this.size = 0;
|
||||
this.path = null;
|
||||
this.name = null;
|
||||
this.type = null;
|
||||
this.hash = null;
|
||||
this.lastModifiedDate = null;
|
||||
|
||||
this._writeStream = null;
|
||||
|
||||
for (var key in properties) {
|
||||
this[key] = properties[key];
|
||||
}
|
||||
|
||||
if(typeof this.hash === 'string') {
|
||||
this.hash = crypto.createHash(properties.hash);
|
||||
} else {
|
||||
this.hash = null;
|
||||
}
|
||||
}
|
||||
module.exports = File;
|
||||
util.inherits(File, EventEmitter);
|
||||
|
||||
File.prototype.open = function() {
|
||||
this._writeStream = new WriteStream(this.path);
|
||||
};
|
||||
|
||||
File.prototype.toJSON = function() {
|
||||
return {
|
||||
size: this.size,
|
||||
path: this.path,
|
||||
name: this.name,
|
||||
type: this.type,
|
||||
mtime: this.lastModifiedDate,
|
||||
length: this.length,
|
||||
filename: this.filename,
|
||||
mime: this.mime
|
||||
};
|
||||
};
|
||||
|
||||
File.prototype.write = function(buffer, cb) {
|
||||
var self = this;
|
||||
if (self.hash) {
|
||||
self.hash.update(buffer);
|
||||
}
|
||||
this._writeStream.write(buffer, function() {
|
||||
self.lastModifiedDate = new Date();
|
||||
self.size += buffer.length;
|
||||
self.emit('progress', self.size);
|
||||
cb();
|
||||
});
|
||||
};
|
||||
|
||||
File.prototype.end = function(cb) {
|
||||
var self = this;
|
||||
if (self.hash) {
|
||||
self.hash = self.hash.digest('hex');
|
||||
}
|
||||
this._writeStream.end(function() {
|
||||
self.emit('end');
|
||||
cb();
|
||||
});
|
||||
};
|
||||
gerado
externo
-535
@@ -1,535 +0,0 @@
|
||||
if (global.GENTLY) require = GENTLY.hijack(require);
|
||||
|
||||
var fs = require('fs');
|
||||
var util = require('util'),
|
||||
path = require('path'),
|
||||
File = require('./file'),
|
||||
MultipartParser = require('./multipart_parser').MultipartParser,
|
||||
QuerystringParser = require('./querystring_parser').QuerystringParser,
|
||||
OctetParser = require('./octet_parser').OctetParser,
|
||||
JSONParser = require('./json_parser').JSONParser,
|
||||
StringDecoder = require('string_decoder').StringDecoder,
|
||||
EventEmitter = require('events').EventEmitter,
|
||||
Stream = require('stream').Stream,
|
||||
os = require('os');
|
||||
|
||||
function IncomingForm(opts) {
|
||||
if (!(this instanceof IncomingForm)) return new IncomingForm(opts);
|
||||
EventEmitter.call(this);
|
||||
|
||||
opts=opts||{};
|
||||
|
||||
this.error = null;
|
||||
this.ended = false;
|
||||
|
||||
this.maxFields = opts.maxFields || 1000;
|
||||
this.maxFieldsSize = opts.maxFieldsSize || 2 * 1024 * 1024;
|
||||
this.keepExtensions = opts.keepExtensions || false;
|
||||
this.uploadDir = opts.uploadDir || os.tmpDir();
|
||||
this.encoding = opts.encoding || 'utf-8';
|
||||
this.headers = null;
|
||||
this.type = null;
|
||||
this.hash = false;
|
||||
|
||||
this.bytesReceived = null;
|
||||
this.bytesExpected = null;
|
||||
|
||||
this._parser = null;
|
||||
this._flushing = 0;
|
||||
this._fieldsSize = 0;
|
||||
this.openedFiles = [];
|
||||
|
||||
return this;
|
||||
};
|
||||
util.inherits(IncomingForm, EventEmitter);
|
||||
exports.IncomingForm = IncomingForm;
|
||||
|
||||
IncomingForm.prototype.parse = function(req, cb) {
|
||||
this.pause = function() {
|
||||
try {
|
||||
req.pause();
|
||||
} catch (err) {
|
||||
// the stream was destroyed
|
||||
if (!this.ended) {
|
||||
// before it was completed, crash & burn
|
||||
this._error(err);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
this.resume = function() {
|
||||
try {
|
||||
req.resume();
|
||||
} catch (err) {
|
||||
// the stream was destroyed
|
||||
if (!this.ended) {
|
||||
// before it was completed, crash & burn
|
||||
this._error(err);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
// Setup callback first, so we don't miss anything from data events emitted
|
||||
// immediately.
|
||||
if (cb) {
|
||||
var fields = {}, files = {};
|
||||
this
|
||||
.on('field', function(name, value) {
|
||||
fields[name] = value;
|
||||
})
|
||||
.on('file', function(name, file) {
|
||||
files[name] = file;
|
||||
})
|
||||
.on('error', function(err) {
|
||||
cb(err, fields, files);
|
||||
})
|
||||
.on('end', function() {
|
||||
cb(null, fields, files);
|
||||
});
|
||||
}
|
||||
|
||||
// Parse headers and setup the parser, ready to start listening for data.
|
||||
this.writeHeaders(req.headers);
|
||||
|
||||
// Start listening for data.
|
||||
var self = this;
|
||||
req
|
||||
.on('error', function(err) {
|
||||
self._error(err);
|
||||
})
|
||||
.on('aborted', function() {
|
||||
self.emit('aborted');
|
||||
self._error(new Error('Request aborted'));
|
||||
})
|
||||
.on('data', function(buffer) {
|
||||
self.write(buffer);
|
||||
})
|
||||
.on('end', function() {
|
||||
if (self.error) {
|
||||
return;
|
||||
}
|
||||
|
||||
var err = self._parser.end();
|
||||
if (err) {
|
||||
self._error(err);
|
||||
}
|
||||
});
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
IncomingForm.prototype.writeHeaders = function(headers) {
|
||||
this.headers = headers;
|
||||
this._parseContentLength();
|
||||
this._parseContentType();
|
||||
};
|
||||
|
||||
IncomingForm.prototype.write = function(buffer) {
|
||||
if (!this._parser) {
|
||||
this._error(new Error('unintialized parser'));
|
||||
return;
|
||||
}
|
||||
|
||||
this.bytesReceived += buffer.length;
|
||||
this.emit('progress', this.bytesReceived, this.bytesExpected);
|
||||
|
||||
var bytesParsed = this._parser.write(buffer);
|
||||
if (bytesParsed !== buffer.length) {
|
||||
this._error(new Error('parser error, '+bytesParsed+' of '+buffer.length+' bytes parsed'));
|
||||
}
|
||||
|
||||
return bytesParsed;
|
||||
};
|
||||
|
||||
IncomingForm.prototype.pause = function() {
|
||||
// this does nothing, unless overwritten in IncomingForm.parse
|
||||
return false;
|
||||
};
|
||||
|
||||
IncomingForm.prototype.resume = function() {
|
||||
// this does nothing, unless overwritten in IncomingForm.parse
|
||||
return false;
|
||||
};
|
||||
|
||||
IncomingForm.prototype.onPart = function(part) {
|
||||
// this method can be overwritten by the user
|
||||
this.handlePart(part);
|
||||
};
|
||||
|
||||
IncomingForm.prototype.handlePart = function(part) {
|
||||
var self = this;
|
||||
|
||||
if (part.filename === undefined) {
|
||||
var value = ''
|
||||
, decoder = new StringDecoder(this.encoding);
|
||||
|
||||
part.on('data', function(buffer) {
|
||||
self._fieldsSize += buffer.length;
|
||||
if (self._fieldsSize > self.maxFieldsSize) {
|
||||
self._error(new Error('maxFieldsSize exceeded, received '+self._fieldsSize+' bytes of field data'));
|
||||
return;
|
||||
}
|
||||
value += decoder.write(buffer);
|
||||
});
|
||||
|
||||
part.on('end', function() {
|
||||
self.emit('field', part.name, value);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this._flushing++;
|
||||
|
||||
var file = new File({
|
||||
path: this._uploadPath(part.filename),
|
||||
name: part.filename,
|
||||
type: part.mime,
|
||||
hash: self.hash
|
||||
});
|
||||
|
||||
this.emit('fileBegin', part.name, file);
|
||||
|
||||
file.open();
|
||||
this.openedFiles.push(file);
|
||||
|
||||
part.on('data', function(buffer) {
|
||||
self.pause();
|
||||
file.write(buffer, function() {
|
||||
self.resume();
|
||||
});
|
||||
});
|
||||
|
||||
part.on('end', function() {
|
||||
file.end(function() {
|
||||
self._flushing--;
|
||||
self.emit('file', part.name, file);
|
||||
self._maybeEnd();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
function dummyParser(self) {
|
||||
return {
|
||||
end: function () {
|
||||
self.ended = true;
|
||||
self._maybeEnd();
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
IncomingForm.prototype._parseContentType = function() {
|
||||
if (this.bytesExpected === 0) {
|
||||
this._parser = dummyParser(this);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.headers['content-type']) {
|
||||
this._error(new Error('bad content-type header, no content-type'));
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.headers['content-type'].match(/octet-stream/i)) {
|
||||
this._initOctetStream();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.headers['content-type'].match(/urlencoded/i)) {
|
||||
this._initUrlencoded();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.headers['content-type'].match(/multipart/i)) {
|
||||
var m;
|
||||
if (m = this.headers['content-type'].match(/boundary=(?:"([^"]+)"|([^;]+))/i)) {
|
||||
this._initMultipart(m[1] || m[2]);
|
||||
} else {
|
||||
this._error(new Error('bad content-type header, no multipart boundary'));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.headers['content-type'].match(/json/i)) {
|
||||
this._initJSONencoded();
|
||||
return;
|
||||
}
|
||||
|
||||
this._error(new Error('bad content-type header, unknown content-type: '+this.headers['content-type']));
|
||||
};
|
||||
|
||||
IncomingForm.prototype._error = function(err) {
|
||||
if (this.error || this.ended) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.error = err;
|
||||
this.pause();
|
||||
this.emit('error', err);
|
||||
|
||||
if (Array.isArray(this.openedFiles)) {
|
||||
this.openedFiles.forEach(function(file) {
|
||||
file._writeStream.destroy();
|
||||
setTimeout(fs.unlink, 0, file.path);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
IncomingForm.prototype._parseContentLength = function() {
|
||||
this.bytesReceived = 0;
|
||||
if (this.headers['content-length']) {
|
||||
this.bytesExpected = parseInt(this.headers['content-length'], 10);
|
||||
} else if (this.headers['transfer-encoding'] === undefined) {
|
||||
this.bytesExpected = 0;
|
||||
}
|
||||
|
||||
if (this.bytesExpected !== null) {
|
||||
this.emit('progress', this.bytesReceived, this.bytesExpected);
|
||||
}
|
||||
};
|
||||
|
||||
IncomingForm.prototype._newParser = function() {
|
||||
return new MultipartParser();
|
||||
};
|
||||
|
||||
IncomingForm.prototype._initMultipart = function(boundary) {
|
||||
this.type = 'multipart';
|
||||
|
||||
var parser = new MultipartParser(),
|
||||
self = this,
|
||||
headerField,
|
||||
headerValue,
|
||||
part;
|
||||
|
||||
parser.initWithBoundary(boundary);
|
||||
|
||||
parser.onPartBegin = function() {
|
||||
part = new Stream();
|
||||
part.readable = true;
|
||||
part.headers = {};
|
||||
part.name = null;
|
||||
part.filename = null;
|
||||
part.mime = null;
|
||||
|
||||
part.transferEncoding = 'binary';
|
||||
part.transferBuffer = '';
|
||||
|
||||
headerField = '';
|
||||
headerValue = '';
|
||||
};
|
||||
|
||||
parser.onHeaderField = function(b, start, end) {
|
||||
headerField += b.toString(self.encoding, start, end);
|
||||
};
|
||||
|
||||
parser.onHeaderValue = function(b, start, end) {
|
||||
headerValue += b.toString(self.encoding, start, end);
|
||||
};
|
||||
|
||||
parser.onHeaderEnd = function() {
|
||||
headerField = headerField.toLowerCase();
|
||||
part.headers[headerField] = headerValue;
|
||||
|
||||
var m;
|
||||
if (headerField == 'content-disposition') {
|
||||
if (m = headerValue.match(/\bname="([^"]+)"/i)) {
|
||||
part.name = m[1];
|
||||
}
|
||||
|
||||
part.filename = self._fileName(headerValue);
|
||||
} else if (headerField == 'content-type') {
|
||||
part.mime = headerValue;
|
||||
} else if (headerField == 'content-transfer-encoding') {
|
||||
part.transferEncoding = headerValue.toLowerCase();
|
||||
}
|
||||
|
||||
headerField = '';
|
||||
headerValue = '';
|
||||
};
|
||||
|
||||
parser.onHeadersEnd = function() {
|
||||
switch(part.transferEncoding){
|
||||
case 'binary':
|
||||
case '7bit':
|
||||
case '8bit':
|
||||
parser.onPartData = function(b, start, end) {
|
||||
part.emit('data', b.slice(start, end));
|
||||
};
|
||||
|
||||
parser.onPartEnd = function() {
|
||||
part.emit('end');
|
||||
};
|
||||
break;
|
||||
|
||||
case 'base64':
|
||||
parser.onPartData = function(b, start, end) {
|
||||
part.transferBuffer += b.slice(start, end).toString('ascii');
|
||||
|
||||
/*
|
||||
four bytes (chars) in base64 converts to three bytes in binary
|
||||
encoding. So we should always work with a number of bytes that
|
||||
can be divided by 4, it will result in a number of buytes that
|
||||
can be divided vy 3.
|
||||
*/
|
||||
var offset = parseInt(part.transferBuffer.length / 4) * 4;
|
||||
part.emit('data', new Buffer(part.transferBuffer.substring(0, offset), 'base64'))
|
||||
part.transferBuffer = part.transferBuffer.substring(offset);
|
||||
};
|
||||
|
||||
parser.onPartEnd = function() {
|
||||
part.emit('data', new Buffer(part.transferBuffer, 'base64'))
|
||||
part.emit('end');
|
||||
};
|
||||
break;
|
||||
|
||||
default:
|
||||
return self._error(new Error('unknown transfer-encoding'));
|
||||
}
|
||||
|
||||
self.onPart(part);
|
||||
};
|
||||
|
||||
|
||||
parser.onEnd = function() {
|
||||
self.ended = true;
|
||||
self._maybeEnd();
|
||||
};
|
||||
|
||||
this._parser = parser;
|
||||
};
|
||||
|
||||
IncomingForm.prototype._fileName = function(headerValue) {
|
||||
var m = headerValue.match(/\bfilename="(.*?)"($|; )/i);
|
||||
if (!m) return;
|
||||
|
||||
var filename = m[1].substr(m[1].lastIndexOf('\\') + 1);
|
||||
filename = filename.replace(/%22/g, '"');
|
||||
filename = filename.replace(/&#([\d]{4});/g, function(m, code) {
|
||||
return String.fromCharCode(code);
|
||||
});
|
||||
return filename;
|
||||
};
|
||||
|
||||
IncomingForm.prototype._initUrlencoded = function() {
|
||||
this.type = 'urlencoded';
|
||||
|
||||
var parser = new QuerystringParser(this.maxFields)
|
||||
, 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._initOctetStream = function() {
|
||||
this.type = 'octet-stream';
|
||||
var filename = this.headers['x-file-name'];
|
||||
var mime = this.headers['content-type'];
|
||||
|
||||
var file = new File({
|
||||
path: this._uploadPath(filename),
|
||||
name: filename,
|
||||
type: mime
|
||||
});
|
||||
|
||||
file.open();
|
||||
|
||||
this.emit('fileBegin', filename, file);
|
||||
|
||||
this._flushing++;
|
||||
|
||||
var self = this;
|
||||
|
||||
self._parser = new OctetParser();
|
||||
|
||||
//Keep track of writes that haven't finished so we don't emit the file before it's done being written
|
||||
var outstandingWrites = 0;
|
||||
|
||||
self._parser.on('data', function(buffer){
|
||||
self.pause();
|
||||
outstandingWrites++;
|
||||
|
||||
file.write(buffer, function() {
|
||||
outstandingWrites--;
|
||||
self.resume();
|
||||
|
||||
if(self.ended){
|
||||
self._parser.emit('doneWritingFile');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
self._parser.on('end', function(){
|
||||
self._flushing--;
|
||||
self.ended = true;
|
||||
|
||||
var done = function(){
|
||||
self.emit('file', 'file', file);
|
||||
self._maybeEnd();
|
||||
};
|
||||
|
||||
if(outstandingWrites === 0){
|
||||
done();
|
||||
} else {
|
||||
self._parser.once('doneWritingFile', done);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
IncomingForm.prototype._initJSONencoded = function() {
|
||||
this.type = 'json';
|
||||
|
||||
var parser = new JSONParser()
|
||||
, self = this;
|
||||
|
||||
if (this.bytesExpected) {
|
||||
parser.initWithLength(this.bytesExpected);
|
||||
}
|
||||
|
||||
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(filename) {
|
||||
var name = '';
|
||||
for (var i = 0; i < 32; i++) {
|
||||
name += Math.floor(Math.random() * 16).toString(16);
|
||||
}
|
||||
|
||||
if (this.keepExtensions) {
|
||||
var ext = path.extname(filename);
|
||||
ext = ext.replace(/(\.[a-z0-9]+).*/, '$1');
|
||||
|
||||
name += ext;
|
||||
}
|
||||
|
||||
return path.join(this.uploadDir, name);
|
||||
};
|
||||
|
||||
IncomingForm.prototype._maybeEnd = function() {
|
||||
if (!this.ended || this._flushing || this.error) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.emit('end');
|
||||
};
|
||||
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
var IncomingForm = require('./incoming_form').IncomingForm;
|
||||
IncomingForm.IncomingForm = IncomingForm;
|
||||
module.exports = IncomingForm;
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
if (global.GENTLY) require = GENTLY.hijack(require);
|
||||
|
||||
var Buffer = require('buffer').Buffer
|
||||
|
||||
function JSONParser() {
|
||||
this.data = new Buffer('');
|
||||
this.bytesWritten = 0;
|
||||
};
|
||||
exports.JSONParser = JSONParser;
|
||||
|
||||
JSONParser.prototype.initWithLength = function(length) {
|
||||
this.data = new Buffer(length);
|
||||
}
|
||||
|
||||
JSONParser.prototype.write = function(buffer) {
|
||||
if (this.data.length >= this.bytesWritten + buffer.length) {
|
||||
buffer.copy(this.data, this.bytesWritten);
|
||||
} else {
|
||||
this.data = Buffer.concat([this.data, buffer]);
|
||||
}
|
||||
this.bytesWritten += buffer.length;
|
||||
return buffer.length;
|
||||
}
|
||||
|
||||
JSONParser.prototype.end = function() {
|
||||
try {
|
||||
var fields = JSON.parse(this.data.toString('utf8'))
|
||||
for (var field in fields) {
|
||||
this.onField(field, fields[field]);
|
||||
}
|
||||
} catch (e) {}
|
||||
this.data = null;
|
||||
|
||||
this.onEnd();
|
||||
}
|
||||
gerado
externo
-324
@@ -1,324 +0,0 @@
|
||||
var Buffer = require('buffer').Buffer,
|
||||
s = 0,
|
||||
S =
|
||||
{ PARSER_UNINITIALIZED: s++,
|
||||
START: s++,
|
||||
START_BOUNDARY: s++,
|
||||
HEADER_FIELD_START: s++,
|
||||
HEADER_FIELD: s++,
|
||||
HEADER_VALUE_START: s++,
|
||||
HEADER_VALUE: s++,
|
||||
HEADER_VALUE_ALMOST_DONE: s++,
|
||||
HEADERS_ALMOST_DONE: s++,
|
||||
PART_DATA_START: s++,
|
||||
PART_DATA: s++,
|
||||
PART_END: s++,
|
||||
END: s++
|
||||
},
|
||||
|
||||
f = 1,
|
||||
F =
|
||||
{ PART_BOUNDARY: f,
|
||||
LAST_BOUNDARY: f *= 2
|
||||
},
|
||||
|
||||
LF = 10,
|
||||
CR = 13,
|
||||
SPACE = 32,
|
||||
HYPHEN = 45,
|
||||
COLON = 58,
|
||||
A = 97,
|
||||
Z = 122,
|
||||
|
||||
lower = function(c) {
|
||||
return c | 0x20;
|
||||
};
|
||||
|
||||
for (s in S) {
|
||||
exports[s] = S[s];
|
||||
}
|
||||
|
||||
function MultipartParser() {
|
||||
this.boundary = null;
|
||||
this.boundaryChars = null;
|
||||
this.lookbehind = null;
|
||||
this.state = S.PARSER_UNINITIALIZED;
|
||||
|
||||
this.index = null;
|
||||
this.flags = 0;
|
||||
};
|
||||
exports.MultipartParser = MultipartParser;
|
||||
|
||||
MultipartParser.stateToString = function(stateNumber) {
|
||||
for (var state in S) {
|
||||
var number = S[state];
|
||||
if (number === stateNumber) return state;
|
||||
}
|
||||
};
|
||||
|
||||
MultipartParser.prototype.initWithBoundary = function(str) {
|
||||
this.boundary = new Buffer(str.length+4);
|
||||
this.boundary.write('\r\n--', 'ascii', 0);
|
||||
this.boundary.write(str, 'ascii', 4);
|
||||
this.lookbehind = new Buffer(this.boundary.length+8);
|
||||
this.state = S.START;
|
||||
|
||||
this.boundaryChars = {};
|
||||
for (var i = 0; i < this.boundary.length; i++) {
|
||||
this.boundaryChars[this.boundary[i]] = true;
|
||||
}
|
||||
};
|
||||
|
||||
MultipartParser.prototype.write = function(buffer) {
|
||||
var self = this,
|
||||
i = 0,
|
||||
len = buffer.length,
|
||||
prevIndex = this.index,
|
||||
index = this.index,
|
||||
state = this.state,
|
||||
flags = this.flags,
|
||||
lookbehind = this.lookbehind,
|
||||
boundary = this.boundary,
|
||||
boundaryChars = this.boundaryChars,
|
||||
boundaryLength = this.boundary.length,
|
||||
boundaryEnd = boundaryLength - 1,
|
||||
bufferLength = buffer.length,
|
||||
c,
|
||||
cl,
|
||||
|
||||
mark = function(name) {
|
||||
self[name+'Mark'] = i;
|
||||
},
|
||||
clear = function(name) {
|
||||
delete self[name+'Mark'];
|
||||
},
|
||||
callback = function(name, buffer, start, end) {
|
||||
if (start !== undefined && start === end) {
|
||||
return;
|
||||
}
|
||||
|
||||
var callbackSymbol = 'on'+name.substr(0, 1).toUpperCase()+name.substr(1);
|
||||
if (callbackSymbol in self) {
|
||||
self[callbackSymbol](buffer, start, end);
|
||||
}
|
||||
},
|
||||
dataCallback = function(name, clear) {
|
||||
var markSymbol = name+'Mark';
|
||||
if (!(markSymbol in self)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!clear) {
|
||||
callback(name, buffer, self[markSymbol], buffer.length);
|
||||
self[markSymbol] = 0;
|
||||
} else {
|
||||
callback(name, buffer, self[markSymbol], i);
|
||||
delete self[markSymbol];
|
||||
}
|
||||
};
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
c = buffer[i];
|
||||
switch (state) {
|
||||
case S.PARSER_UNINITIALIZED:
|
||||
return i;
|
||||
case S.START:
|
||||
index = 0;
|
||||
state = S.START_BOUNDARY;
|
||||
case S.START_BOUNDARY:
|
||||
if (index == boundary.length - 2) {
|
||||
if (c != CR) {
|
||||
return i;
|
||||
}
|
||||
index++;
|
||||
break;
|
||||
} else if (index - 1 == boundary.length - 2) {
|
||||
if (c != LF) {
|
||||
return i;
|
||||
}
|
||||
index = 0;
|
||||
callback('partBegin');
|
||||
state = S.HEADER_FIELD_START;
|
||||
break;
|
||||
}
|
||||
|
||||
if (c != boundary[index+2]) {
|
||||
index = -2;
|
||||
}
|
||||
if (c == boundary[index+2]) {
|
||||
index++;
|
||||
}
|
||||
break;
|
||||
case S.HEADER_FIELD_START:
|
||||
state = S.HEADER_FIELD;
|
||||
mark('headerField');
|
||||
index = 0;
|
||||
case S.HEADER_FIELD:
|
||||
if (c == CR) {
|
||||
clear('headerField');
|
||||
state = S.HEADERS_ALMOST_DONE;
|
||||
break;
|
||||
}
|
||||
|
||||
index++;
|
||||
if (c == HYPHEN) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (c == COLON) {
|
||||
if (index == 1) {
|
||||
// empty header field
|
||||
return i;
|
||||
}
|
||||
dataCallback('headerField', true);
|
||||
state = S.HEADER_VALUE_START;
|
||||
break;
|
||||
}
|
||||
|
||||
cl = lower(c);
|
||||
if (cl < A || cl > Z) {
|
||||
return i;
|
||||
}
|
||||
break;
|
||||
case S.HEADER_VALUE_START:
|
||||
if (c == SPACE) {
|
||||
break;
|
||||
}
|
||||
|
||||
mark('headerValue');
|
||||
state = S.HEADER_VALUE;
|
||||
case S.HEADER_VALUE:
|
||||
if (c == CR) {
|
||||
dataCallback('headerValue', true);
|
||||
callback('headerEnd');
|
||||
state = S.HEADER_VALUE_ALMOST_DONE;
|
||||
}
|
||||
break;
|
||||
case S.HEADER_VALUE_ALMOST_DONE:
|
||||
if (c != LF) {
|
||||
return i;
|
||||
}
|
||||
state = S.HEADER_FIELD_START;
|
||||
break;
|
||||
case S.HEADERS_ALMOST_DONE:
|
||||
if (c != LF) {
|
||||
return i;
|
||||
}
|
||||
|
||||
callback('headersEnd');
|
||||
state = S.PART_DATA_START;
|
||||
break;
|
||||
case S.PART_DATA_START:
|
||||
state = S.PART_DATA;
|
||||
mark('partData');
|
||||
case S.PART_DATA:
|
||||
prevIndex = index;
|
||||
|
||||
if (index == 0) {
|
||||
// boyer-moore derrived algorithm to safely skip non-boundary data
|
||||
i += boundaryEnd;
|
||||
while (i < bufferLength && !(buffer[i] in boundaryChars)) {
|
||||
i += boundaryLength;
|
||||
}
|
||||
i -= boundaryEnd;
|
||||
c = buffer[i];
|
||||
}
|
||||
|
||||
if (index < boundary.length) {
|
||||
if (boundary[index] == c) {
|
||||
if (index == 0) {
|
||||
dataCallback('partData', true);
|
||||
}
|
||||
index++;
|
||||
} else {
|
||||
index = 0;
|
||||
}
|
||||
} else if (index == boundary.length) {
|
||||
index++;
|
||||
if (c == CR) {
|
||||
// CR = part boundary
|
||||
flags |= F.PART_BOUNDARY;
|
||||
} else if (c == HYPHEN) {
|
||||
// HYPHEN = end boundary
|
||||
flags |= F.LAST_BOUNDARY;
|
||||
} else {
|
||||
index = 0;
|
||||
}
|
||||
} else if (index - 1 == boundary.length) {
|
||||
if (flags & F.PART_BOUNDARY) {
|
||||
index = 0;
|
||||
if (c == LF) {
|
||||
// unset the PART_BOUNDARY flag
|
||||
flags &= ~F.PART_BOUNDARY;
|
||||
callback('partEnd');
|
||||
callback('partBegin');
|
||||
state = S.HEADER_FIELD_START;
|
||||
break;
|
||||
}
|
||||
} else if (flags & F.LAST_BOUNDARY) {
|
||||
if (c == HYPHEN) {
|
||||
callback('partEnd');
|
||||
callback('end');
|
||||
state = S.END;
|
||||
} else {
|
||||
index = 0;
|
||||
}
|
||||
} else {
|
||||
index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (index > 0) {
|
||||
// when matching a possible boundary, keep a lookbehind reference
|
||||
// in case it turns out to be a false lead
|
||||
lookbehind[index-1] = c;
|
||||
} else if (prevIndex > 0) {
|
||||
// if our boundary turned out to be rubbish, the captured lookbehind
|
||||
// belongs to partData
|
||||
callback('partData', lookbehind, 0, prevIndex);
|
||||
prevIndex = 0;
|
||||
mark('partData');
|
||||
|
||||
// reconsider the current character even so it interrupted the sequence
|
||||
// it could be the beginning of a new sequence
|
||||
i--;
|
||||
}
|
||||
|
||||
break;
|
||||
case S.END:
|
||||
break;
|
||||
default:
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
dataCallback('headerField');
|
||||
dataCallback('headerValue');
|
||||
dataCallback('partData');
|
||||
|
||||
this.index = index;
|
||||
this.state = state;
|
||||
this.flags = flags;
|
||||
|
||||
return len;
|
||||
};
|
||||
|
||||
MultipartParser.prototype.end = function() {
|
||||
var callback = function(self, name) {
|
||||
var callbackSymbol = 'on'+name.substr(0, 1).toUpperCase()+name.substr(1);
|
||||
if (callbackSymbol in self) {
|
||||
self[callbackSymbol]();
|
||||
}
|
||||
};
|
||||
if ((this.state == S.HEADER_FIELD_START && this.index == 0) ||
|
||||
(this.state == S.PART_DATA && this.index == this.boundary.length)) {
|
||||
callback(this, 'partEnd');
|
||||
callback(this, 'end');
|
||||
} else if (this.state != S.END) {
|
||||
return new Error('MultipartParser.end(): stream ended unexpectedly: ' + this.explain());
|
||||
}
|
||||
};
|
||||
|
||||
MultipartParser.prototype.explain = function() {
|
||||
return 'state = ' + MultipartParser.stateToString(this.state);
|
||||
};
|
||||
gerado
externo
-20
@@ -1,20 +0,0 @@
|
||||
var EventEmitter = require('events').EventEmitter
|
||||
, util = require('util');
|
||||
|
||||
function OctetParser(options){
|
||||
if(!(this instanceof OctetParser)) return new OctetParser(options);
|
||||
EventEmitter.call(this);
|
||||
}
|
||||
|
||||
util.inherits(OctetParser, EventEmitter);
|
||||
|
||||
exports.OctetParser = OctetParser;
|
||||
|
||||
OctetParser.prototype.write = function(buffer) {
|
||||
this.emit('data', buffer);
|
||||
return buffer.length;
|
||||
};
|
||||
|
||||
OctetParser.prototype.end = function() {
|
||||
this.emit('end');
|
||||
};
|
||||
gerado
externo
-27
@@ -1,27 +0,0 @@
|
||||
if (global.GENTLY) require = GENTLY.hijack(require);
|
||||
|
||||
// This is a buffering parser, not quite as nice as the multipart one.
|
||||
// If I find time I'll rewrite this to be fully streaming as well
|
||||
var querystring = require('querystring');
|
||||
|
||||
function QuerystringParser(maxKeys) {
|
||||
this.maxKeys = maxKeys;
|
||||
this.buffer = '';
|
||||
};
|
||||
exports.QuerystringParser = QuerystringParser;
|
||||
|
||||
QuerystringParser.prototype.write = function(buffer) {
|
||||
this.buffer += buffer.toString('ascii');
|
||||
return buffer.length;
|
||||
};
|
||||
|
||||
QuerystringParser.prototype.end = function() {
|
||||
var fields = querystring.parse(this.buffer, '&', '=', { maxKeys: this.maxKeys });
|
||||
for (var field in fields) {
|
||||
this.onField(field, fields[field]);
|
||||
}
|
||||
this.buffer = '';
|
||||
|
||||
this.onEnd();
|
||||
};
|
||||
|
||||
-42
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
-18
@@ -1,18 +0,0 @@
|
||||
var path = require('path');
|
||||
|
||||
var root = path.join(__dirname, '../');
|
||||
exports.dir = {
|
||||
root : root,
|
||||
lib : root + '/lib',
|
||||
fixture : root + '/test/fixture',
|
||||
tmp : root + '/test/tmp',
|
||||
};
|
||||
|
||||
exports.port = 13532;
|
||||
|
||||
exports.formidable = require('..');
|
||||
exports.assert = require('assert');
|
||||
|
||||
exports.require = function(lib) {
|
||||
return require(exports.dir.lib + '/' + lib);
|
||||
};
|
||||
gerado
externo
-24
@@ -1,24 +0,0 @@
|
||||
module.exports['menu_seperator.png.http'] = [
|
||||
{type: 'file', name: 'image', filename: 'menu_separator.png', fixture: 'menu_separator.png',
|
||||
sha1: 'c845ca3ea794be298f2a1b79769b71939eaf4e54'}
|
||||
];
|
||||
|
||||
module.exports['beta-sticker-1.png.http'] = [
|
||||
{type: 'file', name: 'sticker', filename: 'beta-sticker-1.png', fixture: 'beta-sticker-1.png',
|
||||
sha1: '6abbcffd12b4ada5a6a084fe9e4584f846331bc4'}
|
||||
];
|
||||
|
||||
module.exports['blank.gif.http'] = [
|
||||
{type: 'file', name: 'file', filename: 'blank.gif', fixture: 'blank.gif',
|
||||
sha1: 'a1fdee122b95748d81cee426d717c05b5174fe96'}
|
||||
];
|
||||
|
||||
module.exports['binaryfile.tar.gz.http'] = [
|
||||
{type: 'file', name: 'file', filename: 'binaryfile.tar.gz', fixture: 'binaryfile.tar.gz',
|
||||
sha1: 'cfabe13b348e5e69287d677860880c52a69d2155'}
|
||||
];
|
||||
|
||||
module.exports['plain.txt.http'] = [
|
||||
{type: 'file', name: 'file', filename: 'plain.txt', fixture: 'plain.txt',
|
||||
sha1: 'b31d07bac24ac32734de88b3687dddb10e976872'}
|
||||
];
|
||||
gerado
externo
-6
@@ -1,6 +0,0 @@
|
||||
module.exports = {
|
||||
'empty.http': [],
|
||||
'empty-urlencoded.http': [],
|
||||
'empty-multipart.http': [],
|
||||
'minimal.http': [],
|
||||
};
|
||||
gerado
externo
-96
@@ -1,96 +0,0 @@
|
||||
var hashish = require('hashish');
|
||||
var fs = require('fs');
|
||||
var findit = require('findit');
|
||||
var path = require('path');
|
||||
var http = require('http');
|
||||
var net = require('net');
|
||||
var assert = require('assert');
|
||||
|
||||
var common = require('../common');
|
||||
var formidable = common.formidable;
|
||||
|
||||
var server = http.createServer();
|
||||
server.listen(common.port, findFixtures);
|
||||
|
||||
function findFixtures() {
|
||||
var fixtures = [];
|
||||
findit
|
||||
.sync(common.dir.fixture + '/js')
|
||||
.forEach(function(jsPath) {
|
||||
if (!/\.js$/.test(jsPath)) return;
|
||||
|
||||
var group = path.basename(jsPath, '.js');
|
||||
hashish.forEach(require(jsPath), function(fixture, name) {
|
||||
fixtures.push({
|
||||
name : group + '/' + name,
|
||||
fixture : fixture,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
testNext(fixtures);
|
||||
}
|
||||
|
||||
function testNext(fixtures) {
|
||||
var fixture = fixtures.shift();
|
||||
if (!fixture) return server.close();
|
||||
|
||||
var name = fixture.name;
|
||||
var fixture = fixture.fixture;
|
||||
|
||||
uploadFixture(name, function(err, parts) {
|
||||
if (err) throw err;
|
||||
|
||||
fixture.forEach(function(expectedPart, i) {
|
||||
var parsedPart = parts[i];
|
||||
assert.equal(parsedPart.type, expectedPart.type);
|
||||
assert.equal(parsedPart.name, expectedPart.name);
|
||||
|
||||
if (parsedPart.type === 'file') {
|
||||
var file = parsedPart.value;
|
||||
assert.equal(file.name, expectedPart.filename);
|
||||
if(expectedPart.sha1) assert.equal(file.hash, expectedPart.sha1);
|
||||
}
|
||||
});
|
||||
|
||||
testNext(fixtures);
|
||||
});
|
||||
};
|
||||
|
||||
function uploadFixture(name, cb) {
|
||||
server.once('request', function(req, res) {
|
||||
var form = new formidable.IncomingForm();
|
||||
form.uploadDir = common.dir.tmp;
|
||||
form.hash = "sha1";
|
||||
form.parse(req);
|
||||
|
||||
function callback() {
|
||||
var realCallback = cb;
|
||||
cb = function() {};
|
||||
realCallback.apply(null, arguments);
|
||||
}
|
||||
|
||||
var parts = [];
|
||||
form
|
||||
.on('error', callback)
|
||||
.on('fileBegin', function(name, value) {
|
||||
parts.push({type: 'file', name: name, value: value});
|
||||
})
|
||||
.on('field', function(name, value) {
|
||||
parts.push({type: 'field', name: name, value: value});
|
||||
})
|
||||
.on('end', function() {
|
||||
res.end('OK');
|
||||
callback(null, parts);
|
||||
});
|
||||
});
|
||||
|
||||
var socket = net.createConnection(common.port);
|
||||
var file = fs.createReadStream(common.dir.fixture + '/http/' + name);
|
||||
|
||||
file.pipe(socket, {end: false});
|
||||
socket.on('data', function () {
|
||||
socket.end();
|
||||
});
|
||||
|
||||
}
|
||||
gerado
externo
-38
@@ -1,38 +0,0 @@
|
||||
var common = require('../common');
|
||||
var formidable = common.formidable;
|
||||
var http = require('http');
|
||||
var assert = require('assert');
|
||||
|
||||
var testData = {
|
||||
numbers: [1, 2, 3, 4, 5],
|
||||
nested: { key: 'value' }
|
||||
};
|
||||
|
||||
var server = http.createServer(function(req, res) {
|
||||
var form = new formidable.IncomingForm();
|
||||
|
||||
form.parse(req, function(err, fields, files) {
|
||||
assert.deepEqual(fields, testData);
|
||||
|
||||
res.end();
|
||||
server.close();
|
||||
});
|
||||
});
|
||||
|
||||
var port = common.port;
|
||||
|
||||
server.listen(port, function(err){
|
||||
assert.equal(err, null);
|
||||
|
||||
var request = http.request({
|
||||
port: port,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
|
||||
request.write(JSON.stringify(testData));
|
||||
request.end();
|
||||
});
|
||||
|
||||
gerado
externo
-45
@@ -1,45 +0,0 @@
|
||||
var common = require('../common');
|
||||
var formidable = common.formidable;
|
||||
var http = require('http');
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var hashish = require('hashish');
|
||||
var assert = require('assert');
|
||||
|
||||
var testFilePath = path.join(__dirname, '../fixture/file/binaryfile.tar.gz');
|
||||
|
||||
var server = http.createServer(function(req, res) {
|
||||
var form = new formidable.IncomingForm();
|
||||
|
||||
form.parse(req, function(err, fields, files) {
|
||||
assert.equal(hashish(files).length, 1);
|
||||
var file = files.file;
|
||||
|
||||
assert.equal(file.size, 301);
|
||||
|
||||
var uploaded = fs.readFileSync(file.path);
|
||||
var original = fs.readFileSync(testFilePath);
|
||||
|
||||
assert.deepEqual(uploaded, original);
|
||||
|
||||
res.end();
|
||||
server.close();
|
||||
});
|
||||
});
|
||||
|
||||
var port = common.port;
|
||||
|
||||
server.listen(port, function(err){
|
||||
assert.equal(err, null);
|
||||
|
||||
var request = http.request({
|
||||
port: port,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/octet-stream'
|
||||
}
|
||||
});
|
||||
|
||||
fs.createReadStream(testFilePath).pipe(request);
|
||||
});
|
||||
|
||||
gerado
externo
-24
@@ -1,24 +0,0 @@
|
||||
var path = require('path'),
|
||||
fs = require('fs');
|
||||
|
||||
try {
|
||||
global.Gently = require('gently');
|
||||
} catch (e) {
|
||||
throw new Error('this test suite requires node-gently');
|
||||
}
|
||||
|
||||
exports.lib = path.join(__dirname, '../../lib');
|
||||
|
||||
global.GENTLY = new Gently();
|
||||
|
||||
global.assert = require('assert');
|
||||
global.TEST_PORT = 13532;
|
||||
global.TEST_FIXTURES = path.join(__dirname, '../fixture');
|
||||
global.TEST_TMP = path.join(__dirname, '../tmp');
|
||||
|
||||
// Stupid new feature in node that complains about gently attaching too many
|
||||
// listeners to process 'exit'. This is a workaround until I can think of a
|
||||
// better way to deal with this.
|
||||
if (process.setMaxListeners) {
|
||||
process.setMaxListeners(10000);
|
||||
}
|
||||
gerado
externo
-80
@@ -1,80 +0,0 @@
|
||||
var common = require('../common');
|
||||
var CHUNK_LENGTH = 10,
|
||||
multipartParser = require(common.lib + '/multipart_parser'),
|
||||
MultipartParser = multipartParser.MultipartParser,
|
||||
parser = new MultipartParser(),
|
||||
fixtures = require(TEST_FIXTURES + '/multipart'),
|
||||
Buffer = require('buffer').Buffer;
|
||||
|
||||
Object.keys(fixtures).forEach(function(name) {
|
||||
var fixture = fixtures[name],
|
||||
buffer = new Buffer(Buffer.byteLength(fixture.raw, 'binary')),
|
||||
offset = 0,
|
||||
chunk,
|
||||
nparsed,
|
||||
|
||||
parts = [],
|
||||
part = null,
|
||||
headerField,
|
||||
headerValue,
|
||||
endCalled = '';
|
||||
|
||||
parser.initWithBoundary(fixture.boundary);
|
||||
parser.onPartBegin = function() {
|
||||
part = {headers: {}, data: ''};
|
||||
parts.push(part);
|
||||
headerField = '';
|
||||
headerValue = '';
|
||||
};
|
||||
|
||||
parser.onHeaderField = function(b, start, end) {
|
||||
headerField += b.toString('ascii', start, end);
|
||||
};
|
||||
|
||||
parser.onHeaderValue = function(b, start, end) {
|
||||
headerValue += b.toString('ascii', start, end);
|
||||
}
|
||||
|
||||
parser.onHeaderEnd = function() {
|
||||
part.headers[headerField] = headerValue;
|
||||
headerField = '';
|
||||
headerValue = '';
|
||||
};
|
||||
|
||||
parser.onPartData = function(b, start, end) {
|
||||
var str = b.toString('ascii', start, end);
|
||||
part.data += b.slice(start, end);
|
||||
}
|
||||
|
||||
parser.onEnd = function() {
|
||||
endCalled = true;
|
||||
}
|
||||
|
||||
buffer.write(fixture.raw, 'binary', 0);
|
||||
|
||||
while (offset < buffer.length) {
|
||||
if (offset + CHUNK_LENGTH < buffer.length) {
|
||||
chunk = buffer.slice(offset, offset+CHUNK_LENGTH);
|
||||
} else {
|
||||
chunk = buffer.slice(offset, buffer.length);
|
||||
}
|
||||
offset = offset + CHUNK_LENGTH;
|
||||
|
||||
nparsed = parser.write(chunk);
|
||||
if (nparsed != chunk.length) {
|
||||
if (fixture.expectError) {
|
||||
return;
|
||||
}
|
||||
puts('-- ERROR --');
|
||||
p(chunk.toString('ascii'));
|
||||
throw new Error(chunk.length+' bytes written, but only '+nparsed+' bytes parsed!');
|
||||
}
|
||||
}
|
||||
|
||||
if (fixture.expectError) {
|
||||
throw new Error('expected parse error did not happen');
|
||||
}
|
||||
|
||||
assert.ok(endCalled);
|
||||
assert.deepEqual(parts, fixture.parts);
|
||||
});
|
||||
gerado
externo
-104
@@ -1,104 +0,0 @@
|
||||
var common = require('../common');
|
||||
var WriteStreamStub = GENTLY.stub('fs', 'WriteStream');
|
||||
|
||||
var File = require(common.lib + '/file'),
|
||||
EventEmitter = require('events').EventEmitter,
|
||||
file,
|
||||
gently;
|
||||
|
||||
function test(test) {
|
||||
gently = new Gently();
|
||||
file = new File();
|
||||
test();
|
||||
gently.verify(test.name);
|
||||
}
|
||||
|
||||
test(function constructor() {
|
||||
assert.ok(file instanceof EventEmitter);
|
||||
assert.strictEqual(file.size, 0);
|
||||
assert.strictEqual(file.path, null);
|
||||
assert.strictEqual(file.name, null);
|
||||
assert.strictEqual(file.type, null);
|
||||
assert.strictEqual(file.lastModifiedDate, null);
|
||||
|
||||
assert.strictEqual(file._writeStream, null);
|
||||
|
||||
(function testSetProperties() {
|
||||
var file2 = new File({foo: 'bar'});
|
||||
assert.equal(file2.foo, 'bar');
|
||||
})();
|
||||
});
|
||||
|
||||
test(function open() {
|
||||
var WRITE_STREAM;
|
||||
file.path = '/foo';
|
||||
|
||||
gently.expect(WriteStreamStub, 'new', function (path) {
|
||||
WRITE_STREAM = this;
|
||||
assert.strictEqual(path, file.path);
|
||||
});
|
||||
|
||||
file.open();
|
||||
assert.strictEqual(file._writeStream, WRITE_STREAM);
|
||||
});
|
||||
|
||||
test(function write() {
|
||||
var BUFFER = {length: 10},
|
||||
CB_STUB,
|
||||
CB = function() {
|
||||
CB_STUB.apply(this, arguments);
|
||||
};
|
||||
|
||||
file._writeStream = {};
|
||||
|
||||
gently.expect(file._writeStream, 'write', function (buffer, cb) {
|
||||
assert.strictEqual(buffer, BUFFER);
|
||||
|
||||
gently.expect(file, 'emit', function (event, bytesWritten) {
|
||||
assert.ok(file.lastModifiedDate instanceof Date);
|
||||
assert.equal(event, 'progress');
|
||||
assert.equal(bytesWritten, file.size);
|
||||
});
|
||||
|
||||
CB_STUB = gently.expect(function writeCb() {
|
||||
assert.equal(file.size, 10);
|
||||
});
|
||||
|
||||
cb();
|
||||
|
||||
gently.expect(file, 'emit', function (event, bytesWritten) {
|
||||
assert.equal(event, 'progress');
|
||||
assert.equal(bytesWritten, file.size);
|
||||
});
|
||||
|
||||
CB_STUB = gently.expect(function writeCb() {
|
||||
assert.equal(file.size, 20);
|
||||
});
|
||||
|
||||
cb();
|
||||
});
|
||||
|
||||
file.write(BUFFER, CB);
|
||||
});
|
||||
|
||||
test(function end() {
|
||||
var CB_STUB,
|
||||
CB = function() {
|
||||
CB_STUB.apply(this, arguments);
|
||||
};
|
||||
|
||||
file._writeStream = {};
|
||||
|
||||
gently.expect(file._writeStream, 'end', function (cb) {
|
||||
gently.expect(file, 'emit', function (event) {
|
||||
assert.equal(event, 'end');
|
||||
});
|
||||
|
||||
CB_STUB = gently.expect(function endCb() {
|
||||
});
|
||||
|
||||
cb();
|
||||
});
|
||||
|
||||
file.end(CB);
|
||||
});
|
||||
gerado
externo
-756
@@ -1,756 +0,0 @@
|
||||
var common = require('../common');
|
||||
var MultipartParserStub = GENTLY.stub('./multipart_parser', 'MultipartParser'),
|
||||
QuerystringParserStub = GENTLY.stub('./querystring_parser', 'QuerystringParser'),
|
||||
EventEmitterStub = GENTLY.stub('events', 'EventEmitter'),
|
||||
StreamStub = GENTLY.stub('stream', 'Stream'),
|
||||
FileStub = GENTLY.stub('./file');
|
||||
|
||||
var formidable = require(common.lib + '/index'),
|
||||
IncomingForm = formidable.IncomingForm,
|
||||
events = require('events'),
|
||||
fs = require('fs'),
|
||||
path = require('path'),
|
||||
Buffer = require('buffer').Buffer,
|
||||
fixtures = require(TEST_FIXTURES + '/multipart'),
|
||||
form,
|
||||
gently;
|
||||
|
||||
function test(test) {
|
||||
gently = new Gently();
|
||||
gently.expect(EventEmitterStub, 'call');
|
||||
form = new IncomingForm();
|
||||
test();
|
||||
gently.verify(test.name);
|
||||
}
|
||||
|
||||
test(function constructor() {
|
||||
assert.strictEqual(form.error, null);
|
||||
assert.strictEqual(form.ended, false);
|
||||
assert.strictEqual(form.type, null);
|
||||
assert.strictEqual(form.headers, null);
|
||||
assert.strictEqual(form.keepExtensions, false);
|
||||
// Can't assume dir === '/tmp' for portability
|
||||
// assert.strictEqual(form.uploadDir, '/tmp');
|
||||
// Make sure it is a directory instead
|
||||
assert.doesNotThrow(function () {
|
||||
assert(fs.statSync(form.uploadDir).isDirectory());
|
||||
});
|
||||
assert.strictEqual(form.encoding, 'utf-8');
|
||||
assert.strictEqual(form.bytesReceived, null);
|
||||
assert.strictEqual(form.bytesExpected, null);
|
||||
assert.strictEqual(form.maxFieldsSize, 2 * 1024 * 1024);
|
||||
assert.strictEqual(form._parser, null);
|
||||
assert.strictEqual(form._flushing, 0);
|
||||
assert.strictEqual(form._fieldsSize, 0);
|
||||
assert.ok(form instanceof EventEmitterStub);
|
||||
assert.equal(form.constructor.name, 'IncomingForm');
|
||||
|
||||
(function testSimpleConstructor() {
|
||||
gently.expect(EventEmitterStub, 'call');
|
||||
var form = IncomingForm();
|
||||
assert.ok(form instanceof IncomingForm);
|
||||
})();
|
||||
|
||||
(function testSimpleConstructorShortcut() {
|
||||
gently.expect(EventEmitterStub, 'call');
|
||||
var form = formidable();
|
||||
assert.ok(form instanceof IncomingForm);
|
||||
})();
|
||||
});
|
||||
|
||||
test(function parse() {
|
||||
var REQ = {headers: {}}
|
||||
, emit = {};
|
||||
|
||||
gently.expect(form, 'writeHeaders', function(headers) {
|
||||
assert.strictEqual(headers, REQ.headers);
|
||||
});
|
||||
|
||||
var EVENTS = ['error', 'aborted', 'data', 'end'];
|
||||
gently.expect(REQ, 'on', EVENTS.length, function(event, fn) {
|
||||
assert.equal(event, EVENTS.shift());
|
||||
emit[event] = fn;
|
||||
return this;
|
||||
});
|
||||
|
||||
form.parse(REQ);
|
||||
|
||||
(function testPause() {
|
||||
gently.expect(REQ, 'pause');
|
||||
assert.strictEqual(form.pause(), true);
|
||||
})();
|
||||
|
||||
(function testPauseCriticalException() {
|
||||
form.ended = false;
|
||||
|
||||
var ERR = new Error('dasdsa');
|
||||
gently.expect(REQ, 'pause', function() {
|
||||
throw ERR;
|
||||
});
|
||||
|
||||
gently.expect(form, '_error', function(err) {
|
||||
assert.strictEqual(err, ERR);
|
||||
});
|
||||
|
||||
assert.strictEqual(form.pause(), false);
|
||||
})();
|
||||
|
||||
(function testPauseHarmlessException() {
|
||||
form.ended = true;
|
||||
|
||||
var ERR = new Error('dasdsa');
|
||||
gently.expect(REQ, 'pause', function() {
|
||||
throw ERR;
|
||||
});
|
||||
|
||||
assert.strictEqual(form.pause(), false);
|
||||
})();
|
||||
|
||||
(function testResume() {
|
||||
gently.expect(REQ, 'resume');
|
||||
assert.strictEqual(form.resume(), true);
|
||||
})();
|
||||
|
||||
(function testResumeCriticalException() {
|
||||
form.ended = false;
|
||||
|
||||
var ERR = new Error('dasdsa');
|
||||
gently.expect(REQ, 'resume', function() {
|
||||
throw ERR;
|
||||
});
|
||||
|
||||
gently.expect(form, '_error', function(err) {
|
||||
assert.strictEqual(err, ERR);
|
||||
});
|
||||
|
||||
assert.strictEqual(form.resume(), false);
|
||||
})();
|
||||
|
||||
(function testResumeHarmlessException() {
|
||||
form.ended = true;
|
||||
|
||||
var ERR = new Error('dasdsa');
|
||||
gently.expect(REQ, 'resume', function() {
|
||||
throw ERR;
|
||||
});
|
||||
|
||||
assert.strictEqual(form.resume(), false);
|
||||
})();
|
||||
|
||||
(function testEmitError() {
|
||||
var ERR = new Error('something bad happened');
|
||||
gently.expect(form, '_error',function(err) {
|
||||
assert.strictEqual(err, ERR);
|
||||
});
|
||||
emit.error(ERR);
|
||||
})();
|
||||
|
||||
(function testEmitAborted() {
|
||||
gently.expect(form, 'emit',function(event) {
|
||||
assert.equal(event, 'aborted');
|
||||
});
|
||||
gently.expect(form, '_error');
|
||||
|
||||
emit.aborted();
|
||||
})();
|
||||
|
||||
|
||||
(function testEmitData() {
|
||||
var BUFFER = [1, 2, 3];
|
||||
gently.expect(form, 'write', function(buffer) {
|
||||
assert.strictEqual(buffer, BUFFER);
|
||||
});
|
||||
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, 'on', 4, function(event, fn) {
|
||||
if (event == 'field') {
|
||||
fn('field1', 'foo');
|
||||
fn('field1', 'bar');
|
||||
fn('field2', 'nice');
|
||||
}
|
||||
|
||||
if (event == 'file') {
|
||||
fn('file1', '1');
|
||||
fn('file1', '2');
|
||||
fn('file2', '3');
|
||||
}
|
||||
|
||||
if (event == 'end') {
|
||||
fn();
|
||||
}
|
||||
return this;
|
||||
});
|
||||
|
||||
gently.expect(form, 'writeHeaders');
|
||||
|
||||
gently.expect(REQ, 'on', 4, function() {
|
||||
return this;
|
||||
});
|
||||
|
||||
var parseCbOk = function (err, fields, files) {
|
||||
assert.deepEqual(fields, {field1: 'bar', field2: 'nice'});
|
||||
assert.deepEqual(files, {file1: '2', file2: '3'});
|
||||
};
|
||||
form.parse(REQ, parseCbOk);
|
||||
|
||||
var ERR = new Error('test');
|
||||
gently.expect(form, 'on', 3, function(event, fn) {
|
||||
if (event == 'field') {
|
||||
fn('foo', 'bar');
|
||||
}
|
||||
|
||||
if (event == 'error') {
|
||||
fn(ERR);
|
||||
gently.expect(form, 'on');
|
||||
gently.expect(form, 'writeHeaders');
|
||||
gently.expect(REQ, 'on', 4, function() {
|
||||
return this;
|
||||
});
|
||||
}
|
||||
return this;
|
||||
});
|
||||
|
||||
form.parse(REQ, function parseCbErr(err, fields, files) {
|
||||
assert.strictEqual(err, ERR);
|
||||
assert.deepEqual(fields, {foo: 'bar'});
|
||||
});
|
||||
})();
|
||||
|
||||
(function testWriteOrder() {
|
||||
gently.expect(EventEmitterStub, 'call');
|
||||
var form = new IncomingForm();
|
||||
var REQ = new events.EventEmitter();
|
||||
var BUF = {};
|
||||
var DATACB = null;
|
||||
|
||||
REQ.on('newListener', function(event, fn) {
|
||||
if ('data' === event) fn(BUF);
|
||||
});
|
||||
|
||||
gently.expect(form, 'writeHeaders');
|
||||
gently.expect(form, 'write', function(buf) {
|
||||
assert.strictEqual(buf, BUF);
|
||||
});
|
||||
|
||||
form.parse(REQ);
|
||||
})();
|
||||
});
|
||||
|
||||
test(function pause() {
|
||||
assert.strictEqual(form.pause(), false);
|
||||
});
|
||||
|
||||
test(function resume() {
|
||||
assert.strictEqual(form.resume(), false);
|
||||
});
|
||||
|
||||
|
||||
test(function writeHeaders() {
|
||||
var HEADERS = {};
|
||||
gently.expect(form, '_parseContentLength');
|
||||
gently.expect(form, '_parseContentType');
|
||||
|
||||
form.writeHeaders(HEADERS);
|
||||
assert.strictEqual(form.headers, HEADERS);
|
||||
});
|
||||
|
||||
test(function write() {
|
||||
var parser = {},
|
||||
BUFFER = [1, 2, 3];
|
||||
|
||||
form._parser = parser;
|
||||
form.bytesExpected = 523423;
|
||||
|
||||
(function testBasic() {
|
||||
gently.expect(form, 'emit', function(event, bytesReceived, bytesExpected) {
|
||||
assert.equal(event, 'progress');
|
||||
assert.equal(bytesReceived, BUFFER.length);
|
||||
assert.equal(bytesExpected, form.bytesExpected);
|
||||
});
|
||||
|
||||
gently.expect(parser, 'write', function(buffer) {
|
||||
assert.strictEqual(buffer, BUFFER);
|
||||
return buffer.length;
|
||||
});
|
||||
|
||||
assert.equal(form.write(BUFFER), BUFFER.length);
|
||||
assert.equal(form.bytesReceived, BUFFER.length);
|
||||
})();
|
||||
|
||||
(function testParserError() {
|
||||
gently.expect(form, 'emit');
|
||||
|
||||
gently.expect(parser, 'write', function(buffer) {
|
||||
assert.strictEqual(buffer, BUFFER);
|
||||
return buffer.length - 1;
|
||||
});
|
||||
|
||||
gently.expect(form, '_error', function(err) {
|
||||
assert.ok(err.message.match(/parser error/i));
|
||||
});
|
||||
|
||||
assert.equal(form.write(BUFFER), BUFFER.length - 1);
|
||||
assert.equal(form.bytesReceived, BUFFER.length + BUFFER.length);
|
||||
})();
|
||||
|
||||
(function testUninitialized() {
|
||||
delete form._parser;
|
||||
|
||||
gently.expect(form, '_error', function(err) {
|
||||
assert.ok(err.message.match(/unintialized parser/i));
|
||||
});
|
||||
form.write(BUFFER);
|
||||
})();
|
||||
});
|
||||
|
||||
test(function parseContentType() {
|
||||
var HEADERS = {};
|
||||
|
||||
form.headers = {'content-type': 'application/x-www-form-urlencoded'};
|
||||
gently.expect(form, '_initUrlencoded');
|
||||
form._parseContentType();
|
||||
|
||||
// accept anything that has 'urlencoded' in it
|
||||
form.headers = {'content-type': 'broken-client/urlencoded-stupid'};
|
||||
gently.expect(form, '_initUrlencoded');
|
||||
form._parseContentType();
|
||||
|
||||
var BOUNDARY = '---------------------------57814261102167618332366269';
|
||||
form.headers = {'content-type': 'multipart/form-data; boundary='+BOUNDARY};
|
||||
|
||||
gently.expect(form, '_initMultipart', function(boundary) {
|
||||
assert.equal(boundary, BOUNDARY);
|
||||
});
|
||||
form._parseContentType();
|
||||
|
||||
(function testQuotedBoundary() {
|
||||
form.headers = {'content-type': 'multipart/form-data; boundary="' + BOUNDARY + '"'};
|
||||
|
||||
gently.expect(form, '_initMultipart', function(boundary) {
|
||||
assert.equal(boundary, BOUNDARY);
|
||||
});
|
||||
form._parseContentType();
|
||||
})();
|
||||
|
||||
(function testNoBoundary() {
|
||||
form.headers = {'content-type': 'multipart/form-data'};
|
||||
|
||||
gently.expect(form, '_error', function(err) {
|
||||
assert.ok(err.message.match(/no multipart boundary/i));
|
||||
});
|
||||
form._parseContentType();
|
||||
})();
|
||||
|
||||
(function testNoContentType() {
|
||||
form.headers = {};
|
||||
|
||||
gently.expect(form, '_error', function(err) {
|
||||
assert.ok(err.message.match(/no content-type/i));
|
||||
});
|
||||
form._parseContentType();
|
||||
})();
|
||||
|
||||
(function testUnknownContentType() {
|
||||
form.headers = {'content-type': 'invalid'};
|
||||
|
||||
gently.expect(form, '_error', function(err) {
|
||||
assert.ok(err.message.match(/unknown content-type/i));
|
||||
});
|
||||
form._parseContentType();
|
||||
})();
|
||||
});
|
||||
|
||||
test(function parseContentLength() {
|
||||
var HEADERS = {};
|
||||
|
||||
form.headers = {};
|
||||
gently.expect(form, 'emit', function(event, bytesReceived, bytesExpected) {
|
||||
assert.equal(event, 'progress');
|
||||
assert.equal(bytesReceived, 0);
|
||||
assert.equal(bytesExpected, 0);
|
||||
});
|
||||
form._parseContentLength();
|
||||
|
||||
form.headers['content-length'] = '8';
|
||||
gently.expect(form, 'emit', function(event, bytesReceived, bytesExpected) {
|
||||
assert.equal(event, 'progress');
|
||||
assert.equal(bytesReceived, 0);
|
||||
assert.equal(bytesExpected, 8);
|
||||
});
|
||||
form._parseContentLength();
|
||||
assert.strictEqual(form.bytesReceived, 0);
|
||||
assert.strictEqual(form.bytesExpected, 8);
|
||||
|
||||
// JS can be evil, lets make sure we are not
|
||||
form.headers['content-length'] = '08';
|
||||
gently.expect(form, 'emit', function(event, bytesReceived, bytesExpected) {
|
||||
assert.equal(event, 'progress');
|
||||
assert.equal(bytesReceived, 0);
|
||||
assert.equal(bytesExpected, 8);
|
||||
});
|
||||
form._parseContentLength();
|
||||
assert.strictEqual(form.bytesExpected, 8);
|
||||
});
|
||||
|
||||
test(function _initMultipart() {
|
||||
var BOUNDARY = '123',
|
||||
PARSER;
|
||||
|
||||
gently.expect(MultipartParserStub, 'new', function() {
|
||||
PARSER = this;
|
||||
});
|
||||
|
||||
gently.expect(MultipartParserStub.prototype, 'initWithBoundary', function(boundary) {
|
||||
assert.equal(boundary, BOUNDARY);
|
||||
});
|
||||
|
||||
form._initMultipart(BOUNDARY);
|
||||
assert.equal(form.type, 'multipart');
|
||||
assert.strictEqual(form._parser, PARSER);
|
||||
|
||||
(function testRegularField() {
|
||||
var PART;
|
||||
gently.expect(StreamStub, 'new', function() {
|
||||
PART = this;
|
||||
});
|
||||
|
||||
gently.expect(form, 'onPart', function(part) {
|
||||
assert.strictEqual(part, PART);
|
||||
assert.deepEqual
|
||||
( part.headers
|
||||
, { 'content-disposition': 'form-data; name="field1"'
|
||||
, 'foo': 'bar'
|
||||
}
|
||||
);
|
||||
assert.equal(part.name, 'field1');
|
||||
|
||||
var strings = ['hello', ' world'];
|
||||
gently.expect(part, 'emit', 2, function(event, b) {
|
||||
assert.equal(event, 'data');
|
||||
assert.equal(b.toString(), strings.shift());
|
||||
});
|
||||
|
||||
gently.expect(part, 'emit', function(event, b) {
|
||||
assert.equal(event, 'end');
|
||||
});
|
||||
});
|
||||
|
||||
PARSER.onPartBegin();
|
||||
PARSER.onHeaderField(new Buffer('content-disposition'), 0, 10);
|
||||
PARSER.onHeaderField(new Buffer('content-disposition'), 10, 19);
|
||||
PARSER.onHeaderValue(new Buffer('form-data; name="field1"'), 0, 14);
|
||||
PARSER.onHeaderValue(new Buffer('form-data; name="field1"'), 14, 24);
|
||||
PARSER.onHeaderEnd();
|
||||
PARSER.onHeaderField(new Buffer('foo'), 0, 3);
|
||||
PARSER.onHeaderValue(new Buffer('bar'), 0, 3);
|
||||
PARSER.onHeaderEnd();
|
||||
PARSER.onHeadersEnd();
|
||||
PARSER.onPartData(new Buffer('hello world'), 0, 5);
|
||||
PARSER.onPartData(new Buffer('hello world'), 5, 11);
|
||||
PARSER.onPartEnd();
|
||||
})();
|
||||
|
||||
(function testFileField() {
|
||||
var PART;
|
||||
gently.expect(StreamStub, 'new', function() {
|
||||
PART = this;
|
||||
});
|
||||
|
||||
gently.expect(form, 'onPart', function(part) {
|
||||
assert.deepEqual
|
||||
( part.headers
|
||||
, { 'content-disposition': 'form-data; name="field2"; filename="C:\\Documents and Settings\\IE\\Must\\Die\\Sun"et.jpg"'
|
||||
, 'content-type': 'text/plain'
|
||||
}
|
||||
);
|
||||
assert.equal(part.name, 'field2');
|
||||
assert.equal(part.filename, 'Sun"et.jpg');
|
||||
assert.equal(part.mime, 'text/plain');
|
||||
|
||||
gently.expect(part, 'emit', function(event, b) {
|
||||
assert.equal(event, 'data');
|
||||
assert.equal(b.toString(), '... contents of file1.txt ...');
|
||||
});
|
||||
|
||||
gently.expect(part, 'emit', function(event, b) {
|
||||
assert.equal(event, 'end');
|
||||
});
|
||||
});
|
||||
|
||||
PARSER.onPartBegin();
|
||||
PARSER.onHeaderField(new Buffer('content-disposition'), 0, 19);
|
||||
PARSER.onHeaderValue(new Buffer('form-data; name="field2"; filename="C:\\Documents and Settings\\IE\\Must\\Die\\Sun"et.jpg"'), 0, 85);
|
||||
PARSER.onHeaderEnd();
|
||||
PARSER.onHeaderField(new Buffer('Content-Type'), 0, 12);
|
||||
PARSER.onHeaderValue(new Buffer('text/plain'), 0, 10);
|
||||
PARSER.onHeaderEnd();
|
||||
PARSER.onHeadersEnd();
|
||||
PARSER.onPartData(new Buffer('... contents of file1.txt ...'), 0, 29);
|
||||
PARSER.onPartEnd();
|
||||
})();
|
||||
|
||||
(function testEnd() {
|
||||
gently.expect(form, '_maybeEnd');
|
||||
PARSER.onEnd();
|
||||
assert.ok(form.ended);
|
||||
})();
|
||||
});
|
||||
|
||||
test(function _fileName() {
|
||||
// TODO
|
||||
return;
|
||||
});
|
||||
|
||||
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() {
|
||||
var ERR = new Error('bla');
|
||||
|
||||
gently.expect(form, 'pause');
|
||||
gently.expect(form, 'emit', function(event, err) {
|
||||
assert.equal(event, 'error');
|
||||
assert.strictEqual(err, ERR);
|
||||
});
|
||||
|
||||
form._error(ERR);
|
||||
assert.strictEqual(form.error, ERR);
|
||||
|
||||
// make sure _error only does its thing once
|
||||
form._error(ERR);
|
||||
});
|
||||
|
||||
test(function onPart() {
|
||||
var PART = {};
|
||||
gently.expect(form, 'handlePart', function(part) {
|
||||
assert.strictEqual(part, PART);
|
||||
});
|
||||
|
||||
form.onPart(PART);
|
||||
});
|
||||
|
||||
test(function handlePart() {
|
||||
(function testUtf8Field() {
|
||||
var PART = new events.EventEmitter();
|
||||
PART.name = 'my_field';
|
||||
|
||||
gently.expect(form, 'emit', function(event, field, value) {
|
||||
assert.equal(event, 'field');
|
||||
assert.equal(field, 'my_field');
|
||||
assert.equal(value, 'hello world: €');
|
||||
});
|
||||
|
||||
form.handlePart(PART);
|
||||
PART.emit('data', new Buffer('hello'));
|
||||
PART.emit('data', new Buffer(' world: '));
|
||||
PART.emit('data', new Buffer([0xE2]));
|
||||
PART.emit('data', new Buffer([0x82, 0xAC]));
|
||||
PART.emit('end');
|
||||
})();
|
||||
|
||||
(function testBinaryField() {
|
||||
var PART = new events.EventEmitter();
|
||||
PART.name = 'my_field2';
|
||||
|
||||
gently.expect(form, 'emit', function(event, field, value) {
|
||||
assert.equal(event, 'field');
|
||||
assert.equal(field, 'my_field2');
|
||||
assert.equal(value, 'hello world: '+new Buffer([0xE2, 0x82, 0xAC]).toString('binary'));
|
||||
});
|
||||
|
||||
form.encoding = 'binary';
|
||||
form.handlePart(PART);
|
||||
PART.emit('data', new Buffer('hello'));
|
||||
PART.emit('data', new Buffer(' world: '));
|
||||
PART.emit('data', new Buffer([0xE2]));
|
||||
PART.emit('data', new Buffer([0x82, 0xAC]));
|
||||
PART.emit('end');
|
||||
})();
|
||||
|
||||
(function testFieldSize() {
|
||||
form.maxFieldsSize = 8;
|
||||
var PART = new events.EventEmitter();
|
||||
PART.name = 'my_field';
|
||||
|
||||
gently.expect(form, '_error', function(err) {
|
||||
assert.equal(err.message, 'maxFieldsSize exceeded, received 9 bytes of field data');
|
||||
});
|
||||
|
||||
form.handlePart(PART);
|
||||
form._fieldsSize = 1;
|
||||
PART.emit('data', new Buffer(7));
|
||||
PART.emit('data', new Buffer(1));
|
||||
})();
|
||||
|
||||
(function testFilePart() {
|
||||
var PART = new events.EventEmitter(),
|
||||
FILE = new events.EventEmitter(),
|
||||
PATH = '/foo/bar';
|
||||
|
||||
PART.name = 'my_file';
|
||||
PART.filename = 'sweet.txt';
|
||||
PART.mime = 'sweet.txt';
|
||||
|
||||
gently.expect(form, '_uploadPath', function(filename) {
|
||||
assert.equal(filename, PART.filename);
|
||||
return PATH;
|
||||
});
|
||||
|
||||
gently.expect(FileStub, 'new', function(properties) {
|
||||
assert.equal(properties.path, PATH);
|
||||
assert.equal(properties.name, PART.filename);
|
||||
assert.equal(properties.type, PART.mime);
|
||||
FILE = this;
|
||||
|
||||
gently.expect(form, 'emit', function (event, field, file) {
|
||||
assert.equal(event, 'fileBegin');
|
||||
assert.strictEqual(field, PART.name);
|
||||
assert.strictEqual(file, FILE);
|
||||
});
|
||||
|
||||
gently.expect(FILE, 'open');
|
||||
});
|
||||
|
||||
form.handlePart(PART);
|
||||
assert.equal(form._flushing, 1);
|
||||
|
||||
var BUFFER;
|
||||
gently.expect(form, 'pause');
|
||||
gently.expect(FILE, 'write', function(buffer, cb) {
|
||||
assert.strictEqual(buffer, BUFFER);
|
||||
gently.expect(form, 'resume');
|
||||
// @todo handle cb(new Err)
|
||||
cb();
|
||||
});
|
||||
|
||||
PART.emit('data', BUFFER = new Buffer('test'));
|
||||
|
||||
gently.expect(FILE, 'end', function(cb) {
|
||||
gently.expect(form, 'emit', function(event, field, file) {
|
||||
assert.equal(event, 'file');
|
||||
assert.strictEqual(file, FILE);
|
||||
});
|
||||
|
||||
gently.expect(form, '_maybeEnd');
|
||||
|
||||
cb();
|
||||
assert.equal(form._flushing, 0);
|
||||
});
|
||||
|
||||
PART.emit('end');
|
||||
})();
|
||||
});
|
||||
|
||||
test(function _uploadPath() {
|
||||
(function testUniqueId() {
|
||||
var UUID_A, UUID_B;
|
||||
gently.expect(GENTLY.hijacked.path, 'join', function(uploadDir, uuid) {
|
||||
assert.equal(uploadDir, form.uploadDir);
|
||||
UUID_A = uuid;
|
||||
});
|
||||
form._uploadPath();
|
||||
|
||||
gently.expect(GENTLY.hijacked.path, 'join', function(uploadDir, uuid) {
|
||||
UUID_B = uuid;
|
||||
});
|
||||
form._uploadPath();
|
||||
|
||||
assert.notEqual(UUID_A, UUID_B);
|
||||
})();
|
||||
|
||||
(function testFileExtension() {
|
||||
form.keepExtensions = true;
|
||||
var FILENAME = 'foo.jpg',
|
||||
EXT = '.bar';
|
||||
|
||||
gently.expect(GENTLY.hijacked.path, 'extname', function(filename) {
|
||||
assert.equal(filename, FILENAME);
|
||||
gently.restore(path, 'extname');
|
||||
|
||||
return EXT;
|
||||
});
|
||||
|
||||
gently.expect(GENTLY.hijacked.path, 'join', function(uploadDir, name) {
|
||||
assert.equal(path.extname(name), EXT);
|
||||
});
|
||||
form._uploadPath(FILENAME);
|
||||
})();
|
||||
});
|
||||
|
||||
test(function _maybeEnd() {
|
||||
gently.expect(form, 'emit', 0);
|
||||
form._maybeEnd();
|
||||
|
||||
form.ended = true;
|
||||
form._flushing = 1;
|
||||
form._maybeEnd();
|
||||
|
||||
gently.expect(form, 'emit', function(event) {
|
||||
assert.equal(event, 'end');
|
||||
});
|
||||
|
||||
form.ended = true;
|
||||
form._flushing = 0;
|
||||
form._maybeEnd();
|
||||
});
|
||||
gerado
externo
-50
@@ -1,50 +0,0 @@
|
||||
var common = require('../common');
|
||||
var multipartParser = require(common.lib + '/multipart_parser'),
|
||||
MultipartParser = multipartParser.MultipartParser,
|
||||
events = require('events'),
|
||||
Buffer = require('buffer').Buffer,
|
||||
parser;
|
||||
|
||||
function test(test) {
|
||||
parser = new MultipartParser();
|
||||
test();
|
||||
}
|
||||
|
||||
test(function constructor() {
|
||||
assert.equal(parser.boundary, null);
|
||||
assert.equal(parser.state, 0);
|
||||
assert.equal(parser.flags, 0);
|
||||
assert.equal(parser.boundaryChars, null);
|
||||
assert.equal(parser.index, null);
|
||||
assert.equal(parser.lookbehind, null);
|
||||
assert.equal(parser.constructor.name, 'MultipartParser');
|
||||
});
|
||||
|
||||
test(function initWithBoundary() {
|
||||
var boundary = 'abc';
|
||||
parser.initWithBoundary(boundary);
|
||||
assert.deepEqual(Array.prototype.slice.call(parser.boundary), [13, 10, 45, 45, 97, 98, 99]);
|
||||
assert.equal(parser.state, multipartParser.START);
|
||||
|
||||
assert.deepEqual(parser.boundaryChars, {10: true, 13: true, 45: true, 97: true, 98: true, 99: true});
|
||||
});
|
||||
|
||||
test(function parserError() {
|
||||
var boundary = 'abc',
|
||||
buffer = new Buffer(5);
|
||||
|
||||
parser.initWithBoundary(boundary);
|
||||
buffer.write('--ad', 'ascii', 0);
|
||||
assert.equal(parser.write(buffer), 5);
|
||||
});
|
||||
|
||||
test(function end() {
|
||||
(function testError() {
|
||||
assert.equal(parser.end().message, 'MultipartParser.end(): stream ended unexpectedly: ' + parser.explain());
|
||||
})();
|
||||
|
||||
(function testRegular() {
|
||||
parser.state = multipartParser.END;
|
||||
assert.strictEqual(parser.end(), undefined);
|
||||
})();
|
||||
});
|
||||
gerado
externo
-45
@@ -1,45 +0,0 @@
|
||||
var common = require('../common');
|
||||
var QuerystringParser = require(common.lib + '/querystring_parser').QuerystringParser,
|
||||
Buffer = require('buffer').Buffer,
|
||||
gently,
|
||||
parser;
|
||||
|
||||
function test(test) {
|
||||
gently = new Gently();
|
||||
parser = new QuerystringParser();
|
||||
test();
|
||||
gently.verify(test.name);
|
||||
}
|
||||
|
||||
test(function constructor() {
|
||||
assert.equal(parser.buffer, '');
|
||||
assert.equal(parser.constructor.name, 'QuerystringParser');
|
||||
});
|
||||
|
||||
test(function write() {
|
||||
var a = new Buffer('a=1');
|
||||
assert.equal(parser.write(a), a.length);
|
||||
|
||||
var b = new Buffer('&b=2');
|
||||
parser.write(b);
|
||||
assert.equal(parser.buffer, a + b);
|
||||
});
|
||||
|
||||
test(function end() {
|
||||
var FIELDS = {a: ['b', {c: 'd'}], e: 'f'};
|
||||
|
||||
gently.expect(GENTLY.hijacked.querystring, 'parse', function(str) {
|
||||
assert.equal(str, parser.buffer);
|
||||
return FIELDS;
|
||||
});
|
||||
|
||||
gently.expect(parser, 'onField', Object.keys(FIELDS).length, function(key, val) {
|
||||
assert.deepEqual(FIELDS[key], val);
|
||||
});
|
||||
|
||||
gently.expect(parser, 'onEnd');
|
||||
|
||||
parser.buffer = 'my buffer';
|
||||
parser.end();
|
||||
assert.equal(parser.buffer, '');
|
||||
});
|
||||
gerado
externo
-71
@@ -1,71 +0,0 @@
|
||||
var common = require('../common');
|
||||
var BOUNDARY = '---------------------------10102754414578508781458777923',
|
||||
FIXTURE = TEST_FIXTURES+'/multi_video.upload',
|
||||
fs = require('fs'),
|
||||
http = require('http'),
|
||||
formidable = require(common.lib + '/index'),
|
||||
server = http.createServer();
|
||||
|
||||
server.on('request', function(req, res) {
|
||||
var form = new formidable.IncomingForm(),
|
||||
uploads = {};
|
||||
|
||||
form.uploadDir = TEST_TMP;
|
||||
form.hash = 'sha1';
|
||||
form.parse(req);
|
||||
|
||||
form
|
||||
.on('fileBegin', function(field, file) {
|
||||
assert.equal(field, 'upload');
|
||||
|
||||
var tracker = {file: file, progress: [], ended: false};
|
||||
uploads[file.name] = tracker;
|
||||
file
|
||||
.on('progress', function(bytesReceived) {
|
||||
tracker.progress.push(bytesReceived);
|
||||
assert.equal(bytesReceived, file.size);
|
||||
})
|
||||
.on('end', function() {
|
||||
tracker.ended = true;
|
||||
});
|
||||
})
|
||||
.on('field', function(field, value) {
|
||||
assert.equal(field, 'title');
|
||||
assert.equal(value, '');
|
||||
})
|
||||
.on('file', function(field, file) {
|
||||
assert.equal(field, 'upload');
|
||||
assert.strictEqual(uploads[file.name].file, file);
|
||||
})
|
||||
.on('end', function() {
|
||||
assert.ok(uploads['shortest_video.flv']);
|
||||
assert.ok(uploads['shortest_video.flv'].ended);
|
||||
assert.ok(uploads['shortest_video.flv'].progress.length > 3);
|
||||
assert.equal(uploads['shortest_video.flv'].file.hash, 'd6a17616c7143d1b1438ceeef6836d1a09186b3a');
|
||||
assert.equal(uploads['shortest_video.flv'].progress.slice(-1), uploads['shortest_video.flv'].file.size);
|
||||
assert.ok(uploads['shortest_video.mp4']);
|
||||
assert.ok(uploads['shortest_video.mp4'].ended);
|
||||
assert.ok(uploads['shortest_video.mp4'].progress.length > 3);
|
||||
assert.equal(uploads['shortest_video.mp4'].file.hash, '937dfd4db263f4887ceae19341dcc8d63bcd557f');
|
||||
|
||||
server.close();
|
||||
res.writeHead(200);
|
||||
res.end('good');
|
||||
});
|
||||
});
|
||||
|
||||
server.listen(TEST_PORT, function() {
|
||||
var stat, headers, request, fixture;
|
||||
|
||||
stat = fs.statSync(FIXTURE);
|
||||
request = http.request({
|
||||
port: TEST_PORT,
|
||||
path: '/',
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'content-type': 'multipart/form-data; boundary='+BOUNDARY,
|
||||
'content-length': stat.size,
|
||||
},
|
||||
});
|
||||
fs.createReadStream(FIXTURE).pipe(request);
|
||||
});
|
||||
-1
@@ -1 +0,0 @@
|
||||
require('urun')(__dirname)
|
||||
gerado
externo
-67
@@ -1,67 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Convert a file to a base64 request</title>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
function form_submit(e){
|
||||
console.log(e)
|
||||
|
||||
var resultOutput = document.getElementById('resultOutput');
|
||||
var fileInput = document.getElementById('fileInput');
|
||||
var fieldInput = document.getElementById('fieldInput');
|
||||
|
||||
makeRequestBase64(fileInput.files[0], fieldInput.value, function(err, result){
|
||||
resultOutput.value = result;
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function makeRequestBase64(file, fieldName, cb){
|
||||
var boundary = '\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/\\/';
|
||||
var crlf = "\r\n";
|
||||
|
||||
var reader = new FileReader();
|
||||
reader.onload = function(e){
|
||||
var body = '';
|
||||
|
||||
body += '--' + boundary + crlf;
|
||||
body += 'Content-Disposition: form-data; name="' + fieldName + '"; filename="' + escape(file.name)+ '"' + crlf;
|
||||
body += 'Content-Type: ' + file.type + '' + crlf;
|
||||
body += 'Content-Transfer-Encoding: base64' + crlf
|
||||
body += crlf;
|
||||
body += e.target.result.substring(e.target.result.indexOf(',') + 1) + crlf;
|
||||
|
||||
body += '--' + boundary + '--';
|
||||
|
||||
var head = '';
|
||||
head += 'POST /upload HTTP/1.1' + crlf;
|
||||
head += 'Host: localhost:8080' + crlf;
|
||||
head += 'Content-Type: multipart/form-data; boundary=' + boundary + '' + crlf;
|
||||
head += 'Content-Length: ' + body.length + '' + crlf;
|
||||
|
||||
cb(null, head + crlf + body);
|
||||
};
|
||||
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<form action="" onsubmit="return form_submit();">
|
||||
<label>File: <input id="fileInput" type="file" /></label><br />
|
||||
<label>Field: <input id="fieldInput" type="text" value="file" /></label><br />
|
||||
<button type="submit">Ok!</button><br />
|
||||
<label>Request: <textarea id="resultOutput" readonly="readonly" rows="20" cols="80"></textarea></label><br />
|
||||
</form>
|
||||
<p>
|
||||
Don't forget to save the output with windows (CRLF) line endings!
|
||||
</p>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
gerado
externo
-33
@@ -1,33 +0,0 @@
|
||||
var common = require('../common');
|
||||
var test = require('utest');
|
||||
var assert = common.assert;
|
||||
var File = common.require('file');
|
||||
|
||||
var file;
|
||||
var now = new Date;
|
||||
test('IncomingForm', {
|
||||
before: function() {
|
||||
file = new File({
|
||||
size: 1024,
|
||||
path: '/tmp/cat.png',
|
||||
name: 'cat.png',
|
||||
type: 'image/png',
|
||||
lastModifiedDate: now,
|
||||
filename: 'cat.png',
|
||||
mime: 'image/png'
|
||||
})
|
||||
},
|
||||
|
||||
'#toJSON()': function() {
|
||||
var obj = file.toJSON();
|
||||
var len = Object.keys(obj).length;
|
||||
assert.equal(1024, obj.size);
|
||||
assert.equal('/tmp/cat.png', obj.path);
|
||||
assert.equal('cat.png', obj.name);
|
||||
assert.equal('image/png', obj.type);
|
||||
assert.equal('image/png', obj.mime);
|
||||
assert.equal('cat.png', obj.filename);
|
||||
assert.equal(now, obj.mtime);
|
||||
assert.equal(len, 8);
|
||||
}
|
||||
});
|
||||
gerado
externo
-63
@@ -1,63 +0,0 @@
|
||||
var common = require('../common');
|
||||
var test = require('utest');
|
||||
var assert = common.assert;
|
||||
var IncomingForm = common.require('incoming_form').IncomingForm;
|
||||
var path = require('path');
|
||||
|
||||
var form;
|
||||
test('IncomingForm', {
|
||||
before: function() {
|
||||
form = new IncomingForm();
|
||||
},
|
||||
|
||||
'#_fileName with regular characters': function() {
|
||||
var filename = 'foo.txt';
|
||||
assert.equal(form._fileName(makeHeader(filename)), 'foo.txt');
|
||||
},
|
||||
|
||||
'#_fileName with unescaped quote': function() {
|
||||
var filename = 'my".txt';
|
||||
assert.equal(form._fileName(makeHeader(filename)), 'my".txt');
|
||||
},
|
||||
|
||||
'#_fileName with escaped quote': function() {
|
||||
var filename = 'my%22.txt';
|
||||
assert.equal(form._fileName(makeHeader(filename)), 'my".txt');
|
||||
},
|
||||
|
||||
'#_fileName with bad quote and additional sub-header': function() {
|
||||
var filename = 'my".txt';
|
||||
var header = makeHeader(filename) + '; foo="bar"';
|
||||
assert.equal(form._fileName(header), filename);
|
||||
},
|
||||
|
||||
'#_fileName with semicolon': function() {
|
||||
var filename = 'my;.txt';
|
||||
assert.equal(form._fileName(makeHeader(filename)), 'my;.txt');
|
||||
},
|
||||
|
||||
'#_fileName with utf8 character': function() {
|
||||
var filename = 'my☃.txt';
|
||||
assert.equal(form._fileName(makeHeader(filename)), 'my☃.txt');
|
||||
},
|
||||
|
||||
'#_uploadPath strips harmful characters from extension when keepExtensions': function() {
|
||||
form.keepExtensions = true;
|
||||
|
||||
var ext = path.extname(form._uploadPath('fine.jpg?foo=bar'));
|
||||
assert.equal(ext, '.jpg');
|
||||
|
||||
var ext = path.extname(form._uploadPath('fine?foo=bar'));
|
||||
assert.equal(ext, '');
|
||||
|
||||
var ext = path.extname(form._uploadPath('super.cr2+dsad'));
|
||||
assert.equal(ext, '.cr2');
|
||||
|
||||
var ext = path.extname(form._uploadPath('super.bar'));
|
||||
assert.equal(ext, '.bar');
|
||||
},
|
||||
});
|
||||
|
||||
function makeHeader(filename) {
|
||||
return 'Content-Disposition: form-data; name="upload"; filename="' + filename + '"';
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
{
|
||||
// Settings
|
||||
"passfail" : false, // Stop on first error.
|
||||
"maxerr" : 100, // Maximum errors before stopping.
|
||||
|
||||
|
||||
// Predefined globals whom JSHint will ignore.
|
||||
"browser" : false, // Standard browser globals e.g. `window`, `document`.
|
||||
|
||||
"node" : true,
|
||||
"rhino" : false,
|
||||
"couch" : false,
|
||||
"wsh" : false, // Windows Scripting Host.
|
||||
|
||||
"jquery" : false,
|
||||
"prototypejs" : false,
|
||||
"mootools" : false,
|
||||
"dojo" : false,
|
||||
|
||||
|
||||
"predef" : [
|
||||
"describe", "it", "before", "after"
|
||||
],
|
||||
|
||||
// Development.
|
||||
"debug" : true, // Allow debugger statements e.g. browser breakpoints.
|
||||
"devel" : true, // Allow development statements e.g. `console.log();`.
|
||||
|
||||
|
||||
// EcmaScript 5.
|
||||
"es5" : true, // Allow EcmaScript 5 syntax.
|
||||
"strict" : false, // Require `use strict` pragma in every file.
|
||||
"globalstrict" : true, // Allow global "use strict" (also enables 'strict').
|
||||
|
||||
|
||||
// The Good Parts.
|
||||
"asi" : true, // Tolerate Automatic Semicolon Insertion (no semicolons).
|
||||
"laxbreak" : false, // Tolerate unsafe line breaks e.g. `return [\n] x` without semicolons.
|
||||
"laxcomma" : true,
|
||||
"bitwise" : false, // Prohibit bitwise operators (&, |, ^, etc.).
|
||||
"boss" : true, // Tolerate assignments inside if, for & while. Usually conditions & loops are for comparison, not assignments.
|
||||
"curly" : false, // Require {} for every new block or scope.
|
||||
"eqeqeq" : true, // Require triple equals i.e. `===`.
|
||||
"eqnull" : true, // Tolerate use of `== null`.
|
||||
"evil" : false, // Tolerate use of `eval`.
|
||||
"expr" : false, // Tolerate `ExpressionStatement` as Programs.
|
||||
"forin" : false, // Prohibt `for in` loops without `hasOwnProperty`.
|
||||
"immed" : true, // Require immediate invocations to be wrapped in parens e.g. `( function(){}() );`
|
||||
"latedef" : false, // Prohibit variable use before definition.
|
||||
"loopfunc" : false, // Allow functions to be defined within loops.
|
||||
"noarg" : true, // Prohibit use of `arguments.caller` and `arguments.callee`.
|
||||
"regexp" : false, // Prohibit `.` and `[^...]` in regular expressions.
|
||||
"regexdash" : false, // Tolerate unescaped last dash i.e. `[-...]`.
|
||||
"scripturl" : false, // Tolerate script-targeted URLs.
|
||||
"shadow" : false, // Allows re-define variables later in code e.g. `var x=1; x=2;`.
|
||||
"supernew" : false, // Tolerate `new function () { ... };` and `new Object;`.
|
||||
"undef" : true, // Require all non-global variables be declared before they are used.
|
||||
|
||||
|
||||
// Persone styling prefrences.
|
||||
"newcap" : true, // Require capitalization of all constructor functions e.g. `new F()`.
|
||||
"noempty" : true, // Prohibit use of empty blocks.
|
||||
"nonew" : true, // Prohibit use of constructors for side-effects.
|
||||
"nomen" : false, // Prohibit use of initial or trailing underbars in names.
|
||||
"onevar" : false, // Allow only one `var` statement per function.
|
||||
"plusplus" : false, // Prohibit use of `++` & `--`.
|
||||
"sub" : false, // Tolerate all forms of subscript notation besides dot notation e.g. `dict['key']` instead of `dict.key`.
|
||||
"trailing" : true, // Prohibit trailing whitespaces.
|
||||
"white" : false // Check against strict whitespace and indentation rules.
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
/node_modules
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- 0.8
|
||||
- 0.9
|
||||
- "0.8"
|
||||
- "0.9"
|
||||
- "0.10"
|
||||
+169
@@ -0,0 +1,169 @@
|
||||
### 2.1.6
|
||||
|
||||
* expose `hash` as an option to `Form`. (thanks wookiehangover)
|
||||
|
||||
### 2.1.5
|
||||
|
||||
* fix possible 'close' event before all temp files are done
|
||||
|
||||
### 2.1.4
|
||||
|
||||
* fix crash for invalid requests
|
||||
|
||||
### 2.1.3
|
||||
|
||||
* add `file.size`
|
||||
|
||||
### 2.1.2
|
||||
|
||||
* proper backpressure support
|
||||
* update s3 example
|
||||
|
||||
### 2.1.1
|
||||
|
||||
* fix uploads larger than 2KB
|
||||
* fix both s3 and upload example
|
||||
* add part.byteCount and part.byteOffset
|
||||
|
||||
### 2.1.0 (recalled)
|
||||
|
||||
* Complete rewrite. See README for changes and new API.
|
||||
|
||||
### v1.0.13
|
||||
|
||||
* Only update hash if update method exists (Sven Lito)
|
||||
* According to travis v0.10 needs to go quoted (Sven Lito)
|
||||
* Bumping build node versions (Sven Lito)
|
||||
* Additional fix for empty requests (Eugene Girshov)
|
||||
* Change the default to 1000, to match the new Node behaviour. (OrangeDog)
|
||||
* Add ability to control maxKeys in the querystring parser. (OrangeDog)
|
||||
* Adjust test case to work with node 0.9.x (Eugene Girshov)
|
||||
* Update package.json (Sven Lito)
|
||||
* Path adjustment according to eb4468b (Markus Ast)
|
||||
|
||||
### v1.0.12
|
||||
|
||||
* Emit error on aborted connections (Eugene Girshov)
|
||||
* Add support for empty requests (Eugene Girshov)
|
||||
* Fix name/filename handling in Content-Disposition (jesperp)
|
||||
* Tolerate malformed closing boundary in multipart (Eugene Girshov)
|
||||
* Ignore preamble in multipart messages (Eugene Girshov)
|
||||
* Add support for application/json (Mike Frey, Carlos Rodriguez)
|
||||
* Add support for Base64 encoding (Elmer Bulthuis)
|
||||
* Add File#toJSON (TJ Holowaychuk)
|
||||
* Remove support for Node.js 0.4 & 0.6 (Andrew Kelley)
|
||||
* Documentation improvements (Sven Lito, Andre Azevedo)
|
||||
* Add support for application/octet-stream (Ion Lupascu, Chris Scribner)
|
||||
* Use os.tmpDir() to get tmp directory (Andrew Kelley)
|
||||
* Improve package.json (Andrew Kelley, Sven Lito)
|
||||
* Fix benchmark script (Andrew Kelley)
|
||||
* Fix scope issue in incoming_forms (Sven Lito)
|
||||
* Fix file handle leak on error (OrangeDog)
|
||||
|
||||
### v1.0.11
|
||||
|
||||
* Calculate checksums for incoming files (sreuter)
|
||||
* Add definition parameters to "IncomingForm" as an argument (Math-)
|
||||
|
||||
### v1.0.10
|
||||
|
||||
* Make parts to be proper Streams (Matt Robenolt)
|
||||
|
||||
### v1.0.9
|
||||
|
||||
* Emit progress when content length header parsed (Tim Koschützki)
|
||||
* Fix Readme syntax due to GitHub changes (goob)
|
||||
* Replace references to old 'sys' module in Readme with 'util' (Peter Sugihara)
|
||||
|
||||
### v1.0.8
|
||||
|
||||
* Strip potentially unsafe characters when using `keepExtensions: true`.
|
||||
* Switch to utest / urun for testing
|
||||
* Add travis build
|
||||
|
||||
### v1.0.7
|
||||
|
||||
* Remove file from package that was causing problems when installing on windows. (#102)
|
||||
* Fix typos in Readme (Jason Davies).
|
||||
|
||||
### v1.0.6
|
||||
|
||||
* Do not default to the default to the field name for file uploads where
|
||||
filename="".
|
||||
|
||||
### v1.0.5
|
||||
|
||||
* Support filename="" in multipart parts
|
||||
* Explain unexpected end() errors in parser better
|
||||
|
||||
**Note:** Starting with this version, formidable emits 'file' events for empty
|
||||
file input fields. Previously those were incorrectly emitted as regular file
|
||||
input fields with value = "".
|
||||
|
||||
### v1.0.4
|
||||
|
||||
* Detect a good default tmp directory regardless of platform. (#88)
|
||||
|
||||
### v1.0.3
|
||||
|
||||
* Fix problems with utf8 characters (#84) / semicolons in filenames (#58)
|
||||
* Small performance improvements
|
||||
* New test suite and fixture system
|
||||
|
||||
### v1.0.2
|
||||
|
||||
* Exclude node\_modules folder from git
|
||||
* Implement new `'aborted'` event
|
||||
* Fix files in example folder to work with recent node versions
|
||||
* Make gently a devDependency
|
||||
|
||||
[See Commits](https://github.com/felixge/node-formidable/compare/v1.0.1...v1.0.2)
|
||||
|
||||
### v1.0.1
|
||||
|
||||
* Fix package.json to refer to proper main directory. (#68, Dean Landolt)
|
||||
|
||||
[See Commits](https://github.com/felixge/node-formidable/compare/v1.0.0...v1.0.1)
|
||||
|
||||
### v1.0.0
|
||||
|
||||
* Add support for multipart boundaries that are quoted strings. (Jeff Craig)
|
||||
|
||||
This marks the beginning of development on version 2.0 which will include
|
||||
several architectural improvements.
|
||||
|
||||
[See Commits](https://github.com/felixge/node-formidable/compare/v0.9.11...v1.0.0)
|
||||
|
||||
### v0.9.11
|
||||
|
||||
* Emit `'progress'` event when receiving data, regardless of parsing it. (Tim Koschützki)
|
||||
* Use [W3C FileAPI Draft](http://dev.w3.org/2006/webapi/FileAPI/) properties for File class
|
||||
|
||||
**Important:** The old property names of the File class will be removed in a
|
||||
future release.
|
||||
|
||||
[See Commits](https://github.com/felixge/node-formidable/compare/v0.9.10...v0.9.11)
|
||||
|
||||
### Older releases
|
||||
|
||||
These releases were done before starting to maintain the above Changelog:
|
||||
|
||||
* [v0.9.10](https://github.com/felixge/node-formidable/compare/v0.9.9...v0.9.10)
|
||||
* [v0.9.9](https://github.com/felixge/node-formidable/compare/v0.9.8...v0.9.9)
|
||||
* [v0.9.8](https://github.com/felixge/node-formidable/compare/v0.9.7...v0.9.8)
|
||||
* [v0.9.7](https://github.com/felixge/node-formidable/compare/v0.9.6...v0.9.7)
|
||||
* [v0.9.6](https://github.com/felixge/node-formidable/compare/v0.9.5...v0.9.6)
|
||||
* [v0.9.5](https://github.com/felixge/node-formidable/compare/v0.9.4...v0.9.5)
|
||||
* [v0.9.4](https://github.com/felixge/node-formidable/compare/v0.9.3...v0.9.4)
|
||||
* [v0.9.3](https://github.com/felixge/node-formidable/compare/v0.9.2...v0.9.3)
|
||||
* [v0.9.2](https://github.com/felixge/node-formidable/compare/v0.9.1...v0.9.2)
|
||||
* [v0.9.1](https://github.com/felixge/node-formidable/compare/v0.9.0...v0.9.1)
|
||||
* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0)
|
||||
* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0)
|
||||
* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0)
|
||||
* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0)
|
||||
* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0)
|
||||
* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0)
|
||||
* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0)
|
||||
* [v0.9.0](https://github.com/felixge/node-formidable/compare/v0.8.0...v0.9.0)
|
||||
* [v0.1.0](https://github.com/felixge/node-formidable/commits/v0.1.0)
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
[](https://travis-ci.org/superjoe30/node-multiparty)
|
||||
# multiparty
|
||||
|
||||
Parse http requests with content-type `multipart/form-data`, also known as file uploads.
|
||||
|
||||
### Why the fork?
|
||||
|
||||
* This module uses the Node.js v0.10 streams properly, *even in Node.js v0.8*
|
||||
* It will not create a temp file for you unless you want it to.
|
||||
* Counts bytes and does math to help you figure out the `Content-Length` of
|
||||
each part.
|
||||
* You can easily stream uploads to s3 with
|
||||
[knox](https://github.com/LearnBoost/knox), for [example](examples/s3.js).
|
||||
* Less bugs. This code is simpler, has all deprecated functionality removed,
|
||||
has cleaner tests, and does not try to do anything beyond multipart stream
|
||||
parsing.
|
||||
|
||||
## Installation
|
||||
|
||||
```
|
||||
npm install multiparty
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
* See [examples](examples).
|
||||
* Using express or connect? See [connect-multiparty](https://github.com/superjoe30/connect-multiparty)
|
||||
|
||||
Parse an incoming `multipart/form-data` request.
|
||||
|
||||
```js
|
||||
var multiparty = require('multiparty')
|
||||
, http = require('http')
|
||||
, util = require('util')
|
||||
|
||||
http.createServer(function(req, res) {
|
||||
if (req.url === '/upload' && req.method === 'POST') {
|
||||
// parse a file upload
|
||||
var form = new multiparty.Form();
|
||||
|
||||
form.parse(req, function(err, fields, files) {
|
||||
res.writeHead(200, {'content-type': 'text/plain'});
|
||||
res.write('received upload:\n\n');
|
||||
res.end(util.inspect({fields: fields, files: files}));
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// show a file upload form
|
||||
res.writeHead(200, {'content-type': 'text/html'});
|
||||
res.end(
|
||||
'<form action="/upload" enctype="multipart/form-data" method="post">'+
|
||||
'<input type="text" name="title"><br>'+
|
||||
'<input type="file" name="upload" multiple="multiple"><br>'+
|
||||
'<input type="submit" value="Upload">'+
|
||||
'</form>'
|
||||
);
|
||||
}).listen(8080);
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### multiparty.Form
|
||||
```js
|
||||
var form = new multiparty.Form(options)
|
||||
```
|
||||
Creates a new form. Options:
|
||||
|
||||
* `encoding` - sets encoding for the incoming form fields. Defaults to `utf8`.
|
||||
* `maxFieldSize` - Limits the amount of memory a field (not a file) can
|
||||
allocate in bytes. If this value is exceeded, an `error` event is emitted.
|
||||
The default size is 2MB.
|
||||
* `maxFields` - Limits the number of fields that will be parsed before
|
||||
emitting an `error` event. A file counts as a field in this case.
|
||||
Defaults to 1000.
|
||||
* `autoFields` - Enables `field` events. This is automatically set to `true`
|
||||
if you add a `field` listener.
|
||||
* `autoFiles` - Enables `file` events. This is automatically set to `true`
|
||||
if you add a `file` listener.
|
||||
* `uploadDir` - Only relevant when `autoFiles` is `true`. The directory for
|
||||
placing file uploads in. You can move them later using `fs.rename()`.
|
||||
Defaults to `os.tmpDir()`.
|
||||
* `hash` - Only relevant when `autoFiles` is `true`. If you want checksums
|
||||
calculated for incoming files, set this to either `sha1` or `md5`.
|
||||
Defaults to off.
|
||||
|
||||
#### form.parse(request, [cb])
|
||||
|
||||
Parses an incoming node.js `request` containing form data. If `cb` is
|
||||
provided, `autoFields` and `autoFiles` are set to `true` and all fields and
|
||||
files are collected and passed to the callback:
|
||||
|
||||
```js
|
||||
form.parse(req, function(err, fields, files) {
|
||||
// ...
|
||||
});
|
||||
```
|
||||
|
||||
#### form.bytesReceived
|
||||
|
||||
The amount of bytes received for this form so far.
|
||||
|
||||
#### form.bytesExpected
|
||||
|
||||
The expected number of bytes in this form.
|
||||
|
||||
### Events
|
||||
|
||||
#### 'error' (err)
|
||||
|
||||
You definitely want to handle this event. If not your server *will* crash when
|
||||
users submit bogus multipart requests!
|
||||
|
||||
#### 'part' (part)
|
||||
|
||||
Emitted when a part is encountered in the request. `part` is a
|
||||
`ReadableStream`. It also has the following properties:
|
||||
|
||||
* `headers` - the headers for this part. For example, you may be interested
|
||||
in `content-type`.
|
||||
* `name` - the field name for this part
|
||||
* `filename` - only if the part is an incoming file
|
||||
* `byteOffset` - the byte offset of this part in the request body
|
||||
* `byteCount` - assuming that this is the last part in the request,
|
||||
this is the size of this part in bytes. You could use this, for
|
||||
example, to set the `Content-Length` header if uploading to S3.
|
||||
If the part had a `Content-Length` header then that value is used
|
||||
here instead.
|
||||
|
||||
#### 'aborted'
|
||||
|
||||
Emitted when the request is aborted. This event will be followed shortly
|
||||
by an `error` event. In practice you do not need to handle this event.
|
||||
|
||||
#### 'progress' (bytesReceived, bytesExpected)
|
||||
|
||||
#### 'close'
|
||||
|
||||
Emitted after all parts have been parsed and emitted. Not emitted if an `error`
|
||||
event is emitted. This is typically when you would send your response.
|
||||
|
||||
#### 'file' (name, file)
|
||||
|
||||
**By default multiparty will not touch your hard drive.** But if you add this
|
||||
listener, multiparty automatically sets `form.autoFiles` to `true` and will
|
||||
stream uploads to disk for you.
|
||||
|
||||
* `name` - the field name for this file
|
||||
* `file` - an object with these properties:
|
||||
- `originalFilename` - the filename that the user reports for the file
|
||||
- `path` - the absolute path of the uploaded file on disk
|
||||
- `headers` - the HTTP headers that were sent along with this file
|
||||
- `size` - size of the file in bytes
|
||||
|
||||
If you set the `form.hash` option, then `file` will also contain a `hash`
|
||||
property which is the checksum of the file.
|
||||
|
||||
#### 'field' (name, value)
|
||||
|
||||
* `name` - field name
|
||||
* `value` - string field value
|
||||
|
||||
gerado
externo
+41
@@ -0,0 +1,41 @@
|
||||
var http = require('http')
|
||||
, util = require('util')
|
||||
, multiparty = require('../')
|
||||
, azure = require('azure')
|
||||
, PORT = process.env.PORT || 27372
|
||||
|
||||
var server = http.createServer(function(req, res) {
|
||||
if (req.url === '/') {
|
||||
res.writeHead(200, {'content-type': 'text/html'});
|
||||
res.end(
|
||||
'<form action="/upload" enctype="multipart/form-data" method="post">'+
|
||||
'<input type="text" name="title"><br>'+
|
||||
'<input type="file" name="upload"><br>'+
|
||||
'<input type="submit" value="Upload">'+
|
||||
'</form>'
|
||||
);
|
||||
} else if (req.url === '/upload') {
|
||||
|
||||
var blobService = azure.createBlobService();
|
||||
var form = new multiparty.Form();
|
||||
form.on('part', function(part) {
|
||||
if (!part.filename) return;
|
||||
|
||||
var size = part.byteCount - part.byteOffset;
|
||||
var name = part.filename;
|
||||
var container = 'blobContainerName';
|
||||
|
||||
blobService.createBlockBlobFromStream(container, name, part, size, function(error) {
|
||||
if (error) {
|
||||
// error handling
|
||||
}
|
||||
});
|
||||
});
|
||||
form.parse(req);
|
||||
|
||||
res.send('File uploaded successfully');
|
||||
|
||||
});
|
||||
server.listen(PORT, function() {
|
||||
console.info('listening on http://0.0.0.0:'+PORT+'/');
|
||||
});
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
var http = require('http')
|
||||
, util = require('util')
|
||||
, multiparty = require('../')
|
||||
, knox = require('knox')
|
||||
, Batch = require('batch')
|
||||
, PORT = process.env.PORT || 27372
|
||||
|
||||
var s3Client = knox.createClient({
|
||||
secure: false,
|
||||
key: process.env.S3_KEY,
|
||||
secret: process.env.S3_SECRET,
|
||||
bucket: process.env.S3_BUCKET,
|
||||
});
|
||||
|
||||
var server = http.createServer(function(req, res) {
|
||||
if (req.url === '/') {
|
||||
res.writeHead(200, {'content-type': 'text/html'});
|
||||
res.end(
|
||||
'<form action="/upload" enctype="multipart/form-data" method="post">'+
|
||||
'<input type="text" name="path"><br>'+
|
||||
'<input type="file" name="upload"><br>'+
|
||||
'<input type="submit" value="Upload">'+
|
||||
'</form>'
|
||||
);
|
||||
} else if (req.url === '/upload') {
|
||||
var headers = {
|
||||
'x-amz-acl': 'public-read',
|
||||
};
|
||||
var form = new multiparty.Form();
|
||||
var batch = new Batch();
|
||||
batch.push(function(cb) {
|
||||
form.on('field', function(name, value) {
|
||||
if (name === 'path') {
|
||||
var destPath = value;
|
||||
if (destPath[0] !== '/') destPath = '/' + destPath;
|
||||
cb(null, destPath);
|
||||
}
|
||||
});
|
||||
});
|
||||
batch.push(function(cb) {
|
||||
form.on('part', function(part) {
|
||||
if (! part.filename) return;
|
||||
cb(null, part);
|
||||
});
|
||||
});
|
||||
batch.end(function(err, results) {
|
||||
if (err) throw err;
|
||||
form.removeListener('close', onEnd);
|
||||
var destPath = results[0]
|
||||
, part = results[1];
|
||||
|
||||
headers['Content-Length'] = part.byteCount;
|
||||
s3Client.putStream(part, destPath, headers, function(err, s3Response) {
|
||||
if (err) throw err;
|
||||
res.statusCode = s3Response.statusCode;
|
||||
s3Response.pipe(res);
|
||||
console.log("https://s3.amazonaws.com/" + process.env.S3_BUCKET + destPath);
|
||||
});
|
||||
});
|
||||
form.on('close', onEnd);
|
||||
form.parse(req);
|
||||
|
||||
} else {
|
||||
res.writeHead(404, {'content-type': 'text/plain'});
|
||||
res.end('404');
|
||||
}
|
||||
|
||||
function onEnd() {
|
||||
throw new Error("no uploaded file");
|
||||
}
|
||||
});
|
||||
server.listen(PORT, function() {
|
||||
console.info('listening on http://0.0.0.0:'+PORT+'/');
|
||||
});
|
||||
+37
@@ -0,0 +1,37 @@
|
||||
var http = require('http')
|
||||
, util = require('util')
|
||||
, multiparty = require('../')
|
||||
, PORT = process.env.PORT || 27372
|
||||
|
||||
var server = http.createServer(function(req, res) {
|
||||
if (req.url === '/') {
|
||||
res.writeHead(200, {'content-type': 'text/html'});
|
||||
res.end(
|
||||
'<form action="/upload" enctype="multipart/form-data" method="post">'+
|
||||
'<input type="text" name="title"><br>'+
|
||||
'<input type="file" name="upload" multiple="multiple"><br>'+
|
||||
'<input type="submit" value="Upload">'+
|
||||
'</form>'
|
||||
);
|
||||
} else if (req.url === '/upload') {
|
||||
var form = new multiparty.Form();
|
||||
|
||||
form.parse(req, function(err, fields, files) {
|
||||
if (err) {
|
||||
res.writeHead(400, {'content-type': 'text/plain'});
|
||||
res.end("invalid request: " + err.message);
|
||||
return;
|
||||
}
|
||||
res.writeHead(200, {'content-type': 'text/plain'});
|
||||
res.write('received fields:\n\n '+util.inspect(fields));
|
||||
res.write('\n\n');
|
||||
res.end('received files:\n\n '+util.inspect(files));
|
||||
});
|
||||
} else {
|
||||
res.writeHead(404, {'content-type': 'text/plain'});
|
||||
res.end('404');
|
||||
}
|
||||
});
|
||||
server.listen(PORT, function() {
|
||||
console.info('listening on http://0.0.0.0:'+PORT+'/');
|
||||
});
|
||||
gerado
externo
Arquivo executável
+596
@@ -0,0 +1,596 @@
|
||||
exports.Form = Form;
|
||||
|
||||
var stream = require('readable-stream')
|
||||
, util = require('util')
|
||||
, fs = require('fs')
|
||||
, crypto = require('crypto')
|
||||
, path = require('path')
|
||||
, os = require('os')
|
||||
, assert = require('assert')
|
||||
, StringDecoder = require('string_decoder').StringDecoder
|
||||
, StreamCounter = require('stream-counter')
|
||||
|
||||
var START = 0
|
||||
, START_BOUNDARY = 1
|
||||
, HEADER_FIELD_START = 2
|
||||
, HEADER_FIELD = 3
|
||||
, HEADER_VALUE_START = 4
|
||||
, HEADER_VALUE = 5
|
||||
, HEADER_VALUE_ALMOST_DONE = 6
|
||||
, HEADERS_ALMOST_DONE = 7
|
||||
, PART_DATA_START = 8
|
||||
, PART_DATA = 9
|
||||
, PART_END = 10
|
||||
, END = 11
|
||||
|
||||
, LF = 10
|
||||
, CR = 13
|
||||
, SPACE = 32
|
||||
, HYPHEN = 45
|
||||
, COLON = 58
|
||||
, A = 97
|
||||
, Z = 122
|
||||
|
||||
var CONTENT_TYPE_RE = /^multipart\/(form-data|related);\s+boundary=(?:"([^"]+)"|([^;]+))$/i;
|
||||
var FILE_EXT_RE = /(\.[_\-a-zA-Z0-9]{0,16}).*/;
|
||||
var LAST_BOUNDARY_SUFFIX_LEN = 4; // --\r\n
|
||||
|
||||
util.inherits(Form, stream.Writable);
|
||||
function Form(options) {
|
||||
var self = this;
|
||||
stream.Writable.call(self);
|
||||
|
||||
options = options || {};
|
||||
|
||||
self.error = null;
|
||||
self.finished = false;
|
||||
|
||||
self.autoFields = !!options.autoFields;
|
||||
self.autoFiles = !!options.autoFields;
|
||||
|
||||
self.maxFields = options.maxFields || 1000;
|
||||
self.maxFieldsSize = options.maxFieldsSize || 2 * 1024 * 1024;
|
||||
self.uploadDir = options.uploadDir || os.tmpDir();
|
||||
self.encoding = options.encoding || 'utf8';
|
||||
self.hash = options.hash || false;
|
||||
|
||||
self.bytesReceived = 0;
|
||||
self.bytesExpected = null;
|
||||
|
||||
self.openedFiles = [];
|
||||
self.totalFieldSize = 0;
|
||||
self.totalFieldCount = 0;
|
||||
self.flushing = 0;
|
||||
|
||||
self.backpressure = false;
|
||||
self.writeCbs = [];
|
||||
|
||||
if (options.boundary) setUpParser(self, options.boundary);
|
||||
|
||||
self.on('newListener', function(eventName) {
|
||||
if (eventName === 'file') {
|
||||
self.autoFiles = true;
|
||||
} else if (eventName === 'field') {
|
||||
self.autoFields = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Form.prototype.parse = function(req, cb) {
|
||||
var self = this;
|
||||
|
||||
// if the user supplies a callback, this implies autoFields and autoFiles
|
||||
if (cb) {
|
||||
self.autoFields = true;
|
||||
self.autoFiles = true;
|
||||
}
|
||||
|
||||
req.on('error', function(err) {
|
||||
error(self, err);
|
||||
});
|
||||
req.on('aborted', function() {
|
||||
self.emit('aborted');
|
||||
error(self, new Error("Request aborted"));
|
||||
});
|
||||
|
||||
self.bytesExpected = getBytesExpected(req.headers);
|
||||
|
||||
var contentType = req.headers['content-type'];
|
||||
if (!contentType) {
|
||||
error(self, new Error('missing content-type header'));
|
||||
return;
|
||||
}
|
||||
|
||||
var m = contentType.match(CONTENT_TYPE_RE);
|
||||
if (!m) {
|
||||
error(self, new Error('unrecognized content-type: ' + contentType));
|
||||
return;
|
||||
}
|
||||
var boundary = m[2] || m[3];
|
||||
setUpParser(self, boundary);
|
||||
req.pipe(self);
|
||||
|
||||
if (cb) {
|
||||
var fields = {}
|
||||
, files = {};
|
||||
self.on('error', function(err) {
|
||||
cb(err);
|
||||
});
|
||||
self.on('field', function(name, value) {
|
||||
fields[name] = value;
|
||||
});
|
||||
self.on('file', function(name, file) {
|
||||
files[name] = file;
|
||||
});
|
||||
self.on('close', function() {
|
||||
cb(null, fields, files);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
Form.prototype._write = function(buffer, encoding, cb) {
|
||||
var self = this
|
||||
, i = 0
|
||||
, len = buffer.length
|
||||
, prevIndex = self.index
|
||||
, index = self.index
|
||||
, state = self.state
|
||||
, lookbehind = self.lookbehind
|
||||
, boundary = self.boundary
|
||||
, boundaryChars = self.boundaryChars
|
||||
, boundaryLength = self.boundary.length
|
||||
, boundaryEnd = boundaryLength - 1
|
||||
, bufferLength = buffer.length
|
||||
, c
|
||||
, cl
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
c = buffer[i];
|
||||
switch (state) {
|
||||
case START:
|
||||
index = 0;
|
||||
state = START_BOUNDARY;
|
||||
/* falls through */
|
||||
case START_BOUNDARY:
|
||||
if (index === boundaryLength - 2) {
|
||||
if (c !== CR) return error(self, new Error("Expected CR Received " + c));
|
||||
index++;
|
||||
break;
|
||||
} else if (index === boundaryLength - 1) {
|
||||
if (c !== LF) return error(self, new Error("Expected LF Received " + c));
|
||||
index = 0;
|
||||
self.onParsePartBegin();
|
||||
state = HEADER_FIELD_START;
|
||||
break;
|
||||
}
|
||||
|
||||
if (c !== boundary[index+2]) index = -2;
|
||||
if (c === boundary[index+2]) index++;
|
||||
break;
|
||||
case HEADER_FIELD_START:
|
||||
state = HEADER_FIELD;
|
||||
self.headerFieldMark = i;
|
||||
index = 0;
|
||||
/* falls through */
|
||||
case HEADER_FIELD:
|
||||
if (c === CR) {
|
||||
self.headerFieldMark = null;
|
||||
state = HEADERS_ALMOST_DONE;
|
||||
break;
|
||||
}
|
||||
|
||||
index++;
|
||||
if (c === HYPHEN) break;
|
||||
|
||||
if (c === COLON) {
|
||||
if (index === 1) {
|
||||
// empty header field
|
||||
error(self, new Error("Empty header field"));
|
||||
return;
|
||||
}
|
||||
self.onParseHeaderField(buffer.slice(self.headerFieldMark, i));
|
||||
self.headerFieldMark = null;
|
||||
state = HEADER_VALUE_START;
|
||||
break;
|
||||
}
|
||||
|
||||
cl = lower(c);
|
||||
if (cl < A || cl > Z) {
|
||||
error(self, new Error("Expected alphabetic character, received " + c));
|
||||
return;
|
||||
}
|
||||
break;
|
||||
case HEADER_VALUE_START:
|
||||
if (c === SPACE) break;
|
||||
|
||||
self.headerValueMark = i;
|
||||
state = HEADER_VALUE;
|
||||
/* falls through */
|
||||
case HEADER_VALUE:
|
||||
if (c === CR) {
|
||||
self.onParseHeaderValue(buffer.slice(self.headerValueMark, i));
|
||||
self.headerValueMark = null;
|
||||
self.onParseHeaderEnd();
|
||||
state = HEADER_VALUE_ALMOST_DONE;
|
||||
}
|
||||
break;
|
||||
case HEADER_VALUE_ALMOST_DONE:
|
||||
if (c !== LF) return error(self, new Error("Expected LF Received " + c));
|
||||
state = HEADER_FIELD_START;
|
||||
break;
|
||||
case HEADERS_ALMOST_DONE:
|
||||
if (c !== LF) return error(self, new Error("Expected LF Received " + c));
|
||||
var err = self.onParseHeadersEnd(i + 1);
|
||||
if (err) return error(self, err);
|
||||
state = PART_DATA_START;
|
||||
break;
|
||||
case PART_DATA_START:
|
||||
state = PART_DATA;
|
||||
self.partDataMark = i;
|
||||
/* falls through */
|
||||
case PART_DATA:
|
||||
prevIndex = index;
|
||||
|
||||
if (index === 0) {
|
||||
// boyer-moore derrived algorithm to safely skip non-boundary data
|
||||
i += boundaryEnd;
|
||||
while (i < bufferLength && !(buffer[i] in boundaryChars)) {
|
||||
i += boundaryLength;
|
||||
}
|
||||
i -= boundaryEnd;
|
||||
c = buffer[i];
|
||||
}
|
||||
|
||||
if (index < boundaryLength) {
|
||||
if (boundary[index] === c) {
|
||||
if (index === 0) {
|
||||
self.onParsePartData(buffer.slice(self.partDataMark, i));
|
||||
self.partDataMark = null;
|
||||
}
|
||||
index++;
|
||||
} else {
|
||||
index = 0;
|
||||
}
|
||||
} else if (index === boundaryLength) {
|
||||
index++;
|
||||
if (c === CR) {
|
||||
// CR = part boundary
|
||||
self.partBoundaryFlag = true;
|
||||
} else if (c === HYPHEN) {
|
||||
// HYPHEN = end boundary
|
||||
self.lastBoundaryFlag = true;
|
||||
} else {
|
||||
index = 0;
|
||||
}
|
||||
} else if (index - 1 === boundaryLength) {
|
||||
if (self.partBoundaryFlag) {
|
||||
index = 0;
|
||||
if (c === LF) {
|
||||
self.partBoundaryFlag = false;
|
||||
self.onParsePartEnd();
|
||||
self.onParsePartBegin();
|
||||
state = HEADER_FIELD_START;
|
||||
break;
|
||||
}
|
||||
} else if (self.lastBoundaryFlag) {
|
||||
if (c === HYPHEN) {
|
||||
self.onParsePartEnd();
|
||||
self.end();
|
||||
state = END;
|
||||
} else {
|
||||
index = 0;
|
||||
}
|
||||
} else {
|
||||
index = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (index > 0) {
|
||||
// when matching a possible boundary, keep a lookbehind reference
|
||||
// in case it turns out to be a false lead
|
||||
lookbehind[index-1] = c;
|
||||
} else if (prevIndex > 0) {
|
||||
// if our boundary turned out to be rubbish, the captured lookbehind
|
||||
// belongs to partData
|
||||
self.onParsePartData(lookbehind.slice(0, prevIndex));
|
||||
prevIndex = 0;
|
||||
self.partDataMark = i;
|
||||
|
||||
// reconsider the current character even so it interrupted the sequence
|
||||
// it could be the beginning of a new sequence
|
||||
i--;
|
||||
}
|
||||
|
||||
break;
|
||||
case END:
|
||||
break;
|
||||
default:
|
||||
error(self, new Error("Parser has invalid state."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (self.headerFieldMark != null) {
|
||||
self.onParseHeaderField(buffer.slice(self.headerFieldMark));
|
||||
self.headerFieldMark = 0;
|
||||
}
|
||||
if (self.headerValueMark != null) {
|
||||
self.onParseHeaderValue(buffer.slice(self.headerValueMark));
|
||||
self.headerValueMark = 0;
|
||||
}
|
||||
if (self.partDataMark != null) {
|
||||
self.onParsePartData(buffer.slice(self.partDataMark));
|
||||
self.partDataMark = 0;
|
||||
}
|
||||
|
||||
self.index = index;
|
||||
self.state = state;
|
||||
|
||||
self.bytesReceived += buffer.length;
|
||||
self.emit('progress', self.bytesReceived, self.bytesExpected);
|
||||
|
||||
if (self.backpressure) {
|
||||
self.writeCbs.push(cb);
|
||||
} else {
|
||||
cb();
|
||||
}
|
||||
};
|
||||
|
||||
Form.prototype.onParsePartBegin = function() {
|
||||
clearPartVars(this);
|
||||
}
|
||||
|
||||
Form.prototype.onParseHeaderField = function(b) {
|
||||
this.headerField += this.headerFieldDecoder.write(b);
|
||||
}
|
||||
|
||||
Form.prototype.onParseHeaderValue = function(b) {
|
||||
this.headerValue += this.headerValueDecoder.write(b);
|
||||
}
|
||||
|
||||
Form.prototype.onParseHeaderEnd = function() {
|
||||
this.headerField = this.headerField.toLowerCase();
|
||||
this.partHeaders[this.headerField] = this.headerValue;
|
||||
|
||||
var m;
|
||||
if (this.headerField === 'content-disposition') {
|
||||
if (m = this.headerValue.match(/\bname="([^"]+)"/i)) {
|
||||
this.partName = m[1];
|
||||
}
|
||||
this.partFilename = parseFilename(this.headerValue);
|
||||
} else if (this.headerField === 'content-transfer-encoding') {
|
||||
this.partTransferEncoding = this.headerValue.toLowerCase();
|
||||
}
|
||||
|
||||
this.headerFieldDecoder = new StringDecoder(this.encoding);
|
||||
this.headerField = '';
|
||||
this.headerValueDecoder = new StringDecoder(this.encoding);
|
||||
this.headerValue = '';
|
||||
}
|
||||
|
||||
Form.prototype.onParsePartData = function(b) {
|
||||
if (this.partTransferEncoding === 'base64') {
|
||||
this.backpressure = ! this.destStream.write(b.toString('ascii'), 'base64');
|
||||
} else {
|
||||
this.backpressure = ! this.destStream.write(b);
|
||||
}
|
||||
}
|
||||
|
||||
Form.prototype.onParsePartEnd = function() {
|
||||
if (this.destStream) {
|
||||
flushWriteCbs(this);
|
||||
var s = this.destStream;
|
||||
process.nextTick(function() {
|
||||
s.end();
|
||||
});
|
||||
}
|
||||
clearPartVars(this);
|
||||
}
|
||||
|
||||
Form.prototype.onParseHeadersEnd = function(offset) {
|
||||
var self = this;
|
||||
switch(self.partTransferEncoding){
|
||||
case 'binary':
|
||||
case '7bit':
|
||||
case '8bit':
|
||||
self.partTransferEncoding = 'binary';
|
||||
break;
|
||||
|
||||
case 'base64': break;
|
||||
default:
|
||||
return new Error("unknown transfer-encoding: " + self.partTransferEncoding);
|
||||
}
|
||||
|
||||
self.destStream = new stream.PassThrough();
|
||||
self.destStream.on('drain', function() {
|
||||
flushWriteCbs(self);
|
||||
});
|
||||
self.destStream.headers = self.partHeaders;
|
||||
self.destStream.name = self.partName;
|
||||
self.destStream.filename = self.partFilename;
|
||||
self.destStream.byteOffset = self.bytesReceived + offset;
|
||||
var partContentLength = self.destStream.headers['content-length'];
|
||||
self.destStream.byteCount = partContentLength ?
|
||||
parseInt(partContentLength, 10) :
|
||||
(self.bytesExpected - self.destStream.byteOffset -
|
||||
self.boundary.length - LAST_BOUNDARY_SUFFIX_LEN);
|
||||
self.totalFieldCount += 1;
|
||||
if (self.totalFieldCount >= self.maxFields) {
|
||||
error(self, new Error("maxFields " + self.maxFields + " exceeded."));
|
||||
return;
|
||||
}
|
||||
|
||||
self.emit('part', self.destStream);
|
||||
if (self.destStream.filename == null && self.autoFields) {
|
||||
handleField(self, self.destStream);
|
||||
} else if (self.destStream.filename != null && self.autoFiles) {
|
||||
handleFile(self, self.destStream);
|
||||
}
|
||||
}
|
||||
|
||||
function flushWriteCbs(self) {
|
||||
self.writeCbs.forEach(function(cb) {
|
||||
process.nextTick(cb);
|
||||
});
|
||||
self.writeCbs = [];
|
||||
self.backpressure = false;
|
||||
}
|
||||
|
||||
function getBytesExpected(headers) {
|
||||
var contentLength = headers['content-length'];
|
||||
if (contentLength) {
|
||||
return parseInt(contentLength, 10);
|
||||
} else if (headers['transfer-encoding'] == null) {
|
||||
return 0;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function error(self, err) {
|
||||
assert.ok(!self.error, err.stack);
|
||||
self.error = err;
|
||||
self.emit('error', err);
|
||||
self.openedFiles.forEach(function(file) {
|
||||
file.ws.destroy();
|
||||
fs.unlink(file.path, function(err) {
|
||||
// this is already an error condition, ignore 2nd error
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function beginFlush(self) {
|
||||
self.flushing += 1;
|
||||
}
|
||||
|
||||
function endFlush(self) {
|
||||
self.flushing -= 1;
|
||||
maybeClose(self);
|
||||
}
|
||||
|
||||
function maybeClose(self) {
|
||||
if (!self.flushing && self.finished && !self.error) {
|
||||
self.emit('close');
|
||||
}
|
||||
}
|
||||
|
||||
function handleFile(self, fileStream) {
|
||||
beginFlush(self);
|
||||
var file = {
|
||||
originalFilename: fileStream.filename,
|
||||
path: uploadPath(self.uploadDir, fileStream.filename),
|
||||
headers: fileStream.headers,
|
||||
};
|
||||
file.ws = fs.createWriteStream(file.path);
|
||||
self.openedFiles.push(file);
|
||||
fileStream.pipe(file.ws);
|
||||
var counter = new StreamCounter();
|
||||
fileStream.pipe(counter);
|
||||
var hashWorkaroundStream
|
||||
, hash = null;
|
||||
if (self.hash) {
|
||||
// workaround stream because https://github.com/joyent/node/issues/5216
|
||||
hashWorkaroundStream = stream.Writable();
|
||||
hash = crypto.createHash(self.hash);
|
||||
hashWorkaroundStream._write = function(buffer, encoding, callback) {
|
||||
hash.update(buffer);
|
||||
callback();
|
||||
};
|
||||
fileStream.pipe(hashWorkaroundStream);
|
||||
}
|
||||
file.ws.on('error', function(err) {
|
||||
error(self, err);
|
||||
});
|
||||
file.ws.on('close', function() {
|
||||
if (hash) file.hash = hash.digest('hex');
|
||||
file.size = counter.bytes;
|
||||
self.emit('file', fileStream.name, file);
|
||||
endFlush(self);
|
||||
});
|
||||
}
|
||||
|
||||
function handleField(self, fieldStream) {
|
||||
var value = ''
|
||||
, decoder = new StringDecoder(self.encoding);
|
||||
|
||||
beginFlush(self);
|
||||
fieldStream.on('readable', function() {
|
||||
var buffer = fieldStream.read();
|
||||
self.totalFieldSize += buffer.length;
|
||||
if (self.totalFieldSize > self.maxFieldsSize) {
|
||||
error(self, new Error("maxFieldsSize " + self.maxFieldsSize + " exceeded"));
|
||||
return;
|
||||
}
|
||||
value += decoder.write(buffer);
|
||||
});
|
||||
|
||||
fieldStream.on('end', function() {
|
||||
self.emit('field', fieldStream.name, value);
|
||||
endFlush(self);
|
||||
});
|
||||
}
|
||||
|
||||
function clearPartVars(self) {
|
||||
self.partHeaders = {};
|
||||
self.partName = null;
|
||||
self.partFilename = null;
|
||||
self.partTransferEncoding = 'binary';
|
||||
self.destStream = null;
|
||||
|
||||
self.headerFieldDecoder = new StringDecoder(self.encoding);
|
||||
self.headerField = "";
|
||||
self.headerValueDecoder = new StringDecoder(self.encoding);
|
||||
self.headerValue = "";
|
||||
}
|
||||
|
||||
function setUpParser(self, boundary) {
|
||||
self.boundary = new Buffer(boundary.length + 4);
|
||||
self.boundary.write('\r\n--', 0, boundary.length + 4, 'ascii');
|
||||
self.boundary.write(boundary, 4, boundary.length, 'ascii');
|
||||
self.lookbehind = new Buffer(self.boundary.length + 8);
|
||||
self.state = START;
|
||||
self.boundaryChars = {};
|
||||
for (var i = 0; i < self.boundary.length; i++) {
|
||||
self.boundaryChars[self.boundary[i]] = true;
|
||||
}
|
||||
|
||||
self.index = null;
|
||||
self.partBoundaryFlag = false;
|
||||
self.lastBoundaryFlag = false;
|
||||
|
||||
self.on('finish', function() {
|
||||
if ((self.state === HEADER_FIELD_START && self.index === 0) ||
|
||||
(self.state === PART_DATA && self.index === self.boundary.length))
|
||||
{
|
||||
self.onParsePartEnd();
|
||||
} else if (self.state !== END) {
|
||||
error(self, new Error('stream ended unexpectedly'));
|
||||
}
|
||||
self.finished = true;
|
||||
maybeClose(self);
|
||||
});
|
||||
}
|
||||
|
||||
function uploadPath(baseDir, filename) {
|
||||
var ext = path.extname(filename).replace(FILE_EXT_RE, '$1');
|
||||
var name = process.pid + '-' +
|
||||
(Math.random() * 0x100000000 + 1).toString(36) + ext;
|
||||
return path.join(baseDir, name);
|
||||
}
|
||||
|
||||
function parseFilename(headerValue) {
|
||||
var m = headerValue.match(/\bfilename="(.*?)"($|; )/i);
|
||||
if (!m) return;
|
||||
|
||||
var filename = m[1].substr(m[1].lastIndexOf('\\') + 1);
|
||||
filename = filename.replace(/%22/g, '"');
|
||||
filename = filename.replace(/&#([\d]{4});/g, function(m, code) {
|
||||
return String.fromCharCode(code);
|
||||
});
|
||||
return filename;
|
||||
}
|
||||
|
||||
function lower(c) {
|
||||
return c | 0x20;
|
||||
}
|
||||
|
||||
gerado
externo
+27
@@ -0,0 +1,27 @@
|
||||
Copyright (c) Isaac Z. Schlueter ("Author")
|
||||
All rights reserved.
|
||||
|
||||
The BSD License
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS
|
||||
BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
|
||||
IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
gerado
externo
+768
@@ -0,0 +1,768 @@
|
||||
# readable-stream
|
||||
|
||||
A new class of streams for Node.js
|
||||
|
||||
This module provides the new Stream base classes introduced in Node
|
||||
v0.10, for use in Node v0.8. You can use it to have programs that
|
||||
have to work with node v0.8, while being forward-compatible for v0.10
|
||||
and beyond. When you drop support for v0.8, you can remove this
|
||||
module, and only use the native streams.
|
||||
|
||||
This is almost exactly the same codebase as appears in Node v0.10.
|
||||
However:
|
||||
|
||||
1. The exported object is actually the Readable class. Decorating the
|
||||
native `stream` module would be global pollution.
|
||||
2. In v0.10, you can safely use `base64` as an argument to
|
||||
`setEncoding` in Readable streams. However, in v0.8, the
|
||||
StringDecoder class has no `end()` method, which is problematic for
|
||||
Base64. So, don't use that, because it'll break and be weird.
|
||||
|
||||
Other than that, the API is the same as `require('stream')` in v0.10,
|
||||
so the API docs are reproduced below.
|
||||
|
||||
----------
|
||||
|
||||
Stability: 2 - Unstable
|
||||
|
||||
A stream is an abstract interface implemented by various objects in
|
||||
Node. For example a request to an HTTP server is a stream, as is
|
||||
stdout. Streams are readable, writable, or both. All streams are
|
||||
instances of [EventEmitter][]
|
||||
|
||||
You can load the Stream base classes by doing `require('stream')`.
|
||||
There are base classes provided for Readable streams, Writable
|
||||
streams, Duplex streams, and Transform streams.
|
||||
|
||||
## Compatibility
|
||||
|
||||
In earlier versions of Node, the Readable stream interface was
|
||||
simpler, but also less powerful and less useful.
|
||||
|
||||
* Rather than waiting for you to call the `read()` method, `'data'`
|
||||
events would start emitting immediately. If you needed to do some
|
||||
I/O to decide how to handle data, then you had to store the chunks
|
||||
in some kind of buffer so that they would not be lost.
|
||||
* The `pause()` method was advisory, rather than guaranteed. This
|
||||
meant that you still had to be prepared to receive `'data'` events
|
||||
even when the stream was in a paused state.
|
||||
|
||||
In Node v0.10, the Readable class described below was added. For
|
||||
backwards compatibility with older Node programs, Readable streams
|
||||
switch into "old mode" when a `'data'` event handler is added, or when
|
||||
the `pause()` or `resume()` methods are called. The effect is that,
|
||||
even if you are not using the new `read()` method and `'readable'`
|
||||
event, you no longer have to worry about losing `'data'` chunks.
|
||||
|
||||
Most programs will continue to function normally. However, this
|
||||
introduces an edge case in the following conditions:
|
||||
|
||||
* No `'data'` event handler is added.
|
||||
* The `pause()` and `resume()` methods are never called.
|
||||
|
||||
For example, consider the following code:
|
||||
|
||||
```javascript
|
||||
// WARNING! BROKEN!
|
||||
net.createServer(function(socket) {
|
||||
|
||||
// we add an 'end' method, but never consume the data
|
||||
socket.on('end', function() {
|
||||
// It will never get here.
|
||||
socket.end('I got your message (but didnt read it)\n');
|
||||
});
|
||||
|
||||
}).listen(1337);
|
||||
```
|
||||
|
||||
In versions of node prior to v0.10, the incoming message data would be
|
||||
simply discarded. However, in Node v0.10 and beyond, the socket will
|
||||
remain paused forever.
|
||||
|
||||
The workaround in this situation is to call the `resume()` method to
|
||||
trigger "old mode" behavior:
|
||||
|
||||
```javascript
|
||||
// Workaround
|
||||
net.createServer(function(socket) {
|
||||
|
||||
socket.on('end', function() {
|
||||
socket.end('I got your message (but didnt read it)\n');
|
||||
});
|
||||
|
||||
// start the flow of data, discarding it.
|
||||
socket.resume();
|
||||
|
||||
}).listen(1337);
|
||||
```
|
||||
|
||||
In addition to new Readable streams switching into old-mode, pre-v0.10
|
||||
style streams can be wrapped in a Readable class using the `wrap()`
|
||||
method.
|
||||
|
||||
## Class: stream.Readable
|
||||
|
||||
<!--type=class-->
|
||||
|
||||
A `Readable Stream` has the following methods, members, and events.
|
||||
|
||||
Note that `stream.Readable` is an abstract class designed to be
|
||||
extended with an underlying implementation of the `_read(size)`
|
||||
method. (See below.)
|
||||
|
||||
### new stream.Readable([options])
|
||||
|
||||
* `options` {Object}
|
||||
* `highWaterMark` {Number} The maximum number of bytes to store in
|
||||
the internal buffer before ceasing to read from the underlying
|
||||
resource. Default=16kb
|
||||
* `encoding` {String} If specified, then buffers will be decoded to
|
||||
strings using the specified encoding. Default=null
|
||||
* `objectMode` {Boolean} Whether this stream should behave
|
||||
as a stream of objects. Meaning that stream.read(n) returns
|
||||
a single value instead of a Buffer of size n
|
||||
|
||||
In classes that extend the Readable class, make sure to call the
|
||||
constructor so that the buffering settings can be properly
|
||||
initialized.
|
||||
|
||||
### readable.\_read(size)
|
||||
|
||||
* `size` {Number} Number of bytes to read asynchronously
|
||||
|
||||
Note: **This function should NOT be called directly.** It should be
|
||||
implemented by child classes, and called by the internal Readable
|
||||
class methods only.
|
||||
|
||||
All Readable stream implementations must provide a `_read` method
|
||||
to fetch data from the underlying resource.
|
||||
|
||||
This method is prefixed with an underscore because it is internal to
|
||||
the class that defines it, and should not be called directly by user
|
||||
programs. However, you **are** expected to override this method in
|
||||
your own extension classes.
|
||||
|
||||
When data is available, put it into the read queue by calling
|
||||
`readable.push(chunk)`. If `push` returns false, then you should stop
|
||||
reading. When `_read` is called again, you should start pushing more
|
||||
data.
|
||||
|
||||
The `size` argument is advisory. Implementations where a "read" is a
|
||||
single call that returns data can use this to know how much data to
|
||||
fetch. Implementations where that is not relevant, such as TCP or
|
||||
TLS, may ignore this argument, and simply provide data whenever it
|
||||
becomes available. There is no need, for example to "wait" until
|
||||
`size` bytes are available before calling `stream.push(chunk)`.
|
||||
|
||||
### readable.push(chunk)
|
||||
|
||||
* `chunk` {Buffer | null | String} Chunk of data to push into the read queue
|
||||
* return {Boolean} Whether or not more pushes should be performed
|
||||
|
||||
Note: **This function should be called by Readable implementors, NOT
|
||||
by consumers of Readable subclasses.** The `_read()` function will not
|
||||
be called again until at least one `push(chunk)` call is made. If no
|
||||
data is available, then you MAY call `push('')` (an empty string) to
|
||||
allow a future `_read` call, without adding any data to the queue.
|
||||
|
||||
The `Readable` class works by putting data into a read queue to be
|
||||
pulled out later by calling the `read()` method when the `'readable'`
|
||||
event fires.
|
||||
|
||||
The `push()` method will explicitly insert some data into the read
|
||||
queue. If it is called with `null` then it will signal the end of the
|
||||
data.
|
||||
|
||||
In some cases, you may be wrapping a lower-level source which has some
|
||||
sort of pause/resume mechanism, and a data callback. In those cases,
|
||||
you could wrap the low-level source object by doing something like
|
||||
this:
|
||||
|
||||
```javascript
|
||||
// source is an object with readStop() and readStart() methods,
|
||||
// and an `ondata` member that gets called when it has data, and
|
||||
// an `onend` member that gets called when the data is over.
|
||||
|
||||
var stream = new Readable();
|
||||
|
||||
source.ondata = function(chunk) {
|
||||
// if push() returns false, then we need to stop reading from source
|
||||
if (!stream.push(chunk))
|
||||
source.readStop();
|
||||
};
|
||||
|
||||
source.onend = function() {
|
||||
stream.push(null);
|
||||
};
|
||||
|
||||
// _read will be called when the stream wants to pull more data in
|
||||
// the advisory size argument is ignored in this case.
|
||||
stream._read = function(n) {
|
||||
source.readStart();
|
||||
};
|
||||
```
|
||||
|
||||
### readable.unshift(chunk)
|
||||
|
||||
* `chunk` {Buffer | null | String} Chunk of data to unshift onto the read queue
|
||||
* return {Boolean} Whether or not more pushes should be performed
|
||||
|
||||
This is the corollary of `readable.push(chunk)`. Rather than putting
|
||||
the data at the *end* of the read queue, it puts it at the *front* of
|
||||
the read queue.
|
||||
|
||||
This is useful in certain use-cases where a stream is being consumed
|
||||
by a parser, which needs to "un-consume" some data that it has
|
||||
optimistically pulled out of the source.
|
||||
|
||||
```javascript
|
||||
// A parser for a simple data protocol.
|
||||
// The "header" is a JSON object, followed by 2 \n characters, and
|
||||
// then a message body.
|
||||
//
|
||||
// Note: This can be done more simply as a Transform stream. See below.
|
||||
|
||||
function SimpleProtocol(source, options) {
|
||||
if (!(this instanceof SimpleProtocol))
|
||||
return new SimpleProtocol(options);
|
||||
|
||||
Readable.call(this, options);
|
||||
this._inBody = false;
|
||||
this._sawFirstCr = false;
|
||||
|
||||
// source is a readable stream, such as a socket or file
|
||||
this._source = source;
|
||||
|
||||
var self = this;
|
||||
source.on('end', function() {
|
||||
self.push(null);
|
||||
});
|
||||
|
||||
// give it a kick whenever the source is readable
|
||||
// read(0) will not consume any bytes
|
||||
source.on('readable', function() {
|
||||
self.read(0);
|
||||
});
|
||||
|
||||
this._rawHeader = [];
|
||||
this.header = null;
|
||||
}
|
||||
|
||||
SimpleProtocol.prototype = Object.create(
|
||||
Readable.prototype, { constructor: { value: SimpleProtocol }});
|
||||
|
||||
SimpleProtocol.prototype._read = function(n) {
|
||||
if (!this._inBody) {
|
||||
var chunk = this._source.read();
|
||||
|
||||
// if the source doesn't have data, we don't have data yet.
|
||||
if (chunk === null)
|
||||
return this.push('');
|
||||
|
||||
// check if the chunk has a \n\n
|
||||
var split = -1;
|
||||
for (var i = 0; i < chunk.length; i++) {
|
||||
if (chunk[i] === 10) { // '\n'
|
||||
if (this._sawFirstCr) {
|
||||
split = i;
|
||||
break;
|
||||
} else {
|
||||
this._sawFirstCr = true;
|
||||
}
|
||||
} else {
|
||||
this._sawFirstCr = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (split === -1) {
|
||||
// still waiting for the \n\n
|
||||
// stash the chunk, and try again.
|
||||
this._rawHeader.push(chunk);
|
||||
this.push('');
|
||||
} else {
|
||||
this._inBody = true;
|
||||
var h = chunk.slice(0, split);
|
||||
this._rawHeader.push(h);
|
||||
var header = Buffer.concat(this._rawHeader).toString();
|
||||
try {
|
||||
this.header = JSON.parse(header);
|
||||
} catch (er) {
|
||||
this.emit('error', new Error('invalid simple protocol data'));
|
||||
return;
|
||||
}
|
||||
// now, because we got some extra data, unshift the rest
|
||||
// back into the read queue so that our consumer will see it.
|
||||
var b = chunk.slice(split);
|
||||
this.unshift(b);
|
||||
|
||||
// and let them know that we are done parsing the header.
|
||||
this.emit('header', this.header);
|
||||
}
|
||||
} else {
|
||||
// from there on, just provide the data to our consumer.
|
||||
// careful not to push(null), since that would indicate EOF.
|
||||
var chunk = this._source.read();
|
||||
if (chunk) this.push(chunk);
|
||||
}
|
||||
};
|
||||
|
||||
// Usage:
|
||||
var parser = new SimpleProtocol(source);
|
||||
// Now parser is a readable stream that will emit 'header'
|
||||
// with the parsed header data.
|
||||
```
|
||||
|
||||
### readable.wrap(stream)
|
||||
|
||||
* `stream` {Stream} An "old style" readable stream
|
||||
|
||||
If you are using an older Node library that emits `'data'` events and
|
||||
has a `pause()` method that is advisory only, then you can use the
|
||||
`wrap()` method to create a Readable stream that uses the old stream
|
||||
as its data source.
|
||||
|
||||
For example:
|
||||
|
||||
```javascript
|
||||
var OldReader = require('./old-api-module.js').OldReader;
|
||||
var oreader = new OldReader;
|
||||
var Readable = require('stream').Readable;
|
||||
var myReader = new Readable().wrap(oreader);
|
||||
|
||||
myReader.on('readable', function() {
|
||||
myReader.read(); // etc.
|
||||
});
|
||||
```
|
||||
|
||||
### Event: 'readable'
|
||||
|
||||
When there is data ready to be consumed, this event will fire.
|
||||
|
||||
When this event emits, call the `read()` method to consume the data.
|
||||
|
||||
### Event: 'end'
|
||||
|
||||
Emitted when the stream has received an EOF (FIN in TCP terminology).
|
||||
Indicates that no more `'data'` events will happen. If the stream is
|
||||
also writable, it may be possible to continue writing.
|
||||
|
||||
### Event: 'data'
|
||||
|
||||
The `'data'` event emits either a `Buffer` (by default) or a string if
|
||||
`setEncoding()` was used.
|
||||
|
||||
Note that adding a `'data'` event listener will switch the Readable
|
||||
stream into "old mode", where data is emitted as soon as it is
|
||||
available, rather than waiting for you to call `read()` to consume it.
|
||||
|
||||
### Event: 'error'
|
||||
|
||||
Emitted if there was an error receiving data.
|
||||
|
||||
### Event: 'close'
|
||||
|
||||
Emitted when the underlying resource (for example, the backing file
|
||||
descriptor) has been closed. Not all streams will emit this.
|
||||
|
||||
### readable.setEncoding(encoding)
|
||||
|
||||
Makes the `'data'` event emit a string instead of a `Buffer`. `encoding`
|
||||
can be `'utf8'`, `'utf16le'` (`'ucs2'`), `'ascii'`, or `'hex'`.
|
||||
|
||||
The encoding can also be set by specifying an `encoding` field to the
|
||||
constructor.
|
||||
|
||||
### readable.read([size])
|
||||
|
||||
* `size` {Number | null} Optional number of bytes to read.
|
||||
* Return: {Buffer | String | null}
|
||||
|
||||
Note: **This function SHOULD be called by Readable stream users.**
|
||||
|
||||
Call this method to consume data once the `'readable'` event is
|
||||
emitted.
|
||||
|
||||
The `size` argument will set a minimum number of bytes that you are
|
||||
interested in. If not set, then the entire content of the internal
|
||||
buffer is returned.
|
||||
|
||||
If there is no data to consume, or if there are fewer bytes in the
|
||||
internal buffer than the `size` argument, then `null` is returned, and
|
||||
a future `'readable'` event will be emitted when more is available.
|
||||
|
||||
Calling `stream.read(0)` will always return `null`, and will trigger a
|
||||
refresh of the internal buffer, but otherwise be a no-op.
|
||||
|
||||
### readable.pipe(destination, [options])
|
||||
|
||||
* `destination` {Writable Stream}
|
||||
* `options` {Object} Optional
|
||||
* `end` {Boolean} Default=true
|
||||
|
||||
Connects this readable stream to `destination` WriteStream. Incoming
|
||||
data on this stream gets written to `destination`. Properly manages
|
||||
back-pressure so that a slow destination will not be overwhelmed by a
|
||||
fast readable stream.
|
||||
|
||||
This function returns the `destination` stream.
|
||||
|
||||
For example, emulating the Unix `cat` command:
|
||||
|
||||
process.stdin.pipe(process.stdout);
|
||||
|
||||
By default `end()` is called on the destination when the source stream
|
||||
emits `end`, so that `destination` is no longer writable. Pass `{ end:
|
||||
false }` as `options` to keep the destination stream open.
|
||||
|
||||
This keeps `writer` open so that "Goodbye" can be written at the
|
||||
end.
|
||||
|
||||
reader.pipe(writer, { end: false });
|
||||
reader.on("end", function() {
|
||||
writer.end("Goodbye\n");
|
||||
});
|
||||
|
||||
Note that `process.stderr` and `process.stdout` are never closed until
|
||||
the process exits, regardless of the specified options.
|
||||
|
||||
### readable.unpipe([destination])
|
||||
|
||||
* `destination` {Writable Stream} Optional
|
||||
|
||||
Undo a previously established `pipe()`. If no destination is
|
||||
provided, then all previously established pipes are removed.
|
||||
|
||||
### readable.pause()
|
||||
|
||||
Switches the readable stream into "old mode", where data is emitted
|
||||
using a `'data'` event rather than being buffered for consumption via
|
||||
the `read()` method.
|
||||
|
||||
Ceases the flow of data. No `'data'` events are emitted while the
|
||||
stream is in a paused state.
|
||||
|
||||
### readable.resume()
|
||||
|
||||
Switches the readable stream into "old mode", where data is emitted
|
||||
using a `'data'` event rather than being buffered for consumption via
|
||||
the `read()` method.
|
||||
|
||||
Resumes the incoming `'data'` events after a `pause()`.
|
||||
|
||||
|
||||
## Class: stream.Writable
|
||||
|
||||
<!--type=class-->
|
||||
|
||||
A `Writable` Stream has the following methods, members, and events.
|
||||
|
||||
Note that `stream.Writable` is an abstract class designed to be
|
||||
extended with an underlying implementation of the
|
||||
`_write(chunk, encoding, cb)` method. (See below.)
|
||||
|
||||
### new stream.Writable([options])
|
||||
|
||||
* `options` {Object}
|
||||
* `highWaterMark` {Number} Buffer level when `write()` starts
|
||||
returning false. Default=16kb
|
||||
* `decodeStrings` {Boolean} Whether or not to decode strings into
|
||||
Buffers before passing them to `_write()`. Default=true
|
||||
|
||||
In classes that extend the Writable class, make sure to call the
|
||||
constructor so that the buffering settings can be properly
|
||||
initialized.
|
||||
|
||||
### writable.\_write(chunk, encoding, callback)
|
||||
|
||||
* `chunk` {Buffer | String} The chunk to be written. Will always
|
||||
be a buffer unless the `decodeStrings` option was set to `false`.
|
||||
* `encoding` {String} If the chunk is a string, then this is the
|
||||
encoding type. Ignore chunk is a buffer. Note that chunk will
|
||||
**always** be a buffer unless the `decodeStrings` option is
|
||||
explicitly set to `false`.
|
||||
* `callback` {Function} Call this function (optionally with an error
|
||||
argument) when you are done processing the supplied chunk.
|
||||
|
||||
All Writable stream implementations must provide a `_write` method to
|
||||
send data to the underlying resource.
|
||||
|
||||
Note: **This function MUST NOT be called directly.** It should be
|
||||
implemented by child classes, and called by the internal Writable
|
||||
class methods only.
|
||||
|
||||
Call the callback using the standard `callback(error)` pattern to
|
||||
signal that the write completed successfully or with an error.
|
||||
|
||||
If the `decodeStrings` flag is set in the constructor options, then
|
||||
`chunk` may be a string rather than a Buffer, and `encoding` will
|
||||
indicate the sort of string that it is. This is to support
|
||||
implementations that have an optimized handling for certain string
|
||||
data encodings. If you do not explicitly set the `decodeStrings`
|
||||
option to `false`, then you can safely ignore the `encoding` argument,
|
||||
and assume that `chunk` will always be a Buffer.
|
||||
|
||||
This method is prefixed with an underscore because it is internal to
|
||||
the class that defines it, and should not be called directly by user
|
||||
programs. However, you **are** expected to override this method in
|
||||
your own extension classes.
|
||||
|
||||
|
||||
### writable.write(chunk, [encoding], [callback])
|
||||
|
||||
* `chunk` {Buffer | String} Data to be written
|
||||
* `encoding` {String} Optional. If `chunk` is a string, then encoding
|
||||
defaults to `'utf8'`
|
||||
* `callback` {Function} Optional. Called when this chunk is
|
||||
successfully written.
|
||||
* Returns {Boolean}
|
||||
|
||||
Writes `chunk` to the stream. Returns `true` if the data has been
|
||||
flushed to the underlying resource. Returns `false` to indicate that
|
||||
the buffer is full, and the data will be sent out in the future. The
|
||||
`'drain'` event will indicate when the buffer is empty again.
|
||||
|
||||
The specifics of when `write()` will return false, is determined by
|
||||
the `highWaterMark` option provided to the constructor.
|
||||
|
||||
### writable.end([chunk], [encoding], [callback])
|
||||
|
||||
* `chunk` {Buffer | String} Optional final data to be written
|
||||
* `encoding` {String} Optional. If `chunk` is a string, then encoding
|
||||
defaults to `'utf8'`
|
||||
* `callback` {Function} Optional. Called when the final chunk is
|
||||
successfully written.
|
||||
|
||||
Call this method to signal the end of the data being written to the
|
||||
stream.
|
||||
|
||||
### Event: 'drain'
|
||||
|
||||
Emitted when the stream's write queue empties and it's safe to write
|
||||
without buffering again. Listen for it when `stream.write()` returns
|
||||
`false`.
|
||||
|
||||
### Event: 'close'
|
||||
|
||||
Emitted when the underlying resource (for example, the backing file
|
||||
descriptor) has been closed. Not all streams will emit this.
|
||||
|
||||
### Event: 'finish'
|
||||
|
||||
When `end()` is called and there are no more chunks to write, this
|
||||
event is emitted.
|
||||
|
||||
### Event: 'pipe'
|
||||
|
||||
* `source` {Readable Stream}
|
||||
|
||||
Emitted when the stream is passed to a readable stream's pipe method.
|
||||
|
||||
### Event 'unpipe'
|
||||
|
||||
* `source` {Readable Stream}
|
||||
|
||||
Emitted when a previously established `pipe()` is removed using the
|
||||
source Readable stream's `unpipe()` method.
|
||||
|
||||
## Class: stream.Duplex
|
||||
|
||||
<!--type=class-->
|
||||
|
||||
A "duplex" stream is one that is both Readable and Writable, such as a
|
||||
TCP socket connection.
|
||||
|
||||
Note that `stream.Duplex` is an abstract class designed to be
|
||||
extended with an underlying implementation of the `_read(size)`
|
||||
and `_write(chunk, encoding, callback)` methods as you would with a Readable or
|
||||
Writable stream class.
|
||||
|
||||
Since JavaScript doesn't have multiple prototypal inheritance, this
|
||||
class prototypally inherits from Readable, and then parasitically from
|
||||
Writable. It is thus up to the user to implement both the lowlevel
|
||||
`_read(n)` method as well as the lowlevel `_write(chunk, encoding, cb)` method
|
||||
on extension duplex classes.
|
||||
|
||||
### new stream.Duplex(options)
|
||||
|
||||
* `options` {Object} Passed to both Writable and Readable
|
||||
constructors. Also has the following fields:
|
||||
* `allowHalfOpen` {Boolean} Default=true. If set to `false`, then
|
||||
the stream will automatically end the readable side when the
|
||||
writable side ends and vice versa.
|
||||
|
||||
In classes that extend the Duplex class, make sure to call the
|
||||
constructor so that the buffering settings can be properly
|
||||
initialized.
|
||||
|
||||
## Class: stream.Transform
|
||||
|
||||
A "transform" stream is a duplex stream where the output is causally
|
||||
connected in some way to the input, such as a zlib stream or a crypto
|
||||
stream.
|
||||
|
||||
There is no requirement that the output be the same size as the input,
|
||||
the same number of chunks, or arrive at the same time. For example, a
|
||||
Hash stream will only ever have a single chunk of output which is
|
||||
provided when the input is ended. A zlib stream will either produce
|
||||
much smaller or much larger than its input.
|
||||
|
||||
Rather than implement the `_read()` and `_write()` methods, Transform
|
||||
classes must implement the `_transform()` method, and may optionally
|
||||
also implement the `_flush()` method. (See below.)
|
||||
|
||||
### new stream.Transform([options])
|
||||
|
||||
* `options` {Object} Passed to both Writable and Readable
|
||||
constructors.
|
||||
|
||||
In classes that extend the Transform class, make sure to call the
|
||||
constructor so that the buffering settings can be properly
|
||||
initialized.
|
||||
|
||||
### transform.\_transform(chunk, encoding, callback)
|
||||
|
||||
* `chunk` {Buffer | String} The chunk to be transformed. Will always
|
||||
be a buffer unless the `decodeStrings` option was set to `false`.
|
||||
* `encoding` {String} If the chunk is a string, then this is the
|
||||
encoding type. (Ignore if `decodeStrings` chunk is a buffer.)
|
||||
* `callback` {Function} Call this function (optionally with an error
|
||||
argument) when you are done processing the supplied chunk.
|
||||
|
||||
Note: **This function MUST NOT be called directly.** It should be
|
||||
implemented by child classes, and called by the internal Transform
|
||||
class methods only.
|
||||
|
||||
All Transform stream implementations must provide a `_transform`
|
||||
method to accept input and produce output.
|
||||
|
||||
`_transform` should do whatever has to be done in this specific
|
||||
Transform class, to handle the bytes being written, and pass them off
|
||||
to the readable portion of the interface. Do asynchronous I/O,
|
||||
process things, and so on.
|
||||
|
||||
Call `transform.push(outputChunk)` 0 or more times to generate output
|
||||
from this input chunk, depending on how much data you want to output
|
||||
as a result of this chunk.
|
||||
|
||||
Call the callback function only when the current chunk is completely
|
||||
consumed. Note that there may or may not be output as a result of any
|
||||
particular input chunk.
|
||||
|
||||
This method is prefixed with an underscore because it is internal to
|
||||
the class that defines it, and should not be called directly by user
|
||||
programs. However, you **are** expected to override this method in
|
||||
your own extension classes.
|
||||
|
||||
### transform.\_flush(callback)
|
||||
|
||||
* `callback` {Function} Call this function (optionally with an error
|
||||
argument) when you are done flushing any remaining data.
|
||||
|
||||
Note: **This function MUST NOT be called directly.** It MAY be implemented
|
||||
by child classes, and if so, will be called by the internal Transform
|
||||
class methods only.
|
||||
|
||||
In some cases, your transform operation may need to emit a bit more
|
||||
data at the end of the stream. For example, a `Zlib` compression
|
||||
stream will store up some internal state so that it can optimally
|
||||
compress the output. At the end, however, it needs to do the best it
|
||||
can with what is left, so that the data will be complete.
|
||||
|
||||
In those cases, you can implement a `_flush` method, which will be
|
||||
called at the very end, after all the written data is consumed, but
|
||||
before emitting `end` to signal the end of the readable side. Just
|
||||
like with `_transform`, call `transform.push(chunk)` zero or more
|
||||
times, as appropriate, and call `callback` when the flush operation is
|
||||
complete.
|
||||
|
||||
This method is prefixed with an underscore because it is internal to
|
||||
the class that defines it, and should not be called directly by user
|
||||
programs. However, you **are** expected to override this method in
|
||||
your own extension classes.
|
||||
|
||||
### Example: `SimpleProtocol` parser
|
||||
|
||||
The example above of a simple protocol parser can be implemented much
|
||||
more simply by using the higher level `Transform` stream class.
|
||||
|
||||
In this example, rather than providing the input as an argument, it
|
||||
would be piped into the parser, which is a more idiomatic Node stream
|
||||
approach.
|
||||
|
||||
```javascript
|
||||
function SimpleProtocol(options) {
|
||||
if (!(this instanceof SimpleProtocol))
|
||||
return new SimpleProtocol(options);
|
||||
|
||||
Transform.call(this, options);
|
||||
this._inBody = false;
|
||||
this._sawFirstCr = false;
|
||||
this._rawHeader = [];
|
||||
this.header = null;
|
||||
}
|
||||
|
||||
SimpleProtocol.prototype = Object.create(
|
||||
Transform.prototype, { constructor: { value: SimpleProtocol }});
|
||||
|
||||
SimpleProtocol.prototype._transform = function(chunk, encoding, done) {
|
||||
if (!this._inBody) {
|
||||
// check if the chunk has a \n\n
|
||||
var split = -1;
|
||||
for (var i = 0; i < chunk.length; i++) {
|
||||
if (chunk[i] === 10) { // '\n'
|
||||
if (this._sawFirstCr) {
|
||||
split = i;
|
||||
break;
|
||||
} else {
|
||||
this._sawFirstCr = true;
|
||||
}
|
||||
} else {
|
||||
this._sawFirstCr = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (split === -1) {
|
||||
// still waiting for the \n\n
|
||||
// stash the chunk, and try again.
|
||||
this._rawHeader.push(chunk);
|
||||
} else {
|
||||
this._inBody = true;
|
||||
var h = chunk.slice(0, split);
|
||||
this._rawHeader.push(h);
|
||||
var header = Buffer.concat(this._rawHeader).toString();
|
||||
try {
|
||||
this.header = JSON.parse(header);
|
||||
} catch (er) {
|
||||
this.emit('error', new Error('invalid simple protocol data'));
|
||||
return;
|
||||
}
|
||||
// and let them know that we are done parsing the header.
|
||||
this.emit('header', this.header);
|
||||
|
||||
// now, because we got some extra data, emit this first.
|
||||
this.push(b);
|
||||
}
|
||||
} else {
|
||||
// from there on, just provide the data to our consumer as-is.
|
||||
this.push(b);
|
||||
}
|
||||
done();
|
||||
};
|
||||
|
||||
var parser = new SimpleProtocol();
|
||||
source.pipe(parser)
|
||||
|
||||
// Now parser is a readable stream that will emit 'header'
|
||||
// with the parsed header data.
|
||||
```
|
||||
|
||||
|
||||
## Class: stream.PassThrough
|
||||
|
||||
This is a trivial implementation of a `Transform` stream that simply
|
||||
passes the input bytes across to the output. Its purpose is mainly
|
||||
for examples and testing, but there are occasionally use cases where
|
||||
it can come in handy.
|
||||
|
||||
|
||||
[EventEmitter]: events.html#events_class_events_eventemitter
|
||||
gerado
externo
+1
@@ -0,0 +1 @@
|
||||
module.exports = require("./lib/_stream_duplex.js")
|
||||
gerado
externo
+32
@@ -0,0 +1,32 @@
|
||||
var Transform = require('../transform');
|
||||
var inherits = require('util').inherits;
|
||||
|
||||
// subclass
|
||||
function MyStream () {
|
||||
Transform.call(this, {
|
||||
lowWaterMark: 0,
|
||||
encoding: 'utf8'
|
||||
});
|
||||
}
|
||||
inherits(MyStream, Transform);
|
||||
|
||||
MyStream.prototype._transform = function (chunk, outputFn, callback) {
|
||||
outputFn(new Buffer(String(chunk).toUpperCase()));
|
||||
callback();
|
||||
};
|
||||
|
||||
// use it!
|
||||
var s = new MyStream();
|
||||
process.stdin.resume();
|
||||
process.stdin.pipe(s).pipe(process.stdout);
|
||||
if (process.stdin.setRawMode)
|
||||
process.stdin.setRawMode(true);
|
||||
process.stdin.on('data', function (c) {
|
||||
c = c.toString();
|
||||
if (c === '\u0003' || c === '\u0004') {
|
||||
process.stdin.pause();
|
||||
s.end();
|
||||
}
|
||||
if (c === '\r')
|
||||
process.stdout.write('\n');
|
||||
});
|
||||
gerado
externo
+15
@@ -0,0 +1,15 @@
|
||||
var fs = require('fs');
|
||||
var FSReadable = require('../fs.js');
|
||||
var rst = new FSReadable(__filename);
|
||||
|
||||
rst.on('end', function() {
|
||||
process.stdin.pause();
|
||||
});
|
||||
|
||||
process.stdin.setRawMode(true);
|
||||
process.stdin.on('data', function() {
|
||||
var c = rst.read(3);
|
||||
if (!c) return;
|
||||
process.stdout.write(c);
|
||||
});
|
||||
process.stdin.resume();
|
||||
gerado
externo
+17
@@ -0,0 +1,17 @@
|
||||
var fs = require('fs');
|
||||
var fst = fs.createReadStream(__filename);
|
||||
var Readable = require('../readable.js');
|
||||
var rst = new Readable();
|
||||
rst.wrap(fst);
|
||||
|
||||
rst.on('end', function() {
|
||||
process.stdin.pause();
|
||||
});
|
||||
|
||||
process.stdin.setRawMode(true);
|
||||
process.stdin.on('data', function() {
|
||||
var c = rst.read(3);
|
||||
if (!c) return setTimeout(process.exit, 500)
|
||||
process.stdout.write(c);
|
||||
});
|
||||
process.stdin.resume();
|
||||
gerado
externo
+68
@@ -0,0 +1,68 @@
|
||||
diff --git a/lib/_stream_duplex.js b/lib/_stream_duplex.js
|
||||
index c5a741c..a2e0d8e 100644
|
||||
--- a/lib/_stream_duplex.js
|
||||
+++ b/lib/_stream_duplex.js
|
||||
@@ -26,8 +26,8 @@
|
||||
|
||||
module.exports = Duplex;
|
||||
var util = require('util');
|
||||
-var Readable = require('_stream_readable');
|
||||
-var Writable = require('_stream_writable');
|
||||
+var Readable = require('./_stream_readable');
|
||||
+var Writable = require('./_stream_writable');
|
||||
|
||||
util.inherits(Duplex, Readable);
|
||||
|
||||
diff --git a/lib/_stream_passthrough.js b/lib/_stream_passthrough.js
|
||||
index a5e9864..330c247 100644
|
||||
--- a/lib/_stream_passthrough.js
|
||||
+++ b/lib/_stream_passthrough.js
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
module.exports = PassThrough;
|
||||
|
||||
-var Transform = require('_stream_transform');
|
||||
+var Transform = require('./_stream_transform');
|
||||
var util = require('util');
|
||||
util.inherits(PassThrough, Transform);
|
||||
|
||||
diff --git a/lib/_stream_readable.js b/lib/_stream_readable.js
|
||||
index 2259d2e..e6681ee 100644
|
||||
--- a/lib/_stream_readable.js
|
||||
+++ b/lib/_stream_readable.js
|
||||
@@ -23,6 +23,9 @@ module.exports = Readable;
|
||||
Readable.ReadableState = ReadableState;
|
||||
|
||||
var EE = require('events').EventEmitter;
|
||||
+if (!EE.listenerCount) EE.listenerCount = function(emitter, type) {
|
||||
+ return emitter.listeners(type).length;
|
||||
+};
|
||||
var Stream = require('stream');
|
||||
var util = require('util');
|
||||
var StringDecoder;
|
||||
diff --git a/lib/_stream_transform.js b/lib/_stream_transform.js
|
||||
index e925b4b..f08b05e 100644
|
||||
--- a/lib/_stream_transform.js
|
||||
+++ b/lib/_stream_transform.js
|
||||
@@ -64,7 +64,7 @@
|
||||
|
||||
module.exports = Transform;
|
||||
|
||||
-var Duplex = require('_stream_duplex');
|
||||
+var Duplex = require('./_stream_duplex');
|
||||
var util = require('util');
|
||||
util.inherits(Transform, Duplex);
|
||||
|
||||
diff --git a/lib/_stream_writable.js b/lib/_stream_writable.js
|
||||
index a26f711..56ca47d 100644
|
||||
--- a/lib/_stream_writable.js
|
||||
+++ b/lib/_stream_writable.js
|
||||
@@ -109,7 +109,7 @@ function WritableState(options, stream) {
|
||||
function Writable(options) {
|
||||
// Writable ctor is applied to Duplexes, though they're not
|
||||
// instanceof Writable, they're instanceof Readable.
|
||||
- if (!(this instanceof Writable) && !(this instanceof Stream.Duplex))
|
||||
+ if (!(this instanceof Writable) && !(this instanceof require('./_stream_duplex')))
|
||||
return new Writable(options);
|
||||
|
||||
this._writableState = new WritableState(options, this);
|
||||
node_modules/express/node_modules/connect/node_modules/multiparty/node_modules/readable-stream/fs.js
gerado
externo
+1705
Diferenças do arquivo suprimidas por serem muito extensas
Carregar Diff
gerado
externo
+69
@@ -0,0 +1,69 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// 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 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.
|
||||
|
||||
// a duplex stream is just a stream that is both readable and writable.
|
||||
// Since JS doesn't have multiple prototypal inheritance, this class
|
||||
// prototypally inherits from Readable, and then parasitically from
|
||||
// Writable.
|
||||
|
||||
module.exports = Duplex;
|
||||
var util = require('util');
|
||||
var Readable = require('./_stream_readable');
|
||||
var Writable = require('./_stream_writable');
|
||||
|
||||
util.inherits(Duplex, Readable);
|
||||
|
||||
Object.keys(Writable.prototype).forEach(function(method) {
|
||||
if (!Duplex.prototype[method])
|
||||
Duplex.prototype[method] = Writable.prototype[method];
|
||||
});
|
||||
|
||||
function Duplex(options) {
|
||||
if (!(this instanceof Duplex))
|
||||
return new Duplex(options);
|
||||
|
||||
Readable.call(this, options);
|
||||
Writable.call(this, options);
|
||||
|
||||
if (options && options.readable === false)
|
||||
this.readable = false;
|
||||
|
||||
if (options && options.writable === false)
|
||||
this.writable = false;
|
||||
|
||||
this.allowHalfOpen = true;
|
||||
if (options && options.allowHalfOpen === false)
|
||||
this.allowHalfOpen = false;
|
||||
|
||||
this.once('end', onend);
|
||||
}
|
||||
|
||||
// the no-half-open enforcer
|
||||
function onend() {
|
||||
// if we allow half-open state, or if the writable side ended,
|
||||
// then we're ok.
|
||||
if (this.allowHalfOpen || this._writableState.ended)
|
||||
return;
|
||||
|
||||
// no more data can be written.
|
||||
// But allow more writes to happen in this tick.
|
||||
process.nextTick(this.end.bind(this));
|
||||
}
|
||||
gerado
externo
+41
@@ -0,0 +1,41 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// 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 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.
|
||||
|
||||
// a passthrough stream.
|
||||
// basically just the most minimal sort of Transform stream.
|
||||
// Every written chunk gets output as-is.
|
||||
|
||||
module.exports = PassThrough;
|
||||
|
||||
var Transform = require('./_stream_transform');
|
||||
var util = require('util');
|
||||
util.inherits(PassThrough, Transform);
|
||||
|
||||
function PassThrough(options) {
|
||||
if (!(this instanceof PassThrough))
|
||||
return new PassThrough(options);
|
||||
|
||||
Transform.call(this, options);
|
||||
}
|
||||
|
||||
PassThrough.prototype._transform = function(chunk, encoding, cb) {
|
||||
cb(null, chunk);
|
||||
};
|
||||
gerado
externo
+927
@@ -0,0 +1,927 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// 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 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.
|
||||
|
||||
module.exports = Readable;
|
||||
Readable.ReadableState = ReadableState;
|
||||
|
||||
var EE = require('events').EventEmitter;
|
||||
if (!EE.listenerCount) EE.listenerCount = function(emitter, type) {
|
||||
return emitter.listeners(type).length;
|
||||
};
|
||||
var Stream = require('stream');
|
||||
var util = require('util');
|
||||
var StringDecoder;
|
||||
|
||||
util.inherits(Readable, Stream);
|
||||
|
||||
function ReadableState(options, stream) {
|
||||
options = options || {};
|
||||
|
||||
// the point at which it stops calling _read() to fill the buffer
|
||||
// Note: 0 is a valid value, means "don't call _read preemptively ever"
|
||||
var hwm = options.highWaterMark;
|
||||
this.highWaterMark = (hwm || hwm === 0) ? hwm : 16 * 1024;
|
||||
|
||||
// cast to ints.
|
||||
this.highWaterMark = ~~this.highWaterMark;
|
||||
|
||||
this.buffer = [];
|
||||
this.length = 0;
|
||||
this.pipes = null;
|
||||
this.pipesCount = 0;
|
||||
this.flowing = false;
|
||||
this.ended = false;
|
||||
this.endEmitted = false;
|
||||
this.reading = false;
|
||||
|
||||
// In streams that never have any data, and do push(null) right away,
|
||||
// the consumer can miss the 'end' event if they do some I/O before
|
||||
// consuming the stream. So, we don't emit('end') until some reading
|
||||
// happens.
|
||||
this.calledRead = false;
|
||||
|
||||
// a flag to be able to tell if the onwrite cb is called immediately,
|
||||
// or on a later tick. We set this to true at first, becuase any
|
||||
// actions that shouldn't happen until "later" should generally also
|
||||
// not happen before the first write call.
|
||||
this.sync = true;
|
||||
|
||||
// whenever we return null, then we set a flag to say
|
||||
// that we're awaiting a 'readable' event emission.
|
||||
this.needReadable = false;
|
||||
this.emittedReadable = false;
|
||||
this.readableListening = false;
|
||||
|
||||
|
||||
// object stream flag. Used to make read(n) ignore n and to
|
||||
// make all the buffer merging and length checks go away
|
||||
this.objectMode = !!options.objectMode;
|
||||
|
||||
// Crypto is kind of old and crusty. Historically, its default string
|
||||
// encoding is 'binary' so we have to make this configurable.
|
||||
// Everything else in the universe uses 'utf8', though.
|
||||
this.defaultEncoding = options.defaultEncoding || 'utf8';
|
||||
|
||||
// when piping, we only care about 'readable' events that happen
|
||||
// after read()ing all the bytes and not getting any pushback.
|
||||
this.ranOut = false;
|
||||
|
||||
// the number of writers that are awaiting a drain event in .pipe()s
|
||||
this.awaitDrain = 0;
|
||||
|
||||
// if true, a maybeReadMore has been scheduled
|
||||
this.readingMore = false;
|
||||
|
||||
this.decoder = null;
|
||||
this.encoding = null;
|
||||
if (options.encoding) {
|
||||
if (!StringDecoder)
|
||||
StringDecoder = require('string_decoder').StringDecoder;
|
||||
this.decoder = new StringDecoder(options.encoding);
|
||||
this.encoding = options.encoding;
|
||||
}
|
||||
}
|
||||
|
||||
function Readable(options) {
|
||||
if (!(this instanceof Readable))
|
||||
return new Readable(options);
|
||||
|
||||
this._readableState = new ReadableState(options, this);
|
||||
|
||||
// legacy
|
||||
this.readable = true;
|
||||
|
||||
Stream.call(this);
|
||||
}
|
||||
|
||||
// Manually shove something into the read() buffer.
|
||||
// This returns true if the highWaterMark has not been hit yet,
|
||||
// similar to how Writable.write() returns true if you should
|
||||
// write() some more.
|
||||
Readable.prototype.push = function(chunk, encoding) {
|
||||
var state = this._readableState;
|
||||
|
||||
if (typeof chunk === 'string' && !state.objectMode) {
|
||||
encoding = encoding || state.defaultEncoding;
|
||||
if (encoding !== state.encoding) {
|
||||
chunk = new Buffer(chunk, encoding);
|
||||
encoding = '';
|
||||
}
|
||||
}
|
||||
|
||||
return readableAddChunk(this, state, chunk, encoding, false);
|
||||
};
|
||||
|
||||
// Unshift should *always* be something directly out of read()
|
||||
Readable.prototype.unshift = function(chunk) {
|
||||
var state = this._readableState;
|
||||
return readableAddChunk(this, state, chunk, '', true);
|
||||
};
|
||||
|
||||
function readableAddChunk(stream, state, chunk, encoding, addToFront) {
|
||||
var er = chunkInvalid(state, chunk);
|
||||
if (er) {
|
||||
stream.emit('error', er);
|
||||
} else if (chunk === null || chunk === undefined) {
|
||||
state.reading = false;
|
||||
if (!state.ended)
|
||||
onEofChunk(stream, state);
|
||||
} else if (state.objectMode || chunk && chunk.length > 0) {
|
||||
if (state.ended && !addToFront) {
|
||||
var e = new Error('stream.push() after EOF');
|
||||
stream.emit('error', e);
|
||||
} else if (state.endEmitted && addToFront) {
|
||||
var e = new Error('stream.unshift() after end event');
|
||||
stream.emit('error', e);
|
||||
} else {
|
||||
if (state.decoder && !addToFront && !encoding)
|
||||
chunk = state.decoder.write(chunk);
|
||||
|
||||
// update the buffer info.
|
||||
state.length += state.objectMode ? 1 : chunk.length;
|
||||
if (addToFront) {
|
||||
state.buffer.unshift(chunk);
|
||||
} else {
|
||||
state.reading = false;
|
||||
state.buffer.push(chunk);
|
||||
}
|
||||
|
||||
if (state.needReadable)
|
||||
emitReadable(stream);
|
||||
|
||||
maybeReadMore(stream, state);
|
||||
}
|
||||
} else if (!addToFront) {
|
||||
state.reading = false;
|
||||
}
|
||||
|
||||
return needMoreData(state);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// if it's past the high water mark, we can push in some more.
|
||||
// Also, if we have no data yet, we can stand some
|
||||
// more bytes. This is to work around cases where hwm=0,
|
||||
// such as the repl. Also, if the push() triggered a
|
||||
// readable event, and the user called read(largeNumber) such that
|
||||
// needReadable was set, then we ought to push more, so that another
|
||||
// 'readable' event will be triggered.
|
||||
function needMoreData(state) {
|
||||
return !state.ended &&
|
||||
(state.needReadable ||
|
||||
state.length < state.highWaterMark ||
|
||||
state.length === 0);
|
||||
}
|
||||
|
||||
// backwards compatibility.
|
||||
Readable.prototype.setEncoding = function(enc) {
|
||||
if (!StringDecoder)
|
||||
StringDecoder = require('string_decoder').StringDecoder;
|
||||
this._readableState.decoder = new StringDecoder(enc);
|
||||
this._readableState.encoding = enc;
|
||||
};
|
||||
|
||||
// Don't raise the hwm > 128MB
|
||||
var MAX_HWM = 0x800000;
|
||||
function roundUpToNextPowerOf2(n) {
|
||||
if (n >= MAX_HWM) {
|
||||
n = MAX_HWM;
|
||||
} else {
|
||||
// Get the next highest power of 2
|
||||
n--;
|
||||
for (var p = 1; p < 32; p <<= 1) n |= n >> p;
|
||||
n++;
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
||||
function howMuchToRead(n, state) {
|
||||
if (state.length === 0 && state.ended)
|
||||
return 0;
|
||||
|
||||
if (state.objectMode)
|
||||
return n === 0 ? 0 : 1;
|
||||
|
||||
if (isNaN(n) || n === null) {
|
||||
// only flow one buffer at a time
|
||||
if (state.flowing && state.buffer.length)
|
||||
return state.buffer[0].length;
|
||||
else
|
||||
return state.length;
|
||||
}
|
||||
|
||||
if (n <= 0)
|
||||
return 0;
|
||||
|
||||
// If we're asking for more than the target buffer level,
|
||||
// then raise the water mark. Bump up to the next highest
|
||||
// power of 2, to prevent increasing it excessively in tiny
|
||||
// amounts.
|
||||
if (n > state.highWaterMark)
|
||||
state.highWaterMark = roundUpToNextPowerOf2(n);
|
||||
|
||||
// don't have that much. return null, unless we've ended.
|
||||
if (n > state.length) {
|
||||
if (!state.ended) {
|
||||
state.needReadable = true;
|
||||
return 0;
|
||||
} else
|
||||
return state.length;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
// you can override either this method, or the async _read(n) below.
|
||||
Readable.prototype.read = function(n) {
|
||||
var state = this._readableState;
|
||||
state.calledRead = true;
|
||||
var nOrig = n;
|
||||
|
||||
if (typeof n !== 'number' || n > 0)
|
||||
state.emittedReadable = false;
|
||||
|
||||
// if we're doing read(0) to trigger a readable event, but we
|
||||
// already have a bunch of data in the buffer, then just trigger
|
||||
// the 'readable' event and move on.
|
||||
if (n === 0 &&
|
||||
state.needReadable &&
|
||||
(state.length >= state.highWaterMark || state.ended)) {
|
||||
emitReadable(this);
|
||||
return null;
|
||||
}
|
||||
|
||||
n = howMuchToRead(n, state);
|
||||
|
||||
// if we've ended, and we're now clear, then finish it up.
|
||||
if (n === 0 && state.ended) {
|
||||
if (state.length === 0)
|
||||
endReadable(this);
|
||||
return null;
|
||||
}
|
||||
|
||||
// All the actual chunk generation logic needs to be
|
||||
// *below* the call to _read. The reason is that in certain
|
||||
// synthetic stream cases, such as passthrough streams, _read
|
||||
// may be a completely synchronous operation which may change
|
||||
// the state of the read buffer, providing enough data when
|
||||
// before there was *not* enough.
|
||||
//
|
||||
// So, the steps are:
|
||||
// 1. Figure out what the state of things will be after we do
|
||||
// a read from the buffer.
|
||||
//
|
||||
// 2. If that resulting state will trigger a _read, then call _read.
|
||||
// Note that this may be asynchronous, or synchronous. Yes, it is
|
||||
// deeply ugly to write APIs this way, but that still doesn't mean
|
||||
// that the Readable class should behave improperly, as streams are
|
||||
// designed to be sync/async agnostic.
|
||||
// Take note if the _read call is sync or async (ie, if the read call
|
||||
// has returned yet), so that we know whether or not it's safe to emit
|
||||
// 'readable' etc.
|
||||
//
|
||||
// 3. Actually pull the requested chunks out of the buffer and return.
|
||||
|
||||
// if we need a readable event, then we need to do some reading.
|
||||
var doRead = state.needReadable;
|
||||
|
||||
// if we currently have less than the highWaterMark, then also read some
|
||||
if (state.length - n <= state.highWaterMark)
|
||||
doRead = true;
|
||||
|
||||
// however, if we've ended, then there's no point, and if we're already
|
||||
// reading, then it's unnecessary.
|
||||
if (state.ended || state.reading)
|
||||
doRead = false;
|
||||
|
||||
if (doRead) {
|
||||
state.reading = true;
|
||||
state.sync = true;
|
||||
// if the length is currently zero, then we *need* a readable event.
|
||||
if (state.length === 0)
|
||||
state.needReadable = true;
|
||||
// call internal read method
|
||||
this._read(state.highWaterMark);
|
||||
state.sync = false;
|
||||
}
|
||||
|
||||
// If _read called its callback synchronously, then `reading`
|
||||
// will be false, and we need to re-evaluate how much data we
|
||||
// can return to the user.
|
||||
if (doRead && !state.reading)
|
||||
n = howMuchToRead(nOrig, state);
|
||||
|
||||
var ret;
|
||||
if (n > 0)
|
||||
ret = fromList(n, state);
|
||||
else
|
||||
ret = null;
|
||||
|
||||
if (ret === null) {
|
||||
state.needReadable = true;
|
||||
n = 0;
|
||||
}
|
||||
|
||||
state.length -= n;
|
||||
|
||||
// If we have nothing in the buffer, then we want to know
|
||||
// as soon as we *do* get something into the buffer.
|
||||
if (state.length === 0 && !state.ended)
|
||||
state.needReadable = true;
|
||||
|
||||
// If we happened to read() exactly the remaining amount in the
|
||||
// buffer, and the EOF has been seen at this point, then make sure
|
||||
// that we emit 'end' on the very next tick.
|
||||
if (state.ended && !state.endEmitted && state.length === 0)
|
||||
endReadable(this);
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
function chunkInvalid(state, chunk) {
|
||||
var er = null;
|
||||
if (!Buffer.isBuffer(chunk) &&
|
||||
'string' !== typeof chunk &&
|
||||
chunk !== null &&
|
||||
chunk !== undefined &&
|
||||
!state.objectMode &&
|
||||
!er) {
|
||||
er = new TypeError('Invalid non-string/buffer chunk');
|
||||
}
|
||||
return er;
|
||||
}
|
||||
|
||||
|
||||
function onEofChunk(stream, state) {
|
||||
if (state.decoder && !state.ended) {
|
||||
var chunk = state.decoder.end();
|
||||
if (chunk && chunk.length) {
|
||||
state.buffer.push(chunk);
|
||||
state.length += state.objectMode ? 1 : chunk.length;
|
||||
}
|
||||
}
|
||||
state.ended = true;
|
||||
|
||||
// if we've ended and we have some data left, then emit
|
||||
// 'readable' now to make sure it gets picked up.
|
||||
if (state.length > 0)
|
||||
emitReadable(stream);
|
||||
else
|
||||
endReadable(stream);
|
||||
}
|
||||
|
||||
// Don't emit readable right away in sync mode, because this can trigger
|
||||
// another read() call => stack overflow. This way, it might trigger
|
||||
// a nextTick recursion warning, but that's not so bad.
|
||||
function emitReadable(stream) {
|
||||
var state = stream._readableState;
|
||||
state.needReadable = false;
|
||||
if (state.emittedReadable)
|
||||
return;
|
||||
|
||||
state.emittedReadable = true;
|
||||
if (state.sync)
|
||||
process.nextTick(function() {
|
||||
emitReadable_(stream);
|
||||
});
|
||||
else
|
||||
emitReadable_(stream);
|
||||
}
|
||||
|
||||
function emitReadable_(stream) {
|
||||
stream.emit('readable');
|
||||
}
|
||||
|
||||
|
||||
// at this point, the user has presumably seen the 'readable' event,
|
||||
// and called read() to consume some data. that may have triggered
|
||||
// in turn another _read(n) call, in which case reading = true if
|
||||
// it's in progress.
|
||||
// However, if we're not ended, or reading, and the length < hwm,
|
||||
// then go ahead and try to read some more preemptively.
|
||||
function maybeReadMore(stream, state) {
|
||||
if (!state.readingMore) {
|
||||
state.readingMore = true;
|
||||
process.nextTick(function() {
|
||||
maybeReadMore_(stream, state);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function maybeReadMore_(stream, state) {
|
||||
var len = state.length;
|
||||
while (!state.reading && !state.flowing && !state.ended &&
|
||||
state.length < state.highWaterMark) {
|
||||
stream.read(0);
|
||||
if (len === state.length)
|
||||
// didn't get any data, stop spinning.
|
||||
break;
|
||||
else
|
||||
len = state.length;
|
||||
}
|
||||
state.readingMore = false;
|
||||
}
|
||||
|
||||
// abstract method. to be overridden in specific implementation classes.
|
||||
// call cb(er, data) where data is <= n in length.
|
||||
// for virtual (non-string, non-buffer) streams, "length" is somewhat
|
||||
// arbitrary, and perhaps not very meaningful.
|
||||
Readable.prototype._read = function(n) {
|
||||
this.emit('error', new Error('not implemented'));
|
||||
};
|
||||
|
||||
Readable.prototype.pipe = function(dest, pipeOpts) {
|
||||
var src = this;
|
||||
var state = this._readableState;
|
||||
|
||||
switch (state.pipesCount) {
|
||||
case 0:
|
||||
state.pipes = dest;
|
||||
break;
|
||||
case 1:
|
||||
state.pipes = [state.pipes, dest];
|
||||
break;
|
||||
default:
|
||||
state.pipes.push(dest);
|
||||
break;
|
||||
}
|
||||
state.pipesCount += 1;
|
||||
|
||||
var doEnd = (!pipeOpts || pipeOpts.end !== false) &&
|
||||
dest !== process.stdout &&
|
||||
dest !== process.stderr;
|
||||
|
||||
var endFn = doEnd ? onend : cleanup;
|
||||
if (state.endEmitted)
|
||||
process.nextTick(endFn);
|
||||
else
|
||||
src.once('end', endFn);
|
||||
|
||||
dest.on('unpipe', onunpipe);
|
||||
function onunpipe(readable) {
|
||||
if (readable !== src) return;
|
||||
cleanup();
|
||||
}
|
||||
|
||||
function onend() {
|
||||
dest.end();
|
||||
}
|
||||
|
||||
// when the dest drains, it reduces the awaitDrain counter
|
||||
// on the source. This would be more elegant with a .once()
|
||||
// handler in flow(), but adding and removing repeatedly is
|
||||
// too slow.
|
||||
var ondrain = pipeOnDrain(src);
|
||||
dest.on('drain', ondrain);
|
||||
|
||||
function cleanup() {
|
||||
// cleanup event handlers once the pipe is broken
|
||||
dest.removeListener('close', onclose);
|
||||
dest.removeListener('finish', onfinish);
|
||||
dest.removeListener('drain', ondrain);
|
||||
dest.removeListener('error', onerror);
|
||||
dest.removeListener('unpipe', onunpipe);
|
||||
src.removeListener('end', onend);
|
||||
src.removeListener('end', cleanup);
|
||||
|
||||
// if the reader is waiting for a drain event from this
|
||||
// specific writer, then it would cause it to never start
|
||||
// flowing again.
|
||||
// So, if this is awaiting a drain, then we just call it now.
|
||||
// If we don't know, then assume that we are waiting for one.
|
||||
if (!dest._writableState || dest._writableState.needDrain)
|
||||
ondrain();
|
||||
}
|
||||
|
||||
// if the dest has an error, then stop piping into it.
|
||||
// however, don't suppress the throwing behavior for this.
|
||||
function onerror(er) {
|
||||
unpipe();
|
||||
dest.removeListener('error', onerror);
|
||||
if (EE.listenerCount(dest, 'error') === 0)
|
||||
dest.emit('error', er);
|
||||
}
|
||||
// This is a brutally ugly hack to make sure that our error handler
|
||||
// is attached before any userland ones. NEVER DO THIS.
|
||||
if (!dest._events.error)
|
||||
dest.on('error', onerror);
|
||||
else if (Array.isArray(dest._events.error))
|
||||
dest._events.error.unshift(onerror);
|
||||
else
|
||||
dest._events.error = [onerror, dest._events.error];
|
||||
|
||||
|
||||
|
||||
// Both close and finish should trigger unpipe, but only once.
|
||||
function onclose() {
|
||||
dest.removeListener('finish', onfinish);
|
||||
unpipe();
|
||||
}
|
||||
dest.once('close', onclose);
|
||||
function onfinish() {
|
||||
dest.removeListener('close', onclose);
|
||||
unpipe();
|
||||
}
|
||||
dest.once('finish', onfinish);
|
||||
|
||||
function unpipe() {
|
||||
src.unpipe(dest);
|
||||
}
|
||||
|
||||
// tell the dest that it's being piped to
|
||||
dest.emit('pipe', src);
|
||||
|
||||
// start the flow if it hasn't been started already.
|
||||
if (!state.flowing) {
|
||||
// the handler that waits for readable events after all
|
||||
// the data gets sucked out in flow.
|
||||
// This would be easier to follow with a .once() handler
|
||||
// in flow(), but that is too slow.
|
||||
this.on('readable', pipeOnReadable);
|
||||
|
||||
state.flowing = true;
|
||||
process.nextTick(function() {
|
||||
flow(src);
|
||||
});
|
||||
}
|
||||
|
||||
return dest;
|
||||
};
|
||||
|
||||
function pipeOnDrain(src) {
|
||||
return function() {
|
||||
var dest = this;
|
||||
var state = src._readableState;
|
||||
state.awaitDrain--;
|
||||
if (state.awaitDrain === 0)
|
||||
flow(src);
|
||||
};
|
||||
}
|
||||
|
||||
function flow(src) {
|
||||
var state = src._readableState;
|
||||
var chunk;
|
||||
state.awaitDrain = 0;
|
||||
|
||||
function write(dest, i, list) {
|
||||
var written = dest.write(chunk);
|
||||
if (false === written) {
|
||||
state.awaitDrain++;
|
||||
}
|
||||
}
|
||||
|
||||
while (state.pipesCount && null !== (chunk = src.read())) {
|
||||
|
||||
if (state.pipesCount === 1)
|
||||
write(state.pipes, 0, null);
|
||||
else
|
||||
state.pipes.forEach(write);
|
||||
|
||||
src.emit('data', chunk);
|
||||
|
||||
// if anyone needs a drain, then we have to wait for that.
|
||||
if (state.awaitDrain > 0)
|
||||
return;
|
||||
}
|
||||
|
||||
// if every destination was unpiped, either before entering this
|
||||
// function, or in the while loop, then stop flowing.
|
||||
//
|
||||
// NB: This is a pretty rare edge case.
|
||||
if (state.pipesCount === 0) {
|
||||
state.flowing = false;
|
||||
|
||||
// if there were data event listeners added, then switch to old mode.
|
||||
if (EE.listenerCount(src, 'data') > 0)
|
||||
emitDataEvents(src);
|
||||
return;
|
||||
}
|
||||
|
||||
// at this point, no one needed a drain, so we just ran out of data
|
||||
// on the next readable event, start it over again.
|
||||
state.ranOut = true;
|
||||
}
|
||||
|
||||
function pipeOnReadable() {
|
||||
if (this._readableState.ranOut) {
|
||||
this._readableState.ranOut = false;
|
||||
flow(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Readable.prototype.unpipe = function(dest) {
|
||||
var state = this._readableState;
|
||||
|
||||
// if we're not piping anywhere, then do nothing.
|
||||
if (state.pipesCount === 0)
|
||||
return this;
|
||||
|
||||
// just one destination. most common case.
|
||||
if (state.pipesCount === 1) {
|
||||
// passed in one, but it's not the right one.
|
||||
if (dest && dest !== state.pipes)
|
||||
return this;
|
||||
|
||||
if (!dest)
|
||||
dest = state.pipes;
|
||||
|
||||
// got a match.
|
||||
state.pipes = null;
|
||||
state.pipesCount = 0;
|
||||
this.removeListener('readable', pipeOnReadable);
|
||||
state.flowing = false;
|
||||
if (dest)
|
||||
dest.emit('unpipe', this);
|
||||
return this;
|
||||
}
|
||||
|
||||
// slow case. multiple pipe destinations.
|
||||
|
||||
if (!dest) {
|
||||
// remove all.
|
||||
var dests = state.pipes;
|
||||
var len = state.pipesCount;
|
||||
state.pipes = null;
|
||||
state.pipesCount = 0;
|
||||
this.removeListener('readable', pipeOnReadable);
|
||||
state.flowing = false;
|
||||
|
||||
for (var i = 0; i < len; i++)
|
||||
dests[i].emit('unpipe', this);
|
||||
return this;
|
||||
}
|
||||
|
||||
// try to find the right one.
|
||||
var i = state.pipes.indexOf(dest);
|
||||
if (i === -1)
|
||||
return this;
|
||||
|
||||
state.pipes.splice(i, 1);
|
||||
state.pipesCount -= 1;
|
||||
if (state.pipesCount === 1)
|
||||
state.pipes = state.pipes[0];
|
||||
|
||||
dest.emit('unpipe', this);
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
// set up data events if they are asked for
|
||||
// Ensure readable listeners eventually get something
|
||||
Readable.prototype.on = function(ev, fn) {
|
||||
var res = Stream.prototype.on.call(this, ev, fn);
|
||||
|
||||
if (ev === 'data' && !this._readableState.flowing)
|
||||
emitDataEvents(this);
|
||||
|
||||
if (ev === 'readable' && this.readable) {
|
||||
var state = this._readableState;
|
||||
if (!state.readableListening) {
|
||||
state.readableListening = true;
|
||||
state.emittedReadable = false;
|
||||
state.needReadable = true;
|
||||
if (!state.reading) {
|
||||
this.read(0);
|
||||
} else if (state.length) {
|
||||
emitReadable(this, state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
};
|
||||
Readable.prototype.addListener = Readable.prototype.on;
|
||||
|
||||
// pause() and resume() are remnants of the legacy readable stream API
|
||||
// If the user uses them, then switch into old mode.
|
||||
Readable.prototype.resume = function() {
|
||||
emitDataEvents(this);
|
||||
this.read(0);
|
||||
this.emit('resume');
|
||||
};
|
||||
|
||||
Readable.prototype.pause = function() {
|
||||
emitDataEvents(this, true);
|
||||
this.emit('pause');
|
||||
};
|
||||
|
||||
function emitDataEvents(stream, startPaused) {
|
||||
var state = stream._readableState;
|
||||
|
||||
if (state.flowing) {
|
||||
// https://github.com/isaacs/readable-stream/issues/16
|
||||
throw new Error('Cannot switch to old mode now.');
|
||||
}
|
||||
|
||||
var paused = startPaused || false;
|
||||
var readable = false;
|
||||
|
||||
// convert to an old-style stream.
|
||||
stream.readable = true;
|
||||
stream.pipe = Stream.prototype.pipe;
|
||||
stream.on = stream.addListener = Stream.prototype.on;
|
||||
|
||||
stream.on('readable', function() {
|
||||
readable = true;
|
||||
|
||||
var c;
|
||||
while (!paused && (null !== (c = stream.read())))
|
||||
stream.emit('data', c);
|
||||
|
||||
if (c === null) {
|
||||
readable = false;
|
||||
stream._readableState.needReadable = true;
|
||||
}
|
||||
});
|
||||
|
||||
stream.pause = function() {
|
||||
paused = true;
|
||||
this.emit('pause');
|
||||
};
|
||||
|
||||
stream.resume = function() {
|
||||
paused = false;
|
||||
if (readable)
|
||||
process.nextTick(function() {
|
||||
stream.emit('readable');
|
||||
});
|
||||
else
|
||||
this.read(0);
|
||||
this.emit('resume');
|
||||
};
|
||||
|
||||
// now make it start, just in case it hadn't already.
|
||||
stream.emit('readable');
|
||||
}
|
||||
|
||||
// wrap an old-style stream as the async data source.
|
||||
// This is *not* part of the readable stream interface.
|
||||
// It is an ugly unfortunate mess of history.
|
||||
Readable.prototype.wrap = function(stream) {
|
||||
var state = this._readableState;
|
||||
var paused = false;
|
||||
|
||||
var self = this;
|
||||
stream.on('end', function() {
|
||||
if (state.decoder && !state.ended) {
|
||||
var chunk = state.decoder.end();
|
||||
if (chunk && chunk.length)
|
||||
self.push(chunk);
|
||||
}
|
||||
|
||||
self.push(null);
|
||||
});
|
||||
|
||||
stream.on('data', function(chunk) {
|
||||
if (state.decoder)
|
||||
chunk = state.decoder.write(chunk);
|
||||
if (!chunk || !state.objectMode && !chunk.length)
|
||||
return;
|
||||
|
||||
var ret = self.push(chunk);
|
||||
if (!ret) {
|
||||
paused = true;
|
||||
stream.pause();
|
||||
}
|
||||
});
|
||||
|
||||
// proxy all the other methods.
|
||||
// important when wrapping filters and duplexes.
|
||||
for (var i in stream) {
|
||||
if (typeof stream[i] === 'function' &&
|
||||
typeof this[i] === 'undefined') {
|
||||
this[i] = function(method) { return function() {
|
||||
return stream[method].apply(stream, arguments);
|
||||
}}(i);
|
||||
}
|
||||
}
|
||||
|
||||
// proxy certain important events.
|
||||
var events = ['error', 'close', 'destroy', 'pause', 'resume'];
|
||||
events.forEach(function(ev) {
|
||||
stream.on(ev, self.emit.bind(self, ev));
|
||||
});
|
||||
|
||||
// when we try to consume some more bytes, simply unpause the
|
||||
// underlying stream.
|
||||
self._read = function(n) {
|
||||
if (paused) {
|
||||
paused = false;
|
||||
stream.resume();
|
||||
}
|
||||
};
|
||||
|
||||
return self;
|
||||
};
|
||||
|
||||
|
||||
|
||||
// exposed for testing purposes only.
|
||||
Readable._fromList = fromList;
|
||||
|
||||
// Pluck off n bytes from an array of buffers.
|
||||
// Length is the combined lengths of all the buffers in the list.
|
||||
function fromList(n, state) {
|
||||
var list = state.buffer;
|
||||
var length = state.length;
|
||||
var stringMode = !!state.decoder;
|
||||
var objectMode = !!state.objectMode;
|
||||
var ret;
|
||||
|
||||
// nothing in the list, definitely empty.
|
||||
if (list.length === 0)
|
||||
return null;
|
||||
|
||||
if (length === 0)
|
||||
ret = null;
|
||||
else if (objectMode)
|
||||
ret = list.shift();
|
||||
else if (!n || n >= length) {
|
||||
// read it all, truncate the array.
|
||||
if (stringMode)
|
||||
ret = list.join('');
|
||||
else
|
||||
ret = Buffer.concat(list, length);
|
||||
list.length = 0;
|
||||
} else {
|
||||
// read just some of it.
|
||||
if (n < list[0].length) {
|
||||
// just take a part of the first list item.
|
||||
// slice is the same for buffers and strings.
|
||||
var buf = list[0];
|
||||
ret = buf.slice(0, n);
|
||||
list[0] = buf.slice(n);
|
||||
} else if (n === list[0].length) {
|
||||
// first list is a perfect match
|
||||
ret = list.shift();
|
||||
} else {
|
||||
// complex case.
|
||||
// we have enough to cover it, but it spans past the first buffer.
|
||||
if (stringMode)
|
||||
ret = '';
|
||||
else
|
||||
ret = new Buffer(n);
|
||||
|
||||
var c = 0;
|
||||
for (var i = 0, l = list.length; i < l && c < n; i++) {
|
||||
var buf = list[0];
|
||||
var cpy = Math.min(n - c, buf.length);
|
||||
|
||||
if (stringMode)
|
||||
ret += buf.slice(0, cpy);
|
||||
else
|
||||
buf.copy(ret, c, 0, cpy);
|
||||
|
||||
if (cpy < buf.length)
|
||||
list[0] = buf.slice(cpy);
|
||||
else
|
||||
list.shift();
|
||||
|
||||
c += cpy;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
function endReadable(stream) {
|
||||
var state = stream._readableState;
|
||||
|
||||
// If we get here before consuming all the bytes, then that is a
|
||||
// bug in node. Should never happen.
|
||||
if (state.length > 0)
|
||||
throw new Error('endReadable called on non-empty stream');
|
||||
|
||||
if (!state.endEmitted && state.calledRead) {
|
||||
state.ended = true;
|
||||
process.nextTick(function() {
|
||||
// Check that we didn't get one last unshift.
|
||||
if (!state.endEmitted && state.length === 0) {
|
||||
state.endEmitted = true;
|
||||
stream.readable = false;
|
||||
stream.emit('end');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
gerado
externo
+205
@@ -0,0 +1,205 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// 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 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.
|
||||
|
||||
|
||||
// a transform stream is a readable/writable stream where you do
|
||||
// something with the data. Sometimes it's called a "filter",
|
||||
// but that's not a great name for it, since that implies a thing where
|
||||
// some bits pass through, and others are simply ignored. (That would
|
||||
// be a valid example of a transform, of course.)
|
||||
//
|
||||
// While the output is causally related to the input, it's not a
|
||||
// necessarily symmetric or synchronous transformation. For example,
|
||||
// a zlib stream might take multiple plain-text writes(), and then
|
||||
// emit a single compressed chunk some time in the future.
|
||||
//
|
||||
// Here's how this works:
|
||||
//
|
||||
// The Transform stream has all the aspects of the readable and writable
|
||||
// stream classes. When you write(chunk), that calls _write(chunk,cb)
|
||||
// internally, and returns false if there's a lot of pending writes
|
||||
// buffered up. When you call read(), that calls _read(n) until
|
||||
// there's enough pending readable data buffered up.
|
||||
//
|
||||
// In a transform stream, the written data is placed in a buffer. When
|
||||
// _read(n) is called, it transforms the queued up data, calling the
|
||||
// buffered _write cb's as it consumes chunks. If consuming a single
|
||||
// written chunk would result in multiple output chunks, then the first
|
||||
// outputted bit calls the readcb, and subsequent chunks just go into
|
||||
// the read buffer, and will cause it to emit 'readable' if necessary.
|
||||
//
|
||||
// This way, back-pressure is actually determined by the reading side,
|
||||
// since _read has to be called to start processing a new chunk. However,
|
||||
// a pathological inflate type of transform can cause excessive buffering
|
||||
// here. For example, imagine a stream where every byte of input is
|
||||
// interpreted as an integer from 0-255, and then results in that many
|
||||
// bytes of output. Writing the 4 bytes {ff,ff,ff,ff} would result in
|
||||
// 1kb of data being output. In this case, you could write a very small
|
||||
// amount of input, and end up with a very large amount of output. In
|
||||
// such a pathological inflating mechanism, there'd be no way to tell
|
||||
// the system to stop doing the transform. A single 4MB write could
|
||||
// cause the system to run out of memory.
|
||||
//
|
||||
// However, even in such a pathological case, only a single written chunk
|
||||
// would be consumed, and then the rest would wait (un-transformed) until
|
||||
// the results of the previous transformed chunk were consumed.
|
||||
|
||||
module.exports = Transform;
|
||||
|
||||
var Duplex = require('./_stream_duplex');
|
||||
var util = require('util');
|
||||
util.inherits(Transform, Duplex);
|
||||
|
||||
|
||||
function TransformState(options, stream) {
|
||||
this.afterTransform = function(er, data) {
|
||||
return afterTransform(stream, er, data);
|
||||
};
|
||||
|
||||
this.needTransform = false;
|
||||
this.transforming = false;
|
||||
this.writecb = null;
|
||||
this.writechunk = null;
|
||||
}
|
||||
|
||||
function afterTransform(stream, er, data) {
|
||||
var ts = stream._transformState;
|
||||
ts.transforming = false;
|
||||
|
||||
var cb = ts.writecb;
|
||||
|
||||
if (!cb)
|
||||
return stream.emit('error', new Error('no writecb in Transform class'));
|
||||
|
||||
ts.writechunk = null;
|
||||
ts.writecb = null;
|
||||
|
||||
if (data !== null && data !== undefined)
|
||||
stream.push(data);
|
||||
|
||||
if (cb)
|
||||
cb(er);
|
||||
|
||||
var rs = stream._readableState;
|
||||
rs.reading = false;
|
||||
if (rs.needReadable || rs.length < rs.highWaterMark) {
|
||||
stream._read(rs.highWaterMark);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function Transform(options) {
|
||||
if (!(this instanceof Transform))
|
||||
return new Transform(options);
|
||||
|
||||
Duplex.call(this, options);
|
||||
|
||||
var ts = this._transformState = new TransformState(options, this);
|
||||
|
||||
// when the writable side finishes, then flush out anything remaining.
|
||||
var stream = this;
|
||||
|
||||
// start out asking for a readable event once data is transformed.
|
||||
this._readableState.needReadable = true;
|
||||
|
||||
// we have implemented the _read method, and done the other things
|
||||
// that Readable wants before the first _read call, so unset the
|
||||
// sync guard flag.
|
||||
this._readableState.sync = false;
|
||||
|
||||
this.once('finish', function() {
|
||||
if ('function' === typeof this._flush)
|
||||
this._flush(function(er) {
|
||||
done(stream, er);
|
||||
});
|
||||
else
|
||||
done(stream);
|
||||
});
|
||||
}
|
||||
|
||||
Transform.prototype.push = function(chunk, encoding) {
|
||||
this._transformState.needTransform = false;
|
||||
return Duplex.prototype.push.call(this, chunk, encoding);
|
||||
};
|
||||
|
||||
// This is the part where you do stuff!
|
||||
// override this function in implementation classes.
|
||||
// 'chunk' is an input chunk.
|
||||
//
|
||||
// Call `push(newChunk)` to pass along transformed output
|
||||
// to the readable side. You may call 'push' zero or more times.
|
||||
//
|
||||
// Call `cb(err)` when you are done with this chunk. If you pass
|
||||
// an error, then that'll put the hurt on the whole operation. If you
|
||||
// never call cb(), then you'll never get another chunk.
|
||||
Transform.prototype._transform = function(chunk, encoding, cb) {
|
||||
throw new Error('not implemented');
|
||||
};
|
||||
|
||||
Transform.prototype._write = function(chunk, encoding, cb) {
|
||||
var ts = this._transformState;
|
||||
ts.writecb = cb;
|
||||
ts.writechunk = chunk;
|
||||
ts.writeencoding = encoding;
|
||||
if (!ts.transforming) {
|
||||
var rs = this._readableState;
|
||||
if (ts.needTransform ||
|
||||
rs.needReadable ||
|
||||
rs.length < rs.highWaterMark)
|
||||
this._read(rs.highWaterMark);
|
||||
}
|
||||
};
|
||||
|
||||
// Doesn't matter what the args are here.
|
||||
// _transform does all the work.
|
||||
// That we got here means that the readable side wants more data.
|
||||
Transform.prototype._read = function(n) {
|
||||
var ts = this._transformState;
|
||||
|
||||
if (ts.writechunk && ts.writecb && !ts.transforming) {
|
||||
ts.transforming = true;
|
||||
this._transform(ts.writechunk, ts.writeencoding, ts.afterTransform);
|
||||
} else {
|
||||
// mark that we need a transform, so that any data that comes in
|
||||
// will get processed, now that we've asked for it.
|
||||
ts.needTransform = true;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
function done(stream, er) {
|
||||
if (er)
|
||||
return stream.emit('error', er);
|
||||
|
||||
// if there's nothing in the write buffer, then that means
|
||||
// that nothing more will ever be provided
|
||||
var ws = stream._writableState;
|
||||
var rs = stream._readableState;
|
||||
var ts = stream._transformState;
|
||||
|
||||
if (ws.length)
|
||||
throw new Error('calling transform done when ws.length != 0');
|
||||
|
||||
if (ts.transforming)
|
||||
throw new Error('calling transform done when still transforming');
|
||||
|
||||
return stream.push(null);
|
||||
}
|
||||
gerado
externo
+367
@@ -0,0 +1,367 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// 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 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.
|
||||
|
||||
// A bit simpler than readable streams.
|
||||
// Implement an async ._write(chunk, cb), and it'll handle all
|
||||
// the drain event emission and buffering.
|
||||
|
||||
module.exports = Writable;
|
||||
Writable.WritableState = WritableState;
|
||||
|
||||
var util = require('util');
|
||||
var assert = require('assert');
|
||||
var Stream = require('stream');
|
||||
|
||||
util.inherits(Writable, Stream);
|
||||
|
||||
function WriteReq(chunk, encoding, cb) {
|
||||
this.chunk = chunk;
|
||||
this.encoding = encoding;
|
||||
this.callback = cb;
|
||||
}
|
||||
|
||||
function WritableState(options, stream) {
|
||||
options = options || {};
|
||||
|
||||
// the point at which write() starts returning false
|
||||
// Note: 0 is a valid value, means that we always return false if
|
||||
// the entire buffer is not flushed immediately on write()
|
||||
var hwm = options.highWaterMark;
|
||||
this.highWaterMark = (hwm || hwm === 0) ? hwm : 16 * 1024;
|
||||
|
||||
// object stream flag to indicate whether or not this stream
|
||||
// contains buffers or objects.
|
||||
this.objectMode = !!options.objectMode;
|
||||
|
||||
// cast to ints.
|
||||
this.highWaterMark = ~~this.highWaterMark;
|
||||
|
||||
this.needDrain = false;
|
||||
// at the start of calling end()
|
||||
this.ending = false;
|
||||
// when end() has been called, and returned
|
||||
this.ended = false;
|
||||
// when 'finish' is emitted
|
||||
this.finished = false;
|
||||
|
||||
// should we decode strings into buffers before passing to _write?
|
||||
// this is here so that some node-core streams can optimize string
|
||||
// handling at a lower level.
|
||||
var noDecode = options.decodeStrings === false;
|
||||
this.decodeStrings = !noDecode;
|
||||
|
||||
// Crypto is kind of old and crusty. Historically, its default string
|
||||
// encoding is 'binary' so we have to make this configurable.
|
||||
// Everything else in the universe uses 'utf8', though.
|
||||
this.defaultEncoding = options.defaultEncoding || 'utf8';
|
||||
|
||||
// not an actual buffer we keep track of, but a measurement
|
||||
// of how much we're waiting to get pushed to some underlying
|
||||
// socket or file.
|
||||
this.length = 0;
|
||||
|
||||
// a flag to see when we're in the middle of a write.
|
||||
this.writing = false;
|
||||
|
||||
// a flag to be able to tell if the onwrite cb is called immediately,
|
||||
// or on a later tick. We set this to true at first, becuase any
|
||||
// actions that shouldn't happen until "later" should generally also
|
||||
// not happen before the first write call.
|
||||
this.sync = true;
|
||||
|
||||
// a flag to know if we're processing previously buffered items, which
|
||||
// may call the _write() callback in the same tick, so that we don't
|
||||
// end up in an overlapped onwrite situation.
|
||||
this.bufferProcessing = false;
|
||||
|
||||
// the callback that's passed to _write(chunk,cb)
|
||||
this.onwrite = function(er) {
|
||||
onwrite(stream, er);
|
||||
};
|
||||
|
||||
// the callback that the user supplies to write(chunk,encoding,cb)
|
||||
this.writecb = null;
|
||||
|
||||
// the amount that is being written when _write is called.
|
||||
this.writelen = 0;
|
||||
|
||||
this.buffer = [];
|
||||
}
|
||||
|
||||
function Writable(options) {
|
||||
// Writable ctor is applied to Duplexes, though they're not
|
||||
// instanceof Writable, they're instanceof Readable.
|
||||
if (!(this instanceof Writable) && !(this instanceof require('./_stream_duplex')))
|
||||
return new Writable(options);
|
||||
|
||||
this._writableState = new WritableState(options, this);
|
||||
|
||||
// legacy.
|
||||
this.writable = true;
|
||||
|
||||
Stream.call(this);
|
||||
}
|
||||
|
||||
// Otherwise people can pipe Writable streams, which is just wrong.
|
||||
Writable.prototype.pipe = function() {
|
||||
this.emit('error', new Error('Cannot pipe. Not readable.'));
|
||||
};
|
||||
|
||||
|
||||
function writeAfterEnd(stream, state, cb) {
|
||||
var er = new Error('write after end');
|
||||
// TODO: defer error events consistently everywhere, not just the cb
|
||||
stream.emit('error', er);
|
||||
process.nextTick(function() {
|
||||
cb(er);
|
||||
});
|
||||
}
|
||||
|
||||
// If we get something that is not a buffer, string, null, or undefined,
|
||||
// and we're not in objectMode, then that's an error.
|
||||
// Otherwise stream chunks are all considered to be of length=1, and the
|
||||
// watermarks determine how many objects to keep in the buffer, rather than
|
||||
// how many bytes or characters.
|
||||
function validChunk(stream, state, chunk, cb) {
|
||||
var valid = true;
|
||||
if (!Buffer.isBuffer(chunk) &&
|
||||
'string' !== typeof chunk &&
|
||||
chunk !== null &&
|
||||
chunk !== undefined &&
|
||||
!state.objectMode) {
|
||||
var er = new TypeError('Invalid non-string/buffer chunk');
|
||||
stream.emit('error', er);
|
||||
process.nextTick(function() {
|
||||
cb(er);
|
||||
});
|
||||
valid = false;
|
||||
}
|
||||
return valid;
|
||||
}
|
||||
|
||||
Writable.prototype.write = function(chunk, encoding, cb) {
|
||||
var state = this._writableState;
|
||||
var ret = false;
|
||||
|
||||
if (typeof encoding === 'function') {
|
||||
cb = encoding;
|
||||
encoding = null;
|
||||
}
|
||||
|
||||
if (Buffer.isBuffer(chunk))
|
||||
encoding = 'buffer';
|
||||
else if (!encoding)
|
||||
encoding = state.defaultEncoding;
|
||||
|
||||
if (typeof cb !== 'function')
|
||||
cb = function() {};
|
||||
|
||||
if (state.ended)
|
||||
writeAfterEnd(this, state, cb);
|
||||
else if (validChunk(this, state, chunk, cb))
|
||||
ret = writeOrBuffer(this, state, chunk, encoding, cb);
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
function decodeChunk(state, chunk, encoding) {
|
||||
if (!state.objectMode &&
|
||||
state.decodeStrings !== false &&
|
||||
typeof chunk === 'string') {
|
||||
chunk = new Buffer(chunk, encoding);
|
||||
}
|
||||
return chunk;
|
||||
}
|
||||
|
||||
// if we're already writing something, then just put this
|
||||
// in the queue, and wait our turn. Otherwise, call _write
|
||||
// If we return false, then we need a drain event, so set that flag.
|
||||
function writeOrBuffer(stream, state, chunk, encoding, cb) {
|
||||
chunk = decodeChunk(state, chunk, encoding);
|
||||
var len = state.objectMode ? 1 : chunk.length;
|
||||
|
||||
state.length += len;
|
||||
|
||||
var ret = state.length < state.highWaterMark;
|
||||
state.needDrain = !ret;
|
||||
|
||||
if (state.writing)
|
||||
state.buffer.push(new WriteReq(chunk, encoding, cb));
|
||||
else
|
||||
doWrite(stream, state, len, chunk, encoding, cb);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
function doWrite(stream, state, len, chunk, encoding, cb) {
|
||||
state.writelen = len;
|
||||
state.writecb = cb;
|
||||
state.writing = true;
|
||||
state.sync = true;
|
||||
stream._write(chunk, encoding, state.onwrite);
|
||||
state.sync = false;
|
||||
}
|
||||
|
||||
function onwriteError(stream, state, sync, er, cb) {
|
||||
if (sync)
|
||||
process.nextTick(function() {
|
||||
cb(er);
|
||||
});
|
||||
else
|
||||
cb(er);
|
||||
|
||||
stream.emit('error', er);
|
||||
}
|
||||
|
||||
function onwriteStateUpdate(state) {
|
||||
state.writing = false;
|
||||
state.writecb = null;
|
||||
state.length -= state.writelen;
|
||||
state.writelen = 0;
|
||||
}
|
||||
|
||||
function onwrite(stream, er) {
|
||||
var state = stream._writableState;
|
||||
var sync = state.sync;
|
||||
var cb = state.writecb;
|
||||
|
||||
onwriteStateUpdate(state);
|
||||
|
||||
if (er)
|
||||
onwriteError(stream, state, sync, er, cb);
|
||||
else {
|
||||
// Check if we're actually ready to finish, but don't emit yet
|
||||
var finished = needFinish(stream, state);
|
||||
|
||||
if (!finished && !state.bufferProcessing && state.buffer.length)
|
||||
clearBuffer(stream, state);
|
||||
|
||||
if (sync) {
|
||||
process.nextTick(function() {
|
||||
afterWrite(stream, state, finished, cb);
|
||||
});
|
||||
} else {
|
||||
afterWrite(stream, state, finished, cb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function afterWrite(stream, state, finished, cb) {
|
||||
if (!finished)
|
||||
onwriteDrain(stream, state);
|
||||
cb();
|
||||
if (finished)
|
||||
finishMaybe(stream, state);
|
||||
}
|
||||
|
||||
// Must force callback to be called on nextTick, so that we don't
|
||||
// emit 'drain' before the write() consumer gets the 'false' return
|
||||
// value, and has a chance to attach a 'drain' listener.
|
||||
function onwriteDrain(stream, state) {
|
||||
if (state.length === 0 && state.needDrain) {
|
||||
state.needDrain = false;
|
||||
stream.emit('drain');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// if there's something in the buffer waiting, then process it
|
||||
function clearBuffer(stream, state) {
|
||||
state.bufferProcessing = true;
|
||||
|
||||
for (var c = 0; c < state.buffer.length; c++) {
|
||||
var entry = state.buffer[c];
|
||||
var chunk = entry.chunk;
|
||||
var encoding = entry.encoding;
|
||||
var cb = entry.callback;
|
||||
var len = state.objectMode ? 1 : chunk.length;
|
||||
|
||||
doWrite(stream, state, len, chunk, encoding, cb);
|
||||
|
||||
// if we didn't call the onwrite immediately, then
|
||||
// it means that we need to wait until it does.
|
||||
// also, that means that the chunk and cb are currently
|
||||
// being processed, so move the buffer counter past them.
|
||||
if (state.writing) {
|
||||
c++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
state.bufferProcessing = false;
|
||||
if (c < state.buffer.length)
|
||||
state.buffer = state.buffer.slice(c);
|
||||
else
|
||||
state.buffer.length = 0;
|
||||
}
|
||||
|
||||
Writable.prototype._write = function(chunk, encoding, cb) {
|
||||
cb(new Error('not implemented'));
|
||||
};
|
||||
|
||||
Writable.prototype.end = function(chunk, encoding, cb) {
|
||||
var state = this._writableState;
|
||||
|
||||
if (typeof chunk === 'function') {
|
||||
cb = chunk;
|
||||
chunk = null;
|
||||
encoding = null;
|
||||
} else if (typeof encoding === 'function') {
|
||||
cb = encoding;
|
||||
encoding = null;
|
||||
}
|
||||
|
||||
if (typeof chunk !== 'undefined' && chunk !== null)
|
||||
this.write(chunk, encoding);
|
||||
|
||||
// ignore unnecessary end() calls.
|
||||
if (!state.ending && !state.finished)
|
||||
endWritable(this, state, cb);
|
||||
};
|
||||
|
||||
|
||||
function needFinish(stream, state) {
|
||||
return (state.ending &&
|
||||
state.length === 0 &&
|
||||
!state.finished &&
|
||||
!state.writing);
|
||||
}
|
||||
|
||||
function finishMaybe(stream, state) {
|
||||
var need = needFinish(stream, state);
|
||||
if (need) {
|
||||
state.finished = true;
|
||||
stream.emit('finish');
|
||||
}
|
||||
return need;
|
||||
}
|
||||
|
||||
function endWritable(stream, state, cb) {
|
||||
state.ending = true;
|
||||
finishMaybe(stream, state);
|
||||
if (cb) {
|
||||
if (state.finished)
|
||||
process.nextTick(cb);
|
||||
else
|
||||
stream.once('finish', cb);
|
||||
}
|
||||
state.ended = true;
|
||||
}
|
||||
gerado
externo
+39
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
gerado
externo
+1
@@ -0,0 +1 @@
|
||||
module.exports = require("./lib/_stream_passthrough.js")
|
||||
gerado
externo
+6
@@ -0,0 +1,6 @@
|
||||
exports = module.exports = require('./lib/_stream_readable.js');
|
||||
exports.Readable = exports;
|
||||
exports.Writable = require('./lib/_stream_writable.js');
|
||||
exports.Duplex = require('./lib/_stream_duplex.js');
|
||||
exports.Transform = require('./lib/_stream_transform.js');
|
||||
exports.PassThrough = require('./lib/_stream_passthrough.js');
|
||||
gerado
externo
+191
@@ -0,0 +1,191 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// 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 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.
|
||||
|
||||
var path = require('path');
|
||||
var assert = require('assert');
|
||||
|
||||
exports.testDir = path.dirname(__filename);
|
||||
exports.fixturesDir = path.join(exports.testDir, 'fixtures');
|
||||
exports.libDir = path.join(exports.testDir, '../lib');
|
||||
exports.tmpDir = path.join(exports.testDir, 'tmp');
|
||||
exports.PORT = 12346;
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
exports.PIPE = '\\\\.\\pipe\\libuv-test';
|
||||
} else {
|
||||
exports.PIPE = exports.tmpDir + '/test.sock';
|
||||
}
|
||||
|
||||
var util = require('util');
|
||||
for (var i in util) exports[i] = util[i];
|
||||
//for (var i in exports) global[i] = exports[i];
|
||||
|
||||
function protoCtrChain(o) {
|
||||
var result = [];
|
||||
for (; o; o = o.__proto__) { result.push(o.constructor); }
|
||||
return result.join();
|
||||
}
|
||||
|
||||
exports.indirectInstanceOf = function(obj, cls) {
|
||||
if (obj instanceof cls) { return true; }
|
||||
var clsChain = protoCtrChain(cls.prototype);
|
||||
var objChain = protoCtrChain(obj);
|
||||
return objChain.slice(-clsChain.length) === clsChain;
|
||||
};
|
||||
|
||||
|
||||
exports.ddCommand = function(filename, kilobytes) {
|
||||
if (process.platform === 'win32') {
|
||||
var p = path.resolve(exports.fixturesDir, 'create-file.js');
|
||||
return '"' + process.argv[0] + '" "' + p + '" "' +
|
||||
filename + '" ' + (kilobytes * 1024);
|
||||
} else {
|
||||
return 'dd if=/dev/zero of="' + filename + '" bs=1024 count=' + kilobytes;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
exports.spawnPwd = function(options) {
|
||||
var spawn = require('child_process').spawn;
|
||||
|
||||
if (process.platform === 'win32') {
|
||||
return spawn('cmd.exe', ['/c', 'cd'], options);
|
||||
} else {
|
||||
return spawn('pwd', [], options);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Turn this off if the test should not check for global leaks.
|
||||
exports.globalCheck = true;
|
||||
|
||||
process.on('exit', function() {
|
||||
if (!exports.globalCheck) return;
|
||||
var knownGlobals = [setTimeout,
|
||||
setInterval,
|
||||
global.setImmediate,
|
||||
clearTimeout,
|
||||
clearInterval,
|
||||
global.clearImmediate,
|
||||
console,
|
||||
Buffer,
|
||||
process,
|
||||
global];
|
||||
|
||||
if (global.errno) {
|
||||
knownGlobals.push(errno);
|
||||
}
|
||||
|
||||
if (global.gc) {
|
||||
knownGlobals.push(gc);
|
||||
}
|
||||
|
||||
if (global.DTRACE_HTTP_SERVER_RESPONSE) {
|
||||
knownGlobals.push(DTRACE_HTTP_SERVER_RESPONSE);
|
||||
knownGlobals.push(DTRACE_HTTP_SERVER_REQUEST);
|
||||
knownGlobals.push(DTRACE_HTTP_CLIENT_RESPONSE);
|
||||
knownGlobals.push(DTRACE_HTTP_CLIENT_REQUEST);
|
||||
knownGlobals.push(DTRACE_NET_STREAM_END);
|
||||
knownGlobals.push(DTRACE_NET_SERVER_CONNECTION);
|
||||
knownGlobals.push(DTRACE_NET_SOCKET_READ);
|
||||
knownGlobals.push(DTRACE_NET_SOCKET_WRITE);
|
||||
}
|
||||
if (global.COUNTER_NET_SERVER_CONNECTION) {
|
||||
knownGlobals.push(COUNTER_NET_SERVER_CONNECTION);
|
||||
knownGlobals.push(COUNTER_NET_SERVER_CONNECTION_CLOSE);
|
||||
knownGlobals.push(COUNTER_HTTP_SERVER_REQUEST);
|
||||
knownGlobals.push(COUNTER_HTTP_SERVER_RESPONSE);
|
||||
knownGlobals.push(COUNTER_HTTP_CLIENT_REQUEST);
|
||||
knownGlobals.push(COUNTER_HTTP_CLIENT_RESPONSE);
|
||||
}
|
||||
|
||||
if (global.ArrayBuffer) {
|
||||
knownGlobals.push(ArrayBuffer);
|
||||
knownGlobals.push(Int8Array);
|
||||
knownGlobals.push(Uint8Array);
|
||||
knownGlobals.push(Uint8ClampedArray);
|
||||
knownGlobals.push(Int16Array);
|
||||
knownGlobals.push(Uint16Array);
|
||||
knownGlobals.push(Int32Array);
|
||||
knownGlobals.push(Uint32Array);
|
||||
knownGlobals.push(Float32Array);
|
||||
knownGlobals.push(Float64Array);
|
||||
knownGlobals.push(DataView);
|
||||
}
|
||||
|
||||
for (var x in global) {
|
||||
var found = false;
|
||||
|
||||
for (var y in knownGlobals) {
|
||||
if (global[x] === knownGlobals[y]) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
console.error('Unknown global: %s', x);
|
||||
assert.ok(false, 'Unknown global found');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
var mustCallChecks = [];
|
||||
|
||||
|
||||
function runCallChecks() {
|
||||
var failed = mustCallChecks.filter(function(context) {
|
||||
return context.actual !== context.expected;
|
||||
});
|
||||
|
||||
failed.forEach(function(context) {
|
||||
console.log('Mismatched %s function calls. Expected %d, actual %d.',
|
||||
context.name,
|
||||
context.expected,
|
||||
context.actual);
|
||||
console.log(context.stack.split('\n').slice(2).join('\n'));
|
||||
});
|
||||
|
||||
if (failed.length) process.exit(1);
|
||||
}
|
||||
|
||||
|
||||
exports.mustCall = function(fn, expected) {
|
||||
if (typeof expected !== 'number') expected = 1;
|
||||
|
||||
var context = {
|
||||
expected: expected,
|
||||
actual: 0,
|
||||
stack: (new Error).stack,
|
||||
name: fn.name || '<anonymous>'
|
||||
};
|
||||
|
||||
// add the exit listener only once to avoid listener leak warnings
|
||||
if (mustCallChecks.length === 0) process.on('exit', runCallChecks);
|
||||
|
||||
mustCallChecks.push(context);
|
||||
|
||||
return function() {
|
||||
context.actual++;
|
||||
return fn.apply(this, arguments);
|
||||
};
|
||||
};
|
||||
gerado
externo
+1
@@ -0,0 +1 @@
|
||||
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
|
||||
gerado
externo
+475
@@ -0,0 +1,475 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// 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 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.
|
||||
|
||||
|
||||
var common = require('../common.js');
|
||||
var R = require('../../lib/_stream_readable');
|
||||
var assert = require('assert');
|
||||
|
||||
var util = require('util');
|
||||
var EE = require('events').EventEmitter;
|
||||
|
||||
function TestReader(n) {
|
||||
R.apply(this);
|
||||
this._buffer = new Buffer(n || 100);
|
||||
this._buffer.fill('x');
|
||||
this._pos = 0;
|
||||
this._bufs = 10;
|
||||
}
|
||||
|
||||
util.inherits(TestReader, R);
|
||||
|
||||
TestReader.prototype.read = function(n) {
|
||||
if (n === 0) return null;
|
||||
var max = this._buffer.length - this._pos;
|
||||
n = n || max;
|
||||
n = Math.max(n, 0);
|
||||
var toRead = Math.min(n, max);
|
||||
if (toRead === 0) {
|
||||
// simulate the read buffer filling up with some more bytes some time
|
||||
// in the future.
|
||||
setTimeout(function() {
|
||||
this._pos = 0;
|
||||
this._bufs -= 1;
|
||||
if (this._bufs <= 0) {
|
||||
// read them all!
|
||||
if (!this.ended) {
|
||||
this.emit('end');
|
||||
this.ended = true;
|
||||
}
|
||||
} else {
|
||||
this.emit('readable');
|
||||
}
|
||||
}.bind(this), 10);
|
||||
return null;
|
||||
}
|
||||
|
||||
var ret = this._buffer.slice(this._pos, this._pos + toRead);
|
||||
this._pos += toRead;
|
||||
return ret;
|
||||
};
|
||||
|
||||
/////
|
||||
|
||||
function TestWriter() {
|
||||
EE.apply(this);
|
||||
this.received = [];
|
||||
this.flush = false;
|
||||
}
|
||||
|
||||
util.inherits(TestWriter, EE);
|
||||
|
||||
TestWriter.prototype.write = function(c) {
|
||||
this.received.push(c.toString());
|
||||
this.emit('write', c);
|
||||
return true;
|
||||
};
|
||||
|
||||
TestWriter.prototype.end = function(c) {
|
||||
if (c) this.write(c);
|
||||
this.emit('end', this.received);
|
||||
};
|
||||
|
||||
////////
|
||||
|
||||
// tiny node-tap lookalike.
|
||||
var tests = [];
|
||||
var count = 0;
|
||||
|
||||
function test(name, fn) {
|
||||
count++;
|
||||
tests.push([name, fn]);
|
||||
}
|
||||
|
||||
function run() {
|
||||
var next = tests.shift();
|
||||
if (!next)
|
||||
return console.error('ok');
|
||||
|
||||
var name = next[0];
|
||||
var fn = next[1];
|
||||
console.log('# %s', name);
|
||||
fn({
|
||||
same: assert.deepEqual,
|
||||
ok: assert,
|
||||
equal: assert.equal,
|
||||
end: function () {
|
||||
count--;
|
||||
run();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ensure all tests have run
|
||||
process.on("exit", function () {
|
||||
assert.equal(count, 0);
|
||||
});
|
||||
|
||||
process.nextTick(run);
|
||||
|
||||
|
||||
test('a most basic test', function(t) {
|
||||
var r = new TestReader(20);
|
||||
|
||||
var reads = [];
|
||||
var expect = [ 'x',
|
||||
'xx',
|
||||
'xxx',
|
||||
'xxxx',
|
||||
'xxxxx',
|
||||
'xxxxx',
|
||||
'xxxxxxxx',
|
||||
'xxxxxxxxx',
|
||||
'xxx',
|
||||
'xxxxxxxxxxxx',
|
||||
'xxxxxxxx',
|
||||
'xxxxxxxxxxxxxxx',
|
||||
'xxxxx',
|
||||
'xxxxxxxxxxxxxxxxxx',
|
||||
'xx',
|
||||
'xxxxxxxxxxxxxxxxxxxx',
|
||||
'xxxxxxxxxxxxxxxxxxxx',
|
||||
'xxxxxxxxxxxxxxxxxxxx',
|
||||
'xxxxxxxxxxxxxxxxxxxx',
|
||||
'xxxxxxxxxxxxxxxxxxxx' ];
|
||||
|
||||
r.on('end', function() {
|
||||
t.same(reads, expect);
|
||||
t.end();
|
||||
});
|
||||
|
||||
var readSize = 1;
|
||||
function flow() {
|
||||
var res;
|
||||
while (null !== (res = r.read(readSize++))) {
|
||||
reads.push(res.toString());
|
||||
}
|
||||
r.once('readable', flow);
|
||||
}
|
||||
|
||||
flow();
|
||||
});
|
||||
|
||||
test('pipe', function(t) {
|
||||
var r = new TestReader(5);
|
||||
|
||||
var expect = [ 'xxxxx',
|
||||
'xxxxx',
|
||||
'xxxxx',
|
||||
'xxxxx',
|
||||
'xxxxx',
|
||||
'xxxxx',
|
||||
'xxxxx',
|
||||
'xxxxx',
|
||||
'xxxxx',
|
||||
'xxxxx' ]
|
||||
|
||||
var w = new TestWriter;
|
||||
var flush = true;
|
||||
|
||||
w.on('end', function(received) {
|
||||
t.same(received, expect);
|
||||
t.end();
|
||||
});
|
||||
|
||||
r.pipe(w);
|
||||
});
|
||||
|
||||
|
||||
|
||||
[1,2,3,4,5,6,7,8,9].forEach(function(SPLIT) {
|
||||
test('unpipe', function(t) {
|
||||
var r = new TestReader(5);
|
||||
|
||||
// unpipe after 3 writes, then write to another stream instead.
|
||||
var expect = [ 'xxxxx',
|
||||
'xxxxx',
|
||||
'xxxxx',
|
||||
'xxxxx',
|
||||
'xxxxx',
|
||||
'xxxxx',
|
||||
'xxxxx',
|
||||
'xxxxx',
|
||||
'xxxxx',
|
||||
'xxxxx' ];
|
||||
expect = [ expect.slice(0, SPLIT), expect.slice(SPLIT) ];
|
||||
|
||||
var w = [ new TestWriter(), new TestWriter() ];
|
||||
|
||||
var writes = SPLIT;
|
||||
w[0].on('write', function() {
|
||||
if (--writes === 0) {
|
||||
r.unpipe();
|
||||
t.equal(r._readableState.pipes, null);
|
||||
w[0].end();
|
||||
r.pipe(w[1]);
|
||||
t.equal(r._readableState.pipes, w[1]);
|
||||
}
|
||||
});
|
||||
|
||||
var ended = 0;
|
||||
|
||||
var ended0 = false;
|
||||
var ended1 = false;
|
||||
w[0].on('end', function(results) {
|
||||
t.equal(ended0, false);
|
||||
ended0 = true;
|
||||
ended++;
|
||||
t.same(results, expect[0]);
|
||||
});
|
||||
|
||||
w[1].on('end', function(results) {
|
||||
t.equal(ended1, false);
|
||||
ended1 = true;
|
||||
ended++;
|
||||
t.equal(ended, 2);
|
||||
t.same(results, expect[1]);
|
||||
t.end();
|
||||
});
|
||||
|
||||
r.pipe(w[0]);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// both writers should get the same exact data.
|
||||
test('multipipe', function(t) {
|
||||
var r = new TestReader(5);
|
||||
var w = [ new TestWriter, new TestWriter ];
|
||||
|
||||
var expect = [ 'xxxxx',
|
||||
'xxxxx',
|
||||
'xxxxx',
|
||||
'xxxxx',
|
||||
'xxxxx',
|
||||
'xxxxx',
|
||||
'xxxxx',
|
||||
'xxxxx',
|
||||
'xxxxx',
|
||||
'xxxxx' ];
|
||||
|
||||
var c = 2;
|
||||
w[0].on('end', function(received) {
|
||||
t.same(received, expect, 'first');
|
||||
if (--c === 0) t.end();
|
||||
});
|
||||
w[1].on('end', function(received) {
|
||||
t.same(received, expect, 'second');
|
||||
if (--c === 0) t.end();
|
||||
});
|
||||
|
||||
r.pipe(w[0]);
|
||||
r.pipe(w[1]);
|
||||
});
|
||||
|
||||
|
||||
[1,2,3,4,5,6,7,8,9].forEach(function(SPLIT) {
|
||||
test('multi-unpipe', function(t) {
|
||||
var r = new TestReader(5);
|
||||
|
||||
// unpipe after 3 writes, then write to another stream instead.
|
||||
var expect = [ 'xxxxx',
|
||||
'xxxxx',
|
||||
'xxxxx',
|
||||
'xxxxx',
|
||||
'xxxxx',
|
||||
'xxxxx',
|
||||
'xxxxx',
|
||||
'xxxxx',
|
||||
'xxxxx',
|
||||
'xxxxx' ];
|
||||
expect = [ expect.slice(0, SPLIT), expect.slice(SPLIT) ];
|
||||
|
||||
var w = [ new TestWriter(), new TestWriter(), new TestWriter() ];
|
||||
|
||||
var writes = SPLIT;
|
||||
w[0].on('write', function() {
|
||||
if (--writes === 0) {
|
||||
r.unpipe();
|
||||
w[0].end();
|
||||
r.pipe(w[1]);
|
||||
}
|
||||
});
|
||||
|
||||
var ended = 0;
|
||||
|
||||
w[0].on('end', function(results) {
|
||||
ended++;
|
||||
t.same(results, expect[0]);
|
||||
});
|
||||
|
||||
w[1].on('end', function(results) {
|
||||
ended++;
|
||||
t.equal(ended, 2);
|
||||
t.same(results, expect[1]);
|
||||
t.end();
|
||||
});
|
||||
|
||||
r.pipe(w[0]);
|
||||
r.pipe(w[2]);
|
||||
});
|
||||
});
|
||||
|
||||
test('back pressure respected', function (t) {
|
||||
function noop() {}
|
||||
|
||||
var r = new R({ objectMode: true });
|
||||
r._read = noop;
|
||||
var counter = 0;
|
||||
r.push(["one"]);
|
||||
r.push(["two"]);
|
||||
r.push(["three"]);
|
||||
r.push(["four"]);
|
||||
r.push(null);
|
||||
|
||||
var w1 = new R();
|
||||
w1.write = function (chunk) {
|
||||
assert.equal(chunk[0], "one");
|
||||
w1.emit("close");
|
||||
process.nextTick(function () {
|
||||
r.pipe(w2);
|
||||
r.pipe(w3);
|
||||
})
|
||||
};
|
||||
w1.end = noop;
|
||||
|
||||
r.pipe(w1);
|
||||
|
||||
var expected = ["two", "two", "three", "three", "four", "four"];
|
||||
|
||||
var w2 = new R();
|
||||
w2.write = function (chunk) {
|
||||
assert.equal(chunk[0], expected.shift());
|
||||
assert.equal(counter, 0);
|
||||
|
||||
counter++;
|
||||
|
||||
if (chunk[0] === "four") {
|
||||
return true;
|
||||
}
|
||||
|
||||
setTimeout(function () {
|
||||
counter--;
|
||||
w2.emit("drain");
|
||||
}, 10);
|
||||
|
||||
return false;
|
||||
}
|
||||
w2.end = noop;
|
||||
|
||||
var w3 = new R();
|
||||
w3.write = function (chunk) {
|
||||
assert.equal(chunk[0], expected.shift());
|
||||
assert.equal(counter, 1);
|
||||
|
||||
counter++;
|
||||
|
||||
if (chunk[0] === "four") {
|
||||
return true;
|
||||
}
|
||||
|
||||
setTimeout(function () {
|
||||
counter--;
|
||||
w3.emit("drain");
|
||||
}, 50);
|
||||
|
||||
return false;
|
||||
};
|
||||
w3.end = function () {
|
||||
assert.equal(counter, 2);
|
||||
assert.equal(expected.length, 0);
|
||||
t.end();
|
||||
};
|
||||
});
|
||||
|
||||
test('read(0) for ended streams', function (t) {
|
||||
var r = new R();
|
||||
var written = false;
|
||||
var ended = false;
|
||||
r._read = function (n) {};
|
||||
|
||||
r.push(new Buffer("foo"));
|
||||
r.push(null);
|
||||
|
||||
var v = r.read(0);
|
||||
|
||||
assert.equal(v, null);
|
||||
|
||||
var w = new R();
|
||||
|
||||
w.write = function (buffer) {
|
||||
written = true;
|
||||
assert.equal(ended, false);
|
||||
assert.equal(buffer.toString(), "foo")
|
||||
};
|
||||
|
||||
w.end = function () {
|
||||
ended = true;
|
||||
assert.equal(written, true);
|
||||
t.end();
|
||||
};
|
||||
|
||||
r.pipe(w);
|
||||
})
|
||||
|
||||
test('sync _read ending', function (t) {
|
||||
var r = new R();
|
||||
var called = false;
|
||||
r._read = function (n) {
|
||||
r.push(null);
|
||||
};
|
||||
|
||||
r.once('end', function () {
|
||||
called = true;
|
||||
})
|
||||
|
||||
r.read();
|
||||
|
||||
process.nextTick(function () {
|
||||
assert.equal(called, true);
|
||||
t.end();
|
||||
})
|
||||
});
|
||||
|
||||
test('adding readable triggers data flow', function(t) {
|
||||
var r = new R({ highWaterMark: 5 });
|
||||
var onReadable = false;
|
||||
var readCalled = 0;
|
||||
|
||||
r._read = function(n) {
|
||||
if (readCalled++ === 2)
|
||||
r.push(null);
|
||||
else
|
||||
r.push(new Buffer('asdf'));
|
||||
};
|
||||
|
||||
var called = false;
|
||||
r.on('readable', function() {
|
||||
onReadable = true;
|
||||
r.read();
|
||||
});
|
||||
|
||||
r.on('end', function() {
|
||||
t.equal(readCalled, 3);
|
||||
t.ok(onReadable);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// 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 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.
|
||||
|
||||
|
||||
var common = require('../common.js');
|
||||
var R = require('../../lib/_stream_readable');
|
||||
var assert = require('assert');
|
||||
|
||||
var util = require('util');
|
||||
var EE = require('events').EventEmitter;
|
||||
|
||||
var ondataCalled = 0;
|
||||
|
||||
function TestReader() {
|
||||
R.apply(this);
|
||||
this._buffer = new Buffer(100);
|
||||
this._buffer.fill('x');
|
||||
|
||||
this.on('data', function() {
|
||||
ondataCalled++;
|
||||
});
|
||||
}
|
||||
|
||||
util.inherits(TestReader, R);
|
||||
|
||||
TestReader.prototype._read = function(n) {
|
||||
this.push(this._buffer);
|
||||
this._buffer = new Buffer(0);
|
||||
};
|
||||
|
||||
var reader = new TestReader();
|
||||
assert.equal(ondataCalled, 1);
|
||||
gerado
externo
+41
@@ -0,0 +1,41 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// 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 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.
|
||||
|
||||
var common = require('../common.js');
|
||||
var stream = require('../../readable');
|
||||
var Buffer = require('buffer').Buffer;
|
||||
|
||||
var r = new stream.Readable();
|
||||
r._read = function(size) {
|
||||
r.push(new Buffer(size));
|
||||
};
|
||||
|
||||
var w = new stream.Writable();
|
||||
w._write = function(data, encoding, cb) {
|
||||
cb(null);
|
||||
};
|
||||
|
||||
r.pipe(w);
|
||||
|
||||
// This might sound unrealistic, but it happens in net.js. When
|
||||
// `socket.allowHalfOpen === false`, EOF will cause `.destroySoon()` call which
|
||||
// ends the writable side of net.Socket.
|
||||
w.end();
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// 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 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.
|
||||
|
||||
var common = require('../common.js');
|
||||
var assert = require('assert');
|
||||
|
||||
// If everything aligns so that you do a read(n) of exactly the
|
||||
// remaining buffer, then make sure that 'end' still emits.
|
||||
|
||||
var READSIZE = 100;
|
||||
var PUSHSIZE = 20;
|
||||
var PUSHCOUNT = 1000;
|
||||
var HWM = 50;
|
||||
|
||||
var Readable = require('../../readable').Readable;
|
||||
var r = new Readable({
|
||||
highWaterMark: HWM
|
||||
});
|
||||
var rs = r._readableState;
|
||||
|
||||
r._read = push;
|
||||
|
||||
r.on('readable', function() {
|
||||
console.error('>> readable');
|
||||
do {
|
||||
console.error(' > read(%d)', READSIZE);
|
||||
var ret = r.read(READSIZE);
|
||||
console.error(' < %j (%d remain)', ret && ret.length, rs.length);
|
||||
} while (ret && ret.length === READSIZE);
|
||||
|
||||
console.error('<< after read()',
|
||||
ret && ret.length,
|
||||
rs.needReadable,
|
||||
rs.length);
|
||||
});
|
||||
|
||||
var endEmitted = false;
|
||||
r.on('end', function() {
|
||||
endEmitted = true;
|
||||
console.error('end');
|
||||
});
|
||||
|
||||
var pushes = 0;
|
||||
function push() {
|
||||
if (pushes > PUSHCOUNT)
|
||||
return;
|
||||
|
||||
if (pushes++ === PUSHCOUNT) {
|
||||
console.error(' push(EOF)');
|
||||
return r.push(null);
|
||||
}
|
||||
|
||||
console.error(' push #%d', pushes);
|
||||
if (r.push(new Buffer(PUSHSIZE)))
|
||||
setTimeout(push);
|
||||
}
|
||||
|
||||
// start the flow
|
||||
var ret = r.read(0);
|
||||
|
||||
process.on('exit', function() {
|
||||
assert.equal(pushes, PUSHCOUNT + 1);
|
||||
assert(endEmitted);
|
||||
});
|
||||
gerado
externo
+348
@@ -0,0 +1,348 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// 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 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.
|
||||
|
||||
|
||||
var common = require('../common.js');
|
||||
var Readable = require('../../lib/_stream_readable');
|
||||
var Writable = require('../../lib/_stream_writable');
|
||||
var assert = require('assert');
|
||||
|
||||
// tiny node-tap lookalike.
|
||||
var tests = [];
|
||||
var count = 0;
|
||||
|
||||
function test(name, fn) {
|
||||
count++;
|
||||
tests.push([name, fn]);
|
||||
}
|
||||
|
||||
function run() {
|
||||
var next = tests.shift();
|
||||
if (!next)
|
||||
return console.error('ok');
|
||||
|
||||
var name = next[0];
|
||||
var fn = next[1];
|
||||
console.log('# %s', name);
|
||||
fn({
|
||||
same: assert.deepEqual,
|
||||
equal: assert.equal,
|
||||
end: function() {
|
||||
count--;
|
||||
run();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ensure all tests have run
|
||||
process.on('exit', function() {
|
||||
assert.equal(count, 0);
|
||||
});
|
||||
|
||||
process.nextTick(run);
|
||||
|
||||
function toArray(callback) {
|
||||
var stream = new Writable({ objectMode: true });
|
||||
var list = [];
|
||||
stream.write = function(chunk) {
|
||||
list.push(chunk);
|
||||
};
|
||||
|
||||
stream.end = function() {
|
||||
callback(list);
|
||||
};
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
function fromArray(list) {
|
||||
var r = new Readable({ objectMode: true });
|
||||
r._read = noop;
|
||||
list.forEach(function(chunk) {
|
||||
r.push(chunk);
|
||||
});
|
||||
r.push(null);
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
function noop() {}
|
||||
|
||||
test('can read objects from stream', function(t) {
|
||||
var r = fromArray([{ one: '1'}, { two: '2' }]);
|
||||
|
||||
var v1 = r.read();
|
||||
var v2 = r.read();
|
||||
var v3 = r.read();
|
||||
|
||||
assert.deepEqual(v1, { one: '1' });
|
||||
assert.deepEqual(v2, { two: '2' });
|
||||
assert.deepEqual(v3, null);
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('can pipe objects into stream', function(t) {
|
||||
var r = fromArray([{ one: '1'}, { two: '2' }]);
|
||||
|
||||
r.pipe(toArray(function(list) {
|
||||
assert.deepEqual(list, [
|
||||
{ one: '1' },
|
||||
{ two: '2' }
|
||||
]);
|
||||
|
||||
t.end();
|
||||
}));
|
||||
});
|
||||
|
||||
test('read(n) is ignored', function(t) {
|
||||
var r = fromArray([{ one: '1'}, { two: '2' }]);
|
||||
|
||||
var value = r.read(2);
|
||||
|
||||
assert.deepEqual(value, { one: '1' });
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('can read objects from _read (sync)', function(t) {
|
||||
var r = new Readable({ objectMode: true });
|
||||
var list = [{ one: '1'}, { two: '2' }];
|
||||
r._read = function(n) {
|
||||
var item = list.shift();
|
||||
r.push(item || null);
|
||||
};
|
||||
|
||||
r.pipe(toArray(function(list) {
|
||||
assert.deepEqual(list, [
|
||||
{ one: '1' },
|
||||
{ two: '2' }
|
||||
]);
|
||||
|
||||
t.end();
|
||||
}));
|
||||
});
|
||||
|
||||
test('can read objects from _read (async)', function(t) {
|
||||
var r = new Readable({ objectMode: true });
|
||||
var list = [{ one: '1'}, { two: '2' }];
|
||||
r._read = function(n) {
|
||||
var item = list.shift();
|
||||
process.nextTick(function() {
|
||||
r.push(item || null);
|
||||
});
|
||||
};
|
||||
|
||||
r.pipe(toArray(function(list) {
|
||||
assert.deepEqual(list, [
|
||||
{ one: '1' },
|
||||
{ two: '2' }
|
||||
]);
|
||||
|
||||
t.end();
|
||||
}));
|
||||
});
|
||||
|
||||
test('can read strings as objects', function(t) {
|
||||
var r = new Readable({
|
||||
objectMode: true
|
||||
});
|
||||
r._read = noop;
|
||||
var list = ['one', 'two', 'three'];
|
||||
list.forEach(function(str) {
|
||||
r.push(str);
|
||||
});
|
||||
r.push(null);
|
||||
|
||||
r.pipe(toArray(function(array) {
|
||||
assert.deepEqual(array, list);
|
||||
|
||||
t.end();
|
||||
}));
|
||||
});
|
||||
|
||||
test('read(0) for object streams', function(t) {
|
||||
var r = new Readable({
|
||||
objectMode: true
|
||||
});
|
||||
r._read = noop;
|
||||
|
||||
r.push('foobar');
|
||||
r.push(null);
|
||||
|
||||
var v = r.read(0);
|
||||
|
||||
r.pipe(toArray(function(array) {
|
||||
assert.deepEqual(array, ['foobar']);
|
||||
|
||||
t.end();
|
||||
}));
|
||||
});
|
||||
|
||||
test('falsey values', function(t) {
|
||||
var r = new Readable({
|
||||
objectMode: true
|
||||
});
|
||||
r._read = noop;
|
||||
|
||||
r.push(false);
|
||||
r.push(0);
|
||||
r.push('');
|
||||
r.push(null);
|
||||
|
||||
r.pipe(toArray(function(array) {
|
||||
assert.deepEqual(array, [false, 0, '']);
|
||||
|
||||
t.end();
|
||||
}));
|
||||
});
|
||||
|
||||
test('high watermark _read', function(t) {
|
||||
var r = new Readable({
|
||||
highWaterMark: 6,
|
||||
objectMode: true
|
||||
});
|
||||
var calls = 0;
|
||||
var list = ['1', '2', '3', '4', '5', '6', '7', '8'];
|
||||
|
||||
r._read = function(n) {
|
||||
calls++;
|
||||
};
|
||||
|
||||
list.forEach(function(c) {
|
||||
r.push(c);
|
||||
});
|
||||
|
||||
var v = r.read();
|
||||
|
||||
assert.equal(calls, 0);
|
||||
assert.equal(v, '1');
|
||||
|
||||
var v2 = r.read();
|
||||
|
||||
assert.equal(calls, 1);
|
||||
assert.equal(v2, '2');
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('high watermark push', function(t) {
|
||||
var r = new Readable({
|
||||
highWaterMark: 6,
|
||||
objectMode: true
|
||||
});
|
||||
r._read = function(n) {};
|
||||
for (var i = 0; i < 6; i++) {
|
||||
var bool = r.push(i);
|
||||
assert.equal(bool, i === 5 ? false : true);
|
||||
}
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('can write objects to stream', function(t) {
|
||||
var w = new Writable({ objectMode: true });
|
||||
|
||||
w._write = function(chunk, encoding, cb) {
|
||||
assert.deepEqual(chunk, { foo: 'bar' });
|
||||
cb();
|
||||
};
|
||||
|
||||
w.on('finish', function() {
|
||||
t.end();
|
||||
});
|
||||
|
||||
w.write({ foo: 'bar' });
|
||||
w.end();
|
||||
});
|
||||
|
||||
test('can write multiple objects to stream', function(t) {
|
||||
var w = new Writable({ objectMode: true });
|
||||
var list = [];
|
||||
|
||||
w._write = function(chunk, encoding, cb) {
|
||||
list.push(chunk);
|
||||
cb();
|
||||
};
|
||||
|
||||
w.on('finish', function() {
|
||||
assert.deepEqual(list, [0, 1, 2, 3, 4]);
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
w.write(0);
|
||||
w.write(1);
|
||||
w.write(2);
|
||||
w.write(3);
|
||||
w.write(4);
|
||||
w.end();
|
||||
});
|
||||
|
||||
test('can write strings as objects', function(t) {
|
||||
var w = new Writable({
|
||||
objectMode: true
|
||||
});
|
||||
var list = [];
|
||||
|
||||
w._write = function(chunk, encoding, cb) {
|
||||
list.push(chunk);
|
||||
process.nextTick(cb);
|
||||
};
|
||||
|
||||
w.on('finish', function() {
|
||||
assert.deepEqual(list, ['0', '1', '2', '3', '4']);
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
w.write('0');
|
||||
w.write('1');
|
||||
w.write('2');
|
||||
w.write('3');
|
||||
w.write('4');
|
||||
w.end();
|
||||
});
|
||||
|
||||
test('buffers finish until cb is called', function(t) {
|
||||
var w = new Writable({
|
||||
objectMode: true
|
||||
});
|
||||
var called = false;
|
||||
|
||||
w._write = function(chunk, encoding, cb) {
|
||||
assert.equal(chunk, 'foo');
|
||||
|
||||
process.nextTick(function() {
|
||||
called = true;
|
||||
cb();
|
||||
});
|
||||
};
|
||||
|
||||
w.on('finish', function() {
|
||||
assert.equal(called, true);
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
w.write('foo');
|
||||
w.end();
|
||||
});
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// 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 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.
|
||||
|
||||
var common = require('../common');
|
||||
var assert = require('assert');
|
||||
var stream = require('../../readable');
|
||||
|
||||
(function testErrorListenerCatches() {
|
||||
var count = 1000;
|
||||
|
||||
var source = new stream.Readable();
|
||||
source._read = function(n) {
|
||||
n = Math.min(count, n);
|
||||
count -= n;
|
||||
source.push(new Buffer(n));
|
||||
};
|
||||
|
||||
var unpipedDest;
|
||||
source.unpipe = function(dest) {
|
||||
unpipedDest = dest;
|
||||
stream.Readable.prototype.unpipe.call(this, dest);
|
||||
};
|
||||
|
||||
var dest = new stream.Writable();
|
||||
dest._write = function(chunk, encoding, cb) {
|
||||
cb();
|
||||
};
|
||||
|
||||
source.pipe(dest);
|
||||
|
||||
var gotErr = null;
|
||||
dest.on('error', function(err) {
|
||||
gotErr = err;
|
||||
});
|
||||
|
||||
var unpipedSource;
|
||||
dest.on('unpipe', function(src) {
|
||||
unpipedSource = src;
|
||||
});
|
||||
|
||||
var err = new Error('This stream turned into bacon.');
|
||||
dest.emit('error', err);
|
||||
assert.strictEqual(gotErr, err);
|
||||
assert.strictEqual(unpipedSource, source);
|
||||
assert.strictEqual(unpipedDest, dest);
|
||||
})();
|
||||
|
||||
(function testErrorWithoutListenerThrows() {
|
||||
var count = 1000;
|
||||
|
||||
var source = new stream.Readable();
|
||||
source._read = function(n) {
|
||||
n = Math.min(count, n);
|
||||
count -= n;
|
||||
source.push(new Buffer(n));
|
||||
};
|
||||
|
||||
var unpipedDest;
|
||||
source.unpipe = function(dest) {
|
||||
unpipedDest = dest;
|
||||
stream.Readable.prototype.unpipe.call(this, dest);
|
||||
};
|
||||
|
||||
var dest = new stream.Writable();
|
||||
dest._write = function(chunk, encoding, cb) {
|
||||
cb();
|
||||
};
|
||||
|
||||
source.pipe(dest);
|
||||
|
||||
var unpipedSource;
|
||||
dest.on('unpipe', function(src) {
|
||||
unpipedSource = src;
|
||||
});
|
||||
|
||||
var err = new Error('This stream turned into bacon.');
|
||||
|
||||
var gotErr = null;
|
||||
try {
|
||||
dest.emit('error', err);
|
||||
} catch (e) {
|
||||
gotErr = e;
|
||||
}
|
||||
assert.strictEqual(gotErr, err);
|
||||
assert.strictEqual(unpipedSource, source);
|
||||
assert.strictEqual(unpipedDest, dest);
|
||||
})();
|
||||
gerado
externo
+138
@@ -0,0 +1,138 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// 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 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.
|
||||
|
||||
var common = require('../common.js');
|
||||
var stream = require('../../readable');
|
||||
var Readable = stream.Readable;
|
||||
var Writable = stream.Writable;
|
||||
var assert = require('assert');
|
||||
|
||||
var util = require('util');
|
||||
var EE = require('events').EventEmitter;
|
||||
|
||||
|
||||
// a mock thing a bit like the net.Socket/tcp_wrap.handle interaction
|
||||
|
||||
var stream = new Readable({
|
||||
highWaterMark: 16,
|
||||
encoding: 'utf8'
|
||||
});
|
||||
|
||||
var source = new EE;
|
||||
|
||||
stream._read = function() {
|
||||
console.error('stream._read');
|
||||
readStart();
|
||||
};
|
||||
|
||||
var ended = false;
|
||||
stream.on('end', function() {
|
||||
ended = true;
|
||||
});
|
||||
|
||||
source.on('data', function(chunk) {
|
||||
var ret = stream.push(chunk);
|
||||
console.error('data', stream._readableState.length);
|
||||
if (!ret)
|
||||
readStop();
|
||||
});
|
||||
|
||||
source.on('end', function() {
|
||||
stream.push(null);
|
||||
});
|
||||
|
||||
var reading = false;
|
||||
|
||||
function readStart() {
|
||||
console.error('readStart');
|
||||
reading = true;
|
||||
}
|
||||
|
||||
function readStop() {
|
||||
console.error('readStop');
|
||||
reading = false;
|
||||
process.nextTick(function() {
|
||||
var r = stream.read();
|
||||
if (r !== null)
|
||||
writer.write(r);
|
||||
});
|
||||
}
|
||||
|
||||
var writer = new Writable({
|
||||
decodeStrings: false
|
||||
});
|
||||
|
||||
var written = [];
|
||||
|
||||
var expectWritten =
|
||||
[ 'asdfgasdfgasdfgasdfg',
|
||||
'asdfgasdfgasdfgasdfg',
|
||||
'asdfgasdfgasdfgasdfg',
|
||||
'asdfgasdfgasdfgasdfg',
|
||||
'asdfgasdfgasdfgasdfg',
|
||||
'asdfgasdfgasdfgasdfg' ];
|
||||
|
||||
writer._write = function(chunk, encoding, cb) {
|
||||
console.error('WRITE %s', chunk);
|
||||
written.push(chunk);
|
||||
process.nextTick(cb);
|
||||
};
|
||||
|
||||
writer.on('finish', finish);
|
||||
|
||||
|
||||
// now emit some chunks.
|
||||
|
||||
var chunk = "asdfg";
|
||||
|
||||
var set = 0;
|
||||
readStart();
|
||||
data();
|
||||
function data() {
|
||||
assert(reading);
|
||||
source.emit('data', chunk);
|
||||
assert(reading);
|
||||
source.emit('data', chunk);
|
||||
assert(reading);
|
||||
source.emit('data', chunk);
|
||||
assert(reading);
|
||||
source.emit('data', chunk);
|
||||
assert(!reading);
|
||||
if (set++ < 5)
|
||||
setTimeout(data, 10);
|
||||
else
|
||||
end();
|
||||
}
|
||||
|
||||
function finish() {
|
||||
console.error('finish');
|
||||
assert.deepEqual(written, expectWritten);
|
||||
console.log('ok');
|
||||
}
|
||||
|
||||
function end() {
|
||||
source.emit('end');
|
||||
assert(!reading);
|
||||
writer.end(stream.read());
|
||||
setTimeout(function() {
|
||||
assert(ended);
|
||||
});
|
||||
}
|
||||
+54
@@ -0,0 +1,54 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// 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 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.
|
||||
|
||||
var common = require('../common');
|
||||
var assert = require('assert');
|
||||
var Readable = require('../../readable').Readable;
|
||||
var r = new Readable();
|
||||
var N = 256 * 1024;
|
||||
|
||||
// Go ahead and allow the pathological case for this test.
|
||||
// Yes, it's an infinite loop, that's the point.
|
||||
process.maxTickDepth = N + 2;
|
||||
|
||||
var reads = 0;
|
||||
r._read = function(n) {
|
||||
var chunk = reads++ === N ? null : new Buffer(1);
|
||||
r.push(chunk);
|
||||
};
|
||||
|
||||
r.on('readable', function onReadable() {
|
||||
if (!(r._readableState.length % 256))
|
||||
console.error('readable', r._readableState.length);
|
||||
r.read(N * 2);
|
||||
});
|
||||
|
||||
var ended = false;
|
||||
r.on('end', function onEnd() {
|
||||
ended = true;
|
||||
});
|
||||
|
||||
r.read(0);
|
||||
|
||||
process.on('exit', function() {
|
||||
assert(ended);
|
||||
console.log('ok');
|
||||
});
|
||||
+119
@@ -0,0 +1,119 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// 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 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.
|
||||
|
||||
var common = require('../common');
|
||||
var assert = require('assert');
|
||||
|
||||
var Readable = require('../../readable').Readable;
|
||||
|
||||
test1();
|
||||
if (!/^v0\.[0-8]\./.test(process.version))
|
||||
test2();
|
||||
|
||||
function test1() {
|
||||
var r = new Readable();
|
||||
|
||||
// should not end when we get a Buffer(0) or '' as the _read result
|
||||
// that just means that there is *temporarily* no data, but to go
|
||||
// ahead and try again later.
|
||||
//
|
||||
// note that this is very unusual. it only works for crypto streams
|
||||
// because the other side of the stream will call read(0) to cycle
|
||||
// data through openssl. that's why we set the timeouts to call
|
||||
// r.read(0) again later, otherwise there is no more work being done
|
||||
// and the process just exits.
|
||||
|
||||
var buf = new Buffer(5);
|
||||
buf.fill('x');
|
||||
var reads = 5;
|
||||
r._read = function(n) {
|
||||
switch (reads--) {
|
||||
case 0:
|
||||
return r.push(null); // EOF
|
||||
case 1:
|
||||
return r.push(buf);
|
||||
case 2:
|
||||
setTimeout(r.read.bind(r, 0), 10);
|
||||
return r.push(new Buffer(0)); // Not-EOF!
|
||||
case 3:
|
||||
setTimeout(r.read.bind(r, 0), 10);
|
||||
return process.nextTick(function() {
|
||||
return r.push(new Buffer(0));
|
||||
});
|
||||
case 4:
|
||||
setTimeout(r.read.bind(r, 0), 10);
|
||||
return setTimeout(function() {
|
||||
return r.push(new Buffer(0));
|
||||
});
|
||||
case 5:
|
||||
return setTimeout(function() {
|
||||
return r.push(buf);
|
||||
});
|
||||
default:
|
||||
throw new Error('unreachable');
|
||||
}
|
||||
};
|
||||
|
||||
var results = [];
|
||||
function flow() {
|
||||
var chunk;
|
||||
while (null !== (chunk = r.read()))
|
||||
results.push(chunk + '');
|
||||
}
|
||||
r.on('readable', flow);
|
||||
r.on('end', function() {
|
||||
results.push('EOF');
|
||||
});
|
||||
flow();
|
||||
|
||||
process.on('exit', function() {
|
||||
assert.deepEqual(results, [ 'xxxxx', 'xxxxx', 'EOF' ]);
|
||||
console.log('ok');
|
||||
});
|
||||
}
|
||||
|
||||
function test2() {
|
||||
var r = new Readable({ encoding: 'base64' });
|
||||
var reads = 5;
|
||||
r._read = function(n) {
|
||||
if (!reads--)
|
||||
return r.push(null); // EOF
|
||||
else
|
||||
return r.push(new Buffer('x'));
|
||||
};
|
||||
|
||||
var results = [];
|
||||
function flow() {
|
||||
var chunk;
|
||||
while (null !== (chunk = r.read()))
|
||||
results.push(chunk + '');
|
||||
}
|
||||
r.on('readable', flow);
|
||||
r.on('end', function() {
|
||||
results.push('EOF');
|
||||
});
|
||||
flow();
|
||||
|
||||
process.on('exit', function() {
|
||||
assert.deepEqual(results, [ 'eHh4', 'eHg=', 'EOF' ]);
|
||||
console.log('ok');
|
||||
});
|
||||
}
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// 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 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.
|
||||
|
||||
var assert = require('assert');
|
||||
var common = require('../common.js');
|
||||
var fromList = require('../../lib/_stream_readable')._fromList;
|
||||
|
||||
// tiny node-tap lookalike.
|
||||
var tests = [];
|
||||
var count = 0;
|
||||
|
||||
function test(name, fn) {
|
||||
count++;
|
||||
tests.push([name, fn]);
|
||||
}
|
||||
|
||||
function run() {
|
||||
var next = tests.shift();
|
||||
if (!next)
|
||||
return console.error('ok');
|
||||
|
||||
var name = next[0];
|
||||
var fn = next[1];
|
||||
console.log('# %s', name);
|
||||
fn({
|
||||
same: assert.deepEqual,
|
||||
equal: assert.equal,
|
||||
end: function () {
|
||||
count--;
|
||||
run();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ensure all tests have run
|
||||
process.on("exit", function () {
|
||||
assert.equal(count, 0);
|
||||
});
|
||||
|
||||
process.nextTick(run);
|
||||
|
||||
|
||||
|
||||
test('buffers', function(t) {
|
||||
// have a length
|
||||
var len = 16;
|
||||
var list = [ new Buffer('foog'),
|
||||
new Buffer('bark'),
|
||||
new Buffer('bazy'),
|
||||
new Buffer('kuel') ];
|
||||
|
||||
// read more than the first element.
|
||||
var ret = fromList(6, { buffer: list, length: 16 });
|
||||
t.equal(ret.toString(), 'foogba');
|
||||
|
||||
// read exactly the first element.
|
||||
ret = fromList(2, { buffer: list, length: 10 });
|
||||
t.equal(ret.toString(), 'rk');
|
||||
|
||||
// read less than the first element.
|
||||
ret = fromList(2, { buffer: list, length: 8 });
|
||||
t.equal(ret.toString(), 'ba');
|
||||
|
||||
// read more than we have.
|
||||
ret = fromList(100, { buffer: list, length: 6 });
|
||||
t.equal(ret.toString(), 'zykuel');
|
||||
|
||||
// all consumed.
|
||||
t.same(list, []);
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('strings', function(t) {
|
||||
// have a length
|
||||
var len = 16;
|
||||
var list = [ 'foog',
|
||||
'bark',
|
||||
'bazy',
|
||||
'kuel' ];
|
||||
|
||||
// read more than the first element.
|
||||
var ret = fromList(6, { buffer: list, length: 16, decoder: true });
|
||||
t.equal(ret, 'foogba');
|
||||
|
||||
// read exactly the first element.
|
||||
ret = fromList(2, { buffer: list, length: 10, decoder: true });
|
||||
t.equal(ret, 'rk');
|
||||
|
||||
// read less than the first element.
|
||||
ret = fromList(2, { buffer: list, length: 8, decoder: true });
|
||||
t.equal(ret, 'ba');
|
||||
|
||||
// read more than we have.
|
||||
ret = fromList(100, { buffer: list, length: 6, decoder: true });
|
||||
t.equal(ret, 'zykuel');
|
||||
|
||||
// all consumed.
|
||||
t.same(list, []);
|
||||
|
||||
t.end();
|
||||
});
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// 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 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.
|
||||
|
||||
var common = require('../common');
|
||||
var assert = require('assert');
|
||||
|
||||
var Stream = require('../../readable');
|
||||
var Readable = Stream.Readable;
|
||||
|
||||
var r = new Readable();
|
||||
var N = 256;
|
||||
var reads = 0;
|
||||
r._read = function(n) {
|
||||
return r.push(++reads === N ? null : new Buffer(1));
|
||||
};
|
||||
|
||||
var rended = false;
|
||||
r.on('end', function() {
|
||||
rended = true;
|
||||
});
|
||||
|
||||
var w = new Stream();
|
||||
w.writable = true;
|
||||
var writes = 0;
|
||||
var buffered = 0;
|
||||
w.write = function(c) {
|
||||
writes += c.length;
|
||||
buffered += c.length;
|
||||
process.nextTick(drain);
|
||||
return false;
|
||||
};
|
||||
|
||||
function drain() {
|
||||
assert(buffered <= 2);
|
||||
buffered = 0;
|
||||
w.emit('drain');
|
||||
}
|
||||
|
||||
|
||||
var wended = false;
|
||||
w.end = function() {
|
||||
wended = true;
|
||||
};
|
||||
|
||||
// Just for kicks, let's mess with the drain count.
|
||||
// This verifies that even if it gets negative in the
|
||||
// pipe() cleanup function, we'll still function properly.
|
||||
r.on('readable', function() {
|
||||
w.emit('drain');
|
||||
});
|
||||
|
||||
r.pipe(w);
|
||||
process.on('exit', function() {
|
||||
assert(rended);
|
||||
assert(wended);
|
||||
console.error('ok');
|
||||
});
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// 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 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.
|
||||
|
||||
var assert = require('assert');
|
||||
var common = require('../common.js');
|
||||
var Readable = require('../../lib/_stream_readable');
|
||||
|
||||
var len = 0;
|
||||
var chunks = new Array(10);
|
||||
for (var i = 1; i <= 10; i++) {
|
||||
chunks[i-1] = new Buffer(i);
|
||||
len += i;
|
||||
}
|
||||
|
||||
var test = new Readable();
|
||||
var n = 0;
|
||||
test._read = function(size) {
|
||||
var chunk = chunks[n++];
|
||||
setTimeout(function() {
|
||||
test.push(chunk);
|
||||
});
|
||||
};
|
||||
|
||||
test.on('end', thrower);
|
||||
function thrower() {
|
||||
throw new Error('this should not happen!');
|
||||
}
|
||||
|
||||
var bytesread = 0;
|
||||
test.on('readable', function() {
|
||||
var b = len - bytesread - 1;
|
||||
var res = test.read(b);
|
||||
if (res) {
|
||||
bytesread += res.length;
|
||||
console.error('br=%d len=%d', bytesread, len);
|
||||
setTimeout(next);
|
||||
}
|
||||
test.read(0);
|
||||
});
|
||||
test.read(0);
|
||||
|
||||
function next() {
|
||||
// now let's make 'end' happen
|
||||
test.removeListener('end', thrower);
|
||||
|
||||
var endEmitted = false;
|
||||
process.on('exit', function() {
|
||||
assert(endEmitted, 'end should be emitted by now');
|
||||
});
|
||||
test.on('end', function() {
|
||||
endEmitted = true;
|
||||
});
|
||||
|
||||
// one to get the last byte
|
||||
var r = test.read();
|
||||
assert(r);
|
||||
assert.equal(r.length, 1);
|
||||
r = test.read();
|
||||
assert.equal(r, null);
|
||||
}
|
||||
gerado
externo
+312
@@ -0,0 +1,312 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// 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 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.
|
||||
|
||||
|
||||
var common = require('../common.js');
|
||||
var assert = require('assert');
|
||||
var R = require('../../lib/_stream_readable');
|
||||
var util = require('util');
|
||||
|
||||
// tiny node-tap lookalike.
|
||||
var tests = [];
|
||||
var count = 0;
|
||||
|
||||
function test(name, fn) {
|
||||
count++;
|
||||
tests.push([name, fn]);
|
||||
}
|
||||
|
||||
function run() {
|
||||
var next = tests.shift();
|
||||
if (!next)
|
||||
return console.error('ok');
|
||||
|
||||
var name = next[0];
|
||||
var fn = next[1];
|
||||
console.log('# %s', name);
|
||||
fn({
|
||||
same: assert.deepEqual,
|
||||
equal: assert.equal,
|
||||
end: function () {
|
||||
count--;
|
||||
run();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ensure all tests have run
|
||||
process.on("exit", function () {
|
||||
assert.equal(count, 0);
|
||||
});
|
||||
|
||||
process.nextTick(run);
|
||||
|
||||
/////
|
||||
|
||||
util.inherits(TestReader, R);
|
||||
|
||||
function TestReader(n, opts) {
|
||||
R.call(this, opts);
|
||||
|
||||
this.pos = 0;
|
||||
this.len = n || 100;
|
||||
}
|
||||
|
||||
TestReader.prototype._read = function(n) {
|
||||
setTimeout(function() {
|
||||
|
||||
if (this.pos >= this.len) {
|
||||
return this.push(null);
|
||||
}
|
||||
|
||||
n = Math.min(n, this.len - this.pos);
|
||||
if (n <= 0) {
|
||||
return this.push(null);
|
||||
}
|
||||
|
||||
this.pos += n;
|
||||
var ret = new Buffer(n);
|
||||
ret.fill('a');
|
||||
|
||||
console.log("this.push(ret)", ret)
|
||||
|
||||
return this.push(ret);
|
||||
}.bind(this), 1);
|
||||
};
|
||||
|
||||
test('setEncoding utf8', function(t) {
|
||||
var tr = new TestReader(100);
|
||||
tr.setEncoding('utf8');
|
||||
var out = [];
|
||||
var expect =
|
||||
[ 'aaaaaaaaaa',
|
||||
'aaaaaaaaaa',
|
||||
'aaaaaaaaaa',
|
||||
'aaaaaaaaaa',
|
||||
'aaaaaaaaaa',
|
||||
'aaaaaaaaaa',
|
||||
'aaaaaaaaaa',
|
||||
'aaaaaaaaaa',
|
||||
'aaaaaaaaaa',
|
||||
'aaaaaaaaaa' ];
|
||||
|
||||
tr.on('readable', function flow() {
|
||||
var chunk;
|
||||
while (null !== (chunk = tr.read(10)))
|
||||
out.push(chunk);
|
||||
});
|
||||
|
||||
tr.on('end', function() {
|
||||
t.same(out, expect);
|
||||
t.end();
|
||||
});
|
||||
|
||||
// just kick it off.
|
||||
tr.emit('readable');
|
||||
});
|
||||
|
||||
|
||||
test('setEncoding hex', function(t) {
|
||||
var tr = new TestReader(100);
|
||||
tr.setEncoding('hex');
|
||||
var out = [];
|
||||
var expect =
|
||||
[ '6161616161',
|
||||
'6161616161',
|
||||
'6161616161',
|
||||
'6161616161',
|
||||
'6161616161',
|
||||
'6161616161',
|
||||
'6161616161',
|
||||
'6161616161',
|
||||
'6161616161',
|
||||
'6161616161',
|
||||
'6161616161',
|
||||
'6161616161',
|
||||
'6161616161',
|
||||
'6161616161',
|
||||
'6161616161',
|
||||
'6161616161',
|
||||
'6161616161',
|
||||
'6161616161',
|
||||
'6161616161',
|
||||
'6161616161' ];
|
||||
|
||||
tr.on('readable', function flow() {
|
||||
var chunk;
|
||||
while (null !== (chunk = tr.read(10)))
|
||||
out.push(chunk);
|
||||
});
|
||||
|
||||
tr.on('end', function() {
|
||||
t.same(out, expect);
|
||||
t.end();
|
||||
});
|
||||
|
||||
// just kick it off.
|
||||
tr.emit('readable');
|
||||
});
|
||||
|
||||
test('setEncoding hex with read(13)', function(t) {
|
||||
var tr = new TestReader(100);
|
||||
tr.setEncoding('hex');
|
||||
var out = [];
|
||||
var expect =
|
||||
[ "6161616161616",
|
||||
"1616161616161",
|
||||
"6161616161616",
|
||||
"1616161616161",
|
||||
"6161616161616",
|
||||
"1616161616161",
|
||||
"6161616161616",
|
||||
"1616161616161",
|
||||
"6161616161616",
|
||||
"1616161616161",
|
||||
"6161616161616",
|
||||
"1616161616161",
|
||||
"6161616161616",
|
||||
"1616161616161",
|
||||
"6161616161616",
|
||||
"16161" ];
|
||||
|
||||
tr.on('readable', function flow() {
|
||||
console.log("readable once")
|
||||
var chunk;
|
||||
while (null !== (chunk = tr.read(13)))
|
||||
out.push(chunk);
|
||||
});
|
||||
|
||||
tr.on('end', function() {
|
||||
console.log("END")
|
||||
t.same(out, expect);
|
||||
t.end();
|
||||
});
|
||||
|
||||
// just kick it off.
|
||||
tr.emit('readable');
|
||||
});
|
||||
|
||||
test('encoding: utf8', function(t) {
|
||||
var tr = new TestReader(100, { encoding: 'utf8' });
|
||||
var out = [];
|
||||
var expect =
|
||||
[ 'aaaaaaaaaa',
|
||||
'aaaaaaaaaa',
|
||||
'aaaaaaaaaa',
|
||||
'aaaaaaaaaa',
|
||||
'aaaaaaaaaa',
|
||||
'aaaaaaaaaa',
|
||||
'aaaaaaaaaa',
|
||||
'aaaaaaaaaa',
|
||||
'aaaaaaaaaa',
|
||||
'aaaaaaaaaa' ];
|
||||
|
||||
tr.on('readable', function flow() {
|
||||
var chunk;
|
||||
while (null !== (chunk = tr.read(10)))
|
||||
out.push(chunk);
|
||||
});
|
||||
|
||||
tr.on('end', function() {
|
||||
t.same(out, expect);
|
||||
t.end();
|
||||
});
|
||||
|
||||
// just kick it off.
|
||||
tr.emit('readable');
|
||||
});
|
||||
|
||||
|
||||
test('encoding: hex', function(t) {
|
||||
var tr = new TestReader(100, { encoding: 'hex' });
|
||||
var out = [];
|
||||
var expect =
|
||||
[ '6161616161',
|
||||
'6161616161',
|
||||
'6161616161',
|
||||
'6161616161',
|
||||
'6161616161',
|
||||
'6161616161',
|
||||
'6161616161',
|
||||
'6161616161',
|
||||
'6161616161',
|
||||
'6161616161',
|
||||
'6161616161',
|
||||
'6161616161',
|
||||
'6161616161',
|
||||
'6161616161',
|
||||
'6161616161',
|
||||
'6161616161',
|
||||
'6161616161',
|
||||
'6161616161',
|
||||
'6161616161',
|
||||
'6161616161' ];
|
||||
|
||||
tr.on('readable', function flow() {
|
||||
var chunk;
|
||||
while (null !== (chunk = tr.read(10)))
|
||||
out.push(chunk);
|
||||
});
|
||||
|
||||
tr.on('end', function() {
|
||||
t.same(out, expect);
|
||||
t.end();
|
||||
});
|
||||
|
||||
// just kick it off.
|
||||
tr.emit('readable');
|
||||
});
|
||||
|
||||
test('encoding: hex with read(13)', function(t) {
|
||||
var tr = new TestReader(100, { encoding: 'hex' });
|
||||
var out = [];
|
||||
var expect =
|
||||
[ "6161616161616",
|
||||
"1616161616161",
|
||||
"6161616161616",
|
||||
"1616161616161",
|
||||
"6161616161616",
|
||||
"1616161616161",
|
||||
"6161616161616",
|
||||
"1616161616161",
|
||||
"6161616161616",
|
||||
"1616161616161",
|
||||
"6161616161616",
|
||||
"1616161616161",
|
||||
"6161616161616",
|
||||
"1616161616161",
|
||||
"6161616161616",
|
||||
"16161" ];
|
||||
|
||||
tr.on('readable', function flow() {
|
||||
var chunk;
|
||||
while (null !== (chunk = tr.read(13)))
|
||||
out.push(chunk);
|
||||
});
|
||||
|
||||
tr.on('end', function() {
|
||||
t.same(out, expect);
|
||||
t.end();
|
||||
});
|
||||
|
||||
// just kick it off.
|
||||
tr.emit('readable');
|
||||
});
|
||||
gerado
externo
+435
@@ -0,0 +1,435 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// 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 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.
|
||||
|
||||
var assert = require('assert');
|
||||
var common = require('../common.js');
|
||||
var PassThrough = require('../../lib/_stream_passthrough');
|
||||
var Transform = require('../../lib/_stream_transform');
|
||||
|
||||
// tiny node-tap lookalike.
|
||||
var tests = [];
|
||||
var count = 0;
|
||||
|
||||
function test(name, fn) {
|
||||
count++;
|
||||
tests.push([name, fn]);
|
||||
}
|
||||
|
||||
function run() {
|
||||
var next = tests.shift();
|
||||
if (!next)
|
||||
return console.error('ok');
|
||||
|
||||
var name = next[0];
|
||||
var fn = next[1];
|
||||
console.log('# %s', name);
|
||||
fn({
|
||||
same: assert.deepEqual,
|
||||
equal: assert.equal,
|
||||
ok: assert,
|
||||
end: function () {
|
||||
count--;
|
||||
run();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ensure all tests have run
|
||||
process.on("exit", function () {
|
||||
assert.equal(count, 0);
|
||||
});
|
||||
|
||||
process.nextTick(run);
|
||||
|
||||
/////
|
||||
|
||||
test('writable side consumption', function(t) {
|
||||
var tx = new Transform({
|
||||
highWaterMark: 10
|
||||
});
|
||||
|
||||
var transformed = 0;
|
||||
tx._transform = function(chunk, encoding, cb) {
|
||||
transformed += chunk.length;
|
||||
tx.push(chunk);
|
||||
cb();
|
||||
};
|
||||
|
||||
for (var i = 1; i <= 10; i++) {
|
||||
tx.write(new Buffer(i));
|
||||
}
|
||||
tx.end();
|
||||
|
||||
t.equal(tx._readableState.length, 10);
|
||||
t.equal(transformed, 10);
|
||||
t.equal(tx._transformState.writechunk.length, 5);
|
||||
t.same(tx._writableState.buffer.map(function(c) {
|
||||
return c.chunk.length;
|
||||
}), [6, 7, 8, 9, 10]);
|
||||
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('passthrough', function(t) {
|
||||
var pt = new PassThrough();
|
||||
|
||||
pt.write(new Buffer('foog'));
|
||||
pt.write(new Buffer('bark'));
|
||||
pt.write(new Buffer('bazy'));
|
||||
pt.write(new Buffer('kuel'));
|
||||
pt.end();
|
||||
|
||||
t.equal(pt.read(5).toString(), 'foogb');
|
||||
t.equal(pt.read(5).toString(), 'arkba');
|
||||
t.equal(pt.read(5).toString(), 'zykue');
|
||||
t.equal(pt.read(5).toString(), 'l');
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('simple transform', function(t) {
|
||||
var pt = new Transform;
|
||||
pt._transform = function(c, e, cb) {
|
||||
var ret = new Buffer(c.length);
|
||||
ret.fill('x');
|
||||
pt.push(ret);
|
||||
cb();
|
||||
};
|
||||
|
||||
pt.write(new Buffer('foog'));
|
||||
pt.write(new Buffer('bark'));
|
||||
pt.write(new Buffer('bazy'));
|
||||
pt.write(new Buffer('kuel'));
|
||||
pt.end();
|
||||
|
||||
t.equal(pt.read(5).toString(), 'xxxxx');
|
||||
t.equal(pt.read(5).toString(), 'xxxxx');
|
||||
t.equal(pt.read(5).toString(), 'xxxxx');
|
||||
t.equal(pt.read(5).toString(), 'x');
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('async passthrough', function(t) {
|
||||
var pt = new Transform;
|
||||
pt._transform = function(chunk, encoding, cb) {
|
||||
setTimeout(function() {
|
||||
pt.push(chunk);
|
||||
cb();
|
||||
}, 10);
|
||||
};
|
||||
|
||||
pt.write(new Buffer('foog'));
|
||||
pt.write(new Buffer('bark'));
|
||||
pt.write(new Buffer('bazy'));
|
||||
pt.write(new Buffer('kuel'));
|
||||
pt.end();
|
||||
|
||||
pt.on('finish', function() {
|
||||
t.equal(pt.read(5).toString(), 'foogb');
|
||||
t.equal(pt.read(5).toString(), 'arkba');
|
||||
t.equal(pt.read(5).toString(), 'zykue');
|
||||
t.equal(pt.read(5).toString(), 'l');
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
test('assymetric transform (expand)', function(t) {
|
||||
var pt = new Transform;
|
||||
|
||||
// emit each chunk 2 times.
|
||||
pt._transform = function(chunk, encoding, cb) {
|
||||
setTimeout(function() {
|
||||
pt.push(chunk);
|
||||
setTimeout(function() {
|
||||
pt.push(chunk);
|
||||
cb();
|
||||
}, 10)
|
||||
}, 10);
|
||||
};
|
||||
|
||||
pt.write(new Buffer('foog'));
|
||||
pt.write(new Buffer('bark'));
|
||||
pt.write(new Buffer('bazy'));
|
||||
pt.write(new Buffer('kuel'));
|
||||
pt.end();
|
||||
|
||||
pt.on('finish', function() {
|
||||
t.equal(pt.read(5).toString(), 'foogf');
|
||||
t.equal(pt.read(5).toString(), 'oogba');
|
||||
t.equal(pt.read(5).toString(), 'rkbar');
|
||||
t.equal(pt.read(5).toString(), 'kbazy');
|
||||
t.equal(pt.read(5).toString(), 'bazyk');
|
||||
t.equal(pt.read(5).toString(), 'uelku');
|
||||
t.equal(pt.read(5).toString(), 'el');
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
test('assymetric transform (compress)', function(t) {
|
||||
var pt = new Transform;
|
||||
|
||||
// each output is the first char of 3 consecutive chunks,
|
||||
// or whatever's left.
|
||||
pt.state = '';
|
||||
|
||||
pt._transform = function(chunk, encoding, cb) {
|
||||
if (!chunk)
|
||||
chunk = '';
|
||||
var s = chunk.toString();
|
||||
setTimeout(function() {
|
||||
this.state += s.charAt(0);
|
||||
if (this.state.length === 3) {
|
||||
pt.push(new Buffer(this.state));
|
||||
this.state = '';
|
||||
}
|
||||
cb();
|
||||
}.bind(this), 10);
|
||||
};
|
||||
|
||||
pt._flush = function(cb) {
|
||||
// just output whatever we have.
|
||||
pt.push(new Buffer(this.state));
|
||||
this.state = '';
|
||||
cb();
|
||||
};
|
||||
|
||||
pt.write(new Buffer('aaaa'));
|
||||
pt.write(new Buffer('bbbb'));
|
||||
pt.write(new Buffer('cccc'));
|
||||
pt.write(new Buffer('dddd'));
|
||||
pt.write(new Buffer('eeee'));
|
||||
pt.write(new Buffer('aaaa'));
|
||||
pt.write(new Buffer('bbbb'));
|
||||
pt.write(new Buffer('cccc'));
|
||||
pt.write(new Buffer('dddd'));
|
||||
pt.write(new Buffer('eeee'));
|
||||
pt.write(new Buffer('aaaa'));
|
||||
pt.write(new Buffer('bbbb'));
|
||||
pt.write(new Buffer('cccc'));
|
||||
pt.write(new Buffer('dddd'));
|
||||
pt.end();
|
||||
|
||||
// 'abcdeabcdeabcd'
|
||||
pt.on('finish', function() {
|
||||
t.equal(pt.read(5).toString(), 'abcde');
|
||||
t.equal(pt.read(5).toString(), 'abcde');
|
||||
t.equal(pt.read(5).toString(), 'abcd');
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
test('passthrough event emission', function(t) {
|
||||
var pt = new PassThrough();
|
||||
var emits = 0;
|
||||
pt.on('readable', function() {
|
||||
var state = pt._readableState;
|
||||
console.error('>>> emit readable %d', emits);
|
||||
emits++;
|
||||
});
|
||||
|
||||
var i = 0;
|
||||
|
||||
pt.write(new Buffer('foog'));
|
||||
|
||||
console.error('need emit 0');
|
||||
pt.write(new Buffer('bark'));
|
||||
|
||||
console.error('should have emitted readable now 1 === %d', emits);
|
||||
t.equal(emits, 1);
|
||||
|
||||
t.equal(pt.read(5).toString(), 'foogb');
|
||||
t.equal(pt.read(5) + '', 'null');
|
||||
|
||||
console.error('need emit 1');
|
||||
|
||||
pt.write(new Buffer('bazy'));
|
||||
console.error('should have emitted, but not again');
|
||||
pt.write(new Buffer('kuel'));
|
||||
|
||||
console.error('should have emitted readable now 2 === %d', emits);
|
||||
t.equal(emits, 2);
|
||||
|
||||
t.equal(pt.read(5).toString(), 'arkba');
|
||||
t.equal(pt.read(5).toString(), 'zykue');
|
||||
t.equal(pt.read(5), null);
|
||||
|
||||
console.error('need emit 2');
|
||||
|
||||
pt.end();
|
||||
|
||||
t.equal(emits, 3);
|
||||
|
||||
t.equal(pt.read(5).toString(), 'l');
|
||||
t.equal(pt.read(5), null);
|
||||
|
||||
console.error('should not have emitted again');
|
||||
t.equal(emits, 3);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('passthrough event emission reordered', function(t) {
|
||||
var pt = new PassThrough;
|
||||
var emits = 0;
|
||||
pt.on('readable', function() {
|
||||
console.error('emit readable', emits)
|
||||
emits++;
|
||||
});
|
||||
|
||||
pt.write(new Buffer('foog'));
|
||||
console.error('need emit 0');
|
||||
pt.write(new Buffer('bark'));
|
||||
console.error('should have emitted readable now 1 === %d', emits);
|
||||
t.equal(emits, 1);
|
||||
|
||||
t.equal(pt.read(5).toString(), 'foogb');
|
||||
t.equal(pt.read(5), null);
|
||||
|
||||
console.error('need emit 1');
|
||||
pt.once('readable', function() {
|
||||
t.equal(pt.read(5).toString(), 'arkba');
|
||||
|
||||
t.equal(pt.read(5), null);
|
||||
|
||||
console.error('need emit 2');
|
||||
pt.once('readable', function() {
|
||||
t.equal(pt.read(5).toString(), 'zykue');
|
||||
t.equal(pt.read(5), null);
|
||||
pt.once('readable', function() {
|
||||
t.equal(pt.read(5).toString(), 'l');
|
||||
t.equal(pt.read(5), null);
|
||||
t.equal(emits, 4);
|
||||
t.end();
|
||||
});
|
||||
pt.end();
|
||||
});
|
||||
pt.write(new Buffer('kuel'));
|
||||
});
|
||||
|
||||
pt.write(new Buffer('bazy'));
|
||||
});
|
||||
|
||||
test('passthrough facaded', function(t) {
|
||||
console.error('passthrough facaded');
|
||||
var pt = new PassThrough;
|
||||
var datas = [];
|
||||
pt.on('data', function(chunk) {
|
||||
datas.push(chunk.toString());
|
||||
});
|
||||
|
||||
pt.on('end', function() {
|
||||
t.same(datas, ['foog', 'bark', 'bazy', 'kuel']);
|
||||
t.end();
|
||||
});
|
||||
|
||||
pt.write(new Buffer('foog'));
|
||||
setTimeout(function() {
|
||||
pt.write(new Buffer('bark'));
|
||||
setTimeout(function() {
|
||||
pt.write(new Buffer('bazy'));
|
||||
setTimeout(function() {
|
||||
pt.write(new Buffer('kuel'));
|
||||
setTimeout(function() {
|
||||
pt.end();
|
||||
}, 10);
|
||||
}, 10);
|
||||
}, 10);
|
||||
}, 10);
|
||||
});
|
||||
|
||||
test('object transform (json parse)', function(t) {
|
||||
console.error('json parse stream');
|
||||
var jp = new Transform({ objectMode: true });
|
||||
jp._transform = function(data, encoding, cb) {
|
||||
try {
|
||||
jp.push(JSON.parse(data));
|
||||
cb();
|
||||
} catch (er) {
|
||||
cb(er);
|
||||
}
|
||||
};
|
||||
|
||||
// anything except null/undefined is fine.
|
||||
// those are "magic" in the stream API, because they signal EOF.
|
||||
var objects = [
|
||||
{ foo: 'bar' },
|
||||
100,
|
||||
"string",
|
||||
{ nested: { things: [ { foo: 'bar' }, 100, "string" ] } }
|
||||
];
|
||||
|
||||
var ended = false;
|
||||
jp.on('end', function() {
|
||||
ended = true;
|
||||
});
|
||||
|
||||
objects.forEach(function(obj) {
|
||||
jp.write(JSON.stringify(obj));
|
||||
var res = jp.read();
|
||||
t.same(res, obj);
|
||||
});
|
||||
|
||||
jp.end();
|
||||
|
||||
process.nextTick(function() {
|
||||
t.ok(ended);
|
||||
t.end();
|
||||
})
|
||||
});
|
||||
|
||||
test('object transform (json stringify)', function(t) {
|
||||
console.error('json parse stream');
|
||||
var js = new Transform({ objectMode: true });
|
||||
js._transform = function(data, encoding, cb) {
|
||||
try {
|
||||
js.push(JSON.stringify(data));
|
||||
cb();
|
||||
} catch (er) {
|
||||
cb(er);
|
||||
}
|
||||
};
|
||||
|
||||
// anything except null/undefined is fine.
|
||||
// those are "magic" in the stream API, because they signal EOF.
|
||||
var objects = [
|
||||
{ foo: 'bar' },
|
||||
100,
|
||||
"string",
|
||||
{ nested: { things: [ { foo: 'bar' }, 100, "string" ] } }
|
||||
];
|
||||
|
||||
var ended = false;
|
||||
js.on('end', function() {
|
||||
ended = true;
|
||||
});
|
||||
|
||||
objects.forEach(function(obj) {
|
||||
js.write(obj);
|
||||
var res = js.read();
|
||||
t.equal(res, JSON.stringify(obj));
|
||||
});
|
||||
|
||||
js.end();
|
||||
|
||||
process.nextTick(function() {
|
||||
t.ok(ended);
|
||||
t.end();
|
||||
})
|
||||
});
|
||||
gerado
externo
+76
@@ -0,0 +1,76 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// 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 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.
|
||||
|
||||
|
||||
var common = require('../common.js');
|
||||
var assert = require('assert');
|
||||
var stream = require('../../readable');
|
||||
var crypto = require('crypto');
|
||||
|
||||
var util = require('util');
|
||||
|
||||
function TestWriter() {
|
||||
stream.Writable.call(this);
|
||||
}
|
||||
util.inherits(TestWriter, stream.Writable);
|
||||
|
||||
TestWriter.prototype._write = function (buffer, encoding, callback) {
|
||||
console.log('write called');
|
||||
// super slow write stream (callback never called)
|
||||
};
|
||||
|
||||
var dest = new TestWriter();
|
||||
|
||||
function TestReader(id) {
|
||||
stream.Readable.call(this);
|
||||
this.reads = 0;
|
||||
}
|
||||
util.inherits(TestReader, stream.Readable);
|
||||
|
||||
TestReader.prototype._read = function (size) {
|
||||
this.reads += 1;
|
||||
this.push(crypto.randomBytes(size));
|
||||
};
|
||||
|
||||
var src1 = new TestReader();
|
||||
var src2 = new TestReader();
|
||||
|
||||
src1.pipe(dest);
|
||||
|
||||
src1.once('readable', function () {
|
||||
process.nextTick(function () {
|
||||
|
||||
src2.pipe(dest);
|
||||
|
||||
src2.once('readable', function () {
|
||||
process.nextTick(function () {
|
||||
|
||||
src1.unpipe(dest);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
process.on('exit', function () {
|
||||
assert.equal(src1.reads, 2);
|
||||
assert.equal(src2.reads, 2);
|
||||
});
|
||||
gerado
externo
+74
@@ -0,0 +1,74 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// 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 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.
|
||||
|
||||
|
||||
var common = require('../common.js');
|
||||
var assert = require('assert');
|
||||
var stream = require('../../readable');
|
||||
|
||||
var chunk = new Buffer('hallo');
|
||||
|
||||
var util = require('util');
|
||||
|
||||
function TestWriter() {
|
||||
stream.Writable.call(this);
|
||||
}
|
||||
util.inherits(TestWriter, stream.Writable);
|
||||
|
||||
TestWriter.prototype._write = function(buffer, encoding, callback) {
|
||||
callback(null);
|
||||
};
|
||||
|
||||
var dest = new TestWriter();
|
||||
|
||||
// Set this high so that we'd trigger a nextTick warning
|
||||
// and/or RangeError if we do maybeReadMore wrong.
|
||||
function TestReader() {
|
||||
stream.Readable.call(this, { highWaterMark: 0x10000 });
|
||||
}
|
||||
util.inherits(TestReader, stream.Readable);
|
||||
|
||||
TestReader.prototype._read = function(size) {
|
||||
this.push(chunk);
|
||||
};
|
||||
|
||||
var src = new TestReader();
|
||||
|
||||
for (var i = 0; i < 10; i++) {
|
||||
src.pipe(dest);
|
||||
src.unpipe(dest);
|
||||
}
|
||||
|
||||
assert.equal(src.listeners('end').length, 0);
|
||||
assert.equal(src.listeners('readable').length, 0);
|
||||
|
||||
assert.equal(dest.listeners('unpipe').length, 0);
|
||||
assert.equal(dest.listeners('drain').length, 0);
|
||||
assert.equal(dest.listeners('error').length, 0);
|
||||
assert.equal(dest.listeners('close').length, 0);
|
||||
assert.equal(dest.listeners('finish').length, 0);
|
||||
|
||||
console.error(src._readableState);
|
||||
process.on('exit', function() {
|
||||
assert(src._readableState.length >= src._readableState.highWaterMark);
|
||||
src._readableState.buffer.length = 0;
|
||||
console.error(src._readableState);
|
||||
});
|
||||
gerado
externo
+328
@@ -0,0 +1,328 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// 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 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.
|
||||
|
||||
var common = require('../common.js');
|
||||
var W = require('../../lib/_stream_writable');
|
||||
var D = require('../../lib/_stream_duplex');
|
||||
var assert = require('assert');
|
||||
|
||||
var util = require('util');
|
||||
util.inherits(TestWriter, W);
|
||||
|
||||
function TestWriter() {
|
||||
W.apply(this, arguments);
|
||||
this.buffer = [];
|
||||
this.written = 0;
|
||||
}
|
||||
|
||||
TestWriter.prototype._write = function(chunk, encoding, cb) {
|
||||
// simulate a small unpredictable latency
|
||||
setTimeout(function() {
|
||||
this.buffer.push(chunk.toString());
|
||||
this.written += chunk.length;
|
||||
cb();
|
||||
}.bind(this), Math.floor(Math.random() * 10));
|
||||
};
|
||||
|
||||
var chunks = new Array(50);
|
||||
for (var i = 0; i < chunks.length; i++) {
|
||||
chunks[i] = new Array(i + 1).join('x');
|
||||
}
|
||||
|
||||
// tiny node-tap lookalike.
|
||||
var tests = [];
|
||||
var count = 0;
|
||||
|
||||
function test(name, fn) {
|
||||
count++;
|
||||
tests.push([name, fn]);
|
||||
}
|
||||
|
||||
function run() {
|
||||
var next = tests.shift();
|
||||
if (!next)
|
||||
return console.error('ok');
|
||||
|
||||
var name = next[0];
|
||||
var fn = next[1];
|
||||
console.log('# %s', name);
|
||||
fn({
|
||||
same: assert.deepEqual,
|
||||
equal: assert.equal,
|
||||
end: function () {
|
||||
count--;
|
||||
run();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ensure all tests have run
|
||||
process.on("exit", function () {
|
||||
assert.equal(count, 0);
|
||||
});
|
||||
|
||||
process.nextTick(run);
|
||||
|
||||
test('write fast', function(t) {
|
||||
var tw = new TestWriter({
|
||||
highWaterMark: 100
|
||||
});
|
||||
|
||||
tw.on('finish', function() {
|
||||
t.same(tw.buffer, chunks, 'got chunks in the right order');
|
||||
t.end();
|
||||
});
|
||||
|
||||
chunks.forEach(function(chunk) {
|
||||
// screw backpressure. Just buffer it all up.
|
||||
tw.write(chunk);
|
||||
});
|
||||
tw.end();
|
||||
});
|
||||
|
||||
test('write slow', function(t) {
|
||||
var tw = new TestWriter({
|
||||
highWaterMark: 100
|
||||
});
|
||||
|
||||
tw.on('finish', function() {
|
||||
t.same(tw.buffer, chunks, 'got chunks in the right order');
|
||||
t.end();
|
||||
});
|
||||
|
||||
var i = 0;
|
||||
(function W() {
|
||||
tw.write(chunks[i++]);
|
||||
if (i < chunks.length)
|
||||
setTimeout(W, 10);
|
||||
else
|
||||
tw.end();
|
||||
})();
|
||||
});
|
||||
|
||||
test('write backpressure', function(t) {
|
||||
var tw = new TestWriter({
|
||||
highWaterMark: 50
|
||||
});
|
||||
|
||||
var drains = 0;
|
||||
|
||||
tw.on('finish', function() {
|
||||
t.same(tw.buffer, chunks, 'got chunks in the right order');
|
||||
t.equal(drains, 17);
|
||||
t.end();
|
||||
});
|
||||
|
||||
tw.on('drain', function() {
|
||||
drains++;
|
||||
});
|
||||
|
||||
var i = 0;
|
||||
(function W() {
|
||||
do {
|
||||
var ret = tw.write(chunks[i++]);
|
||||
} while (ret !== false && i < chunks.length);
|
||||
|
||||
if (i < chunks.length) {
|
||||
assert(tw._writableState.length >= 50);
|
||||
tw.once('drain', W);
|
||||
} else {
|
||||
tw.end();
|
||||
}
|
||||
})();
|
||||
});
|
||||
|
||||
test('write bufferize', function(t) {
|
||||
var tw = new TestWriter({
|
||||
highWaterMark: 100
|
||||
});
|
||||
|
||||
var encodings =
|
||||
[ 'hex',
|
||||
'utf8',
|
||||
'utf-8',
|
||||
'ascii',
|
||||
'binary',
|
||||
'base64',
|
||||
'ucs2',
|
||||
'ucs-2',
|
||||
'utf16le',
|
||||
'utf-16le',
|
||||
undefined ];
|
||||
|
||||
tw.on('finish', function() {
|
||||
t.same(tw.buffer, chunks, 'got the expected chunks');
|
||||
});
|
||||
|
||||
chunks.forEach(function(chunk, i) {
|
||||
var enc = encodings[ i % encodings.length ];
|
||||
chunk = new Buffer(chunk);
|
||||
tw.write(chunk.toString(enc), enc);
|
||||
});
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('write no bufferize', function(t) {
|
||||
var tw = new TestWriter({
|
||||
highWaterMark: 100,
|
||||
decodeStrings: false
|
||||
});
|
||||
|
||||
tw._write = function(chunk, encoding, cb) {
|
||||
assert(typeof chunk === 'string');
|
||||
chunk = new Buffer(chunk, encoding);
|
||||
return TestWriter.prototype._write.call(this, chunk, encoding, cb);
|
||||
};
|
||||
|
||||
var encodings =
|
||||
[ 'hex',
|
||||
'utf8',
|
||||
'utf-8',
|
||||
'ascii',
|
||||
'binary',
|
||||
'base64',
|
||||
'ucs2',
|
||||
'ucs-2',
|
||||
'utf16le',
|
||||
'utf-16le',
|
||||
undefined ];
|
||||
|
||||
tw.on('finish', function() {
|
||||
t.same(tw.buffer, chunks, 'got the expected chunks');
|
||||
});
|
||||
|
||||
chunks.forEach(function(chunk, i) {
|
||||
var enc = encodings[ i % encodings.length ];
|
||||
chunk = new Buffer(chunk);
|
||||
tw.write(chunk.toString(enc), enc);
|
||||
});
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('write callbacks', function (t) {
|
||||
var callbacks = chunks.map(function(chunk, i) {
|
||||
return [i, function(er) {
|
||||
callbacks._called[i] = chunk;
|
||||
}];
|
||||
}).reduce(function(set, x) {
|
||||
set['callback-' + x[0]] = x[1];
|
||||
return set;
|
||||
}, {});
|
||||
callbacks._called = [];
|
||||
|
||||
var tw = new TestWriter({
|
||||
highWaterMark: 100
|
||||
});
|
||||
|
||||
tw.on('finish', function() {
|
||||
process.nextTick(function() {
|
||||
t.same(tw.buffer, chunks, 'got chunks in the right order');
|
||||
t.same(callbacks._called, chunks, 'called all callbacks');
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
chunks.forEach(function(chunk, i) {
|
||||
tw.write(chunk, callbacks['callback-' + i]);
|
||||
});
|
||||
tw.end();
|
||||
});
|
||||
|
||||
test('end callback', function (t) {
|
||||
var tw = new TestWriter();
|
||||
tw.end(function () {
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
test('end callback with chunk', function (t) {
|
||||
var tw = new TestWriter();
|
||||
tw.end(new Buffer('hello world'), function () {
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
test('end callback with chunk and encoding', function (t) {
|
||||
var tw = new TestWriter();
|
||||
tw.end('hello world', 'ascii', function () {
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
test('end callback after .write() call', function (t) {
|
||||
var tw = new TestWriter();
|
||||
tw.write(new Buffer('hello world'));
|
||||
tw.end(function () {
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
|
||||
test('encoding should be ignored for buffers', function(t) {
|
||||
var tw = new W();
|
||||
var hex = '018b5e9a8f6236ffe30e31baf80d2cf6eb';
|
||||
tw._write = function(chunk, encoding, cb) {
|
||||
t.equal(chunk.toString('hex'), hex);
|
||||
t.end();
|
||||
};
|
||||
var buf = new Buffer(hex, 'hex');
|
||||
tw.write(buf, 'binary');
|
||||
});
|
||||
|
||||
test('writables are not pipable', function(t) {
|
||||
var w = new W();
|
||||
w._write = function() {};
|
||||
var gotError = false;
|
||||
w.on('error', function(er) {
|
||||
gotError = true;
|
||||
});
|
||||
w.pipe(process.stdout);
|
||||
assert(gotError);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('duplexes are pipable', function(t) {
|
||||
var d = new D();
|
||||
d._read = function() {};
|
||||
d._write = function() {};
|
||||
var gotError = false;
|
||||
d.on('error', function(er) {
|
||||
gotError = true;
|
||||
});
|
||||
d.pipe(process.stdout);
|
||||
assert(!gotError);
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('end(chunk) two times is an error', function(t) {
|
||||
var w = new W();
|
||||
w._write = function() {};
|
||||
var gotError = false;
|
||||
w.on('error', function(er) {
|
||||
gotError = true;
|
||||
t.equal(er.message, 'write after end');
|
||||
});
|
||||
w.end('this is the end');
|
||||
w.end('and so is this');
|
||||
process.nextTick(function() {
|
||||
assert(gotError);
|
||||
t.end();
|
||||
});
|
||||
});
|
||||
gerado
externo
+1
@@ -0,0 +1 @@
|
||||
module.exports = require("./lib/_stream_transform.js")
|
||||
gerado
externo
+1
@@ -0,0 +1 @@
|
||||
module.exports = require("./lib/_stream_writable.js")
|
||||
gerado
externo
+452
@@ -0,0 +1,452 @@
|
||||
// Copyright Joyent, Inc. and other Node contributors.
|
||||
//
|
||||
// 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 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.
|
||||
|
||||
var Transform = require('./lib/_stream_transform.js');
|
||||
|
||||
var binding = process.binding('zlib');
|
||||
var util = require('util');
|
||||
var assert = require('assert').ok;
|
||||
|
||||
// zlib doesn't provide these, so kludge them in following the same
|
||||
// const naming scheme zlib uses.
|
||||
binding.Z_MIN_WINDOWBITS = 8;
|
||||
binding.Z_MAX_WINDOWBITS = 15;
|
||||
binding.Z_DEFAULT_WINDOWBITS = 15;
|
||||
|
||||
// fewer than 64 bytes per chunk is stupid.
|
||||
// technically it could work with as few as 8, but even 64 bytes
|
||||
// is absurdly low. Usually a MB or more is best.
|
||||
binding.Z_MIN_CHUNK = 64;
|
||||
binding.Z_MAX_CHUNK = Infinity;
|
||||
binding.Z_DEFAULT_CHUNK = (16 * 1024);
|
||||
|
||||
binding.Z_MIN_MEMLEVEL = 1;
|
||||
binding.Z_MAX_MEMLEVEL = 9;
|
||||
binding.Z_DEFAULT_MEMLEVEL = 8;
|
||||
|
||||
binding.Z_MIN_LEVEL = -1;
|
||||
binding.Z_MAX_LEVEL = 9;
|
||||
binding.Z_DEFAULT_LEVEL = binding.Z_DEFAULT_COMPRESSION;
|
||||
|
||||
// expose all the zlib constants
|
||||
Object.keys(binding).forEach(function(k) {
|
||||
if (k.match(/^Z/)) exports[k] = binding[k];
|
||||
});
|
||||
|
||||
// translation table for return codes.
|
||||
exports.codes = {
|
||||
Z_OK: binding.Z_OK,
|
||||
Z_STREAM_END: binding.Z_STREAM_END,
|
||||
Z_NEED_DICT: binding.Z_NEED_DICT,
|
||||
Z_ERRNO: binding.Z_ERRNO,
|
||||
Z_STREAM_ERROR: binding.Z_STREAM_ERROR,
|
||||
Z_DATA_ERROR: binding.Z_DATA_ERROR,
|
||||
Z_MEM_ERROR: binding.Z_MEM_ERROR,
|
||||
Z_BUF_ERROR: binding.Z_BUF_ERROR,
|
||||
Z_VERSION_ERROR: binding.Z_VERSION_ERROR
|
||||
};
|
||||
|
||||
Object.keys(exports.codes).forEach(function(k) {
|
||||
exports.codes[exports.codes[k]] = k;
|
||||
});
|
||||
|
||||
exports.Deflate = Deflate;
|
||||
exports.Inflate = Inflate;
|
||||
exports.Gzip = Gzip;
|
||||
exports.Gunzip = Gunzip;
|
||||
exports.DeflateRaw = DeflateRaw;
|
||||
exports.InflateRaw = InflateRaw;
|
||||
exports.Unzip = Unzip;
|
||||
|
||||
exports.createDeflate = function(o) {
|
||||
return new Deflate(o);
|
||||
};
|
||||
|
||||
exports.createInflate = function(o) {
|
||||
return new Inflate(o);
|
||||
};
|
||||
|
||||
exports.createDeflateRaw = function(o) {
|
||||
return new DeflateRaw(o);
|
||||
};
|
||||
|
||||
exports.createInflateRaw = function(o) {
|
||||
return new InflateRaw(o);
|
||||
};
|
||||
|
||||
exports.createGzip = function(o) {
|
||||
return new Gzip(o);
|
||||
};
|
||||
|
||||
exports.createGunzip = function(o) {
|
||||
return new Gunzip(o);
|
||||
};
|
||||
|
||||
exports.createUnzip = function(o) {
|
||||
return new Unzip(o);
|
||||
};
|
||||
|
||||
|
||||
// Convenience methods.
|
||||
// compress/decompress a string or buffer in one step.
|
||||
exports.deflate = function(buffer, callback) {
|
||||
zlibBuffer(new Deflate(), buffer, callback);
|
||||
};
|
||||
|
||||
exports.gzip = function(buffer, callback) {
|
||||
zlibBuffer(new Gzip(), buffer, callback);
|
||||
};
|
||||
|
||||
exports.deflateRaw = function(buffer, callback) {
|
||||
zlibBuffer(new DeflateRaw(), buffer, callback);
|
||||
};
|
||||
|
||||
exports.unzip = function(buffer, callback) {
|
||||
zlibBuffer(new Unzip(), buffer, callback);
|
||||
};
|
||||
|
||||
exports.inflate = function(buffer, callback) {
|
||||
zlibBuffer(new Inflate(), buffer, callback);
|
||||
};
|
||||
|
||||
exports.gunzip = function(buffer, callback) {
|
||||
zlibBuffer(new Gunzip(), buffer, callback);
|
||||
};
|
||||
|
||||
exports.inflateRaw = function(buffer, callback) {
|
||||
zlibBuffer(new InflateRaw(), buffer, callback);
|
||||
};
|
||||
|
||||
function zlibBuffer(engine, buffer, callback) {
|
||||
var buffers = [];
|
||||
var nread = 0;
|
||||
|
||||
engine.on('error', onError);
|
||||
engine.on('end', onEnd);
|
||||
|
||||
engine.end(buffer);
|
||||
flow();
|
||||
|
||||
function flow() {
|
||||
var chunk;
|
||||
while (null !== (chunk = engine.read())) {
|
||||
buffers.push(chunk);
|
||||
nread += chunk.length;
|
||||
}
|
||||
engine.once('readable', flow);
|
||||
}
|
||||
|
||||
function onError(err) {
|
||||
engine.removeListener('end', onEnd);
|
||||
engine.removeListener('readable', flow);
|
||||
callback(err);
|
||||
}
|
||||
|
||||
function onEnd() {
|
||||
var buf = Buffer.concat(buffers, nread);
|
||||
buffers = [];
|
||||
callback(null, buf);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// generic zlib
|
||||
// minimal 2-byte header
|
||||
function Deflate(opts) {
|
||||
if (!(this instanceof Deflate)) return new Deflate(opts);
|
||||
Zlib.call(this, opts, binding.DEFLATE);
|
||||
}
|
||||
|
||||
function Inflate(opts) {
|
||||
if (!(this instanceof Inflate)) return new Inflate(opts);
|
||||
Zlib.call(this, opts, binding.INFLATE);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// gzip - bigger header, same deflate compression
|
||||
function Gzip(opts) {
|
||||
if (!(this instanceof Gzip)) return new Gzip(opts);
|
||||
Zlib.call(this, opts, binding.GZIP);
|
||||
}
|
||||
|
||||
function Gunzip(opts) {
|
||||
if (!(this instanceof Gunzip)) return new Gunzip(opts);
|
||||
Zlib.call(this, opts, binding.GUNZIP);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// raw - no header
|
||||
function DeflateRaw(opts) {
|
||||
if (!(this instanceof DeflateRaw)) return new DeflateRaw(opts);
|
||||
Zlib.call(this, opts, binding.DEFLATERAW);
|
||||
}
|
||||
|
||||
function InflateRaw(opts) {
|
||||
if (!(this instanceof InflateRaw)) return new InflateRaw(opts);
|
||||
Zlib.call(this, opts, binding.INFLATERAW);
|
||||
}
|
||||
|
||||
|
||||
// auto-detect header.
|
||||
function Unzip(opts) {
|
||||
if (!(this instanceof Unzip)) return new Unzip(opts);
|
||||
Zlib.call(this, opts, binding.UNZIP);
|
||||
}
|
||||
|
||||
|
||||
// the Zlib class they all inherit from
|
||||
// This thing manages the queue of requests, and returns
|
||||
// true or false if there is anything in the queue when
|
||||
// you call the .write() method.
|
||||
|
||||
function Zlib(opts, mode) {
|
||||
this._opts = opts = opts || {};
|
||||
this._chunkSize = opts.chunkSize || exports.Z_DEFAULT_CHUNK;
|
||||
|
||||
Transform.call(this, opts);
|
||||
|
||||
// means a different thing there.
|
||||
this._readableState.chunkSize = null;
|
||||
|
||||
if (opts.chunkSize) {
|
||||
if (opts.chunkSize < exports.Z_MIN_CHUNK ||
|
||||
opts.chunkSize > exports.Z_MAX_CHUNK) {
|
||||
throw new Error('Invalid chunk size: ' + opts.chunkSize);
|
||||
}
|
||||
}
|
||||
|
||||
if (opts.windowBits) {
|
||||
if (opts.windowBits < exports.Z_MIN_WINDOWBITS ||
|
||||
opts.windowBits > exports.Z_MAX_WINDOWBITS) {
|
||||
throw new Error('Invalid windowBits: ' + opts.windowBits);
|
||||
}
|
||||
}
|
||||
|
||||
if (opts.level) {
|
||||
if (opts.level < exports.Z_MIN_LEVEL ||
|
||||
opts.level > exports.Z_MAX_LEVEL) {
|
||||
throw new Error('Invalid compression level: ' + opts.level);
|
||||
}
|
||||
}
|
||||
|
||||
if (opts.memLevel) {
|
||||
if (opts.memLevel < exports.Z_MIN_MEMLEVEL ||
|
||||
opts.memLevel > exports.Z_MAX_MEMLEVEL) {
|
||||
throw new Error('Invalid memLevel: ' + opts.memLevel);
|
||||
}
|
||||
}
|
||||
|
||||
if (opts.strategy) {
|
||||
if (opts.strategy != exports.Z_FILTERED &&
|
||||
opts.strategy != exports.Z_HUFFMAN_ONLY &&
|
||||
opts.strategy != exports.Z_RLE &&
|
||||
opts.strategy != exports.Z_FIXED &&
|
||||
opts.strategy != exports.Z_DEFAULT_STRATEGY) {
|
||||
throw new Error('Invalid strategy: ' + opts.strategy);
|
||||
}
|
||||
}
|
||||
|
||||
if (opts.dictionary) {
|
||||
if (!Buffer.isBuffer(opts.dictionary)) {
|
||||
throw new Error('Invalid dictionary: it should be a Buffer instance');
|
||||
}
|
||||
}
|
||||
|
||||
this._binding = new binding.Zlib(mode);
|
||||
|
||||
var self = this;
|
||||
this._hadError = false;
|
||||
this._binding.onerror = function(message, errno) {
|
||||
// there is no way to cleanly recover.
|
||||
// continuing only obscures problems.
|
||||
self._binding = null;
|
||||
self._hadError = true;
|
||||
|
||||
var error = new Error(message);
|
||||
error.errno = errno;
|
||||
error.code = exports.codes[errno];
|
||||
self.emit('error', error);
|
||||
};
|
||||
|
||||
this._binding.init(opts.windowBits || exports.Z_DEFAULT_WINDOWBITS,
|
||||
opts.level || exports.Z_DEFAULT_COMPRESSION,
|
||||
opts.memLevel || exports.Z_DEFAULT_MEMLEVEL,
|
||||
opts.strategy || exports.Z_DEFAULT_STRATEGY,
|
||||
opts.dictionary);
|
||||
|
||||
this._buffer = new Buffer(this._chunkSize);
|
||||
this._offset = 0;
|
||||
this._closed = false;
|
||||
|
||||
this.once('end', this.close);
|
||||
}
|
||||
|
||||
util.inherits(Zlib, Transform);
|
||||
|
||||
Zlib.prototype.reset = function reset() {
|
||||
return this._binding.reset();
|
||||
};
|
||||
|
||||
Zlib.prototype._flush = function(output, callback) {
|
||||
var rs = this._readableState;
|
||||
var self = this;
|
||||
this._transform(null, output, function(er) {
|
||||
if (er)
|
||||
return callback(er);
|
||||
|
||||
// now a weird thing happens... it could be that you called flush
|
||||
// but everything had already actually been consumed, but it wasn't
|
||||
// enough to get over the Readable class's lowWaterMark.
|
||||
// In that case, we emit 'readable' now to make sure it's consumed.
|
||||
if (rs.length &&
|
||||
rs.length < rs.lowWaterMark &&
|
||||
!rs.ended &&
|
||||
rs.needReadable)
|
||||
self.emit('readable');
|
||||
|
||||
callback();
|
||||
});
|
||||
};
|
||||
|
||||
Zlib.prototype.flush = function(callback) {
|
||||
var ws = this._writableState;
|
||||
var ts = this._transformState;
|
||||
|
||||
if (ws.writing) {
|
||||
ws.needDrain = true;
|
||||
var self = this;
|
||||
this.once('drain', function() {
|
||||
self._flush(ts.output, callback);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
this._flush(ts.output, callback || function() {});
|
||||
};
|
||||
|
||||
Zlib.prototype.close = function(callback) {
|
||||
if (callback)
|
||||
process.nextTick(callback);
|
||||
|
||||
if (this._closed)
|
||||
return;
|
||||
|
||||
this._closed = true;
|
||||
|
||||
this._binding.close();
|
||||
|
||||
var self = this;
|
||||
process.nextTick(function() {
|
||||
self.emit('close');
|
||||
});
|
||||
};
|
||||
|
||||
Zlib.prototype._transform = function(chunk, output, cb) {
|
||||
var flushFlag;
|
||||
var ws = this._writableState;
|
||||
var ending = ws.ending || ws.ended;
|
||||
var last = ending && (!chunk || ws.length === chunk.length);
|
||||
|
||||
if (chunk !== null && !Buffer.isBuffer(chunk))
|
||||
return cb(new Error('invalid input'));
|
||||
|
||||
// If it's the last chunk, or a final flush, we use the Z_FINISH flush flag.
|
||||
// If it's explicitly flushing at some other time, then we use
|
||||
// Z_FULL_FLUSH. Otherwise, use Z_NO_FLUSH for maximum compression
|
||||
// goodness.
|
||||
if (last)
|
||||
flushFlag = binding.Z_FINISH;
|
||||
else if (chunk === null)
|
||||
flushFlag = binding.Z_FULL_FLUSH;
|
||||
else
|
||||
flushFlag = binding.Z_NO_FLUSH;
|
||||
|
||||
var availInBefore = chunk && chunk.length;
|
||||
var availOutBefore = this._chunkSize - this._offset;
|
||||
var inOff = 0;
|
||||
|
||||
var req = this._binding.write(flushFlag,
|
||||
chunk, // in
|
||||
inOff, // in_off
|
||||
availInBefore, // in_len
|
||||
this._buffer, // out
|
||||
this._offset, //out_off
|
||||
availOutBefore); // out_len
|
||||
|
||||
req.buffer = chunk;
|
||||
req.callback = callback;
|
||||
|
||||
var self = this;
|
||||
function callback(availInAfter, availOutAfter, buffer) {
|
||||
if (self._hadError)
|
||||
return;
|
||||
|
||||
var have = availOutBefore - availOutAfter;
|
||||
assert(have >= 0, 'have should not go down');
|
||||
|
||||
if (have > 0) {
|
||||
var out = self._buffer.slice(self._offset, self._offset + have);
|
||||
self._offset += have;
|
||||
// serve some output to the consumer.
|
||||
output(out);
|
||||
}
|
||||
|
||||
// exhausted the output buffer, or used all the input create a new one.
|
||||
if (availOutAfter === 0 || self._offset >= self._chunkSize) {
|
||||
availOutBefore = self._chunkSize;
|
||||
self._offset = 0;
|
||||
self._buffer = new Buffer(self._chunkSize);
|
||||
}
|
||||
|
||||
if (availOutAfter === 0) {
|
||||
// Not actually done. Need to reprocess.
|
||||
// Also, update the availInBefore to the availInAfter value,
|
||||
// so that if we have to hit it a third (fourth, etc.) time,
|
||||
// it'll have the correct byte counts.
|
||||
inOff += (availInBefore - availInAfter);
|
||||
availInBefore = availInAfter;
|
||||
|
||||
var newReq = self._binding.write(flushFlag,
|
||||
chunk,
|
||||
inOff,
|
||||
availInBefore,
|
||||
self._buffer,
|
||||
self._offset,
|
||||
self._chunkSize);
|
||||
newReq.callback = callback; // this same function
|
||||
newReq.buffer = chunk;
|
||||
return;
|
||||
}
|
||||
|
||||
// finished with the chunk.
|
||||
cb();
|
||||
}
|
||||
};
|
||||
|
||||
util.inherits(Deflate, Zlib);
|
||||
util.inherits(Inflate, Zlib);
|
||||
util.inherits(Gzip, Zlib);
|
||||
util.inherits(Gunzip, Zlib);
|
||||
util.inherits(DeflateRaw, Zlib);
|
||||
util.inherits(InflateRaw, Zlib);
|
||||
util.inherits(Unzip, Zlib);
|
||||
gerado
externo
+1
@@ -0,0 +1 @@
|
||||
/node_modules
|
||||
gerado
externo
+14
@@ -0,0 +1,14 @@
|
||||
# stream-counter
|
||||
|
||||
Keep track of how many bytes have been written to a stream.
|
||||
|
||||
## Usage
|
||||
|
||||
```js
|
||||
var StreamCounter = require('stream-counter');
|
||||
var counter = new StreamCounter();
|
||||
counter.on('progress', function() {
|
||||
console.log("progress", counter.bytes);
|
||||
});
|
||||
fs.createReadStream('foo.txt').pipe(counter);
|
||||
```
|
||||
gerado
externo
+16
@@ -0,0 +1,16 @@
|
||||
module.exports = ByteCounter;
|
||||
|
||||
var Writable = require('readable-stream').Writable;
|
||||
var util = require('util');
|
||||
|
||||
util.inherits(ByteCounter, Writable);
|
||||
function ByteCounter(options) {
|
||||
Writable.call(this, options);
|
||||
this.bytes = 0;
|
||||
}
|
||||
|
||||
ByteCounter.prototype._write = function(chunk, encoding, cb) {
|
||||
this.bytes += chunk.length;
|
||||
this.emit('progress');
|
||||
cb();
|
||||
};
|
||||
gerado
externo
+31
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"name": "stream-counter",
|
||||
"version": "0.1.0",
|
||||
"description": "keeps track of how many bytes have been written to a stream",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "node test/test.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/superjoe30/node-stream-counter.git"
|
||||
},
|
||||
"author": {
|
||||
"name": "Andrew Kelley",
|
||||
"email": "superjoe30@gmail.com"
|
||||
},
|
||||
"license": "BSD",
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"readable-stream": "~1.0.2"
|
||||
},
|
||||
"readme": "# stream-counter\n\nKeep track of how many bytes have been written to a stream.\n\n## Usage\n\n```js\nvar StreamCounter = require('stream-counter');\nvar counter = new StreamCounter();\ncounter.on('progress', function() {\n console.log(\"progress\", counter.bytes);\n});\nfs.createReadStream('foo.txt').pipe(counter);\n```\n",
|
||||
"readmeFilename": "README.md",
|
||||
"bugs": {
|
||||
"url": "https://github.com/superjoe30/node-stream-counter/issues"
|
||||
},
|
||||
"_id": "stream-counter@0.1.0",
|
||||
"_from": "stream-counter@~0.1.0"
|
||||
}
|
||||
gerado
externo
+20
@@ -0,0 +1,20 @@
|
||||
var ByteCounter = require('../');
|
||||
var fs = require('fs');
|
||||
var path = require('path');
|
||||
var assert = require('assert');
|
||||
|
||||
var counter = new ByteCounter();
|
||||
var remainingTests = 2;
|
||||
counter.once('progress', function() {
|
||||
assert.strictEqual(counter.bytes, 5);
|
||||
remainingTests -= 1;
|
||||
});
|
||||
var is = fs.createReadStream(path.join(__dirname, 'test.txt'));
|
||||
is.pipe(counter);
|
||||
is.on('end', function() {
|
||||
remainingTests -= 1;
|
||||
assert.strictEqual(counter.bytes, 5);
|
||||
});
|
||||
process.on('exit', function() {
|
||||
assert.strictEqual(remainingTests, 0);
|
||||
});
|
||||
gerado
externo
+1
@@ -0,0 +1 @@
|
||||
1234
|
||||
+46
Diff do arquivo suprimido porque uma ou mais linhas são muito longas
gerado
externo
+76
@@ -0,0 +1,76 @@
|
||||
var assert = require('assert')
|
||||
, Form = require('../').Form
|
||||
, boundary = '-----------------------------168072824752491622650073'
|
||||
, mb = 100
|
||||
, buffer = createMultipartBuffer(boundary, mb * 1024 * 1024)
|
||||
|
||||
var callbacks = {
|
||||
partBegin: -1,
|
||||
partEnd: -1,
|
||||
headerField: -1,
|
||||
headerValue: -1,
|
||||
partData: -1,
|
||||
end: -1,
|
||||
};
|
||||
|
||||
var form = new Form({ boundary: boundary });
|
||||
|
||||
hijack('onParseHeaderField', function() {
|
||||
callbacks.headerField++;
|
||||
});
|
||||
|
||||
hijack('onParseHeaderValue', function() {
|
||||
callbacks.headerValue++;
|
||||
});
|
||||
|
||||
hijack('onParsePartBegin', function() {
|
||||
callbacks.partBegin++;
|
||||
});
|
||||
|
||||
hijack('onParsePartData', function() {
|
||||
callbacks.partData++;
|
||||
});
|
||||
|
||||
hijack('onParsePartEnd', function() {
|
||||
callbacks.partEnd++;
|
||||
});
|
||||
|
||||
form.on('finish', function() {
|
||||
callbacks.end++;
|
||||
});
|
||||
|
||||
var start = new Date();
|
||||
form.write(buffer, function(err) {
|
||||
var duration = new Date() - start;
|
||||
assert.ifError(err);
|
||||
var mbPerSec = (mb / (duration / 1000)).toFixed(2);
|
||||
console.log(mbPerSec+' mb/sec');
|
||||
});
|
||||
|
||||
process.on('exit', function() {
|
||||
for (var k in callbacks) {
|
||||
assert.equal(0, callbacks[k], k+' count off by '+callbacks[k]);
|
||||
}
|
||||
});
|
||||
|
||||
function createMultipartBuffer(boundary, size) {
|
||||
var head =
|
||||
'--'+boundary+'\r\n' +
|
||||
'content-disposition: form-data; name="field1"\r\n' +
|
||||
'\r\n'
|
||||
, tail = '\r\n--'+boundary+'--\r\n'
|
||||
, buffer = new Buffer(size);
|
||||
|
||||
buffer.write(head, 'ascii', 0);
|
||||
buffer.write(tail, 'ascii', buffer.length - tail.length);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
function hijack(name, fn) {
|
||||
var oldFn = form[name];
|
||||
form[name] = function() {
|
||||
fn();
|
||||
return oldFn.apply(this, arguments);
|
||||
};
|
||||
}
|
||||
|
||||
|
Antes Largura: | Altura: | Tamanho: 1.6 KiB Depois Largura: | Altura: | Tamanho: 1.6 KiB |
|
Antes Largura: | Altura: | Tamanho: 49 B Depois Largura: | Altura: | Tamanho: 49 B |
Alguns arquivos não foram exibidos porque demasiados arquivos foram alterados neste diff Mostrar Mais
Referência em uma Nova Issue
Bloquear um usuário