then-request
Advanced tools
Comparing version 0.0.4 to 1.0.0
'use strict'; | ||
var Promise = require('promise'); | ||
var Response = require('request-shared/lib/response'); | ||
var Response = require('http-response-object'); | ||
var handleQs = require('./lib/handle-qs.js'); | ||
module.exports = doRequest; | ||
function doRequest(uri, options) { | ||
return new Promise(function (resolve, reject) { | ||
function doRequest(method, url, options, callback) { | ||
var result = new Promise(function (resolve, reject) { | ||
var xhr = new window.XMLHttpRequest(); | ||
// 1 - handle variable list of arguments | ||
if (typeof uri === 'undefined') { | ||
throw new TypeError('undefined is not a valid uri or options object.'); | ||
// check types of arguments | ||
if (typeof method !== 'string') { | ||
throw new TypeError('The method must be a string.'); | ||
} | ||
if (options && typeof options === 'object') { | ||
options.uri = uri; | ||
} else if (typeof uri === 'string') { | ||
options = {uri: uri}; | ||
} else { | ||
options = uri; | ||
if (typeof url !== 'string') { | ||
throw new TypeError('The URL/path must be a string.'); | ||
} | ||
options = copy(options); | ||
if (options.url && !options.uri) { | ||
options.uri = options.url; | ||
delete options.url; | ||
if (typeof options === 'function') { | ||
callback = options; | ||
options = {}; | ||
} | ||
if (options === null || options === undefined) { | ||
options = {}; | ||
} | ||
if (typeof options !== 'object') { | ||
throw new TypeError('Options must be an object (or null).'); | ||
} | ||
if (typeof callback !== 'function') { | ||
callback = undefined; | ||
} | ||
method = method.toUpperCase(); | ||
options.headers = options.headers || {}; | ||
// 2 - handle cross domain option | ||
if (!options.crossDomain) { | ||
var match; | ||
options.crossDomain = !!((match = /^([\w-]+:)?\/\/([^\/]+)/.exec(options.uri)) && | ||
(match[2] != window.location.host)); | ||
// handle cross domain | ||
var match; | ||
var crossDomain = !!((match = /^([\w-]+:)?\/\/([^\/]+)/.exec(options.uri)) && (match[2] != window.location.host)); | ||
if (!crossDomain) options.headers['X-Requested-With'] = 'XMLHttpRequest'; | ||
// handle query string | ||
if (options.qs) { | ||
url = handleQs(url, options.qs); | ||
} | ||
if (!options.crossDomain) options.headers['X-Requested-With'] = 'XMLHttpRequest'; | ||
options.method = (options.method || 'GET').toUpperCase(); | ||
// handle json body | ||
if (options.json) { | ||
options.body = JSON.stringify(options.json); | ||
options.headers['content-type'] = 'application/json'; | ||
} | ||
xhr.onreadystatechange = function () { | ||
@@ -51,5 +67,6 @@ if (xhr.readyState === 4) { | ||
}; | ||
// type, uri, async | ||
xhr.open(options.method, options.uri, true); | ||
// method, url, async | ||
xhr.open(method, url, true); | ||
for (var name in options.headers) { | ||
@@ -62,27 +79,6 @@ xhr.setRequestHeader(name.toLowerCase(), options.headers[name]); | ||
}); | ||
result.getBody = function () { | ||
return result.then(function (res) { return res.getBody(); }); | ||
}; | ||
return result.nodeify(callback); | ||
} | ||
function copy(obj, seen) { | ||
seen = seen || []; | ||
if (seen.indexOf(obj) !== -1) { | ||
throw new Error('Unexpected circular reference in options'); | ||
} | ||
seen.push(obj); | ||
if (Array.isArray(obj)) { | ||
return obj.map(function (item) { | ||
return copy(item, seen); | ||
}); | ||
} else if (obj && typeof obj === 'object') { | ||
var o = {} | ||
Object.keys(obj).forEach(function (i) { | ||
o[i] = copy(obj[i], seen) | ||
}) | ||
return o | ||
} else { | ||
return obj; | ||
} | ||
} | ||
function empty() { | ||
} | ||
79
index.js
'use strict'; | ||
var parseUrl = require('url').parse; | ||
var Promise = require('promise'); | ||
var Request = require('request-shared').Request; | ||
var Response = require('request-shared').Response; | ||
var concat = require('concat-stream'); | ||
var protocols = { | ||
http: require('http'), | ||
https: require('https') | ||
}; | ||
var Response = require('http-response-object'); | ||
var handleQs = require('./lib/handle-qs.js'); | ||
module.exports = doRequest; | ||
function doRequest(url, options) { | ||
return new Promise(function (resolve, reject) { | ||
var request = new Request(url, options); | ||
if (['http', 'https'].indexOf(request.protocol) === -1) { | ||
throw new Error('Invalid protocol ' + request.protocol); | ||
module.exports._request = require('http-basic'); | ||
function doRequest(method, url, options, callback) { | ||
var result = new Promise(function (resolve, reject) { | ||
// check types of arguments | ||
if (typeof method !== 'string') { | ||
throw new TypeError('The method must be a string.'); | ||
} | ||
var http = protocols[request.protocol]; | ||
delete request.protocol; | ||
var req = http.request(request, function (res) { | ||
var body = ''; | ||
res.once('error', reject); | ||
res.pipe(concat(function (body) { | ||
if (typeof url !== 'string') { | ||
throw new TypeError('The URL/path must be a string.'); | ||
} | ||
if (typeof options === 'function') { | ||
callback = options; | ||
options = {}; | ||
} | ||
if (options === null || options === undefined) { | ||
options = {}; | ||
} | ||
if (typeof options !== 'object') { | ||
throw new TypeError('Options must be an object (or null).'); | ||
} | ||
if (typeof callback !== 'function') { | ||
callback = undefined; | ||
} | ||
method = method.toUpperCase(); | ||
options.headers = options.headers || {}; | ||
// handle query string | ||
if (options.qs) { | ||
url = handleQs(url, options.qs); | ||
} | ||
// handle json body | ||
if (options.json) { | ||
options.body = JSON.stringify(options.json); | ||
options.headers['content-type'] = 'application/json'; | ||
} | ||
var req = module.exports._request(method, url, { | ||
headers: options.headers, | ||
followRedirects: true, | ||
gzip: true, | ||
cache: options.cache | ||
}, function (err, res) { | ||
if (err) return reject(err); | ||
res.body.on('error', reject); | ||
res.body.pipe(concat(function (body) { | ||
resolve(new Response(res.statusCode, res.headers, Array.isArray(body) ? new Buffer(0) : body)); | ||
})); | ||
}); | ||
req.once('error', reject); | ||
req.end(request.body); | ||
if (req) { | ||
req.end(options.body ? options.body : new Buffer(0)); | ||
} | ||
}); | ||
} | ||
result.getBody = function (encoding) { | ||
return result.then(function (res) { return res.getBody(encoding); }); | ||
}; | ||
return result.nodeify(callback); | ||
} |
{ | ||
"name": "then-request", | ||
"version": "0.0.4", | ||
"version": "1.0.0", | ||
"description": "A request library that returns promises, inspired by request", | ||
@@ -8,14 +8,14 @@ "keywords": [], | ||
"dependencies": { | ||
"request-shared": "0.0.1", | ||
"promise": "~3.2.0", | ||
"concat-stream": "~1.4.1" | ||
"promise": "^5.0.0", | ||
"concat-stream": "^1.4.1", | ||
"qs": "^0.6.6", | ||
"http-response-object": "^1.0.1", | ||
"http-basic": "^1.0.2" | ||
}, | ||
"devDependencies": { | ||
"chromedriver": "~2.8.1", | ||
"cabbie": "0.0.5", | ||
"run-browser": "~1.2.0" | ||
"testit": "^1.2.0", | ||
"istanbul": "^0.3.0" | ||
}, | ||
"scripts": { | ||
"test": "node test/server.js & node test/index.js", | ||
"test-browser": "node test/server.js & run-browser tests/index.js" | ||
"test": "node test/index.js && istanbul cover test/index.js" | ||
}, | ||
@@ -22,0 +22,0 @@ "repository": { |
@@ -13,4 +13,59 @@ # then-request | ||
## Usage | ||
`request(method, url, options, callback?)` | ||
e.g. | ||
```js | ||
request('GET', 'http://example.com').done(function (res) { | ||
console.log(res.getBody()); | ||
}); | ||
``` | ||
**Method:** | ||
An HTTP method (e.g. `GET`, `POST`, `PUT`, `DELETE` or `HEAD`). It is not case sensitive. | ||
**URL:** | ||
A url as a string (e.g. `http://example.com`). Relative URLs are allowed in the browser. | ||
**Options:** | ||
- `qs` - an object containing querystring values to be appended to the uri | ||
- `headers` - http headers (default: `{}`) | ||
- `body` - body for PATCH, POST and PUT requests. Must be a `Buffer` or `String` (only strings are accepted client side) | ||
- `json` - sets `body` but to JSON representation of value and adds `Content-type: application/json`. Does not have any affect on how the response is treated. | ||
- `cache` - only used in node.js (browsers already have their own caches) Can be `'memory'`, `'file'` or your own custom implementaton (see https://github.com/ForbesLindesay/http-basic#implementing-a-cache). | ||
**Callback / Returns:** | ||
If a callback is provided it is called with `err` and `res`. If no callback is provided, a [Promise](https://www.promisejs.org/) is returned that eventually resolves to `res`. The resulting Promise also has an additional `.getBody(encoding?)` method that is equivallent to calling `.then(function (res) { return res.getBody(); })`. | ||
### Response | ||
Note that even for status codes that represent an error, the promise will be resolved as the request succeeded. You can call `getBody` if you want to error on invalid status codes. The response has the following properties: | ||
- `statusCode` - a number representing the HTTP status code | ||
- `headers` - http response headers | ||
- `body` - a string if in the browser or a buffer if on the server | ||
It also has a method `getBody(encoding?)` which looks like: | ||
```js | ||
function getBody(encoding) { | ||
if (this.statusCode >= 300) { | ||
var err = new Error('Server responded with status code ' + this.statusCode + ':\n' + this.body.toString(encoding)); | ||
err.statusCode = this.statusCode; | ||
err.headers = this.headers; | ||
err.body = this.body; | ||
throw err; | ||
} | ||
return encoding ? this.body.toString(encoding) : this.body; | ||
} | ||
``` | ||
## License | ||
MIT |
'use strict'; | ||
var assert = require('assert'); | ||
var request = require('../'); | ||
var test = require('testit'); | ||
module.exports = request('http://localhost:3000/foo', {method: 'POST'}) | ||
.then(function (res) { | ||
assert(res.statusCode === 200); | ||
assert(res.headers); | ||
assert(res.headers['my-header'] === 'value'); | ||
assert(res.body); | ||
test('./lib/handle-qs.js', function () { | ||
var handleQs = require('../lib/handle-qs.js'); | ||
assert(handleQs('http://example.com/', {foo: 'bar'}) === 'http://example.com/?foo=bar'); | ||
assert(handleQs('http://example.com/', {foo: {bar: 'baz'}}) === 'http://example.com/?foo[bar]=baz'); | ||
assert(handleQs('http://example.com/', {foo: 'bar', bing: 'bong'}) === 'http://example.com/?foo=bar&bing=bong'); | ||
assert(handleQs('http://example.com/?foo=bar', {bing: 'bong'}) === 'http://example.com/?foo=bar&bing=bong'); | ||
assert(handleQs('http://example.com/?foo=bar#ding', {bing: 'bong'}) === 'http://example.com/?foo=bar&bing=bong#ding'); | ||
}); | ||
module.exports.done(function () { | ||
if (typeof window !== 'undefined') { | ||
window.testsPassed = true; | ||
} | ||
console.log('tests passed'); | ||
}, function (err) { | ||
if (typeof window !== 'undefined') { | ||
window.testsPassed = false; | ||
} | ||
console.log('tests failed'); | ||
throw err; | ||
}); | ||
require('./browser.js'); | ||
require('./server.js'); | ||
function testEnv(env) { | ||
var request = require(env === 'browser' ? '../browser.js' : '../index.js'); | ||
test(env + ' - GET', function () { | ||
return request('GET', 'http://example.com').then(function (res) { | ||
assert(res.statusCode === 200); | ||
assert(res.headers['foo'] === 'bar'); | ||
assert(res.body.toString() === 'body'); | ||
}); | ||
}); | ||
test(env + ' - GET query', function () { | ||
return request('GET', 'http://example.com', {qs: {foo: 'baz'}}).then(function (res) { | ||
assert(res.statusCode === 200); | ||
assert(res.headers['foo'] === 'baz'); | ||
assert(res.body.toString() === 'body'); | ||
}); | ||
}); | ||
test(env + ' - GET -> .getBody("utf8")', function () { | ||
return request('GET', 'http://example.com').getBody('utf8').then(function (body) { | ||
assert(body === 'body'); | ||
}); | ||
}); | ||
test(env + ' - POST json', function () { | ||
return request('POST', 'http://example.com', {json: {foo: 'baz'}}).then(function (res) { | ||
assert(res.statusCode === 200); | ||
assert(res.body.toString() === 'json body'); | ||
}); | ||
}); | ||
test(env + ' - invalid method', function () { | ||
return request({}, 'http://example.com').then(function (res) { | ||
throw new Error('Expected an error'); | ||
}, function (err) { | ||
assert(err instanceof TypeError); | ||
}); | ||
}); | ||
test(env + ' - invalid url', function () { | ||
return request('GET', {}).then(function (res) { | ||
throw new Error('Expected an error'); | ||
}, function (err) { | ||
assert(err instanceof TypeError); | ||
}); | ||
}); | ||
test(env + ' - invalid options', function () { | ||
return request('GET', 'http://example.com', 'options').then(function (res) { | ||
throw new Error('Expected an error'); | ||
}, function (err) { | ||
assert(err instanceof TypeError); | ||
}); | ||
}); | ||
} | ||
testEnv('browser'); | ||
testEnv('server'); |
'use strict'; | ||
var http = require('http'); | ||
var fs = require('fs'); | ||
var crypto = require('crypto'); | ||
var runBrowser = require('run-browser'); | ||
var concat = require('concat-stream'); | ||
var util = require('util'); | ||
var assert = require('assert'); | ||
var PassThrough = require('stream').PassThrough; | ||
var getResponse = require('./get-mock-response.js'); | ||
var handler = runBrowser.createHandler(require.resolve('./index.js')); | ||
var resultHandlers = []; | ||
function onResult(pass) { | ||
for (var i = 0; i < resultHandlers.length; i++) { | ||
resultHandlers[i](pass); | ||
require('../index.js')._request = function (method, url, options, callback) { | ||
assert(typeof callback === 'function'); | ||
var duplex = !(method === 'GET' || method === 'DELETE' || method === 'HEAD'); | ||
if (duplex) { | ||
return { | ||
end: function (body) { | ||
gotResponse(getResponse(method, url, options.headers, body, {isClient: false})); | ||
} | ||
}; | ||
} else { | ||
gotResponse(getResponse(method, url, options.headers, null, {isClient: false})); | ||
} | ||
} | ||
var server = http.createServer(function (req, res) { | ||
if (runBrowser.handles(req)) { | ||
return handler(req, res); | ||
function gotResponse(res) { | ||
var stream = new PassThrough(); | ||
stream.end(res.body); | ||
res.body = stream; | ||
callback(null, res); | ||
} | ||
if (req.url === '/result/pass') { | ||
onResult(true); | ||
res.end(); | ||
} | ||
if (req.url === '/result/fail') { | ||
onResult(false); | ||
res.end(); | ||
} | ||
res.setHeader('My-Header', 'value'); | ||
if (req.url === '/204') { | ||
req = { | ||
httpVersion: req.httpVersion, | ||
headers: req.headers, | ||
method: req.method, | ||
url: req.url | ||
}; | ||
// console.log(util.inspect(req, {colors: true}).replace(/^/gm, ' ')); | ||
res.statusCode = 204; | ||
return res.end(); | ||
} | ||
req.pipe(concat(function (body) { | ||
req = { | ||
httpVersion: req.httpVersion, | ||
headers: req.headers, | ||
method: req.method, | ||
url: req.url, | ||
body: body | ||
}; | ||
// console.log(util.inspect(req, {colors: true}).replace(/^/gm, ' ')); | ||
var shasum = crypto.createHash('sha1'); | ||
shasum.update(req.httpVersion); | ||
shasum.update(req.headers['host'] || ''); | ||
shasum.update(req.headers['headers-a'] || ''); | ||
shasum.update(req.headers['headers-b'] || ''); | ||
shasum.update(req.headers['authorization'] || ''); | ||
shasum.update(req.headers['accept'] || ''); | ||
shasum.update(req.headers['content-type'] || ''); | ||
shasum.update(req.headers['content-length'] || ''); | ||
shasum.update(req.headers['transfer-encoding'] || ''); | ||
shasum.update(req.url); | ||
shasum.update(req.body); | ||
res.end(shasum.digest('hex')); | ||
})); | ||
}); | ||
server.listen(3000); | ||
module.exports.close = function () { | ||
server.close(); | ||
}; | ||
module.exports.on = function (name, fn) { | ||
if (name === 'result') { | ||
resultHandlers.push(fn); | ||
} | ||
}; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 2 instances in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 2 instances in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
15560
2
12
304
1
71
1
5
5
+ Addedhttp-basic@^1.0.2
+ Addedhttp-response-object@^1.0.1
+ Addedqs@^0.6.6
+ Addedasap@1.0.0(transitive)
+ Addedbuffer-from@1.1.2(transitive)
+ Addedconcat-stream@1.6.2(transitive)
+ Addedhttp-basic@1.1.1(transitive)
+ Addedhttp-response-object@1.1.0(transitive)
+ Addedisarray@1.0.0(transitive)
+ Addedprocess-nextick-args@2.0.1(transitive)
+ Addedpromise@5.0.0(transitive)
+ Addedreadable-stream@2.3.8(transitive)
+ Addedsafe-buffer@5.1.2(transitive)
+ Addedstring_decoder@1.1.1(transitive)
+ Addedtypedarray@0.0.6(transitive)
+ Addedutil-deprecate@1.0.2(transitive)
- Removedrequest-shared@0.0.1
- Removedconcat-stream@1.4.11(transitive)
- Removedisarray@0.0.1(transitive)
- Removedpromise@3.2.0(transitive)
- Removedreadable-stream@1.1.14(transitive)
- Removedrequest-shared@0.0.1(transitive)
- Removedstring_decoder@0.10.31(transitive)
- Removedtypedarray@0.0.7(transitive)
Updatedconcat-stream@^1.4.1
Updatedpromise@^5.0.0