mirror of
https://github.com/MrUnknownDE/UnknownBot.git
synced 2026-04-16 12:13:45 +02:00
start nodejs bot
This commit is contained in:
27
node_modules/needle/.github/workflows/nodejs.yml
generated
vendored
Normal file
27
node_modules/needle/.github/workflows/nodejs.yml
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
name: Node CI
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [0.10.0, 4.x, 6.x, 8.x, 10.x, 12.x]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- name: npm install, build, and test
|
||||
run: |
|
||||
npm install
|
||||
mkdir -p test/keys
|
||||
openssl genrsa -out test/keys/ssl.key 2048
|
||||
openssl req -new -key test/keys/ssl.key -subj "/C=US/ST=Denial/L=Springfield/O=Dis/CN=www.example.com" -x509 -days 999 -out test/keys/ssl.cert
|
||||
npm run build --if-present
|
||||
npm test
|
||||
env:
|
||||
CI: true
|
||||
612
node_modules/needle/README.md
generated
vendored
Normal file
612
node_modules/needle/README.md
generated
vendored
Normal file
@@ -0,0 +1,612 @@
|
||||
Needle
|
||||
======
|
||||
|
||||
[](https://nodei.co/npm/needle/)
|
||||
|
||||
The leanest and most handsome HTTP client in the Nodelands.
|
||||
|
||||
```js
|
||||
var needle = require('needle');
|
||||
|
||||
needle.get('http://www.google.com', function(error, response) {
|
||||
if (!error && response.statusCode == 200)
|
||||
console.log(response.body);
|
||||
});
|
||||
```
|
||||
|
||||
Callbacks not floating your boat? Needle got your back.
|
||||
|
||||
``` js
|
||||
var data = {
|
||||
file: '/home/johnlennon/walrus.png',
|
||||
content_type: 'image/png'
|
||||
};
|
||||
|
||||
// the callback is optional, and needle returns a `readableStream` object
|
||||
// that triggers a 'done' event when the request/response process is complete.
|
||||
needle
|
||||
.post('https://my.server.com/foo', data, { multipart: true })
|
||||
.on('readable', function() { /* eat your chunks */ })
|
||||
.on('done', function(err, resp) {
|
||||
console.log('Ready-o!');
|
||||
})
|
||||
```
|
||||
|
||||
From version 2.0.x up, Promises are also supported. Just call `needle()` directly and you'll get a native Promise object.
|
||||
|
||||
```js
|
||||
needle('put', 'https://hacking.the.gibson/login', { password: 'god' }, { json: true })
|
||||
.then(function(response) {
|
||||
return doSomethingWith(response)
|
||||
})
|
||||
.catch(function(err) {
|
||||
console.log('Call the locksmith!')
|
||||
})
|
||||
```
|
||||
|
||||
With only two real dependencies, Needle supports:
|
||||
|
||||
- HTTP/HTTPS requests, with the usual verbs you would expect
|
||||
- All of Node's native TLS options, such as 'rejectUnauthorized' (see below)
|
||||
- Basic & Digest authentication with auto-detection
|
||||
- Multipart form-data (e.g. file uploads)
|
||||
- HTTP Proxy forwarding, optionally with authentication
|
||||
- Streaming gzip, deflate, and brotli decompression
|
||||
- Automatic XML & JSON parsing
|
||||
- 301/302/303 redirect following, with fine-grained tuning, and
|
||||
- Streaming non-UTF-8 charset decoding, via `iconv-lite`
|
||||
|
||||
And yes, Mr. Wayne, it does come in black.
|
||||
|
||||
This makes Needle an ideal alternative for performing quick HTTP requests in Node, either for API interaction, downloading or uploading streams of data, and so on. If you need OAuth, AWS support or anything fancier, you should check out mikeal's request module.
|
||||
|
||||
Install
|
||||
-------
|
||||
|
||||
```
|
||||
$ npm install needle
|
||||
```
|
||||
|
||||
Usage
|
||||
-----
|
||||
|
||||
```js
|
||||
// using promises
|
||||
needle('get', 'https://server.com/posts/12')
|
||||
.then(function(resp) {
|
||||
// ...
|
||||
})
|
||||
.catch(function(err) {
|
||||
// ...
|
||||
});
|
||||
|
||||
// with callback
|
||||
needle.get('ifconfig.me/all.json', function(error, response, body) {
|
||||
if (error) throw error;
|
||||
|
||||
// body is an alias for `response.body`,
|
||||
// that in this case holds a JSON-decoded object.
|
||||
console.log(body.ip_addr);
|
||||
});
|
||||
|
||||
// no callback, using streams
|
||||
var out = fs.createWriteStream('logo.png');
|
||||
needle.get('https://google.com/images/logo.png').pipe(out).on('done', function() {
|
||||
console.log('Pipe finished!');
|
||||
});
|
||||
```
|
||||
|
||||
As you can see, you can use Needle with Promises or without them. When using Promises or when a callback is passed, the response's body will be buffered and written to `response.body`, and the callback will be fired when all of the data has been collected and processed (e.g. decompressed, decoded and/or parsed).
|
||||
|
||||
When no callback is passed, however, the buffering logic will be skipped but the response stream will still go through Needle's processing pipeline, so you get all the benefits of post-processing while keeping the streamishness we all love from Node.
|
||||
|
||||
Response pipeline
|
||||
-----------------
|
||||
|
||||
Depending on the response's Content-Type, Needle will either attempt to parse JSON or XML streams, or, if a text response was received, will ensure that the final encoding you get is UTF-8.
|
||||
|
||||
You can also request a gzip/deflated/brotli response, which, if sent by the server, will be processed before parsing or decoding is performed. (Note: brotli is only supported on Node 10.16.0 or above, and will not be requested or processed on earlier versions.)
|
||||
|
||||
```js
|
||||
needle.get('http://stackoverflow.com/feeds', { compressed: true }, function(err, resp) {
|
||||
console.log(resp.body); // this little guy won't be a Gzipped binary blob
|
||||
// but a nice object containing all the latest entries
|
||||
});
|
||||
```
|
||||
|
||||
Or in anti-callback mode, using a few other options:
|
||||
|
||||
```js
|
||||
var options = {
|
||||
compressed : true, // sets 'Accept-Encoding' to 'gzip, deflate, br'
|
||||
follow_max : 5, // follow up to five redirects
|
||||
rejectUnauthorized : true // verify SSL certificate
|
||||
}
|
||||
|
||||
var stream = needle.get('https://backend.server.com/everything.html', options);
|
||||
|
||||
// read the chunks from the 'readable' event, so the stream gets consumed.
|
||||
stream.on('readable', function() {
|
||||
while (data = this.read()) {
|
||||
console.log(data.toString());
|
||||
}
|
||||
})
|
||||
|
||||
stream.on('done', function(err) {
|
||||
// if our request had an error, our 'done' event will tell us.
|
||||
if (!err) console.log('Great success!');
|
||||
})
|
||||
```
|
||||
|
||||
API
|
||||
---
|
||||
|
||||
### needle(method, url[, data][, options][, callback]) `(> 2.0.x)`
|
||||
|
||||
Calling `needle()` directly returns a Promise. Besides `method` and `url`, all parameters are optional, although when sending a `post`, `put` or `patch` request you will get an error if `data` is not present.
|
||||
|
||||
```js
|
||||
needle('get', 'http://some.url.com')
|
||||
.then(function(resp) { console.log(resp.body) })
|
||||
.catch(function(err) { console.error(err) })
|
||||
})
|
||||
```
|
||||
|
||||
Except from the above, all of Needle's request methods return a Readable stream, and both `options` and `callback` are optional. If passed, the callback will return three arguments: `error`, `response` and `body`, which is basically an alias for `response.body`.
|
||||
|
||||
### needle.head(url[, options][, callback])
|
||||
|
||||
```js
|
||||
needle.head('https://my.backend.server.com', {
|
||||
open_timeout: 5000 // if we're not able to open a connection in 5 seconds, boom.
|
||||
}, function(err, resp) {
|
||||
if (err)
|
||||
console.log('Shoot! Something is wrong: ' + err.message)
|
||||
else
|
||||
console.log('Yup, still alive.')
|
||||
})
|
||||
```
|
||||
|
||||
### needle.get(url[, options][, callback])
|
||||
|
||||
```js
|
||||
needle.get('google.com/search?q=syd+barrett', function(err, resp) {
|
||||
// if no http:// is found, Needle will automagically prepend it.
|
||||
});
|
||||
```
|
||||
|
||||
### needle.post(url, data[, options][, callback])
|
||||
|
||||
```js
|
||||
var options = {
|
||||
headers: { 'X-Custom-Header': 'Bumbaway atuna' }
|
||||
}
|
||||
|
||||
needle.post('https://my.app.com/endpoint', 'foo=bar', options, function(err, resp) {
|
||||
// you can pass params as a string or as an object.
|
||||
});
|
||||
```
|
||||
|
||||
### needle.put(url, data[, options][, callback])
|
||||
|
||||
```js
|
||||
var nested = {
|
||||
params: {
|
||||
are: {
|
||||
also: 'supported'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
needle.put('https://api.app.com/v2', nested, function(err, resp) {
|
||||
console.log('Got ' + resp.bytes + ' bytes.') // another nice treat from this handsome fella.
|
||||
});
|
||||
```
|
||||
|
||||
### needle.patch(url, data[, options][, callback])
|
||||
|
||||
Same behaviour as PUT.
|
||||
|
||||
### needle.delete(url, data[, options][, callback])
|
||||
|
||||
```js
|
||||
var options = {
|
||||
username: 'fidelio',
|
||||
password: 'x'
|
||||
}
|
||||
|
||||
needle.delete('https://api.app.com/messages/123', null, options, function(err, resp) {
|
||||
// in this case, data may be null, but you need to explicity pass it.
|
||||
});
|
||||
```
|
||||
|
||||
### needle.request(method, url, data[, options][, callback])
|
||||
|
||||
Generic request. This not only allows for flexibility, but also lets you perform a GET request with data, in which case will be appended to the request as a query string, unless you pass a `json: true` option (read below).
|
||||
|
||||
```js
|
||||
var params = {
|
||||
q : 'a very smart query',
|
||||
page : 2
|
||||
}
|
||||
|
||||
needle.request('get', 'forum.com/search', params, function(err, resp) {
|
||||
if (!err && resp.statusCode == 200)
|
||||
console.log(resp.body); // here you go, mister.
|
||||
});
|
||||
```
|
||||
|
||||
Now, if you set pass `json: true` among the options, Needle won't set your params as a querystring but instead send a JSON representation of your data through the request's body, as well as set the `Content-Type` and `Accept` headers to `application/json`.
|
||||
|
||||
```js
|
||||
needle.request('get', 'forum.com/search', params, { json: true }, function(err, resp) {
|
||||
if (resp.statusCode == 200) console.log('It worked!');
|
||||
});
|
||||
```
|
||||
|
||||
Events
|
||||
------
|
||||
|
||||
The [Readable stream](https://nodejs.org/api/stream.html#stream_class_stream_readable) object returned by the above request methods emits the following events, in addition to the regular ones (e.g. `end`, `close`, `data`, `pipe`, `readable`).
|
||||
|
||||
### Event: `'response'`
|
||||
|
||||
- `response <http.IncomingMessage>`
|
||||
|
||||
Emitted when the underlying [http.ClientRequest](https://nodejs.org/api/http.html#http_class_http_clientrequest) emits a response event. This is after the connection is established and the header received, but before any of it is processed (e.g. authorization required or redirect to be followed). No data has been consumed at this point.
|
||||
|
||||
### Event: `'redirect'`
|
||||
|
||||
- `location <String>`
|
||||
|
||||
Indicates that the a redirect is being followed. This means that the response code was a redirect (`301`, `302`, `303`, `307`) and the given [redirect options](#redirect-options) allowed following the URL received in the `Location` header.
|
||||
|
||||
### Event: `'header'`
|
||||
|
||||
- `statusCode <Integer>`
|
||||
- `headers <Object>`
|
||||
|
||||
Triggered after the header has been processed, and just before the data is to be consumed. This implies that no redirect was followed and/or authentication header was received. In other words, we got a "valid" response.
|
||||
|
||||
### Event: `'done'` (previously 'end')
|
||||
|
||||
- `exception <Error>` (optional)
|
||||
|
||||
Emitted when the request/response process has finished, either because all data was consumed or an error ocurred somewhere in between. Unlike a regular stream's `end` event, Needle's `done` will be fired either on success or on failure, which is why the first argument may be an Error object. In other words:
|
||||
|
||||
```js
|
||||
var resp = needle.get('something.worthy/of/being/streamed/by/needle');
|
||||
resp.pipe(someWritableStream);
|
||||
|
||||
resp.on('done', function(err) {
|
||||
if (err) console.log('An error ocurred: ' + err.message);
|
||||
else console.log('Great success!');
|
||||
})
|
||||
```
|
||||
|
||||
### Event: `'err'`
|
||||
|
||||
- `exception <Error>`
|
||||
|
||||
Emitted when an error ocurrs. This should only happen once in the lifecycle of a Needle request.
|
||||
|
||||
### Event: `'timeout'`
|
||||
|
||||
- `type <String>`
|
||||
|
||||
Emitted when an timeout error occurs. Type can be either 'open', 'response', or 'read'. This will called right before aborting the request, which will also trigger an `err` event, a described above, with an `ECONNRESET` (Socket hang up) exception.
|
||||
|
||||
Request options
|
||||
---------------
|
||||
|
||||
For information about options that've changed, there's always [the changelog](https://github.com/tomas/needle/releases).
|
||||
|
||||
- `agent` : Uses an [http.Agent](https://nodejs.org/api/http.html#http_class_http_agent) of your choice, instead of the global, default one. Useful for tweaking the behaviour at the connection level, such as when doing tunneling (see below for an example).
|
||||
- `json` : When `true`, sets content type to `application/json` and sends request body as JSON string, instead of a query string.
|
||||
- `open_timeout`: (or `timeout`) Returns error if connection takes longer than X milisecs to establish. Defaults to `10000` (10 secs). `0` means no timeout.
|
||||
- `response_timeout`: Returns error if no response headers are received in X milisecs, counting from when the connection is opened. Defaults to `0` (no response timeout).
|
||||
- `read_timeout`: Returns error if data transfer takes longer than X milisecs, once response headers are received. Defaults to `0` (no timeout).
|
||||
- `follow_max` : (or `follow`) Number of redirects to follow. Defaults to `0`. See below for more redirect options.
|
||||
- `multipart` : Enables multipart/form-data encoding. Defaults to `false`. Use it when uploading files.
|
||||
- `proxy` : Forwards request through HTTP(s) proxy. Eg. `proxy: 'http://user:pass@proxy.server.com:3128'`. For more advanced proxying/tunneling use a custom `agent`, as described below.
|
||||
- `headers` : Object containing custom HTTP headers for request. Overrides defaults described below.
|
||||
- `auth` : Determines what to do with provided username/password. Options are `auto`, `digest` or `basic` (default). `auto` will detect the type of authentication depending on the response headers.
|
||||
- `stream_length`: When sending streams, this lets you manually set the Content-Length header --if the stream's bytecount is known beforehand--, preventing ECONNRESET (socket hang up) errors on some servers that misbehave when receiving payloads of unknown size. Set it to `0` and Needle will get and set the stream's length for you, or leave unset for the default behaviour, which is no Content-Length header for stream payloads.
|
||||
- `localAddress`: <string>, IP address. Passed to http/https request. Local interface from which the request should be emitted.
|
||||
- `uri_modifier`: Anonymous function taking request (or redirect location if following redirects) URI as an argument and modifying it given logic. It has to return a valid URI string for successful request.
|
||||
|
||||
Response options
|
||||
----------------
|
||||
|
||||
- `decode_response` : (or `decode`) Whether to decode the text responses to UTF-8, if Content-Type header shows a different charset. Defaults to `true`.
|
||||
- `parse_response` : (or `parse`) Whether to parse XML or JSON response bodies automagically. Defaults to `true`. You can also set this to 'xml' or 'json' in which case Needle will *only* parse the response if the content type matches.
|
||||
- `output` : Dump response output to file. This occurs after parsing and charset decoding is done.
|
||||
- `parse_cookies` : Whether to parse response’s `Set-Cookie` header. Defaults to `true`. If parsed, response cookies will be available at `resp.cookies`.
|
||||
|
||||
HTTP Header options
|
||||
-------------------
|
||||
|
||||
These are basically shortcuts to the `headers` option described above.
|
||||
|
||||
- `cookies` : Builds and sets a Cookie header from a `{ key: 'value' }` object.
|
||||
- `compressed`: If `true`, sets 'Accept-Encoding' header to 'gzip,deflate', and inflates content if zipped. Defaults to `false`.
|
||||
- `username` : For HTTP basic auth.
|
||||
- `password` : For HTTP basic auth. Requires username to be passed, but is optional.
|
||||
- `accept` : Sets 'Accept' HTTP header. Defaults to `*/*`.
|
||||
- `connection`: Sets 'Connection' HTTP header. Not set by default, unless running Node < 0.11.4 in which case it defaults to `close`. More info about this below.
|
||||
- `user_agent`: Sets the 'User-Agent' HTTP header. Defaults to `Needle/{version} (Node.js {node_version})`.
|
||||
- `content_type`: Sets the 'Content-Type' header. Unset by default, unless you're sending data in which case it's set accordingly to whatever is being sent (`application/x-www-form-urlencoded`, `application/json` or `multipart/form-data`). That is, of course, unless the option is passed, either here or through `options.headers`. You're the boss.
|
||||
|
||||
Node.js TLS Options
|
||||
-------------------
|
||||
|
||||
These options are passed directly to `https.request` if present. Taken from the [original documentation](http://nodejs.org/docs/latest/api/https.html):
|
||||
|
||||
- `pfx` : Certificate, Private key and CA certificates to use for SSL.
|
||||
- `key` : Private key to use for SSL.
|
||||
- `passphrase` : A string of passphrase for the private key or pfx.
|
||||
- `cert` : Public x509 certificate to use.
|
||||
- `ca` : An authority certificate or array of authority certificates to check the remote host against.
|
||||
- `ciphers` : A string describing the ciphers to use or exclude.
|
||||
- `rejectUnauthorized` : If true, the server certificate is verified against the list of supplied CAs. An 'error' event is emitted if verification fails. Verification happens at the connection level, before the HTTP request is sent.
|
||||
- `secureProtocol` : The SSL method to use, e.g. SSLv3_method to force SSL version 3.
|
||||
- `family` : IP address family to use when resolving host and hostname. Valid values are 4 or 6. When unspecified, both IP v4 and v6 will be used.
|
||||
|
||||
Redirect options
|
||||
----------------
|
||||
|
||||
These options only apply if the `follow_max` (or `follow`) option is higher than 0.
|
||||
|
||||
- `follow_set_cookies` : Sends the cookies received in the `set-cookie` header as part of the following request. `false` by default.
|
||||
- `follow_set_referer` : Sets the 'Referer' header to the requested URI when following a redirect. `false` by default.
|
||||
- `follow_keep_method` : If enabled, resends the request using the original verb instead of being rewritten to `get` with no data. `false` by default.
|
||||
- `follow_if_same_host` : When true, Needle will only follow redirects that point to the same host as the original request. `false` by default.
|
||||
- `follow_if_same_protocol` : When true, Needle will only follow redirects that point to the same protocol as the original request. `false` by default.
|
||||
- `follow_if_same_location` : Unless true, Needle will not follow redirects that point to same location (as set in the response header) as the original request URL. `false` by default.
|
||||
|
||||
Overriding Defaults
|
||||
-------------------
|
||||
|
||||
Yes sir, we have it. Needle includes a `defaults()` method, that lets you override some of the defaults for all future requests. Like this:
|
||||
|
||||
```js
|
||||
needle.defaults({
|
||||
open_timeout: 60000,
|
||||
user_agent: 'MyApp/1.2.3',
|
||||
parse_response: false });
|
||||
```
|
||||
|
||||
This will override Needle's default user agent and 10-second timeout, and disable response parsing, so you don't need to pass those options in every other request.
|
||||
|
||||
More advanced Proxy support
|
||||
---------------------------
|
||||
|
||||
Since you can pass a custom HTTPAgent to Needle you can do all sorts of neat stuff. For example, if you want to use the [`tunnel`](https://github.com/koichik/node-tunnel) module for HTTPS proxying, you can do this:
|
||||
|
||||
```js
|
||||
var tunnel = require('tunnel');
|
||||
var myAgent = tunnel.httpOverHttp({
|
||||
proxy: { host: 'localhost' }
|
||||
});
|
||||
|
||||
needle.get('foobar.com', { agent: myAgent });
|
||||
```
|
||||
|
||||
Otherwise, you can use the [`hpagent`](https://github.com/delvedor/hpagent) package, which keeps the internal sockets alive to be reused.
|
||||
|
||||
```js
|
||||
const { HttpsProxyAgent } = require('hpagent');
|
||||
needle('get', 'https://localhost:9200', {
|
||||
agent: new HttpsProxyAgent({
|
||||
keepAlive: true,
|
||||
keepAliveMsecs: 1000,
|
||||
maxSockets: 256,
|
||||
maxFreeSockets: 256,
|
||||
scheduling: 'lifo',
|
||||
proxy: 'https://localhost:8080'
|
||||
})
|
||||
});
|
||||
```
|
||||
|
||||
Regarding the 'Connection' header
|
||||
---------------------------------
|
||||
|
||||
Unless you're running an old version of Node (< 0.11.4), by default Needle won't set the Connection header on requests, yielding Node's default behaviour of keeping the connection alive with the target server. This speeds up immensely the process of sending several requests to the same host.
|
||||
|
||||
On older versions, however, this has the unwanted behaviour of preventing the runtime from exiting, either because of a bug or 'feature' that was changed on 0.11.4. To overcome this Needle does set the 'Connection' header to 'close' on those versions, however this also means that making new requests to the same host doesn't benefit from Keep-Alive.
|
||||
|
||||
So if you're stuck on 0.10 or even lower and want full speed, you can simply set the Connection header to 'Keep-Alive' by using `{ connection: 'Keep-Alive' }`. Please note, though, that an event loop handler will prevent the runtime from exiting so you'll need to manually call `process.exit()` or the universe will collapse.
|
||||
|
||||
Examples Galore
|
||||
---------------
|
||||
|
||||
### HTTPS GET with Basic Auth
|
||||
|
||||
```js
|
||||
needle.get('https://api.server.com', { username: 'you', password: 'secret' },
|
||||
function(err, resp) {
|
||||
// used HTTP auth
|
||||
});
|
||||
```
|
||||
|
||||
Or use [RFC-1738](http://tools.ietf.org/html/rfc1738#section-3.1) basic auth URL syntax:
|
||||
|
||||
```js
|
||||
needle.get('https://username:password@api.server.com', function(err, resp) {
|
||||
// used HTTP auth from URL
|
||||
});
|
||||
```
|
||||
|
||||
### Digest Auth
|
||||
|
||||
```js
|
||||
needle.get('other.server.com', { username: 'you', password: 'secret', auth: 'digest' },
|
||||
function(err, resp, body) {
|
||||
// needle prepends 'http://' to your URL, if missing
|
||||
});
|
||||
```
|
||||
|
||||
### Custom Accept header, deflate
|
||||
|
||||
```js
|
||||
var options = {
|
||||
compressed : true,
|
||||
follow : 10,
|
||||
accept : 'application/vnd.github.full+json'
|
||||
}
|
||||
|
||||
needle.get('api.github.com/users/tomas', options, function(err, resp, body) {
|
||||
// body will contain a JSON.parse(d) object
|
||||
// if parsing fails, you'll simply get the original body
|
||||
});
|
||||
```
|
||||
|
||||
### GET XML object
|
||||
|
||||
```js
|
||||
needle.get('https://news.ycombinator.com/rss', function(err, resp, body) {
|
||||
// you'll get a nice object containing the nodes in the RSS
|
||||
});
|
||||
```
|
||||
|
||||
### GET binary, output to file
|
||||
|
||||
```js
|
||||
needle.get('http://upload.server.com/tux.png', { output: '/tmp/tux.png' }, function(err, resp, body) {
|
||||
// you can dump any response to a file, not only binaries.
|
||||
});
|
||||
```
|
||||
|
||||
### GET through proxy
|
||||
|
||||
```js
|
||||
needle.get('http://search.npmjs.org', { proxy: 'http://localhost:1234' }, function(err, resp, body) {
|
||||
// request passed through proxy
|
||||
});
|
||||
```
|
||||
|
||||
### GET a very large document in a stream (from 0.7+)
|
||||
|
||||
```js
|
||||
var stream = needle.get('http://www.as35662.net/100.log');
|
||||
|
||||
stream.on('readable', function() {
|
||||
var chunk;
|
||||
while (chunk = this.read()) {
|
||||
console.log('got data: ', chunk);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### GET JSON object in a stream (from 0.7+)
|
||||
|
||||
```js
|
||||
var stream = needle.get('http://jsonplaceholder.typicode.com/db', { parse: true });
|
||||
|
||||
stream.on('readable', function() {
|
||||
var node;
|
||||
|
||||
// our stream will only emit a single JSON root node.
|
||||
while (node = this.read()) {
|
||||
console.log('got data: ', node);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### GET JSONStream flexible parser with search query (from 0.7+)
|
||||
|
||||
```js
|
||||
|
||||
// The 'data' element of this stream will be the string representation
|
||||
// of the titles of all posts.
|
||||
|
||||
needle.get('http://jsonplaceholder.typicode.com/db', { parse: true })
|
||||
.pipe(new JSONStream.parse('posts.*.title'));
|
||||
.on('data', function (obj) {
|
||||
console.log('got post title: %s', obj);
|
||||
});
|
||||
```
|
||||
|
||||
### File upload using multipart, passing file path
|
||||
|
||||
```js
|
||||
var data = {
|
||||
foo: 'bar',
|
||||
image: { file: '/home/tomas/linux.png', content_type: 'image/png' }
|
||||
}
|
||||
|
||||
needle.post('http://my.other.app.com', data, { multipart: true }, function(err, resp, body) {
|
||||
// needle will read the file and include it in the form-data as binary
|
||||
});
|
||||
```
|
||||
|
||||
### Stream upload, PUT or POST
|
||||
|
||||
``` js
|
||||
needle.put('https://api.app.com/v2', fs.createReadStream('myfile.txt'), function(err, resp, body) {
|
||||
// stream content is uploaded verbatim
|
||||
});
|
||||
```
|
||||
|
||||
### Multipart POST, passing data buffer
|
||||
|
||||
```js
|
||||
var buffer = fs.readFileSync('/path/to/package.zip');
|
||||
|
||||
var data = {
|
||||
zip_file: {
|
||||
buffer : buffer,
|
||||
filename : 'mypackage.zip',
|
||||
content_type : 'application/octet-stream'
|
||||
}
|
||||
}
|
||||
|
||||
needle.post('http://somewhere.com/over/the/rainbow', data, { multipart: true }, function(err, resp, body) {
|
||||
// if you see, when using buffers we need to pass the filename for the multipart body.
|
||||
// you can also pass a filename when using the file path method, in case you want to override
|
||||
// the default filename to be received on the other end.
|
||||
});
|
||||
```
|
||||
|
||||
### Multipart with custom Content-Type
|
||||
|
||||
```js
|
||||
var data = {
|
||||
token: 'verysecret',
|
||||
payload: {
|
||||
value: JSON.stringify({ title: 'test', version: 1 }),
|
||||
content_type: 'application/json'
|
||||
}
|
||||
}
|
||||
|
||||
needle.post('http://test.com/', data, { timeout: 5000, multipart: true }, function(err, resp, body) {
|
||||
// in this case, if the request takes more than 5 seconds
|
||||
// the callback will return a [Socket closed] error
|
||||
});
|
||||
```
|
||||
|
||||
For even more examples, check out the examples directory in the repo.
|
||||
|
||||
### Testing
|
||||
|
||||
To run tests, you need to generate a self-signed SSL certificate in the `test` directory. After cloning the repository, run the following commands:
|
||||
|
||||
$ mkdir -p test/keys
|
||||
$ openssl genrsa -out test/keys/ssl.key 2048
|
||||
$ openssl req -new -key test/keys/ssl.key -x509 -days 999 -out test/keys/ssl.cert
|
||||
|
||||
Then you should be able to run `npm test` once you have the dependencies in place.
|
||||
|
||||
> Note: Tests currently only work on linux-based environments that have `/proc/self/fd`. They *do not* work on MacOS environments.
|
||||
> You can use Docker to run tests by creating a container and mounting the needle project directory on `/app`
|
||||
> `docker create --name Needle -v /app -w /app -v /app/node_modules -i node:argon`
|
||||
|
||||
Credits
|
||||
-------
|
||||
|
||||
Written by Tomás Pollak, with the help of contributors.
|
||||
|
||||
Copyright
|
||||
---------
|
||||
|
||||
(c) Fork Ltd. Licensed under the MIT license.
|
||||
40
node_modules/needle/bin/needle
generated
vendored
Normal file
40
node_modules/needle/bin/needle
generated
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
#!/usr/bin/env node
|
||||
var needle = require('./../lib/needle');
|
||||
|
||||
function exit(code, str) {
|
||||
console.log(str) || process.exit(code);
|
||||
}
|
||||
|
||||
function usage() {
|
||||
var out = ['Usage: needle [get|head|post|put|delete] url [query]'];
|
||||
out.push('Examples: \n needle get google.com\n needle post server.com/api foo=bar');
|
||||
exit(1, out.join('\n'))
|
||||
}
|
||||
|
||||
if (process.argv[2] == '-v' || process.argv[2] == '--version')
|
||||
exit(0, needle.version);
|
||||
else if (process.argv[2] == null)
|
||||
usage();
|
||||
|
||||
var method = process.argv[2],
|
||||
url = process.argv[3],
|
||||
options = { compressed: true, parse_response: true, follow_max: 5, timeout: 10000 };
|
||||
|
||||
if (!needle[method]) {
|
||||
url = method;
|
||||
method = 'get';
|
||||
}
|
||||
|
||||
var callback = function(err, resp) {
|
||||
if (err) return exit(1, "Error: " + err.message);
|
||||
|
||||
if (process.argv.indexOf('-i') != -1)
|
||||
console.log(resp.headers) || console.log('');
|
||||
|
||||
console.log(resp.body.toString());
|
||||
};
|
||||
|
||||
if (method == 'post' || method == 'put')
|
||||
needle[method](url, process.argv[4], options, callback);
|
||||
else
|
||||
needle[method](url, options, callback);
|
||||
22
node_modules/needle/examples/deflated-stream.js
generated
vendored
Normal file
22
node_modules/needle/examples/deflated-stream.js
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
var fs = require('fs'),
|
||||
stream = require('stream'),
|
||||
needle = require('./../');
|
||||
|
||||
var url = 'http://ibl.gamechaser.net/f/tagqfxtteucbuldhezkz/bt_level1.gz';
|
||||
|
||||
var resp = needle.get(url, { compressed: true, follow_max: 10 });
|
||||
console.log('Downloading...');
|
||||
|
||||
resp.on('readable', function() {
|
||||
|
||||
while (data = this.read()) {
|
||||
var lines = data.toString().split('\n');
|
||||
console.log('Got ' + lines.length + ' items.');
|
||||
// console.log(lines);
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
resp.on('done', function(data) {
|
||||
console.log('Done');
|
||||
})
|
||||
16
node_modules/needle/examples/digest-auth.js
generated
vendored
Normal file
16
node_modules/needle/examples/digest-auth.js
generated
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
var needle = require('./..');
|
||||
|
||||
var opts = {
|
||||
username: 'user3',
|
||||
password: 'user3',
|
||||
auth: 'digest'
|
||||
}
|
||||
|
||||
needle.get('http://test.webdav.org/auth-digest/', opts, function(err, resp, body) {
|
||||
console.log(resp.headers);
|
||||
|
||||
if (resp.statusCode == 401)
|
||||
console.log('\nIt failed.')
|
||||
else
|
||||
console.log('\nIt worked!')
|
||||
});
|
||||
18
node_modules/needle/examples/download-to-file.js
generated
vendored
Normal file
18
node_modules/needle/examples/download-to-file.js
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
var fs = require('fs'),
|
||||
needle = require('./..'),
|
||||
path = require('path');
|
||||
|
||||
var url = process.argv[2] || 'https://upload.wikimedia.org/wikipedia/commons/a/af/Tux.png';
|
||||
var file = path.basename(url);
|
||||
|
||||
console.log('Downloading ' + file);
|
||||
|
||||
needle.get(url, { output: file, follow: 3 }, function(err, resp, data){
|
||||
console.log('File saved: ' + process.cwd() + '/' + file);
|
||||
|
||||
var size = fs.statSync(file).size;
|
||||
if (size == resp.bytes)
|
||||
console.log(resp.bytes + ' bytes written to file.');
|
||||
else
|
||||
throw new Error('File size mismatch: ' + size + ' != ' + resp.bytes);
|
||||
});
|
||||
25
node_modules/needle/examples/multipart-stream.js
generated
vendored
Normal file
25
node_modules/needle/examples/multipart-stream.js
generated
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
var needle = require('./../');
|
||||
|
||||
var url = 'http://posttestserver.com/post.php?dir=needle';
|
||||
|
||||
var black_pixel = Buffer.from("R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs=", 'base64');
|
||||
|
||||
var data = {
|
||||
foo: 'bar',
|
||||
nested: {
|
||||
test: 123
|
||||
},
|
||||
image: { buffer: black_pixel, content_type: 'image/gif' }
|
||||
}
|
||||
|
||||
var resp = needle.post(url, data, { multipart: true });
|
||||
|
||||
resp.on('readable', function() {
|
||||
while (data = this.read()) {
|
||||
console.log(data.toString());
|
||||
}
|
||||
})
|
||||
|
||||
resp.on('done', function(data) {
|
||||
console.log('Done.');
|
||||
})
|
||||
23
node_modules/needle/examples/parsed-stream.js
generated
vendored
Normal file
23
node_modules/needle/examples/parsed-stream.js
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
//////////////////////////////////////////
|
||||
// This example demonstrates what happends
|
||||
// when you use the built-in JSON parser.
|
||||
//////////////////////////////////////////
|
||||
|
||||
var fs = require('fs'),
|
||||
stream = require('stream'),
|
||||
needle = require('./../');
|
||||
|
||||
var url = 'http://ip.jsontest.com/',
|
||||
resp = needle.get(url, { parse: true });
|
||||
|
||||
resp.on('readable', function(obj) {
|
||||
var chunk;
|
||||
|
||||
while (chunk = this.read()) {
|
||||
console.log('root = ', chunk);
|
||||
}
|
||||
});
|
||||
|
||||
resp.on('done', function() {
|
||||
console.log('Done.');
|
||||
});
|
||||
21
node_modules/needle/examples/parsed-stream2.js
generated
vendored
Normal file
21
node_modules/needle/examples/parsed-stream2.js
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
//////////////////////////////////////////
|
||||
// This example illustrates a more complex
|
||||
// example of parsing a JSON stream.
|
||||
//////////////////////////////////////////
|
||||
|
||||
var needle = require('./../'),
|
||||
JSONStream = require('JSONStream');
|
||||
|
||||
var url = 'http://jsonplaceholder.typicode.com/db';
|
||||
|
||||
// Initialize our GET request with our default (JSON)
|
||||
// parsers disabled.
|
||||
|
||||
var json = new needle.get(url, {parse: false})
|
||||
// And now interpret the stream as JSON, returning only the
|
||||
// title of all the posts.
|
||||
.pipe(new JSONStream.parse('posts.*.title'));
|
||||
|
||||
json.on('data', function (obj) {
|
||||
console.log('got title: \'' + obj + '\'');
|
||||
})
|
||||
23
node_modules/needle/examples/stream-events.js
generated
vendored
Normal file
23
node_modules/needle/examples/stream-events.js
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
var needle = require('./..');
|
||||
|
||||
var resp = needle.get('google.com', { follow_max: 10, timeout: 5000 });
|
||||
|
||||
resp.on('readable', function() {
|
||||
var chunk;
|
||||
while (chunk = this.read()) {
|
||||
console.log('Got ' + chunk.length + ' bytes');
|
||||
}
|
||||
})
|
||||
|
||||
resp.on('headers', function(headers) {
|
||||
console.log('Got headers', headers);
|
||||
})
|
||||
|
||||
resp.on('redirect', function(url) {
|
||||
console.log('Redirected to url ' + url);
|
||||
})
|
||||
|
||||
resp.on('done', function(err) {
|
||||
console.log('Finished. No more data to receive.');
|
||||
if (err) console.log('With error', err)
|
||||
})
|
||||
14
node_modules/needle/examples/stream-to-file.js
generated
vendored
Normal file
14
node_modules/needle/examples/stream-to-file.js
generated
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
var fs = require('fs'),
|
||||
needle = require('./..'),
|
||||
path = require('path');
|
||||
|
||||
var url = process.argv[2] || 'http://www.google.com/images/errors/robot.png';
|
||||
var file = path.basename(url);
|
||||
|
||||
console.log('Downloading ' + file + '...');
|
||||
needle
|
||||
.get(url)
|
||||
.pipe(fs.createWriteStream(file))
|
||||
.on('done', function() {
|
||||
console.log('Done!')
|
||||
})
|
||||
51
node_modules/needle/examples/upload-image.js
generated
vendored
Normal file
51
node_modules/needle/examples/upload-image.js
generated
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
var needle = require('../'),
|
||||
path = require('path');
|
||||
|
||||
var image = 'https://upload.wikimedia.org/wikipedia/commons/a/af/Tux.png';
|
||||
|
||||
function upload(obj, cb) {
|
||||
console.log('Uploading image...');
|
||||
|
||||
var url = 'http://deviantsart.com';
|
||||
|
||||
var opts = {
|
||||
timeout: 10000,
|
||||
follow: 3,
|
||||
multipart: true
|
||||
};
|
||||
|
||||
var params = {
|
||||
file: obj
|
||||
}
|
||||
|
||||
needle.post(url, params, opts, function(err, resp) {
|
||||
if (err || !resp.body.match('url'))
|
||||
return cb(err || new Error('No image URL found.'))
|
||||
|
||||
cb(null, JSON.parse(resp.body).url)
|
||||
})
|
||||
}
|
||||
|
||||
function download(url, cb) {
|
||||
console.log('Getting ' + url);
|
||||
needle.get(url, function(err, resp) {
|
||||
if (err) throw err;
|
||||
|
||||
cb(null, resp.body);
|
||||
})
|
||||
}
|
||||
|
||||
////////////////////////////////////////
|
||||
// ok, now go.
|
||||
|
||||
download(image, function(err, buffer) {
|
||||
if (err) throw err;
|
||||
|
||||
var obj = { buffer: buffer, content_type: 'image/png' };
|
||||
|
||||
upload(obj, function(err, url) {
|
||||
if (err) throw err;
|
||||
|
||||
console.log('Image uploaded to ' + url);
|
||||
})
|
||||
})
|
||||
19
node_modules/needle/license.txt
generated
vendored
Normal file
19
node_modules/needle/license.txt
generated
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
Copyright (c) Fork, Ltd.
|
||||
|
||||
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.
|
||||
102
node_modules/needle/package.json
generated
vendored
Normal file
102
node_modules/needle/package.json
generated
vendored
Normal file
@@ -0,0 +1,102 @@
|
||||
{
|
||||
"_from": "needle@^2.6.0",
|
||||
"_id": "needle@2.6.0",
|
||||
"_inBundle": false,
|
||||
"_integrity": "sha512-KKYdza4heMsEfSWD7VPUIz3zX2XDwOyX2d+geb4vrERZMT5RMU6ujjaD+I5Yr54uZxQ2w6XRTAhHBbSCyovZBg==",
|
||||
"_location": "/needle",
|
||||
"_phantomChildren": {},
|
||||
"_requested": {
|
||||
"type": "range",
|
||||
"registry": true,
|
||||
"raw": "needle@^2.6.0",
|
||||
"name": "needle",
|
||||
"escapedName": "needle",
|
||||
"rawSpec": "^2.6.0",
|
||||
"saveSpec": null,
|
||||
"fetchSpec": "^2.6.0"
|
||||
},
|
||||
"_requiredBy": [
|
||||
"/@discordjs/node-pre-gyp"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/needle/-/needle-2.6.0.tgz",
|
||||
"_shasum": "24dbb55f2509e2324b4a99d61f413982013ccdbe",
|
||||
"_spec": "needle@^2.6.0",
|
||||
"_where": "C:\\Users\\MCGFX\\OneDrive\\Dokumente\\GitHub\\UnknownBot\\node_modules\\@discordjs\\node-pre-gyp",
|
||||
"author": {
|
||||
"name": "Tomás Pollak",
|
||||
"email": "tomas@forkhq.com"
|
||||
},
|
||||
"bin": {
|
||||
"needle": "bin/needle"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/tomas/needle/issues"
|
||||
},
|
||||
"bundleDependencies": false,
|
||||
"dependencies": {
|
||||
"debug": "^3.2.6",
|
||||
"iconv-lite": "^0.4.4",
|
||||
"sax": "^1.2.4"
|
||||
},
|
||||
"deprecated": false,
|
||||
"description": "The leanest and most handsome HTTP client in the Nodelands.",
|
||||
"devDependencies": {
|
||||
"JSONStream": "^1.3.5",
|
||||
"jschardet": "^1.6.0",
|
||||
"mocha": "^5.2.0",
|
||||
"q": "^1.5.1",
|
||||
"should": "^13.2.3",
|
||||
"sinon": "^2.3.0",
|
||||
"xml2js": "^0.4.19"
|
||||
},
|
||||
"directories": {
|
||||
"lib": "./lib"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 4.4.x"
|
||||
},
|
||||
"homepage": "https://github.com/tomas/needle#readme",
|
||||
"keywords": [
|
||||
"http",
|
||||
"https",
|
||||
"simple",
|
||||
"request",
|
||||
"client",
|
||||
"multipart",
|
||||
"upload",
|
||||
"proxy",
|
||||
"deflate",
|
||||
"timeout",
|
||||
"charset",
|
||||
"iconv",
|
||||
"cookie",
|
||||
"redirect"
|
||||
],
|
||||
"license": "MIT",
|
||||
"main": "./lib/needle",
|
||||
"name": "needle",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/tomas/needle.git"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "mocha test"
|
||||
},
|
||||
"tags": [
|
||||
"http",
|
||||
"https",
|
||||
"simple",
|
||||
"request",
|
||||
"client",
|
||||
"multipart",
|
||||
"upload",
|
||||
"proxy",
|
||||
"deflate",
|
||||
"timeout",
|
||||
"charset",
|
||||
"iconv",
|
||||
"cookie",
|
||||
"redirect"
|
||||
],
|
||||
"version": "2.6.0"
|
||||
}
|
||||
192
node_modules/needle/test/auth_digest_spec.js
generated
vendored
Normal file
192
node_modules/needle/test/auth_digest_spec.js
generated
vendored
Normal file
@@ -0,0 +1,192 @@
|
||||
var needle = require('../'),
|
||||
auth = require('../lib/auth'),
|
||||
sinon = require('sinon'),
|
||||
should = require('should'),
|
||||
http = require('http'),
|
||||
helpers = require('./helpers');
|
||||
|
||||
var createHash = require('crypto').createHash;
|
||||
|
||||
function md5(string) {
|
||||
return createHash('md5').update(string).digest('hex');
|
||||
}
|
||||
|
||||
function parse_header(header) {
|
||||
var challenge = {},
|
||||
matches = header.match(/([a-z0-9_-]+)="?([a-z0-9=\/\.@\s-\+]+)"?/gi);
|
||||
|
||||
for (var i = 0, l = matches.length; i < l; i++) {
|
||||
var parts = matches[i].split('='),
|
||||
key = parts.shift(),
|
||||
val = parts.join('=').replace(/^"/, '').replace(/"$/, '');
|
||||
|
||||
challenge[key] = val;
|
||||
}
|
||||
|
||||
return challenge;
|
||||
}
|
||||
|
||||
describe('auth_digest', function() {
|
||||
describe('With qop (RFC 2617)', function() {
|
||||
it('should generate a proper header', function() {
|
||||
// from https://tools.ietf.org/html/rfc2617
|
||||
var performDigest = function() {
|
||||
var header = 'Digest realm="testrealm@host.com", qop="auth,auth-int", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41"';
|
||||
var user = 'Mufasa';
|
||||
var pass = 'Circle Of Life';
|
||||
var method = 'get';
|
||||
var path = '/dir/index.html';
|
||||
|
||||
var updatedHeader = auth.digest(header, user, pass, method, path);
|
||||
var parsedUpdatedHeader = parse_header(updatedHeader);
|
||||
|
||||
var ha1 = md5(user + ':' + parsedUpdatedHeader.realm + ':' + pass);
|
||||
var ha2 = md5(method.toUpperCase() + ':' + path);
|
||||
var expectedResponse = md5([
|
||||
ha1,
|
||||
parsedUpdatedHeader.nonce,
|
||||
parsedUpdatedHeader.nc,
|
||||
parsedUpdatedHeader.cnonce,
|
||||
parsedUpdatedHeader.qop,
|
||||
ha2
|
||||
].join(':'));
|
||||
|
||||
return {
|
||||
header: updatedHeader,
|
||||
parsed: parsedUpdatedHeader,
|
||||
expectedResponse: expectedResponse,
|
||||
}
|
||||
}
|
||||
|
||||
const result = performDigest();
|
||||
|
||||
(result.header).should
|
||||
.match(/qop="auth"/)
|
||||
.match(/uri="\/dir\/index.html"/)
|
||||
.match(/opaque="5ccc069c403ebaf9f0171e9517f40e41"/)
|
||||
.match(/realm="testrealm@host\.com"/)
|
||||
.match(/response=/)
|
||||
.match(/nc=/)
|
||||
.match(/nonce=/)
|
||||
.match(/cnonce=/);
|
||||
|
||||
(result.parsed.response).should.be.eql(result.expectedResponse);
|
||||
});
|
||||
});
|
||||
|
||||
describe('With plus character in nonce header', function() {
|
||||
it('should generate a proper header', function() {
|
||||
// from https://tools.ietf.org/html/rfc2617
|
||||
var performDigest = function() {
|
||||
var header = 'Digest realm="testrealm@host.com", qop="auth,auth-int", nonce="dcd98b7102dd2f0e8b11d0f6+00bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41"';
|
||||
var user = 'Mufasa';
|
||||
var pass = 'Circle Of Life';
|
||||
var method = 'get';
|
||||
var path = '/dir/index.html';
|
||||
|
||||
var updatedHeader = auth.digest(header, user, pass, method, path);
|
||||
var parsedUpdatedHeader = parse_header(updatedHeader);
|
||||
|
||||
var ha1 = md5(user + ':' + parsedUpdatedHeader.realm + ':' + pass);
|
||||
var ha2 = md5(method.toUpperCase() + ':' + path);
|
||||
var expectedResponse = md5([
|
||||
ha1,
|
||||
parsedUpdatedHeader.nonce,
|
||||
parsedUpdatedHeader.nc,
|
||||
parsedUpdatedHeader.cnonce,
|
||||
parsedUpdatedHeader.qop,
|
||||
ha2
|
||||
].join(':'));
|
||||
|
||||
return {
|
||||
header: updatedHeader,
|
||||
parsed: parsedUpdatedHeader,
|
||||
expectedResponse: expectedResponse,
|
||||
}
|
||||
}
|
||||
|
||||
const result = performDigest();
|
||||
|
||||
(result.header).should
|
||||
.match(/nonce="dcd98b7102dd2f0e8b11d0f6\+00bfb0c093"/)
|
||||
});
|
||||
});
|
||||
|
||||
describe('With brackets in realm header', function() {
|
||||
it('should generate a proper header', function() {
|
||||
// from https://tools.ietf.org/html/rfc2617
|
||||
var performDigest = function() {
|
||||
var header = 'Digest qop="auth", realm="IP Camera(76475)", nonce="4e4449794d575269597a706b5a575935595441324d673d3d", stale="FALSE", Basic realm="IP Camera(76475)"';
|
||||
var user = 'Mufasa';
|
||||
var pass = 'Circle Of Life';
|
||||
var method = 'get';
|
||||
var path = '/dir/index.html';
|
||||
|
||||
var updatedHeader = auth.digest(header, user, pass, method, path);
|
||||
var parsedUpdatedHeader = parse_header(updatedHeader);
|
||||
|
||||
var ha1 = md5(user + ':' + parsedUpdatedHeader.realm + ':' + pass);
|
||||
var ha2 = md5(method.toUpperCase() + ':' + path);
|
||||
var expectedResponse = md5([
|
||||
ha1,
|
||||
parsedUpdatedHeader.nonce,
|
||||
parsedUpdatedHeader.nc,
|
||||
parsedUpdatedHeader.cnonce,
|
||||
parsedUpdatedHeader.qop,
|
||||
ha2
|
||||
].join(':'));
|
||||
|
||||
return {
|
||||
header: updatedHeader,
|
||||
parsed: parsedUpdatedHeader,
|
||||
expectedResponse: expectedResponse,
|
||||
}
|
||||
}
|
||||
|
||||
const result = performDigest();
|
||||
|
||||
(result.header).should
|
||||
.match(/realm="IP Camera\(76475\)"/)
|
||||
});
|
||||
});
|
||||
|
||||
describe('Without qop (RFC 2617)', function() {
|
||||
it('should generate a proper header', function() {
|
||||
// from https://tools.ietf.org/html/rfc2069
|
||||
var performDigest = function() {
|
||||
var header = 'Digest realm="testrealm@host.com", nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093", opaque="5ccc069c403ebaf9f0171e9517f40e41"';
|
||||
var user = 'Mufasa';
|
||||
var pass = 'Circle Of Life';
|
||||
var method = 'get';
|
||||
var path = '/dir/index.html';
|
||||
|
||||
var updatedHeader = auth.digest(header, user, pass, method, path);
|
||||
var parsedUpdatedHeader = parse_header(updatedHeader);
|
||||
|
||||
var ha1 = md5(user + ':' + parsedUpdatedHeader.realm + ':' + pass);
|
||||
var ha2 = md5(method.toUpperCase() + ':' + path);
|
||||
var expectedResponse = md5([ha1, parsedUpdatedHeader.nonce, ha2].join(':'));
|
||||
|
||||
return {
|
||||
header: updatedHeader,
|
||||
parsed: parsedUpdatedHeader,
|
||||
expectedResponse: expectedResponse,
|
||||
}
|
||||
}
|
||||
|
||||
const result = performDigest();
|
||||
|
||||
(result.header).should
|
||||
.not.match(/qop=/)
|
||||
.match(/uri="\/dir\/index.html"/)
|
||||
.match(/opaque="5ccc069c403ebaf9f0171e9517f40e41"/)
|
||||
.match(/realm="testrealm@host\.com"/)
|
||||
.match(/response=/)
|
||||
.not.match(/nc=/)
|
||||
.match(/nonce=/)
|
||||
.not.match(/cnonce=/);
|
||||
|
||||
(result.parsed.response).should.be.eql(result.expectedResponse);
|
||||
});
|
||||
});
|
||||
})
|
||||
196
node_modules/needle/test/basic_auth_spec.js
generated
vendored
Normal file
196
node_modules/needle/test/basic_auth_spec.js
generated
vendored
Normal file
@@ -0,0 +1,196 @@
|
||||
var helpers = require('./helpers'),
|
||||
should = require('should'),
|
||||
needle = require('./../'),
|
||||
server;
|
||||
|
||||
var port = 7707;
|
||||
|
||||
describe('Basic Auth', function() {
|
||||
|
||||
before(function(done) {
|
||||
server = helpers.server({ port: port }, done);
|
||||
})
|
||||
|
||||
after(function(done) {
|
||||
server.close(done);
|
||||
})
|
||||
|
||||
///////////////// helpers
|
||||
|
||||
var get_auth = function(header) {
|
||||
var token = header.split(/\s+/).pop();
|
||||
return token && Buffer.from(token, 'base64').toString().split(':');
|
||||
}
|
||||
|
||||
describe('when neither username or password are passed', function() {
|
||||
|
||||
it('doesnt send any Authorization headers', function(done) {
|
||||
needle.get('localhost:' + port, { parse: true }, function(err, resp) {
|
||||
var sent_headers = resp.body.headers;
|
||||
Object.keys(sent_headers).should.not.containEql('authorization');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('when username is an empty string, and password is a valid string', function() {
|
||||
|
||||
var opts = { username: '', password: 'foobar', parse: true };
|
||||
|
||||
it('doesnt send any Authorization headers', function(done) {
|
||||
needle.get('localhost:' + port, { parse: true }, function(err, resp) {
|
||||
var sent_headers = resp.body.headers;
|
||||
Object.keys(sent_headers).should.not.containEql('authorization');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
describe('when username is a valid string, but no username is passed', function() {
|
||||
|
||||
var opts = { username: 'foobar', parse: true };
|
||||
|
||||
it('sends Authorization header', function(done) {
|
||||
needle.get('localhost:' + port, opts, function(err, resp) {
|
||||
var sent_headers = resp.body.headers;
|
||||
Object.keys(sent_headers).should.containEql('authorization');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('Basic Auth only includes username, without colon', function(done) {
|
||||
needle.get('localhost:' + port, opts, function(err, resp) {
|
||||
var sent_headers = resp.body.headers;
|
||||
var auth = get_auth(sent_headers['authorization']);
|
||||
auth[0].should.equal('foobar');
|
||||
auth.should.have.lengthOf(1);
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('when username is a valid string, and password is null', function() {
|
||||
|
||||
var opts = { username: 'foobar', password: null, parse: true };
|
||||
|
||||
it('sends Authorization header', function(done) {
|
||||
needle.get('localhost:' + port, opts, function(err, resp) {
|
||||
var sent_headers = resp.body.headers;
|
||||
Object.keys(sent_headers).should.containEql('authorization');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('Basic Auth only includes both username and password', function(done) {
|
||||
needle.get('localhost:' + port, opts, function(err, resp) {
|
||||
var sent_headers = resp.body.headers;
|
||||
var auth = get_auth(sent_headers['authorization']);
|
||||
auth[0].should.equal('foobar');
|
||||
auth[1].should.equal('');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('when username is a valid string, and password is an empty string', function() {
|
||||
|
||||
var opts = { username: 'foobar', password: '', parse: true };
|
||||
|
||||
it('sends Authorization header', function(done) {
|
||||
needle.get('localhost:' + port, opts, function(err, resp) {
|
||||
var sent_headers = resp.body.headers;
|
||||
Object.keys(sent_headers).should.containEql('authorization');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('Basic Auth only includes both username and password', function(done) {
|
||||
needle.get('localhost:' + port, opts, function(err, resp) {
|
||||
var sent_headers = resp.body.headers;
|
||||
var auth = get_auth(sent_headers['authorization']);
|
||||
auth[0].should.equal('foobar');
|
||||
auth[1].should.equal('');
|
||||
auth.should.have.lengthOf(2);
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('when username AND password are non empty strings', function() {
|
||||
|
||||
var opts = { username: 'foobar', password: 'jakub', parse: true };
|
||||
|
||||
it('sends Authorization header', function(done) {
|
||||
needle.get('localhost:' + port, opts, function(err, resp) {
|
||||
var sent_headers = resp.body.headers;
|
||||
Object.keys(sent_headers).should.containEql('authorization');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('Basic Auth only includes both user and password', function(done) {
|
||||
needle.get('localhost:' + port, opts, function(err, resp) {
|
||||
var sent_headers = resp.body.headers;
|
||||
var auth = get_auth(sent_headers['authorization']);
|
||||
auth[0].should.equal('foobar');
|
||||
auth[1].should.equal('jakub');
|
||||
auth.should.have.lengthOf(2);
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('URL with @ but not username/pass', function() {
|
||||
it('doesnt send Authorization header', function(done) {
|
||||
var url = 'localhost:' + port + '/abc/@def/xyz.zip';
|
||||
|
||||
needle.get(url, {}, function(err, resp) {
|
||||
var sent_headers = resp.body.headers;
|
||||
Object.keys(sent_headers).should.not.containEql('authorization');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('sends user:pass headers if passed via options', function(done) {
|
||||
var url = 'localhost:' + port + '/abc/@def/xyz.zip';
|
||||
|
||||
needle.get(url, { username: 'foo' }, function(err, resp) {
|
||||
var sent_headers = resp.body.headers;
|
||||
Object.keys(sent_headers).should.containEql('authorization');
|
||||
sent_headers['authorization'].should.eql('Basic Zm9v')
|
||||
done();
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('when username/password are included in URL', function() {
|
||||
var opts = { parse: true };
|
||||
|
||||
it('sends Authorization header', function(done) {
|
||||
needle.get('foobar:jakub@localhost:' + port, opts, function(err, resp) {
|
||||
var sent_headers = resp.body.headers;
|
||||
Object.keys(sent_headers).should.containEql('authorization');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('Basic Auth only includes both user and password', function(done) {
|
||||
needle.get('foobar:jakub@localhost:' + port, opts, function(err, resp) {
|
||||
var sent_headers = resp.body.headers;
|
||||
var auth = get_auth(sent_headers['authorization']);
|
||||
auth[0].should.equal('foobar');
|
||||
auth[1].should.equal('jakub');
|
||||
auth.should.have.lengthOf(2);
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
112
node_modules/needle/test/compression_spec.js
generated
vendored
Normal file
112
node_modules/needle/test/compression_spec.js
generated
vendored
Normal file
@@ -0,0 +1,112 @@
|
||||
var should = require('should'),
|
||||
needle = require('./../'),
|
||||
http = require('http'),
|
||||
zlib = require('zlib'),
|
||||
stream = require('stream'),
|
||||
port = 11123,
|
||||
server;
|
||||
|
||||
describe('compression', function(){
|
||||
|
||||
require.bind(null, 'zlib').should.not.throw()
|
||||
|
||||
var jsonData = '{"foo":"bar"}';
|
||||
|
||||
describe('when server supports compression', function(){
|
||||
|
||||
before(function(){
|
||||
server = http.createServer(function(req, res) {
|
||||
var raw = new stream.PassThrough();
|
||||
|
||||
var acceptEncoding = req.headers['accept-encoding'];
|
||||
if (!acceptEncoding) {
|
||||
acceptEncoding = '';
|
||||
}
|
||||
|
||||
if (acceptEncoding.match(/\bdeflate\b/)) {
|
||||
res.setHeader('Content-Encoding', 'deflate');
|
||||
raw.pipe(zlib.createDeflate()).pipe(res);
|
||||
} else if (acceptEncoding.match(/\bgzip\b/)) {
|
||||
res.setHeader('Content-Encoding', 'gzip');
|
||||
raw.pipe(zlib.createGzip()).pipe(res);
|
||||
} else if (acceptEncoding.match(/\bbr\b/)) {
|
||||
res.setHeader('Content-Encoding', 'br');
|
||||
raw.pipe(zlib.createBrotliCompress()).pipe(res);
|
||||
} else {
|
||||
raw.pipe(res);
|
||||
}
|
||||
|
||||
res.setHeader('Content-Type', 'application/json')
|
||||
if (req.headers['with-bad']) {
|
||||
res.end('foo'); // end, no deflate data
|
||||
} else {
|
||||
raw.end(jsonData)
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
server.listen(port);
|
||||
});
|
||||
|
||||
after(function(done){
|
||||
server.close(done);
|
||||
})
|
||||
|
||||
describe('and client requests no compression', function() {
|
||||
it('should have the body decompressed', function(done){
|
||||
needle.get('localhost:' + port, function(err, response, body){
|
||||
should.ifError(err);
|
||||
body.should.have.property('foo', 'bar');
|
||||
response.bytes.should.equal(jsonData.length);
|
||||
done();
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('and client requests gzip compression', function() {
|
||||
it('should have the body decompressed', function(done){
|
||||
needle.get('localhost:' + port, {headers: {'Accept-Encoding': 'gzip'}}, function(err, response, body){
|
||||
should.ifError(err);
|
||||
body.should.have.property('foo', 'bar');
|
||||
response.bytes.should.not.equal(jsonData.length);
|
||||
done();
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('and client requests deflate compression', function() {
|
||||
it('should have the body decompressed', function(done){
|
||||
needle.get('localhost:' + port, {headers: {'Accept-Encoding': 'deflate'}}, function(err, response, body){
|
||||
should.ifError(err);
|
||||
body.should.have.property('foo', 'bar');
|
||||
response.bytes.should.not.equal(jsonData.length);
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('should rethrow errors from decompressors', function(done){
|
||||
needle.get('localhost:' + port, {headers: {'Accept-Encoding': 'deflate', 'With-Bad': 'true'}}, function(err, response, body) {
|
||||
should.exist(err);
|
||||
err.message.should.equal("incorrect header check");
|
||||
err.code.should.equal("Z_DATA_ERROR")
|
||||
done();
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('and client requests brotli compression', function() {
|
||||
it('should have the body decompressed', function(done){
|
||||
// Skip this test if Brotli is not supported
|
||||
if (typeof zlib.BrotliDecompress !== 'function') {
|
||||
return done();
|
||||
}
|
||||
needle.get('localhost:' + port, {headers: {'Accept-Encoding': 'br'}}, function(err, response, body){
|
||||
should.ifError(err);
|
||||
body.should.have.property('foo', 'bar');
|
||||
response.bytes.should.not.equal(jsonData.length);
|
||||
done();
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
396
node_modules/needle/test/cookies_spec.js
generated
vendored
Normal file
396
node_modules/needle/test/cookies_spec.js
generated
vendored
Normal file
@@ -0,0 +1,396 @@
|
||||
var needle = require('../'),
|
||||
cookies = require('../lib/cookies'),
|
||||
sinon = require('sinon'),
|
||||
http = require('http'),
|
||||
should = require('should');
|
||||
|
||||
var WEIRD_COOKIE_NAME = 'wc',
|
||||
BASE64_COOKIE_NAME = 'bc',
|
||||
FORBIDDEN_COOKIE_NAME = 'fc',
|
||||
NUMBER_COOKIE_NAME = 'nc';
|
||||
|
||||
var WEIRD_COOKIE_VALUE = '!\'*+#()&-./0123456789:<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~',
|
||||
BASE64_COOKIE_VALUE = 'Y29va2llCg==',
|
||||
FORBIDDEN_COOKIE_VALUE = ' ;"\\,',
|
||||
NUMBER_COOKIE_VALUE = 12354342;
|
||||
|
||||
var NO_COOKIES_TEST_PORT = 11112,
|
||||
ALL_COOKIES_TEST_PORT = 11113;
|
||||
|
||||
describe('cookies', function() {
|
||||
|
||||
var setCookieHeader, headers, server, opts;
|
||||
|
||||
function decode(str) {
|
||||
return decodeURIComponent(str);
|
||||
}
|
||||
|
||||
function encode(str) {
|
||||
str = str.toString().replace(/[\x00-\x1F\x7F]/g, encodeURIComponent);
|
||||
return str.replace(/[\s\"\,;\\%]/g, encodeURIComponent);
|
||||
}
|
||||
|
||||
before(function() {
|
||||
setCookieHeader = [
|
||||
WEIRD_COOKIE_NAME + '=' + encode(WEIRD_COOKIE_VALUE) + ';',
|
||||
BASE64_COOKIE_NAME + '=' + encode(BASE64_COOKIE_VALUE) + ';',
|
||||
FORBIDDEN_COOKIE_NAME + '=' + encode(FORBIDDEN_COOKIE_VALUE) + ';',
|
||||
NUMBER_COOKIE_NAME + '=' + encode(NUMBER_COOKIE_VALUE) + ';'
|
||||
];
|
||||
});
|
||||
|
||||
before(function(done) {
|
||||
serverAllCookies = http.createServer(function(req, res) {
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.setHeader('Set-Cookie', setCookieHeader);
|
||||
res.end('200');
|
||||
}).listen(ALL_COOKIES_TEST_PORT, done);
|
||||
});
|
||||
|
||||
after(function(done) {
|
||||
serverAllCookies.close(done);
|
||||
});
|
||||
|
||||
describe('with default options', function() {
|
||||
it('no cookie header is set on request', function(done) {
|
||||
needle.get(
|
||||
'localhost:' + ALL_COOKIES_TEST_PORT, function(err, response) {
|
||||
should.not.exist(response.req._headers.cookie);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('if response does not contain cookies', function() {
|
||||
before(function(done) {
|
||||
serverNoCookies = http.createServer(function(req, res) {
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.end('200');
|
||||
}).listen(NO_COOKIES_TEST_PORT, done);
|
||||
});
|
||||
|
||||
it('response.cookies is undefined', function(done) {
|
||||
needle.get(
|
||||
'localhost:' + NO_COOKIES_TEST_PORT, function(error, response) {
|
||||
should.not.exist(response.cookies);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
after(function(done) {
|
||||
serverNoCookies.close(done);
|
||||
});
|
||||
});
|
||||
|
||||
describe('if response contains cookies', function() {
|
||||
|
||||
it('puts them on resp.cookies', function(done) {
|
||||
needle.get(
|
||||
'localhost:' + ALL_COOKIES_TEST_PORT, function(error, response) {
|
||||
response.should.have.property('cookies');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('parses them as a object', function(done) {
|
||||
needle.get(
|
||||
'localhost:' + ALL_COOKIES_TEST_PORT, function(error, response) {
|
||||
response.cookies.should.be.an.instanceOf(Object)
|
||||
.and.have.property(WEIRD_COOKIE_NAME);
|
||||
response.cookies.should.have.property(BASE64_COOKIE_NAME);
|
||||
response.cookies.should.have.property(FORBIDDEN_COOKIE_NAME);
|
||||
response.cookies.should.have.property(NUMBER_COOKIE_NAME);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('must decode it', function(done) {
|
||||
needle.get(
|
||||
'localhost:' + ALL_COOKIES_TEST_PORT, function(error, response) {
|
||||
response.cookies.wc.should.be.eql(WEIRD_COOKIE_VALUE);
|
||||
response.cookies.bc.should.be.eql(BASE64_COOKIE_VALUE);
|
||||
response.cookies.fc.should.be.eql(FORBIDDEN_COOKIE_VALUE);
|
||||
response.cookies.nc.should.be.eql(NUMBER_COOKIE_VALUE.toString());
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('when a cookie value is invalid', function() {
|
||||
|
||||
before(function() {
|
||||
setCookieHeader = [
|
||||
'geo_city=%D1%E0%ED%EA%F2-%CF%E5%F2%E5%F0%E1%F3%F0%E3'
|
||||
];
|
||||
})
|
||||
|
||||
it('doesnt blow up', function(done) {
|
||||
needle.get('localhost:' + ALL_COOKIES_TEST_PORT, function(error, response) {
|
||||
should.not.exist(error)
|
||||
var whatever = 'efbfbdefbfbdefbfbdefbfbdefbfbd2defbfbdefbfbdefbfbdefbfbdefbfbdefbfbdefbfbdefbfbdefbfbd';
|
||||
Buffer.from(response.cookies.geo_city).toString('hex').should.eql(whatever)
|
||||
done();
|
||||
});
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('and response is a redirect', function() {
|
||||
|
||||
var redirectServer, testPort = 22222;
|
||||
var requestCookies = [];
|
||||
|
||||
var responseCookies = [
|
||||
[ // first req
|
||||
WEIRD_COOKIE_NAME + '=' + encode(WEIRD_COOKIE_VALUE) + ';',
|
||||
BASE64_COOKIE_NAME + '=' + encode(BASE64_COOKIE_VALUE) + ';',
|
||||
'FOO=123;'
|
||||
], [ // second req
|
||||
FORBIDDEN_COOKIE_NAME + '=' + encode(FORBIDDEN_COOKIE_VALUE) + ';',
|
||||
NUMBER_COOKIE_NAME + '=' + encode(NUMBER_COOKIE_VALUE) + ';'
|
||||
], [ // third red
|
||||
'FOO=BAR;'
|
||||
]
|
||||
]
|
||||
|
||||
before(function(done) {
|
||||
redirectServer = http.createServer(function(req, res) {
|
||||
var number = parseInt(req.url.replace('/', ''));
|
||||
var nextUrl = 'http://' + 'localhost:' + testPort + '/' + (number + 1);
|
||||
|
||||
if (number == 0) requestCookies = []; // reset
|
||||
requestCookies.push(req.headers['cookie']);
|
||||
|
||||
if (responseCookies[number]) { // we should send cookies for this request
|
||||
res.statusCode = 302;
|
||||
res.setHeader('Set-Cookie', responseCookies[number]);
|
||||
res.setHeader('Location', nextUrl);
|
||||
} else if (number == 3) {
|
||||
res.statusCode = 302; // redirect but without cookies
|
||||
res.setHeader('Location', nextUrl);
|
||||
}
|
||||
|
||||
res.end('OK');
|
||||
}).listen(22222, done);
|
||||
});
|
||||
|
||||
after(function(done) {
|
||||
redirectServer.close(done);
|
||||
})
|
||||
|
||||
describe('and follow_set_cookies is false', function() {
|
||||
|
||||
describe('with original request cookie', function() {
|
||||
|
||||
var opts = {
|
||||
follow_set_cookies: false,
|
||||
follow_max: 4,
|
||||
cookies: { 'xxx': 123 }
|
||||
};
|
||||
|
||||
it('request cookie is not passed to redirects', function(done) {
|
||||
needle.get('localhost:' + testPort + '/0', opts, function(err, resp) {
|
||||
requestCookies.should.eql(["xxx=123", undefined, undefined, undefined, undefined])
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('response cookies are not passed either', function(done) {
|
||||
needle.get('localhost:' + testPort + '/0', opts, function(err, resp) {
|
||||
should.not.exist(resp.cookies);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
})
|
||||
|
||||
describe('without original request cookie', function() {
|
||||
|
||||
var opts = {
|
||||
follow_set_cookies: false,
|
||||
follow_max: 4,
|
||||
};
|
||||
|
||||
it('no request cookies are sent', function(done) {
|
||||
needle.get('localhost:' + testPort + '/0', opts, function(err, resp) {
|
||||
requestCookies.should.eql([undefined, undefined, undefined, undefined, undefined])
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('response cookies are not passed either', function(done) {
|
||||
needle.get('localhost:' + testPort + '/0', opts, function(err, resp) {
|
||||
should.not.exist(resp.cookies);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
describe('and follow_set_cookies is true', function() {
|
||||
|
||||
describe('with original request cookie', function() {
|
||||
|
||||
var opts = {
|
||||
follow_set_cookies: true,
|
||||
follow_max: 4,
|
||||
cookies: { 'xxx': 123 }
|
||||
};
|
||||
|
||||
it('request cookie is passed passed to redirects, and response cookies are added too', function(done) {
|
||||
needle.get('localhost:' + testPort + '/0', opts, function(err, resp) {
|
||||
requestCookies.should.eql([
|
||||
"xxx=123",
|
||||
"xxx=123; wc=!'*+#()&-./0123456789:<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~; bc=Y29va2llCg==; FOO=123",
|
||||
"xxx=123; wc=!\'*+#()&-./0123456789:<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~; bc=Y29va2llCg==; FOO=123; fc=%20%3B%22%5C%2C; nc=12354342",
|
||||
"xxx=123; wc=!\'*+#()&-./0123456789:<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~; bc=Y29va2llCg==; FOO=BAR; fc=%20%3B%22%5C%2C; nc=12354342",
|
||||
"xxx=123; wc=!\'*+#()&-./0123456789:<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~; bc=Y29va2llCg==; FOO=BAR; fc=%20%3B%22%5C%2C; nc=12354342"
|
||||
])
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('response cookies are passed as well', function(done) {
|
||||
needle.get('localhost:' + testPort + '/0', opts, function(err, resp) {
|
||||
resp.cookies.should.have.property(WEIRD_COOKIE_NAME);
|
||||
resp.cookies.should.have.property(BASE64_COOKIE_NAME);
|
||||
resp.cookies.should.have.property(FORBIDDEN_COOKIE_NAME);
|
||||
resp.cookies.should.have.property(NUMBER_COOKIE_NAME);
|
||||
resp.cookies.should.have.property('FOO');
|
||||
resp.cookies.FOO.should.eql('BAR'); // should overwrite previous one
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
})
|
||||
|
||||
describe('without original request cookie', function() {
|
||||
|
||||
var opts = {
|
||||
follow_set_cookies: true,
|
||||
follow_max: 4,
|
||||
};
|
||||
|
||||
it('response cookies are passed to redirects', function(done) {
|
||||
needle.get('localhost:' + testPort + '/0', opts, function(err, resp) {
|
||||
requestCookies.should.eql([
|
||||
undefined,
|
||||
"wc=!'*+#()&-./0123456789:<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~; bc=Y29va2llCg==; FOO=123",
|
||||
"wc=!\'*+#()&-./0123456789:<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~; bc=Y29va2llCg==; FOO=123; fc=%20%3B%22%5C%2C; nc=12354342",
|
||||
"wc=!\'*+#()&-./0123456789:<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~; bc=Y29va2llCg==; FOO=BAR; fc=%20%3B%22%5C%2C; nc=12354342",
|
||||
"wc=!\'*+#()&-./0123456789:<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~; bc=Y29va2llCg==; FOO=BAR; fc=%20%3B%22%5C%2C; nc=12354342"
|
||||
])
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('response cookies are passed as well', function(done) {
|
||||
needle.get('localhost:' + testPort + '/0', opts, function(err, resp) {
|
||||
// resp.cookies.should.have.property(WEIRD_COOKIE_NAME);
|
||||
// resp.cookies.should.have.property(BASE64_COOKIE_NAME);
|
||||
// resp.cookies.should.have.property(FORBIDDEN_COOKIE_NAME);
|
||||
// resp.cookies.should.have.property(NUMBER_COOKIE_NAME);
|
||||
// resp.cookies.should.have.property('FOO');
|
||||
// resp.cookies.FOO.should.eql('BAR'); // should overwrite previous one
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
})
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe('with parse_cookies = false', function() {
|
||||
it('does not parse them', function(done) {
|
||||
needle.get(
|
||||
'localhost:' + ALL_COOKIES_TEST_PORT, { parse_cookies: false }, function(error, response) {
|
||||
should.not.exist(response.cookies);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('if request contains cookie header', function() {
|
||||
var opts = {
|
||||
cookies: {}
|
||||
};
|
||||
|
||||
before(function() {
|
||||
opts.cookies[WEIRD_COOKIE_NAME] = WEIRD_COOKIE_VALUE;
|
||||
opts.cookies[BASE64_COOKIE_NAME] = BASE64_COOKIE_VALUE;
|
||||
opts.cookies[FORBIDDEN_COOKIE_NAME] = FORBIDDEN_COOKIE_VALUE;
|
||||
opts.cookies[NUMBER_COOKIE_NAME] = NUMBER_COOKIE_VALUE;
|
||||
});
|
||||
|
||||
it('must be a valid cookie string', function(done) {
|
||||
var COOKIE_PAIR = /^([^=\s]+)\s*=\s*("?)\s*(.*)\s*\2\s*$/;
|
||||
|
||||
var full_header = [
|
||||
WEIRD_COOKIE_NAME + '=' + WEIRD_COOKIE_VALUE,
|
||||
BASE64_COOKIE_NAME + '=' + BASE64_COOKIE_VALUE,
|
||||
FORBIDDEN_COOKIE_NAME + '=' + encode(FORBIDDEN_COOKIE_VALUE),
|
||||
NUMBER_COOKIE_NAME + '=' + NUMBER_COOKIE_VALUE
|
||||
].join('; ')
|
||||
|
||||
needle.get('localhost:' + ALL_COOKIES_TEST_PORT, opts, function(error, response) {
|
||||
var cookieString = response.req._headers.cookie;
|
||||
cookieString.should.be.type('string');
|
||||
|
||||
cookieString.split(/\s*;\s*/).forEach(function(pair) {
|
||||
COOKIE_PAIR.test(pair).should.be.exactly(true);
|
||||
});
|
||||
|
||||
cookieString.should.be.exactly(full_header);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('dont have to encode allowed characters', function(done) {
|
||||
var COOKIE_PAIR = /^([^=\s]+)\s*=\s*("?)\s*(.*)\s*\2\s*$/,
|
||||
KEY_INDEX = 1,
|
||||
VALUE_INEX = 3;
|
||||
|
||||
needle.get('localhost:' + ALL_COOKIES_TEST_PORT, opts, function(error, response) {
|
||||
var cookieObj = {},
|
||||
cookieString = response.req._headers.cookie;
|
||||
|
||||
cookieString.split(/\s*;\s*/).forEach(function(str) {
|
||||
var pair = COOKIE_PAIR.exec(str);
|
||||
cookieObj[pair[KEY_INDEX]] = pair[VALUE_INEX];
|
||||
});
|
||||
|
||||
cookieObj[WEIRD_COOKIE_NAME].should.be.exactly(WEIRD_COOKIE_VALUE);
|
||||
cookieObj[BASE64_COOKIE_NAME].should.be.exactly(BASE64_COOKIE_VALUE);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('must encode forbidden characters', function(done) {
|
||||
var COOKIE_PAIR = /^([^=\s]+)\s*=\s*("?)\s*(.*)\s*\2\s*$/,
|
||||
KEY_INDEX = 1,
|
||||
VALUE_INEX = 3;
|
||||
|
||||
needle.get('localhost:' + ALL_COOKIES_TEST_PORT, opts, function(error, response) {
|
||||
var cookieObj = {},
|
||||
cookieString = response.req._headers.cookie;
|
||||
|
||||
cookieString.split(/\s*;\s*/).forEach(function(str) {
|
||||
var pair = COOKIE_PAIR.exec(str);
|
||||
cookieObj[pair[KEY_INDEX]] = pair[VALUE_INEX];
|
||||
});
|
||||
|
||||
cookieObj[FORBIDDEN_COOKIE_NAME].should.not.be.eql(
|
||||
FORBIDDEN_COOKIE_VALUE);
|
||||
cookieObj[FORBIDDEN_COOKIE_NAME].should.be.exactly(
|
||||
encode(FORBIDDEN_COOKIE_VALUE));
|
||||
cookieObj[FORBIDDEN_COOKIE_NAME].should.be.exactly(
|
||||
encodeURIComponent(FORBIDDEN_COOKIE_VALUE));
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
121
node_modules/needle/test/decoder_spec.js
generated
vendored
Normal file
121
node_modules/needle/test/decoder_spec.js
generated
vendored
Normal file
@@ -0,0 +1,121 @@
|
||||
var should = require('should'),
|
||||
needle = require('./../'),
|
||||
Q = require('q'),
|
||||
chardet = require('jschardet'),
|
||||
helpers = require('./helpers');
|
||||
|
||||
describe('character encoding', function() {
|
||||
|
||||
var url;
|
||||
this.timeout(5000);
|
||||
|
||||
describe('Given content-type: "text/html; charset=EUC-JP"', function() {
|
||||
|
||||
before(function() {
|
||||
url = 'http://www.nina.jp/server/slackware/webapp/tomcat_charset.html';
|
||||
})
|
||||
|
||||
describe('with decode = false', function() {
|
||||
|
||||
it('does not decode', function(done) {
|
||||
|
||||
needle.get(url, { decode: false }, function(err, resp) {
|
||||
resp.body.should.be.a.String;
|
||||
chardet.detect(resp.body).encoding.should.eql('windows-1252');
|
||||
resp.body.indexOf('EUCを使う').should.eql(-1);
|
||||
done();
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('with decode = true', function() {
|
||||
|
||||
it('decodes', function(done) {
|
||||
|
||||
needle.get(url, { decode: true }, function(err, resp) {
|
||||
resp.body.should.be.a.String;
|
||||
chardet.detect(resp.body).encoding.should.eql('ascii');
|
||||
resp.body.indexOf('EUCを使う').should.not.eql(-1);
|
||||
done();
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('Given content-type: "text/html but file is charset: gb2312', function() {
|
||||
|
||||
it('encodes to UTF-8', function(done) {
|
||||
|
||||
// Our Needle wrapper that requests a chinese website.
|
||||
var task = Q.nbind(needle.get, needle, 'http://www.chinesetop100.com/');
|
||||
|
||||
// Different instantiations of this task
|
||||
var tasks = [Q.fcall(task, {decode: true}),
|
||||
Q.fcall(task, {decode: false})];
|
||||
|
||||
var results = tasks.map(function(task) {
|
||||
return task.then(function(obj) {
|
||||
return obj[0].body;
|
||||
});
|
||||
});
|
||||
|
||||
// Execute all requests concurrently
|
||||
Q.all(results).done(function(bodies) {
|
||||
|
||||
var charsets = [
|
||||
chardet.detect(bodies[0]).encoding,
|
||||
chardet.detect(bodies[1]).encoding,
|
||||
]
|
||||
|
||||
// We wanted to decode our first stream as specified by options
|
||||
charsets[0].should.equal('ascii');
|
||||
bodies[0].indexOf('全球中文网站前二十强').should.not.equal(-1);
|
||||
|
||||
// But not our second stream
|
||||
charsets[1].should.equal('windows-1252');
|
||||
bodies[1].indexOf('全球中文网站前二十强').should.equal(-1);
|
||||
|
||||
done();
|
||||
});
|
||||
})
|
||||
})
|
||||
|
||||
describe('Given content-type: "text/html"', function () {
|
||||
|
||||
var server,
|
||||
port = 54321,
|
||||
text = 'Magyarországi Fióktelepe'
|
||||
|
||||
before(function(done) {
|
||||
server = helpers.server({
|
||||
port: port,
|
||||
response: text,
|
||||
headers: { 'Content-Type': 'text/html' }
|
||||
}, done);
|
||||
})
|
||||
|
||||
after(function(done) {
|
||||
server.close(done)
|
||||
})
|
||||
|
||||
describe('with decode = false', function () {
|
||||
it('decodes by default to utf-8', function (done) {
|
||||
|
||||
needle.get('http://localhost:' + port, { decode: false }, function (err, resp) {
|
||||
resp.body.should.be.a.String;
|
||||
chardet.detect(resp.body).encoding.should.eql('ISO-8859-2');
|
||||
resp.body.should.eql('Magyarországi Fióktelepe')
|
||||
done();
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
})
|
||||
286
node_modules/needle/test/errors_spec.js
generated
vendored
Normal file
286
node_modules/needle/test/errors_spec.js
generated
vendored
Normal file
@@ -0,0 +1,286 @@
|
||||
var needle = require('../'),
|
||||
sinon = require('sinon'),
|
||||
should = require('should'),
|
||||
http = require('http'),
|
||||
Emitter = require('events').EventEmitter,
|
||||
helpers = require('./helpers');
|
||||
|
||||
var get_catch = function(url, opts) {
|
||||
var err;
|
||||
try {
|
||||
needle.get(url, opts);
|
||||
} catch(e) {
|
||||
err = e;
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
describe('errors', function() {
|
||||
|
||||
after(function(done) {
|
||||
setTimeout(done, 100)
|
||||
})
|
||||
|
||||
describe('when host does not exist', function() {
|
||||
|
||||
var url = 'http://unexistinghost/foo';
|
||||
|
||||
describe('with callback', function() {
|
||||
|
||||
it('does not throw', function() {
|
||||
var ex = get_catch(url);
|
||||
should.not.exist(ex);
|
||||
})
|
||||
|
||||
it('callbacks an error', function(done) {
|
||||
needle.get(url, function(err) {
|
||||
err.should.be.a.Error;
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('error should be ENOTFOUND or EADDRINFO or EAI_AGAIN', function(done) {
|
||||
needle.get(url, function(err) {
|
||||
err.code.should.match(/ENOTFOUND|EADDRINFO|EAI_AGAIN/)
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('does not callback a response', function(done) {
|
||||
needle.get(url, function(err, resp) {
|
||||
should.not.exist(resp);
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('does not emit an error event', function(done) {
|
||||
var emitted = false;
|
||||
var req = needle.get(url, function(err, resp) { })
|
||||
|
||||
req.on('error', function() {
|
||||
emitted = true;
|
||||
})
|
||||
|
||||
setTimeout(function() {
|
||||
emitted.should.eql(false);
|
||||
done();
|
||||
}, 100);
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('without callback', function() {
|
||||
|
||||
it('does not throw', function() {
|
||||
var ex = get_catch(url);
|
||||
should.not.exist(ex);
|
||||
})
|
||||
|
||||
it('emits end event once, with error', function(done) {
|
||||
var callcount = 0,
|
||||
stream = needle.get(url);
|
||||
|
||||
stream.on('done', function(err) {
|
||||
callcount++;
|
||||
})
|
||||
|
||||
setTimeout(function() {
|
||||
callcount.should.equal(1);
|
||||
done();
|
||||
}, 200)
|
||||
})
|
||||
|
||||
it('error should be ENOTFOUND or EADDRINFO or EAI_AGAIN', function(done) {
|
||||
var errorific,
|
||||
stream = needle.get(url);
|
||||
|
||||
stream.on('done', function(err) {
|
||||
errorific = err;
|
||||
})
|
||||
|
||||
setTimeout(function() {
|
||||
should.exist(errorific);
|
||||
errorific.code.should.match(/ENOTFOUND|EADDRINFO|EAI_AGAIN/)
|
||||
done();
|
||||
}, 200)
|
||||
})
|
||||
|
||||
it('does not emit a readable event', function(done) {
|
||||
var called = false,
|
||||
stream = needle.get(url);
|
||||
|
||||
stream.on('readable', function() {
|
||||
called = true;
|
||||
})
|
||||
|
||||
setTimeout(function() {
|
||||
called.should.be.false;
|
||||
done();
|
||||
}, 50)
|
||||
})
|
||||
|
||||
it('does not emit an error event', function(done) {
|
||||
var emitted = false,
|
||||
req = needle.get(url);
|
||||
|
||||
req.on('error', function() {
|
||||
emitted = true;
|
||||
})
|
||||
|
||||
setTimeout(function() {
|
||||
emitted.should.eql(false);
|
||||
done();
|
||||
}, 100);
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('when request times out waiting for response', function() {
|
||||
|
||||
var server,
|
||||
url = 'http://localhost:3333/foo';
|
||||
|
||||
var send_request = function(cb) {
|
||||
return needle.get(url, { response_timeout: 200 }, cb);
|
||||
}
|
||||
|
||||
before(function() {
|
||||
server = helpers.server({ port: 3333, wait: 1000 });
|
||||
})
|
||||
|
||||
after(function() {
|
||||
server.close();
|
||||
})
|
||||
|
||||
describe('with callback', function() {
|
||||
|
||||
it('aborts the request', function(done) {
|
||||
|
||||
var time = new Date();
|
||||
|
||||
send_request(function(err) {
|
||||
var timediff = (new Date() - time);
|
||||
timediff.should.be.within(200, 300);
|
||||
done();
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
it('callbacks an error', function(done) {
|
||||
send_request(function(err) {
|
||||
err.should.be.a.Error;
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('error should be ECONNRESET', function(done) {
|
||||
send_request(function(err) {
|
||||
err.code.should.equal('ECONNRESET')
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('does not callback a response', function(done) {
|
||||
send_request(function(err, resp) {
|
||||
should.not.exist(resp);
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('does not emit an error event', function(done) {
|
||||
var emitted = false;
|
||||
|
||||
var req = send_request(function(err, resp) {
|
||||
should.not.exist(resp);
|
||||
})
|
||||
|
||||
req.on('error', function() {
|
||||
emitted = true;
|
||||
})
|
||||
|
||||
setTimeout(function() {
|
||||
emitted.should.eql(false);
|
||||
done();
|
||||
}, 350);
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('without callback', function() {
|
||||
|
||||
it('emits done event once, with error', function(done) {
|
||||
var called = 0,
|
||||
stream = send_request();
|
||||
|
||||
stream.on('done', function(err) {
|
||||
called++;
|
||||
})
|
||||
|
||||
setTimeout(function() {
|
||||
called.should.equal(1);
|
||||
done();
|
||||
}, 250)
|
||||
})
|
||||
|
||||
it('aborts the request', function(done) {
|
||||
|
||||
var time = new Date();
|
||||
var stream = send_request();
|
||||
|
||||
stream.on('done', function(err) {
|
||||
var timediff = (new Date() - time);
|
||||
timediff.should.be.within(200, 300);
|
||||
done();
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
it('error should be ECONNRESET', function(done) {
|
||||
var error,
|
||||
stream = send_request();
|
||||
|
||||
stream.on('done', function(err) {
|
||||
error = err;
|
||||
})
|
||||
|
||||
setTimeout(function() {
|
||||
error.code.should.equal('ECONNRESET')
|
||||
done();
|
||||
}, 250)
|
||||
})
|
||||
|
||||
it('does not emit a readable event', function(done) {
|
||||
var called = false,
|
||||
stream = send_request();
|
||||
|
||||
stream.on('readable', function() {
|
||||
called = true;
|
||||
})
|
||||
|
||||
setTimeout(function() {
|
||||
called.should.be.false;
|
||||
done();
|
||||
}, 250)
|
||||
})
|
||||
|
||||
it('does not emit an error event', function(done) {
|
||||
var emitted = false;
|
||||
var req = send_request();
|
||||
|
||||
req.on('error', function() {
|
||||
emitted = true;
|
||||
})
|
||||
|
||||
setTimeout(function() {
|
||||
emitted.should.eql(false);
|
||||
done();
|
||||
}, 100);
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
203
node_modules/needle/test/headers_spec.js
generated
vendored
Normal file
203
node_modules/needle/test/headers_spec.js
generated
vendored
Normal file
@@ -0,0 +1,203 @@
|
||||
var http = require('http'),
|
||||
helpers = require('./helpers'),
|
||||
should = require('should');
|
||||
|
||||
var port = 54321;
|
||||
|
||||
describe('request headers', function() {
|
||||
|
||||
var needle,
|
||||
server,
|
||||
existing_sockets,
|
||||
original_defaultMaxSockets;
|
||||
|
||||
before(function(done) {
|
||||
setTimeout(function() {
|
||||
existing_sockets = get_active_sockets().length;
|
||||
server = helpers.server({ port: port }, done);
|
||||
}, 100);
|
||||
})
|
||||
|
||||
after(function(done) {
|
||||
server.close(done);
|
||||
})
|
||||
|
||||
function send_request(opts, cb) {
|
||||
needle.get('http://localhost:' + port, opts, cb);
|
||||
}
|
||||
|
||||
function get_active_sockets() {
|
||||
var handles = process._getActiveHandles();
|
||||
|
||||
return handles.filter(function(el) {
|
||||
if (el.constructor.name.toString() == 'Socket') {
|
||||
return el.destroyed !== true;
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
describe('old node versions (<0.11.4) with persistent keep-alive connections', function() {
|
||||
|
||||
before(function() {
|
||||
delete require.cache[require.resolve('..')] // in case it was already loaded
|
||||
original_defaultMaxSockets = http.Agent.defaultMaxSockets;
|
||||
http.Agent.defaultMaxSockets = 5;
|
||||
needle = require('..');
|
||||
})
|
||||
|
||||
after(function() {
|
||||
http.Agent.defaultMaxSockets = original_defaultMaxSockets;
|
||||
delete require.cache[require.resolve('..')]
|
||||
})
|
||||
|
||||
describe('default options', function() {
|
||||
|
||||
it('sends a Connection: close header', function(done) {
|
||||
send_request({}, function(err, resp) {
|
||||
resp.body.headers['connection'].should.eql('close');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('no open sockets remain after request', function(done) {
|
||||
send_request({}, function(err, resp) {
|
||||
setTimeout(function() {
|
||||
get_active_sockets().length.should.eql(existing_sockets);
|
||||
done();
|
||||
}, 10)
|
||||
});
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('passing connection: close', function() {
|
||||
|
||||
it('sends a Connection: close header', function(done) {
|
||||
send_request({ connection: 'close' }, function(err, resp) {
|
||||
resp.body.headers['connection'].should.eql('close');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('no open sockets remain after request', function(done) {
|
||||
send_request({ connection: 'close' }, function(err, resp) {
|
||||
setTimeout(function() {
|
||||
get_active_sockets().length.should.eql(existing_sockets);
|
||||
done();
|
||||
}, 10)
|
||||
});
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('passing connection: keep-alive', function() {
|
||||
|
||||
it('sends a Connection: keep-alive header (using options.headers.connection)', function(done) {
|
||||
send_request({ headers: { connection: 'keep-alive' }}, function(err, resp) {
|
||||
resp.body.headers['connection'].should.eql('keep-alive');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('sends a Connection: keep-alive header (using options.connection)', function(done) {
|
||||
send_request({ connection: 'keep-alive' }, function(err, resp) {
|
||||
resp.body.headers['connection'].should.eql('keep-alive');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('one open socket remain after request', function(done) {
|
||||
send_request({ connection: 'keep-alive' }, function(err, resp) {
|
||||
get_active_sockets().length.should.eql(existing_sockets + 1);
|
||||
done();
|
||||
});
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('new node versions with smarter connection disposing', function() {
|
||||
|
||||
before(function() {
|
||||
delete require.cache[require.resolve('..')]
|
||||
original_defaultMaxSockets = http.Agent.defaultMaxSockets;
|
||||
http.Agent.defaultMaxSockets = Infinity;
|
||||
needle = require('..');
|
||||
})
|
||||
|
||||
after(function() {
|
||||
http.Agent.defaultMaxSockets = original_defaultMaxSockets;
|
||||
delete require.cache[require.resolve('..')]
|
||||
})
|
||||
|
||||
describe('default options', function() {
|
||||
|
||||
// TODO:
|
||||
// this is weird. by default, new node versions set a 'close' header
|
||||
// while older versions set a keep-alive header
|
||||
|
||||
it.skip('sets a Connection header', function(done) {
|
||||
send_request({}, function(err, resp) {
|
||||
// should.not.exist(resp.body.headers['connection']);
|
||||
// done();
|
||||
})
|
||||
})
|
||||
|
||||
it.skip('one open sockets remain after request', function(done) {
|
||||
send_request({}, function(err, resp) {
|
||||
// get_active_sockets().length.should.eql(1);
|
||||
// done();
|
||||
});
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('passing connection: close', function() {
|
||||
|
||||
it('sends a Connection: close header', function(done) {
|
||||
send_request({ connection: 'close' }, function(err, resp) {
|
||||
resp.body.headers['connection'].should.eql('close');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('no open sockets remain after request', function(done) {
|
||||
send_request({ connection: 'close' }, function(err, resp) {
|
||||
setTimeout(function() {
|
||||
get_active_sockets().length.should.eql(existing_sockets);
|
||||
done();
|
||||
}, 10);
|
||||
});
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('passing connection: keep-alive', function() {
|
||||
|
||||
it('sends a Connection: keep-alive header (using options.headers.connection)', function(done) {
|
||||
send_request({ headers: { connection: 'keep-alive' }}, function(err, resp) {
|
||||
resp.body.headers['connection'].should.eql('keep-alive');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('sends a Connection: keep-alive header (using options.connection)', function(done) {
|
||||
send_request({ connection: 'keep-alive' }, function(err, resp) {
|
||||
resp.body.headers['connection'].should.eql('keep-alive');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('one open socket remain after request', function(done) {
|
||||
send_request({ connection: 'keep-alive' }, function(err, resp) {
|
||||
get_active_sockets().length.should.eql(existing_sockets + 1);
|
||||
done();
|
||||
});
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
72
node_modules/needle/test/helpers.js
generated
vendored
Normal file
72
node_modules/needle/test/helpers.js
generated
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
var fs = require('fs');
|
||||
|
||||
var protocols = {
|
||||
http : require('http'),
|
||||
https : require('https')
|
||||
}
|
||||
|
||||
var keys = {
|
||||
cert : fs.readFileSync(__dirname + '/keys/ssl.cert'),
|
||||
key : fs.readFileSync(__dirname + '/keys/ssl.key')
|
||||
}
|
||||
|
||||
var helpers = {};
|
||||
|
||||
helpers.server = function(opts, cb) {
|
||||
|
||||
var defaults = {
|
||||
code : 200,
|
||||
headers : {'Content-Type': 'application/json'}
|
||||
}
|
||||
|
||||
var mirror_response = function(req) {
|
||||
return JSON.stringify({
|
||||
headers: req.headers,
|
||||
body: req.body
|
||||
})
|
||||
}
|
||||
|
||||
var get = function(what) {
|
||||
if (!opts[what])
|
||||
return defaults[what];
|
||||
|
||||
if (typeof opts[what] == 'function')
|
||||
return opts[what](); // set them at runtime
|
||||
else
|
||||
return opts[what];
|
||||
}
|
||||
|
||||
var finish = function(req, res) {
|
||||
res.writeHead(get('code'), get('headers'));
|
||||
res.end(opts.response || mirror_response(req));
|
||||
}
|
||||
|
||||
var handler = function(req, res) {
|
||||
|
||||
req.setEncoding('utf8'); // get as string
|
||||
req.body = '';
|
||||
req.on('data', function(str) { req.body += str })
|
||||
req.socket.on('error', function(e) {
|
||||
// res.writeHead(500, {'Content-Type': 'text/plain'});
|
||||
// res.end('Error: ' + e.message);
|
||||
})
|
||||
|
||||
setTimeout(function(){
|
||||
finish(req, res);
|
||||
}, opts.wait || 0);
|
||||
|
||||
};
|
||||
|
||||
var protocol = opts.protocol || 'http';
|
||||
var server;
|
||||
|
||||
if (protocol == 'https')
|
||||
server = protocols[protocol].createServer(keys, handler);
|
||||
else
|
||||
server = protocols[protocol].createServer(handler);
|
||||
|
||||
server.listen(opts.port, cb);
|
||||
return server;
|
||||
}
|
||||
|
||||
module.exports = helpers;
|
||||
34
node_modules/needle/test/long_string_spec.js
generated
vendored
Normal file
34
node_modules/needle/test/long_string_spec.js
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
var needle = require('../'),
|
||||
should = require('should');
|
||||
|
||||
describe('when posting a very long string', function() {
|
||||
|
||||
this.timeout(20000);
|
||||
|
||||
function get_string(length) {
|
||||
var str = '';
|
||||
for (var i = 0; i < length; i++) {
|
||||
str += 'x';
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
it("shouldn't throw an EPIPE error out of nowhere", function(done) {
|
||||
var error;
|
||||
|
||||
function finished() {
|
||||
setTimeout(function() {
|
||||
should.not.exist(error);
|
||||
done();
|
||||
}, 300);
|
||||
}
|
||||
|
||||
try {
|
||||
needle.post('https://google.com', { data: get_string(Math.pow(2, 20)) }, finished)
|
||||
} catch(e) {
|
||||
error = e;
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
254
node_modules/needle/test/output_spec.js
generated
vendored
Normal file
254
node_modules/needle/test/output_spec.js
generated
vendored
Normal file
@@ -0,0 +1,254 @@
|
||||
var should = require('should'),
|
||||
needle = require('./../'),
|
||||
http = require('http'),
|
||||
sinon = require('sinon'),
|
||||
stream = require('stream'),
|
||||
fs = require('fs'),
|
||||
port = 11111,
|
||||
server;
|
||||
|
||||
describe('with output option', function() {
|
||||
|
||||
var server, handler, file = '/tmp/foobar.out';
|
||||
|
||||
function send_request_cb(where, cb) {
|
||||
var url = 'http://localhost:' + port + '/whatever.file';
|
||||
return needle.get(url, { output: where }, cb);
|
||||
}
|
||||
|
||||
function send_request_stream(where, cb) {
|
||||
var url = 'http://localhost:' + port + '/whatever.file';
|
||||
var stream = needle.get(url, { output: where });
|
||||
stream.on('end', cb);
|
||||
}
|
||||
|
||||
// this will only work in UNICES
|
||||
function get_open_file_descriptors() {
|
||||
var list = fs.readdirSync('/proc/self/fd');
|
||||
return list.length;
|
||||
}
|
||||
|
||||
var send_request = send_request_cb;
|
||||
|
||||
before(function(){
|
||||
server = http.createServer(function(req, res) {
|
||||
handler(req, res);
|
||||
}).listen(port);
|
||||
});
|
||||
|
||||
after(function() {
|
||||
server.close();
|
||||
})
|
||||
|
||||
beforeEach(function() {
|
||||
try { fs.unlinkSync(file) } catch(e) { };
|
||||
})
|
||||
|
||||
describe('and a 404 response', function() {
|
||||
|
||||
before(function() {
|
||||
handler = function(req, res) {
|
||||
res.writeHead(404, {'Content-Type': 'text/plain' });
|
||||
res.end();
|
||||
}
|
||||
})
|
||||
|
||||
it('doesnt attempt to write a file', function(done) {
|
||||
var spy = sinon.spy(fs, 'createWriteStream');
|
||||
send_request(file, function(err, resp) {
|
||||
resp.statusCode.should.eql(404);
|
||||
spy.called.should.eql(false);
|
||||
spy.restore();
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('doesnt actually write a file', function(done) {
|
||||
send_request(file, function(err, resp) {
|
||||
resp.statusCode.should.eql(404);
|
||||
fs.existsSync(file).should.eql(false);
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('and a 200 response', function() {
|
||||
|
||||
describe('for an empty response', function() {
|
||||
|
||||
before(function() {
|
||||
handler = function(req, res) {
|
||||
res.writeHead(200, { 'Content-Type': 'text/plain' });
|
||||
res.end();
|
||||
}
|
||||
})
|
||||
|
||||
it('uses a writableStream', function(done) {
|
||||
var spy = sinon.spy(fs, 'createWriteStream');
|
||||
send_request(file, function(err, resp) {
|
||||
resp.statusCode.should.eql(200);
|
||||
spy.called.should.eql(true);
|
||||
spy.restore();
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('writes a file', function(done) {
|
||||
fs.existsSync(file).should.eql(false);
|
||||
send_request(file, function(err, resp) {
|
||||
fs.existsSync(file).should.eql(true);
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('file is zero bytes in length', function(done) {
|
||||
send_request(file, function(err, resp) {
|
||||
fs.statSync(file).size.should.equal(0);
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
if (process.platform != 'win32') {
|
||||
it('closes the file descriptor', function(done) {
|
||||
var open_descriptors = get_open_file_descriptors();
|
||||
send_request(file + Math.random(), function(err, resp) {
|
||||
var current_descriptors = get_open_file_descriptors();
|
||||
open_descriptors.should.eql(current_descriptors);
|
||||
done()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
describe('for a JSON response', function() {
|
||||
|
||||
before(function() {
|
||||
handler = function(req, res) {
|
||||
res.writeHead(200, { 'Content-Type': 'application/javascript' });
|
||||
res.end(JSON.stringify({foo: 'bar'}));
|
||||
}
|
||||
})
|
||||
|
||||
it('uses a writableStream', function(done) {
|
||||
var spy = sinon.spy(fs, 'createWriteStream');
|
||||
send_request(file, function(err, resp) {
|
||||
resp.statusCode.should.eql(200);
|
||||
spy.called.should.eql(true);
|
||||
spy.restore();
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('writes a file', function(done) {
|
||||
fs.existsSync(file).should.eql(false);
|
||||
send_request(file, function(err, resp) {
|
||||
fs.existsSync(file).should.eql(true);
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('file size equals response length', function(done) {
|
||||
send_request(file, function(err, resp) {
|
||||
fs.statSync(file).size.should.equal(resp.bytes);
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('response pipeline is honoured (JSON is decoded by default)', function(done) {
|
||||
send_request_stream(file, function(err, resp) {
|
||||
// we need to wait a bit since writing to config.output
|
||||
// happens independently of needle's callback logic.
|
||||
setTimeout(function() {
|
||||
fs.readFileSync(file).toString().should.eql('{\"foo\":\"bar\"}');
|
||||
done();
|
||||
}, 20);
|
||||
})
|
||||
})
|
||||
|
||||
it('closes the file descriptor', function(done) {
|
||||
var open_descriptors = get_open_file_descriptors();
|
||||
send_request(file + Math.random(), function(err, resp) {
|
||||
var current_descriptors = get_open_file_descriptors();
|
||||
open_descriptors.should.eql(current_descriptors);
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('for a binary file', function() {
|
||||
|
||||
var pixel = Buffer.from("base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs", "base64");
|
||||
|
||||
before(function() {
|
||||
handler = function(req, res) {
|
||||
res.writeHead(200, { 'Content-Type': 'application/octet-stream', 'Transfer-Encoding': 'chunked' });
|
||||
res.write(pixel.slice(0, 10));
|
||||
res.write(pixel.slice(10, 20));
|
||||
res.write(pixel.slice(20, 30));
|
||||
res.write(pixel.slice(30));
|
||||
res.end();
|
||||
}
|
||||
})
|
||||
|
||||
it('uses a writableStream', function(done) {
|
||||
var spy = sinon.spy(fs, 'createWriteStream');
|
||||
send_request(file, function(err, resp) {
|
||||
resp.statusCode.should.eql(200);
|
||||
spy.called.should.eql(true);
|
||||
spy.restore();
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('writes a file', function(done) {
|
||||
fs.existsSync(file).should.eql(false);
|
||||
send_request(file, function(err, resp) {
|
||||
fs.existsSync(file).should.eql(true);
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('file size equals response length', function(done) {
|
||||
send_request(file, function(err, resp) {
|
||||
fs.statSync(file).size.should.equal(resp.bytes);
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('file is equal to original buffer', function(done) {
|
||||
send_request(file, function(err, resp) {
|
||||
// we need to wait a bit since writing to config.output
|
||||
// happens independently of needle's callback logic.
|
||||
setTimeout(function() {
|
||||
fs.readFileSync(file).should.eql(pixel);
|
||||
done();
|
||||
}, 20);
|
||||
})
|
||||
})
|
||||
|
||||
it('returns the data in resp.body too', function(done) {
|
||||
send_request(file, function(err, resp) {
|
||||
resp.body.should.eql(pixel);
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
if (process.platform != 'win32') {
|
||||
it('closes the file descriptor', function(done) {
|
||||
var open_descriptors = get_open_file_descriptors();
|
||||
send_request(file + Math.random(), function(err, resp) {
|
||||
var current_descriptors = get_open_file_descriptors();
|
||||
open_descriptors.should.eql(current_descriptors);
|
||||
done()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
494
node_modules/needle/test/parsing_spec.js
generated
vendored
Normal file
494
node_modules/needle/test/parsing_spec.js
generated
vendored
Normal file
@@ -0,0 +1,494 @@
|
||||
var should = require('should'),
|
||||
needle = require('./../'),
|
||||
http = require('http'),
|
||||
port = 11111,
|
||||
server;
|
||||
|
||||
describe('parsing', function(){
|
||||
|
||||
describe('when response is an JSON string', function(){
|
||||
|
||||
var json_string = '{"foo":"bar"}';
|
||||
|
||||
before(function(done){
|
||||
server = http.createServer(function(req, res) {
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
res.end(json_string);
|
||||
}).listen(port, done);
|
||||
});
|
||||
|
||||
after(function(done){
|
||||
server.close(done);
|
||||
})
|
||||
|
||||
describe('and parse option is not passed', function() {
|
||||
|
||||
describe('with default parse_response', function() {
|
||||
|
||||
before(function() {
|
||||
needle.defaults().parse_response.should.eql('all')
|
||||
})
|
||||
|
||||
it('should return object', function(done){
|
||||
needle.get('localhost:' + port, function(err, response, body){
|
||||
should.ifError(err);
|
||||
body.should.have.property('foo', 'bar');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('and default parse_response is set to false', function() {
|
||||
|
||||
it('does NOT return object when disabled using .defaults', function(done){
|
||||
needle.defaults({ parse_response: false })
|
||||
|
||||
needle.get('localhost:' + port, function(err, response, body) {
|
||||
should.not.exist(err);
|
||||
body.should.be.an.instanceof(Buffer)
|
||||
body.toString().should.eql('{"foo":"bar"}');
|
||||
|
||||
needle.defaults({ parse_response: 'all' });
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('and parse option is true', function() {
|
||||
|
||||
describe('and JSON is valid', function() {
|
||||
|
||||
it('should return object', function(done) {
|
||||
needle.get('localhost:' + port, { parse: true }, function(err, response, body){
|
||||
should.not.exist(err);
|
||||
body.should.have.property('foo', 'bar')
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('should have a .parser = json property', function(done) {
|
||||
needle.get('localhost:' + port, { parse: true }, function(err, resp) {
|
||||
should.not.exist(err);
|
||||
resp.parser.should.eql('json');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
describe('and response is empty', function() {
|
||||
|
||||
var old_json_string;
|
||||
|
||||
before(function() {
|
||||
old_json_string = json_string;
|
||||
json_string = "";
|
||||
});
|
||||
|
||||
after(function() {
|
||||
json_string = old_json_string;
|
||||
});
|
||||
|
||||
it('should return an empty string', function(done) {
|
||||
needle.get('localhost:' + port, { parse: true }, function(err, resp) {
|
||||
should.not.exist(err);
|
||||
resp.body.should.equal('');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('and JSON is invalid', function() {
|
||||
|
||||
var old_json_string;
|
||||
|
||||
before(function() {
|
||||
old_json_string = json_string;
|
||||
json_string = "this is not going to work";
|
||||
});
|
||||
|
||||
after(function() {
|
||||
json_string = old_json_string;
|
||||
});
|
||||
|
||||
it('does not throw', function(done) {
|
||||
(function(){
|
||||
needle.get('localhost:' + port, { parse: true }, done);
|
||||
}).should.not.throw();
|
||||
});
|
||||
|
||||
it('does NOT return object', function(done) {
|
||||
needle.get('localhost:' + port, { parse: true }, function(err, response, body) {
|
||||
should.not.exist(err);
|
||||
body.should.be.a.String;
|
||||
body.toString().should.eql('this is not going to work');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
})
|
||||
|
||||
describe('and parse option is false', function() {
|
||||
|
||||
it('does NOT return object', function(done){
|
||||
needle.get('localhost:' + port, { parse: false }, function(err, response, body) {
|
||||
should.not.exist(err);
|
||||
body.should.be.an.instanceof(Buffer)
|
||||
body.toString().should.eql('{"foo":"bar"}');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('should NOT have a .parser = json property', function(done) {
|
||||
needle.get('localhost:' + port, { parse: false }, function(err, resp) {
|
||||
should.not.exist(err);
|
||||
should.not.exist(resp.parser);
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('and parse option is "xml"', function() {
|
||||
|
||||
it('does NOT return object', function(done){
|
||||
needle.get('localhost:' + port, { parse: 'xml' }, function(err, response, body) {
|
||||
should.not.exist(err);
|
||||
body.should.be.an.instanceof(Buffer)
|
||||
body.toString().should.eql('{"foo":"bar"}');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('should NOT have a .parser = json property', function(done) {
|
||||
needle.get('localhost:' + port, { parse: 'xml' }, function(err, resp) {
|
||||
should.not.exist(err);
|
||||
should.not.exist(resp.parser);
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
describe('when response is JSON \'false\'', function(){
|
||||
|
||||
var json_string = 'false';
|
||||
|
||||
before(function(done){
|
||||
server = http.createServer(function(req, res) {
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
res.end(json_string);
|
||||
}).listen(port, done);
|
||||
});
|
||||
|
||||
after(function(done){
|
||||
server.close(done);
|
||||
})
|
||||
|
||||
describe('and parse option is not passed', function() {
|
||||
|
||||
it('should return object', function(done){
|
||||
needle.get('localhost:' + port, function(err, response, body){
|
||||
should.ifError(err);
|
||||
body.should.equal(false);
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('and parse option is true', function() {
|
||||
|
||||
describe('and JSON is valid', function() {
|
||||
|
||||
it('should return object', function(done){
|
||||
needle.get('localhost:' + port, { parse: true }, function(err, response, body){
|
||||
should.not.exist(err);
|
||||
body.should.equal(false)
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
describe('and response is empty', function() {
|
||||
|
||||
var old_json_string;
|
||||
|
||||
before(function() {
|
||||
old_json_string = json_string;
|
||||
json_string = "";
|
||||
});
|
||||
|
||||
after(function() {
|
||||
json_string = old_json_string;
|
||||
});
|
||||
|
||||
it('should return an empty string', function(done) {
|
||||
needle.get('localhost:' + port, { parse: true }, function(err, resp) {
|
||||
should.not.exist(err);
|
||||
resp.body.should.equal('');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('and JSON is invalid', function() {
|
||||
|
||||
var old_json_string;
|
||||
|
||||
before(function() {
|
||||
old_json_string = json_string;
|
||||
json_string = "this is not going to work";
|
||||
});
|
||||
|
||||
after(function() {
|
||||
json_string = old_json_string;
|
||||
});
|
||||
|
||||
it('does not throw', function(done) {
|
||||
(function(){
|
||||
needle.get('localhost:' + port, { parse: true }, done);
|
||||
}).should.not.throw();
|
||||
});
|
||||
|
||||
it('does NOT return object', function(done) {
|
||||
needle.get('localhost:' + port, { parse: true }, function(err, response, body) {
|
||||
should.not.exist(err);
|
||||
body.should.be.a.String;
|
||||
body.toString().should.eql('this is not going to work');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
});
|
||||
|
||||
})
|
||||
|
||||
describe('and parse option is false', function() {
|
||||
|
||||
it('does NOT return object', function(done){
|
||||
needle.get('localhost:' + port, { parse: false }, function(err, response, body) {
|
||||
should.not.exist(err);
|
||||
body.should.be.an.instanceof(Buffer)
|
||||
body.toString().should.eql('false');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('and parse option is "xml"', function() {
|
||||
|
||||
it('does NOT return object', function(done){
|
||||
needle.get('localhost:' + port, { parse: 'xml' }, function(err, response, body) {
|
||||
should.not.exist(err);
|
||||
body.should.be.an.instanceof(Buffer)
|
||||
body.toString().should.eql('false');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
|
||||
});
|
||||
|
||||
describe('when response is an invalid XML string', function(){
|
||||
|
||||
before(function(done){
|
||||
server = http.createServer(function(req, res) {
|
||||
res.writeHeader(200, {'Content-Type': 'application/xml'})
|
||||
res.end("<post><body there11post>")
|
||||
}).listen(port, done);
|
||||
});
|
||||
|
||||
after(function(done){
|
||||
server.close(done);
|
||||
})
|
||||
|
||||
describe('and parse_response is true', function(){
|
||||
|
||||
it('should return original string', function(done) {
|
||||
needle.get('localhost:' + port, { parse_response: true }, function(err, response, body) {
|
||||
should.not.exist(err);
|
||||
body.should.eql('<post><body there11post>')
|
||||
should.not.exist(body.name);
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('should not have a .parser = xml property', function(done) {
|
||||
needle.get('localhost:' + port, { parse_response: true }, function(err, resp) {
|
||||
should.not.exist(err);
|
||||
should.not.exist(resp.parser);
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('and parse response is false', function(){
|
||||
|
||||
it('should return valid object', function(done) {
|
||||
needle.get('localhost:' + port, { parse_response: false }, function(err, response, body){
|
||||
should.not.exist(err);
|
||||
body.toString().should.eql('<post><body there11post>')
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('should not have a .parser property', function(done) {
|
||||
needle.get('localhost:' + port, { parse_response: false }, function(err, resp) {
|
||||
should.not.exist(err);
|
||||
should.not.exist(resp.parser)
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('when response is a valid XML string', function(){
|
||||
|
||||
before(function(done) {
|
||||
server = http.createServer(function(req, res) {
|
||||
res.writeHeader(200, {'Content-Type': 'application/xml'})
|
||||
res.end("<post><p>hello</p><p><![CDATA[world]]></p></post>")
|
||||
}).listen(port, done);
|
||||
});
|
||||
|
||||
after(function(done) {
|
||||
server.close(done);
|
||||
})
|
||||
|
||||
describe('and parse_response is true', function(){
|
||||
|
||||
it('should return valid object', function(done) {
|
||||
needle.get('localhost:' + port, { parse_response: true }, function(err, response, body) {
|
||||
should.not.exist(err);
|
||||
body.name.should.eql('post')
|
||||
body.children[0].name.should.eql('p')
|
||||
body.children[0].value.should.eql('hello')
|
||||
|
||||
body.children[1].name.should.eql('p')
|
||||
body.children[1].value.should.eql('world')
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('should have a .parser = xml property', function(done) {
|
||||
needle.get('localhost:' + port, { parse_response: true }, function(err, resp) {
|
||||
should.not.exist(err);
|
||||
resp.parser.should.eql('xml');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('and parse response is false', function(){
|
||||
|
||||
it('should return valid object', function(done) {
|
||||
needle.get('localhost:' + port, { parse_response: false }, function(err, response, body){
|
||||
should.not.exist(err);
|
||||
body.toString().should.eql('<post><p>hello</p><p><![CDATA[world]]></p></post>')
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('should not have a .parser property', function(done) {
|
||||
needle.get('localhost:' + port, { parse_response: false }, function(err, resp) {
|
||||
should.not.exist(err);
|
||||
should.not.exist(resp.parser)
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
|
||||
describe('valid XML, using xml2js', function() {
|
||||
|
||||
var parsers, origParser;
|
||||
|
||||
before(function(done) {
|
||||
var xml2js = require('xml2js')
|
||||
parsers = require('../lib/parsers');
|
||||
origParser = parsers['application/xml'];
|
||||
|
||||
var customParser = require('xml2js').parseString;
|
||||
parsers.use('xml2js', ['application/xml'], function(buff, cb) {
|
||||
var opts = { explicitRoot: true, explicitArray: false };
|
||||
customParser(buff, opts, cb);
|
||||
})
|
||||
|
||||
server = http.createServer(function(req, res) {
|
||||
res.writeHeader(200, {'Content-Type': 'application/xml'})
|
||||
res.end("<post><p>hello</p><p>world</p></post>")
|
||||
}).listen(port, done);
|
||||
});
|
||||
|
||||
after(function(done) {
|
||||
parsers['application/xml'] = origParser;
|
||||
server.close(done);
|
||||
})
|
||||
|
||||
describe('and parse_response is true', function(){
|
||||
|
||||
it('should return valid object', function(done) {
|
||||
needle.get('localhost:' + port, { parse_response: true }, function(err, response, body) {
|
||||
should.not.exist(err);
|
||||
body.should.eql({ post: { p: ['hello', 'world' ]}})
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('should have a .parser = xml property', function(done) {
|
||||
needle.get('localhost:' + port, { parse_response: true }, function(err, resp) {
|
||||
should.not.exist(err);
|
||||
resp.parser.should.eql('xml2js');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('and parse response is false', function(){
|
||||
|
||||
it('should return valid object', function(done) {
|
||||
needle.get('localhost:' + port, { parse_response: false }, function(err, response, body){
|
||||
should.not.exist(err);
|
||||
body.toString().should.eql('<post><p>hello</p><p>world</p></post>')
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('should not have a .parser property', function(done) {
|
||||
needle.get('localhost:' + port, { parse_response: false }, function(err, resp) {
|
||||
should.not.exist(err);
|
||||
should.not.exist(resp.parser)
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
|
||||
})
|
||||
1021
node_modules/needle/test/post_data_spec.js
generated
vendored
Normal file
1021
node_modules/needle/test/post_data_spec.js
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
202
node_modules/needle/test/proxy_spec.js
generated
vendored
Normal file
202
node_modules/needle/test/proxy_spec.js
generated
vendored
Normal file
@@ -0,0 +1,202 @@
|
||||
var helpers = require('./helpers'),
|
||||
should = require('should'),
|
||||
sinon = require('sinon'),
|
||||
http = require('http'),
|
||||
needle = require('./../');
|
||||
|
||||
var port = 7707;
|
||||
var url = 'localhost:' + port;
|
||||
var nonexisting_host = 'awepfokawepofawe.com';
|
||||
|
||||
describe('proxy option', function() {
|
||||
|
||||
var spy, opts;
|
||||
|
||||
function send_request(opts, done) {
|
||||
if (spy) spy.restore();
|
||||
spy = sinon.spy(http, 'request');
|
||||
needle.get(url, opts, done);
|
||||
}
|
||||
|
||||
//////////////////////
|
||||
// proxy opts helpers
|
||||
|
||||
function not_proxied(done) {
|
||||
return function(err, resp) {
|
||||
var path = spy.args[0][0].path;
|
||||
path.should.eql('/'); // not the full original URI
|
||||
spy.restore();
|
||||
done();
|
||||
}
|
||||
}
|
||||
|
||||
function proxied(host, port, done) {
|
||||
return function(err, resp) {
|
||||
var path = spy.args[0][0].path;
|
||||
path.should.eql('http://' + url); // the full original URI
|
||||
|
||||
var http_host = spy.args[0][0].host;
|
||||
if (http_host) http_host.should.eql(host);
|
||||
|
||||
var http_port = spy.args[0][0].port;
|
||||
if (http_port) http_port.should.eql(port);
|
||||
|
||||
spy.restore();
|
||||
done();
|
||||
}
|
||||
}
|
||||
|
||||
//////////////////////
|
||||
// auth helpers
|
||||
|
||||
function get_auth(header) {
|
||||
var token = header.split(/\s+/).pop();
|
||||
return token && Buffer.from(token, 'base64').toString().split(':');
|
||||
}
|
||||
|
||||
function no_proxy_auth(done) {
|
||||
return function(err, resp) {
|
||||
var headers = spy.args[0][0].headers;
|
||||
Object.keys(headers).should.not.containEql('proxy-authorization');
|
||||
done();
|
||||
}
|
||||
}
|
||||
|
||||
function header_set(name, user, pass, done) {
|
||||
return function(err, resp) {
|
||||
var headers = spy.args[0][0].headers;
|
||||
var auth = get_auth(headers[name]);
|
||||
auth[0].should.eql(user);
|
||||
auth[1].should.eql(pass);
|
||||
done();
|
||||
}
|
||||
}
|
||||
|
||||
function proxy_auth_set(user, pass, done) {
|
||||
return header_set('proxy-authorization', user, pass, done);
|
||||
}
|
||||
|
||||
function basic_auth_set(user, pass, done) {
|
||||
return header_set('authorization', user, pass, done);
|
||||
}
|
||||
|
||||
after(function() {
|
||||
spy.restore();
|
||||
})
|
||||
|
||||
describe('when null proxy is passed', function() {
|
||||
|
||||
it('does not proxy', function(done) {
|
||||
send_request({ proxy: null }, not_proxied(done))
|
||||
})
|
||||
|
||||
describe('but defaults has been set', function() {
|
||||
|
||||
before(function() {
|
||||
needle.defaults({ proxy: 'foobar' });
|
||||
})
|
||||
|
||||
after(function() {
|
||||
needle.defaults({ proxy: null });
|
||||
})
|
||||
|
||||
it('tries to proxy anyway', function(done) {
|
||||
send_request({}, proxied('foobar', 80, done))
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('when weird string is passed', function() {
|
||||
|
||||
it('tries to proxy anyway', function(done) {
|
||||
send_request({ proxy: 'alfalfa' }, proxied('alfalfa', 80, done))
|
||||
})
|
||||
})
|
||||
|
||||
describe('when valid url is passed', function() {
|
||||
|
||||
it('proxies request', function(done) {
|
||||
send_request({ proxy: nonexisting_host + ':123/done' }, proxied(nonexisting_host, '123', done))
|
||||
})
|
||||
|
||||
it('does not set a Proxy-Authorization header', function(done) {
|
||||
send_request({ proxy: nonexisting_host + ':123/done' }, no_proxy_auth(done));
|
||||
})
|
||||
|
||||
describe('and proxy url contains user:pass', function() {
|
||||
|
||||
before(function() {
|
||||
opts = {
|
||||
proxy: 'http://mj:x@' + nonexisting_host + ':123/done'
|
||||
}
|
||||
})
|
||||
|
||||
it('proxies request', function(done) {
|
||||
send_request(opts, proxied(nonexisting_host, '123', done))
|
||||
})
|
||||
|
||||
it('sets Proxy-Authorization header', function(done) {
|
||||
send_request(opts, proxy_auth_set('mj', 'x', done));
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('and a proxy_user is passed', function() {
|
||||
|
||||
before(function() {
|
||||
opts = {
|
||||
proxy: nonexisting_host + ':123',
|
||||
proxy_user: 'someone',
|
||||
proxy_pass: 'else'
|
||||
}
|
||||
})
|
||||
|
||||
it('proxies request', function(done) {
|
||||
send_request(opts, proxied(nonexisting_host, '123', done))
|
||||
})
|
||||
|
||||
it('sets Proxy-Authorization header', function(done) {
|
||||
send_request(opts, proxy_auth_set('someone', 'else', done));
|
||||
})
|
||||
|
||||
describe('and url also contains user:pass', function() {
|
||||
|
||||
it('url user:pass wins', function(done) {
|
||||
var opts = {
|
||||
proxy: 'http://xxx:yyy@' + nonexisting_host + ':123',
|
||||
proxy_user: 'someone',
|
||||
proxy_pass: 'else'
|
||||
}
|
||||
|
||||
send_request(opts, proxy_auth_set('xxx', 'yyy', done));
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('and options.username is also present', function() {
|
||||
|
||||
before(function() {
|
||||
opts = { proxy_user: 'foobar', username: 'someone' };
|
||||
})
|
||||
|
||||
it('a separate Authorization header is set', function(done) {
|
||||
var opts = {
|
||||
proxy: nonexisting_host + ':123',
|
||||
proxy_user: 'someone',
|
||||
proxy_pass: 'else',
|
||||
username: 'test',
|
||||
password: 'X'
|
||||
}
|
||||
|
||||
send_request(opts, basic_auth_set('test', 'X', done));
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
128
node_modules/needle/test/querystring_spec.js
generated
vendored
Normal file
128
node_modules/needle/test/querystring_spec.js
generated
vendored
Normal file
@@ -0,0 +1,128 @@
|
||||
var should = require('should'),
|
||||
stringify = require('../lib/querystring').build;
|
||||
|
||||
describe('stringify', function() {
|
||||
|
||||
describe('with null', function() {
|
||||
|
||||
it('throws', function() {
|
||||
(function() {
|
||||
var res = stringify(null);
|
||||
}).should.throw();
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('with a number', function() {
|
||||
|
||||
it('throws', function() {
|
||||
(function() {
|
||||
var res = stringify(100);
|
||||
}).should.throw();
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('with a string', function() {
|
||||
|
||||
describe('that is empty', function() {
|
||||
|
||||
it('throws', function() {
|
||||
(function() {
|
||||
var res = stringify('');
|
||||
}).should.throw();
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('that doesnt contain an equal sign', function() {
|
||||
|
||||
it('throws', function() {
|
||||
(function() {
|
||||
var res = stringify('boomshagalaga');
|
||||
}).should.throw();
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('that contains an equal sign', function() {
|
||||
|
||||
it('works', function() {
|
||||
var res = stringify('hello=123');
|
||||
res.should.eql('hello=123');
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('with an array', function() {
|
||||
|
||||
describe('with key val objects', function() {
|
||||
|
||||
it('works', function() {
|
||||
var res = stringify([ {foo: 'bar'} ]);
|
||||
res.should.eql('foo=bar');
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('where all elements are strings with an equal sign', function() {
|
||||
|
||||
it('works', function() {
|
||||
var res = stringify([ 'bar=123', 'quux=' ]);
|
||||
res.should.eql('bar=123&quux=');
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('with random words', function() {
|
||||
|
||||
it('throws', function() {
|
||||
(function() {
|
||||
var res = stringify(['hello', 'there']);
|
||||
}).should.throw();
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('with integers', function() {
|
||||
|
||||
it('throws', function() {
|
||||
(function() {
|
||||
var res = stringify([123, 432]);
|
||||
}).should.throw();
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('with an object', function() {
|
||||
|
||||
it('works', function() {
|
||||
var res = stringify({ test: 100 });
|
||||
res.should.eql('test=100');
|
||||
})
|
||||
|
||||
describe('with object where val is an array', function() {
|
||||
|
||||
it('works', function() {
|
||||
var res = stringify({ foo: ['bar', 'baz'] });
|
||||
res.should.eql('foo[]=bar&foo[]=baz');
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('with object where val is an array of key val objects', function() {
|
||||
|
||||
it('works', function() {
|
||||
var res = stringify({ foo: [{'1': 'bar'}, {'2': 'baz'}] });
|
||||
res.should.eql('foo[][1]=bar&foo[][2]=baz');
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
394
node_modules/needle/test/redirect_spec.js
generated
vendored
Normal file
394
node_modules/needle/test/redirect_spec.js
generated
vendored
Normal file
@@ -0,0 +1,394 @@
|
||||
var helpers = require('./helpers'),
|
||||
should = require('should'),
|
||||
sinon = require('sinon'),
|
||||
needle = require('./../');
|
||||
|
||||
var ports = {
|
||||
http : 8888,
|
||||
https : 9999
|
||||
}
|
||||
|
||||
var protocols = {
|
||||
http : require('http'),
|
||||
https : require('https')
|
||||
}
|
||||
|
||||
var code = 301;
|
||||
var location; // var to set the response location
|
||||
|
||||
function response_code() {
|
||||
return code;
|
||||
}
|
||||
|
||||
function response_headers() {
|
||||
return { 'Content-Type': 'text/plain', 'Location': location }
|
||||
}
|
||||
|
||||
describe('redirects', function() {
|
||||
|
||||
var spies = {},
|
||||
servers = {};
|
||||
|
||||
var current_protocol;
|
||||
var hostname = require('os').hostname();
|
||||
|
||||
// open two servers, one that responds to a redirect
|
||||
before(function(done) {
|
||||
|
||||
var conf = {
|
||||
port : ports.http,
|
||||
code : response_code,
|
||||
headers : response_headers
|
||||
}
|
||||
|
||||
servers.http = helpers.server(conf, function() {
|
||||
conf.port = ports.https;
|
||||
conf.protocol = 'https';
|
||||
servers.https = helpers.server(conf, done);
|
||||
});
|
||||
})
|
||||
|
||||
after(function(done) {
|
||||
servers.http.close(function() {
|
||||
servers.https.close(done);
|
||||
});
|
||||
})
|
||||
|
||||
var prots = {'http': 'https'};
|
||||
Object.keys(prots).forEach(function(protocol) {
|
||||
|
||||
current_protocol = protocol;
|
||||
var other_protocol = protocol == 'http' ? 'https' : 'http';
|
||||
|
||||
var opts, // each test will modify this
|
||||
host = '127.0.0.1',
|
||||
url = protocol + '://' + host + ':' + ports[protocol] + '/hello';
|
||||
|
||||
function send_request(opts, cb) {
|
||||
opts.rejectUnauthorized = false;
|
||||
// console.log(' -- sending request ' + url + ' -- redirect to ' + location);
|
||||
needle.post(url, { foo: 'bar' }, opts, cb);
|
||||
}
|
||||
|
||||
function not_followed(done) {
|
||||
send_request(opts, function(err, resp) {
|
||||
resp.statusCode.should.eql(301);
|
||||
if (current_protocol == 'http') {
|
||||
spies.http.callCount.should.eql(1); // only original request
|
||||
spies.https.callCount.should.eql(0);
|
||||
} else {
|
||||
spies.http.callCount.should.eql(0);
|
||||
spies.https.callCount.should.eql(1); // only original request
|
||||
}
|
||||
done();
|
||||
})
|
||||
}
|
||||
|
||||
function followed_same_protocol(done) {
|
||||
send_request(opts, function(err, resp) {
|
||||
// the original request plus the redirect one
|
||||
spies[current_protocol].callCount.should.eql(2);
|
||||
done();
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
function followed_other_protocol(done) {
|
||||
send_request(opts, function(err, resp) {
|
||||
// on new-ish node versions, https.request calls http.request internally,
|
||||
// so we need to amount for that additional call.
|
||||
// update: this doesn't happen on node > 10.x
|
||||
|
||||
var node_major_ver = process.version.split('.')[0].replace('v', '');
|
||||
var http_calls = protocols.http.Agent.defaultMaxSockets == Infinity && parseInt(node_major_ver) < 10 ? 2 : 1;
|
||||
|
||||
spies.http.callCount.should.eql(http_calls); // the one(s) from http.request
|
||||
spies.https.callCount.should.eql(1); // the one from https.request (redirect)
|
||||
done();
|
||||
})
|
||||
}
|
||||
|
||||
// set a spy on [protocol].request
|
||||
// so we can see how many times a request was made
|
||||
before(function() {
|
||||
spies.http = sinon.spy(protocols.http, 'request');
|
||||
spies.https = sinon.spy(protocols.https, 'request');
|
||||
})
|
||||
|
||||
// and make sure it is restored after each test
|
||||
afterEach(function() {
|
||||
spies.http.reset();
|
||||
spies.https.reset();
|
||||
})
|
||||
|
||||
after(function() {
|
||||
spies.http.restore();
|
||||
spies.https.restore();
|
||||
})
|
||||
|
||||
describe('when overriding defaults', function() {
|
||||
|
||||
before(function() {
|
||||
needle.defaults({ follow_max: 10 });
|
||||
opts = {};
|
||||
})
|
||||
|
||||
after(function() {
|
||||
// reset values to previous
|
||||
needle.defaults({ follow_max: 0 });
|
||||
})
|
||||
|
||||
describe('and redirected to the same path on same host and protocol', function() {
|
||||
before(function() {
|
||||
location = url;
|
||||
})
|
||||
it('does not follow redirect', not_followed);
|
||||
})
|
||||
|
||||
describe('and redirected to the same path on same host and different protocol', function() {
|
||||
before(function() {
|
||||
location = url.replace(protocol, other_protocol).replace(ports[protocol], ports[other_protocol]);
|
||||
})
|
||||
|
||||
it('follows redirect', followed_other_protocol);
|
||||
})
|
||||
|
||||
describe('and redirected to a different path on same host, same protocol', function() {
|
||||
before(function() {
|
||||
location = url.replace('/hello', '/goodbye');
|
||||
})
|
||||
it('follows redirect', followed_same_protocol);
|
||||
})
|
||||
|
||||
describe('and redirected to a different path on same host, different protocol', function() {
|
||||
before(function() {
|
||||
location = url.replace('/hello', '/goodbye').replace(protocol, other_protocol).replace(ports[protocol], ports[other_protocol]);
|
||||
})
|
||||
it('follows redirect', followed_other_protocol);
|
||||
})
|
||||
|
||||
describe('and redirected to same path on another host, same protocol', function() {
|
||||
before(function() {
|
||||
location = url.replace(host, hostname);
|
||||
})
|
||||
it('follows redirect', followed_same_protocol);
|
||||
})
|
||||
|
||||
describe('and redirected to same path on another host, different protocol', function() {
|
||||
before(function() {
|
||||
location = url.replace(host, hostname).replace(protocol, other_protocol).replace(ports[protocol], ports[other_protocol]);
|
||||
})
|
||||
it('follows redirect', followed_other_protocol);
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
// false and null have the same result
|
||||
var values = [false, null];
|
||||
values.forEach(function(value) {
|
||||
|
||||
describe('when follow is ' + value, function() {
|
||||
|
||||
before(function() {
|
||||
opts = { follow: value };
|
||||
})
|
||||
|
||||
|
||||
|
||||
describe('and redirected to the same path on same host and protocol', function() {
|
||||
before(function() {
|
||||
location = url;
|
||||
})
|
||||
|
||||
it('throws an error', function() {
|
||||
(function() {
|
||||
send_request(opts, function() { });
|
||||
}).should.throw;
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('when follow is true', function() {
|
||||
|
||||
before(function() {
|
||||
opts = { follow: true };
|
||||
})
|
||||
|
||||
describe('and redirected to the same path on same host and protocol', function() {
|
||||
before(function() { location = url })
|
||||
|
||||
it('throws an error', function() {
|
||||
(function() {
|
||||
send_request(opts, function() { });
|
||||
}).should.throw;
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('when follow is > 0', function() {
|
||||
|
||||
before(function() {
|
||||
needle.defaults({ follow: 10 });
|
||||
})
|
||||
|
||||
after(function() {
|
||||
needle.defaults({ follow: 0 });
|
||||
})
|
||||
|
||||
describe('when keep_method is false', function() {
|
||||
|
||||
before(function() {
|
||||
opts = { follow_keep_method: false };
|
||||
})
|
||||
|
||||
// defaults to follow host and protocol
|
||||
describe('and redirected to the same path on same host and different protocol', function() {
|
||||
|
||||
before(function() {
|
||||
location = url.replace(protocol, other_protocol);
|
||||
})
|
||||
|
||||
it('follows redirect', followed_other_protocol);
|
||||
|
||||
it('sends a GET request with no data', function(done) {
|
||||
send_request(opts, function(err, resp) {
|
||||
spies.http.args[0][0].method.should.eql('GET');
|
||||
// spy.args[0][3].should.eql(null);
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('and set_referer is true', function() {
|
||||
|
||||
before(function() {
|
||||
opts = { follow_set_referer: true };
|
||||
})
|
||||
|
||||
// defaults to follow host and protocol
|
||||
describe('and redirected to the same path on same host and different protocol', function() {
|
||||
|
||||
before(function() {
|
||||
location = url.replace(protocol, other_protocol);
|
||||
})
|
||||
|
||||
it('follows redirect', followed_other_protocol);
|
||||
|
||||
it('sets Referer header when following redirect', function(done) {
|
||||
send_request(opts, function(err, resp) {
|
||||
spies.http.args[0][0].headers['referer'].should.eql("http://" + host + ":8888/hello");
|
||||
// spies.http.args[0][3].should.eql({ foo: 'bar'});
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('and keep_method is true', function() {
|
||||
|
||||
before(function() {
|
||||
opts = { follow_keep_method: true };
|
||||
})
|
||||
|
||||
// defaults to follow host and protocol
|
||||
describe('and redirected to the same path on same host and different protocol', function() {
|
||||
|
||||
before(function() {
|
||||
location = url.replace(protocol, other_protocol);
|
||||
})
|
||||
|
||||
it('follows redirect', followed_other_protocol);
|
||||
|
||||
it('sends a POST request with the original data', function(done) {
|
||||
send_request(opts, function(err, resp) {
|
||||
spies.http.args[0][0].method.should.eql('post');
|
||||
// spies.http.args[0][3].should.eql({ foo: 'bar'});
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('and if_same_host is false', function() {
|
||||
|
||||
before(function() {
|
||||
opts = { follow_if_same_host: false };
|
||||
})
|
||||
|
||||
// by default it will follow other protocols
|
||||
describe('and redirected to same path on another domain, same protocol', function() {
|
||||
before(function() {
|
||||
location = url.replace(host, hostname);
|
||||
})
|
||||
it('follows redirect', followed_same_protocol);
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('and if_same_host is true', function() {
|
||||
|
||||
before(function() {
|
||||
opts = { follow_if_same_host: true };
|
||||
})
|
||||
|
||||
// by default it will follow other protocols
|
||||
describe('and redirected to same path on another domain, same protocol', function() {
|
||||
before(function() {
|
||||
location = url.replace(host, hostname);
|
||||
})
|
||||
|
||||
it('does not follow redirect', not_followed);
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('and if_same_protocol is false', function() {
|
||||
|
||||
before(function() {
|
||||
opts = { follow_if_same_protocol: false };
|
||||
})
|
||||
|
||||
// by default it will follow other hosts
|
||||
describe('and redirected to same path on another domain, different protocol', function() {
|
||||
before(function() {
|
||||
location = url.replace(host, hostname).replace(protocol, other_protocol).replace(ports[protocol], ports[other_protocol]);
|
||||
})
|
||||
it('follows redirect', followed_other_protocol);
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('and if_same_protocol is true', function() {
|
||||
|
||||
before(function() {
|
||||
opts = { follow_if_same_protocol: true };
|
||||
})
|
||||
|
||||
// by default it will follow other hosts
|
||||
describe('and redirected to same path on another domain, different protocol', function() {
|
||||
before(function() {
|
||||
location = url.replace(host, hostname).replace(protocol, other_protocol).replace(ports[protocol], ports[other_protocol]);
|
||||
})
|
||||
it('does not follow redirect', not_followed);
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
});
|
||||
45
node_modules/needle/test/redirect_with_timeout.js
generated
vendored
Normal file
45
node_modules/needle/test/redirect_with_timeout.js
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
var should = require('should')
|
||||
var needle = require('./../')
|
||||
|
||||
describe('follow redirects when read_timeout is set', function () {
|
||||
|
||||
it('clear timeout before following redirect', function (done) {
|
||||
var opts = {
|
||||
open_timeout: 1000,
|
||||
read_timeout: 3000,
|
||||
follow: 5,
|
||||
user_agent: 'Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36'
|
||||
}
|
||||
|
||||
var timedOut = 0
|
||||
var redirects = 0
|
||||
|
||||
var timer = setTimeout(function () {
|
||||
var hasRedirects = redirects > 0
|
||||
hasRedirects.should.equal(true)
|
||||
done()
|
||||
}, opts.read_timeout || 3000)
|
||||
|
||||
var resp = needle.get('http://google.com/', opts, function (err, resp, body) {
|
||||
var noErr = err === null
|
||||
var hasBody = body.length > 0
|
||||
noErr.should.equal(true);
|
||||
hasBody.should.equal(true);
|
||||
});
|
||||
|
||||
resp.on('redirect', function (location) {
|
||||
redirects++
|
||||
// console.info(' Redirected to ', location)
|
||||
})
|
||||
|
||||
resp.on('timeout', function (type) {
|
||||
timedOut++
|
||||
timedOut.should.equal(0)
|
||||
// console.error(' ', type, 'timeout')
|
||||
clearTimeout(timer)
|
||||
done()
|
||||
})
|
||||
|
||||
}).timeout(30000)
|
||||
|
||||
})
|
||||
236
node_modules/needle/test/request_stream_spec.js
generated
vendored
Normal file
236
node_modules/needle/test/request_stream_spec.js
generated
vendored
Normal file
@@ -0,0 +1,236 @@
|
||||
var fs = require('fs'),
|
||||
needle = require('..'),
|
||||
stream = require('stream'),
|
||||
http = require('http'),
|
||||
should = require('should'),
|
||||
sinon = require('sinon');
|
||||
|
||||
var port = 2233;
|
||||
|
||||
var node_major_ver = parseInt(process.version.split('.')[0].replace('v', ''));
|
||||
|
||||
describe('request stream length', function() {
|
||||
|
||||
var server, writable;
|
||||
|
||||
function createServer() {
|
||||
return http.createServer(function(req, res) {
|
||||
|
||||
req.on('data', function(chunk) {
|
||||
// console.log(chunk.length);
|
||||
})
|
||||
|
||||
req.on('end', function() {
|
||||
res.writeHeader(200, { 'Content-Type': 'application/json'})
|
||||
res.end(JSON.stringify({ headers: req.headers }))
|
||||
})
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
before(function(done) {
|
||||
server = createServer();
|
||||
server.listen(port, done)
|
||||
})
|
||||
|
||||
beforeEach(function() {
|
||||
writable = new stream.Readable();
|
||||
writable._read = function() {
|
||||
this.push('hello world');
|
||||
this.push(null);
|
||||
}
|
||||
})
|
||||
|
||||
after(function(done) {
|
||||
server.close(done)
|
||||
})
|
||||
|
||||
function send_request(opts, cb) {
|
||||
needle.post('http://localhost:' + port, writable, opts, function(err, resp) {
|
||||
cb(err, resp)
|
||||
})
|
||||
}
|
||||
|
||||
describe('no stream_length set', function() {
|
||||
|
||||
it('doesnt set Content-Length header', function(done) {
|
||||
send_request({}, function(err, resp) {
|
||||
should.not.exist(resp.body.headers['content-length']);
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
if (node_major_ver >= 10) {
|
||||
it('returns 400 if Transfer-Encoding is set to a blank string', function(done) {
|
||||
send_request({ headers: { 'Transfer-Encoding': '' }}, function(err, resp) {
|
||||
should.not.exist(err);
|
||||
resp.statusCode.should.eql(400);
|
||||
done()
|
||||
})
|
||||
})
|
||||
} else {
|
||||
it('doesnt work if Transfer-Encoding is set to a blank string', function(done) {
|
||||
send_request({ headers: { 'Transfer-Encoding': '' }}, function(err, resp) {
|
||||
err.code.should.eql('ECONNRESET');
|
||||
done()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
it('works if Transfer-Encoding is not set', function(done) {
|
||||
send_request({}, function(err, resp) {
|
||||
should.not.exist(err);
|
||||
resp.statusCode.should.eql(200);
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('stream_length set to invalid value', function() {
|
||||
|
||||
if (node_major_ver >= 10) {
|
||||
|
||||
it('returns 400 if Transfer-Encoding is set to a blank string', function(done) {
|
||||
send_request({ stream_length: 5, headers: { 'Transfer-Encoding': '' }}, function(err, resp) {
|
||||
should.not.exist(err);
|
||||
resp.statusCode.should.eql(400);
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('returns 400 if Transfer-Encoding is not set', function(done) {
|
||||
send_request({ stream_length: 5 }, function(err, resp) {
|
||||
should.not.exist(err);
|
||||
resp.statusCode.should.eql(400);
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
} else {
|
||||
|
||||
it('doesnt work if Transfer-Encoding is set to a blank string', function(done) {
|
||||
send_request({ stream_length: 5, headers: { 'Transfer-Encoding': '' }}, function(err, resp) {
|
||||
err.code.should.eql('ECONNRESET');
|
||||
done()
|
||||
})
|
||||
})
|
||||
it('doesnt work if Transfer-Encoding is not set', function(done) {
|
||||
send_request({ stream_length: 5 }, function(err, resp) {
|
||||
err.code.should.eql('ECONNRESET');
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
describe('stream_length is set to valid value', function() {
|
||||
|
||||
it('sets Content-Length header to that value', function(done) {
|
||||
send_request({ stream_length: 11 }, function(err, resp) {
|
||||
resp.body.headers['content-length'].should.eql('11');
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('works if Transfer-Encoding is set to a blank string', function(done) {
|
||||
send_request({ stream_length: 11, headers: { 'Transfer-Encoding': '' }}, function(err, resp) {
|
||||
should.not.exist(err);
|
||||
resp.statusCode.should.eql(200);
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('works if Transfer-Encoding is not set', function(done) {
|
||||
send_request({ stream_length: 11 }, function(err, resp) {
|
||||
should.not.exist(err);
|
||||
resp.statusCode.should.eql(200);
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
|
||||
describe('stream_length set to 0', function() {
|
||||
|
||||
describe('stream with path', function() {
|
||||
|
||||
var stub;
|
||||
|
||||
beforeEach(function() {
|
||||
writable.path = '/foo/bar';
|
||||
stub = sinon.stub(fs, 'stat', function(path, cb) {
|
||||
cb(null, { size: 11 })
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(function() {
|
||||
stub.restore();
|
||||
})
|
||||
|
||||
it('sets Content-Length header to streams length', function(done) {
|
||||
send_request({ stream_length: 0 }, function(err, resp) {
|
||||
resp.body.headers['content-length'].should.eql('11');
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('works if Transfer-Encoding is set to a blank string', function(done) {
|
||||
send_request({ stream_length: 0, headers: { 'Transfer-Encoding': '' }}, function(err, resp) {
|
||||
should.not.exist(err);
|
||||
resp.statusCode.should.eql(200);
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('works if Transfer-Encoding is not set', function(done) {
|
||||
send_request({ stream_length: 0 }, function(err, resp) {
|
||||
should.not.exist(err);
|
||||
resp.statusCode.should.eql(200);
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('stream without path', function() {
|
||||
|
||||
it('does not set Content-Length header', function(done) {
|
||||
send_request({ stream_length: 0 }, function(err, resp) {
|
||||
should.not.exist(resp.body.headers['content-length']);
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
if (node_major_ver >= 10) {
|
||||
it('returns 400 if Transfer-Encoding is set to a blank string', function(done) {
|
||||
send_request({ stream_length: 0, headers: { 'Transfer-Encoding': '' }}, function(err, resp) {
|
||||
should.not.exist(err);
|
||||
resp.statusCode.should.eql(400);
|
||||
done()
|
||||
})
|
||||
})
|
||||
} else {
|
||||
it('throws ECONNRESET if Transfer-Encoding is set to a blank string', function(done) {
|
||||
send_request({ stream_length: 0, headers: { 'Transfer-Encoding': '' }}, function(err, resp) {
|
||||
err.code.should.eql('ECONNRESET');
|
||||
done()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
it('works if Transfer-Encoding is not set', function(done) {
|
||||
send_request({ stream_length: 0 }, function(err, resp) {
|
||||
should.not.exist(err);
|
||||
resp.statusCode.should.eql(200);
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
138
node_modules/needle/test/response_stream_spec.js
generated
vendored
Normal file
138
node_modules/needle/test/response_stream_spec.js
generated
vendored
Normal file
@@ -0,0 +1,138 @@
|
||||
var should = require('should'),
|
||||
needle = require('./../'),
|
||||
http = require('http'),
|
||||
stream = require('stream'),
|
||||
fs = require('fs'),
|
||||
port = 11111,
|
||||
server;
|
||||
|
||||
describe('response streams', function() {
|
||||
|
||||
describe('when the server sends back json', function(){
|
||||
|
||||
before(function(done) {
|
||||
server = http.createServer(function(req, res) {
|
||||
res.setHeader('Content-Type', 'application/json')
|
||||
res.end('{"foo":"bar"}')
|
||||
}).listen(port, done);
|
||||
});
|
||||
|
||||
after(function(done) {
|
||||
server.close(done);
|
||||
})
|
||||
|
||||
describe('and the client uses streams', function(){
|
||||
|
||||
it('creates a proper streams2 stream', function(done) {
|
||||
var stream = needle.get('localhost:' + port)
|
||||
|
||||
// newer node versions set this to null instead of false
|
||||
var bool = !!stream._readableState.flowing;
|
||||
should.equal(false, bool);
|
||||
|
||||
var readableCalled = false;
|
||||
stream.on('readable', function() {
|
||||
readableCalled = true;
|
||||
})
|
||||
|
||||
stream.on('finish', function() {
|
||||
readableCalled.should.be.true;
|
||||
done();
|
||||
});
|
||||
|
||||
stream.resume();
|
||||
})
|
||||
|
||||
it('emits a single data item which is our JSON object', function(done) {
|
||||
var stream = needle.get('localhost:' + port)
|
||||
|
||||
var chunks = [];
|
||||
stream.on('readable', function () {
|
||||
while (chunk = this.read()) {
|
||||
chunk.should.be.an.Object;
|
||||
chunks.push(chunk);
|
||||
}
|
||||
})
|
||||
|
||||
stream.on('done', function () {
|
||||
chunks.should.have.length(1)
|
||||
chunks[0].should.have.property('foo', 'bar');
|
||||
done();
|
||||
});
|
||||
})
|
||||
|
||||
it('emits a raw buffer if we do not want to parse JSON', function(done) {
|
||||
var stream = needle.get('localhost:' + port, { parse: false })
|
||||
|
||||
var chunks = [];
|
||||
stream.on('readable', function () {
|
||||
while (chunk = this.read()) {
|
||||
Buffer.isBuffer(chunk).should.be.true;
|
||||
chunks.push(chunk);
|
||||
}
|
||||
})
|
||||
|
||||
stream.on('done', function() {
|
||||
var body = Buffer.concat(chunks).toString();
|
||||
body.should.equal('{"foo":"bar"}')
|
||||
done();
|
||||
});
|
||||
})
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
describe('when the server sends back what was posted to it', function () {
|
||||
var file = 'asdf.txt';
|
||||
|
||||
before(function(done){
|
||||
server = http.createServer(function(req, res) {
|
||||
res.setHeader('Content-Type', 'application/octet')
|
||||
req.pipe(res);
|
||||
}).listen(port);
|
||||
|
||||
fs.writeFile(file, 'contents of stream', done);
|
||||
});
|
||||
|
||||
after(function(done){
|
||||
server.close();
|
||||
fs.unlink(file, done);
|
||||
})
|
||||
|
||||
it('can PUT a stream', function (done) {
|
||||
var stream = needle.put('localhost:' + port, fs.createReadStream(file), { stream: true });
|
||||
|
||||
var chunks = [];
|
||||
stream.on('readable', function () {
|
||||
while (chunk = this.read()) {
|
||||
Buffer.isBuffer(chunk).should.be.true;
|
||||
chunks.push(chunk);
|
||||
}
|
||||
})
|
||||
|
||||
stream.on('end', function () {
|
||||
var body = Buffer.concat(chunks).toString();
|
||||
body.should.equal('contents of stream')
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('can PATCH a stream', function (done) {
|
||||
var stream = needle.patch('localhost:' + port, fs.createReadStream(file), { stream: true });
|
||||
|
||||
var chunks = [];
|
||||
stream.on('readable', function () {
|
||||
while (chunk = this.read()) {
|
||||
Buffer.isBuffer(chunk).should.be.true;
|
||||
chunks.push(chunk);
|
||||
}
|
||||
})
|
||||
|
||||
stream.on('end', function () {
|
||||
var body = Buffer.concat(chunks).toString();
|
||||
body.should.equal('contents of stream')
|
||||
done();
|
||||
});
|
||||
});
|
||||
})
|
||||
})
|
||||
67
node_modules/needle/test/socket_pool_spec.js
generated
vendored
Normal file
67
node_modules/needle/test/socket_pool_spec.js
generated
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
var needle = require('../'),
|
||||
should = require('should'),
|
||||
http = require('http');
|
||||
|
||||
var server, port = 11112;
|
||||
|
||||
describe('socket reuse', function() {
|
||||
|
||||
var httpAgent = new http.Agent({
|
||||
keepAlive : true,
|
||||
maxSockets : 1
|
||||
});
|
||||
|
||||
before(function(done) {
|
||||
server = http.createServer(function(req, res) {
|
||||
res.setHeader('Content-Type', 'application/json');
|
||||
setTimeout(function() {
|
||||
res.end('{"foo":"bar"}');
|
||||
}, 50);
|
||||
}).listen(port, done);
|
||||
});
|
||||
|
||||
after(function(done) {
|
||||
httpAgent.destroy();
|
||||
server.close(done);
|
||||
});
|
||||
|
||||
describe('when sockets are reused', function() {
|
||||
|
||||
it('does not duplicate listeners on .end', function(done) {
|
||||
|
||||
var last_error;
|
||||
var count = 10;
|
||||
|
||||
function completed(err) {
|
||||
--count || done(last_error);
|
||||
}
|
||||
|
||||
function send() {
|
||||
needle.get('localhost:' + port, { agent: httpAgent }, function(err, resp) {
|
||||
if (err)
|
||||
throw new Error("Unexpected error: " + err);
|
||||
|
||||
// lets go through all sockets and inspect all socket objects
|
||||
for (hostTarget in httpAgent.sockets) {
|
||||
httpAgent.sockets[hostTarget].forEach(function(socket) {
|
||||
// normally, there are 2 internal listeners and 1 needle sets up,
|
||||
// but to be sure the test does not fail even if newer node versions
|
||||
// introduce additional listeners, we use a higher limit.
|
||||
try {
|
||||
socket.listeners('end').length.should.be.below(5, "too many listeners on the socket object's end event");
|
||||
} catch (e) {
|
||||
last_error = e;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
completed();
|
||||
});
|
||||
}
|
||||
|
||||
for (var i = 0; i < count; i++) {
|
||||
send();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
46
node_modules/needle/test/uri_modifier_spec.js
generated
vendored
Normal file
46
node_modules/needle/test/uri_modifier_spec.js
generated
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
var needle = require('../'),
|
||||
sinon = require('sinon'),
|
||||
should = require('should'),
|
||||
http = require('http'),
|
||||
helpers = require('./helpers');
|
||||
|
||||
var port = 3456;
|
||||
|
||||
describe('uri_modifier config parameter function', function() {
|
||||
|
||||
var server, uri;
|
||||
|
||||
function send_request(mw, cb) {
|
||||
needle.get(uri, { uri_modifier: mw }, cb);
|
||||
}
|
||||
|
||||
before(function(done){
|
||||
server = helpers.server({ port: port }, done);
|
||||
})
|
||||
|
||||
after(function(done) {
|
||||
server.close(done);
|
||||
})
|
||||
|
||||
describe('modifies uri', function() {
|
||||
|
||||
var path = '/foo/replace';
|
||||
|
||||
before(function() {
|
||||
uri = 'localhost:' + port + path
|
||||
});
|
||||
|
||||
it('should modify path', function(done) {
|
||||
send_request(function(uri) {
|
||||
return uri.replace('/replace', '');
|
||||
}, function(err, res) {
|
||||
should.not.exist(err);
|
||||
should(res.req.path).be.exactly('/foo');
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
155
node_modules/needle/test/url_spec.js
generated
vendored
Normal file
155
node_modules/needle/test/url_spec.js
generated
vendored
Normal file
@@ -0,0 +1,155 @@
|
||||
var needle = require('../'),
|
||||
sinon = require('sinon'),
|
||||
should = require('should'),
|
||||
http = require('http'),
|
||||
helpers = require('./helpers');
|
||||
|
||||
var port = 3456;
|
||||
|
||||
describe('urls', function() {
|
||||
|
||||
var server, url;
|
||||
|
||||
function send_request(cb) {
|
||||
return needle.get(url, cb);
|
||||
}
|
||||
|
||||
before(function(done){
|
||||
server = helpers.server({ port: port }, done);
|
||||
})
|
||||
|
||||
after(function(done) {
|
||||
server.close(done);
|
||||
})
|
||||
|
||||
describe('null URL', function(){
|
||||
|
||||
it('throws', function(){
|
||||
(function() {
|
||||
send_request()
|
||||
}).should.throw();
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('invalid protocol', function(){
|
||||
|
||||
before(function() {
|
||||
url = 'foo://google.com/what'
|
||||
})
|
||||
|
||||
it('does not throw', function(done) {
|
||||
(function() {
|
||||
send_request(function(err) {
|
||||
done();
|
||||
})
|
||||
}).should.not.throw()
|
||||
})
|
||||
|
||||
it('returns an error', function(done) {
|
||||
send_request(function(err) {
|
||||
err.should.be.an.Error;
|
||||
err.code.should.match(/ENOTFOUND|EADDRINFO|EAI_AGAIN/)
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('invalid host', function(){
|
||||
|
||||
before(function() {
|
||||
url = 'http://s1\\\u0002.com/'
|
||||
})
|
||||
|
||||
it('fails', function(done) {
|
||||
(function() {
|
||||
send_request(function(){ })
|
||||
}.should.throw(TypeError))
|
||||
done()
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
/*
|
||||
describe('invalid path', function(){
|
||||
|
||||
before(function() {
|
||||
url = 'http://www.google.com\\\/x\\\ %^&*() /x2.com/'
|
||||
})
|
||||
|
||||
it('fails', function(done) {
|
||||
send_request(function(err) {
|
||||
err.should.be.an.Error;
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
*/
|
||||
|
||||
describe('valid protocol and path', function() {
|
||||
|
||||
before(function() {
|
||||
url = 'http://localhost:' + port + '/foo';
|
||||
})
|
||||
|
||||
it('works', function(done) {
|
||||
send_request(function(err){
|
||||
should.not.exist(err);
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('no protocol but with slashes and valid path', function() {
|
||||
|
||||
before(function() {
|
||||
url = '//localhost:' + port + '/foo';
|
||||
})
|
||||
|
||||
it('works', function(done) {
|
||||
send_request(function(err){
|
||||
should.not.exist(err);
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('no protocol nor slashes and valid path', function() {
|
||||
|
||||
before(function() {
|
||||
url = 'localhost:' + port + '/foo';
|
||||
})
|
||||
|
||||
it('works', function(done) {
|
||||
send_request(function(err){
|
||||
should.not.exist(err);
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('double encoding', function() {
|
||||
|
||||
var path = '/foo?email=' + encodeURIComponent('what-ever@Example.Com');
|
||||
|
||||
before(function() {
|
||||
url = 'localhost:' + port + path
|
||||
});
|
||||
|
||||
it('should not occur', function(done) {
|
||||
send_request(function(err, res) {
|
||||
should.not.exist(err);
|
||||
should(res.req.path).be.exactly(path);
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
})
|
||||
|
||||
})
|
||||
17
node_modules/needle/test/utils/formidable.js
generated
vendored
Normal file
17
node_modules/needle/test/utils/formidable.js
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
var formidable = require('formidable'),
|
||||
http = require('http'),
|
||||
util = require('util');
|
||||
|
||||
var port = process.argv[2] || 8888;
|
||||
|
||||
http.createServer(function(req, res) {
|
||||
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');
|
||||
console.log(util.inspect({fields: fields, files: files}))
|
||||
res.end(util.inspect({fields: fields, files: files}));
|
||||
});
|
||||
}).listen(port);
|
||||
|
||||
console.log('HTTP server listening on port ' + port);
|
||||
62
node_modules/needle/test/utils/proxy.js
generated
vendored
Normal file
62
node_modules/needle/test/utils/proxy.js
generated
vendored
Normal file
@@ -0,0 +1,62 @@
|
||||
var http = require('http'),
|
||||
https = require('https'),
|
||||
url = require('url');
|
||||
|
||||
var port = 1234,
|
||||
log = true,
|
||||
request_auth = false;
|
||||
|
||||
http.createServer(function(request, response) {
|
||||
|
||||
console.log(request.headers);
|
||||
console.log("Got request: " + request.url);
|
||||
console.log("Forwarding request to " + request.headers['host']);
|
||||
|
||||
if (request_auth) {
|
||||
if (!request.headers['proxy-authorization']) {
|
||||
response.writeHead(407, {'Proxy-Authenticate': 'Basic realm="proxy.com"'})
|
||||
return response.end('Hello.');
|
||||
}
|
||||
}
|
||||
|
||||
var remote = url.parse(request.url);
|
||||
var protocol = remote.protocol == 'https:' ? https : http;
|
||||
|
||||
var opts = {
|
||||
host: request.headers['host'],
|
||||
port: remote.port || (remote.protocol == 'https:' ? 443 : 80),
|
||||
method: request.method,
|
||||
path: remote.pathname,
|
||||
headers: request.headers
|
||||
}
|
||||
|
||||
var proxy_request = protocol.request(opts, function(proxy_response){
|
||||
|
||||
proxy_response.on('data', function(chunk) {
|
||||
if (log) console.log(chunk.toString());
|
||||
response.write(chunk, 'binary');
|
||||
});
|
||||
proxy_response.on('end', function() {
|
||||
response.end();
|
||||
});
|
||||
|
||||
response.writeHead(proxy_response.statusCode, proxy_response.headers);
|
||||
});
|
||||
|
||||
request.on('data', function(chunk) {
|
||||
if (log) console.log(chunk.toString());
|
||||
proxy_request.write(chunk, 'binary');
|
||||
});
|
||||
|
||||
request.on('end', function() {
|
||||
proxy_request.end();
|
||||
});
|
||||
|
||||
}).listen(port);
|
||||
|
||||
process.on('uncaughtException', function(err){
|
||||
console.log('Uncaught exception!');
|
||||
console.log(err);
|
||||
});
|
||||
|
||||
console.log("Proxy server listening on port " + port);
|
||||
104
node_modules/needle/test/utils/test.js
generated
vendored
Normal file
104
node_modules/needle/test/utils/test.js
generated
vendored
Normal file
@@ -0,0 +1,104 @@
|
||||
// TODO: write specs. :)
|
||||
|
||||
var fs = require('fs'),
|
||||
client = require('./../../');
|
||||
|
||||
process.env.DEBUG = true;
|
||||
|
||||
var response_callback = function(err, resp, body){
|
||||
console.log(err);
|
||||
if(resp) console.log("Got status code " + resp.statusCode)
|
||||
console.log(body);
|
||||
}
|
||||
|
||||
function simple_head(){
|
||||
client.head('http://www.amazon.com', response_callback);
|
||||
}
|
||||
|
||||
function simple_get(){
|
||||
client.get('http://www.nodejs.org', response_callback);
|
||||
}
|
||||
|
||||
function proxy_get(){
|
||||
client.get('https://www.google.com/search?q=nodejs', {proxy: 'http://localhost:1234'}, response_callback);
|
||||
}
|
||||
|
||||
function auth_get(){
|
||||
client.get('https://www.twitter.com', {username: 'asd', password: '123'}, response_callback);
|
||||
}
|
||||
|
||||
function simple_post(url){
|
||||
|
||||
var data = {
|
||||
foo: 'bar',
|
||||
baz: {
|
||||
nested: 'attribute'
|
||||
}
|
||||
}
|
||||
|
||||
client.post(url, data, response_callback);
|
||||
|
||||
}
|
||||
|
||||
function multipart_post(url){
|
||||
|
||||
var filename = 'test_file.txt';
|
||||
var data = 'Plain text data.\nLorem ipsum dolor sit amet.\nBla bla bla.\n';
|
||||
fs.writeFileSync(filename, data);
|
||||
|
||||
var black_pixel = Buffer.from("data:image/gif;base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs=".replace(/^data:image\/\w+;base64,/, ""), "base64");
|
||||
|
||||
var data = {
|
||||
foo: 'bar',
|
||||
bar: 'baz',
|
||||
nested: {
|
||||
my_document: { file: filename, content_type: 'text/plain' },
|
||||
even: {
|
||||
more: 'nesting'
|
||||
}
|
||||
},
|
||||
pixel: { filename: 'black_pixel.gif', buffer: black_pixel, content_type: 'image/gif' },
|
||||
field2: {value: JSON.stringify({"json":[ {"one":1}, {"two":2} ]}), content_type: 'application/json' }
|
||||
}
|
||||
|
||||
client.post(url, data, {multipart: true}, function(err, resp, body){
|
||||
|
||||
console.log(err);
|
||||
console.log("Got status code " + resp.statusCode)
|
||||
console.log(body);
|
||||
fs.unlink(filename);
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
switch(process.argv[2]){
|
||||
case 'head':
|
||||
simple_head();
|
||||
break;
|
||||
case 'get':
|
||||
simple_get();
|
||||
break;
|
||||
case 'auth':
|
||||
auth_get();
|
||||
break;
|
||||
case 'proxy':
|
||||
proxy_get();
|
||||
break;
|
||||
case 'post':
|
||||
simple_post(process.argv[3] || 'http://posttestserver.com/post.php');
|
||||
break;
|
||||
case 'multipart':
|
||||
multipart_post(process.argv[3] || 'http://posttestserver.com/post.php?dir=example');
|
||||
break;
|
||||
case 'all':
|
||||
simple_head();
|
||||
simple_get();
|
||||
auth_get();
|
||||
proxy_get();
|
||||
simple_post(process.argv[3] || 'http://posttestserver.com/post.php');
|
||||
multipart_post(process.argv[3] || 'http://posttestserver.com/post.php?dir=example');
|
||||
break;
|
||||
default:
|
||||
console.log("Usage: ./test.js [head|get|auth|proxy|multipart]")
|
||||
}
|
||||
Reference in New Issue
Block a user