diff --git a/README.md b/README.md index 6ee45b205..1e346779d 100644 --- a/README.md +++ b/README.md @@ -733,7 +733,7 @@ The first argument can be either a `url` or an `options` object. The only requir --- -- `body` - entity body for PATCH, POST and PUT requests. Must be a `Buffer` or `String`, unless `json` is `true`. If `json` is `true`, then `body` must be a JSON-serializable object. +- `body` - entity body for PATCH, POST and PUT requests. Must be a `Buffer`, `String` or `ReadStream`. If `json` is `true`, then `body` must be a JSON-serializable object. - `form` - when passed an object or a querystring, this sets `body` to a querystring representation of value, and adds `Content-type: application/x-www-form-urlencoded` header. When passed no options, a `FormData` instance is returned (and is piped to request). See "Forms" section above. - `formData` - Data to pass for a `multipart/form-data` request. See [Forms](#forms) section above. diff --git a/request.js b/request.js index e4a181237..cb3862b0e 100644 --- a/request.js +++ b/request.js @@ -16,6 +16,7 @@ var http = require('http') , ForeverAgent = require('forever-agent') , FormData = require('form-data') , extend = require('extend') + , isstream = require('isstream') , isTypedArray = require('is-typedarray').strict , helpers = require('./lib/helpers') , cookies = require('./lib/cookies') @@ -452,7 +453,7 @@ Request.prototype.init = function (options) { } } } - if (self.body) { + if (self.body && !isstream(self.body)) { setContentLength() } @@ -552,15 +553,19 @@ Request.prototype.init = function (options) { self._multipart.body.pipe(self) } if (self.body) { - setContentLength() - if (Array.isArray(self.body)) { - self.body.forEach(function (part) { - self.write(part) - }) + if (isstream(self.body)) { + self.body.pipe(self) } else { - self.write(self.body) + setContentLength() + if (Array.isArray(self.body)) { + self.body.forEach(function (part) { + self.write(part) + }) + } else { + self.write(self.body) + } + self.end() } - self.end() } else if (self.requestBodyStream) { console.warn('options.requestBodyStream is deprecated, please pass the request object to stream.pipe.') self.requestBodyStream.pipe(self) diff --git a/tests/test-stream.js b/tests/test-stream.js new file mode 100644 index 000000000..b1fbfad0f --- /dev/null +++ b/tests/test-stream.js @@ -0,0 +1,34 @@ + +var fs = require('fs') +var path = require('path') +var http = require('http') +var tape = require('tape') +var request = require('../') +var server + + +tape('before', function (t) { + server = http.createServer() + server.on('request', function (req, res) { + req.pipe(res) + }) + server.listen(6767, t.end) +}) + +tape('request body stream', function (t) { + var fpath = path.join(__dirname, 'unicycle.jpg') + var input = fs.createReadStream(fpath, {highWaterMark: 1000}) + request({ + uri: 'http://localhost:6767', + method: 'POST', + body: input, + encoding: null + }, function (err, res, body) { + t.equal(body.length, fs.statSync(fpath).size) + t.end() + }) +}) + +tape('after', function (t) { + server.close(t.end) +})