├── .github ├── dependabot.yml └── workflows │ ├── ci.yml │ └── test.yml ├── .gitignore ├── .npmrc ├── LICENSE ├── README.md ├── index.js ├── package.json └── test ├── auth.js ├── basic.js ├── concat.js ├── headers.js ├── post.js ├── redirect.js └── request.js /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: / 5 | schedule: 6 | interval: daily 7 | labels: 8 | - dependency 9 | versioning-strategy: increase-if-necessary 10 | - package-ecosystem: github-actions 11 | directory: / 12 | schedule: 13 | interval: daily 14 | labels: 15 | - dependency 16 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 'on': 3 | - push 4 | - pull_request 5 | jobs: 6 | test: 7 | name: Node ${{ matrix.node }} / ${{ matrix.os }} 8 | runs-on: ${{ matrix.os }} 9 | strategy: 10 | fail-fast: false 11 | matrix: 12 | os: 13 | - ubuntu-latest 14 | node: 15 | - '14' 16 | steps: 17 | - uses: actions/checkout@v2 18 | - uses: actions/setup-node@v2 19 | with: 20 | node-version: ${{ matrix.node }} 21 | - run: npm install 22 | - run: npm run build --if-present 23 | - run: npm test 24 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Node CI 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - master 7 | 8 | jobs: 9 | test: 10 | strategy: 11 | matrix: 12 | os: [macos-latest, ubuntu-latest, windows-latest] 13 | node: [10.0.0, 12.0.0, 14.0.0, 16.0.0] 14 | runs-on: ${{ matrix.os }} 15 | steps: 16 | - uses: actions/checkout@v2 17 | - name: Use Node.js ${{ matrix.node }} 18 | uses: actions/setup-node@v2 19 | with: 20 | node-version: ${{ matrix.node }} 21 | - name: Install dependencies 22 | run: npm install 23 | - name: Run tests 24 | run: npm test 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules/ 2 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) Feross Aboukhadijeh 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # simple-get [![ci][ci-image]][ci-url] [![npm][npm-image]][npm-url] [![downloads][downloads-image]][downloads-url] [![javascript style guide][standard-image]][standard-url] 2 | 3 | [ci-image]: https://img.shields.io/github/workflow/status/feross/simple-get/ci/master 4 | [ci-url]: https://github.com/feross/simple-get/actions 5 | [npm-image]: https://img.shields.io/npm/v/simple-get.svg 6 | [npm-url]: https://npmjs.org/package/simple-get 7 | [downloads-image]: https://img.shields.io/npm/dm/simple-get.svg 8 | [downloads-url]: https://npmjs.org/package/simple-get 9 | [standard-image]: https://img.shields.io/badge/code_style-standard-brightgreen.svg 10 | [standard-url]: https://standardjs.com 11 | 12 | ### Simplest way to make http get requests 13 | 14 | ## features 15 | 16 | This module is the lightest possible wrapper on top of node.js `http`, but supporting these essential features: 17 | 18 | - follows redirects 19 | - automatically handles gzip/deflate responses 20 | - supports HTTPS 21 | - supports specifying a timeout 22 | - supports convenience `url` key so there's no need to use `url.parse` on the url when specifying options 23 | - composes well with npm packages for features like cookies, proxies, form data, & OAuth 24 | 25 | All this in < 100 lines of code. 26 | 27 | ## install 28 | 29 | ``` 30 | npm install simple-get 31 | ``` 32 | 33 | ## usage 34 | 35 | Note, all these examples also work in the browser with [browserify](http://browserify.org/). 36 | 37 | ### simple GET request 38 | 39 | Doesn't get easier than this: 40 | 41 | ```js 42 | const get = require('simple-get') 43 | 44 | get('http://example.com', function (err, res) { 45 | if (err) throw err 46 | console.log(res.statusCode) // 200 47 | res.pipe(process.stdout) // `res` is a stream 48 | }) 49 | ``` 50 | 51 | ### even simpler GET request 52 | 53 | If you just want the data, and don't want to deal with streams: 54 | 55 | ```js 56 | const get = require('simple-get') 57 | 58 | get.concat('http://example.com', function (err, res, data) { 59 | if (err) throw err 60 | console.log(res.statusCode) // 200 61 | console.log(data) // Buffer('this is the server response') 62 | }) 63 | ``` 64 | 65 | ### POST, PUT, PATCH, HEAD, DELETE support 66 | 67 | For `POST`, call `get.post` or use option `{ method: 'POST' }`. 68 | 69 | ```js 70 | const get = require('simple-get') 71 | 72 | const opts = { 73 | url: 'http://example.com', 74 | body: 'this is the POST body' 75 | } 76 | get.post(opts, function (err, res) { 77 | if (err) throw err 78 | res.pipe(process.stdout) // `res` is a stream 79 | }) 80 | ``` 81 | 82 | #### A more complex example: 83 | 84 | ```js 85 | const get = require('simple-get') 86 | 87 | get({ 88 | url: 'http://example.com', 89 | method: 'POST', 90 | body: 'this is the POST body', 91 | 92 | // simple-get accepts all options that node.js `http` accepts 93 | // See: http://nodejs.org/api/http.html#http_http_request_options_callback 94 | headers: { 95 | 'user-agent': 'my cool app' 96 | } 97 | }, function (err, res) { 98 | if (err) throw err 99 | 100 | // All properties/methods from http.IncomingResponse are available, 101 | // even if a gunzip/inflate transform stream was returned. 102 | // See: http://nodejs.org/api/http.html#http_http_incomingmessage 103 | res.setTimeout(10000) 104 | console.log(res.headers) 105 | 106 | res.on('data', function (chunk) { 107 | // `chunk` is the decoded response, after it's been gunzipped or inflated 108 | // (if applicable) 109 | console.log('got a chunk of the response: ' + chunk) 110 | })) 111 | 112 | }) 113 | ``` 114 | 115 | ### JSON 116 | 117 | You can serialize/deserialize request and response with JSON: 118 | 119 | ```js 120 | const get = require('simple-get') 121 | 122 | const opts = { 123 | method: 'POST', 124 | url: 'http://example.com', 125 | body: { 126 | key: 'value' 127 | }, 128 | json: true 129 | } 130 | get.concat(opts, function (err, res, data) { 131 | if (err) throw err 132 | console.log(data.key) // `data` is an object 133 | }) 134 | ``` 135 | 136 | ### Timeout 137 | 138 | You can set a timeout (in milliseconds) on the request with the `timeout` option. 139 | If the request takes longer than `timeout` to complete, then the entire request 140 | will fail with an `Error`. 141 | 142 | ```js 143 | const get = require('simple-get') 144 | 145 | const opts = { 146 | url: 'http://example.com', 147 | timeout: 2000 // 2 second timeout 148 | } 149 | 150 | get(opts, function (err, res) {}) 151 | ``` 152 | 153 | ### One Quick Tip 154 | 155 | It's a good idea to set the `'user-agent'` header so the provider can more easily 156 | see how their resource is used. 157 | 158 | ```js 159 | const get = require('simple-get') 160 | const pkg = require('./package.json') 161 | 162 | get('http://example.com', { 163 | headers: { 164 | 'user-agent': `my-module/${pkg.version} (https://github.com/username/my-module)` 165 | } 166 | }) 167 | ``` 168 | 169 | ### Proxies 170 | 171 | You can use the [`tunnel`](https://github.com/koichik/node-tunnel) module with the 172 | `agent` option to work with proxies: 173 | 174 | ```js 175 | const get = require('simple-get') 176 | const tunnel = require('tunnel') 177 | 178 | const opts = { 179 | url: 'http://example.com', 180 | agent: tunnel.httpOverHttp({ 181 | proxy: { 182 | host: 'localhost' 183 | } 184 | }) 185 | } 186 | 187 | get(opts, function (err, res) {}) 188 | ``` 189 | 190 | ### Cookies 191 | 192 | You can use the [`cookie`](https://github.com/jshttp/cookie) module to include 193 | cookies in a request: 194 | 195 | ```js 196 | const get = require('simple-get') 197 | const cookie = require('cookie') 198 | 199 | const opts = { 200 | url: 'http://example.com', 201 | headers: { 202 | cookie: cookie.serialize('foo', 'bar') 203 | } 204 | } 205 | 206 | get(opts, function (err, res) {}) 207 | ``` 208 | 209 | ### Form data 210 | 211 | You can use the [`form-data`](https://github.com/form-data/form-data) module to 212 | create POST request with form data: 213 | 214 | ```js 215 | const fs = require('fs') 216 | const get = require('simple-get') 217 | const FormData = require('form-data') 218 | const form = new FormData() 219 | 220 | form.append('my_file', fs.createReadStream('/foo/bar.jpg')) 221 | 222 | const opts = { 223 | url: 'http://example.com', 224 | body: form 225 | } 226 | 227 | get.post(opts, function (err, res) {}) 228 | ``` 229 | 230 | #### Or, include `application/x-www-form-urlencoded` form data manually: 231 | 232 | ```js 233 | const get = require('simple-get') 234 | 235 | const opts = { 236 | url: 'http://example.com', 237 | form: { 238 | key: 'value' 239 | } 240 | } 241 | get.post(opts, function (err, res) {}) 242 | ``` 243 | 244 | ### Specifically disallowing redirects 245 | 246 | ```js 247 | const get = require('simple-get') 248 | 249 | const opts = { 250 | url: 'http://example.com/will-redirect-elsewhere', 251 | followRedirects: false 252 | } 253 | // res.statusCode will be 301, no error thrown 254 | get(opts, function (err, res) {}) 255 | ``` 256 | 257 | ### Basic Auth 258 | 259 | ```js 260 | const user = 'someuser' 261 | const pass = 'pa$$word' 262 | const encodedAuth = Buffer.from(`${user}:${pass}`).toString('base64') 263 | 264 | get('http://example.com', { 265 | headers: { 266 | authorization: `Basic ${encodedAuth}` 267 | } 268 | }) 269 | ``` 270 | 271 | ### OAuth 272 | 273 | You can use the [`oauth-1.0a`](https://github.com/ddo/oauth-1.0a) module to create 274 | a signed OAuth request: 275 | 276 | ```js 277 | const get = require('simple-get') 278 | const crypto = require('crypto') 279 | const OAuth = require('oauth-1.0a') 280 | 281 | const oauth = OAuth({ 282 | consumer: { 283 | key: process.env.CONSUMER_KEY, 284 | secret: process.env.CONSUMER_SECRET 285 | }, 286 | signature_method: 'HMAC-SHA1', 287 | hash_function: (baseString, key) => crypto.createHmac('sha1', key).update(baseString).digest('base64') 288 | }) 289 | 290 | const token = { 291 | key: process.env.ACCESS_TOKEN, 292 | secret: process.env.ACCESS_TOKEN_SECRET 293 | } 294 | 295 | const url = 'https://api.twitter.com/1.1/statuses/home_timeline.json' 296 | 297 | const opts = { 298 | url: url, 299 | headers: oauth.toHeader(oauth.authorize({url, method: 'GET'}, token)), 300 | json: true 301 | } 302 | 303 | get(opts, function (err, res) {}) 304 | ``` 305 | 306 | ### Throttle requests 307 | 308 | You can use [limiter](https://github.com/jhurliman/node-rate-limiter) to throttle requests. This is useful when calling an API that is rate limited. 309 | 310 | ```js 311 | const simpleGet = require('simple-get') 312 | const RateLimiter = require('limiter').RateLimiter 313 | const limiter = new RateLimiter(1, 'second') 314 | 315 | const get = (opts, cb) => limiter.removeTokens(1, () => simpleGet(opts, cb)) 316 | get.concat = (opts, cb) => limiter.removeTokens(1, () => simpleGet.concat(opts, cb)) 317 | 318 | var opts = { 319 | url: 'http://example.com' 320 | } 321 | 322 | get.concat(opts, processResult) 323 | get.concat(opts, processResult) 324 | 325 | function processResult (err, res, data) { 326 | if (err) throw err 327 | console.log(data.toString()) 328 | } 329 | ``` 330 | 331 | ## license 332 | 333 | MIT. Copyright (c) [Feross Aboukhadijeh](http://feross.org). 334 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /*! simple-get. MIT License. Feross Aboukhadijeh */ 2 | module.exports = simpleGet 3 | 4 | const concat = require('simple-concat') 5 | const decompressResponse = require('decompress-response') // excluded from browser build 6 | const http = require('http') 7 | const https = require('https') 8 | const once = require('once') 9 | const querystring = require('querystring') 10 | const url = require('url') 11 | 12 | const isStream = o => o !== null && typeof o === 'object' && typeof o.pipe === 'function' 13 | 14 | function simpleGet (opts, cb) { 15 | opts = Object.assign({ maxRedirects: 10 }, typeof opts === 'string' ? { url: opts } : opts) 16 | cb = once(cb) 17 | 18 | if (opts.url) { 19 | const { hostname, port, protocol, auth, path } = url.parse(opts.url) // eslint-disable-line node/no-deprecated-api 20 | delete opts.url 21 | if (!hostname && !port && !protocol && !auth) opts.path = path // Relative redirect 22 | else Object.assign(opts, { hostname, port, protocol, auth, path }) // Absolute redirect 23 | } 24 | 25 | const headers = { 'accept-encoding': 'gzip, deflate' } 26 | if (opts.headers) Object.keys(opts.headers).forEach(k => (headers[k.toLowerCase()] = opts.headers[k])) 27 | opts.headers = headers 28 | 29 | let body 30 | if (opts.body) { 31 | body = opts.json && !isStream(opts.body) ? JSON.stringify(opts.body) : opts.body 32 | } else if (opts.form) { 33 | body = typeof opts.form === 'string' ? opts.form : querystring.stringify(opts.form) 34 | opts.headers['content-type'] = 'application/x-www-form-urlencoded' 35 | } 36 | 37 | if (body) { 38 | if (!opts.method) opts.method = 'POST' 39 | if (!isStream(body)) opts.headers['content-length'] = Buffer.byteLength(body) 40 | if (opts.json && !opts.form) opts.headers['content-type'] = 'application/json' 41 | } 42 | delete opts.body; delete opts.form 43 | 44 | if (opts.json) opts.headers.accept = 'application/json' 45 | if (opts.method) opts.method = opts.method.toUpperCase() 46 | 47 | const originalHost = opts.hostname // hostname before potential redirect 48 | const protocol = opts.protocol === 'https:' ? https : http // Support http/https urls 49 | const req = protocol.request(opts, res => { 50 | if (opts.followRedirects !== false && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) { 51 | opts.url = res.headers.location // Follow 3xx redirects 52 | delete opts.headers.host // Discard `host` header on redirect (see #32) 53 | res.resume() // Discard response 54 | 55 | const redirectHost = url.parse(opts.url).hostname // eslint-disable-line node/no-deprecated-api 56 | // If redirected host is different than original host, drop headers to prevent cookie leak (#73) 57 | if (redirectHost !== null && redirectHost !== originalHost) { 58 | delete opts.headers.cookie 59 | delete opts.headers.authorization 60 | } 61 | 62 | if (opts.method === 'POST' && [301, 302].includes(res.statusCode)) { 63 | opts.method = 'GET' // On 301/302 redirect, change POST to GET (see #35) 64 | delete opts.headers['content-length']; delete opts.headers['content-type'] 65 | } 66 | 67 | if (opts.maxRedirects-- === 0) return cb(new Error('too many redirects')) 68 | else return simpleGet(opts, cb) 69 | } 70 | 71 | const tryUnzip = typeof decompressResponse === 'function' && opts.method !== 'HEAD' 72 | cb(null, tryUnzip ? decompressResponse(res) : res) 73 | }) 74 | req.on('timeout', () => { 75 | req.abort() 76 | cb(new Error('Request timed out')) 77 | }) 78 | req.on('error', cb) 79 | 80 | if (isStream(body)) body.on('error', cb).pipe(req) 81 | else req.end(body) 82 | 83 | return req 84 | } 85 | 86 | simpleGet.concat = (opts, cb) => { 87 | return simpleGet(opts, (err, res) => { 88 | if (err) return cb(err) 89 | concat(res, (err, data) => { 90 | if (err) return cb(err) 91 | if (opts.json) { 92 | try { 93 | data = JSON.parse(data.toString()) 94 | } catch (err) { 95 | return cb(err, res, data) 96 | } 97 | } 98 | cb(null, res, data) 99 | }) 100 | }) 101 | } 102 | 103 | ;['get', 'post', 'put', 'patch', 'head', 'delete'].forEach(method => { 104 | simpleGet[method] = (opts, cb) => { 105 | if (typeof opts === 'string') opts = { url: opts } 106 | return simpleGet(Object.assign({ method: method.toUpperCase() }, opts), cb) 107 | } 108 | }) 109 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "simple-get", 3 | "description": "Simplest way to make http get requests. Supports HTTPS, redirects, gzip/deflate, streams in < 100 lines.", 4 | "version": "4.0.1", 5 | "author": { 6 | "name": "Feross Aboukhadijeh", 7 | "email": "feross@feross.org", 8 | "url": "https://feross.org" 9 | }, 10 | "browser": { 11 | "decompress-response": false 12 | }, 13 | "bugs": { 14 | "url": "https://github.com/feross/simple-get/issues" 15 | }, 16 | "dependencies": { 17 | "decompress-response": "^6.0.0", 18 | "once": "^1.3.1", 19 | "simple-concat": "^1.0.0" 20 | }, 21 | "devDependencies": { 22 | "self-signed-https": "^1.0.5", 23 | "standard": "*", 24 | "string-to-stream": "^3.0.0", 25 | "tape": "^5.0.0" 26 | }, 27 | "engines": { 28 | "node": ">=10" 29 | }, 30 | "files": [ 31 | "index.js" 32 | ], 33 | "homepage": "https://github.com/feross/simple-get", 34 | "keywords": [ 35 | "request", 36 | "http", 37 | "GET", 38 | "get request", 39 | "http.get", 40 | "redirects", 41 | "follow redirects", 42 | "gzip", 43 | "deflate", 44 | "https", 45 | "http-https", 46 | "stream", 47 | "simple request", 48 | "simple get" 49 | ], 50 | "license": "MIT", 51 | "main": "index.js", 52 | "repository": { 53 | "type": "git", 54 | "url": "git://github.com/feross/simple-get.git" 55 | }, 56 | "scripts": { 57 | "test": "standard && tape test/*.js" 58 | }, 59 | "funding": [ 60 | { 61 | "type": "github", 62 | "url": "https://github.com/sponsors/feross" 63 | }, 64 | { 65 | "type": "patreon", 66 | "url": "https://www.patreon.com/feross" 67 | }, 68 | { 69 | "type": "consulting", 70 | "url": "https://feross.org/support" 71 | } 72 | ] 73 | } 74 | -------------------------------------------------------------------------------- /test/auth.js: -------------------------------------------------------------------------------- 1 | const concat = require('simple-concat') 2 | const get = require('../') 3 | const http = require('http') 4 | const test = require('tape') 5 | 6 | test('basic auth', function (t) { 7 | t.plan(5) 8 | 9 | const server = http.createServer(function (req, res) { 10 | t.equal(req.headers.authorization, 'Basic Zm9vOmJhcg==') 11 | res.statusCode = 200 12 | res.end('response') 13 | }) 14 | 15 | server.listen(0, function () { 16 | const port = server.address().port 17 | get('http://foo:bar@localhost:' + port, function (err, res) { 18 | t.error(err) 19 | t.equal(res.statusCode, 200) 20 | concat(res, function (err, data) { 21 | t.error(err) 22 | t.equal(data.toString(), 'response') 23 | server.close() 24 | }) 25 | }) 26 | }) 27 | }) 28 | 29 | test('basic auth + host', function (t) { 30 | t.plan(5) 31 | 32 | const server = http.createServer(function (req, res) { 33 | t.equal(req.headers.authorization, 'Basic Zm9vOmJhcg==') 34 | res.statusCode = 200 35 | res.end('response') 36 | }) 37 | 38 | server.listen(0, function () { 39 | const port = server.address().port 40 | get({ auth: 'foo:bar', host: 'localhost', port }, function (err, res) { 41 | t.error(err) 42 | t.equal(res.statusCode, 200) 43 | concat(res, function (err, data) { 44 | t.error(err) 45 | t.equal(data.toString(), 'response') 46 | server.close() 47 | }) 48 | }) 49 | }) 50 | }) 51 | 52 | test('basic auth + hostname', function (t) { 53 | t.plan(5) 54 | 55 | const server = http.createServer(function (req, res) { 56 | t.equal(req.headers.authorization, 'Basic Zm9vOmJhcg==') 57 | res.statusCode = 200 58 | res.end('response') 59 | }) 60 | 61 | server.listen(0, function () { 62 | const port = server.address().port 63 | get({ auth: 'foo:bar', hostname: 'localhost', port }, function (err, res) { 64 | t.error(err) 65 | t.equal(res.statusCode, 200) 66 | concat(res, function (err, data) { 67 | t.error(err) 68 | t.equal(data.toString(), 'response') 69 | server.close() 70 | }) 71 | }) 72 | }) 73 | }) 74 | -------------------------------------------------------------------------------- /test/basic.js: -------------------------------------------------------------------------------- 1 | const concat = require('simple-concat') 2 | const get = require('../') 3 | const http = require('http') 4 | const selfSignedHttps = require('self-signed-https') 5 | const test = require('tape') 6 | 7 | test('simple get', function (t) { 8 | t.plan(5) 9 | 10 | const server = http.createServer(function (req, res) { 11 | t.equal(req.url, '/path') 12 | res.statusCode = 200 13 | res.end('response') 14 | }) 15 | 16 | server.listen(0, function () { 17 | const port = server.address().port 18 | get('http://localhost:' + port + '/path', function (err, res) { 19 | t.error(err) 20 | t.equal(res.statusCode, 200) 21 | concat(res, function (err, data) { 22 | t.error(err) 23 | t.equal(data.toString(), 'response') 24 | server.close() 25 | }) 26 | }) 27 | }) 28 | }) 29 | 30 | test('https', function (t) { 31 | t.plan(5) 32 | 33 | const server = selfSignedHttps(function (req, res) { 34 | t.equal(req.url, '/path') 35 | res.statusCode = 200 36 | res.end('response') 37 | }) 38 | 39 | server.listen(0, function () { 40 | const port = server.address().port 41 | get({ 42 | url: 'https://localhost:' + port + '/path', 43 | rejectUnauthorized: false 44 | }, function (err, res) { 45 | t.error(err) 46 | t.equal(res.statusCode, 200) 47 | concat(res, function (err, data) { 48 | t.error(err) 49 | t.equal(data.toString(), 'response') 50 | server.close() 51 | }) 52 | }) 53 | }) 54 | }) 55 | 56 | test('simple get json', function (t) { 57 | t.plan(6) 58 | 59 | const server = http.createServer(function (req, res) { 60 | t.equal(req.url, '/path') 61 | t.equal(req.headers.accept, 'application/json') 62 | res.statusCode = 200 63 | res.end('{"message":"response"}') 64 | }) 65 | 66 | server.listen(0, function () { 67 | const port = server.address().port 68 | const opts = { 69 | url: 'http://localhost:' + port + '/path', 70 | json: true 71 | } 72 | get(opts, function (err, res) { 73 | t.error(err) 74 | t.equal(res.statusCode, 200) 75 | concat(res, function (err, data) { 76 | t.error(err) 77 | t.equal(data.toString(), '{"message":"response"}') 78 | server.close() 79 | }) 80 | }) 81 | }) 82 | }) 83 | 84 | test('HEAD request', function (t) { 85 | t.plan(3) 86 | 87 | const server = http.createServer(function (req, res) { 88 | t.equal(req.method, 'HEAD') 89 | // Taken from real-world response from HEAD request to GitHub.com 90 | res.setHeader('content-type', 'text/html; charset=utf-8') 91 | res.setHeader('content-encoding', 'gzip') 92 | res.setHeader('connection', 'close') 93 | res.statusCode = 200 94 | req.pipe(res) 95 | }) 96 | 97 | server.listen(0, function () { 98 | const port = server.address().port 99 | const opts = { 100 | method: 'HEAD', 101 | url: 'http://localhost:' + port 102 | } 103 | get.head(opts, function (err, res) { 104 | t.error(err) 105 | t.equal(res.statusCode, 200) 106 | server.close() 107 | }) 108 | }) 109 | }) 110 | 111 | test('timeout option', function (t) { 112 | t.plan(2) 113 | 114 | const server = http.createServer(function (req, res) { 115 | t.equal(req.url, '/path') 116 | setTimeout(function () { 117 | // response should not be sent - should timeout before it's sent 118 | res.end('response') 119 | }, 2000) 120 | }) 121 | 122 | server.listen(0, function () { 123 | const port = server.address().port 124 | get({ 125 | url: 'http://localhost:' + port + '/path', 126 | timeout: 1000 127 | }, function (err, res) { 128 | t.ok(err instanceof Error) 129 | server.close() 130 | }) 131 | }) 132 | }) 133 | 134 | test('rewrite POST redirects to GET', function (t) { 135 | t.plan(8) 136 | 137 | let redirected = false 138 | 139 | const server = http.createServer(function (req, res) { 140 | if (redirected) { 141 | t.equal(req.url, '/getthis') 142 | t.equal(req.method, 'GET') 143 | t.notOk(req.headers['content-length']) 144 | res.statusCode = 200 145 | req.pipe(res) 146 | } else { 147 | t.equal(req.method, 'POST') 148 | redirected = true 149 | res.statusCode = 301 150 | res.setHeader('Location', '/getthis') 151 | res.end() 152 | } 153 | }) 154 | 155 | server.listen(0, function () { 156 | const port = server.address().port 157 | const opts = { 158 | method: 'POST', 159 | body: '123', 160 | url: 'http://localhost:' + port 161 | } 162 | get(opts, function (err, res) { 163 | t.error(err) 164 | t.equal(res.statusCode, 200) 165 | concat(res, function (err, data) { 166 | t.error(err) 167 | t.equal(data.toString(), '') 168 | server.close() 169 | }) 170 | }) 171 | }) 172 | }) 173 | 174 | test('simple get hostname + url', function (t) { 175 | t.plan(5) 176 | 177 | const server = http.createServer(function (req, res) { 178 | t.equal(req.url, '/path') 179 | res.statusCode = 200 180 | res.end('response') 181 | }) 182 | 183 | server.listen(0, function () { 184 | const port = server.address().port 185 | get({ host: 'localhost', port, url: '/path' }, function (err, res) { 186 | t.error(err) 187 | t.equal(res.statusCode, 200) 188 | concat(res, function (err, data) { 189 | t.error(err) 190 | t.equal(data.toString(), 'response') 191 | server.close() 192 | }) 193 | }) 194 | }) 195 | }) 196 | -------------------------------------------------------------------------------- /test/concat.js: -------------------------------------------------------------------------------- 1 | const get = require('../') 2 | const http = require('http') 3 | const str = require('string-to-stream') 4 | const test = require('tape') 5 | 6 | test('get.concat (post, stream body, and json option)', function (t) { 7 | t.plan(4) 8 | 9 | const server = http.createServer(function (req, res) { 10 | res.statusCode = 200 11 | req.pipe(res) 12 | }) 13 | 14 | server.listen(0, function () { 15 | const port = server.address().port 16 | const opts = { 17 | url: 'http://localhost:' + port, 18 | body: str('{"a": "b"}'), 19 | method: 'POST', 20 | json: true 21 | } 22 | get.concat(opts, function (err, res, data) { 23 | t.error(err) 24 | t.equal(typeof data, 'object') 25 | t.deepEqual(Object.keys(data), ['a']) 26 | t.equal(data.a, 'b') 27 | server.close() 28 | }) 29 | }) 30 | }) 31 | 32 | test('get.concat', function (t) { 33 | t.plan(4) 34 | const server = http.createServer(function (req, res) { 35 | res.statusCode = 200 36 | res.end('blah blah blah') 37 | }) 38 | 39 | server.listen(0, function () { 40 | const port = server.address().port 41 | get.concat('http://localhost:' + port, function (err, res, data) { 42 | t.error(err) 43 | t.equal(res.statusCode, 200) 44 | t.ok(Buffer.isBuffer(data), '`data` is type buffer') 45 | t.equal(data.toString(), 'blah blah blah') 46 | server.close() 47 | }) 48 | }) 49 | }) 50 | 51 | test('get.concat json', function (t) { 52 | t.plan(3) 53 | const server = http.createServer(function (req, res) { 54 | res.statusCode = 200 55 | res.end('{"message":"response"}') 56 | }) 57 | 58 | server.listen(0, function () { 59 | const port = server.address().port 60 | const opts = { 61 | url: 'http://localhost:' + port + '/path', 62 | json: true 63 | } 64 | get.concat(opts, function (err, res, data) { 65 | t.error(err) 66 | t.equal(res.statusCode, 200) 67 | t.equal(data.message, 'response') 68 | server.close() 69 | }) 70 | }) 71 | }) 72 | 73 | test('get.concat json error', function (t) { 74 | t.plan(1) 75 | const server = http.createServer(function (req, res) { 76 | res.statusCode = 500 77 | res.end('not json') 78 | }) 79 | 80 | server.listen(0, function () { 81 | const port = server.address().port 82 | const opts = { 83 | url: 'http://localhost:' + port + '/path', 84 | json: true 85 | } 86 | get.concat(opts, function (err, res, data) { 87 | t.ok(err instanceof Error) 88 | server.close() 89 | }) 90 | }) 91 | }) 92 | -------------------------------------------------------------------------------- /test/headers.js: -------------------------------------------------------------------------------- 1 | const concat = require('simple-concat') 2 | const get = require('../') 3 | const http = require('http') 4 | const str = require('string-to-stream') 5 | const test = require('tape') 6 | const zlib = require('zlib') 7 | 8 | test('custom headers', function (t) { 9 | t.plan(2) 10 | 11 | const server = http.createServer(function (req, res) { 12 | t.equal(req.headers['custom-header'], 'custom-value') 13 | res.statusCode = 200 14 | res.end('response') 15 | }) 16 | 17 | server.listen(0, function () { 18 | const port = server.address().port 19 | get({ 20 | url: 'http://localhost:' + port, 21 | headers: { 22 | 'custom-header': 'custom-value' 23 | } 24 | }, function (err, res) { 25 | t.error(err) 26 | res.resume() 27 | server.close() 28 | }) 29 | }) 30 | }) 31 | 32 | test('gzip response', function (t) { 33 | t.plan(4) 34 | 35 | const server = http.createServer(function (req, res) { 36 | res.statusCode = 200 37 | res.setHeader('content-encoding', 'gzip') 38 | str('response').pipe(zlib.createGzip()).pipe(res) 39 | }) 40 | 41 | server.listen(0, function () { 42 | const port = server.address().port 43 | get('http://localhost:' + port, function (err, res) { 44 | t.error(err) 45 | t.equal(res.statusCode, 200) // statusCode still works on gunzip stream 46 | concat(res, function (err, data) { 47 | t.error(err) 48 | t.equal(data.toString(), 'response') 49 | server.close() 50 | }) 51 | }) 52 | }) 53 | }) 54 | 55 | test('deflate response', function (t) { 56 | t.plan(4) 57 | 58 | const server = http.createServer(function (req, res) { 59 | res.statusCode = 200 60 | res.setHeader('content-encoding', 'deflate') 61 | str('response').pipe(zlib.createDeflate()).pipe(res) 62 | }) 63 | 64 | server.listen(0, function () { 65 | const port = server.address().port 66 | get('http://localhost:' + port, function (err, res) { 67 | t.error(err) 68 | t.equal(res.statusCode, 200) // statusCode still works on inflate stream 69 | concat(res, function (err, data) { 70 | t.error(err) 71 | t.equal(data.toString(), 'response') 72 | server.close() 73 | }) 74 | }) 75 | }) 76 | }) 77 | -------------------------------------------------------------------------------- /test/post.js: -------------------------------------------------------------------------------- 1 | const concat = require('simple-concat') 2 | const get = require('../') 3 | const http = require('http') 4 | const querystring = require('querystring') 5 | const str = require('string-to-stream') 6 | const test = require('tape') 7 | 8 | test('post (text body)', function (t) { 9 | t.plan(5) 10 | 11 | const server = http.createServer(function (req, res) { 12 | t.equal(req.method, 'POST') 13 | res.statusCode = 200 14 | req.pipe(res) 15 | }) 16 | 17 | server.listen(0, function () { 18 | const port = server.address().port 19 | const opts = { 20 | url: 'http://localhost:' + port, 21 | body: 'this is the body' 22 | } 23 | get.post(opts, function (err, res) { 24 | t.error(err) 25 | t.equal(res.statusCode, 200) 26 | concat(res, function (err, data) { 27 | t.error(err) 28 | t.equal(data.toString(), 'this is the body') 29 | server.close() 30 | }) 31 | }) 32 | }) 33 | }) 34 | 35 | test('post (utf-8 text body)', function (t) { 36 | t.plan(5) 37 | 38 | const server = http.createServer(function (req, res) { 39 | t.equal(req.method, 'POST') 40 | res.statusCode = 200 41 | req.pipe(res) 42 | }) 43 | 44 | server.listen(0, function () { 45 | const port = server.address().port 46 | const opts = { 47 | url: 'http://localhost:' + port, 48 | body: 'jedan dva tri četiri' 49 | } 50 | get.post(opts, function (err, res) { 51 | t.error(err) 52 | t.equal(res.statusCode, 200) 53 | concat(res, function (err, data) { 54 | t.error(err) 55 | t.equal(data.toString(), 'jedan dva tri četiri') 56 | server.close() 57 | }) 58 | }) 59 | }) 60 | }) 61 | 62 | test('post (buffer body)', function (t) { 63 | t.plan(5) 64 | 65 | const server = http.createServer(function (req, res) { 66 | t.equal(req.method, 'POST') 67 | res.statusCode = 200 68 | req.pipe(res) 69 | }) 70 | 71 | server.listen(0, function () { 72 | const port = server.address().port 73 | const opts = { 74 | url: 'http://localhost:' + port, 75 | body: Buffer.from('this is the body') 76 | } 77 | get.post(opts, function (err, res) { 78 | t.error(err) 79 | t.equal(res.statusCode, 200) 80 | concat(res, function (err, data) { 81 | t.error(err) 82 | t.equal(data.toString(), 'this is the body') 83 | server.close() 84 | }) 85 | }) 86 | }) 87 | }) 88 | 89 | test('post (stream body)', function (t) { 90 | t.plan(6) 91 | 92 | const server = http.createServer(function (req, res) { 93 | t.equal(req.method, 'POST') 94 | res.statusCode = 200 95 | t.notOk(req.headers['content-length']) 96 | req.pipe(res) 97 | }) 98 | 99 | server.listen(0, function () { 100 | const port = server.address().port 101 | const opts = { 102 | url: 'http://localhost:' + port, 103 | body: str('this is the body') 104 | } 105 | get.post(opts, function (err, res) { 106 | t.error(err) 107 | t.equal(res.statusCode, 200) 108 | concat(res, function (err, data) { 109 | t.error(err) 110 | t.equal(data.toString(), 'this is the body') 111 | server.close() 112 | }) 113 | }) 114 | }) 115 | }) 116 | 117 | test('post (json body)', function (t) { 118 | t.plan(5) 119 | 120 | const server = http.createServer(function (req, res) { 121 | t.equal(req.method, 'POST') 122 | t.equal(req.headers['content-type'], 'application/json') 123 | res.statusCode = 200 124 | req.pipe(res) 125 | }) 126 | 127 | server.listen(0, function () { 128 | const port = server.address().port 129 | const opts = { 130 | method: 'POST', 131 | url: 'http://localhost:' + port, 132 | body: { 133 | message: 'this is the body' 134 | }, 135 | json: true 136 | } 137 | get.concat(opts, function (err, res, data) { 138 | t.error(err) 139 | t.equal(res.statusCode, 200) 140 | t.equal(data.message, 'this is the body') 141 | server.close() 142 | }) 143 | }) 144 | }) 145 | 146 | test('post (form, object)', function (t) { 147 | t.plan(5) 148 | 149 | const formData = Object.create(null) 150 | formData.foo = 'bar' 151 | 152 | const server = http.createServer(function (req, res) { 153 | t.equal(req.method, 'POST') 154 | t.equal(req.headers['content-type'], 'application/x-www-form-urlencoded') 155 | res.statusCode = 200 156 | req.pipe(res) 157 | }) 158 | 159 | server.listen(0, function () { 160 | const port = server.address().port 161 | const opts = { 162 | method: 'POST', 163 | url: 'http://localhost:' + port, 164 | form: formData 165 | } 166 | get.concat(opts, function (err, res, data) { 167 | t.error(err) 168 | t.equal(res.statusCode, 200) 169 | t.deepEqual(querystring.parse(data.toString()), formData) 170 | server.close() 171 | }) 172 | }) 173 | }) 174 | 175 | test('post (form, querystring)', function (t) { 176 | t.plan(5) 177 | 178 | const formData = 'foo=bar' 179 | 180 | const server = http.createServer(function (req, res) { 181 | t.equal(req.method, 'POST') 182 | t.equal(req.headers['content-type'], 'application/x-www-form-urlencoded') 183 | res.statusCode = 200 184 | req.pipe(res) 185 | }) 186 | 187 | server.listen(0, function () { 188 | const port = server.address().port 189 | const opts = { 190 | method: 'POST', 191 | url: 'http://localhost:' + port, 192 | form: formData 193 | } 194 | get.concat(opts, function (err, res, data) { 195 | t.error(err) 196 | t.equal(res.statusCode, 200) 197 | t.equal(data.toString(), formData) 198 | server.close() 199 | }) 200 | }) 201 | }) 202 | -------------------------------------------------------------------------------- /test/redirect.js: -------------------------------------------------------------------------------- 1 | const concat = require('simple-concat') 2 | const get = require('../') 3 | const http = require('http') 4 | const selfSignedHttps = require('self-signed-https') 5 | const test = require('tape') 6 | 7 | test('follow redirects (up to 10)', function (t) { 8 | t.plan(15) 9 | 10 | let num = 0 11 | const server = http.createServer(function (req, res) { 12 | t.equal(req.url, '/' + num, 'visited /' + num) 13 | 14 | if (num < 10) { 15 | num += 1 16 | res.statusCode = 301 17 | res.setHeader('Location', '/' + num) 18 | res.end() 19 | } else { 20 | res.statusCode = 200 21 | res.end('response') 22 | } 23 | }) 24 | 25 | server.listen(0, function () { 26 | const port = server.address().port 27 | get('http://localhost:' + port + '/0', function (err, res) { 28 | t.error(err) 29 | t.equal(res.statusCode, 200) 30 | concat(res, function (err, data) { 31 | t.error(err) 32 | t.equal(data.toString(), 'response') 33 | server.close() 34 | }) 35 | }) 36 | }) 37 | }) 38 | 39 | test('do not follow redirects', function (t) { 40 | t.plan(2) 41 | 42 | const server = http.createServer(function (req, res) { 43 | t.equal(req.url, '/0', 'visited /0') 44 | 45 | res.statusCode = 301 46 | res.setHeader('Location', '/1') 47 | res.end() 48 | }) 49 | 50 | server.listen(0, function () { 51 | const port = server.address().port 52 | get({ 53 | url: 'http://localhost:' + port + '/0', 54 | maxRedirects: 0 55 | }, function (err) { 56 | t.ok(err instanceof Error, 'got error') 57 | server.close() 58 | }) 59 | }) 60 | }) 61 | 62 | test('do not follow redirects and do not error', function (t) { 63 | t.plan(4) 64 | 65 | const server = http.createServer(function (req, res) { 66 | t.equal(req.url, '/0', 'visited /0') 67 | 68 | res.statusCode = 301 69 | res.setHeader('Location', '/1') 70 | res.end() 71 | }) 72 | 73 | server.listen(0, function () { 74 | const port = server.address().port 75 | get({ 76 | url: 'http://localhost:' + port + '/0', 77 | followRedirects: false 78 | }, function (err, res) { 79 | t.ok(!err, 'got no error') 80 | t.equal(res.statusCode, 301, 'status code 301') 81 | t.equal(res.headers.location, '/1', 'redirect location') 82 | server.close() 83 | }) 84 | }) 85 | }) 86 | 87 | test('follow redirects (11 is too many)', function (t) { 88 | t.plan(12) 89 | 90 | let num = 0 91 | const server = http.createServer(function (req, res) { 92 | t.equal(req.url, '/' + num, 'visited /' + num) 93 | 94 | if (num < 11) { 95 | num += 1 96 | res.statusCode = 301 97 | res.setHeader('Location', '/' + num) 98 | res.end() 99 | } else { 100 | t.fail('no request to /11 should be made, should error first') 101 | } 102 | }) 103 | 104 | server.listen(0, function () { 105 | const port = server.address().port 106 | get('http://localhost:' + port + '/0', function (err) { 107 | t.ok(err instanceof Error, 'got error') 108 | server.close() 109 | }) 110 | }) 111 | }) 112 | 113 | test('redirect https to http', function (t) { 114 | t.plan(6) 115 | 116 | let httpPort = null 117 | let httpsPort = null 118 | 119 | const httpsServer = selfSignedHttps(function (req, res) { 120 | t.equal(req.url, '/path1') 121 | res.statusCode = 301 122 | res.setHeader('Location', 'http://localhost:' + httpPort + '/path2') 123 | res.end() 124 | }) 125 | 126 | const httpServer = http.createServer(function (req, res) { 127 | t.equal(req.url, '/path2') 128 | res.statusCode = 200 129 | res.end('response') 130 | }) 131 | 132 | httpsServer.listen(0, function () { 133 | httpsPort = httpsServer.address().port 134 | httpServer.listen(0, function () { 135 | httpPort = httpServer.address().port 136 | get({ 137 | url: 'https://localhost:' + httpsPort + '/path1', 138 | rejectUnauthorized: false 139 | }, function (err, res) { 140 | t.error(err) 141 | t.equal(res.statusCode, 200) 142 | concat(res, function (err, data) { 143 | t.error(err) 144 | t.equal(data.toString(), 'response') 145 | httpsServer.close() 146 | httpServer.close() 147 | }) 148 | }) 149 | }) 150 | }) 151 | }) 152 | 153 | test('redirect http to https', function (t) { 154 | t.plan(6) 155 | 156 | let httpsPort = null 157 | let httpPort = null 158 | 159 | const httpServer = http.createServer(function (req, res) { 160 | t.equal(req.url, '/path1') 161 | res.statusCode = 301 162 | res.setHeader('Location', 'https://localhost:' + httpsPort + '/path2') 163 | res.end() 164 | }) 165 | 166 | const httpsServer = selfSignedHttps(function (req, res) { 167 | t.equal(req.url, '/path2') 168 | res.statusCode = 200 169 | res.end('response') 170 | }) 171 | 172 | httpServer.listen(0, function () { 173 | httpPort = httpServer.address().port 174 | httpsServer.listen(0, function () { 175 | httpsPort = httpsServer.address().port 176 | get({ 177 | url: 'http://localhost:' + httpPort + '/path1', 178 | rejectUnauthorized: false 179 | }, function (err, res) { 180 | t.error(err) 181 | t.equal(res.statusCode, 200) 182 | concat(res, function (err, data) { 183 | t.error(err) 184 | t.equal(data.toString(), 'response') 185 | httpsServer.close() 186 | httpServer.close() 187 | }) 188 | }) 189 | }) 190 | }) 191 | }) 192 | 193 | test('redirect to different host/port', function (t) { 194 | t.plan(7) 195 | 196 | let port1 = null 197 | let port2 = null 198 | 199 | const server1 = http.createServer(function (req, res) { 200 | t.equal(req.url, '/path1') 201 | res.statusCode = 301 202 | // Redirect from localhost:port1 to 127.0.0.1:port2 (different host and port!) 203 | res.setHeader('Location', 'http://127.0.0.1:' + port2 + '/path2') 204 | res.end() 205 | }) 206 | 207 | const server2 = http.createServer(function (req, res) { 208 | t.equal(req.url, '/path2') 209 | // Confirm that request was made with new host and port (127.0.0.1:port2) 210 | t.equal(req.headers.host, `127.0.0.1:${port2}`) 211 | res.statusCode = 200 212 | res.end('response') 213 | }) 214 | 215 | server1.listen(0, function () { 216 | port1 = server1.address().port 217 | server2.listen(0, function () { 218 | port2 = server2.address().port 219 | get('http://localhost:' + port1 + '/path1', function (err, res) { 220 | t.error(err) 221 | t.equal(res.statusCode, 200) 222 | concat(res, function (err, data) { 223 | t.error(err) 224 | t.equal(data.toString(), 'response') 225 | server1.close() 226 | server2.close() 227 | }) 228 | }) 229 | }) 230 | }) 231 | }) 232 | 233 | // See https://github.com/feross/simple-get/issues/32 234 | test('redirect should clear explicitly specified `host` header', function (t) { 235 | t.plan(8) 236 | 237 | let port1 = null 238 | let port2 = null 239 | 240 | const server1 = http.createServer(function (req, res) { 241 | t.equal(req.url, '/path1') 242 | t.equal(req.headers.host, `localhost:${port1}`) 243 | res.statusCode = 301 244 | // Redirect from localhost:port1 to 127.0.0.1:port2 (different host and port!) 245 | res.setHeader('Location', 'http://127.0.0.1:' + port2 + '/path2') 246 | res.end() 247 | }) 248 | 249 | const server2 = http.createServer(function (req, res) { 250 | t.equal(req.url, '/path2') 251 | // Confirm that request was made with new host and port (127.0.0.1:port2), i.e. 252 | // that the explicitly specified `Host` header was cleared upon redirect. 253 | t.equal(req.headers.host, `127.0.0.1:${port2}`) 254 | res.statusCode = 200 255 | res.end('response') 256 | }) 257 | 258 | server1.listen(0, function () { 259 | port1 = server1.address().port 260 | server2.listen(0, function () { 261 | port2 = server2.address().port 262 | get({ 263 | url: `http://localhost:${port1}/path1`, 264 | // Explicitly specify a `Host` header, so it won't be set automatically 265 | headers: { 266 | host: `localhost:${port1}` 267 | } 268 | }, function (err, res) { 269 | t.error(err) 270 | t.equal(res.statusCode, 200) 271 | concat(res, function (err, data) { 272 | t.error(err) 273 | t.equal(data.toString(), 'response') 274 | server1.close() 275 | server2.close() 276 | }) 277 | }) 278 | }) 279 | }) 280 | }) 281 | 282 | test('redirect should clear explicitly specified `Host` (note uppercase) header', function (t) { 283 | t.plan(8) 284 | 285 | let port1 = null 286 | let port2 = null 287 | 288 | const server1 = http.createServer(function (req, res) { 289 | t.equal(req.url, '/path1') 290 | t.equal(req.headers.host, `localhost:${port1}`) 291 | res.statusCode = 301 292 | // Redirect from localhost:port1 to 127.0.0.1:port2 (different host and port!) 293 | res.setHeader('Location', 'http://127.0.0.1:' + port2 + '/path2') 294 | res.end() 295 | }) 296 | 297 | const server2 = http.createServer(function (req, res) { 298 | t.equal(req.url, '/path2') 299 | // Confirm that request was made with new host and port (127.0.0.1:port2), i.e. 300 | // that the explicitly specified `Host` header was cleared upon redirect. 301 | t.equal(req.headers.host, `127.0.0.1:${port2}`) 302 | res.statusCode = 200 303 | res.end('response') 304 | }) 305 | 306 | server1.listen(0, function () { 307 | port1 = server1.address().port 308 | server2.listen(0, function () { 309 | port2 = server2.address().port 310 | get({ 311 | url: `http://localhost:${port1}/path1`, 312 | // Explicitly specify a `Host` header, so it won't be set automatically 313 | headers: { 314 | Host: `localhost:${port1}` 315 | } 316 | }, function (err, res) { 317 | t.error(err) 318 | t.equal(res.statusCode, 200) 319 | concat(res, function (err, data) { 320 | t.error(err) 321 | t.equal(data.toString(), 'response') 322 | server1.close() 323 | server2.close() 324 | }) 325 | }) 326 | }) 327 | }) 328 | }) 329 | 330 | test('follow redirects without "url" option', function (t) { 331 | t.plan(15) 332 | 333 | let num = 0 334 | const server = http.createServer(function (req, res) { 335 | t.equal(req.url, '/' + num, 'visited /' + num) 336 | 337 | if (num < 10) { 338 | num += 1 339 | res.statusCode = 301 340 | res.setHeader('Location', '/' + num) 341 | res.end() 342 | } else { 343 | res.statusCode = 200 344 | res.end('response') 345 | } 346 | }) 347 | 348 | server.listen(0, function () { 349 | const port = server.address().port 350 | get({ hostname: 'localhost', port, path: '/0' }, function (err, res) { 351 | t.error(err) 352 | t.equal(res.statusCode, 200) 353 | concat(res, function (err, data) { 354 | t.error(err) 355 | t.equal(data.toString(), 'response') 356 | server.close() 357 | }) 358 | }) 359 | }) 360 | }) 361 | -------------------------------------------------------------------------------- /test/request.js: -------------------------------------------------------------------------------- 1 | const get = require('../') 2 | const http = require('http') 3 | const test = require('tape') 4 | 5 | test('access `req` object', function (t) { 6 | t.plan(2) 7 | 8 | const server = http.createServer(function (req, res) { 9 | res.statusCode = 200 10 | res.end('response') 11 | }) 12 | 13 | server.listen(0, function () { 14 | const port = server.address().port 15 | const req = get('http://localhost:' + port, function (err, res) { 16 | t.error(err) 17 | res.resume() // discard data 18 | server.close() 19 | }) 20 | 21 | req.on('socket', function () { 22 | t.pass('got `socket` event') 23 | }) 24 | }) 25 | }) 26 | --------------------------------------------------------------------------------