pax_global_header00006660000000000000000000000064133333363350014517gustar00rootroot0000000000000052 comment=8162961dfdb73dc35a5a4bfeefb858c2ed2ccbb7 request-2.88.1/000077500000000000000000000000001333333633500133075ustar00rootroot00000000000000request-2.88.1/.github/000077500000000000000000000000001333333633500146475ustar00rootroot00000000000000request-2.88.1/.github/ISSUE_TEMPLATE.md000066400000000000000000000042201333333633500173520ustar00rootroot00000000000000 ### Summary ### Simplest Example to Reproduce ```js request({ method: 'GET', url: 'http://example.com', // a public URL that we can hit to reproduce, if possible more: { 'options': 'here' } }, ``` ### Expected Behavior ### Current Behavior ### Possible Solution ### Context ### Your Environment | software | version | ---------------- | ------- | request | | node | | npm | | Operating System | request-2.88.1/.github/PULL_REQUEST_TEMPLATE.md000066400000000000000000000012461333333633500204530ustar00rootroot00000000000000## PR Checklist: - [ ] I have run `npm test` locally and all tests are passing. - [ ] I have added/updated tests for any new behavior. - [ ] If this is a significant change, an issue has already been created where the problem / solution was discussed: [N/A, or add link to issue here] ## PR Description request-2.88.1/.gitignore000066400000000000000000000000741333333633500153000ustar00rootroot00000000000000node_modules coverage .idea npm-debug.log package-lock.json request-2.88.1/.travis.yml000066400000000000000000000006371333333633500154260ustar00rootroot00000000000000 language: node_js node_js: - node - 10 - 8 - 6 - 4 after_script: - npm run test-cov - codecov - cat ./coverage/lcov.info | coveralls webhooks: urls: https://webhooks.gitter.im/e/237280ed4796c19cc626 on_success: change # options: [always|never|change] default: always on_failure: always # options: [always|never|change] default: always on_start: false # default: false sudo: false request-2.88.1/CHANGELOG.md000066400000000000000000002055021333333633500151240ustar00rootroot00000000000000## Change Log ### v2.87.0 (2018/05/21) - [#2943](https://github.com/request/request/pull/2943) Replace hawk dependency with a local implemenation (#2943) (@hueniverse) ### v2.86.0 (2018/05/15) - [#2885](https://github.com/request/request/pull/2885) Remove redundant code (for Node.js 0.9.4 and below) and dependency (@ChALkeR) - [#2942](https://github.com/request/request/pull/2942) Make Test GREEN Again! (@simov) - [#2923](https://github.com/request/request/pull/2923) Alterations for failing CI tests (@gareth-robinson) ### v2.85.0 (2018/03/12) - [#2880](https://github.com/request/request/pull/2880) Revert "Update hawk to 7.0.7 (#2880)" (@simov) ### v2.84.0 (2018/03/12) - [#2793](https://github.com/request/request/pull/2793) Fixed calculation of oauth_body_hash, issue #2792 (@dvishniakov) - [#2880](https://github.com/request/request/pull/2880) Update hawk to 7.0.7 (#2880) (@kornel-kedzierski) ### v2.83.0 (2017/09/27) - [#2776](https://github.com/request/request/pull/2776) Updating tough-cookie due to security fix. (#2776) (@karlnorling) ### v2.82.0 (2017/09/19) - [#2703](https://github.com/request/request/pull/2703) Add Node.js v8 to Travis CI (@ryysud) - [#2751](https://github.com/request/request/pull/2751) Update of hawk and qs to latest version (#2751) (@Olivier-Moreau) - [#2658](https://github.com/request/request/pull/2658) Fixed some text in README.md (#2658) (@Marketionist) - [#2635](https://github.com/request/request/pull/2635) chore(package): update aws-sign2 to version 0.7.0 (#2635) (@greenkeeperio-bot) - [#2641](https://github.com/request/request/pull/2641) Update README to simplify & update convenience methods (#2641) (@FredKSchott) - [#2541](https://github.com/request/request/pull/2541) Add convenience method for HTTP OPTIONS (#2541) (@jamesseanwright) - [#2605](https://github.com/request/request/pull/2605) Add promise support section to README (#2605) (@FredKSchott) - [#2579](https://github.com/request/request/pull/2579) refactor(lint): replace eslint with standard (#2579) (@ahmadnassri) - [#2598](https://github.com/request/request/pull/2598) Update codecov to version 2.0.2 🚀 (@greenkeeperio-bot) - [#2590](https://github.com/request/request/pull/2590) Adds test-timing keepAlive test (@nicjansma) - [#2589](https://github.com/request/request/pull/2589) fix tabulation on request example README.MD (@odykyi) - [#2594](https://github.com/request/request/pull/2594) chore(dependencies): har-validator to 5.x [removes babel dep] (@ahmadnassri) ### v2.81.0 (2017/03/09) - [#2584](https://github.com/request/request/pull/2584) Security issue: Upgrade qs to version 6.4.0 (@sergejmueller) - [#2578](https://github.com/request/request/pull/2578) safe-buffer doesn't zero-fill by default, its just a polyfill. (#2578) (@mikeal) - [#2566](https://github.com/request/request/pull/2566) Timings: Tracks 'lookup', adds 'wait' time, fixes connection re-use (#2566) (@nicjansma) - [#2574](https://github.com/request/request/pull/2574) Migrating to safe-buffer for improved security. (@mikeal) - [#2573](https://github.com/request/request/pull/2573) fixes #2572 (@ahmadnassri) ### v2.80.0 (2017/03/04) - [#2571](https://github.com/request/request/pull/2571) Correctly format the Host header for IPv6 addresses (@JamesMGreene) - [#2558](https://github.com/request/request/pull/2558) Update README.md example snippet (@FredKSchott) - [#2221](https://github.com/request/request/pull/2221) Adding a simple Response object reference in argument specification (@calamarico) - [#2452](https://github.com/request/request/pull/2452) Adds .timings array with DNC, TCP, request and response times (@nicjansma) - [#2553](https://github.com/request/request/pull/2553) add ISSUE_TEMPLATE, move PR template (@FredKSchott) - [#2539](https://github.com/request/request/pull/2539) Create PULL_REQUEST_TEMPLATE.md (@FredKSchott) - [#2524](https://github.com/request/request/pull/2524) Update caseless to version 0.12.0 🚀 (@greenkeeperio-bot) - [#2460](https://github.com/request/request/pull/2460) Fix wrong MIME type in example (@OwnageIsMagic) - [#2514](https://github.com/request/request/pull/2514) Change tags to keywords in package.json (@humphd) - [#2492](https://github.com/request/request/pull/2492) More lenient gzip decompression (@addaleax) ### v2.79.0 (2016/11/18) - [#2368](https://github.com/request/request/pull/2368) Fix typeof check in test-pool.js (@forivall) - [#2394](https://github.com/request/request/pull/2394) Use `files` in package.json (@SimenB) - [#2463](https://github.com/request/request/pull/2463) AWS support for session tokens for temporary credentials (@simov) - [#2467](https://github.com/request/request/pull/2467) Migrate to uuid (@simov, @antialias) - [#2459](https://github.com/request/request/pull/2459) Update taper to version 0.5.0 🚀 (@greenkeeperio-bot) - [#2448](https://github.com/request/request/pull/2448) Make other connect timeout test more reliable too (@mscdex) ### v2.78.0 (2016/11/03) - [#2447](https://github.com/request/request/pull/2447) Always set request timeout on keep-alive connections (@mscdex) ### v2.77.0 (2016/11/03) - [#2439](https://github.com/request/request/pull/2439) Fix socket 'connect' listener handling (@mscdex) - [#2442](https://github.com/request/request/pull/2442) 👻😱 Node.js 0.10 is unmaintained 😱👻 (@greenkeeperio-bot) - [#2435](https://github.com/request/request/pull/2435) Add followOriginalHttpMethod to redirect to original HTTP method (@kirrg001) - [#2414](https://github.com/request/request/pull/2414) Improve test-timeout reliability (@mscdex) ### v2.76.0 (2016/10/25) - [#2424](https://github.com/request/request/pull/2424) Handle buffers directly instead of using "bl" (@zertosh) - [#2415](https://github.com/request/request/pull/2415) Re-enable timeout tests on Travis + other fixes (@mscdex) - [#2431](https://github.com/request/request/pull/2431) Improve timeouts accuracy and node v6.8.0+ compatibility (@mscdex, @greenkeeperio-bot) - [#2428](https://github.com/request/request/pull/2428) Update qs to version 6.3.0 🚀 (@greenkeeperio-bot) - [#2420](https://github.com/request/request/pull/2420) change .on to .once, remove possible memory leaks (@duereg) - [#2426](https://github.com/request/request/pull/2426) Remove "isFunction" helper in favor of "typeof" check (@zertosh) - [#2425](https://github.com/request/request/pull/2425) Simplify "defer" helper creation (@zertosh) - [#2402](https://github.com/request/request/pull/2402) form-data@2.1.1 breaks build 🚨 (@greenkeeperio-bot) - [#2393](https://github.com/request/request/pull/2393) Update form-data to version 2.1.0 🚀 (@greenkeeperio-bot) ### v2.75.0 (2016/09/17) - [#2381](https://github.com/request/request/pull/2381) Drop support for Node 0.10 (@simov) - [#2377](https://github.com/request/request/pull/2377) Update form-data to version 2.0.0 🚀 (@greenkeeperio-bot) - [#2353](https://github.com/request/request/pull/2353) Add greenkeeper ignored packages (@simov) - [#2351](https://github.com/request/request/pull/2351) Update karma-tap to version 3.0.1 🚀 (@greenkeeperio-bot) - [#2348](https://github.com/request/request/pull/2348) form-data@1.0.1 breaks build 🚨 (@greenkeeperio-bot) - [#2349](https://github.com/request/request/pull/2349) Check error type instead of string (@scotttrinh) ### v2.74.0 (2016/07/22) - [#2295](https://github.com/request/request/pull/2295) Update tough-cookie to 2.3.0 (@stash-sfdc) - [#2280](https://github.com/request/request/pull/2280) Update karma-tap to version 2.0.1 🚀 (@greenkeeperio-bot) ### v2.73.0 (2016/07/09) - [#2240](https://github.com/request/request/pull/2240) Remove connectionErrorHandler to fix #1903 (@zarenner) - [#2251](https://github.com/request/request/pull/2251) tape@4.6.0 breaks build 🚨 (@greenkeeperio-bot) - [#2225](https://github.com/request/request/pull/2225) Update docs (@ArtskydJ) - [#2203](https://github.com/request/request/pull/2203) Update browserify to version 13.0.1 🚀 (@greenkeeperio-bot) - [#2275](https://github.com/request/request/pull/2275) Update karma to version 1.1.1 🚀 (@greenkeeperio-bot) - [#2204](https://github.com/request/request/pull/2204) Add codecov.yml and disable PR comments (@simov) - [#2212](https://github.com/request/request/pull/2212) Fix link to http.IncomingMessage documentation (@nazieb) - [#2208](https://github.com/request/request/pull/2208) Update to form-data RC4 and pass null values to it (@simov) - [#2207](https://github.com/request/request/pull/2207) Move aws4 require statement to the top (@simov) - [#2199](https://github.com/request/request/pull/2199) Update karma-coverage to version 1.0.0 🚀 (@greenkeeperio-bot) - [#2206](https://github.com/request/request/pull/2206) Update qs to version 6.2.0 🚀 (@greenkeeperio-bot) - [#2205](https://github.com/request/request/pull/2205) Use server-destory to close hanging sockets in tests (@simov) - [#2200](https://github.com/request/request/pull/2200) Update karma-cli to version 1.0.0 🚀 (@greenkeeperio-bot) ### v2.72.0 (2016/04/17) - [#2176](https://github.com/request/request/pull/2176) Do not try to pipe Gzip responses with no body (@simov) - [#2175](https://github.com/request/request/pull/2175) Add 'delete' alias for the 'del' API method (@simov, @MuhanZou) - [#2172](https://github.com/request/request/pull/2172) Add support for deflate content encoding (@czardoz) - [#2169](https://github.com/request/request/pull/2169) Add callback option (@simov) - [#2165](https://github.com/request/request/pull/2165) Check for self.req existence inside the write method (@simov) - [#2167](https://github.com/request/request/pull/2167) Fix TravisCI badge reference master branch (@a0viedo) ### v2.71.0 (2016/04/12) - [#2164](https://github.com/request/request/pull/2164) Catch errors from the underlying http module (@simov) ### v2.70.0 (2016/04/05) - [#2147](https://github.com/request/request/pull/2147) Update eslint to version 2.5.3 🚀 (@simov, @greenkeeperio-bot) - [#2009](https://github.com/request/request/pull/2009) Support JSON stringify replacer argument. (@elyobo) - [#2142](https://github.com/request/request/pull/2142) Update eslint to version 2.5.1 🚀 (@greenkeeperio-bot) - [#2128](https://github.com/request/request/pull/2128) Update browserify-istanbul to version 2.0.0 🚀 (@greenkeeperio-bot) - [#2115](https://github.com/request/request/pull/2115) Update eslint to version 2.3.0 🚀 (@simov, @greenkeeperio-bot) - [#2089](https://github.com/request/request/pull/2089) Fix badges (@simov) - [#2092](https://github.com/request/request/pull/2092) Update browserify-istanbul to version 1.0.0 🚀 (@greenkeeperio-bot) - [#2079](https://github.com/request/request/pull/2079) Accept read stream as body option (@simov) - [#2070](https://github.com/request/request/pull/2070) Update bl to version 1.1.2 🚀 (@greenkeeperio-bot) - [#2063](https://github.com/request/request/pull/2063) Up bluebird and oauth-sign (@simov) - [#2058](https://github.com/request/request/pull/2058) Karma fixes for latest versions (@eiriksm) - [#2057](https://github.com/request/request/pull/2057) Update contributing guidelines (@simov) - [#2054](https://github.com/request/request/pull/2054) Update qs to version 6.1.0 🚀 (@greenkeeperio-bot) ### v2.69.0 (2016/01/27) - [#2041](https://github.com/request/request/pull/2041) restore aws4 as regular dependency (@rmg) ### v2.68.0 (2016/01/27) - [#2036](https://github.com/request/request/pull/2036) Add AWS Signature Version 4 (@simov, @mirkods) - [#2022](https://github.com/request/request/pull/2022) Convert numeric multipart bodies to string (@simov, @feross) - [#2024](https://github.com/request/request/pull/2024) Update har-validator dependency for nsp advisory #76 (@TylerDixon) - [#2016](https://github.com/request/request/pull/2016) Update qs to version 6.0.2 🚀 (@greenkeeperio-bot) - [#2007](https://github.com/request/request/pull/2007) Use the `extend` module instead of util._extend (@simov) - [#2003](https://github.com/request/request/pull/2003) Update browserify to version 13.0.0 🚀 (@greenkeeperio-bot) - [#1989](https://github.com/request/request/pull/1989) Update buffer-equal to version 1.0.0 🚀 (@greenkeeperio-bot) - [#1956](https://github.com/request/request/pull/1956) Check form-data content-length value before setting up the header (@jongyoonlee) - [#1958](https://github.com/request/request/pull/1958) Use IncomingMessage.destroy method (@simov) - [#1952](https://github.com/request/request/pull/1952) Adds example for Tor proxy (@prometheansacrifice) - [#1943](https://github.com/request/request/pull/1943) Update eslint to version 1.10.3 🚀 (@simov, @greenkeeperio-bot) - [#1924](https://github.com/request/request/pull/1924) Update eslint to version 1.10.1 🚀 (@greenkeeperio-bot) - [#1915](https://github.com/request/request/pull/1915) Remove content-length and transfer-encoding headers from defaultProxyHeaderWhiteList (@yaxia) ### v2.67.0 (2015/11/19) - [#1913](https://github.com/request/request/pull/1913) Update http-signature to version 1.1.0 🚀 (@greenkeeperio-bot) ### v2.66.0 (2015/11/18) - [#1906](https://github.com/request/request/pull/1906) Update README URLs based on HTTP redirects (@ReadmeCritic) - [#1905](https://github.com/request/request/pull/1905) Convert typed arrays into regular buffers (@simov) - [#1902](https://github.com/request/request/pull/1902) node-uuid@1.4.7 breaks build 🚨 (@greenkeeperio-bot) - [#1894](https://github.com/request/request/pull/1894) Fix tunneling after redirection from https (Original: #1881) (@simov, @falms) - [#1893](https://github.com/request/request/pull/1893) Update eslint to version 1.9.0 🚀 (@greenkeeperio-bot) - [#1852](https://github.com/request/request/pull/1852) Update eslint to version 1.7.3 🚀 (@simov, @greenkeeperio-bot, @paulomcnally, @michelsalib, @arbaaz, @nsklkn, @LoicMahieu, @JoshWillik, @jzaefferer, @ryanwholey, @djchie, @thisconnect, @mgenereu, @acroca, @Sebmaster, @KoltesDigital) - [#1876](https://github.com/request/request/pull/1876) Implement loose matching for har mime types (@simov) - [#1875](https://github.com/request/request/pull/1875) Update bluebird to version 3.0.2 🚀 (@simov, @greenkeeperio-bot) - [#1871](https://github.com/request/request/pull/1871) Update browserify to version 12.0.1 🚀 (@greenkeeperio-bot) - [#1866](https://github.com/request/request/pull/1866) Add missing quotes on x-token property in README (@miguelmota) - [#1874](https://github.com/request/request/pull/1874) Fix typo in README.md (@gswalden) - [#1860](https://github.com/request/request/pull/1860) Improve referer header tests and docs (@simov) - [#1861](https://github.com/request/request/pull/1861) Remove redundant call to Stream constructor (@watson) - [#1857](https://github.com/request/request/pull/1857) Fix Referer header to point to the original host name (@simov) - [#1850](https://github.com/request/request/pull/1850) Update karma-coverage to version 0.5.3 🚀 (@greenkeeperio-bot) - [#1847](https://github.com/request/request/pull/1847) Use node's latest version when building (@simov) - [#1836](https://github.com/request/request/pull/1836) Tunnel: fix wrong property name (@KoltesDigital) - [#1820](https://github.com/request/request/pull/1820) Set href as request.js uses it (@mgenereu) - [#1840](https://github.com/request/request/pull/1840) Update http-signature to version 1.0.2 🚀 (@greenkeeperio-bot) - [#1845](https://github.com/request/request/pull/1845) Update istanbul to version 0.4.0 🚀 (@greenkeeperio-bot) ### v2.65.0 (2015/10/11) - [#1833](https://github.com/request/request/pull/1833) Update aws-sign2 to version 0.6.0 🚀 (@greenkeeperio-bot) - [#1811](https://github.com/request/request/pull/1811) Enable loose cookie parsing in tough-cookie (@Sebmaster) - [#1830](https://github.com/request/request/pull/1830) Bring back tilde ranges for all dependencies (@simov) - [#1821](https://github.com/request/request/pull/1821) Implement support for RFC 2617 MD5-sess algorithm. (@BigDSK) - [#1828](https://github.com/request/request/pull/1828) Updated qs dependency to 5.2.0 (@acroca) - [#1818](https://github.com/request/request/pull/1818) Extract `readResponseBody` method out of `onRequestResponse` (@pvoisin) - [#1819](https://github.com/request/request/pull/1819) Run stringify once (@mgenereu) - [#1814](https://github.com/request/request/pull/1814) Updated har-validator to version 2.0.2 (@greenkeeperio-bot) - [#1807](https://github.com/request/request/pull/1807) Updated tough-cookie to version 2.1.0 (@greenkeeperio-bot) - [#1800](https://github.com/request/request/pull/1800) Add caret ranges for devDependencies, except eslint (@simov) - [#1799](https://github.com/request/request/pull/1799) Updated karma-browserify to version 4.4.0 (@greenkeeperio-bot) - [#1797](https://github.com/request/request/pull/1797) Updated tape to version 4.2.0 (@greenkeeperio-bot) - [#1788](https://github.com/request/request/pull/1788) Pinned all dependencies (@greenkeeperio-bot) ### v2.64.0 (2015/09/25) - [#1787](https://github.com/request/request/pull/1787) npm ignore examples, release.sh and disabled.appveyor.yml (@thisconnect) - [#1775](https://github.com/request/request/pull/1775) Fix typo in README.md (@djchie) - [#1776](https://github.com/request/request/pull/1776) Changed word 'conjuction' to read 'conjunction' in README.md (@ryanwholey) - [#1785](https://github.com/request/request/pull/1785) Revert: Set default application/json content-type when using json option #1772 (@simov) ### v2.63.0 (2015/09/21) - [#1772](https://github.com/request/request/pull/1772) Set default application/json content-type when using json option (@jzaefferer) ### v2.62.0 (2015/09/15) - [#1768](https://github.com/request/request/pull/1768) Add node 4.0 to the list of build targets (@simov) - [#1767](https://github.com/request/request/pull/1767) Query strings now cooperate with unix sockets (@JoshWillik) - [#1750](https://github.com/request/request/pull/1750) Revert doc about installation of tough-cookie added in #884 (@LoicMahieu) - [#1746](https://github.com/request/request/pull/1746) Missed comma in Readme (@nsklkn) - [#1743](https://github.com/request/request/pull/1743) Fix options not being initialized in defaults method (@simov) ### v2.61.0 (2015/08/19) - [#1721](https://github.com/request/request/pull/1721) Minor fix in README.md (@arbaaz) - [#1733](https://github.com/request/request/pull/1733) Avoid useless Buffer transformation (@michelsalib) - [#1726](https://github.com/request/request/pull/1726) Update README.md (@paulomcnally) - [#1715](https://github.com/request/request/pull/1715) Fix forever option in node > 0.10 #1709 (@calibr) - [#1716](https://github.com/request/request/pull/1716) Do not create Buffer from Object in setContentLength(iojs v3.0 issue) (@calibr) - [#1711](https://github.com/request/request/pull/1711) Add ability to detect connect timeouts (@kevinburke) - [#1712](https://github.com/request/request/pull/1712) Set certificate expiration to August 2, 2018 (@kevinburke) - [#1700](https://github.com/request/request/pull/1700) debug() when JSON.parse() on a response body fails (@phillipj) ### v2.60.0 (2015/07/21) - [#1687](https://github.com/request/request/pull/1687) Fix caseless bug - content-type not being set for multipart/form-data (@simov, @garymathews) ### v2.59.0 (2015/07/20) - [#1671](https://github.com/request/request/pull/1671) Add tests and docs for using the agent, agentClass, agentOptions and forever options. Forever option defaults to using http(s).Agent in node 0.12+ (@simov) - [#1679](https://github.com/request/request/pull/1679) Fix - do not remove OAuth param when using OAuth realm (@simov, @jhalickman) - [#1668](https://github.com/request/request/pull/1668) updated dependencies (@deamme) - [#1656](https://github.com/request/request/pull/1656) Fix form method (@simov) - [#1651](https://github.com/request/request/pull/1651) Preserve HEAD method when using followAllRedirects (@simov) - [#1652](https://github.com/request/request/pull/1652) Update `encoding` option documentation in README.md (@daniel347x) - [#1650](https://github.com/request/request/pull/1650) Allow content-type overriding when using the `form` option (@simov) - [#1646](https://github.com/request/request/pull/1646) Clarify the nature of setting `ca` in `agentOptions` (@jeffcharles) ### v2.58.0 (2015/06/16) - [#1638](https://github.com/request/request/pull/1638) Use the `extend` module to deep extend in the defaults method (@simov) - [#1631](https://github.com/request/request/pull/1631) Move tunnel logic into separate module (@simov) - [#1634](https://github.com/request/request/pull/1634) Fix OAuth query transport_method (@simov) - [#1603](https://github.com/request/request/pull/1603) Add codecov (@simov) ### v2.57.0 (2015/05/31) - [#1615](https://github.com/request/request/pull/1615) Replace '.client' with '.socket' as the former was deprecated in 2.2.0. (@ChALkeR) ### v2.56.0 (2015/05/28) - [#1610](https://github.com/request/request/pull/1610) Bump module dependencies (@simov) - [#1600](https://github.com/request/request/pull/1600) Extract the querystring logic into separate module (@simov) - [#1607](https://github.com/request/request/pull/1607) Re-generate certificates (@simov) - [#1599](https://github.com/request/request/pull/1599) Move getProxyFromURI logic below the check for Invaild URI (#1595) (@simov) - [#1598](https://github.com/request/request/pull/1598) Fix the way http verbs are defined in order to please intellisense IDEs (@simov, @flannelJesus) - [#1591](https://github.com/request/request/pull/1591) A few minor fixes: (@simov) - [#1584](https://github.com/request/request/pull/1584) Refactor test-default tests (according to comments in #1430) (@simov) - [#1585](https://github.com/request/request/pull/1585) Fixing documentation regarding TLS options (#1583) (@mainakae) - [#1574](https://github.com/request/request/pull/1574) Refresh the oauth_nonce on redirect (#1573) (@simov) - [#1570](https://github.com/request/request/pull/1570) Discovered tests that weren't properly running (@seanstrom) - [#1569](https://github.com/request/request/pull/1569) Fix pause before response arrives (@kevinoid) - [#1558](https://github.com/request/request/pull/1558) Emit error instead of throw (@simov) - [#1568](https://github.com/request/request/pull/1568) Fix stall when piping gzipped response (@kevinoid) - [#1560](https://github.com/request/request/pull/1560) Update combined-stream (@apechimp) - [#1543](https://github.com/request/request/pull/1543) Initial support for oauth_body_hash on json payloads (@simov, @aesopwolf) - [#1541](https://github.com/request/request/pull/1541) Fix coveralls (@simov) - [#1540](https://github.com/request/request/pull/1540) Fix recursive defaults for convenience methods (@simov) - [#1536](https://github.com/request/request/pull/1536) More eslint style rules (@froatsnook) - [#1533](https://github.com/request/request/pull/1533) Adding dependency status bar to README.md (@YasharF) - [#1539](https://github.com/request/request/pull/1539) ensure the latest version of har-validator is included (@ahmadnassri) - [#1516](https://github.com/request/request/pull/1516) forever+pool test (@devTristan) ### v2.55.0 (2015/04/05) - [#1520](https://github.com/request/request/pull/1520) Refactor defaults (@simov) - [#1525](https://github.com/request/request/pull/1525) Delete request headers with undefined value. (@froatsnook) - [#1521](https://github.com/request/request/pull/1521) Add promise tests (@simov) - [#1518](https://github.com/request/request/pull/1518) Fix defaults (@simov) - [#1515](https://github.com/request/request/pull/1515) Allow static invoking of convenience methods (@simov) - [#1505](https://github.com/request/request/pull/1505) Fix multipart boundary extraction regexp (@simov) - [#1510](https://github.com/request/request/pull/1510) Fix basic auth form data (@simov) ### v2.54.0 (2015/03/24) - [#1501](https://github.com/request/request/pull/1501) HTTP Archive 1.2 support (@ahmadnassri) - [#1486](https://github.com/request/request/pull/1486) Add a test for the forever agent (@akshayp) - [#1500](https://github.com/request/request/pull/1500) Adding handling for no auth method and null bearer (@philberg) - [#1498](https://github.com/request/request/pull/1498) Add table of contents in readme (@simov) - [#1477](https://github.com/request/request/pull/1477) Add support for qs options via qsOptions key (@simov) - [#1496](https://github.com/request/request/pull/1496) Parameters encoded to base 64 should be decoded as UTF-8, not ASCII. (@albanm) - [#1494](https://github.com/request/request/pull/1494) Update eslint (@froatsnook) - [#1474](https://github.com/request/request/pull/1474) Require Colon in Basic Auth (@erykwalder) - [#1481](https://github.com/request/request/pull/1481) Fix baseUrl and redirections. (@burningtree) - [#1469](https://github.com/request/request/pull/1469) Feature/base url (@froatsnook) - [#1459](https://github.com/request/request/pull/1459) Add option to time request/response cycle (including rollup of redirects) (@aaron-em) - [#1468](https://github.com/request/request/pull/1468) Re-enable io.js/node 0.12 build (@simov, @mikeal, @BBB) - [#1442](https://github.com/request/request/pull/1442) Fixed the issue with strictSSL tests on 0.12 & io.js by explicitly setting a cipher that matches the cert. (@BBB, @nickmccurdy, @demohi, @simov, @0x4139) - [#1460](https://github.com/request/request/pull/1460) localAddress or proxy config is lost when redirecting (@simov, @0x4139) - [#1453](https://github.com/request/request/pull/1453) Test on Node.js 0.12 and io.js with allowed failures (@nickmccurdy, @demohi) - [#1426](https://github.com/request/request/pull/1426) Fixing tests to pass on io.js and node 0.12 (only test-https.js stiff failing) (@mikeal) - [#1446](https://github.com/request/request/pull/1446) Missing HTTP referer header with redirects Fixes #1038 (@simov, @guimon) - [#1428](https://github.com/request/request/pull/1428) Deprecate Node v0.8.x (@nylen) - [#1436](https://github.com/request/request/pull/1436) Add ability to set a requester without setting default options (@tikotzky) - [#1435](https://github.com/request/request/pull/1435) dry up verb methods (@sethpollack) - [#1423](https://github.com/request/request/pull/1423) Allow fully qualified multipart content-type header (@simov) - [#1430](https://github.com/request/request/pull/1430) Fix recursive requester (@tikotzky) - [#1429](https://github.com/request/request/pull/1429) Throw error when making HEAD request with a body (@tikotzky) - [#1419](https://github.com/request/request/pull/1419) Add note that the project is broken in 0.12.x (@nylen) - [#1413](https://github.com/request/request/pull/1413) Fix basic auth (@simov) - [#1397](https://github.com/request/request/pull/1397) Improve pipe-from-file tests (@nylen) ### v2.53.0 (2015/02/02) - [#1396](https://github.com/request/request/pull/1396) Do not rfc3986 escape JSON bodies (@nylen, @simov) - [#1392](https://github.com/request/request/pull/1392) Improve `timeout` option description (@watson) ### v2.52.0 (2015/02/02) - [#1383](https://github.com/request/request/pull/1383) Add missing HTTPS options that were not being passed to tunnel (@brichard19) (@nylen) - [#1388](https://github.com/request/request/pull/1388) Upgrade mime-types package version (@roderickhsiao) - [#1389](https://github.com/request/request/pull/1389) Revise Setup Tunnel Function (@seanstrom) - [#1374](https://github.com/request/request/pull/1374) Allow explicitly disabling tunneling for proxied https destinations (@nylen) - [#1376](https://github.com/request/request/pull/1376) Use karma-browserify for tests. Add browser test coverage reporter. (@eiriksm) - [#1366](https://github.com/request/request/pull/1366) Refactor OAuth into separate module (@simov) - [#1373](https://github.com/request/request/pull/1373) Rewrite tunnel test to be pure Node.js (@nylen) - [#1371](https://github.com/request/request/pull/1371) Upgrade test reporter (@nylen) - [#1360](https://github.com/request/request/pull/1360) Refactor basic, bearer, digest auth logic into separate class (@simov) - [#1354](https://github.com/request/request/pull/1354) Remove circular dependency from debugging code (@nylen) - [#1351](https://github.com/request/request/pull/1351) Move digest auth into private prototype method (@simov) - [#1352](https://github.com/request/request/pull/1352) Update hawk dependency to ~2.3.0 (@mridgway) - [#1353](https://github.com/request/request/pull/1353) Correct travis-ci badge (@dogancelik) - [#1349](https://github.com/request/request/pull/1349) Make sure we return on errored browser requests. (@eiriksm) - [#1346](https://github.com/request/request/pull/1346) getProxyFromURI Extraction Refactor (@seanstrom) - [#1337](https://github.com/request/request/pull/1337) Standardize test ports on 6767 (@nylen) - [#1341](https://github.com/request/request/pull/1341) Emit FormData error events as Request error events (@nylen, @rwky) - [#1343](https://github.com/request/request/pull/1343) Clean up readme badges, and add Travis and Coveralls badges (@nylen) - [#1345](https://github.com/request/request/pull/1345) Update README.md (@Aaron-Hartwig) - [#1338](https://github.com/request/request/pull/1338) Always wait for server.close() callback in tests (@nylen) - [#1342](https://github.com/request/request/pull/1342) Add mock https server and redo start of browser tests for this purpose. (@eiriksm) - [#1339](https://github.com/request/request/pull/1339) Improve auth docs (@nylen) - [#1335](https://github.com/request/request/pull/1335) Add support for OAuth plaintext signature method (@simov) - [#1332](https://github.com/request/request/pull/1332) Add clean script to remove test-browser.js after the tests run (@seanstrom) - [#1327](https://github.com/request/request/pull/1327) Fix errors generating coverage reports. (@nylen) - [#1330](https://github.com/request/request/pull/1330) Return empty buffer upon empty response body and encoding is set to null (@seanstrom) - [#1326](https://github.com/request/request/pull/1326) Use faster container-based infrastructure on Travis (@nylen) - [#1315](https://github.com/request/request/pull/1315) Implement rfc3986 option (@simov, @nylen, @apoco, @DullReferenceException, @mmalecki, @oliamb, @cliffcrosland, @LewisJEllis, @eiriksm, @poislagarde) - [#1314](https://github.com/request/request/pull/1314) Detect urlencoded form data header via regex (@simov) - [#1317](https://github.com/request/request/pull/1317) Improve OAuth1.0 server side flow example (@simov) ### v2.51.0 (2014/12/10) - [#1310](https://github.com/request/request/pull/1310) Revert changes introduced in https://github.com/request/request/pull/1282 (@simov) ### v2.50.0 (2014/12/09) - [#1308](https://github.com/request/request/pull/1308) Add browser test to keep track of browserify compability. (@eiriksm) - [#1299](https://github.com/request/request/pull/1299) Add optional support for jsonReviver (@poislagarde) - [#1277](https://github.com/request/request/pull/1277) Add Coveralls configuration (@simov) - [#1307](https://github.com/request/request/pull/1307) Upgrade form-data, add back browserify compability. Fixes #455. (@eiriksm) - [#1305](https://github.com/request/request/pull/1305) Fix typo in README.md (@LewisJEllis) - [#1288](https://github.com/request/request/pull/1288) Update README.md to explain custom file use case (@cliffcrosland) ### v2.49.0 (2014/11/28) - [#1295](https://github.com/request/request/pull/1295) fix(proxy): no-proxy false positive (@oliamb) - [#1292](https://github.com/request/request/pull/1292) Upgrade `caseless` to 0.8.1 (@mmalecki) - [#1276](https://github.com/request/request/pull/1276) Set transfer encoding for multipart/related to chunked by default (@simov) - [#1275](https://github.com/request/request/pull/1275) Fix multipart content-type headers detection (@simov) - [#1269](https://github.com/request/request/pull/1269) adds streams example for review (@tbuchok) - [#1238](https://github.com/request/request/pull/1238) Add examples README.md (@simov) ### v2.48.0 (2014/11/12) - [#1263](https://github.com/request/request/pull/1263) Fixed a syntax error / typo in README.md (@xna2) - [#1253](https://github.com/request/request/pull/1253) Add multipart chunked flag (@simov, @nylen) - [#1251](https://github.com/request/request/pull/1251) Clarify that defaults() does not modify global defaults (@nylen) - [#1250](https://github.com/request/request/pull/1250) Improve documentation for pool and maxSockets options (@nylen) - [#1237](https://github.com/request/request/pull/1237) Documenting error handling when using streams (@vmattos) - [#1244](https://github.com/request/request/pull/1244) Finalize changelog command (@nylen) - [#1241](https://github.com/request/request/pull/1241) Fix typo (@alexanderGugel) - [#1223](https://github.com/request/request/pull/1223) Show latest version number instead of "upcoming" in changelog (@nylen) - [#1236](https://github.com/request/request/pull/1236) Document how to use custom CA in README (#1229) (@hypesystem) - [#1228](https://github.com/request/request/pull/1228) Support for oauth with RSA-SHA1 signing (@nylen) - [#1216](https://github.com/request/request/pull/1216) Made json and multipart options coexist (@nylen, @simov) - [#1225](https://github.com/request/request/pull/1225) Allow header white/exclusive lists in any case. (@RReverser) ### v2.47.0 (2014/10/26) - [#1222](https://github.com/request/request/pull/1222) Move from mikeal/request to request/request (@nylen) - [#1220](https://github.com/request/request/pull/1220) update qs dependency to 2.3.1 (@FredKSchott) - [#1212](https://github.com/request/request/pull/1212) Improve tests/test-timeout.js (@nylen) - [#1219](https://github.com/request/request/pull/1219) remove old globalAgent workaround for node 0.4 (@request) - [#1214](https://github.com/request/request/pull/1214) Remove cruft left over from optional dependencies (@nylen) - [#1215](https://github.com/request/request/pull/1215) Add proxyHeaderExclusiveList option for proxy-only headers. (@RReverser) - [#1211](https://github.com/request/request/pull/1211) Allow 'Host' header instead of 'host' and remember case across redirects (@nylen) - [#1208](https://github.com/request/request/pull/1208) Improve release script (@nylen) - [#1213](https://github.com/request/request/pull/1213) Support for custom cookie store (@nylen, @mitsuru) - [#1197](https://github.com/request/request/pull/1197) Clean up some code around setting the agent (@FredKSchott) - [#1209](https://github.com/request/request/pull/1209) Improve multipart form append test (@simov) - [#1207](https://github.com/request/request/pull/1207) Update changelog (@nylen) - [#1185](https://github.com/request/request/pull/1185) Stream multipart/related bodies (@simov) ### v2.46.0 (2014/10/23) - [#1198](https://github.com/request/request/pull/1198) doc for TLS/SSL protocol options (@shawnzhu) - [#1200](https://github.com/request/request/pull/1200) Add a Gitter chat badge to README.md (@gitter-badger) - [#1196](https://github.com/request/request/pull/1196) Upgrade taper test reporter to v0.3.0 (@nylen) - [#1199](https://github.com/request/request/pull/1199) Fix lint error: undeclared var i (@nylen) - [#1191](https://github.com/request/request/pull/1191) Move self.proxy decision logic out of init and into a helper (@FredKSchott) - [#1190](https://github.com/request/request/pull/1190) Move _buildRequest() logic back into init (@FredKSchott) - [#1186](https://github.com/request/request/pull/1186) Support Smarter Unix URL Scheme (@FredKSchott) - [#1178](https://github.com/request/request/pull/1178) update form documentation for new usage (@FredKSchott) - [#1180](https://github.com/request/request/pull/1180) Enable no-mixed-requires linting rule (@nylen) - [#1184](https://github.com/request/request/pull/1184) Don't forward authorization header across redirects to different hosts (@nylen) - [#1183](https://github.com/request/request/pull/1183) Correct README about pre and postamble CRLF using multipart and not mult... (@netpoetica) - [#1179](https://github.com/request/request/pull/1179) Lint tests directory (@nylen) - [#1169](https://github.com/request/request/pull/1169) add metadata for form-data file field (@dotcypress) - [#1173](https://github.com/request/request/pull/1173) remove optional dependencies (@seanstrom) - [#1165](https://github.com/request/request/pull/1165) Cleanup event listeners and remove function creation from init (@FredKSchott) - [#1174](https://github.com/request/request/pull/1174) update the request.cookie docs to have a valid cookie example (@seanstrom) - [#1168](https://github.com/request/request/pull/1168) create a detach helper and use detach helper in replace of nextTick (@seanstrom) - [#1171](https://github.com/request/request/pull/1171) in post can send form data and use callback (@MiroRadenovic) - [#1159](https://github.com/request/request/pull/1159) accept charset for x-www-form-urlencoded content-type (@seanstrom) - [#1157](https://github.com/request/request/pull/1157) Update README.md: body with json=true (@Rob--W) - [#1164](https://github.com/request/request/pull/1164) Disable tests/test-timeout.js on Travis (@nylen) - [#1153](https://github.com/request/request/pull/1153) Document how to run a single test (@nylen) - [#1144](https://github.com/request/request/pull/1144) adds documentation for the "response" event within the streaming section (@tbuchok) - [#1162](https://github.com/request/request/pull/1162) Update eslintrc file to no longer allow past errors (@FredKSchott) - [#1155](https://github.com/request/request/pull/1155) Support/use self everywhere (@seanstrom) - [#1161](https://github.com/request/request/pull/1161) fix no-use-before-define lint warnings (@emkay) - [#1156](https://github.com/request/request/pull/1156) adding curly brackets to get rid of lint errors (@emkay) - [#1151](https://github.com/request/request/pull/1151) Fix localAddress test on OS X (@nylen) - [#1145](https://github.com/request/request/pull/1145) documentation: fix outdated reference to setCookieSync old name in README (@FredKSchott) - [#1131](https://github.com/request/request/pull/1131) Update pool documentation (@FredKSchott) - [#1143](https://github.com/request/request/pull/1143) Rewrite all tests to use tape (@nylen) - [#1137](https://github.com/request/request/pull/1137) Add ability to specifiy querystring lib in options. (@jgrund) - [#1138](https://github.com/request/request/pull/1138) allow hostname and port in place of host on uri (@cappslock) - [#1134](https://github.com/request/request/pull/1134) Fix multiple redirects and `self.followRedirect` (@blakeembrey) - [#1130](https://github.com/request/request/pull/1130) documentation fix: add note about npm test for contributing (@FredKSchott) - [#1120](https://github.com/request/request/pull/1120) Support/refactor request setup tunnel (@seanstrom) - [#1129](https://github.com/request/request/pull/1129) linting fix: convert double quote strings to use single quotes (@FredKSchott) - [#1124](https://github.com/request/request/pull/1124) linting fix: remove unneccesary semi-colons (@FredKSchott) ### v2.45.0 (2014/10/06) - [#1128](https://github.com/request/request/pull/1128) Add test for setCookie regression (@nylen) - [#1127](https://github.com/request/request/pull/1127) added tests around using objects as values in a query string (@bcoe) - [#1103](https://github.com/request/request/pull/1103) Support/refactor request constructor (@nylen, @seanstrom) - [#1119](https://github.com/request/request/pull/1119) add basic linting to request library (@FredKSchott) - [#1121](https://github.com/request/request/pull/1121) Revert "Explicitly use sync versions of cookie functions" (@nylen) - [#1118](https://github.com/request/request/pull/1118) linting fix: Restructure bad empty if statement (@FredKSchott) - [#1117](https://github.com/request/request/pull/1117) Fix a bad check for valid URIs (@FredKSchott) - [#1113](https://github.com/request/request/pull/1113) linting fix: space out operators (@FredKSchott) - [#1116](https://github.com/request/request/pull/1116) Fix typo in `noProxyHost` definition (@FredKSchott) - [#1114](https://github.com/request/request/pull/1114) linting fix: Added a `new` operator that was missing when creating and throwing a new error (@FredKSchott) - [#1096](https://github.com/request/request/pull/1096) No_proxy support (@samcday) - [#1107](https://github.com/request/request/pull/1107) linting-fix: remove unused variables (@FredKSchott) - [#1112](https://github.com/request/request/pull/1112) linting fix: Make return values consistent and more straitforward (@FredKSchott) - [#1111](https://github.com/request/request/pull/1111) linting fix: authPieces was getting redeclared (@FredKSchott) - [#1105](https://github.com/request/request/pull/1105) Use strict mode in request (@FredKSchott) - [#1110](https://github.com/request/request/pull/1110) linting fix: replace lazy '==' with more strict '===' (@FredKSchott) - [#1109](https://github.com/request/request/pull/1109) linting fix: remove function call from if-else conditional statement (@FredKSchott) - [#1102](https://github.com/request/request/pull/1102) Fix to allow setting a `requester` on recursive calls to `request.defaults` (@tikotzky) - [#1095](https://github.com/request/request/pull/1095) Tweaking engines in package.json (@pdehaan) - [#1082](https://github.com/request/request/pull/1082) Forward the socket event from the httpModule request (@seanstrom) - [#972](https://github.com/request/request/pull/972) Clarify gzip handling in the README (@kevinoid) - [#1089](https://github.com/request/request/pull/1089) Mention that encoding defaults to utf8, not Buffer (@stuartpb) - [#1088](https://github.com/request/request/pull/1088) Fix cookie example in README.md and make it more clear (@pipi32167) - [#1027](https://github.com/request/request/pull/1027) Add support for multipart form data in request options. (@crocket) - [#1076](https://github.com/request/request/pull/1076) use Request.abort() to abort the request when the request has timed-out (@seanstrom) - [#1068](https://github.com/request/request/pull/1068) add optional postamble required by .NET multipart requests (@netpoetica) ### v2.43.0 (2014/09/18) - [#1057](https://github.com/request/request/pull/1057) Defaults should not overwrite defined options (@davidwood) - [#1046](https://github.com/request/request/pull/1046) Propagate datastream errors, useful in case gzip fails. (@ZJONSSON, @Janpot) - [#1063](https://github.com/request/request/pull/1063) copy the input headers object #1060 (@finnp) - [#1031](https://github.com/request/request/pull/1031) Explicitly use sync versions of cookie functions (@ZJONSSON) - [#1056](https://github.com/request/request/pull/1056) Fix redirects when passing url.parse(x) as URL to convenience method (@nylen) ### v2.42.0 (2014/09/04) - [#1053](https://github.com/request/request/pull/1053) Fix #1051 Parse auth properly when using non-tunneling proxy (@isaacs) ### v2.41.0 (2014/09/04) - [#1050](https://github.com/request/request/pull/1050) Pass whitelisted headers to tunneling proxy. Organize all tunneling logic. (@isaacs, @Feldhacker) - [#1035](https://github.com/request/request/pull/1035) souped up nodei.co badge (@rvagg) - [#1048](https://github.com/request/request/pull/1048) Aws is now possible over a proxy (@steven-aerts) - [#1039](https://github.com/request/request/pull/1039) extract out helper functions to a helper file (@seanstrom) - [#1021](https://github.com/request/request/pull/1021) Support/refactor indexjs (@seanstrom) - [#1033](https://github.com/request/request/pull/1033) Improve and document debug options (@nylen) - [#1034](https://github.com/request/request/pull/1034) Fix readme headings (@nylen) - [#1030](https://github.com/request/request/pull/1030) Allow recursive request.defaults (@tikotzky) - [#1029](https://github.com/request/request/pull/1029) Fix a couple of typos (@nylen) - [#675](https://github.com/request/request/pull/675) Checking for SSL fault on connection before reading SSL properties (@VRMink) - [#989](https://github.com/request/request/pull/989) Added allowRedirect function. Should return true if redirect is allowed or false otherwise (@doronin) - [#1025](https://github.com/request/request/pull/1025) [fixes #1023] Set self._ended to true once response has ended (@mridgway) - [#1020](https://github.com/request/request/pull/1020) Add back removed debug metadata (@FredKSchott) - [#1008](https://github.com/request/request/pull/1008) Moving to module instead of cutomer buffer concatenation. (@mikeal) - [#770](https://github.com/request/request/pull/770) Added dependency badge for README file; (@timgluz, @mafintosh, @lalitkapoor, @stash, @bobyrizov) - [#1016](https://github.com/request/request/pull/1016) toJSON no longer results in an infinite loop, returns simple objects (@FredKSchott) - [#1018](https://github.com/request/request/pull/1018) Remove pre-0.4.4 HTTPS fix (@mmalecki) - [#1006](https://github.com/request/request/pull/1006) Migrate to caseless, fixes #1001 (@mikeal) - [#995](https://github.com/request/request/pull/995) Fix parsing array of objects (@sjonnet19) - [#999](https://github.com/request/request/pull/999) Fix fallback for browserify for optional modules. (@eiriksm) - [#996](https://github.com/request/request/pull/996) Wrong oauth signature when multiple same param keys exist [updated] (@bengl) ### v2.40.0 (2014/08/06) - [#992](https://github.com/request/request/pull/992) Fix security vulnerability. Update qs (@poeticninja) - [#988](https://github.com/request/request/pull/988) “--” -> “—” (@upisfree) - [#987](https://github.com/request/request/pull/987) Show optional modules as being loaded by the module that reqeusted them (@iarna) ### v2.39.0 (2014/07/24) - [#976](https://github.com/request/request/pull/976) Update README.md (@pvoznenko) ### v2.38.0 (2014/07/22) - [#952](https://github.com/request/request/pull/952) Adding support to client certificate with proxy use case (@ofirshaked) - [#884](https://github.com/request/request/pull/884) Documented tough-cookie installation. (@wbyoung) - [#935](https://github.com/request/request/pull/935) Correct repository url (@fritx) - [#963](https://github.com/request/request/pull/963) Update changelog (@nylen) - [#960](https://github.com/request/request/pull/960) Support gzip with encoding on node pre-v0.9.4 (@kevinoid) - [#953](https://github.com/request/request/pull/953) Add async Content-Length computation when using form-data (@LoicMahieu) - [#844](https://github.com/request/request/pull/844) Add support for HTTP[S]_PROXY environment variables. Fixes #595. (@jvmccarthy) - [#946](https://github.com/request/request/pull/946) defaults: merge headers (@aj0strow) ### v2.37.0 (2014/07/07) - [#957](https://github.com/request/request/pull/957) Silence EventEmitter memory leak warning #311 (@watson) - [#955](https://github.com/request/request/pull/955) check for content-length header before setting it in nextTick (@camilleanne) - [#951](https://github.com/request/request/pull/951) Add support for gzip content decoding (@kevinoid) - [#949](https://github.com/request/request/pull/949) Manually enter querystring in form option (@charlespwd) - [#944](https://github.com/request/request/pull/944) Make request work with browserify (@eiriksm) - [#943](https://github.com/request/request/pull/943) New mime module (@eiriksm) - [#927](https://github.com/request/request/pull/927) Bump version of hawk dep. (@samccone) - [#907](https://github.com/request/request/pull/907) append secureOptions to poolKey (@medovob) ### v2.35.0 (2014/05/17) - [#901](https://github.com/request/request/pull/901) Fixes #555 (@pigulla) - [#897](https://github.com/request/request/pull/897) merge with default options (@vohof) - [#891](https://github.com/request/request/pull/891) fixes 857 - options object is mutated by calling request (@lalitkapoor) - [#869](https://github.com/request/request/pull/869) Pipefilter test (@tgohn) - [#866](https://github.com/request/request/pull/866) Fix typo (@dandv) - [#861](https://github.com/request/request/pull/861) Add support for RFC 6750 Bearer Tokens (@phedny) - [#809](https://github.com/request/request/pull/809) upgrade tunnel-proxy to 0.4.0 (@ksato9700) - [#850](https://github.com/request/request/pull/850) Fix word consistency in readme (@0xNobody) - [#810](https://github.com/request/request/pull/810) add some exposition to mpu example in README.md (@mikermcneil) - [#840](https://github.com/request/request/pull/840) improve error reporting for invalid protocols (@FND) - [#821](https://github.com/request/request/pull/821) added secureOptions back (@nw) - [#815](https://github.com/request/request/pull/815) Create changelog based on pull requests (@lalitkapoor) ### v2.34.0 (2014/02/18) - [#516](https://github.com/request/request/pull/516) UNIX Socket URL Support (@lyuzashi) - [#801](https://github.com/request/request/pull/801) 794 ignore cookie parsing and domain errors (@lalitkapoor) - [#802](https://github.com/request/request/pull/802) Added the Apache license to the package.json. (@keskival) - [#793](https://github.com/request/request/pull/793) Adds content-length calculation when submitting forms using form-data li... (@Juul) - [#785](https://github.com/request/request/pull/785) Provide ability to override content-type when `json` option used (@vvo) - [#781](https://github.com/request/request/pull/781) simpler isReadStream function (@joaojeronimo) ### v2.32.0 (2014/01/16) - [#767](https://github.com/request/request/pull/767) Use tough-cookie CookieJar sync API (@stash) - [#764](https://github.com/request/request/pull/764) Case-insensitive authentication scheme (@bobyrizov) - [#763](https://github.com/request/request/pull/763) Upgrade tough-cookie to 0.10.0 (@stash) - [#744](https://github.com/request/request/pull/744) Use Cookie.parse (@lalitkapoor) - [#757](https://github.com/request/request/pull/757) require aws-sign2 (@mafintosh) ### v2.31.0 (2014/01/08) - [#645](https://github.com/request/request/pull/645) update twitter api url to v1.1 (@mick) - [#746](https://github.com/request/request/pull/746) README: Markdown code highlight (@weakish) - [#745](https://github.com/request/request/pull/745) updating setCookie example to make it clear that the callback is required (@emkay) - [#742](https://github.com/request/request/pull/742) Add note about JSON output body type (@iansltx) - [#741](https://github.com/request/request/pull/741) README example is using old cookie jar api (@emkay) - [#736](https://github.com/request/request/pull/736) Fix callback arguments documentation (@mmalecki) - [#732](https://github.com/request/request/pull/732) JSHINT: Creating global 'for' variable. Should be 'for (var ...'. (@Fritz-Lium) - [#730](https://github.com/request/request/pull/730) better HTTP DIGEST support (@dai-shi) - [#728](https://github.com/request/request/pull/728) Fix TypeError when calling request.cookie (@scarletmeow) - [#727](https://github.com/request/request/pull/727) fix requester bug (@jchris) - [#724](https://github.com/request/request/pull/724) README.md: add custom HTTP Headers example. (@tcort) - [#719](https://github.com/request/request/pull/719) Made a comment gender neutral. (@unsetbit) - [#715](https://github.com/request/request/pull/715) Request.multipart no longer crashes when header 'Content-type' present (@pastaclub) - [#710](https://github.com/request/request/pull/710) Fixing listing in callback part of docs. (@lukasz-zak) - [#696](https://github.com/request/request/pull/696) Edited README.md for formatting and clarity of phrasing (@Zearin) - [#694](https://github.com/request/request/pull/694) Typo in README (@VRMink) - [#690](https://github.com/request/request/pull/690) Handle blank password in basic auth. (@diversario) - [#682](https://github.com/request/request/pull/682) Optional dependencies (@Turbo87) - [#683](https://github.com/request/request/pull/683) Travis CI support (@Turbo87) - [#674](https://github.com/request/request/pull/674) change cookie module,to tough-cookie.please check it . (@sxyizhiren) - [#666](https://github.com/request/request/pull/666) make `ciphers` and `secureProtocol` to work in https request (@richarddong) - [#656](https://github.com/request/request/pull/656) Test case for #304. (@diversario) - [#662](https://github.com/request/request/pull/662) option.tunnel to explicitly disable tunneling (@seanmonstar) - [#659](https://github.com/request/request/pull/659) fix failure when running with NODE_DEBUG=request, and a test for that (@jrgm) - [#630](https://github.com/request/request/pull/630) Send random cnonce for HTTP Digest requests (@wprl) - [#619](https://github.com/request/request/pull/619) decouple things a bit (@joaojeronimo) - [#613](https://github.com/request/request/pull/613) Fixes #583, moved initialization of self.uri.pathname (@lexander) - [#605](https://github.com/request/request/pull/605) Only include ":" + pass in Basic Auth if it's defined (fixes #602) (@bendrucker) - [#596](https://github.com/request/request/pull/596) Global agent is being used when pool is specified (@Cauldrath) - [#594](https://github.com/request/request/pull/594) Emit complete event when there is no callback (@RomainLK) - [#601](https://github.com/request/request/pull/601) Fixed a small typo (@michalstanko) - [#589](https://github.com/request/request/pull/589) Prevent setting headers after they are sent (@geek) - [#587](https://github.com/request/request/pull/587) Global cookie jar disabled by default (@threepointone) - [#544](https://github.com/request/request/pull/544) Update http-signature version. (@davidlehn) - [#581](https://github.com/request/request/pull/581) Fix spelling of "ignoring." (@bigeasy) - [#568](https://github.com/request/request/pull/568) use agentOptions to create agent when specified in request (@SamPlacette) - [#564](https://github.com/request/request/pull/564) Fix redirections (@criloz) - [#541](https://github.com/request/request/pull/541) The exported request function doesn't have an auth method (@tschaub) - [#542](https://github.com/request/request/pull/542) Expose Request class (@regality) - [#536](https://github.com/request/request/pull/536) Allow explicitly empty user field for basic authentication. (@mikeando) - [#532](https://github.com/request/request/pull/532) fix typo (@fredericosilva) - [#497](https://github.com/request/request/pull/497) Added redirect event (@Cauldrath) - [#503](https://github.com/request/request/pull/503) Fix basic auth for passwords that contain colons (@tonistiigi) - [#521](https://github.com/request/request/pull/521) Improving test-localAddress.js (@noway) - [#529](https://github.com/request/request/pull/529) dependencies versions bump (@jodaka) - [#523](https://github.com/request/request/pull/523) Updating dependencies (@noway) - [#520](https://github.com/request/request/pull/520) Fixing test-tunnel.js (@noway) - [#519](https://github.com/request/request/pull/519) Update internal path state on post-creation QS changes (@jblebrun) - [#510](https://github.com/request/request/pull/510) Add HTTP Signature support. (@davidlehn) - [#502](https://github.com/request/request/pull/502) Fix POST (and probably other) requests that are retried after 401 Unauthorized (@nylen) - [#508](https://github.com/request/request/pull/508) Honor the .strictSSL option when using proxies (tunnel-agent) (@jhs) - [#512](https://github.com/request/request/pull/512) Make password optional to support the format: http://username@hostname/ (@pajato1) - [#513](https://github.com/request/request/pull/513) add 'localAddress' support (@yyfrankyy) - [#498](https://github.com/request/request/pull/498) Moving response emit above setHeaders on destination streams (@kenperkins) - [#490](https://github.com/request/request/pull/490) Empty response body (3-rd argument) must be passed to callback as an empty string (@Olegas) - [#479](https://github.com/request/request/pull/479) Changing so if Accept header is explicitly set, sending json does not ov... (@RoryH) - [#475](https://github.com/request/request/pull/475) Use `unescape` from `querystring` (@shimaore) - [#473](https://github.com/request/request/pull/473) V0.10 compat (@isaacs) - [#471](https://github.com/request/request/pull/471) Using querystring library from visionmedia (@kbackowski) - [#461](https://github.com/request/request/pull/461) Strip the UTF8 BOM from a UTF encoded response (@kppullin) - [#460](https://github.com/request/request/pull/460) hawk 0.10.0 (@hueniverse) - [#462](https://github.com/request/request/pull/462) if query params are empty, then request path shouldn't end with a '?' (merges cleanly now) (@jaipandya) - [#456](https://github.com/request/request/pull/456) hawk 0.9.0 (@hueniverse) - [#429](https://github.com/request/request/pull/429) Copy options before adding callback. (@nrn, @nfriedly, @youurayy, @jplock, @kapetan, @landeiro, @othiym23, @mmalecki) - [#454](https://github.com/request/request/pull/454) Destroy the response if present when destroying the request (clean merge) (@mafintosh) - [#310](https://github.com/request/request/pull/310) Twitter Oauth Stuff Out of Date; Now Updated (@joemccann, @isaacs, @mscdex) - [#413](https://github.com/request/request/pull/413) rename googledoodle.png to .jpg (@nfriedly, @youurayy, @jplock, @kapetan, @landeiro, @othiym23, @mmalecki) - [#448](https://github.com/request/request/pull/448) Convenience method for PATCH (@mloar) - [#444](https://github.com/request/request/pull/444) protect against double callbacks on error path (@spollack) - [#433](https://github.com/request/request/pull/433) Added support for HTTPS cert & key (@mmalecki) - [#430](https://github.com/request/request/pull/430) Respect specified {Host,host} headers, not just {host} (@andrewschaaf) - [#415](https://github.com/request/request/pull/415) Fixed a typo. (@jerem) - [#338](https://github.com/request/request/pull/338) Add more auth options, including digest support (@nylen) - [#403](https://github.com/request/request/pull/403) Optimize environment lookup to happen once only (@mmalecki) - [#398](https://github.com/request/request/pull/398) Add more reporting to tests (@mmalecki) - [#388](https://github.com/request/request/pull/388) Ensure "safe" toJSON doesn't break EventEmitters (@othiym23) - [#381](https://github.com/request/request/pull/381) Resolving "Invalid signature. Expected signature base string: " (@landeiro) - [#380](https://github.com/request/request/pull/380) Fixes missing host header on retried request when using forever agent (@mac-) - [#376](https://github.com/request/request/pull/376) Headers lost on redirect (@kapetan) - [#375](https://github.com/request/request/pull/375) Fix for missing oauth_timestamp parameter (@jplock) - [#374](https://github.com/request/request/pull/374) Correct Host header for proxy tunnel CONNECT (@youurayy) - [#370](https://github.com/request/request/pull/370) Twitter reverse auth uses x_auth_mode not x_auth_type (@drudge) - [#369](https://github.com/request/request/pull/369) Don't remove x_auth_mode for Twitter reverse auth (@drudge) - [#344](https://github.com/request/request/pull/344) Make AWS auth signing find headers correctly (@nlf) - [#363](https://github.com/request/request/pull/363) rfc3986 on base_uri, now passes tests (@jeffmarshall) - [#362](https://github.com/request/request/pull/362) Running `rfc3986` on `base_uri` in `oauth.hmacsign` instead of just `encodeURIComponent` (@jeffmarshall) - [#361](https://github.com/request/request/pull/361) Don't create a Content-Length header if we already have it set (@danjenkins) - [#360](https://github.com/request/request/pull/360) Delete self._form along with everything else on redirect (@jgautier) - [#355](https://github.com/request/request/pull/355) stop sending erroneous headers on redirected requests (@azylman) - [#332](https://github.com/request/request/pull/332) Fix #296 - Only set Content-Type if body exists (@Marsup) - [#343](https://github.com/request/request/pull/343) Allow AWS to work in more situations, added a note in the README on its usage (@nlf) - [#320](https://github.com/request/request/pull/320) request.defaults() doesn't need to wrap jar() (@StuartHarris) - [#322](https://github.com/request/request/pull/322) Fix + test for piped into request bumped into redirect. #321 (@alexindigo) - [#326](https://github.com/request/request/pull/326) Do not try to remove listener from an undefined connection (@CartoDB) - [#318](https://github.com/request/request/pull/318) Pass servername to tunneling secure socket creation (@isaacs) - [#317](https://github.com/request/request/pull/317) Workaround for #313 (@isaacs) - [#293](https://github.com/request/request/pull/293) Allow parser errors to bubble up to request (@mscdex) - [#290](https://github.com/request/request/pull/290) A test for #289 (@isaacs) - [#280](https://github.com/request/request/pull/280) Like in node.js print options if NODE_DEBUG contains the word request (@Filirom1) - [#207](https://github.com/request/request/pull/207) Fix #206 Change HTTP/HTTPS agent when redirecting between protocols (@isaacs) - [#214](https://github.com/request/request/pull/214) documenting additional behavior of json option (@jphaas, @vpulim) - [#272](https://github.com/request/request/pull/272) Boundary begins with CRLF? (@elspoono, @timshadel, @naholyr, @nanodocumet, @TehShrike) - [#284](https://github.com/request/request/pull/284) Remove stray `console.log()` call in multipart generator. (@bcherry) - [#241](https://github.com/request/request/pull/241) Composability updates suggested by issue #239 (@polotek) - [#282](https://github.com/request/request/pull/282) OAuth Authorization header contains non-"oauth_" parameters (@jplock) - [#279](https://github.com/request/request/pull/279) fix tests with boundary by injecting boundry from header (@benatkin) - [#273](https://github.com/request/request/pull/273) Pipe back pressure issue (@mafintosh) - [#268](https://github.com/request/request/pull/268) I'm not OCD seriously (@TehShrike) - [#263](https://github.com/request/request/pull/263) Bug in OAuth key generation for sha1 (@nanodocumet) - [#265](https://github.com/request/request/pull/265) uncaughtException when redirected to invalid URI (@naholyr) - [#262](https://github.com/request/request/pull/262) JSON test should check for equality (@timshadel) - [#261](https://github.com/request/request/pull/261) Setting 'pool' to 'false' does NOT disable Agent pooling (@timshadel) - [#249](https://github.com/request/request/pull/249) Fix for the fix of your (closed) issue #89 where self.headers[content-length] is set to 0 for all methods (@sethbridges, @polotek, @zephrax, @jeromegn) - [#255](https://github.com/request/request/pull/255) multipart allow body === '' ( the empty string ) (@Filirom1) - [#260](https://github.com/request/request/pull/260) fixed just another leak of 'i' (@sreuter) - [#246](https://github.com/request/request/pull/246) Fixing the set-cookie header (@jeromegn) - [#243](https://github.com/request/request/pull/243) Dynamic boundary (@zephrax) - [#240](https://github.com/request/request/pull/240) don't error when null is passed for options (@polotek) - [#211](https://github.com/request/request/pull/211) Replace all occurrences of special chars in RFC3986 (@chriso, @vpulim) - [#224](https://github.com/request/request/pull/224) Multipart content-type change (@janjongboom) - [#217](https://github.com/request/request/pull/217) need to use Authorization (titlecase) header with Tumblr OAuth (@visnup) - [#203](https://github.com/request/request/pull/203) Fix cookie and redirect bugs and add auth support for HTTPS tunnel (@vpulim) - [#199](https://github.com/request/request/pull/199) Tunnel (@isaacs) - [#198](https://github.com/request/request/pull/198) Bugfix on forever usage of util.inherits (@isaacs) - [#197](https://github.com/request/request/pull/197) Make ForeverAgent work with HTTPS (@isaacs) - [#193](https://github.com/request/request/pull/193) Fixes GH-119 (@goatslacker) - [#188](https://github.com/request/request/pull/188) Add abort support to the returned request (@itay) - [#176](https://github.com/request/request/pull/176) Querystring option (@csainty) - [#182](https://github.com/request/request/pull/182) Fix request.defaults to support (uri, options, callback) api (@twilson63) - [#180](https://github.com/request/request/pull/180) Modified the post, put, head and del shortcuts to support uri optional param (@twilson63) - [#179](https://github.com/request/request/pull/179) fix to add opts in .pipe(stream, opts) (@substack) - [#177](https://github.com/request/request/pull/177) Issue #173 Support uri as first and optional config as second argument (@twilson63) - [#170](https://github.com/request/request/pull/170) can't create a cookie in a wrapped request (defaults) (@fabianonunes) - [#168](https://github.com/request/request/pull/168) Picking off an EasyFix by adding some missing mimetypes. (@serby) - [#161](https://github.com/request/request/pull/161) Fix cookie jar/headers.cookie collision (#125) (@papandreou) - [#162](https://github.com/request/request/pull/162) Fix issue #159 (@dpetukhov) - [#90](https://github.com/request/request/pull/90) add option followAllRedirects to follow post/put redirects (@jroes) - [#148](https://github.com/request/request/pull/148) Retry Agent (@thejh) - [#146](https://github.com/request/request/pull/146) Multipart should respect content-type if previously set (@apeace) - [#144](https://github.com/request/request/pull/144) added "form" option to readme (@petejkim) - [#133](https://github.com/request/request/pull/133) Fixed cookies parsing (@afanasy) - [#135](https://github.com/request/request/pull/135) host vs hostname (@iangreenleaf) - [#132](https://github.com/request/request/pull/132) return the body as a Buffer when encoding is set to null (@jahewson) - [#112](https://github.com/request/request/pull/112) Support using a custom http-like module (@jhs) - [#104](https://github.com/request/request/pull/104) Cookie handling contains bugs (@janjongboom) - [#121](https://github.com/request/request/pull/121) Another patch for cookie handling regression (@jhurliman) - [#117](https://github.com/request/request/pull/117) Remove the global `i` (@3rd-Eden) - [#110](https://github.com/request/request/pull/110) Update to Iris Couch URL (@jhs) - [#86](https://github.com/request/request/pull/86) Can't post binary to multipart requests (@kkaefer) - [#105](https://github.com/request/request/pull/105) added test for proxy option. (@dominictarr) - [#102](https://github.com/request/request/pull/102) Implemented cookies - closes issue 82: https://github.com/mikeal/request/issues/82 (@alessioalex) - [#97](https://github.com/request/request/pull/97) Typo in previous pull causes TypeError in non-0.5.11 versions (@isaacs) - [#96](https://github.com/request/request/pull/96) Authless parsed url host support (@isaacs) - [#81](https://github.com/request/request/pull/81) Enhance redirect handling (@danmactough) - [#78](https://github.com/request/request/pull/78) Don't try to do strictSSL for non-ssl connections (@isaacs) - [#76](https://github.com/request/request/pull/76) Bug when a request fails and a timeout is set (@Marsup) - [#70](https://github.com/request/request/pull/70) add test script to package.json (@isaacs, @aheckmann) - [#73](https://github.com/request/request/pull/73) Fix #71 Respect the strictSSL flag (@isaacs) - [#69](https://github.com/request/request/pull/69) Flatten chunked requests properly (@isaacs) - [#67](https://github.com/request/request/pull/67) fixed global variable leaks (@aheckmann) - [#66](https://github.com/request/request/pull/66) Do not overwrite established content-type headers for read stream deliver (@voodootikigod) - [#53](https://github.com/request/request/pull/53) Parse json: Issue #51 (@benatkin) - [#45](https://github.com/request/request/pull/45) Added timeout option (@mbrevoort) - [#35](https://github.com/request/request/pull/35) The "end" event isn't emitted for some responses (@voxpelli) - [#31](https://github.com/request/request/pull/31) Error on piping a request to a destination (@tobowers)request-2.88.1/CONTRIBUTING.md000066400000000000000000000063001333333633500155370ustar00rootroot00000000000000 # Contributing to Request :+1::tada: First off, thanks for taking the time to contribute! :tada::+1: The following is a set of guidelines for contributing to Request and its packages, which are hosted in the [Request Organization](https://github.com/request) on GitHub. These are just guidelines, not rules, use your best judgment and feel free to propose changes to this document in a pull request. ## Submitting an Issue 1. Provide a small self **sufficient** code example to **reproduce** the issue. 2. Run your test code using [request-debug](https://github.com/request/request-debug) and copy/paste the results inside the issue. 3. You should **always** use fenced code blocks when submitting code examples or any other formatted output:
  ```js
  put your javascript code here
  ```

  ```
  put any other formatted output here,
  like for example the one returned from using request-debug
  ```
  
If the problem cannot be reliably reproduced, the issue will be marked as `Not enough info (see CONTRIBUTING.md)`. If the problem is not related to request the issue will be marked as `Help (please use Stackoverflow)`. ## Submitting a Pull Request 1. In almost all of the cases your PR **needs tests**. Make sure you have any. 2. Run `npm test` locally. Fix any errors before pushing to GitHub. 3. After submitting the PR a build will be triggered on TravisCI. Wait for it to ends and make sure all jobs are passing. ----------------------------------------- ## Becoming a Contributor Individuals making significant and valuable contributions are given commit-access to the project to contribute as they see fit. This project is more like an open wiki than a standard guarded open source project. ## Rules There are a few basic ground-rules for contributors: 1. **No `--force` pushes** or modifying the Git history in any way. 1. **Non-master branches** ought to be used for ongoing work. 1. **Any** change should be added through Pull Request. 1. **External API changes and significant modifications** ought to be subject to an **internal pull-request** to solicit feedback from other contributors. 1. Internal pull-requests to solicit feedback are *encouraged* for any other non-trivial contribution but left to the discretion of the contributor. 1. For significant changes wait a full 24 hours before merging so that active contributors who are distributed throughout the world have a chance to weigh in. 1. Contributors should attempt to adhere to the prevailing code-style. 1. Run `npm test` locally before submitting your PR, to catch any easy to miss style & testing issues. To diagnose test failures, there are two ways to run a single test file: - `node_modules/.bin/taper tests/test-file.js` - run using the default [`taper`](https://github.com/nylen/taper) test reporter. - `node tests/test-file.js` - view the raw [tap](https://testanything.org/) output. ## Releases Declaring formal releases remains the prerogative of the project maintainer. ## Changes to this arrangement This is an experiment and feedback is welcome! This document may also be subject to pull-requests or changes by contributors where you believe you have something valuable to add or change. request-2.88.1/LICENSE000066400000000000000000000216641333333633500143250ustar00rootroot00000000000000Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: You must give any other recipients of the Work or Derivative Works a copy of this License; and You must cause any modified files to carry prominent notices stating that You changed the files; and You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONSrequest-2.88.1/README.md000066400000000000000000001271071333333633500145760ustar00rootroot00000000000000 # Request - Simplified HTTP client [![npm package](https://nodei.co/npm/request.png?downloads=true&downloadRank=true&stars=true)](https://nodei.co/npm/request/) [![Build status](https://img.shields.io/travis/request/request/master.svg?style=flat-square)](https://travis-ci.org/request/request) [![Coverage](https://img.shields.io/codecov/c/github/request/request.svg?style=flat-square)](https://codecov.io/github/request/request?branch=master) [![Coverage](https://img.shields.io/coveralls/request/request.svg?style=flat-square)](https://coveralls.io/r/request/request) [![Dependency Status](https://img.shields.io/david/request/request.svg?style=flat-square)](https://david-dm.org/request/request) [![Known Vulnerabilities](https://snyk.io/test/npm/request/badge.svg?style=flat-square)](https://snyk.io/test/npm/request) [![Gitter](https://img.shields.io/badge/gitter-join_chat-blue.svg?style=flat-square)](https://gitter.im/request/request?utm_source=badge) ## Super simple to use Request is designed to be the simplest way possible to make http calls. It supports HTTPS and follows redirects by default. ```js var request = require('request'); request('http://www.google.com', function (error, response, body) { console.log('error:', error); // Print the error if one occurred console.log('statusCode:', response && response.statusCode); // Print the response status code if a response was received console.log('body:', body); // Print the HTML for the Google homepage. }); ``` ## Table of contents - [Streaming](#streaming) - [Promises & Async/Await](#promises--asyncawait) - [Forms](#forms) - [HTTP Authentication](#http-authentication) - [Custom HTTP Headers](#custom-http-headers) - [OAuth Signing](#oauth-signing) - [Proxies](#proxies) - [Unix Domain Sockets](#unix-domain-sockets) - [TLS/SSL Protocol](#tlsssl-protocol) - [Support for HAR 1.2](#support-for-har-12) - [**All Available Options**](#requestoptions-callback) Request also offers [convenience methods](#convenience-methods) like `request.defaults` and `request.post`, and there are lots of [usage examples](#examples) and several [debugging techniques](#debugging). --- ## Streaming You can stream any response to a file stream. ```js request('http://google.com/doodle.png').pipe(fs.createWriteStream('doodle.png')) ``` You can also stream a file to a PUT or POST request. This method will also check the file extension against a mapping of file extensions to content-types (in this case `application/json`) and use the proper `content-type` in the PUT request (if the headers don’t already provide one). ```js fs.createReadStream('file.json').pipe(request.put('http://mysite.com/obj.json')) ``` Request can also `pipe` to itself. When doing so, `content-type` and `content-length` are preserved in the PUT headers. ```js request.get('http://google.com/img.png').pipe(request.put('http://mysite.com/img.png')) ``` Request emits a "response" event when a response is received. The `response` argument will be an instance of [http.IncomingMessage](https://nodejs.org/api/http.html#http_class_http_incomingmessage). ```js request .get('http://google.com/img.png') .on('response', function(response) { console.log(response.statusCode) // 200 console.log(response.headers['content-type']) // 'image/png' }) .pipe(request.put('http://mysite.com/img.png')) ``` To easily handle errors when streaming requests, listen to the `error` event before piping: ```js request .get('http://mysite.com/doodle.png') .on('error', function(err) { console.log(err) }) .pipe(fs.createWriteStream('doodle.png')) ``` Now let’s get fancy. ```js http.createServer(function (req, resp) { if (req.url === '/doodle.png') { if (req.method === 'PUT') { req.pipe(request.put('http://mysite.com/doodle.png')) } else if (req.method === 'GET' || req.method === 'HEAD') { request.get('http://mysite.com/doodle.png').pipe(resp) } } }) ``` You can also `pipe()` from `http.ServerRequest` instances, as well as to `http.ServerResponse` instances. The HTTP method, headers, and entity-body data will be sent. Which means that, if you don't really care about security, you can do: ```js http.createServer(function (req, resp) { if (req.url === '/doodle.png') { var x = request('http://mysite.com/doodle.png') req.pipe(x) x.pipe(resp) } }) ``` And since `pipe()` returns the destination stream in ≥ Node 0.5.x you can do one line proxying. :) ```js req.pipe(request('http://mysite.com/doodle.png')).pipe(resp) ``` Also, none of this new functionality conflicts with requests previous features, it just expands them. ```js var r = request.defaults({'proxy':'http://localproxy.com'}) http.createServer(function (req, resp) { if (req.url === '/doodle.png') { r.get('http://google.com/doodle.png').pipe(resp) } }) ``` You can still use intermediate proxies, the requests will still follow HTTP forwards, etc. [back to top](#table-of-contents) --- ## Promises & Async/Await `request` supports both streaming and callback interfaces natively. If you'd like `request` to return a Promise instead, you can use an alternative interface wrapper for `request`. These wrappers can be useful if you prefer to work with Promises, or if you'd like to use `async`/`await` in ES2017. Several alternative interfaces are provided by the request team, including: - [`request-promise`](https://github.com/request/request-promise) (uses [Bluebird](https://github.com/petkaantonov/bluebird) Promises) - [`request-promise-native`](https://github.com/request/request-promise-native) (uses native Promises) - [`request-promise-any`](https://github.com/request/request-promise-any) (uses [any-promise](https://www.npmjs.com/package/any-promise) Promises) [back to top](#table-of-contents) --- ## Forms `request` supports `application/x-www-form-urlencoded` and `multipart/form-data` form uploads. For `multipart/related` refer to the `multipart` API. #### application/x-www-form-urlencoded (URL-Encoded Forms) URL-encoded forms are simple. ```js request.post('http://service.com/upload', {form:{key:'value'}}) // or request.post('http://service.com/upload').form({key:'value'}) // or request.post({url:'http://service.com/upload', form: {key:'value'}}, function(err,httpResponse,body){ /* ... */ }) ``` #### multipart/form-data (Multipart Form Uploads) For `multipart/form-data` we use the [form-data](https://github.com/form-data/form-data) library by [@felixge](https://github.com/felixge). For the most cases, you can pass your upload form data via the `formData` option. ```js var formData = { // Pass a simple key-value pair my_field: 'my_value', // Pass data via Buffers my_buffer: Buffer.from([1, 2, 3]), // Pass data via Streams my_file: fs.createReadStream(__dirname + '/unicycle.jpg'), // Pass multiple values /w an Array attachments: [ fs.createReadStream(__dirname + '/attachment1.jpg'), fs.createReadStream(__dirname + '/attachment2.jpg') ], // Pass optional meta-data with an 'options' object with style: {value: DATA, options: OPTIONS} // Use case: for some types of streams, you'll need to provide "file"-related information manually. // See the `form-data` README for more information about options: https://github.com/form-data/form-data custom_file: { value: fs.createReadStream('/dev/urandom'), options: { filename: 'topsecret.jpg', contentType: 'image/jpeg' } } }; request.post({url:'http://service.com/upload', formData: formData}, function optionalCallback(err, httpResponse, body) { if (err) { return console.error('upload failed:', err); } console.log('Upload successful! Server responded with:', body); }); ``` For advanced cases, you can access the form-data object itself via `r.form()`. This can be modified until the request is fired on the next cycle of the event-loop. (Note that this calling `form()` will clear the currently set form data for that request.) ```js // NOTE: Advanced use-case, for normal use see 'formData' usage above var r = request.post('http://service.com/upload', function optionalCallback(err, httpResponse, body) {...}) var form = r.form(); form.append('my_field', 'my_value'); form.append('my_buffer', Buffer.from([1, 2, 3])); form.append('custom_file', fs.createReadStream(__dirname + '/unicycle.jpg'), {filename: 'unicycle.jpg'}); ``` See the [form-data README](https://github.com/form-data/form-data) for more information & examples. #### multipart/related Some variations in different HTTP implementations require a newline/CRLF before, after, or both before and after the boundary of a `multipart/related` request (using the multipart option). This has been observed in the .NET WebAPI version 4.0. You can turn on a boundary preambleCRLF or postamble by passing them as `true` to your request options. ```js request({ method: 'PUT', preambleCRLF: true, postambleCRLF: true, uri: 'http://service.com/upload', multipart: [ { 'content-type': 'application/json', body: JSON.stringify({foo: 'bar', _attachments: {'message.txt': {follows: true, length: 18, 'content_type': 'text/plain' }}}) }, { body: 'I am an attachment' }, { body: fs.createReadStream('image.png') } ], // alternatively pass an object containing additional options multipart: { chunked: false, data: [ { 'content-type': 'application/json', body: JSON.stringify({foo: 'bar', _attachments: {'message.txt': {follows: true, length: 18, 'content_type': 'text/plain' }}}) }, { body: 'I am an attachment' } ] } }, function (error, response, body) { if (error) { return console.error('upload failed:', error); } console.log('Upload successful! Server responded with:', body); }) ``` [back to top](#table-of-contents) --- ## HTTP Authentication ```js request.get('http://some.server.com/').auth('username', 'password', false); // or request.get('http://some.server.com/', { 'auth': { 'user': 'username', 'pass': 'password', 'sendImmediately': false } }); // or request.get('http://some.server.com/').auth(null, null, true, 'bearerToken'); // or request.get('http://some.server.com/', { 'auth': { 'bearer': 'bearerToken' } }); ``` If passed as an option, `auth` should be a hash containing values: - `user` || `username` - `pass` || `password` - `sendImmediately` (optional) - `bearer` (optional) The method form takes parameters `auth(username, password, sendImmediately, bearer)`. `sendImmediately` defaults to `true`, which causes a basic or bearer authentication header to be sent. If `sendImmediately` is `false`, then `request` will retry with a proper authentication header after receiving a `401` response from the server (which must contain a `WWW-Authenticate` header indicating the required authentication method). Note that you can also specify basic authentication using the URL itself, as detailed in [RFC 1738](http://www.ietf.org/rfc/rfc1738.txt). Simply pass the `user:password` before the host with an `@` sign: ```js var username = 'username', password = 'password', url = 'http://' + username + ':' + password + '@some.server.com'; request({url: url}, function (error, response, body) { // Do more stuff with 'body' here }); ``` Digest authentication is supported, but it only works with `sendImmediately` set to `false`; otherwise `request` will send basic authentication on the initial request, which will probably cause the request to fail. Bearer authentication is supported, and is activated when the `bearer` value is available. The value may be either a `String` or a `Function` returning a `String`. Using a function to supply the bearer token is particularly useful if used in conjunction with `defaults` to allow a single function to supply the last known token at the time of sending a request, or to compute one on the fly. [back to top](#table-of-contents) --- ## Custom HTTP Headers HTTP Headers, such as `User-Agent`, can be set in the `options` object. In the example below, we call the github API to find out the number of stars and forks for the request repository. This requires a custom `User-Agent` header as well as https. ```js var request = require('request'); var options = { url: 'https://api.github.com/repos/request/request', headers: { 'User-Agent': 'request' } }; function callback(error, response, body) { if (!error && response.statusCode == 200) { var info = JSON.parse(body); console.log(info.stargazers_count + " Stars"); console.log(info.forks_count + " Forks"); } } request(options, callback); ``` [back to top](#table-of-contents) --- ## OAuth Signing [OAuth version 1.0](https://tools.ietf.org/html/rfc5849) is supported. The default signing algorithm is [HMAC-SHA1](https://tools.ietf.org/html/rfc5849#section-3.4.2): ```js // OAuth1.0 - 3-legged server side flow (Twitter example) // step 1 var qs = require('querystring') , oauth = { callback: 'http://mysite.com/callback/' , consumer_key: CONSUMER_KEY , consumer_secret: CONSUMER_SECRET } , url = 'https://api.twitter.com/oauth/request_token' ; request.post({url:url, oauth:oauth}, function (e, r, body) { // Ideally, you would take the body in the response // and construct a URL that a user clicks on (like a sign in button). // The verifier is only available in the response after a user has // verified with twitter that they are authorizing your app. // step 2 var req_data = qs.parse(body) var uri = 'https://api.twitter.com/oauth/authenticate' + '?' + qs.stringify({oauth_token: req_data.oauth_token}) // redirect the user to the authorize uri // step 3 // after the user is redirected back to your server var auth_data = qs.parse(body) , oauth = { consumer_key: CONSUMER_KEY , consumer_secret: CONSUMER_SECRET , token: auth_data.oauth_token , token_secret: req_data.oauth_token_secret , verifier: auth_data.oauth_verifier } , url = 'https://api.twitter.com/oauth/access_token' ; request.post({url:url, oauth:oauth}, function (e, r, body) { // ready to make signed requests on behalf of the user var perm_data = qs.parse(body) , oauth = { consumer_key: CONSUMER_KEY , consumer_secret: CONSUMER_SECRET , token: perm_data.oauth_token , token_secret: perm_data.oauth_token_secret } , url = 'https://api.twitter.com/1.1/users/show.json' , qs = { screen_name: perm_data.screen_name , user_id: perm_data.user_id } ; request.get({url:url, oauth:oauth, qs:qs, json:true}, function (e, r, user) { console.log(user) }) }) }) ``` For [RSA-SHA1 signing](https://tools.ietf.org/html/rfc5849#section-3.4.3), make the following changes to the OAuth options object: * Pass `signature_method : 'RSA-SHA1'` * Instead of `consumer_secret`, specify a `private_key` string in [PEM format](http://how2ssl.com/articles/working_with_pem_files/) For [PLAINTEXT signing](http://oauth.net/core/1.0/#anchor22), make the following changes to the OAuth options object: * Pass `signature_method : 'PLAINTEXT'` To send OAuth parameters via query params or in a post body as described in The [Consumer Request Parameters](http://oauth.net/core/1.0/#consumer_req_param) section of the oauth1 spec: * Pass `transport_method : 'query'` or `transport_method : 'body'` in the OAuth options object. * `transport_method` defaults to `'header'` To use [Request Body Hash](https://oauth.googlecode.com/svn/spec/ext/body_hash/1.0/oauth-bodyhash.html) you can either * Manually generate the body hash and pass it as a string `body_hash: '...'` * Automatically generate the body hash by passing `body_hash: true` [back to top](#table-of-contents) --- ## Proxies If you specify a `proxy` option, then the request (and any subsequent redirects) will be sent via a connection to the proxy server. If your endpoint is an `https` url, and you are using a proxy, then request will send a `CONNECT` request to the proxy server *first*, and then use the supplied connection to connect to the endpoint. That is, first it will make a request like: ``` HTTP/1.1 CONNECT endpoint-server.com:80 Host: proxy-server.com User-Agent: whatever user agent you specify ``` and then the proxy server make a TCP connection to `endpoint-server` on port `80`, and return a response that looks like: ``` HTTP/1.1 200 OK ``` At this point, the connection is left open, and the client is communicating directly with the `endpoint-server.com` machine. See [the wikipedia page on HTTP Tunneling](https://en.wikipedia.org/wiki/HTTP_tunnel) for more information. By default, when proxying `http` traffic, request will simply make a standard proxied `http` request. This is done by making the `url` section of the initial line of the request a fully qualified url to the endpoint. For example, it will make a single request that looks like: ``` HTTP/1.1 GET http://endpoint-server.com/some-url Host: proxy-server.com Other-Headers: all go here request body or whatever ``` Because a pure "http over http" tunnel offers no additional security or other features, it is generally simpler to go with a straightforward HTTP proxy in this case. However, if you would like to force a tunneling proxy, you may set the `tunnel` option to `true`. You can also make a standard proxied `http` request by explicitly setting `tunnel : false`, but **note that this will allow the proxy to see the traffic to/from the destination server**. If you are using a tunneling proxy, you may set the `proxyHeaderWhiteList` to share certain headers with the proxy. You can also set the `proxyHeaderExclusiveList` to share certain headers only with the proxy and not with destination host. By default, this set is: ``` accept accept-charset accept-encoding accept-language accept-ranges cache-control content-encoding content-language content-length content-location content-md5 content-range content-type connection date expect max-forwards pragma proxy-authorization referer te transfer-encoding user-agent via ``` Note that, when using a tunneling proxy, the `proxy-authorization` header and any headers from custom `proxyHeaderExclusiveList` are *never* sent to the endpoint server, but only to the proxy server. ### Controlling proxy behaviour using environment variables The following environment variables are respected by `request`: * `HTTP_PROXY` / `http_proxy` * `HTTPS_PROXY` / `https_proxy` * `NO_PROXY` / `no_proxy` When `HTTP_PROXY` / `http_proxy` are set, they will be used to proxy non-SSL requests that do not have an explicit `proxy` configuration option present. Similarly, `HTTPS_PROXY` / `https_proxy` will be respected for SSL requests that do not have an explicit `proxy` configuration option. It is valid to define a proxy in one of the environment variables, but then override it for a specific request, using the `proxy` configuration option. Furthermore, the `proxy` configuration option can be explicitly set to false / null to opt out of proxying altogether for that request. `request` is also aware of the `NO_PROXY`/`no_proxy` environment variables. These variables provide a granular way to opt out of proxying, on a per-host basis. It should contain a comma separated list of hosts to opt out of proxying. It is also possible to opt of proxying when a particular destination port is used. Finally, the variable may be set to `*` to opt out of the implicit proxy configuration of the other environment variables. Here's some examples of valid `no_proxy` values: * `google.com` - don't proxy HTTP/HTTPS requests to Google. * `google.com:443` - don't proxy HTTPS requests to Google, but *do* proxy HTTP requests to Google. * `google.com:443, yahoo.com:80` - don't proxy HTTPS requests to Google, and don't proxy HTTP requests to Yahoo! * `*` - ignore `https_proxy`/`http_proxy` environment variables altogether. [back to top](#table-of-contents) --- ## UNIX Domain Sockets `request` supports making requests to [UNIX Domain Sockets](https://en.wikipedia.org/wiki/Unix_domain_socket). To make one, use the following URL scheme: ```js /* Pattern */ 'http://unix:SOCKET:PATH' /* Example */ request.get('http://unix:/absolute/path/to/unix.socket:/request/path') ``` Note: The `SOCKET` path is assumed to be absolute to the root of the host file system. [back to top](#table-of-contents) --- ## TLS/SSL Protocol TLS/SSL Protocol options, such as `cert`, `key` and `passphrase`, can be set directly in `options` object, in the `agentOptions` property of the `options` object, or even in `https.globalAgent.options`. Keep in mind that, although `agentOptions` allows for a slightly wider range of configurations, the recommended way is via `options` object directly, as using `agentOptions` or `https.globalAgent.options` would not be applied in the same way in proxied environments (as data travels through a TLS connection instead of an http/https agent). ```js var fs = require('fs') , path = require('path') , certFile = path.resolve(__dirname, 'ssl/client.crt') , keyFile = path.resolve(__dirname, 'ssl/client.key') , caFile = path.resolve(__dirname, 'ssl/ca.cert.pem') , request = require('request'); var options = { url: 'https://api.some-server.com/', cert: fs.readFileSync(certFile), key: fs.readFileSync(keyFile), passphrase: 'password', ca: fs.readFileSync(caFile) }; request.get(options); ``` ### Using `options.agentOptions` In the example below, we call an API that requires client side SSL certificate (in PEM format) with passphrase protected private key (in PEM format) and disable the SSLv3 protocol: ```js var fs = require('fs') , path = require('path') , certFile = path.resolve(__dirname, 'ssl/client.crt') , keyFile = path.resolve(__dirname, 'ssl/client.key') , request = require('request'); var options = { url: 'https://api.some-server.com/', agentOptions: { cert: fs.readFileSync(certFile), key: fs.readFileSync(keyFile), // Or use `pfx` property replacing `cert` and `key` when using private key, certificate and CA certs in PFX or PKCS12 format: // pfx: fs.readFileSync(pfxFilePath), passphrase: 'password', securityOptions: 'SSL_OP_NO_SSLv3' } }; request.get(options); ``` It is able to force using SSLv3 only by specifying `secureProtocol`: ```js request.get({ url: 'https://api.some-server.com/', agentOptions: { secureProtocol: 'SSLv3_method' } }); ``` It is possible to accept other certificates than those signed by generally allowed Certificate Authorities (CAs). This can be useful, for example, when using self-signed certificates. To require a different root certificate, you can specify the signing CA by adding the contents of the CA's certificate file to the `agentOptions`. The certificate the domain presents must be signed by the root certificate specified: ```js request.get({ url: 'https://api.some-server.com/', agentOptions: { ca: fs.readFileSync('ca.cert.pem') } }); ``` [back to top](#table-of-contents) --- ## Support for HAR 1.2 The `options.har` property will override the values: `url`, `method`, `qs`, `headers`, `form`, `formData`, `body`, `json`, as well as construct multipart data and read files from disk when `request.postData.params[].fileName` is present without a matching `value`. A validation step will check if the HAR Request format matches the latest spec (v1.2) and will skip parsing if not matching. ```js var request = require('request') request({ // will be ignored method: 'GET', uri: 'http://www.google.com', // HTTP Archive Request Object har: { url: 'http://www.mockbin.com/har', method: 'POST', headers: [ { name: 'content-type', value: 'application/x-www-form-urlencoded' } ], postData: { mimeType: 'application/x-www-form-urlencoded', params: [ { name: 'foo', value: 'bar' }, { name: 'hello', value: 'world' } ] } } }) // a POST request will be sent to http://www.mockbin.com // with body an application/x-www-form-urlencoded body: // foo=bar&hello=world ``` [back to top](#table-of-contents) --- ## request(options, callback) The first argument can be either a `url` or an `options` object. The only required option is `uri`; all others are optional. - `uri` || `url` - fully qualified uri or a parsed url object from `url.parse()` - `baseUrl` - fully qualified uri string used as the base url. Most useful with `request.defaults`, for example when you want to do many requests to the same domain. If `baseUrl` is `https://example.com/api/`, then requesting `/end/point?test=true` will fetch `https://example.com/api/end/point?test=true`. When `baseUrl` is given, `uri` must also be a string. - `method` - http method (default: `"GET"`) - `headers` - http headers (default: `{}`) --- - `qs` - object containing querystring values to be appended to the `uri` - `qsParseOptions` - object containing options to pass to the [qs.parse](https://github.com/hapijs/qs#parsing-objects) method. Alternatively pass options to the [querystring.parse](https://nodejs.org/docs/v0.12.0/api/querystring.html#querystring_querystring_parse_str_sep_eq_options) method using this format `{sep:';', eq:':', options:{}}` - `qsStringifyOptions` - object containing options to pass to the [qs.stringify](https://github.com/hapijs/qs#stringifying) method. Alternatively pass options to the [querystring.stringify](https://nodejs.org/docs/v0.12.0/api/querystring.html#querystring_querystring_stringify_obj_sep_eq_options) method using this format `{sep:';', eq:':', options:{}}`. For example, to change the way arrays are converted to query strings using the `qs` module pass the `arrayFormat` option with one of `indices|brackets|repeat` - `useQuerystring` - if true, use `querystring` to stringify and parse querystrings, otherwise use `qs` (default: `false`). Set this option to `true` if you need arrays to be serialized as `foo=bar&foo=baz` instead of the default `foo[0]=bar&foo[1]=baz`. --- - `body` - entity body for PATCH, POST and PUT requests. Must be a `Buffer`, `String` or `ReadStream`. If `json` is `true`, then `body` must be a JSON-serializable object. - `form` - when passed an object or a querystring, this sets `body` to a querystring representation of value, and adds `Content-type: application/x-www-form-urlencoded` header. When passed no options, a `FormData` instance is returned (and is piped to request). See "Forms" section above. - `formData` - data to pass for a `multipart/form-data` request. See [Forms](#forms) section above. - `multipart` - array of objects which contain their own headers and `body` attributes. Sends a `multipart/related` request. See [Forms](#forms) section above. - Alternatively you can pass in an object `{chunked: false, data: []}` where `chunked` is used to specify whether the request is sent in [chunked transfer encoding](https://en.wikipedia.org/wiki/Chunked_transfer_encoding) In non-chunked requests, data items with body streams are not allowed. - `preambleCRLF` - append a newline/CRLF before the boundary of your `multipart/form-data` request. - `postambleCRLF` - append a newline/CRLF at the end of the boundary of your `multipart/form-data` request. - `json` - sets `body` to JSON representation of value and adds `Content-type: application/json` header. Additionally, parses the response body as JSON. - `jsonReviver` - a [reviver function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse) that will be passed to `JSON.parse()` when parsing a JSON response body. - `jsonReplacer` - a [replacer function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) that will be passed to `JSON.stringify()` when stringifying a JSON request body. --- - `auth` - a hash containing values `user` || `username`, `pass` || `password`, and `sendImmediately` (optional). See documentation above. - `oauth` - options for OAuth HMAC-SHA1 signing. See documentation above. - `hawk` - options for [Hawk signing](https://github.com/hueniverse/hawk). The `credentials` key must contain the necessary signing info, [see hawk docs for details](https://github.com/hueniverse/hawk#usage-example). - `aws` - `object` containing AWS signing information. Should have the properties `key`, `secret`, and optionally `session` (note that this only works for services that require session as part of the canonical string). Also requires the property `bucket`, unless you’re specifying your `bucket` as part of the path, or the request doesn’t use a bucket (i.e. GET Services). If you want to use AWS sign version 4 use the parameter `sign_version` with value `4` otherwise the default is version 2. If you are using SigV4, you can also include a `service` property that specifies the service name. **Note:** you need to `npm install aws4` first. - `httpSignature` - options for the [HTTP Signature Scheme](https://github.com/joyent/node-http-signature/blob/master/http_signing.md) using [Joyent's library](https://github.com/joyent/node-http-signature). The `keyId` and `key` properties must be specified. See the docs for other options. --- - `followRedirect` - follow HTTP 3xx responses as redirects (default: `true`). This property can also be implemented as function which gets `response` object as a single argument and should return `true` if redirects should continue or `false` otherwise. - `followAllRedirects` - follow non-GET HTTP 3xx responses as redirects (default: `false`) - `followOriginalHttpMethod` - by default we redirect to HTTP method GET. you can enable this property to redirect to the original HTTP method (default: `false`) - `maxRedirects` - the maximum number of redirects to follow (default: `10`) - `removeRefererHeader` - removes the referer header when a redirect happens (default: `false`). **Note:** if true, referer header set in the initial request is preserved during redirect chain. --- - `encoding` - encoding to be used on `setEncoding` of response data. If `null`, the `body` is returned as a `Buffer`. Anything else **(including the default value of `undefined`)** will be passed as the [encoding](http://nodejs.org/api/buffer.html#buffer_buffer) parameter to `toString()` (meaning this is effectively `utf8` by default). (**Note:** if you expect binary data, you should set `encoding: null`.) - `gzip` - if `true`, add an `Accept-Encoding` header to request compressed content encodings from the server (if not already present) and decode supported content encodings in the response. **Note:** Automatic decoding of the response content is performed on the body data returned through `request` (both through the `request` stream and passed to the callback function) but is not performed on the `response` stream (available from the `response` event) which is the unmodified `http.IncomingMessage` object which may contain compressed data. See example below. - `jar` - if `true`, remember cookies for future use (or define your custom cookie jar; see examples section) --- - `agent` - `http(s).Agent` instance to use - `agentClass` - alternatively specify your agent's class name - `agentOptions` - and pass its options. **Note:** for HTTPS see [tls API doc for TLS/SSL options](http://nodejs.org/api/tls.html#tls_tls_connect_options_callback) and the [documentation above](#using-optionsagentoptions). - `forever` - set to `true` to use the [forever-agent](https://github.com/request/forever-agent) **Note:** Defaults to `http(s).Agent({keepAlive:true})` in node 0.12+ - `pool` - an object describing which agents to use for the request. If this option is omitted the request will use the global agent (as long as your options allow for it). Otherwise, request will search the pool for your custom agent. If no custom agent is found, a new agent will be created and added to the pool. **Note:** `pool` is used only when the `agent` option is not specified. - A `maxSockets` property can also be provided on the `pool` object to set the max number of sockets for all agents created (ex: `pool: {maxSockets: Infinity}`). - Note that if you are sending multiple requests in a loop and creating multiple new `pool` objects, `maxSockets` will not work as intended. To work around this, either use [`request.defaults`](#requestdefaultsoptions) with your pool options or create the pool object with the `maxSockets` property outside of the loop. - `timeout` - integer containing the number of milliseconds to wait for a server to send response headers (and start the response body) before aborting the request. Note that if the underlying TCP connection cannot be established, the OS-wide TCP connection timeout will overrule the `timeout` option ([the default in Linux can be anywhere from 20-120 seconds][linux-timeout]). [linux-timeout]: http://www.sekuda.com/overriding_the_default_linux_kernel_20_second_tcp_socket_connect_timeout --- - `localAddress` - local interface to bind for network connections. - `proxy` - an HTTP proxy to be used. Supports proxy Auth with Basic Auth, identical to support for the `url` parameter (by embedding the auth info in the `uri`) - `strictSSL` - if `true`, requires SSL certificates be valid. **Note:** to use your own certificate authority, you need to specify an agent that was created with that CA as an option. - `tunnel` - controls the behavior of [HTTP `CONNECT` tunneling](https://en.wikipedia.org/wiki/HTTP_tunnel#HTTP_CONNECT_tunneling) as follows: - `undefined` (default) - `true` if the destination is `https`, `false` otherwise - `true` - always tunnel to the destination by making a `CONNECT` request to the proxy - `false` - request the destination as a `GET` request. - `proxyHeaderWhiteList` - a whitelist of headers to send to a tunneling proxy. - `proxyHeaderExclusiveList` - a whitelist of headers to send exclusively to a tunneling proxy and not to destination. --- - `time` - if `true`, the request-response cycle (including all redirects) is timed at millisecond resolution. When set, the following properties are added to the response object: - `elapsedTime` Duration of the entire request/response in milliseconds (*deprecated*). - `responseStartTime` Timestamp when the response began (in Unix Epoch milliseconds) (*deprecated*). - `timingStart` Timestamp of the start of the request (in Unix Epoch milliseconds). - `timings` Contains event timestamps in millisecond resolution relative to `timingStart`. If there were redirects, the properties reflect the timings of the final request in the redirect chain: - `socket` Relative timestamp when the [`http`](https://nodejs.org/api/http.html#http_event_socket) module's `socket` event fires. This happens when the socket is assigned to the request. - `lookup` Relative timestamp when the [`net`](https://nodejs.org/api/net.html#net_event_lookup) module's `lookup` event fires. This happens when the DNS has been resolved. - `connect`: Relative timestamp when the [`net`](https://nodejs.org/api/net.html#net_event_connect) module's `connect` event fires. This happens when the server acknowledges the TCP connection. - `response`: Relative timestamp when the [`http`](https://nodejs.org/api/http.html#http_event_response) module's `response` event fires. This happens when the first bytes are received from the server. - `end`: Relative timestamp when the last bytes of the response are received. - `timingPhases` Contains the durations of each request phase. If there were redirects, the properties reflect the timings of the final request in the redirect chain: - `wait`: Duration of socket initialization (`timings.socket`) - `dns`: Duration of DNS lookup (`timings.lookup` - `timings.socket`) - `tcp`: Duration of TCP connection (`timings.connect` - `timings.socket`) - `firstByte`: Duration of HTTP server response (`timings.response` - `timings.connect`) - `download`: Duration of HTTP download (`timings.end` - `timings.response`) - `total`: Duration entire HTTP round-trip (`timings.end`) - `har` - a [HAR 1.2 Request Object](http://www.softwareishard.com/blog/har-12-spec/#request), will be processed from HAR format into options overwriting matching values *(see the [HAR 1.2 section](#support-for-har-1.2) for details)* - `callback` - alternatively pass the request's callback in the options object The callback argument gets 3 arguments: 1. An `error` when applicable (usually from [`http.ClientRequest`](http://nodejs.org/api/http.html#http_class_http_clientrequest) object) 2. An [`http.IncomingMessage`](https://nodejs.org/api/http.html#http_class_http_incomingmessage) object (Response object) 3. The third is the `response` body (`String` or `Buffer`, or JSON object if the `json` option is supplied) [back to top](#table-of-contents) --- ## Convenience methods There are also shorthand methods for different HTTP METHODs and some other conveniences. ### request.defaults(options) This method **returns a wrapper** around the normal request API that defaults to whatever options you pass to it. **Note:** `request.defaults()` **does not** modify the global request API; instead, it **returns a wrapper** that has your default settings applied to it. **Note:** You can call `.defaults()` on the wrapper that is returned from `request.defaults` to add/override defaults that were previously defaulted. For example: ```js //requests using baseRequest() will set the 'x-token' header var baseRequest = request.defaults({ headers: {'x-token': 'my-token'} }) //requests using specialRequest() will include the 'x-token' header set in //baseRequest and will also include the 'special' header var specialRequest = baseRequest.defaults({ headers: {special: 'special value'} }) ``` ### request.METHOD() These HTTP method convenience functions act just like `request()` but with a default method already set for you: - *request.get()*: Defaults to `method: "GET"`. - *request.post()*: Defaults to `method: "POST"`. - *request.put()*: Defaults to `method: "PUT"`. - *request.patch()*: Defaults to `method: "PATCH"`. - *request.del() / request.delete()*: Defaults to `method: "DELETE"`. - *request.head()*: Defaults to `method: "HEAD"`. - *request.options()*: Defaults to `method: "OPTIONS"`. ### request.cookie() Function that creates a new cookie. ```js request.cookie('key1=value1') ``` ### request.jar() Function that creates a new cookie jar. ```js request.jar() ``` [back to top](#table-of-contents) --- ## Debugging There are at least three ways to debug the operation of `request`: 1. Launch the node process like `NODE_DEBUG=request node script.js` (`lib,request,otherlib` works too). 2. Set `require('request').debug = true` at any time (this does the same thing as #1). 3. Use the [request-debug module](https://github.com/request/request-debug) to view request and response headers and bodies. [back to top](#table-of-contents) --- ## Timeouts Most requests to external servers should have a timeout attached, in case the server is not responding in a timely manner. Without a timeout, your code may have a socket open/consume resources for minutes or more. There are two main types of timeouts: **connection timeouts** and **read timeouts**. A connect timeout occurs if the timeout is hit while your client is attempting to establish a connection to a remote machine (corresponding to the [connect() call][connect] on the socket). A read timeout occurs any time the server is too slow to send back a part of the response. These two situations have widely different implications for what went wrong with the request, so it's useful to be able to distinguish them. You can detect timeout errors by checking `err.code` for an 'ETIMEDOUT' value. Further, you can detect whether the timeout was a connection timeout by checking if the `err.connect` property is set to `true`. ```js request.get('http://10.255.255.1', {timeout: 1500}, function(err) { console.log(err.code === 'ETIMEDOUT'); // Set to `true` if the timeout was a connection timeout, `false` or // `undefined` otherwise. console.log(err.connect === true); process.exit(0); }); ``` [connect]: http://linux.die.net/man/2/connect ## Examples: ```js var request = require('request') , rand = Math.floor(Math.random()*100000000).toString() ; request( { method: 'PUT' , uri: 'http://mikeal.iriscouch.com/testjs/' + rand , multipart: [ { 'content-type': 'application/json' , body: JSON.stringify({foo: 'bar', _attachments: {'message.txt': {follows: true, length: 18, 'content_type': 'text/plain' }}}) } , { body: 'I am an attachment' } ] } , function (error, response, body) { if(response.statusCode == 201){ console.log('document saved as: http://mikeal.iriscouch.com/testjs/'+ rand) } else { console.log('error: '+ response.statusCode) console.log(body) } } ) ``` For backwards-compatibility, response compression is not supported by default. To accept gzip-compressed responses, set the `gzip` option to `true`. Note that the body data passed through `request` is automatically decompressed while the response object is unmodified and will contain compressed data if the server sent a compressed response. ```js var request = require('request') request( { method: 'GET' , uri: 'http://www.google.com' , gzip: true } , function (error, response, body) { // body is the decompressed response body console.log('server encoded the data as: ' + (response.headers['content-encoding'] || 'identity')) console.log('the decoded data is: ' + body) } ) .on('data', function(data) { // decompressed data as it is received console.log('decoded chunk: ' + data) }) .on('response', function(response) { // unmodified http.IncomingMessage object response.on('data', function(data) { // compressed data as it is received console.log('received ' + data.length + ' bytes of compressed data') }) }) ``` Cookies are disabled by default (else, they would be used in subsequent requests). To enable cookies, set `jar` to `true` (either in `defaults` or `options`). ```js var request = request.defaults({jar: true}) request('http://www.google.com', function () { request('http://images.google.com') }) ``` To use a custom cookie jar (instead of `request`’s global cookie jar), set `jar` to an instance of `request.jar()` (either in `defaults` or `options`) ```js var j = request.jar() var request = request.defaults({jar:j}) request('http://www.google.com', function () { request('http://images.google.com') }) ``` OR ```js var j = request.jar(); var cookie = request.cookie('key1=value1'); var url = 'http://www.google.com'; j.setCookie(cookie, url); request({url: url, jar: j}, function () { request('http://images.google.com') }) ``` To use a custom cookie store (such as a [`FileCookieStore`](https://github.com/mitsuru/tough-cookie-filestore) which supports saving to and restoring from JSON files), pass it as a parameter to `request.jar()`: ```js var FileCookieStore = require('tough-cookie-filestore'); // NOTE - currently the 'cookies.json' file must already exist! var j = request.jar(new FileCookieStore('cookies.json')); request = request.defaults({ jar : j }) request('http://www.google.com', function() { request('http://images.google.com') }) ``` The cookie store must be a [`tough-cookie`](https://github.com/SalesforceEng/tough-cookie) store and it must support synchronous operations; see the [`CookieStore` API docs](https://github.com/SalesforceEng/tough-cookie#cookiestore-api) for details. To inspect your cookie jar after a request: ```js var j = request.jar() request({url: 'http://www.google.com', jar: j}, function () { var cookie_string = j.getCookieString(url); // "key1=value1; key2=value2; ..." var cookies = j.getCookies(url); // [{key: 'key1', value: 'value1', domain: "www.google.com", ...}, ...] }) ``` [back to top](#table-of-contents) request-2.88.1/codecov.yml000066400000000000000000000000201333333633500154440ustar00rootroot00000000000000 comment: false request-2.88.1/disabled.appveyor.yml000066400000000000000000000016541333333633500174530ustar00rootroot00000000000000# http://www.appveyor.com/docs/appveyor-yml # Fix line endings in Windows. (runs before repo cloning) init: - git config --global core.autocrlf input # Test against these versions of Node.js. environment: matrix: - nodejs_version: "0.10" - nodejs_version: "0.8" - nodejs_version: "0.11" # Allow failing jobs for bleeding-edge Node.js versions. matrix: allow_failures: - nodejs_version: "0.11" # Install scripts. (runs after repo cloning) install: # Get the latest stable version of Node 0.STABLE.latest - ps: Update-NodeJsInstallation (Get-NodeJsLatestBuild $env:nodejs_version) # Typical npm stuff. - npm install # Post-install test scripts. test_script: # Output useful info for debugging. - ps: "npm test # PowerShell" # Pass comment to PS for easier debugging - cmd: npm test # Don't actually build. build: off # Set build version format here instead of in the admin panel. version: "{build}" request-2.88.1/examples/000077500000000000000000000000001333333633500151255ustar00rootroot00000000000000request-2.88.1/examples/README.md000066400000000000000000000055631333333633500164150ustar00rootroot00000000000000 # Authentication ## OAuth ### OAuth1.0 Refresh Token - http://oauth.googlecode.com/svn/spec/ext/session/1.0/drafts/1/spec.html#anchor4 - https://developer.yahoo.com/oauth/guide/oauth-refreshaccesstoken.html ```js request.post('https://api.login.yahoo.com/oauth/v2/get_token', { oauth: { consumer_key: '...', consumer_secret: '...', token: '...', token_secret: '...', session_handle: '...' } }, function (err, res, body) { var result = require('querystring').parse(body) // assert.equal(typeof result, 'object') }) ``` ### OAuth2 Refresh Token - https://tools.ietf.org/html/draft-ietf-oauth-v2-31#section-6 ```js request.post('https://accounts.google.com/o/oauth2/token', { form: { grant_type: 'refresh_token', client_id: '...', client_secret: '...', refresh_token: '...' }, json: true }, function (err, res, body) { // assert.equal(typeof body, 'object') }) ``` # Multipart ## multipart/form-data ### Flickr Image Upload - https://www.flickr.com/services/api/upload.api.html ```js request.post('https://up.flickr.com/services/upload', { oauth: { consumer_key: '...', consumer_secret: '...', token: '...', token_secret: '...' }, // all meta data should be included here for proper signing qs: { title: 'My cat is awesome', description: 'Sent on ' + new Date(), is_public: 1 }, // again the same meta data + the actual photo formData: { title: 'My cat is awesome', description: 'Sent on ' + new Date(), is_public: 1, photo:fs.createReadStream('cat.png') }, json: true }, function (err, res, body) { // assert.equal(typeof body, 'object') }) ``` # Streams ## `POST` data Use Request as a Writable stream to easily `POST` Readable streams (like files, other HTTP requests, or otherwise). TL;DR: Pipe a Readable Stream onto Request via: ``` READABLE.pipe(request.post(URL)); ``` A more detailed example: ```js var fs = require('fs') , path = require('path') , http = require('http') , request = require('request') , TMP_FILE_PATH = path.join(path.sep, 'tmp', 'foo') ; // write a temporary file: fs.writeFileSync(TMP_FILE_PATH, 'foo bar baz quk\n'); http.createServer(function(req, res) { console.log('the server is receiving data!\n'); req .on('end', res.end.bind(res)) .pipe(process.stdout) ; }).listen(3000).unref(); fs.createReadStream(TMP_FILE_PATH) .pipe(request.post('http://127.0.0.1:3000')) ; ``` # Proxys Run tor on the terminal and try the following. (Needs `socks5-http-client` to connect to tor) ```js var request = require('../index.js'); var Agent = require('socks5-http-client/lib/Agent'); request.get({ url: 'http://www.tenreads.io', agentClass: Agent, agentOptions: { socksHost: 'localhost', // Defaults to 'localhost'. socksPort: 9050 // Defaults to 1080. } }, function (err, res) { console.log(res.body); }); ``` request-2.88.1/index.js000077500000000000000000000076061333333633500147700ustar00rootroot00000000000000// Copyright 2010-2012 Mikeal Rogers // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. 'use strict' var extend = require('extend') var cookies = require('./lib/cookies') var helpers = require('./lib/helpers') var paramsHaveRequestBody = helpers.paramsHaveRequestBody // organize params for patch, post, put, head, del function initParams (uri, options, callback) { if (typeof options === 'function') { callback = options } var params = {} if (typeof options === 'object') { extend(params, options, {uri: uri}) } else if (typeof uri === 'string') { extend(params, {uri: uri}) } else { extend(params, uri) } params.callback = callback || params.callback return params } function request (uri, options, callback) { if (typeof uri === 'undefined') { throw new Error('undefined is not a valid uri or options object.') } var params = initParams(uri, options, callback) if (params.method === 'HEAD' && paramsHaveRequestBody(params)) { throw new Error('HTTP HEAD requests MUST NOT include a request body.') } return new request.Request(params) } function verbFunc (verb) { var method = verb.toUpperCase() return function (uri, options, callback) { var params = initParams(uri, options, callback) params.method = method return request(params, params.callback) } } // define like this to please codeintel/intellisense IDEs request.get = verbFunc('get') request.head = verbFunc('head') request.options = verbFunc('options') request.post = verbFunc('post') request.put = verbFunc('put') request.patch = verbFunc('patch') request.del = verbFunc('delete') request['delete'] = verbFunc('delete') request.jar = function (store) { return cookies.jar(store) } request.cookie = function (str) { return cookies.parse(str) } function wrapRequestMethod (method, options, requester, verb) { return function (uri, opts, callback) { var params = initParams(uri, opts, callback) var target = {} extend(true, target, options, params) target.pool = params.pool || options.pool if (verb) { target.method = verb.toUpperCase() } if (typeof requester === 'function') { method = requester } return method(target, target.callback) } } request.defaults = function (options, requester) { var self = this options = options || {} if (typeof options === 'function') { requester = options options = {} } var defaults = wrapRequestMethod(self, options, requester) var verbs = ['get', 'head', 'post', 'put', 'patch', 'del', 'delete'] verbs.forEach(function (verb) { defaults[verb] = wrapRequestMethod(self[verb], options, requester, verb) }) defaults.cookie = wrapRequestMethod(self.cookie, options, requester) defaults.jar = self.jar defaults.defaults = self.defaults return defaults } request.forever = function (agentOptions, optionsArg) { var options = {} if (optionsArg) { extend(options, optionsArg) } if (agentOptions) { options.agentOptions = agentOptions } options.forever = true return request.defaults(options) } // Exports module.exports = request request.Request = require('./request') request.initParams = initParams // Backwards compatibility for request.debug Object.defineProperty(request, 'debug', { enumerable: true, get: function () { return request.Request.debug }, set: function (debug) { request.Request.debug = debug } }) request-2.88.1/lib/000077500000000000000000000000001333333633500140555ustar00rootroot00000000000000request-2.88.1/lib/auth.js000066400000000000000000000112461333333633500153600ustar00rootroot00000000000000'use strict' var caseless = require('caseless') var uuid = require('uuid/v4') var helpers = require('./helpers') var md5 = helpers.md5 var toBase64 = helpers.toBase64 function Auth (request) { // define all public properties here this.request = request this.hasAuth = false this.sentAuth = false this.bearerToken = null this.user = null this.pass = null } Auth.prototype.basic = function (user, pass, sendImmediately) { var self = this if (typeof user !== 'string' || (pass !== undefined && typeof pass !== 'string')) { self.request.emit('error', new Error('auth() received invalid user or password')) } self.user = user self.pass = pass self.hasAuth = true var header = user + ':' + (pass || '') if (sendImmediately || typeof sendImmediately === 'undefined') { var authHeader = 'Basic ' + toBase64(header) self.sentAuth = true return authHeader } } Auth.prototype.bearer = function (bearer, sendImmediately) { var self = this self.bearerToken = bearer self.hasAuth = true if (sendImmediately || typeof sendImmediately === 'undefined') { if (typeof bearer === 'function') { bearer = bearer() } var authHeader = 'Bearer ' + (bearer || '') self.sentAuth = true return authHeader } } Auth.prototype.digest = function (method, path, authHeader) { // TODO: More complete implementation of RFC 2617. // - handle challenge.domain // - support qop="auth-int" only // - handle Authentication-Info (not necessarily?) // - check challenge.stale (not necessarily?) // - increase nc (not necessarily?) // For reference: // http://tools.ietf.org/html/rfc2617#section-3 // https://github.com/bagder/curl/blob/master/lib/http_digest.c var self = this var challenge = {} var re = /([a-z0-9_-]+)=(?:"([^"]+)"|([a-z0-9_-]+))/gi for (;;) { var match = re.exec(authHeader) if (!match) { break } challenge[match[1]] = match[2] || match[3] } /** * RFC 2617: handle both MD5 and MD5-sess algorithms. * * If the algorithm directive's value is "MD5" or unspecified, then HA1 is * HA1=MD5(username:realm:password) * If the algorithm directive's value is "MD5-sess", then HA1 is * HA1=MD5(MD5(username:realm:password):nonce:cnonce) */ var ha1Compute = function (algorithm, user, realm, pass, nonce, cnonce) { var ha1 = md5(user + ':' + realm + ':' + pass) if (algorithm && algorithm.toLowerCase() === 'md5-sess') { return md5(ha1 + ':' + nonce + ':' + cnonce) } else { return ha1 } } var qop = /(^|,)\s*auth\s*($|,)/.test(challenge.qop) && 'auth' var nc = qop && '00000001' var cnonce = qop && uuid().replace(/-/g, '') var ha1 = ha1Compute(challenge.algorithm, self.user, challenge.realm, self.pass, challenge.nonce, cnonce) var ha2 = md5(method + ':' + path) var digestResponse = qop ? md5(ha1 + ':' + challenge.nonce + ':' + nc + ':' + cnonce + ':' + qop + ':' + ha2) : md5(ha1 + ':' + challenge.nonce + ':' + ha2) var authValues = { username: self.user, realm: challenge.realm, nonce: challenge.nonce, uri: path, qop: qop, response: digestResponse, nc: nc, cnonce: cnonce, algorithm: challenge.algorithm, opaque: challenge.opaque } authHeader = [] for (var k in authValues) { if (authValues[k]) { if (k === 'qop' || k === 'nc' || k === 'algorithm') { authHeader.push(k + '=' + authValues[k]) } else { authHeader.push(k + '="' + authValues[k] + '"') } } } authHeader = 'Digest ' + authHeader.join(', ') self.sentAuth = true return authHeader } Auth.prototype.onRequest = function (user, pass, sendImmediately, bearer) { var self = this var request = self.request var authHeader if (bearer === undefined && user === undefined) { self.request.emit('error', new Error('no auth mechanism defined')) } else if (bearer !== undefined) { authHeader = self.bearer(bearer, sendImmediately) } else { authHeader = self.basic(user, pass, sendImmediately) } if (authHeader) { request.setHeader('authorization', authHeader) } } Auth.prototype.onResponse = function (response) { var self = this var request = self.request if (!self.hasAuth || self.sentAuth) { return null } var c = caseless(response.headers) var authHeader = c.get('www-authenticate') var authVerb = authHeader && authHeader.split(' ')[0].toLowerCase() request.debug('reauth', authVerb) switch (authVerb) { case 'basic': return self.basic(self.user, self.pass, true) case 'bearer': return self.bearer(self.bearerToken, true) case 'digest': return self.digest(request.method, request.path, authHeader) } } exports.Auth = Auth request-2.88.1/lib/cookies.js000066400000000000000000000017161333333633500160540ustar00rootroot00000000000000'use strict' var tough = require('tough-cookie') var Cookie = tough.Cookie var CookieJar = tough.CookieJar exports.parse = function (str) { if (str && str.uri) { str = str.uri } if (typeof str !== 'string') { throw new Error('The cookie function only accepts STRING as param') } return Cookie.parse(str, {loose: true}) } // Adapt the sometimes-Async api of tough.CookieJar to our requirements function RequestJar (store) { var self = this self._jar = new CookieJar(store, {looseMode: true}) } RequestJar.prototype.setCookie = function (cookieOrStr, uri, options) { var self = this return self._jar.setCookieSync(cookieOrStr, uri, options || {}) } RequestJar.prototype.getCookieString = function (uri) { var self = this return self._jar.getCookieStringSync(uri) } RequestJar.prototype.getCookies = function (uri) { var self = this return self._jar.getCookiesSync(uri) } exports.jar = function (store) { return new RequestJar(store) } request-2.88.1/lib/getProxyFromURI.js000066400000000000000000000043011333333633500174360ustar00rootroot00000000000000'use strict' function formatHostname (hostname) { // canonicalize the hostname, so that 'oogle.com' won't match 'google.com' return hostname.replace(/^\.*/, '.').toLowerCase() } function parseNoProxyZone (zone) { zone = zone.trim().toLowerCase() var zoneParts = zone.split(':', 2) var zoneHost = formatHostname(zoneParts[0]) var zonePort = zoneParts[1] var hasPort = zone.indexOf(':') > -1 return {hostname: zoneHost, port: zonePort, hasPort: hasPort} } function uriInNoProxy (uri, noProxy) { var port = uri.port || (uri.protocol === 'https:' ? '443' : '80') var hostname = formatHostname(uri.hostname) var noProxyList = noProxy.split(',') // iterate through the noProxyList until it finds a match. return noProxyList.map(parseNoProxyZone).some(function (noProxyZone) { var isMatchedAt = hostname.indexOf(noProxyZone.hostname) var hostnameMatched = ( isMatchedAt > -1 && (isMatchedAt === hostname.length - noProxyZone.hostname.length) ) if (noProxyZone.hasPort) { return (port === noProxyZone.port) && hostnameMatched } return hostnameMatched }) } function getProxyFromURI (uri) { // Decide the proper request proxy to use based on the request URI object and the // environmental variables (NO_PROXY, HTTP_PROXY, etc.) // respect NO_PROXY environment variables (see: http://lynx.isc.org/current/breakout/lynx_help/keystrokes/environments.html) var noProxy = process.env.NO_PROXY || process.env.no_proxy || '' // if the noProxy is a wildcard then return null if (noProxy === '*') { return null } // if the noProxy is not empty and the uri is found return null if (noProxy !== '' && uriInNoProxy(uri, noProxy)) { return null } // Check for HTTP or HTTPS Proxy in environment Else default to null if (uri.protocol === 'http:') { return process.env.HTTP_PROXY || process.env.http_proxy || null } if (uri.protocol === 'https:') { return process.env.HTTPS_PROXY || process.env.https_proxy || process.env.HTTP_PROXY || process.env.http_proxy || null } // if none of that works, return null // (What uri protocol are you using then?) return null } module.exports = getProxyFromURI request-2.88.1/lib/har.js000066400000000000000000000113061333333633500151660ustar00rootroot00000000000000'use strict' var fs = require('fs') var qs = require('querystring') var validate = require('har-validator') var extend = require('extend') function Har (request) { this.request = request } Har.prototype.reducer = function (obj, pair) { // new property ? if (obj[pair.name] === undefined) { obj[pair.name] = pair.value return obj } // existing? convert to array var arr = [ obj[pair.name], pair.value ] obj[pair.name] = arr return obj } Har.prototype.prep = function (data) { // construct utility properties data.queryObj = {} data.headersObj = {} data.postData.jsonObj = false data.postData.paramsObj = false // construct query objects if (data.queryString && data.queryString.length) { data.queryObj = data.queryString.reduce(this.reducer, {}) } // construct headers objects if (data.headers && data.headers.length) { // loweCase header keys data.headersObj = data.headers.reduceRight(function (headers, header) { headers[header.name] = header.value return headers }, {}) } // construct Cookie header if (data.cookies && data.cookies.length) { var cookies = data.cookies.map(function (cookie) { return cookie.name + '=' + cookie.value }) if (cookies.length) { data.headersObj.cookie = cookies.join('; ') } } // prep body function some (arr) { return arr.some(function (type) { return data.postData.mimeType.indexOf(type) === 0 }) } if (some([ 'multipart/mixed', 'multipart/related', 'multipart/form-data', 'multipart/alternative'])) { // reset values data.postData.mimeType = 'multipart/form-data' } else if (some([ 'application/x-www-form-urlencoded'])) { if (!data.postData.params) { data.postData.text = '' } else { data.postData.paramsObj = data.postData.params.reduce(this.reducer, {}) // always overwrite data.postData.text = qs.stringify(data.postData.paramsObj) } } else if (some([ 'text/json', 'text/x-json', 'application/json', 'application/x-json'])) { data.postData.mimeType = 'application/json' if (data.postData.text) { try { data.postData.jsonObj = JSON.parse(data.postData.text) } catch (e) { this.request.debug(e) // force back to text/plain data.postData.mimeType = 'text/plain' } } } return data } Har.prototype.options = function (options) { // skip if no har property defined if (!options.har) { return options } var har = {} extend(har, options.har) // only process the first entry if (har.log && har.log.entries) { har = har.log.entries[0] } // add optional properties to make validation successful har.url = har.url || options.url || options.uri || options.baseUrl || '/' har.httpVersion = har.httpVersion || 'HTTP/1.1' har.queryString = har.queryString || [] har.headers = har.headers || [] har.cookies = har.cookies || [] har.postData = har.postData || {} har.postData.mimeType = har.postData.mimeType || 'application/octet-stream' har.bodySize = 0 har.headersSize = 0 har.postData.size = 0 if (!validate.request(har)) { return options } // clean up and get some utility properties var req = this.prep(har) // construct new options if (req.url) { options.url = req.url } if (req.method) { options.method = req.method } if (Object.keys(req.queryObj).length) { options.qs = req.queryObj } if (Object.keys(req.headersObj).length) { options.headers = req.headersObj } function test (type) { return req.postData.mimeType.indexOf(type) === 0 } if (test('application/x-www-form-urlencoded')) { options.form = req.postData.paramsObj } else if (test('application/json')) { if (req.postData.jsonObj) { options.body = req.postData.jsonObj options.json = true } } else if (test('multipart/form-data')) { options.formData = {} req.postData.params.forEach(function (param) { var attachment = {} if (!param.fileName && !param.fileName && !param.contentType) { options.formData[param.name] = param.value return } // attempt to read from disk! if (param.fileName && !param.value) { attachment.value = fs.createReadStream(param.fileName) } else if (param.value) { attachment.value = param.value } if (param.fileName) { attachment.options = { filename: param.fileName, contentType: param.contentType ? param.contentType : null } } options.formData[param.name] = attachment }) } else { if (req.postData.text) { options.body = req.postData.text } } return options } exports.Har = Har request-2.88.1/lib/hawk.js000066400000000000000000000052761333333633500153570ustar00rootroot00000000000000'use strict' var crypto = require('crypto') function randomString (size) { var bits = (size + 1) * 6 var buffer = crypto.randomBytes(Math.ceil(bits / 8)) var string = buffer.toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '') return string.slice(0, size) } function calculatePayloadHash (payload, algorithm, contentType) { var hash = crypto.createHash(algorithm) hash.update('hawk.1.payload\n') hash.update((contentType ? contentType.split(';')[0].trim().toLowerCase() : '') + '\n') hash.update(payload || '') hash.update('\n') return hash.digest('base64') } exports.calculateMac = function (credentials, opts) { var normalized = 'hawk.1.header\n' + opts.ts + '\n' + opts.nonce + '\n' + (opts.method || '').toUpperCase() + '\n' + opts.resource + '\n' + opts.host.toLowerCase() + '\n' + opts.port + '\n' + (opts.hash || '') + '\n' if (opts.ext) { normalized = normalized + opts.ext.replace('\\', '\\\\').replace('\n', '\\n') } normalized = normalized + '\n' if (opts.app) { normalized = normalized + opts.app + '\n' + (opts.dlg || '') + '\n' } var hmac = crypto.createHmac(credentials.algorithm, credentials.key).update(normalized) var digest = hmac.digest('base64') return digest } exports.header = function (uri, method, opts) { var timestamp = opts.timestamp || Math.floor((Date.now() + (opts.localtimeOffsetMsec || 0)) / 1000) var credentials = opts.credentials if (!credentials || !credentials.id || !credentials.key || !credentials.algorithm) { return '' } if (['sha1', 'sha256'].indexOf(credentials.algorithm) === -1) { return '' } var artifacts = { ts: timestamp, nonce: opts.nonce || randomString(6), method: method, resource: uri.pathname + (uri.search || ''), host: uri.hostname, port: uri.port || (uri.protocol === 'http:' ? 80 : 443), hash: opts.hash, ext: opts.ext, app: opts.app, dlg: opts.dlg } if (!artifacts.hash && (opts.payload || opts.payload === '')) { artifacts.hash = calculatePayloadHash(opts.payload, credentials.algorithm, opts.contentType) } var mac = exports.calculateMac(credentials, artifacts) var hasExt = artifacts.ext !== null && artifacts.ext !== undefined && artifacts.ext !== '' var header = 'Hawk id="' + credentials.id + '", ts="' + artifacts.ts + '", nonce="' + artifacts.nonce + (artifacts.hash ? '", hash="' + artifacts.hash : '') + (hasExt ? '", ext="' + artifacts.ext.replace(/\\/g, '\\\\').replace(/"/g, '\\"') : '') + '", mac="' + mac + '"' if (artifacts.app) { header = header + ', app="' + artifacts.app + (artifacts.dlg ? '", dlg="' + artifacts.dlg : '') + '"' } return header } request-2.88.1/lib/helpers.js000066400000000000000000000026031333333633500160560ustar00rootroot00000000000000'use strict' var jsonSafeStringify = require('json-stringify-safe') var crypto = require('crypto') var Buffer = require('safe-buffer').Buffer var defer = typeof setImmediate === 'undefined' ? process.nextTick : setImmediate function paramsHaveRequestBody (params) { return ( params.body || params.requestBodyStream || (params.json && typeof params.json !== 'boolean') || params.multipart ) } function safeStringify (obj, replacer) { var ret try { ret = JSON.stringify(obj, replacer) } catch (e) { ret = jsonSafeStringify(obj, replacer) } return ret } function md5 (str) { return crypto.createHash('md5').update(str).digest('hex') } function isReadStream (rs) { return rs.readable && rs.path && rs.mode } function toBase64 (str) { return Buffer.from(str || '', 'utf8').toString('base64') } function copy (obj) { var o = {} Object.keys(obj).forEach(function (i) { o[i] = obj[i] }) return o } function version () { var numbers = process.version.replace('v', '').split('.') return { major: parseInt(numbers[0], 10), minor: parseInt(numbers[1], 10), patch: parseInt(numbers[2], 10) } } exports.paramsHaveRequestBody = paramsHaveRequestBody exports.safeStringify = safeStringify exports.md5 = md5 exports.isReadStream = isReadStream exports.toBase64 = toBase64 exports.copy = copy exports.version = version exports.defer = defer request-2.88.1/lib/multipart.js000066400000000000000000000051621333333633500164400ustar00rootroot00000000000000'use strict' var uuid = require('uuid/v4') var CombinedStream = require('combined-stream') var isstream = require('isstream') var Buffer = require('safe-buffer').Buffer function Multipart (request) { this.request = request this.boundary = uuid() this.chunked = false this.body = null } Multipart.prototype.isChunked = function (options) { var self = this var chunked = false var parts = options.data || options if (!parts.forEach) { self.request.emit('error', new Error('Argument error, options.multipart.')) } if (options.chunked !== undefined) { chunked = options.chunked } if (self.request.getHeader('transfer-encoding') === 'chunked') { chunked = true } if (!chunked) { parts.forEach(function (part) { if (typeof part.body === 'undefined') { self.request.emit('error', new Error('Body attribute missing in multipart.')) } if (isstream(part.body)) { chunked = true } }) } return chunked } Multipart.prototype.setHeaders = function (chunked) { var self = this if (chunked && !self.request.hasHeader('transfer-encoding')) { self.request.setHeader('transfer-encoding', 'chunked') } var header = self.request.getHeader('content-type') if (!header || header.indexOf('multipart') === -1) { self.request.setHeader('content-type', 'multipart/related; boundary=' + self.boundary) } else { if (header.indexOf('boundary') !== -1) { self.boundary = header.replace(/.*boundary=([^\s;]+).*/, '$1') } else { self.request.setHeader('content-type', header + '; boundary=' + self.boundary) } } } Multipart.prototype.build = function (parts, chunked) { var self = this var body = chunked ? new CombinedStream() : [] function add (part) { if (typeof part === 'number') { part = part.toString() } return chunked ? body.append(part) : body.push(Buffer.from(part)) } if (self.request.preambleCRLF) { add('\r\n') } parts.forEach(function (part) { var preamble = '--' + self.boundary + '\r\n' Object.keys(part).forEach(function (key) { if (key === 'body') { return } preamble += key + ': ' + part[key] + '\r\n' }) preamble += '\r\n' add(preamble) add(part.body) add('\r\n') }) add('--' + self.boundary + '--') if (self.request.postambleCRLF) { add('\r\n') } return body } Multipart.prototype.onRequest = function (options) { var self = this var chunked = self.isChunked(options) var parts = options.data || options self.setHeaders(chunked) self.chunked = chunked self.body = self.build(parts, chunked) } exports.Multipart = Multipart request-2.88.1/lib/oauth.js000066400000000000000000000100501333333633500155270ustar00rootroot00000000000000'use strict' var url = require('url') var qs = require('qs') var caseless = require('caseless') var uuid = require('uuid/v4') var oauth = require('oauth-sign') var crypto = require('crypto') var Buffer = require('safe-buffer').Buffer function OAuth (request) { this.request = request this.params = null } OAuth.prototype.buildParams = function (_oauth, uri, method, query, form, qsLib) { var oa = {} for (var i in _oauth) { oa['oauth_' + i] = _oauth[i] } if (!oa.oauth_version) { oa.oauth_version = '1.0' } if (!oa.oauth_timestamp) { oa.oauth_timestamp = Math.floor(Date.now() / 1000).toString() } if (!oa.oauth_nonce) { oa.oauth_nonce = uuid().replace(/-/g, '') } if (!oa.oauth_signature_method) { oa.oauth_signature_method = 'HMAC-SHA1' } var consumer_secret_or_private_key = oa.oauth_consumer_secret || oa.oauth_private_key // eslint-disable-line camelcase delete oa.oauth_consumer_secret delete oa.oauth_private_key var token_secret = oa.oauth_token_secret // eslint-disable-line camelcase delete oa.oauth_token_secret var realm = oa.oauth_realm delete oa.oauth_realm delete oa.oauth_transport_method var baseurl = uri.protocol + '//' + uri.host + uri.pathname var params = qsLib.parse([].concat(query, form, qsLib.stringify(oa)).join('&')) oa.oauth_signature = oauth.sign( oa.oauth_signature_method, method, baseurl, params, consumer_secret_or_private_key, // eslint-disable-line camelcase token_secret // eslint-disable-line camelcase ) if (realm) { oa.realm = realm } return oa } OAuth.prototype.buildBodyHash = function (_oauth, body) { if (['HMAC-SHA1', 'RSA-SHA1'].indexOf(_oauth.signature_method || 'HMAC-SHA1') < 0) { this.request.emit('error', new Error('oauth: ' + _oauth.signature_method + ' signature_method not supported with body_hash signing.')) } var shasum = crypto.createHash('sha1') shasum.update(body || '') var sha1 = shasum.digest('hex') return Buffer.from(sha1, 'hex').toString('base64') } OAuth.prototype.concatParams = function (oa, sep, wrap) { wrap = wrap || '' var params = Object.keys(oa).filter(function (i) { return i !== 'realm' && i !== 'oauth_signature' }).sort() if (oa.realm) { params.splice(0, 0, 'realm') } params.push('oauth_signature') return params.map(function (i) { return i + '=' + wrap + oauth.rfc3986(oa[i]) + wrap }).join(sep) } OAuth.prototype.onRequest = function (_oauth) { var self = this self.params = _oauth var uri = self.request.uri || {} var method = self.request.method || '' var headers = caseless(self.request.headers) var body = self.request.body || '' var qsLib = self.request.qsLib || qs var form var query var contentType = headers.get('content-type') || '' var formContentType = 'application/x-www-form-urlencoded' var transport = _oauth.transport_method || 'header' if (contentType.slice(0, formContentType.length) === formContentType) { contentType = formContentType form = body } if (uri.query) { query = uri.query } if (transport === 'body' && (method !== 'POST' || contentType !== formContentType)) { self.request.emit('error', new Error('oauth: transport_method of body requires POST ' + 'and content-type ' + formContentType)) } if (!form && typeof _oauth.body_hash === 'boolean') { _oauth.body_hash = self.buildBodyHash(_oauth, self.request.body.toString()) } var oa = self.buildParams(_oauth, uri, method, query, form, qsLib) switch (transport) { case 'header': self.request.setHeader('Authorization', 'OAuth ' + self.concatParams(oa, ',', '"')) break case 'query': var href = self.request.uri.href += (query ? '&' : '?') + self.concatParams(oa, '&') self.request.uri = url.parse(href) self.request.path = self.request.uri.path break case 'body': self.request.body = (form ? form + '&' : '') + self.concatParams(oa, '&') break default: self.request.emit('error', new Error('oauth: transport_method invalid')) } } exports.OAuth = OAuth request-2.88.1/lib/querystring.js000066400000000000000000000024661333333633500170170ustar00rootroot00000000000000'use strict' var qs = require('qs') var querystring = require('querystring') function Querystring (request) { this.request = request this.lib = null this.useQuerystring = null this.parseOptions = null this.stringifyOptions = null } Querystring.prototype.init = function (options) { if (this.lib) { return } this.useQuerystring = options.useQuerystring this.lib = (this.useQuerystring ? querystring : qs) this.parseOptions = options.qsParseOptions || {} this.stringifyOptions = options.qsStringifyOptions || {} } Querystring.prototype.stringify = function (obj) { return (this.useQuerystring) ? this.rfc3986(this.lib.stringify(obj, this.stringifyOptions.sep || null, this.stringifyOptions.eq || null, this.stringifyOptions)) : this.lib.stringify(obj, this.stringifyOptions) } Querystring.prototype.parse = function (str) { return (this.useQuerystring) ? this.lib.parse(str, this.parseOptions.sep || null, this.parseOptions.eq || null, this.parseOptions) : this.lib.parse(str, this.parseOptions) } Querystring.prototype.rfc3986 = function (str) { return str.replace(/[!'()*]/g, function (c) { return '%' + c.charCodeAt(0).toString(16).toUpperCase() }) } Querystring.prototype.unescape = querystring.unescape exports.Querystring = Querystring request-2.88.1/lib/redirect.js000066400000000000000000000110331333333633500162120ustar00rootroot00000000000000'use strict' var url = require('url') var isUrl = /^https?:/ function Redirect (request) { this.request = request this.followRedirect = true this.followRedirects = true this.followAllRedirects = false this.followOriginalHttpMethod = false this.allowRedirect = function () { return true } this.maxRedirects = 10 this.redirects = [] this.redirectsFollowed = 0 this.removeRefererHeader = false } Redirect.prototype.onRequest = function (options) { var self = this if (options.maxRedirects !== undefined) { self.maxRedirects = options.maxRedirects } if (typeof options.followRedirect === 'function') { self.allowRedirect = options.followRedirect } if (options.followRedirect !== undefined) { self.followRedirects = !!options.followRedirect } if (options.followAllRedirects !== undefined) { self.followAllRedirects = options.followAllRedirects } if (self.followRedirects || self.followAllRedirects) { self.redirects = self.redirects || [] } if (options.removeRefererHeader !== undefined) { self.removeRefererHeader = options.removeRefererHeader } if (options.followOriginalHttpMethod !== undefined) { self.followOriginalHttpMethod = options.followOriginalHttpMethod } } Redirect.prototype.redirectTo = function (response) { var self = this var request = self.request var redirectTo = null if (response.statusCode >= 300 && response.statusCode < 400 && response.caseless.has('location')) { var location = response.caseless.get('location') request.debug('redirect', location) if (self.followAllRedirects) { redirectTo = location } else if (self.followRedirects) { switch (request.method) { case 'PATCH': case 'PUT': case 'POST': case 'DELETE': // Do not follow redirects break default: redirectTo = location break } } } else if (response.statusCode === 401) { var authHeader = request._auth.onResponse(response) if (authHeader) { request.setHeader('authorization', authHeader) redirectTo = request.uri } } return redirectTo } Redirect.prototype.onResponse = function (response) { var self = this var request = self.request var redirectTo = self.redirectTo(response) if (!redirectTo || !self.allowRedirect.call(request, response)) { return false } request.debug('redirect to', redirectTo) // ignore any potential response body. it cannot possibly be useful // to us at this point. // response.resume should be defined, but check anyway before calling. Workaround for browserify. if (response.resume) { response.resume() } if (self.redirectsFollowed >= self.maxRedirects) { request.emit('error', new Error('Exceeded maxRedirects. Probably stuck in a redirect loop ' + request.uri.href)) return false } self.redirectsFollowed += 1 if (!isUrl.test(redirectTo)) { redirectTo = url.resolve(request.uri.href, redirectTo) } var uriPrev = request.uri request.uri = url.parse(redirectTo) // handle the case where we change protocol from https to http or vice versa if (request.uri.protocol !== uriPrev.protocol) { delete request.agent } self.redirects.push({ statusCode: response.statusCode, redirectUri: redirectTo }) if (self.followAllRedirects && request.method !== 'HEAD' && response.statusCode !== 401 && response.statusCode !== 307) { request.method = self.followOriginalHttpMethod ? request.method : 'GET' } // request.method = 'GET' // Force all redirects to use GET || commented out fixes #215 delete request.src delete request.req delete request._started if (response.statusCode !== 401 && response.statusCode !== 307) { // Remove parameters from the previous response, unless this is the second request // for a server that requires digest authentication. delete request.body delete request._form if (request.headers) { request.removeHeader('host') request.removeHeader('content-type') request.removeHeader('content-length') if (request.uri.hostname !== request.originalHost.split(':')[0]) { // Remove authorization if changing hostnames (but not if just // changing ports or protocols). This matches the behavior of curl: // https://github.com/bagder/curl/blob/6beb0eee/lib/http.c#L710 request.removeHeader('authorization') } } } if (!self.removeRefererHeader) { request.setHeader('referer', uriPrev.href) } request.emit('redirect') request.init() return true } exports.Redirect = Redirect request-2.88.1/lib/tunnel.js000066400000000000000000000105001333333633500157140ustar00rootroot00000000000000'use strict' var url = require('url') var tunnel = require('tunnel-agent') var defaultProxyHeaderWhiteList = [ 'accept', 'accept-charset', 'accept-encoding', 'accept-language', 'accept-ranges', 'cache-control', 'content-encoding', 'content-language', 'content-location', 'content-md5', 'content-range', 'content-type', 'connection', 'date', 'expect', 'max-forwards', 'pragma', 'referer', 'te', 'user-agent', 'via' ] var defaultProxyHeaderExclusiveList = [ 'proxy-authorization' ] function constructProxyHost (uriObject) { var port = uriObject.port var protocol = uriObject.protocol var proxyHost = uriObject.hostname + ':' if (port) { proxyHost += port } else if (protocol === 'https:') { proxyHost += '443' } else { proxyHost += '80' } return proxyHost } function constructProxyHeaderWhiteList (headers, proxyHeaderWhiteList) { var whiteList = proxyHeaderWhiteList .reduce(function (set, header) { set[header.toLowerCase()] = true return set }, {}) return Object.keys(headers) .filter(function (header) { return whiteList[header.toLowerCase()] }) .reduce(function (set, header) { set[header] = headers[header] return set }, {}) } function constructTunnelOptions (request, proxyHeaders) { var proxy = request.proxy var tunnelOptions = { proxy: { host: proxy.hostname, port: +proxy.port, proxyAuth: proxy.auth, headers: proxyHeaders }, headers: request.headers, ca: request.ca, cert: request.cert, key: request.key, passphrase: request.passphrase, pfx: request.pfx, ciphers: request.ciphers, rejectUnauthorized: request.rejectUnauthorized, secureOptions: request.secureOptions, secureProtocol: request.secureProtocol } return tunnelOptions } function constructTunnelFnName (uri, proxy) { var uriProtocol = (uri.protocol === 'https:' ? 'https' : 'http') var proxyProtocol = (proxy.protocol === 'https:' ? 'Https' : 'Http') return [uriProtocol, proxyProtocol].join('Over') } function getTunnelFn (request) { var uri = request.uri var proxy = request.proxy var tunnelFnName = constructTunnelFnName(uri, proxy) return tunnel[tunnelFnName] } function Tunnel (request) { this.request = request this.proxyHeaderWhiteList = defaultProxyHeaderWhiteList this.proxyHeaderExclusiveList = [] if (typeof request.tunnel !== 'undefined') { this.tunnelOverride = request.tunnel } } Tunnel.prototype.isEnabled = function () { var self = this var request = self.request // Tunnel HTTPS by default. Allow the user to override this setting. // If self.tunnelOverride is set (the user specified a value), use it. if (typeof self.tunnelOverride !== 'undefined') { return self.tunnelOverride } // If the destination is HTTPS, tunnel. if (request.uri.protocol === 'https:') { return true } // Otherwise, do not use tunnel. return false } Tunnel.prototype.setup = function (options) { var self = this var request = self.request options = options || {} if (typeof request.proxy === 'string') { request.proxy = url.parse(request.proxy) } if (!request.proxy || !request.tunnel) { return false } // Setup Proxy Header Exclusive List and White List if (options.proxyHeaderWhiteList) { self.proxyHeaderWhiteList = options.proxyHeaderWhiteList } if (options.proxyHeaderExclusiveList) { self.proxyHeaderExclusiveList = options.proxyHeaderExclusiveList } var proxyHeaderExclusiveList = self.proxyHeaderExclusiveList.concat(defaultProxyHeaderExclusiveList) var proxyHeaderWhiteList = self.proxyHeaderWhiteList.concat(proxyHeaderExclusiveList) // Setup Proxy Headers and Proxy Headers Host // Only send the Proxy White Listed Header names var proxyHeaders = constructProxyHeaderWhiteList(request.headers, proxyHeaderWhiteList) proxyHeaders.host = constructProxyHost(request.uri) proxyHeaderExclusiveList.forEach(request.removeHeader, request) // Set Agent from Tunnel Data var tunnelFn = getTunnelFn(request) var tunnelOptions = constructTunnelOptions(request, proxyHeaders) request.agent = tunnelFn(tunnelOptions) return true } Tunnel.defaultProxyHeaderWhiteList = defaultProxyHeaderWhiteList Tunnel.defaultProxyHeaderExclusiveList = defaultProxyHeaderExclusiveList exports.Tunnel = Tunnel request-2.88.1/package.json000066400000000000000000000040441333333633500155770ustar00rootroot00000000000000{ "name": "request", "description": "Simplified HTTP request client.", "keywords": [ "http", "simple", "util", "utility" ], "version": "2.88.1", "author": "Mikeal Rogers ", "repository": { "type": "git", "url": "https://github.com/request/request.git" }, "bugs": { "url": "http://github.com/request/request/issues" }, "license": "Apache-2.0", "engines": { "node": ">= 4" }, "main": "index.js", "files": [ "lib/", "index.js", "request.js" ], "dependencies": { "aws-sign2": "~0.7.0", "aws4": "^1.8.0", "caseless": "~0.12.0", "combined-stream": "~1.0.6", "extend": "~3.0.2", "forever-agent": "~0.6.1", "form-data": "~2.3.2", "har-validator": "~5.1.0", "http-signature": "~1.2.0", "is-typedarray": "~1.0.0", "isstream": "~0.1.2", "json-stringify-safe": "~5.0.1", "mime-types": "~2.1.19", "oauth-sign": "~0.9.0", "performance-now": "^2.1.0", "qs": "~6.5.2", "safe-buffer": "^5.1.2", "tough-cookie": "~2.4.3", "tunnel-agent": "^0.6.0", "uuid": "^3.3.2" }, "scripts": { "test": "npm run lint && npm run test-ci && npm run test-browser", "test-ci": "taper tests/test-*.js", "test-cov": "istanbul cover tape tests/test-*.js", "test-browser": "node tests/browser/start.js", "lint": "standard" }, "devDependencies": { "bluebird": "^3.2.1", "browserify": "^13.0.1", "browserify-istanbul": "^2.0.0", "buffer-equal": "^1.0.0", "codecov": "^3.0.4", "coveralls": "^3.0.2", "function-bind": "^1.0.2", "istanbul": "^0.4.0", "karma": "^3.0.0", "karma-browserify": "^5.0.1", "karma-cli": "^1.0.0", "karma-coverage": "^1.0.0", "karma-phantomjs-launcher": "^1.0.0", "karma-tap": "^3.0.1", "phantomjs-prebuilt": "^2.1.3", "rimraf": "^2.2.8", "server-destroy": "^1.0.1", "standard": "^9.0.0", "tape": "^4.6.0", "taper": "^0.5.0" }, "greenkeeper": { "ignore": [ "hawk", "har-validator" ] } } request-2.88.1/release.sh000077500000000000000000000023561333333633500152740ustar00rootroot00000000000000#!/bin/sh if [ -z "`which github-changes`" ]; then # specify version because github-changes "is under heavy development. Things # may break between releases" until 0.1.0 echo "First, do: [sudo] npm install -g github-changes@0.0.14" exit 1 fi if [ -d .git/refs/remotes/upstream ]; then remote=upstream else remote=origin fi # Increment v2.x.y -> v2.x+1.0 npm version minor || exit 1 # Generate changelog from pull requests github-changes -o request -r request \ --auth --verbose \ --file CHANGELOG.md \ --only-pulls --use-commit-body \ --date-format '(YYYY/MM/DD)' \ || exit 1 # Since the tag for the new version hasn't been pushed yet, any changes in it # will be marked as "upcoming" version="$(grep '"version"' package.json | cut -d'"' -f4)" sed -i -e "s/^### upcoming/### v$version/" CHANGELOG.md # This may fail if no changelog updates # TODO: would this ever actually happen? handle it better? git add CHANGELOG.md; git commit -m 'Update changelog' # Publish the new version to npm npm publish || exit 1 # Increment v2.x.0 -> v2.x.1 # For rationale, see: # https://github.com/request/oauth-sign/issues/10#issuecomment-58917018 npm version patch || exit 1 # Push back to the main repo git push $remote master --tags || exit 1 request-2.88.1/request.js000066400000000000000000001267401333333633500153470ustar00rootroot00000000000000'use strict' var http = require('http') var https = require('https') var url = require('url') var util = require('util') var stream = require('stream') var zlib = require('zlib') var aws2 = require('aws-sign2') var aws4 = require('aws4') var httpSignature = require('http-signature') var mime = require('mime-types') var caseless = require('caseless') var ForeverAgent = require('forever-agent') var FormData = require('form-data') var extend = require('extend') var isstream = require('isstream') var isTypedArray = require('is-typedarray').strict var helpers = require('./lib/helpers') var cookies = require('./lib/cookies') var getProxyFromURI = require('./lib/getProxyFromURI') var Querystring = require('./lib/querystring').Querystring var Har = require('./lib/har').Har var Auth = require('./lib/auth').Auth var OAuth = require('./lib/oauth').OAuth var hawk = require('./lib/hawk') var Multipart = require('./lib/multipart').Multipart var Redirect = require('./lib/redirect').Redirect var Tunnel = require('./lib/tunnel').Tunnel var now = require('performance-now') var Buffer = require('safe-buffer').Buffer var safeStringify = helpers.safeStringify var isReadStream = helpers.isReadStream var toBase64 = helpers.toBase64 var defer = helpers.defer var copy = helpers.copy var version = helpers.version var globalCookieJar = cookies.jar() var globalPool = {} function filterForNonReserved (reserved, options) { // Filter out properties that are not reserved. // Reserved values are passed in at call site. var object = {} for (var i in options) { var notReserved = (reserved.indexOf(i) === -1) if (notReserved) { object[i] = options[i] } } return object } function filterOutReservedFunctions (reserved, options) { // Filter out properties that are functions and are reserved. // Reserved values are passed in at call site. var object = {} for (var i in options) { var isReserved = !(reserved.indexOf(i) === -1) var isFunction = (typeof options[i] === 'function') if (!(isReserved && isFunction)) { object[i] = options[i] } } return object } // Return a simpler request object to allow serialization function requestToJSON () { var self = this return { uri: self.uri, method: self.method, headers: self.headers } } // Return a simpler response object to allow serialization function responseToJSON () { var self = this return { statusCode: self.statusCode, body: self.body, headers: self.headers, request: requestToJSON.call(self.request) } } function Request (options) { // if given the method property in options, set property explicitMethod to true // extend the Request instance with any non-reserved properties // remove any reserved functions from the options object // set Request instance to be readable and writable // call init var self = this // start with HAR, then override with additional options if (options.har) { self._har = new Har(self) options = self._har.options(options) } stream.Stream.call(self) var reserved = Object.keys(Request.prototype) var nonReserved = filterForNonReserved(reserved, options) extend(self, nonReserved) options = filterOutReservedFunctions(reserved, options) self.readable = true self.writable = true if (options.method) { self.explicitMethod = true } self._qs = new Querystring(self) self._auth = new Auth(self) self._oauth = new OAuth(self) self._multipart = new Multipart(self) self._redirect = new Redirect(self) self._tunnel = new Tunnel(self) self.init(options) } util.inherits(Request, stream.Stream) // Debugging Request.debug = process.env.NODE_DEBUG && /\brequest\b/.test(process.env.NODE_DEBUG) function debug () { if (Request.debug) { console.error('REQUEST %s', util.format.apply(util, arguments)) } } Request.prototype.debug = debug Request.prototype.init = function (options) { // init() contains all the code to setup the request object. // the actual outgoing request is not started until start() is called // this function is called from both the constructor and on redirect. var self = this if (!options) { options = {} } self.headers = self.headers ? copy(self.headers) : {} // Delete headers with value undefined since they break // ClientRequest.OutgoingMessage.setHeader in node 0.12 for (var headerName in self.headers) { if (typeof self.headers[headerName] === 'undefined') { delete self.headers[headerName] } } caseless.httpify(self, self.headers) if (!self.method) { self.method = options.method || 'GET' } if (!self.localAddress) { self.localAddress = options.localAddress } self._qs.init(options) debug(options) if (!self.pool && self.pool !== false) { self.pool = globalPool } self.dests = self.dests || [] self.__isRequestRequest = true // Protect against double callback if (!self._callback && self.callback) { self._callback = self.callback self.callback = function () { if (self._callbackCalled) { return // Print a warning maybe? } self._callbackCalled = true self._callback.apply(self, arguments) } self.on('error', self.callback.bind()) self.on('complete', self.callback.bind(self, null)) } // People use this property instead all the time, so support it if (!self.uri && self.url) { self.uri = self.url delete self.url } // If there's a baseUrl, then use it as the base URL (i.e. uri must be // specified as a relative path and is appended to baseUrl). if (self.baseUrl) { if (typeof self.baseUrl !== 'string') { return self.emit('error', new Error('options.baseUrl must be a string')) } if (typeof self.uri !== 'string') { return self.emit('error', new Error('options.uri must be a string when using options.baseUrl')) } if (self.uri.indexOf('//') === 0 || self.uri.indexOf('://') !== -1) { return self.emit('error', new Error('options.uri must be a path when using options.baseUrl')) } // Handle all cases to make sure that there's only one slash between // baseUrl and uri. var baseUrlEndsWithSlash = self.baseUrl.lastIndexOf('/') === self.baseUrl.length - 1 var uriStartsWithSlash = self.uri.indexOf('/') === 0 if (baseUrlEndsWithSlash && uriStartsWithSlash) { self.uri = self.baseUrl + self.uri.slice(1) } else if (baseUrlEndsWithSlash || uriStartsWithSlash) { self.uri = self.baseUrl + self.uri } else if (self.uri === '') { self.uri = self.baseUrl } else { self.uri = self.baseUrl + '/' + self.uri } delete self.baseUrl } // A URI is needed by this point, emit error if we haven't been able to get one if (!self.uri) { return self.emit('error', new Error('options.uri is a required argument')) } // If a string URI/URL was given, parse it into a URL object if (typeof self.uri === 'string') { self.uri = url.parse(self.uri) } // Some URL objects are not from a URL parsed string and need href added if (!self.uri.href) { self.uri.href = url.format(self.uri) } // DEPRECATED: Warning for users of the old Unix Sockets URL Scheme if (self.uri.protocol === 'unix:') { return self.emit('error', new Error('`unix://` URL scheme is no longer supported. Please use the format `http://unix:SOCKET:PATH`')) } // Support Unix Sockets if (self.uri.host === 'unix') { self.enableUnixSocket() } if (self.strictSSL === false) { self.rejectUnauthorized = false } if (!self.uri.pathname) { self.uri.pathname = '/' } if (!(self.uri.host || (self.uri.hostname && self.uri.port)) && !self.uri.isUnix) { // Invalid URI: it may generate lot of bad errors, like 'TypeError: Cannot call method `indexOf` of undefined' in CookieJar // Detect and reject it as soon as possible var faultyUri = url.format(self.uri) var message = 'Invalid URI "' + faultyUri + '"' if (Object.keys(options).length === 0) { // No option ? This can be the sign of a redirect // As this is a case where the user cannot do anything (they didn't call request directly with this URL) // they should be warned that it can be caused by a redirection (can save some hair) message += '. This can be caused by a crappy redirection.' } // This error was fatal self.abort() return self.emit('error', new Error(message)) } if (!self.hasOwnProperty('proxy')) { self.proxy = getProxyFromURI(self.uri) } self.tunnel = self._tunnel.isEnabled() if (self.proxy) { self._tunnel.setup(options) } self._redirect.onRequest(options) self.setHost = false if (!self.hasHeader('host')) { var hostHeaderName = self.originalHostHeaderName || 'host' self.setHeader(hostHeaderName, self.uri.host) // Drop :port suffix from Host header if known protocol. if (self.uri.port) { if ((self.uri.port === '80' && self.uri.protocol === 'http:') || (self.uri.port === '443' && self.uri.protocol === 'https:')) { self.setHeader(hostHeaderName, self.uri.hostname) } } self.setHost = true } self.jar(self._jar || options.jar) if (!self.uri.port) { if (self.uri.protocol === 'http:') { self.uri.port = 80 } else if (self.uri.protocol === 'https:') { self.uri.port = 443 } } if (self.proxy && !self.tunnel) { self.port = self.proxy.port self.host = self.proxy.hostname } else { self.port = self.uri.port self.host = self.uri.hostname } if (options.form) { self.form(options.form) } if (options.formData) { var formData = options.formData var requestForm = self.form() var appendFormValue = function (key, value) { if (value && value.hasOwnProperty('value') && value.hasOwnProperty('options')) { requestForm.append(key, value.value, value.options) } else { requestForm.append(key, value) } } for (var formKey in formData) { if (formData.hasOwnProperty(formKey)) { var formValue = formData[formKey] if (formValue instanceof Array) { for (var j = 0; j < formValue.length; j++) { appendFormValue(formKey, formValue[j]) } } else { appendFormValue(formKey, formValue) } } } } if (options.qs) { self.qs(options.qs) } if (self.uri.path) { self.path = self.uri.path } else { self.path = self.uri.pathname + (self.uri.search || '') } if (self.path.length === 0) { self.path = '/' } // Auth must happen last in case signing is dependent on other headers if (options.aws) { self.aws(options.aws) } if (options.hawk) { self.hawk(options.hawk) } if (options.httpSignature) { self.httpSignature(options.httpSignature) } if (options.auth) { if (Object.prototype.hasOwnProperty.call(options.auth, 'username')) { options.auth.user = options.auth.username } if (Object.prototype.hasOwnProperty.call(options.auth, 'password')) { options.auth.pass = options.auth.password } self.auth( options.auth.user, options.auth.pass, options.auth.sendImmediately, options.auth.bearer ) } if (self.gzip && !self.hasHeader('accept-encoding')) { self.setHeader('accept-encoding', 'gzip, deflate') } if (self.uri.auth && !self.hasHeader('authorization')) { var uriAuthPieces = self.uri.auth.split(':').map(function (item) { return self._qs.unescape(item) }) self.auth(uriAuthPieces[0], uriAuthPieces.slice(1).join(':'), true) } if (!self.tunnel && self.proxy && self.proxy.auth && !self.hasHeader('proxy-authorization')) { var proxyAuthPieces = self.proxy.auth.split(':').map(function (item) { return self._qs.unescape(item) }) var authHeader = 'Basic ' + toBase64(proxyAuthPieces.join(':')) self.setHeader('proxy-authorization', authHeader) } if (self.proxy && !self.tunnel) { self.path = (self.uri.protocol + '//' + self.uri.host + self.path) } if (options.json) { self.json(options.json) } if (options.multipart) { self.multipart(options.multipart) } if (options.time) { self.timing = true // NOTE: elapsedTime is deprecated in favor of .timings self.elapsedTime = self.elapsedTime || 0 } function setContentLength () { if (isTypedArray(self.body)) { self.body = Buffer.from(self.body) } if (!self.hasHeader('content-length')) { var length if (typeof self.body === 'string') { length = Buffer.byteLength(self.body) } else if (Array.isArray(self.body)) { length = self.body.reduce(function (a, b) { return a + b.length }, 0) } else { length = self.body.length } if (length) { self.setHeader('content-length', length) } else { self.emit('error', new Error('Argument error, options.body.')) } } } if (self.body && !isstream(self.body)) { setContentLength() } if (options.oauth) { self.oauth(options.oauth) } else if (self._oauth.params && self.hasHeader('authorization')) { self.oauth(self._oauth.params) } var protocol = self.proxy && !self.tunnel ? self.proxy.protocol : self.uri.protocol var defaultModules = {'http:': http, 'https:': https} var httpModules = self.httpModules || {} self.httpModule = httpModules[protocol] || defaultModules[protocol] if (!self.httpModule) { return self.emit('error', new Error('Invalid protocol: ' + protocol)) } if (options.ca) { self.ca = options.ca } if (!self.agent) { if (options.agentOptions) { self.agentOptions = options.agentOptions } if (options.agentClass) { self.agentClass = options.agentClass } else if (options.forever) { var v = version() // use ForeverAgent in node 0.10- only if (v.major === 0 && v.minor <= 10) { self.agentClass = protocol === 'http:' ? ForeverAgent : ForeverAgent.SSL } else { self.agentClass = self.httpModule.Agent self.agentOptions = self.agentOptions || {} self.agentOptions.keepAlive = true } } else { self.agentClass = self.httpModule.Agent } } if (self.pool === false) { self.agent = false } else { self.agent = self.agent || self.getNewAgent() } self.on('pipe', function (src) { if (self.ntick && self._started) { self.emit('error', new Error('You cannot pipe to this stream after the outbound request has started.')) } self.src = src if (isReadStream(src)) { if (!self.hasHeader('content-type')) { self.setHeader('content-type', mime.lookup(src.path)) } } else { if (src.headers) { for (var i in src.headers) { if (!self.hasHeader(i)) { self.setHeader(i, src.headers[i]) } } } if (self._json && !self.hasHeader('content-type')) { self.setHeader('content-type', 'application/json') } if (src.method && !self.explicitMethod) { self.method = src.method } } // self.on('pipe', function () { // console.error('You have already piped to this stream. Pipeing twice is likely to break the request.') // }) }) defer(function () { if (self._aborted) { return } var end = function () { if (self._form) { if (!self._auth.hasAuth) { self._form.pipe(self) } else if (self._auth.hasAuth && self._auth.sentAuth) { self._form.pipe(self) } } if (self._multipart && self._multipart.chunked) { self._multipart.body.pipe(self) } if (self.body) { if (isstream(self.body)) { self.body.pipe(self) } else { setContentLength() if (Array.isArray(self.body)) { self.body.forEach(function (part) { self.write(part) }) } else { self.write(self.body) } self.end() } } else if (self.requestBodyStream) { console.warn('options.requestBodyStream is deprecated, please pass the request object to stream.pipe.') self.requestBodyStream.pipe(self) } else if (!self.src) { if (self._auth.hasAuth && !self._auth.sentAuth) { self.end() return } if (self.method !== 'GET' && typeof self.method !== 'undefined') { self.setHeader('content-length', 0) } self.end() } } if (self._form && !self.hasHeader('content-length')) { // Before ending the request, we had to compute the length of the whole form, asyncly self.setHeader(self._form.getHeaders(), true) self._form.getLength(function (err, length) { if (!err && !isNaN(length)) { self.setHeader('content-length', length) } end() }) } else { end() } self.ntick = true }) } Request.prototype.getNewAgent = function () { var self = this var Agent = self.agentClass var options = {} if (self.agentOptions) { for (var i in self.agentOptions) { options[i] = self.agentOptions[i] } } if (self.ca) { options.ca = self.ca } if (self.ciphers) { options.ciphers = self.ciphers } if (self.secureProtocol) { options.secureProtocol = self.secureProtocol } if (self.secureOptions) { options.secureOptions = self.secureOptions } if (typeof self.rejectUnauthorized !== 'undefined') { options.rejectUnauthorized = self.rejectUnauthorized } if (self.cert && self.key) { options.key = self.key options.cert = self.cert } if (self.pfx) { options.pfx = self.pfx } if (self.passphrase) { options.passphrase = self.passphrase } var poolKey = '' // different types of agents are in different pools if (Agent !== self.httpModule.Agent) { poolKey += Agent.name } // ca option is only relevant if proxy or destination are https var proxy = self.proxy if (typeof proxy === 'string') { proxy = url.parse(proxy) } var isHttps = (proxy && proxy.protocol === 'https:') || this.uri.protocol === 'https:' if (isHttps) { if (options.ca) { if (poolKey) { poolKey += ':' } poolKey += options.ca } if (typeof options.rejectUnauthorized !== 'undefined') { if (poolKey) { poolKey += ':' } poolKey += options.rejectUnauthorized } if (options.cert) { if (poolKey) { poolKey += ':' } poolKey += options.cert.toString('ascii') + options.key.toString('ascii') } if (options.pfx) { if (poolKey) { poolKey += ':' } poolKey += options.pfx.toString('ascii') } if (options.ciphers) { if (poolKey) { poolKey += ':' } poolKey += options.ciphers } if (options.secureProtocol) { if (poolKey) { poolKey += ':' } poolKey += options.secureProtocol } if (options.secureOptions) { if (poolKey) { poolKey += ':' } poolKey += options.secureOptions } } if (self.pool === globalPool && !poolKey && Object.keys(options).length === 0 && self.httpModule.globalAgent) { // not doing anything special. Use the globalAgent return self.httpModule.globalAgent } // we're using a stored agent. Make sure it's protocol-specific poolKey = self.uri.protocol + poolKey // generate a new agent for this setting if none yet exists if (!self.pool[poolKey]) { self.pool[poolKey] = new Agent(options) // properly set maxSockets on new agents if (self.pool.maxSockets) { self.pool[poolKey].maxSockets = self.pool.maxSockets } } return self.pool[poolKey] } Request.prototype.start = function () { // start() is called once we are ready to send the outgoing HTTP request. // this is usually called on the first write(), end() or on nextTick() var self = this if (self.timing) { // All timings will be relative to this request's startTime. In order to do this, // we need to capture the wall-clock start time (via Date), immediately followed // by the high-resolution timer (via now()). While these two won't be set // at the _exact_ same time, they should be close enough to be able to calculate // high-resolution, monotonically non-decreasing timestamps relative to startTime. var startTime = new Date().getTime() var startTimeNow = now() } if (self._aborted) { return } self._started = true self.method = self.method || 'GET' self.href = self.uri.href if (self.src && self.src.stat && self.src.stat.size && !self.hasHeader('content-length')) { self.setHeader('content-length', self.src.stat.size) } if (self._aws) { self.aws(self._aws, true) } // We have a method named auth, which is completely different from the http.request // auth option. If we don't remove it, we're gonna have a bad time. var reqOptions = copy(self) delete reqOptions.auth debug('make request', self.uri.href) // node v6.8.0 now supports a `timeout` value in `http.request()`, but we // should delete it for now since we handle timeouts manually for better // consistency with node versions before v6.8.0 delete reqOptions.timeout try { self.req = self.httpModule.request(reqOptions) } catch (err) { self.emit('error', err) return } if (self.timing) { self.startTime = startTime self.startTimeNow = startTimeNow // Timing values will all be relative to startTime (by comparing to startTimeNow // so we have an accurate clock) self.timings = {} } var timeout if (self.timeout && !self.timeoutTimer) { if (self.timeout < 0) { timeout = 0 } else if (typeof self.timeout === 'number' && isFinite(self.timeout)) { timeout = self.timeout } } self.req.on('response', self.onRequestResponse.bind(self)) self.req.on('error', self.onRequestError.bind(self)) self.req.on('drain', function () { self.emit('drain') }) self.req.on('socket', function (socket) { // `._connecting` was the old property which was made public in node v6.1.0 var isConnecting = socket._connecting || socket.connecting if (self.timing) { self.timings.socket = now() - self.startTimeNow if (isConnecting) { var onLookupTiming = function () { self.timings.lookup = now() - self.startTimeNow } var onConnectTiming = function () { self.timings.connect = now() - self.startTimeNow } socket.once('lookup', onLookupTiming) socket.once('connect', onConnectTiming) // clean up timing event listeners if needed on error self.req.once('error', function () { socket.removeListener('lookup', onLookupTiming) socket.removeListener('connect', onConnectTiming) }) } } var setReqTimeout = function () { // This timeout sets the amount of time to wait *between* bytes sent // from the server once connected. // // In particular, it's useful for erroring if the server fails to send // data halfway through streaming a response. self.req.setTimeout(timeout, function () { if (self.req) { self.abort() var e = new Error('ESOCKETTIMEDOUT') e.code = 'ESOCKETTIMEDOUT' e.connect = false self.emit('error', e) } }) } if (timeout !== undefined) { // Only start the connection timer if we're actually connecting a new // socket, otherwise if we're already connected (because this is a // keep-alive connection) do not bother. This is important since we won't // get a 'connect' event for an already connected socket. if (isConnecting) { var onReqSockConnect = function () { socket.removeListener('connect', onReqSockConnect) clearTimeout(self.timeoutTimer) self.timeoutTimer = null setReqTimeout() } socket.on('connect', onReqSockConnect) self.req.on('error', function (err) { // eslint-disable-line handle-callback-err socket.removeListener('connect', onReqSockConnect) }) // Set a timeout in memory - this block will throw if the server takes more // than `timeout` to write the HTTP status and headers (corresponding to // the on('response') event on the client). NB: this measures wall-clock // time, not the time between bytes sent by the server. self.timeoutTimer = setTimeout(function () { socket.removeListener('connect', onReqSockConnect) self.abort() var e = new Error('ETIMEDOUT') e.code = 'ETIMEDOUT' e.connect = true self.emit('error', e) }, timeout) } else { // We're already connected setReqTimeout() } } self.emit('socket', socket) }) self.emit('request', self.req) } Request.prototype.onRequestError = function (error) { var self = this if (self._aborted) { return } if (self.req && self.req._reusedSocket && error.code === 'ECONNRESET' && self.agent.addRequestNoreuse) { self.agent = { addRequest: self.agent.addRequestNoreuse.bind(self.agent) } self.start() self.req.end() return } if (self.timeout && self.timeoutTimer) { clearTimeout(self.timeoutTimer) self.timeoutTimer = null } self.emit('error', error) } Request.prototype.onRequestResponse = function (response) { var self = this if (self.timing) { self.timings.response = now() - self.startTimeNow } debug('onRequestResponse', self.uri.href, response.statusCode, response.headers) response.on('end', function () { if (self.timing) { self.timings.end = now() - self.startTimeNow response.timingStart = self.startTime // fill in the blanks for any periods that didn't trigger, such as // no lookup or connect due to keep alive if (!self.timings.socket) { self.timings.socket = 0 } if (!self.timings.lookup) { self.timings.lookup = self.timings.socket } if (!self.timings.connect) { self.timings.connect = self.timings.lookup } if (!self.timings.response) { self.timings.response = self.timings.connect } debug('elapsed time', self.timings.end) // elapsedTime includes all redirects self.elapsedTime += Math.round(self.timings.end) // NOTE: elapsedTime is deprecated in favor of .timings response.elapsedTime = self.elapsedTime // timings is just for the final fetch response.timings = self.timings // pre-calculate phase timings as well response.timingPhases = { wait: self.timings.socket, dns: self.timings.lookup - self.timings.socket, tcp: self.timings.connect - self.timings.lookup, firstByte: self.timings.response - self.timings.connect, download: self.timings.end - self.timings.response, total: self.timings.end } } debug('response end', self.uri.href, response.statusCode, response.headers) }) if (self._aborted) { debug('aborted', self.uri.href) response.resume() return } self.response = response response.request = self response.toJSON = responseToJSON // XXX This is different on 0.10, because SSL is strict by default if (self.httpModule === https && self.strictSSL && (!response.hasOwnProperty('socket') || !response.socket.authorized)) { debug('strict ssl error', self.uri.href) var sslErr = response.hasOwnProperty('socket') ? response.socket.authorizationError : self.uri.href + ' does not support SSL' self.emit('error', new Error('SSL Error: ' + sslErr)) return } // Save the original host before any redirect (if it changes, we need to // remove any authorization headers). Also remember the case of the header // name because lots of broken servers expect Host instead of host and we // want the caller to be able to specify this. self.originalHost = self.getHeader('host') if (!self.originalHostHeaderName) { self.originalHostHeaderName = self.hasHeader('host') } if (self.setHost) { self.removeHeader('host') } if (self.timeout && self.timeoutTimer) { clearTimeout(self.timeoutTimer) self.timeoutTimer = null } var targetCookieJar = (self._jar && self._jar.setCookie) ? self._jar : globalCookieJar var addCookie = function (cookie) { // set the cookie if it's domain in the href's domain. try { targetCookieJar.setCookie(cookie, self.uri.href, {ignoreError: true}) } catch (e) { self.emit('error', e) } } response.caseless = caseless(response.headers) if (response.caseless.has('set-cookie') && (!self._disableCookies)) { var headerName = response.caseless.has('set-cookie') if (Array.isArray(response.headers[headerName])) { response.headers[headerName].forEach(addCookie) } else { addCookie(response.headers[headerName]) } } if (self._redirect.onResponse(response)) { return // Ignore the rest of the response } else { // Be a good stream and emit end when the response is finished. // Hack to emit end on close because of a core bug that never fires end response.on('close', function () { if (!self._ended) { self.response.emit('end') } }) response.once('end', function () { self._ended = true }) var noBody = function (code) { return ( self.method === 'HEAD' || // Informational (code >= 100 && code < 200) || // No Content code === 204 || // Not Modified code === 304 ) } var responseContent if (self.gzip && !noBody(response.statusCode)) { var contentEncoding = response.headers['content-encoding'] || 'identity' contentEncoding = contentEncoding.trim().toLowerCase() // Be more lenient with decoding compressed responses, since (very rarely) // servers send slightly invalid gzip responses that are still accepted // by common browsers. // Always using Z_SYNC_FLUSH is what cURL does. var zlibOptions = { flush: zlib.Z_SYNC_FLUSH, finishFlush: zlib.Z_SYNC_FLUSH } if (contentEncoding === 'gzip') { responseContent = zlib.createGunzip(zlibOptions) response.pipe(responseContent) } else if (contentEncoding === 'deflate') { responseContent = zlib.createInflate(zlibOptions) response.pipe(responseContent) } else { // Since previous versions didn't check for Content-Encoding header, // ignore any invalid values to preserve backwards-compatibility if (contentEncoding !== 'identity') { debug('ignoring unrecognized Content-Encoding ' + contentEncoding) } responseContent = response } } else { responseContent = response } if (self.encoding) { if (self.dests.length !== 0) { console.error('Ignoring encoding parameter as this stream is being piped to another stream which makes the encoding option invalid.') } else { responseContent.setEncoding(self.encoding) } } if (self._paused) { responseContent.pause() } self.responseContent = responseContent self.emit('response', response) self.dests.forEach(function (dest) { self.pipeDest(dest) }) responseContent.on('data', function (chunk) { if (self.timing && !self.responseStarted) { self.responseStartTime = (new Date()).getTime() // NOTE: responseStartTime is deprecated in favor of .timings response.responseStartTime = self.responseStartTime } self._destdata = true self.emit('data', chunk) }) responseContent.once('end', function (chunk) { self.emit('end', chunk) }) responseContent.on('error', function (error) { self.emit('error', error) }) responseContent.on('close', function () { self.emit('close') }) if (self.callback) { self.readResponseBody(response) } else { // if no callback self.on('end', function () { if (self._aborted) { debug('aborted', self.uri.href) return } self.emit('complete', response) }) } } debug('finish init function', self.uri.href) } Request.prototype.readResponseBody = function (response) { var self = this debug("reading response's body") var buffers = [] var bufferLength = 0 var strings = [] self.on('data', function (chunk) { if (!Buffer.isBuffer(chunk)) { strings.push(chunk) } else if (chunk.length) { bufferLength += chunk.length buffers.push(chunk) } }) self.on('end', function () { debug('end event', self.uri.href) if (self._aborted) { debug('aborted', self.uri.href) // `buffer` is defined in the parent scope and used in a closure it exists for the life of the request. // This can lead to leaky behavior if the user retains a reference to the request object. buffers = [] bufferLength = 0 return } if (bufferLength) { debug('has body', self.uri.href, bufferLength) response.body = Buffer.concat(buffers, bufferLength) if (self.encoding !== null) { response.body = response.body.toString(self.encoding) } // `buffer` is defined in the parent scope and used in a closure it exists for the life of the Request. // This can lead to leaky behavior if the user retains a reference to the request object. buffers = [] bufferLength = 0 } else if (strings.length) { // The UTF8 BOM [0xEF,0xBB,0xBF] is converted to [0xFE,0xFF] in the JS UTC16/UCS2 representation. // Strip this value out when the encoding is set to 'utf8', as upstream consumers won't expect it and it breaks JSON.parse(). if (self.encoding === 'utf8' && strings[0].length > 0 && strings[0][0] === '\uFEFF') { strings[0] = strings[0].substring(1) } response.body = strings.join('') } if (self._json) { try { response.body = JSON.parse(response.body, self._jsonReviver) } catch (e) { debug('invalid JSON received', self.uri.href) } } debug('emitting complete', self.uri.href) if (typeof response.body === 'undefined' && !self._json) { response.body = self.encoding === null ? Buffer.alloc(0) : '' } self.emit('complete', response, response.body) }) } Request.prototype.abort = function () { var self = this self._aborted = true if (self.req) { self.req.abort() } else if (self.response) { self.response.destroy() } self.emit('abort') } Request.prototype.pipeDest = function (dest) { var self = this var response = self.response // Called after the response is received if (dest.headers && !dest.headersSent) { if (response.caseless.has('content-type')) { var ctname = response.caseless.has('content-type') if (dest.setHeader) { dest.setHeader(ctname, response.headers[ctname]) } else { dest.headers[ctname] = response.headers[ctname] } } if (response.caseless.has('content-length')) { var clname = response.caseless.has('content-length') if (dest.setHeader) { dest.setHeader(clname, response.headers[clname]) } else { dest.headers[clname] = response.headers[clname] } } } if (dest.setHeader && !dest.headersSent) { for (var i in response.headers) { // If the response content is being decoded, the Content-Encoding header // of the response doesn't represent the piped content, so don't pass it. if (!self.gzip || i !== 'content-encoding') { dest.setHeader(i, response.headers[i]) } } dest.statusCode = response.statusCode } if (self.pipefilter) { self.pipefilter(response, dest) } } Request.prototype.qs = function (q, clobber) { var self = this var base if (!clobber && self.uri.query) { base = self._qs.parse(self.uri.query) } else { base = {} } for (var i in q) { base[i] = q[i] } var qs = self._qs.stringify(base) if (qs === '') { return self } self.uri = url.parse(self.uri.href.split('?')[0] + '?' + qs) self.url = self.uri self.path = self.uri.path if (self.uri.host === 'unix') { self.enableUnixSocket() } return self } Request.prototype.form = function (form) { var self = this if (form) { if (!/^application\/x-www-form-urlencoded\b/.test(self.getHeader('content-type'))) { self.setHeader('content-type', 'application/x-www-form-urlencoded') } self.body = (typeof form === 'string') ? self._qs.rfc3986(form.toString('utf8')) : self._qs.stringify(form).toString('utf8') return self } // create form-data object self._form = new FormData() self._form.on('error', function (err) { err.message = 'form-data: ' + err.message self.emit('error', err) self.abort() }) return self._form } Request.prototype.multipart = function (multipart) { var self = this self._multipart.onRequest(multipart) if (!self._multipart.chunked) { self.body = self._multipart.body } return self } Request.prototype.json = function (val) { var self = this if (!self.hasHeader('accept')) { self.setHeader('accept', 'application/json') } if (typeof self.jsonReplacer === 'function') { self._jsonReplacer = self.jsonReplacer } self._json = true if (typeof val === 'boolean') { if (self.body !== undefined) { if (!/^application\/x-www-form-urlencoded\b/.test(self.getHeader('content-type'))) { self.body = safeStringify(self.body, self._jsonReplacer) } else { self.body = self._qs.rfc3986(self.body) } if (!self.hasHeader('content-type')) { self.setHeader('content-type', 'application/json') } } } else { self.body = safeStringify(val, self._jsonReplacer) if (!self.hasHeader('content-type')) { self.setHeader('content-type', 'application/json') } } if (typeof self.jsonReviver === 'function') { self._jsonReviver = self.jsonReviver } return self } Request.prototype.getHeader = function (name, headers) { var self = this var result, re, match if (!headers) { headers = self.headers } Object.keys(headers).forEach(function (key) { if (key.length !== name.length) { return } re = new RegExp(name, 'i') match = key.match(re) if (match) { result = headers[key] } }) return result } Request.prototype.enableUnixSocket = function () { // Get the socket & request paths from the URL var unixParts = this.uri.path.split(':') var host = unixParts[0] var path = unixParts[1] // Apply unix properties to request this.socketPath = host this.uri.pathname = path this.uri.path = path this.uri.host = host this.uri.hostname = host this.uri.isUnix = true } Request.prototype.auth = function (user, pass, sendImmediately, bearer) { var self = this self._auth.onRequest(user, pass, sendImmediately, bearer) return self } Request.prototype.aws = function (opts, now) { var self = this if (!now) { self._aws = opts return self } if (opts.sign_version === 4 || opts.sign_version === '4') { // use aws4 var options = { host: self.uri.host, path: self.uri.path, method: self.method, headers: self.headers, body: self.body } if (opts.service) { options.service = opts.service } var signRes = aws4.sign(options, { accessKeyId: opts.key, secretAccessKey: opts.secret, sessionToken: opts.session }) self.setHeader('authorization', signRes.headers.Authorization) self.setHeader('x-amz-date', signRes.headers['X-Amz-Date']) if (signRes.headers['X-Amz-Security-Token']) { self.setHeader('x-amz-security-token', signRes.headers['X-Amz-Security-Token']) } } else { // default: use aws-sign2 var date = new Date() self.setHeader('date', date.toUTCString()) var auth = { key: opts.key, secret: opts.secret, verb: self.method.toUpperCase(), date: date, contentType: self.getHeader('content-type') || '', md5: self.getHeader('content-md5') || '', amazonHeaders: aws2.canonicalizeHeaders(self.headers) } var path = self.uri.path if (opts.bucket && path) { auth.resource = '/' + opts.bucket + path } else if (opts.bucket && !path) { auth.resource = '/' + opts.bucket } else if (!opts.bucket && path) { auth.resource = path } else if (!opts.bucket && !path) { auth.resource = '/' } auth.resource = aws2.canonicalizeResource(auth.resource) self.setHeader('authorization', aws2.authorization(auth)) } return self } Request.prototype.httpSignature = function (opts) { var self = this httpSignature.signRequest({ getHeader: function (header) { return self.getHeader(header, self.headers) }, setHeader: function (header, value) { self.setHeader(header, value) }, method: self.method, path: self.path }, opts) debug('httpSignature authorization', self.getHeader('authorization')) return self } Request.prototype.hawk = function (opts) { var self = this self.setHeader('Authorization', hawk.header(self.uri, self.method, opts)) } Request.prototype.oauth = function (_oauth) { var self = this self._oauth.onRequest(_oauth) return self } Request.prototype.jar = function (jar) { var self = this var cookies if (self._redirect.redirectsFollowed === 0) { self.originalCookieHeader = self.getHeader('cookie') } if (!jar) { // disable cookies cookies = false self._disableCookies = true } else { var targetCookieJar = (jar && jar.getCookieString) ? jar : globalCookieJar var urihref = self.uri.href // fetch cookie in the Specified host if (targetCookieJar) { cookies = targetCookieJar.getCookieString(urihref) } } // if need cookie and cookie is not empty if (cookies && cookies.length) { if (self.originalCookieHeader) { // Don't overwrite existing Cookie header self.setHeader('cookie', self.originalCookieHeader + '; ' + cookies) } else { self.setHeader('cookie', cookies) } } self._jar = jar return self } // Stream API Request.prototype.pipe = function (dest, opts) { var self = this if (self.response) { if (self._destdata) { self.emit('error', new Error('You cannot pipe after data has been emitted from the response.')) } else if (self._ended) { self.emit('error', new Error('You cannot pipe after the response has been ended.')) } else { stream.Stream.prototype.pipe.call(self, dest, opts) self.pipeDest(dest) return dest } } else { self.dests.push(dest) stream.Stream.prototype.pipe.call(self, dest, opts) return dest } } Request.prototype.write = function () { var self = this if (self._aborted) { return } if (!self._started) { self.start() } if (self.req) { return self.req.write.apply(self.req, arguments) } } Request.prototype.end = function (chunk) { var self = this if (self._aborted) { return } if (chunk) { self.write(chunk) } if (!self._started) { self.start() } if (self.req) { self.req.end() } } Request.prototype.pause = function () { var self = this if (!self.responseContent) { self._paused = true } else { self.responseContent.pause.apply(self.responseContent, arguments) } } Request.prototype.resume = function () { var self = this if (!self.responseContent) { self._paused = false } else { self.responseContent.resume.apply(self.responseContent, arguments) } } Request.prototype.destroy = function () { var self = this if (!self._ended) { self.end() } else if (self.response) { self.response.destroy() } } Request.defaultProxyHeaderWhiteList = Tunnel.defaultProxyHeaderWhiteList.slice() Request.defaultProxyHeaderExclusiveList = Tunnel.defaultProxyHeaderExclusiveList.slice() // Exports Request.prototype.toJSON = requestToJSON module.exports = Request request-2.88.1/tests/000077500000000000000000000000001333333633500144515ustar00rootroot00000000000000request-2.88.1/tests/browser/000077500000000000000000000000001333333633500161345ustar00rootroot00000000000000request-2.88.1/tests/browser/karma.conf.js000066400000000000000000000021761333333633500205170ustar00rootroot00000000000000'use strict' var istanbul = require('browserify-istanbul') module.exports = function (config) { config.set({ client: { requestTestUrl: process.argv[4] }, basePath: '../..', frameworks: ['tap', 'browserify'], preprocessors: { 'tests/browser/test.js': ['browserify'], '*.js,!(tests)/**/*.js': ['coverage'] }, files: [ 'tests/browser/test.js' ], port: 9876, reporters: ['dots', 'coverage'], colors: true, logLevel: config.LOG_ERROR, autoWatch: false, browsers: ['PhantomJS_without_security'], singleRun: true, plugins: [ 'karma-phantomjs-launcher', 'karma-coverage', 'karma-browserify', 'karma-tap' ], browserify: { debug: true, transform: [istanbul({ ignore: ['**/node_modules/**', '**/tests/**'] })] }, coverageReporter: { type: 'lcov', dir: 'coverage/' }, // Custom launcher to allowe self signed certs. customLaunchers: { PhantomJS_without_security: { base: 'PhantomJS', flags: [ '--ignore-ssl-errors=true' ] } } }) } request-2.88.1/tests/browser/ssl/000077500000000000000000000000001333333633500167355ustar00rootroot00000000000000request-2.88.1/tests/browser/ssl/ca.crt000066400000000000000000000014521333333633500200340ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIICKTCCAZICCQDB/6lRlsirjzANBgkqhkiG9w0BAQUFADBZMQswCQYDVQQGEwJB VTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0 cyBQdHkgTHRkMRIwEAYDVQQDEwlsb2NhbGhvc3QwHhcNMTUwMTA3MTcwODM2WhcN MjUwMTA0MTcwODM2WjBZMQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0 ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRIwEAYDVQQDEwls b2NhbGhvc3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMba6FQ1VkgW8vWa FBxV1VdLhQ5HP0eKZ/CyEGG4r89CzfzC0+V3bnFWGBGF2kSJlVjc7eVSSVio383A inq3i+86Mavfy18BwcP4I0NqUSvvcV9yduBLUySklJhOlhhHeFUlycQyxuODbrG9 QOd411c4eccsbDHq5vSnS7AJh6tVAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAI0H3 bJIUQgnSGyrSHmjoKm66QLBPvQ1Zd7Zxjlg1uFv6glPOhdkeTQx9XQPT/WDG3qmJ BdHvQLDtPS9P8vRaiQW1OCP7dQJkVYCxyFbSQiieuzwBAEGtZcLdZbvcp3PKRGbx sIrkzyYdAXE1EZ5z7yLVcpWwTKnBnuRz2D0XOk4= -----END CERTIFICATE----- request-2.88.1/tests/browser/ssl/server.crt000066400000000000000000000014521333333633500207570ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIICKTCCAZICCQDl9xx8ZXLMPTANBgkqhkiG9w0BAQUFADBZMQswCQYDVQQGEwJB VTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0 cyBQdHkgTHRkMRIwEAYDVQQDEwlsb2NhbGhvc3QwHhcNMTUwMTA3MTcwOTQ4WhcN MjUwMTA0MTcwOTQ4WjBZMQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0 ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRIwEAYDVQQDEwls b2NhbGhvc3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKWxvvLNi8AcT0wI sf+LoWAvtoIV29ypI6j1JRqmsPO433JP/ijLhJLFc6RKXpKs6pd4am82vzk8Bxs3 VtUXJ0yKh3KMevT7L4X1hw+QxvYAZD6Kl/kaNvKFTuAgcaeSnmnWGjQYLF/i20w7 7dpeXDmnNMCKwdg+kLeJdPtW0d+vAgMBAAEwDQYJKoZIhvcNAQEFBQADgYEADL6l Z2mDKj4kPyGD58uBGeBHjmcDA2oJcnsHhOiC1u1NuCwQW4RDWs6Ili0GhuHYHP0B JDcPw6ZSa1Gx3ZaUJ5yM/+YHpbLev34CjmiwQeG36DF2rAxfoIQk/wI4iWmu1+ed 5Wwc0cZAb10uy0ihmMK98yhVQPmkBOEyw2O1xJw= -----END CERTIFICATE----- request-2.88.1/tests/browser/ssl/server.key000066400000000000000000000015671333333633500207660ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIICXAIBAAKBgQClsb7yzYvAHE9MCLH/i6FgL7aCFdvcqSOo9SUaprDzuN9yT/4o y4SSxXOkSl6SrOqXeGpvNr85PAcbN1bVFydMiodyjHr0+y+F9YcPkMb2AGQ+ipf5 GjbyhU7gIHGnkp5p1ho0GCxf4ttMO+3aXlw5pzTAisHYPpC3iXT7VtHfrwIDAQAB AoGBAJa5edmk4NuA5SFlR4YOnl3BCWSMPdQciDPJzFbSC2WpZpm16p1xhMd+lhN9 E0qZwUzIXQmN46VM1aoMTRDKXxPqujUIvhn7kxMLmD8lajHzFUIhgnp1XQCfxIIV sCcnIoP+cbnzP+AegAEPjds/0ngI3YM28UeooqZAmZCHQ0cBAkEAz0go7tCxXqED +cY+P2axAKuGR+6srx08g5kONTpUx8jXr4su02F376dxhPB9UXWOJkYiGEBwKEds OnUSNTF/RQJBAMyjUzjb/u6sZqTcHd3owes3UsCC+kfSb814qdG3Z9qYX9p55ESu hA7Sbjq0WdTHGZdgEexwpfLtTRS8x5ldiGMCQFC3GLlmKqtep92rhLHLm0FXiYKZ PkUybU4RW6b+f+UMIHELEcDeQ4Xe/iV2QFZoIGJnDP/El+gXZ92bmOt9ysECQD/i zVx28gO5NuJJBdn9jGzOfLs1KMW7YMQY44thYr7Pyzz9yNHYWcn20Arruw++iLLF f1L9aBGLHAFZXkb2+FkCQA5/3Z3SgiZrRYX/bWcNe6N65+nGfJv8ZBVsX13pKOxR 8JzJLyEmx67IOGZvVgfVABrCHJvTrKlUO3x3mDoEPzI= -----END RSA PRIVATE KEY----- request-2.88.1/tests/browser/start.js000066400000000000000000000022541333333633500176320ustar00rootroot00000000000000'use strict' var spawn = require('child_process').spawn var https = require('https') var fs = require('fs') var path = require('path') var server = https.createServer({ key: fs.readFileSync(path.join(__dirname, '/ssl/server.key')), cert: fs.readFileSync(path.join(__dirname, '/ssl/server.crt')), ca: fs.readFileSync(path.join(__dirname, '/ssl/ca.crt')), requestCert: true, rejectUnauthorized: false }, function (req, res) { // Set CORS header, since that is something we are testing. res.setHeader('Access-Control-Allow-Origin', '*') res.writeHead(200) res.end('Can you hear the sound of an enormous door slamming in the depths of hell?\n') }) server.listen(0, function () { var port = this.address().port console.log('Started https server for karma tests on port ' + port) // Spawn process for karma. var c = spawn('karma', [ 'start', path.join(__dirname, '/karma.conf.js'), 'https://localhost:' + port ]) c.stdout.pipe(process.stdout) c.stderr.pipe(process.stderr) c.on('exit', function (c) { // Exit process with karma exit code. if (c !== 0) { throw new Error('Karma exited with status code ' + c) } server.close() }) }) request-2.88.1/tests/browser/test.js000066400000000000000000000016131333333633500174520ustar00rootroot00000000000000'use strict' if (!Function.prototype.bind) { // This is because of the fact that phantom.js does not have Function.bind. // This is a bug in phantom.js. // More info: https://github.com/ariya/phantomjs/issues/10522 /* eslint no-extend-native:0 */ Function.prototype.bind = require('function-bind') } var tape = require('tape') var request = require('../../index') tape('returns on error', function (t) { t.plan(1) request({ uri: 'https://stupid.nonexistent.path:port123/\\<-great-idea', withCredentials: false }, function (error, response) { t.equal(typeof error, 'object') t.end() }) }) tape('succeeds on valid URLs (with https and CORS)', function (t) { t.plan(1) request({ uri: __karma__.config.requestTestUrl, // eslint-disable-line no-undef withCredentials: false }, function (_, response) { t.equal(response.statusCode, 200) t.end() }) }) request-2.88.1/tests/fixtures/000077500000000000000000000000001333333633500163225ustar00rootroot00000000000000request-2.88.1/tests/fixtures/har.json000066400000000000000000000053441333333633500177750ustar00rootroot00000000000000{ "application-form-encoded": { "method": "POST", "headers": [ { "name": "content-type", "value": "application/x-www-form-urlencoded" } ], "postData": { "mimeType": "application/x-www-form-urlencoded; charset=UTF-8", "params": [ { "name": "foo", "value": "bar" }, { "name": "hello", "value": "world" } ] } }, "application-json": { "method": "POST", "headers": [ { "name": "content-type", "value": "application/json" } ], "postData": { "mimeType": "application/json", "text": "{\"number\":1,\"string\":\"f\\\"oo\",\"arr\":[1,2,3],\"nested\":{\"a\":\"b\"},\"arr_mix\":[1,\"a\",{\"arr_mix_nested\":{}}]}" } }, "cookies": { "method": "POST", "cookies": [ { "name": "foo", "value": "bar" }, { "name": "bar", "value": "baz" } ] }, "custom-method": { "method": "PROPFIND" }, "headers": { "method": "GET", "headers": [ { "name": "x-foo", "value": "Bar" } ] }, "multipart-data": { "method": "POST", "headers": [ { "name": "content-type", "value": "multipart/form-data" } ], "postData": { "mimeType": "multipart/form-data", "params": [ { "name": "foo", "value": "Hello World", "fileName": "hello.txt", "contentType": "text/plain" } ] } }, "multipart-file": { "method": "POST", "headers": [ { "name": "content-type", "value": "multipart/form-data" } ], "postData": { "mimeType": "multipart/form-data", "params": [ { "name": "foo", "fileName": "../tests/unicycle.jpg", "contentType": "image/jpeg" } ] } }, "multipart-form-data": { "method": "POST", "headers": [ { "name": "content-type", "value": "multipart/form-data" } ], "postData": { "mimeType": "multipart/form-data", "params": [ { "name": "foo", "value": "bar" } ] } }, "query": { "method": "GET", "queryString": [ { "name": "foo", "value": "bar" }, { "name": "foo", "value": "baz" }, { "name": "baz", "value": "abc" } ] }, "text-plain": { "method": "POST", "headers": [ { "name": "content-type", "value": "text/plain" } ], "postData": { "mimeType": "text/plain", "text": "Hello World" } } } request-2.88.1/tests/googledoodle.jpg000066400000000000000000000160231333333633500176200ustar00rootroot00000000000000PNG  IHDRd8 pHYs   OiCCPPhotoshop ICC profilexڝSgTS=BKKoR RB&*! J!QEEȠQ, !{kּ> H3Q5 B.@ $pd!s#~<<+"x M0B\t8K@zB@F&S`cbP-`'{[! eDh;VEX0fK9-0IWfH  0Q){`##xFW<+*x<$9E[-qWW.(I+6aa@.y24x6_-"bbϫp@t~,/;m%h^ uf@Wp~<5j>{-]cK'Xto(hw?G%fIq^D$.Tʳ?D*A, `6B$BB dr`)B(Ͱ*`/@4Qhp.U=pa( Aa!ڈbX#!H$ ɈQ"K5H1RT UH=r9\F;2G1Q= C7F dt1r=6Ыhڏ>C03l0.B8, c˱" VcϱwE 6wB aAHXLXNH $4 7 Q'"K&b21XH,#/{C7$C2'ITFnR#,4H#dk9, +ȅ3![ b@qS(RjJ4e2AURݨT5ZBRQ4u9̓IKhhitݕNWGw Ljg(gwLӋT071oUX**| J&*/Tު UUT^S}FU3S ԖUPSSg;goT?~YYLOCQ_ cx,!k u5&|v*=9C3J3WRf?qtN (~))4L1e\kXHQG6EYAJ'\'GgSSݧ M=:.kDwn^Loy}/TmG X $ <5qo</QC]@Caaᄑ.ȽJtq]zۯ6iܟ4)Y3sCQ? 0k߬~OCOg#/c/Wװwa>>r><72Y_7ȷOo_C#dz%gA[z|!?:eAAA!h쐭!ΑiP~aa~ 'W?pX15wCsDDDޛg1O9-J5*>.j<74?.fYXXIlK9.*6nl {/]py.,:@LN8A*%w% yg"/6шC\*NH*Mz쑼5y$3,幄'L Lݛ:v m2=:1qB!Mggfvˬen/kY- BTZ(*geWf͉9+̳ې7ᒶKW-X潬j9(xoʿܔĹdff-[n ڴ VE/(ۻCɾUUMfeI?m]Nmq#׹=TR+Gw- 6 U#pDy  :v{vg/jBFS[b[O>zG499?rCd&ˮ/~јѡ򗓿m|x31^VwwO| (hSЧc3-gAMA|Q cHRMz%u0`:o_F.IDATx]MOW18EV^.n|Q|8&>ReME64"q0p?=d9oy5X:G =^NV9W*Ę_OSAE{ \ #)ݔ/qgb x +T8pn\5^*Z9W>H@b `##4saLƍMܜ Tyfu4*uN{I(mĈ$b]ĭ|?RywGoZ7pG%1>%SM7"Әrg` Vkc;z߲$k\)KdzVsaD TE'8*`+= ?R1ag0TÚrqm)RQ"rw#ɓ 2[y܈ܼ.A3jVP"Sa@ f}VEJC-:ʄn0ٌ^hi2iB! ޵T_20BJfF WogF&f}?2ŀ/ Yq8HDpk~I!U`{QyO ip3_|ݎ(1^@(/iYLCR)!BWUu +%Ơ1eD9cU:q,~䠊FzsYQJ"8zQn`+z7KgGpVL*mbRA/~8 K1^pWO}2D^I 7?Z>L}8xWRNjP2*^ NldpYkIř_*8=!-4Fda|gǏ|C+ys<|,_!/g&\$`IZc?os-Jv9WZgJ+& *n~gGZ)JKG vrr,-! y_rrPWvlX-H+C:@>JtB(F2\@Y]c%H评R@ФVwaMTUK i+0zosS@ZJGEN/j4KYMB*i齉ФV@r-]J)S4,hVg> xZC2as˥Hh,v}(dYE !0ܿ{8$%ڞtH@aWyH;2' g~E@Rҏчr@ؗf㊐!Jͱ@KeMk0KkD,ԥV{ c-!Ŷ{xaӄ~XtdDϊ*: OC»0]cͭ3(54 b,%Ԏ13mc73_|}_?Zu]k!kMÂ"nזMqQA -)iTp(˃،X:˃a&1=UQ {<~HvcW"y5aq)aH$ ,KB:sOwmKv0Oe[`H ybe u }̕l&R@D嗔~ )8ɮwMw$t8e=U&M5 *䷪ [77Fra"[ bN5NtH)%*ƩhRa:.95TJotH 6U/;AVS7#=ƠLd ظ6G*Q Tx Hob9.G'J8N$*.&`[tZ+s궻fȳ#H{CKR%*l?K~rˆpS_2.X kDJ;I!m3˔Vfkخ9b^rs'N+hHQ?+Ote8cK)Re\%:egIxTY'$geIYeٲ]I:V҈d N5%? IL0R GUq g Qu \$G?-K =Ԛ[GgdC]Mnùd΀`<<a1$Cn@dC떢O`8.E ݀ &^`$K^9% gc@p*mp~gL FK|Y#LZP_ ٥rG>_4yT;+ kTblι2jpf$/VnUbD&d%vf%=8&<0ǎ~c].]:!]VR}I~; |e;Mv8ZcKc,+HɹΗ\|:Wlx,&(]݅Y%59O`W Nϳ6ub1!¹ A Qx8߃ Q֏ **z}q)x*zaꮕ A%$zD@P@P@P@Ct<\_ t]h!jWl ]T9Vz k.<&a=E!n#8U;)w.¤æ;0&γh4I]Xhns%;_,seг PLpfg?O~Q_K/H=:l+{6'}Wpu_r;Iph 7D%ƲguK& E8gw˔m ݂3`XQ<ol`863=B;lkIɣ [ceW$ɨNRVEq/J+h3N*pZ]B{voҲ$1@NL7D|X]UI~Y8g#Y{~DǾnޠ-I =gB &GT K = m=1N/I =mDjCt+H>'$>EyDC,tgHF}WI%1+X+wT+yxEyn'`O ܧs y>suZĤP-Rk}]sV `@֥LX: 4EO?Q+e?@IENDB`request-2.88.1/tests/server.js000066400000000000000000000074151333333633500163240ustar00rootroot00000000000000'use strict' var fs = require('fs') var http = require('http') var path = require('path') var https = require('https') var stream = require('stream') var assert = require('assert') exports.createServer = function () { var s = http.createServer(function (req, resp) { s.emit(req.url.replace(/(\?.*)/, ''), req, resp) }) s.on('listening', function () { s.port = this.address().port s.url = 'http://localhost:' + s.port }) s.port = 0 s.protocol = 'http' return s } exports.createEchoServer = function () { var s = http.createServer(function (req, resp) { var b = '' req.on('data', function (chunk) { b += chunk }) req.on('end', function () { resp.writeHead(200, {'content-type': 'application/json'}) resp.write(JSON.stringify({ url: req.url, method: req.method, headers: req.headers, body: b })) resp.end() }) }) s.on('listening', function () { s.port = this.address().port s.url = 'http://localhost:' + s.port }) s.port = 0 s.protocol = 'http' return s } exports.createSSLServer = function (opts) { var i var options = { 'key': path.join(__dirname, 'ssl', 'test.key'), 'cert': path.join(__dirname, 'ssl', 'test.crt') } if (opts) { for (i in opts) { options[i] = opts[i] } } for (i in options) { if (i !== 'requestCert' && i !== 'rejectUnauthorized' && i !== 'ciphers') { options[i] = fs.readFileSync(options[i]) } } var s = https.createServer(options, function (req, resp) { s.emit(req.url, req, resp) }) s.on('listening', function () { s.port = this.address().port s.url = 'https://localhost:' + s.port }) s.port = 0 s.protocol = 'https' return s } exports.createPostStream = function (text) { var postStream = new stream.Stream() postStream.writeable = true postStream.readable = true setTimeout(function () { postStream.emit('data', Buffer.from(text)) postStream.emit('end') }, 0) return postStream } exports.createPostValidator = function (text, reqContentType) { var l = function (req, resp) { var r = '' req.on('data', function (chunk) { r += chunk }) req.on('end', function () { if (req.headers['content-type'] && req.headers['content-type'].indexOf('boundary=') >= 0) { var boundary = req.headers['content-type'].split('boundary=')[1] text = text.replace(/__BOUNDARY__/g, boundary) } assert.equal(r, text) if (reqContentType) { assert.ok(req.headers['content-type']) assert.ok(~req.headers['content-type'].indexOf(reqContentType)) } resp.writeHead(200, {'content-type': 'text/plain'}) resp.write(r) resp.end() }) } return l } exports.createPostJSONValidator = function (value, reqContentType) { var l = function (req, resp) { var r = '' req.on('data', function (chunk) { r += chunk }) req.on('end', function () { var parsedValue = JSON.parse(r) assert.deepEqual(parsedValue, value) if (reqContentType) { assert.ok(req.headers['content-type']) assert.ok(~req.headers['content-type'].indexOf(reqContentType)) } resp.writeHead(200, {'content-type': 'application/json'}) resp.write(r) resp.end() }) } return l } exports.createGetResponse = function (text, contentType) { var l = function (req, resp) { contentType = contentType || 'text/plain' resp.writeHead(200, {'content-type': contentType}) resp.write(text) resp.end() } return l } exports.createChunkResponse = function (chunks, contentType) { var l = function (req, resp) { contentType = contentType || 'text/plain' resp.writeHead(200, {'content-type': contentType}) chunks.forEach(function (chunk) { resp.write(chunk) }) resp.end() } return l } request-2.88.1/tests/squid.conf000066400000000000000000000046701333333633500164540ustar00rootroot00000000000000# # Recommended minimum configuration: # acl localhost src 127.0.0.1/32 ::1 acl to_localhost dst 127.0.0.0/8 0.0.0.0/32 ::1 # Example rule allowing access from your local networks. # Adapt to list your (internal) IP networks from where browsing # should be allowed acl localnet src 10.0.0.0/8 # RFC1918 possible internal network acl localnet src 172.16.0.0/12 # RFC1918 possible internal network acl localnet src 192.168.0.0/16 # RFC1918 possible internal network acl localnet src fc00::/7 # RFC 4193 local private network range acl localnet src fe80::/10 # RFC 4291 link-local (directly plugged) machines acl SSL_ports port 443 acl Safe_ports port 80 # http acl Safe_ports port 21 # ftp acl Safe_ports port 443 # https acl Safe_ports port 70 # gopher acl Safe_ports port 210 # wais acl Safe_ports port 1025-65535 # unregistered ports acl Safe_ports port 280 # http-mgmt acl Safe_ports port 488 # gss-http acl Safe_ports port 591 # filemaker acl Safe_ports port 777 # multiling http acl CONNECT method CONNECT # # Recommended minimum Access Permission configuration: # # Only allow cachemgr access from localhost http_access allow manager localhost http_access deny manager # Deny requests to certain unsafe ports http_access deny !Safe_ports # Deny CONNECT to other than secure SSL ports #http_access deny CONNECT !SSL_ports # We strongly recommend the following be uncommented to protect innocent # web applications running on the proxy server who think the only # one who can access services on "localhost" is a local user #http_access deny to_localhost # # INSERT YOUR OWN RULE(S) HERE TO ALLOW ACCESS FROM YOUR CLIENTS # # Example rule allowing access from your local networks. # Adapt localnet in the ACL section to list your (internal) IP networks # from where browsing should be allowed http_access allow localnet http_access allow localhost # And finally deny all other access to this proxy http_access deny all # Squid normally listens to port 3128 http_port 3128 # We recommend you to use at least the following line. hierarchy_stoplist cgi-bin ? # Uncomment and adjust the following to add a disk cache directory. #cache_dir ufs /usr/local/var/cache 100 16 256 # Leave coredumps in the first cache dir coredump_dir /usr/local/var/cache # Add any of your own refresh_pattern entries above these. refresh_pattern ^ftp: 1440 20% 10080 refresh_pattern ^gopher: 1440 0% 1440 refresh_pattern -i (/cgi-bin/|\?) 0 0% 0 refresh_pattern . 0 20% 4320 request-2.88.1/tests/ssl/000077500000000000000000000000001333333633500152525ustar00rootroot00000000000000request-2.88.1/tests/ssl/ca/000077500000000000000000000000001333333633500156355ustar00rootroot00000000000000request-2.88.1/tests/ssl/ca/ca.cnf000066400000000000000000000011041333333633500167040ustar00rootroot00000000000000[ req ] default_bits = 1024 days = 3650 distinguished_name = req_distinguished_name attributes = req_attributes prompt = no output_password = password [ req_distinguished_name ] C = US ST = CA L = Oakland O = request OU = request Certificate Authority CN = requestCA emailAddress = mikeal@mikealrogers.com [ req_attributes ] challengePassword = password challenge request-2.88.1/tests/ssl/ca/ca.crl000066400000000000000000000000001333333633500167100ustar00rootroot00000000000000request-2.88.1/tests/ssl/ca/ca.crt000066400000000000000000000017611333333633500167370ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIICvTCCAiYCCQDn+P/MSbDsWjANBgkqhkiG9w0BAQUFADCBojELMAkGA1UEBhMC VVMxCzAJBgNVBAgTAkNBMRAwDgYDVQQHEwdPYWtsYW5kMRAwDgYDVQQKEwdyZXF1 ZXN0MSYwJAYDVQQLEx1yZXF1ZXN0IENlcnRpZmljYXRlIEF1dGhvcml0eTESMBAG A1UEAxMJcmVxdWVzdENBMSYwJAYJKoZIhvcNAQkBFhdtaWtlYWxAbWlrZWFscm9n ZXJzLmNvbTAeFw0xMjAzMDEyMjUwNTZaFw0yMjAyMjcyMjUwNTZaMIGiMQswCQYD VQQGEwJVUzELMAkGA1UECBMCQ0ExEDAOBgNVBAcTB09ha2xhbmQxEDAOBgNVBAoT B3JlcXVlc3QxJjAkBgNVBAsTHXJlcXVlc3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5 MRIwEAYDVQQDEwlyZXF1ZXN0Q0ExJjAkBgkqhkiG9w0BCQEWF21pa2VhbEBtaWtl YWxyb2dlcnMuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC7t9pQUAK4 5XJYTI6NrF0n3G2HZsfN+rPYSVzzL8SuVyb1tHXos+vbPm3NKI4E8X1yVAXU8CjJ 5SqXnp4DAypAhaseho81cbhk7LXUhFz78OvAa+OD+xTAEAnNQ8tGUr4VGyplEjfD xsBVuqV2j8GPNTftr+drOCFlqfAgMrBn4wIDAQABMA0GCSqGSIb3DQEBBQUAA4GB ADVdTlVAL45R+PACNS7Gs4o81CwSclukBu4FJbxrkd4xGQmurgfRrYYKjtqiopQm D7ysRamS3HMN9/VKq2T7r3z1PMHPAy7zM4uoXbbaTKwlnX4j/8pGPn8Ca3qHXYlo 88L/OOPc6Di7i7qckS3HFbXQCTiULtxWmy97oEuTwrAj -----END CERTIFICATE----- request-2.88.1/tests/ssl/ca/ca.csr000066400000000000000000000014111333333633500167260ustar00rootroot00000000000000-----BEGIN CERTIFICATE REQUEST----- MIICBjCCAW8CAQAwgaIxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEQMA4GA1UE BxMHT2FrbGFuZDEQMA4GA1UEChMHcmVxdWVzdDEmMCQGA1UECxMdcmVxdWVzdCBD ZXJ0aWZpY2F0ZSBBdXRob3JpdHkxEjAQBgNVBAMTCXJlcXVlc3RDQTEmMCQGCSqG SIb3DQEJARYXbWlrZWFsQG1pa2VhbHJvZ2Vycy5jb20wgZ8wDQYJKoZIhvcNAQEB BQADgY0AMIGJAoGBALu32lBQArjlclhMjo2sXSfcbYdmx836s9hJXPMvxK5XJvW0 deiz69s+bc0ojgTxfXJUBdTwKMnlKpeengMDKkCFqx6GjzVxuGTstdSEXPvw68Br 44P7FMAQCc1Dy0ZSvhUbKmUSN8PGwFW6pXaPwY81N+2v52s4IWWp8CAysGfjAgMB AAGgIzAhBgkqhkiG9w0BCQcxFBMScGFzc3dvcmQgY2hhbGxlbmdlMA0GCSqGSIb3 DQEBBQUAA4GBAGJO7grHeVHXetjHEK8urIxdnvfB2qeZeObz4GPKIkqUurjr0rfj bA3EK1kDMR5aeQWR8RunixdM16Q6Ry0lEdLVWkdSwRN9dmirIHT9cypqnD/FYOia SdezZ0lUzXgmJIwRYRwB1KSMMocIf52ll/xC2bEGg7/ZAEuAyAgcZV3X -----END CERTIFICATE REQUEST----- request-2.88.1/tests/ssl/ca/ca.key000066400000000000000000000017031333333633500167330ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- Proc-Type: 4,ENCRYPTED DEK-Info: DES-EDE3-CBC,C8B5887048377F02 nyD5ZH0Wup2uWsDvurq5mKDaDrf8lvNn9w0SH/ZkVnfR1/bkwqrFriqJWvZNUG+q nS0iBYczsWLJnbub9a1zLOTENWUKVD5uqbC3aGHhnoUTNSa27DONgP8gHOn6JgR+ GAKo01HCSTiVT4LjkwN337QKHnMP2fTzg+IoC/CigvMcq09hRLwU1/guq0GJKGwH gTxYNuYmQC4Tjh8vdS4liF+Ve/P3qPR2CehZrIOkDT8PHJBGQJRo4xGUIB7Tpk38 VCk+UZ0JCS2coY8VkY/9tqFJp/ZnnQQVmaNbdRqg7ECKL+bXnNo7yjzmazPZmPe3 /ShbE0+CTt7LrjCaQAxWbeDzqfo1lQfgN1LulTm8MCXpQaJpv7v1VhIhQ7afjMYb 4thW/ypHPiYS2YJCAkAVlua9Oxzzh1qJoh8Df19iHtpd79Q77X/qf+1JvITlMu0U gi7yEatmQcmYNws1mtTC1q2DXrO90c+NZ0LK/Alse6NRL/xiUdjug2iHeTf/idOR Gg/5dSZbnnlj1E5zjSMDkzg6EHAFmHV4jYGSAFLEQgp4V3ZhMVoWZrvvSHgKV/Qh FqrAK4INr1G2+/QTd09AIRzfy3/j6yD4A9iNaOsEf9Ua7Qh6RcALRCAZTWR5QtEf dX+iSNJ4E85qXs0PqwkMDkoaxIJ+tmIRJY7y8oeylV8cfGAi8Soubt/i3SlR8IHC uDMas/2OnwafK3N7ODeE1i7r7wkzQkSHaEz0TrF8XRnP25jAICCSLiMdAAjKfxVb EvzsFSuAy3Jt6bU3hSLY9o4YVYKE+68ITMv9yNjvTsEiW+T+IbN34w== -----END RSA PRIVATE KEY----- request-2.88.1/tests/ssl/ca/ca.srl000066400000000000000000000000211333333633500167330ustar00rootroot00000000000000ADF62016AA40C9C5 request-2.88.1/tests/ssl/ca/client-enc.key000066400000000000000000000033461333333633500203760ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- Proc-Type: 4,ENCRYPTED DEK-Info: AES-128-CBC,7FD4AC5078EE99FCA866A397D64EAC81 +CpgDIrsR4ZjXY5uo4rG8WxFgwnElNlj4c9ujgUgyx1vwerNYn59MKruDi2oMUHc jDyN68zSiRIc2zuXnfHPtUUbt4CN1Xy2DOCZ0Fkrr4hNOlwpP6IZYJcjTJ+evnIO EeayFEYoZhqyPgnGleWwNpOEc/33jsXup/DQHfmi0ot6rfdg1kpT/pAhhYE2ivQo mfdAizNAluyM2yggdEmRJoWrC+YAxb5fW1wAQFT5YOS+t5TBlCphj18JhsEbeKZg eS1ZNg+8YRSHYF2U7xN7AKtzdBi4Fof8sXhW1MOVU0Ebfg7QibBtPoaz59NT0+Pj r507m2RrXjBwuoU08L1hOA4C5rXd/sT3B925jBpTE05GC8zYouNoazafxMwij+ZO 7HK2Uza3EuaQHIEi1QxARM+m0xv3LDPRJyWKnaOxTzeKmeEpM4471x9YJU07E1+K VrKomLLiWenb92ZAYVf+Mm0BSZyfKaSLf/zvgaPKPutNzGM0zdjsfFTe4EgcZbLq 2HpaN8TziAxTrqTXlGqs2yiOnzEcpxU7z+skWZxY/bOVtBAEoCgZW1G6Kn4ndgwf YBKmi+8RQoV6F6kOvIdoQKueAlIpDuiwR26nS64LPe8Otzu7Mz8oY6Ffqk+REw/h NM/iEatq7q5AQ4abMcM0+Zzv6xwmWQsUdLim3GOc/OMnOP3SS6syFgcG4AMfe+1F sppwie3SfeJvmhfMvM1nSYLfK4Uh4J4fZ/OnXl31U9kWNKhRpTcFAhcY2Rx0GuKY zY8bFoPwJ69wyYpdzCukeegsDgWdxZS5XBgD5rJET6fgDc+M83TSDbEEsShsXyEy CX94x8RHJkGuC34ZwZgCOUhkt0E7Xk6nlWLhQKTG4JpF3q627W1pbf5l8odrJGEm axddw8ooynru3m60lNm6oWjnkJ/xa+Q96X2OWpKM78R1TK6YeDpwYJ+7qz9k+vnO FNzN5uVxeUvsL9myqGovxqWoYFcpqjJH7Jm6n8S/YeAeJ3CfK8ooZBqKQGQjPt5r E1wWZWfHIj0Eb53X2+C7aG/4FtjZGw7srESgNMeN/H9eRh/vWBRFqOeiX2T3/Ha9 tG38RZaMjxJ9nPvw58yWtVSOF1Ws5Are/nhJz5Yto8Rh89rnKw6N/L1/oF9nwdiy 2/xx4SiO3UiYQJ+EpiEfoBzup17ZrwWgcGSoWR+3wzt0Ci0VzrdbpKzrIUKLHr8V 5QrdBb0Z0qzHODc6e/k92n+py1XXQ0gyLhuk6YngzkKADcAQFlh8ry0/izEOeMKe RP9huGEX/KSVkWg5F9d1s+49YbYXkhWbrninLu2SUTs02E04+Y9xuyFqXZRU0PzZ J/zn/FS/Uby0G5vhj7J5G1nOCHrqS0xI3QF4CVmXE3NWj0w3XwypDwVW6aG2gvrm Bql/YGL2PC9c74rN/OnIrW0VprWh61+QNUqL+4yTP864HxGw9cAiSU2NLiAf7vye yAKzUf2c9ZNqpafmiPRzg772yPIemPthNUNXV4AuH33LRz5V7mUYqExnAOTNi+FR XE98PcNiGCkrdX+VyqQq77SB52O0tHuNyAk+DE0siz3XbJtTXGPSHjNXc2gNWc9n -----END RSA PRIVATE KEY----- request-2.88.1/tests/ssl/ca/client.cnf000066400000000000000000000010611333333633500176010ustar00rootroot00000000000000[ req ] default_bits = 1024 days = 3650 distinguished_name = req_distinguished_name attributes = req_attributes prompt = no output_password = password [ req_distinguished_name ] C = US ST = CA L = Oakland O = request OU = request@localhost CN = TestClient emailAddress = do.not@email.me [ req_attributes ] challengePassword = password challenge request-2.88.1/tests/ssl/ca/client.crt000066400000000000000000000022141333333633500176240ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIDLjCCApcCCQCt9iAWqkDJxDANBgkqhkiG9w0BAQsFADCBojELMAkGA1UEBhMC VVMxCzAJBgNVBAgTAkNBMRAwDgYDVQQHEwdPYWtsYW5kMRAwDgYDVQQKEwdyZXF1 ZXN0MSYwJAYDVQQLEx1yZXF1ZXN0IENlcnRpZmljYXRlIEF1dGhvcml0eTESMBAG A1UEAxMJcmVxdWVzdENBMSYwJAYJKoZIhvcNAQkBFhdtaWtlYWxAbWlrZWFscm9n ZXJzLmNvbTAeFw0xODA3MDgxMTQ5MjRaFw0yMTA3MDcxMTQ5MjRaMIGPMQswCQYD VQQGEwJVUzELMAkGA1UECAwCQ0ExEDAOBgNVBAcMB09ha2xhbmQxEDAOBgNVBAoM B3JlcXVlc3QxGjAYBgNVBAsMEXJlcXVlc3RAbG9jYWxob3N0MRMwEQYDVQQDDApU ZXN0Q2xpZW50MR4wHAYJKoZIhvcNAQkBFg9kby5ub3RAZW1haWwubWUwggEiMA0G CSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDRD3GmedALZpDCXQ78C2Ehn0R+F7jn 4cbOTOHOBKGbFEb25Is7kHWQfB4AbOgXLs/S6x8DgFHNKr5iIoHiJerSIeGRTJtL NnJsXfn7c9OZtBwmR3euiQO/evP7HSJZvel4rhb+8pRMf2MAxBC0D6z56iblnULJ aohqjDPv9D38g7RI7YVurK05fjwnwB21/GRYh+vm5qdw78N+CTMP2rY5IHj4MJcx qNpJbgMWZ8vDUY1Uf9JNQbrl1lKYeDFzhE3j+1uAPV711srJLvCUqoXPbfS1KKAJ AJQcheBzd4Ul6wWULBCMLGPw3j6xOoBz6iKwAn+qOQgro2QQpidj/gkFAgMBAAEw DQYJKoZIhvcNAQELBQADgYEAXopA8nlbYxyKoJeNXKX/+sWtbqYxzfxVo/6iOFMX 3ZSggFFMKCw7pEVgXy4nONNR98C4ga74lo6ljUY+B3xGQxEDYwK1xVgekA2XfQYJ /ygAdoliF8BEkQ8b9ZoIwmBAIZRQO9b0DzucycvCag7km0O2uWJYQGzFIOQCxJ+v 9r0= -----END CERTIFICATE----- request-2.88.1/tests/ssl/ca/client.csr000066400000000000000000000021221333333633500176210ustar00rootroot00000000000000-----BEGIN CERTIFICATE REQUEST----- MIIC+DCCAeACAQAwgY8xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTEQMA4GA1UE BwwHT2FrbGFuZDEQMA4GA1UECgwHcmVxdWVzdDEaMBgGA1UECwwRcmVxdWVzdEBs b2NhbGhvc3QxEzARBgNVBAMMClRlc3RDbGllbnQxHjAcBgkqhkiG9w0BCQEWD2Rv Lm5vdEBlbWFpbC5tZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBANEP caZ50AtmkMJdDvwLYSGfRH4XuOfhxs5M4c4EoZsURvbkizuQdZB8HgBs6Bcuz9Lr HwOAUc0qvmIigeIl6tIh4ZFMm0s2cmxd+ftz05m0HCZHd66JA7968/sdIlm96Xiu Fv7ylEx/YwDEELQPrPnqJuWdQslqiGqMM+/0PfyDtEjthW6srTl+PCfAHbX8ZFiH 6+bmp3Dvw34JMw/atjkgePgwlzGo2kluAxZny8NRjVR/0k1BuuXWUph4MXOETeP7 W4A9XvXWysku8JSqhc9t9LUooAkAlByF4HN3hSXrBZQsEIwsY/DePrE6gHPqIrAC f6o5CCujZBCmJ2P+CQUCAwEAAaAjMCEGCSqGSIb3DQEJBzEUDBJwYXNzd29yZCBj aGFsbGVuZ2UwDQYJKoZIhvcNAQELBQADggEBAIVRC0Ct5EETEdbCZRrd2/F7Ujkp 1y7M9diKeXEN+3OuGDuStPe6DM/nO4wz++JBB+NzKAfbr/bMEFnS8wbRFsxGY287 HyqAYAG8JZZpkcMnr2aXgdcT0YpCuGYh23+r18b34L2050Wmc/C1tJtxj0hAt4qg Vr1HJQ67V4d2w3BIzq8wTmvBD//ofwydweYXWd7F1zcLgO36HcA8Na4eko6m0dpw jRbxD1hyrXGkC1CkD43TnZWkIpARXtWzv2G9iaUGyVsVvRrAyts8+ZRu1SGNfdkG HmBqEzn8mMBc92OYO2OGf/CkueSPivJ0JrbxWKktjytpsBUWwnwBsO/vwDQ= -----END CERTIFICATE REQUEST----- request-2.88.1/tests/ssl/ca/client.key000066400000000000000000000032171333333633500176300ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEA0Q9xpnnQC2aQwl0O/AthIZ9Efhe45+HGzkzhzgShmxRG9uSL O5B1kHweAGzoFy7P0usfA4BRzSq+YiKB4iXq0iHhkUybSzZybF35+3PTmbQcJkd3 rokDv3rz+x0iWb3peK4W/vKUTH9jAMQQtA+s+eom5Z1CyWqIaowz7/Q9/IO0SO2F bqytOX48J8AdtfxkWIfr5uancO/DfgkzD9q2OSB4+DCXMajaSW4DFmfLw1GNVH/S TUG65dZSmHgxc4RN4/tbgD1e9dbKyS7wlKqFz230tSigCQCUHIXgc3eFJesFlCwQ jCxj8N4+sTqAc+oisAJ/qjkIK6NkEKYnY/4JBQIDAQABAoIBAF7P3EEd2YZyG5Cq V5NjLcfrzUpKQ+eV8224XGfsncYRKiXqfGKlH0xJnemfepqY9lO3ojcaSP79NZ6X +8OuYpKuHvigf4VaygXvkOHDI+H/VwzdOKAFL5f1kRT/n4aHpIzAl1lEdpFC7Il6 YgDnYxFsafuUmKd0Ey4PK7bVVA9icagrWCaRcNBuA8rOHUKejlwag9uFthQzXVib mRNl0Oc8TgYRnP53vicsJm2zxj/Mvg/ZpefoSDaq1zanNWGjbr0exI3/bFAScWkF ThfTn9NIzyrRCFwNLRV3BcgfALPrP86Npc7fkGDhSUj0Vg5I0FqiF3Bzx5zx5mSB ZO08JnkCgYEA8Vt8zEhhEU96Lys15K4oeX9YXliUmpF8ACjiOc5MTGG5wbjFUptF 8nYfxzgMIYfimPeGUY7E6dgaAwh1tNm5DZUjKuhGHeKxkBHsWeKC3/yRXjdZHAt8 bQr1W/GIA/fWg4N03n0oq4uPcbyUbLY2rJ6eIRvfFiEMTlxciKO7lOMCgYEA3b5a K9fQ3Bm1UxuQt25KmR8DfQkc/ylXMEE2mcMbi8A9Gnw2t/OFyJGruXMWkhoOusms 0EO20Qc7cR+sY68qLpygHvtTyouEKv4ss6BYZrLd8eFTQI6m2pQNhKKxdzKyeb8n Xr06v15Z7WhuENMN2/vE7BC+cXDZg9zotbm4tvcCgYEA0mGy6MZ2hgZHNPJvacQ9 V5qfRq3j6s/BzMnWWBjw/Ot6ZdhPd/ANConYrWi3ekreRAQOuuy9zDAojFhFcb0O xz4mh3IsHETMDg7xfHArMF8Rv5RzQjTo4ovYz6o7q2nPPJfLuVxTpSRjhvqgThqO ke05XRbUYI+yEGQF7Lz7940CgYBz06+UQTo3DjK6A6cXOcQ7sYiH8o+z9Ss26ImV zeWAnV0NjZ6jfc//EaBq0WQT0wqopRng+83t5+Iz2ACbXW8iQ+wb4tpE7ZWPQ4+k EHi8xGfMpg9vpFQhzr407yrWAaRalfABu8SJG8bLjQYZQbV2mE+no6Nm7DSifW0N J8MFxwKBgQDlNxXCIFtNNLpCUV6mHryOseCF5GYdHcqozS85ea4DkGJPwHxt9/Ev t+aFdki2eROSv5bFZv8IGR+7+h80x3fuWtjPRX4acG35voLDw+VKUkmLr3Haw1TO XQdHNklrXAWWSfvdQjnPg+80/7ecDZyRPIlKvehxpfj91duxoVPRLQ== -----END RSA PRIVATE KEY----- request-2.88.1/tests/ssl/ca/gen-client.sh000077500000000000000000000012501333333633500202170ustar00rootroot00000000000000#!/bin/sh # Adapted from: # http://nodejs.org/api/tls.html # https://github.com/joyent/node/blob/master/test/fixtures/keys/Makefile # Create a private key openssl genrsa -out client.key 2048 # Create a certificate signing request openssl req -new -sha256 -key client.key -out client.csr -config client.cnf -days 1095 # Use the CSR and the CA key (previously generated) to create a certificate openssl x509 -req \ -in client.csr \ -CA ca.crt \ -CAkey ca.key \ -set_serial 0x`cat ca.srl` \ -passin 'pass:password' \ -out client.crt \ -days 1095 # Encrypt with password openssl rsa -aes128 -in client.key -out client-enc.key -passout 'pass:password' request-2.88.1/tests/ssl/ca/gen-localhost.sh000077500000000000000000000011211333333633500207260ustar00rootroot00000000000000#!/bin/sh # Adapted from: # http://nodejs.org/api/tls.html # https://github.com/joyent/node/blob/master/test/fixtures/keys/Makefile # Create a private key openssl genrsa -out localhost.key 2048 # Create a certificate signing request openssl req -new -sha256 -key localhost.key -out localhost.csr -config localhost.cnf -days 1095 # Use the CSR and the CA key (previously generated) to create a certificate openssl x509 -req \ -in localhost.csr \ -CA ca.crt \ -CAkey ca.key \ -set_serial 0x`cat ca.srl` \ -passin 'pass:password' \ -out localhost.crt \ -days 1095 request-2.88.1/tests/ssl/ca/gen-server.sh000077500000000000000000000005211333333633500202470ustar00rootroot00000000000000#!/bin/sh # fixes: # Error: error:140AB18F:SSL routines:SSL_CTX_use_certificate:ee key too small # on Node > v10 openssl genrsa 4096 > server.key openssl req -new -nodes -sha256 -key server.key -config server.cnf -out server.csr openssl x509 -req -sha256 -in server.csr -CA ca.crt -CAkey ca.key -out server.crt # password: password request-2.88.1/tests/ssl/ca/localhost.cnf000066400000000000000000000010601333333633500203120ustar00rootroot00000000000000[ req ] default_bits = 1024 days = 3650 distinguished_name = req_distinguished_name attributes = req_attributes prompt = no output_password = password [ req_distinguished_name ] C = US ST = CA L = Oakland O = request OU = request@localhost CN = localhost emailAddress = do.not@email.me [ req_attributes ] challengePassword = password challenge request-2.88.1/tests/ssl/ca/localhost.crt000066400000000000000000000022141333333633500203360ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIDLTCCApYCCQCt9iAWqkDJxDANBgkqhkiG9w0BAQsFADCBojELMAkGA1UEBhMC VVMxCzAJBgNVBAgTAkNBMRAwDgYDVQQHEwdPYWtsYW5kMRAwDgYDVQQKEwdyZXF1 ZXN0MSYwJAYDVQQLEx1yZXF1ZXN0IENlcnRpZmljYXRlIEF1dGhvcml0eTESMBAG A1UEAxMJcmVxdWVzdENBMSYwJAYJKoZIhvcNAQkBFhdtaWtlYWxAbWlrZWFscm9n ZXJzLmNvbTAeFw0xODA3MDgxMTQ5MjlaFw0yMTA3MDcxMTQ5MjlaMIGOMQswCQYD VQQGEwJVUzELMAkGA1UECAwCQ0ExEDAOBgNVBAcMB09ha2xhbmQxEDAOBgNVBAoM B3JlcXVlc3QxGjAYBgNVBAsMEXJlcXVlc3RAbG9jYWxob3N0MRIwEAYDVQQDDAls b2NhbGhvc3QxHjAcBgkqhkiG9w0BCQEWD2RvLm5vdEBlbWFpbC5tZTCCASIwDQYJ KoZIhvcNAQEBBQADggEPADCCAQoCggEBAKoTEIRDVlcBLaEggfE4eosRFjLhc0sg a3NlLC/67T6hAN2/xbH1yCQpvrq0Xgi5FMXFp0rkFqcLSOEQ4mQH/wFtbj5+IbTj eZvv2d7G5J+3hY7ALCDoMlwb0ifX8w5qMwPaiGdk7l0Wp/M81IALyVyKrKwlOVqT ti/2hmhQBHGVLITso/QaGJenCnJ7tkZ6nFYYps0b2sl863jHnmaeY/QYGdCH+Nqn n6nyuRfLekjboUfRAIqMfxarwVRxBVg4N9YLvT+Qm0U4ZtCCuMXRaKC5YRp5sK/7 GSngACB3En3ndP71ry6sxwova3Yb4Qeei1S/JonIr+KDTlmko8SXtnkCAwEAATAN BgkqhkiG9w0BAQsFAAOBgQCpn2KTeSNsI95wVwDaXS4zkb4FtsFdG4368Bt0tKSc HUlv8OL+h+gJOSfap+0WbY/cBMzGym+mS8MZFXYpDEmknyuSv+Rqs3DEP5nkBZWb HaaIv1UrUF6XHh/C6kToNXRjZQCKYu2TWiqeA1psdBZBMJPwnvKiG+FKr+fZUAEv Ug== -----END CERTIFICATE----- request-2.88.1/tests/ssl/ca/localhost.csr000066400000000000000000000021221333333633500203330ustar00rootroot00000000000000-----BEGIN CERTIFICATE REQUEST----- MIIC9zCCAd8CAQAwgY4xCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTEQMA4GA1UE BwwHT2FrbGFuZDEQMA4GA1UECgwHcmVxdWVzdDEaMBgGA1UECwwRcmVxdWVzdEBs b2NhbGhvc3QxEjAQBgNVBAMMCWxvY2FsaG9zdDEeMBwGCSqGSIb3DQEJARYPZG8u bm90QGVtYWlsLm1lMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqhMQ hENWVwEtoSCB8Th6ixEWMuFzSyBrc2UsL/rtPqEA3b/FsfXIJCm+urReCLkUxcWn SuQWpwtI4RDiZAf/AW1uPn4htON5m+/Z3sbkn7eFjsAsIOgyXBvSJ9fzDmozA9qI Z2TuXRan8zzUgAvJXIqsrCU5WpO2L/aGaFAEcZUshOyj9BoYl6cKcnu2RnqcVhim zRvayXzreMeeZp5j9BgZ0If42qefqfK5F8t6SNuhR9EAiox/FqvBVHEFWDg31gu9 P5CbRThm0IK4xdFooLlhGnmwr/sZKeAAIHcSfed0/vWvLqzHCi9rdhvhB56LVL8m iciv4oNOWaSjxJe2eQIDAQABoCMwIQYJKoZIhvcNAQkHMRQMEnBhc3N3b3JkIGNo YWxsZW5nZTANBgkqhkiG9w0BAQsFAAOCAQEAW/XOubJr04gbdTAkBLjpsYthwOzd uaX9V8K/tTa8bHKSLGN1AMLAVXu8LTfak8JoWXpGrHlFzvnzZZWMUeUAyaG97fBd ewnmainh6auACjH8iK1/iRot0D9rvW+32kUyAK9p3mgtRraELiIWMcPZ9eWndZc/ qRm3S4tPsSSqPLPZNI9BeJ6u7eSGvC0LjdoP5usyNvd+GCO9ZXozBpUfVqV6LULc D1mMSh08V9/54UcGVDoG5A+BZJx0Eq9ALirJnFXj96lpVc1VRQ4R7tRA+qFaJr7R 017go+qy2ZS7SMoTB2eA6M7eitfurQaBcBntPzAqq6nkRNOekzSYYFtYvg== -----END CERTIFICATE REQUEST----- request-2.88.1/tests/ssl/ca/localhost.js000066400000000000000000000014001333333633500201560ustar00rootroot00000000000000'use strict' var fs = require('fs') var https = require('https') var options = { key: fs.readFileSync('./localhost.key'), cert: fs.readFileSync('./localhost.crt') } var server = https.createServer(options, function (req, res) { res.writeHead(200) res.end() server.close() }) server.listen(0, function () { var ca = fs.readFileSync('./ca.crt') var agent = new https.Agent({ host: 'localhost', port: this.address().port, ca: ca }) https.request({ host: 'localhost', method: 'HEAD', port: this.address().port, agent: agent, ca: [ ca ], path: '/' }, function (res) { if (res.socket.authorized) { console.log('node test: OK') } else { throw new Error(res.socket.authorizationError) } }).end() }) request-2.88.1/tests/ssl/ca/localhost.key000066400000000000000000000032171333333633500203420ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEAqhMQhENWVwEtoSCB8Th6ixEWMuFzSyBrc2UsL/rtPqEA3b/F sfXIJCm+urReCLkUxcWnSuQWpwtI4RDiZAf/AW1uPn4htON5m+/Z3sbkn7eFjsAs IOgyXBvSJ9fzDmozA9qIZ2TuXRan8zzUgAvJXIqsrCU5WpO2L/aGaFAEcZUshOyj 9BoYl6cKcnu2RnqcVhimzRvayXzreMeeZp5j9BgZ0If42qefqfK5F8t6SNuhR9EA iox/FqvBVHEFWDg31gu9P5CbRThm0IK4xdFooLlhGnmwr/sZKeAAIHcSfed0/vWv LqzHCi9rdhvhB56LVL8miciv4oNOWaSjxJe2eQIDAQABAoIBAH3b++YVOujKC21o 9CCB7lXJwEbJBpw7Eqlj3q5nIHivh0eS6odG3uS8K9RZNBl6df/hxGqsnoLh2/4K k675J+JzjBkdGG6XxF/8wJuXTotPsbuxRTbY/qOhRwWLTuiE+NnKOBVj4O3snT9o 7c0Qa+RbD2uZZHc+Rp357v9078TpLVO57DpSNpUDGBLzVkzrh2X9oRoqt4BqLKAo kknn0X03e2LTcULA8ABwCESHzqQPZgUCeR2xqmKt+zH3KeAJCViECZLokQRIbFb9 XjmQil4sJ9LmHobd0DeVjpduEe2hjy7RNs1JiWg0DipJjcYVijY0TFrWSDO55Qih F/3gAq0CgYEA4FMJtWx79yeSuagp3rhVoKMGkDDqRCxbZCFt24uddW1qZjW6ibNU QszjbyISFBgApM3CSdzE15gyik0scdfWyVRawDnphMG6p8Trc2dsQYmbb9tlplNb hh5gTOCHs9xeAUYBA6jwxhM6/3kPHfsTm2mBpCOIUtljpyVll5qD4nMCgYEAwhb7 F20Mg6clXVOQECuvrwAwVVnZhjaCRaYm1mYrbotcVLkHIJaGoOE4DlYxZLwUcRzd GcaW/eJ/lNkXC4b+tj+ebcfjgW38A6hk9J/e0PaT6SIUJ4PLAU9RUp33oOiuxZAh SnREJGJurJ26Rm0t2Zj/ymmobu34gmUGGwHvTGMCgYEAkuVNokRcGUkMyA7a/EHx sLORBLNDdUkmv7c0XWRbxB3WYwAkGzAXqXbKKGhDNm1RXppu9Ddhn1zHG9HVnOce e9CFbQN/a7QBKwPEu1mqhnA6HVGqivRjJryVi0ItGxbfaC4TU/Y5VTwaklkQES3t dQPuJTIvfzFkFHxkvpYsbwkCgYBv6oxy36CdsZ3sCKlDic1OHc/BB4RUzc2kl8BB VLyqi5V8DQ09D08mKXgHXFAzA/jNmJUtrcOXNinXDK8rKHZrZJfYObDIC0GMYmeE X0M+P1De15XDi01dvfzopMoLcOCGbyujIRPB3zhuNK0auw37MSwd7XsALBxmJBa3 MBBqfQKBgQDWKUZQQ+ctq4VMSTddgvD6kT2A2Wlr7Eph5wyYC+2oX6IqFtMzYnQT 0eCGGM3WFhdEGwqxYWhKCKuPLBnrM/wig9WibBkNtP/n+2vP9sHK0p3/O6tkkpcq PvF9QWP+2mDwcoh11z7ZD+C83nq08GwqnHHh0X/RtAjWSTWdLEwkLg== -----END RSA PRIVATE KEY----- request-2.88.1/tests/ssl/ca/server.cnf000066400000000000000000000010431333333633500176310ustar00rootroot00000000000000[ req ] default_bits = 1024 days = 3650 distinguished_name = req_distinguished_name attributes = req_attributes prompt = no [ req_distinguished_name ] C = US ST = CA L = Oakland O = request OU = testing CN = testing.request.mikealrogers.com emailAddress = mikeal@mikealrogers.com [ req_attributes ] challengePassword = password challenge request-2.88.1/tests/ssl/ca/server.crt000066400000000000000000000030011333333633500176470ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIEQjCCA6sCCQCt9iAWqkDJxTANBgkqhkiG9w0BAQsFADCBojELMAkGA1UEBhMC VVMxCzAJBgNVBAgTAkNBMRAwDgYDVQQHEwdPYWtsYW5kMRAwDgYDVQQKEwdyZXF1 ZXN0MSYwJAYDVQQLEx1yZXF1ZXN0IENlcnRpZmljYXRlIEF1dGhvcml0eTESMBAG A1UEAxMJcmVxdWVzdENBMSYwJAYJKoZIhvcNAQkBFhdtaWtlYWxAbWlrZWFscm9n ZXJzLmNvbTAeFw0xODA3MDgxMTQ5MzlaFw0xODA4MDcxMTQ5MzlaMIGjMQswCQYD VQQGEwJVUzELMAkGA1UECAwCQ0ExEDAOBgNVBAcMB09ha2xhbmQxEDAOBgNVBAoM B3JlcXVlc3QxEDAOBgNVBAsMB3Rlc3RpbmcxKTAnBgNVBAMMIHRlc3RpbmcucmVx dWVzdC5taWtlYWxyb2dlcnMuY29tMSYwJAYJKoZIhvcNAQkBFhdtaWtlYWxAbWlr ZWFscm9nZXJzLmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAKzo ebuLRx+c/880mgpX3+oxcO9pnCHELvQyVgvlk1Tx5FXunmB3odoxobM37wqzw1Bb /JyU58+C/kPDVz5ixp5FIBgQCBM7t6VrFvANqe63FCFhwSptdJQHpOJlBZaZ2rRY cjN8MyoaeLpUpra094N2VCWMaNw2vvY8sCJBj59E+0FScX4obFHmkEX0W1eccS4+ U6Dd82eLN6bPPhspWlq1XsOny4et5Ik8XHfWJ/alRAml2RdDFzV9aWYIg2LIv2wB jlnNRX6jve+3+5SkrrnsFZWZO+vSNdqCMUp3pCepwrZo3lal+Yz6Of1jmEtmHGUG agUwdmg2Fka1HOAKhSXepFaBsM8CJbLBAVUFRmufXBiOxRc2a8+OheHV33TU/V7Z PdBfpvKX55Qqnm6ekFY2h4LvxNge2WVCVErIvPig+PJLlsm9gHV/KIqZvmJM4j5L W4towjRfIE4Qw+DoTGcDnCbwRuqcSehFftKYfLPRuAoiAhXPIllE2WD/rwMqRFqW 0jfiHdRYu9AamNJYIJjRAY88iTRrb2/28acMvbz9Lg8jA13kVD3TEETvBX9meEOx 5tnr+nilcoZDdzHsB78mDdX9sQbN9Ic0alFcp92zH7Qw6YBK/6JcFXKECqDn+2J5 Q9kuI0A2XKMXCn8U8tToL2DRASB7hyi8N3Y9un3nAgMBAAEwDQYJKoZIhvcNAQEL BQADgYEAF/dmc81+FQWLhrT07oC3UWacbP8LJGigDM18cijn4JgBM/un1AOdaZON oUP2NQefuB12t6xjAXmY+JuhZqojUjK8OkehKiCUR0x25KYOPyaHLTp7+p9x9aJB ifN/qMN/ONiAO+ZDvKdFmlEOPzm/oTyzvIY9X3UqxSfjzfLGsN8= -----END CERTIFICATE----- request-2.88.1/tests/ssl/ca/server.csr000066400000000000000000000034411333333633500176560ustar00rootroot00000000000000-----BEGIN CERTIFICATE REQUEST----- MIIFDDCCAvQCAQAwgaMxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTEQMA4GA1UE BwwHT2FrbGFuZDEQMA4GA1UECgwHcmVxdWVzdDEQMA4GA1UECwwHdGVzdGluZzEp MCcGA1UEAwwgdGVzdGluZy5yZXF1ZXN0Lm1pa2VhbHJvZ2Vycy5jb20xJjAkBgkq hkiG9w0BCQEWF21pa2VhbEBtaWtlYWxyb2dlcnMuY29tMIICIjANBgkqhkiG9w0B AQEFAAOCAg8AMIICCgKCAgEArOh5u4tHH5z/zzSaClff6jFw72mcIcQu9DJWC+WT VPHkVe6eYHeh2jGhszfvCrPDUFv8nJTnz4L+Q8NXPmLGnkUgGBAIEzu3pWsW8A2p 7rcUIWHBKm10lAek4mUFlpnatFhyM3wzKhp4ulSmtrT3g3ZUJYxo3Da+9jywIkGP n0T7QVJxfihsUeaQRfRbV5xxLj5ToN3zZ4s3ps8+GylaWrVew6fLh63kiTxcd9Yn 9qVECaXZF0MXNX1pZgiDYsi/bAGOWc1FfqO977f7lKSuuewVlZk769I12oIxSnek J6nCtmjeVqX5jPo5/WOYS2YcZQZqBTB2aDYWRrUc4AqFJd6kVoGwzwIlssEBVQVG a59cGI7FFzZrz46F4dXfdNT9Xtk90F+m8pfnlCqebp6QVjaHgu/E2B7ZZUJUSsi8 +KD48kuWyb2AdX8oipm+YkziPktbi2jCNF8gThDD4OhMZwOcJvBG6pxJ6EV+0ph8 s9G4CiICFc8iWUTZYP+vAypEWpbSN+Id1Fi70BqY0lggmNEBjzyJNGtvb/bxpwy9 vP0uDyMDXeRUPdMQRO8Ff2Z4Q7Hm2ev6eKVyhkN3MewHvyYN1f2xBs30hzRqUVyn 3bMftDDpgEr/olwVcoQKoOf7YnlD2S4jQDZcoxcKfxTy1OgvYNEBIHuHKLw3dj26 fecCAwEAAaAjMCEGCSqGSIb3DQEJBzEUDBJwYXNzd29yZCBjaGFsbGVuZ2UwDQYJ KoZIhvcNAQELBQADggIBAF6uhMroKufNBhl15XGU45hk2aIG01H6mQlmJWoaC2Od rhJrXF5uBY5m2JhI3rMt8J4bnxziVXBJpxgvuRhM0oHZLVCMb/MPvu6isKk9HXdq yHny/xSZiOFdyMQhcJ9gtPCPV3tXXhQ2GMMetG6qi1UpBGytWUWND5sNBOwD+stP lnKge/jTEhBkdBivTplVgOJDGr2hxSUAorYOW6sqLU/A5Hk6R1XG/7GTlPm6f1eO +PNpNspZmrsHTIwAPjtLIEedTx+wqPzpldVTGxV54PPVpYX8E+W+wwvS4VGC3oA9 Z3+I2Z1ZYmAjb5kMxtD7y+a9520UKTAifAJB42LaBh9WDXXB+6aQNsmr8mx5P2BQ iuGtqMuqWdmv9rzgeOuy4+V5/f7y1mzDHy1x5YwfI3o8RE9Ooo5Om5RiOUd0wF0i Y/5PLNwdKNMaKTZKuYBW1VEd99qk7+Ugfc2a5buvXe4UPn5GWpPncqsT014msxmy 4H9IilYusafZcTZY5yrQ8VLwUnhLSbLo6JDjZVj4+4sSOstPITG0Nd5Tw1FCBJvz e+TgKQJl6A5+N0JnJVhCwpCzR9Vmr5tClMn2LXwvQxl+9Lhu8wr15R5QCkelo5NC GuS83D+eEeVL2foY7YcWiyBTnGZXhj0EESgF6hv+b9TouD7FcNGMUgdH2DUZqy8L -----END CERTIFICATE REQUEST----- request-2.88.1/tests/ssl/ca/server.js000066400000000000000000000014651333333633500175070ustar00rootroot00000000000000'use strict' var fs = require('fs') var https = require('https') var options = { key: fs.readFileSync('./server.key'), cert: fs.readFileSync('./server.crt') } var server = https.createServer(options, function (req, res) { res.writeHead(200) res.end() server.close() }) server.listen(0, function () { var ca = fs.readFileSync('./ca.crt') var agent = new https.Agent({ host: 'localhost', port: this.address().port, ca: ca }) https.request({ host: 'localhost', method: 'HEAD', port: this.address().port, headers: { host: 'testing.request.mikealrogers.com' }, agent: agent, ca: [ ca ], path: '/' }, function (res) { if (res.socket.authorized) { console.log('node test: OK') } else { throw new Error(res.socket.authorizationError) } }).end() }) request-2.88.1/tests/ssl/ca/server.key000066400000000000000000000062531333333633500176630ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIIJJwIBAAKCAgEArOh5u4tHH5z/zzSaClff6jFw72mcIcQu9DJWC+WTVPHkVe6e YHeh2jGhszfvCrPDUFv8nJTnz4L+Q8NXPmLGnkUgGBAIEzu3pWsW8A2p7rcUIWHB Km10lAek4mUFlpnatFhyM3wzKhp4ulSmtrT3g3ZUJYxo3Da+9jywIkGPn0T7QVJx fihsUeaQRfRbV5xxLj5ToN3zZ4s3ps8+GylaWrVew6fLh63kiTxcd9Yn9qVECaXZ F0MXNX1pZgiDYsi/bAGOWc1FfqO977f7lKSuuewVlZk769I12oIxSnekJ6nCtmje VqX5jPo5/WOYS2YcZQZqBTB2aDYWRrUc4AqFJd6kVoGwzwIlssEBVQVGa59cGI7F FzZrz46F4dXfdNT9Xtk90F+m8pfnlCqebp6QVjaHgu/E2B7ZZUJUSsi8+KD48kuW yb2AdX8oipm+YkziPktbi2jCNF8gThDD4OhMZwOcJvBG6pxJ6EV+0ph8s9G4CiIC Fc8iWUTZYP+vAypEWpbSN+Id1Fi70BqY0lggmNEBjzyJNGtvb/bxpwy9vP0uDyMD XeRUPdMQRO8Ff2Z4Q7Hm2ev6eKVyhkN3MewHvyYN1f2xBs30hzRqUVyn3bMftDDp gEr/olwVcoQKoOf7YnlD2S4jQDZcoxcKfxTy1OgvYNEBIHuHKLw3dj26fecCAwEA AQKCAgAQIiTxpd4+CeUojUzuOCLRdEmIXT9PO0HyURwMQiCMJYHbrsciUydL96OR 2F86jWlk/yBD7/TtPNjCs+BZFthXfjWvaReHy+On0KU0QuIfPv/m2XsvnUTqZwgw g6KQ2cw5VaNaQHV5sTygjjN8CsipgIn7cu151rXcve7lU162SrZy8uFaFyV8Qtol XNaFBzjcSr583RjQCYJo0x+FY0dl/VRZRzfLciNH1tT97YKPFf6SM+JctErfF9OU zKiNuBN8XWzN3kRku5yGWJFl3jPbbzbYXZLkvxl9SPaWbzFm7gUYBhLw3M27JMHy ba+RIXb2yjFsSIhT0vAjKtUF5pVjuusCZt7iTvWl54uSDgmCyNl2WkeUBmJatzbG iFOoqvh3+60IQTffbfbIX/C0u4QEthT+6CjyJEbwPNLOOaVMolKH+HAdAJy8srSc YSod14aCjZ36w8Vg70PrDB35mm7Z5aLRM7ig2AW82mzjvheWqIYxbO+a/9UsaT7K 3HL2X/kBCvI5RroDBrQ4ESchRwVulWAHsCAMTfaEcanoU9gWMUXOiSYSBQ8IUOWP r6tlrupG7aXX0V9nANcfa+GIba0Aq6brpLwVMeXoM6usqFnRzQlR7mxzPvQmsPa3 f249QKQ8DZqzYpA2mZBJLWfHcu7zxPW7O8YuBTH4WwdB7AC3MQKCAQEA1+EyndsG GEmEc3J+kh14AQFQSgoZy391WOxfbd2ZGCMtTtkGz80dfzyRB146qkmitsvMQAJl D1eWYaWPLwLBXCz0U9fqGSsH4k0hLGj18Yev5Q1CCvG45mF8pdK23vkoXYJeN1gU 3y/AsMoYmb5bmb2xQqMJ66mmVYaqoj2NGxBqitgW7cPu5CFAHwnF1jTjRNvvJSgP BP67n1K6mt9O37AvTAsmZPzDtFcn7gzjQVYLvuj1JqUNm4akxiTwVZ7mgj3M5Y0d fpxBBaGhOfLAfBrF7KUZmE7zkkqGkDtAeOrbf7RIpMhU/bZDTw2cIFUw0xOncUQm NjAPgcaZHMTm+QKCAQEAzQrXHa324xRzv2ismhymqC6w1dW6enBjWpZL5Qw9rHpI z6L1bNjXqCt6wWkeN6VuNZqZdXLlDDJJTHsyJCaMhS1bKK5xwnykWkerVLJclslI sLfaL3bybEanjU/KBkq7gnG/Z+7E9zovnKvCtCCnAKO7ama11tI8hnHQJK7CAP66 QNcpqhJnCYvrZg2g3j5do/viUGjffIKa9wZ0TgQF0Pa+mfKvA7beuQo4ysDfs26V 5U/NcYMeP+ERoh818MbGcYJH/SY3g2C5/zs3BWykBh3iGEsybzru9hweefRFurwZ jGObiSmjRBVothJed5ef+8JrZsvDqgYxl+XrGbtj3wKCAQBLkIxDLRNpFPLD5mPf iWkkEO2lvPtie+77fWG590NLa6TEYLs9qbKVgwHQ7m7ihHvsOFH6ZdwyDpqY+3av IevE9AdiAcXzoVhVImJmRScxsCklqApiAlKScbVL5gIU8mnqsWOBQ9eqd/Ce8V7D EhrGKdwOUzt5vhx2+3hm6dymiIyCpTkBaQAJ4omrU6RoYoLa65E+FFONkAzkq/Sd mWTmb6lemNiLqN5oFcnoTaKOkCv0W17UdBnbQroSkYN+tOxC0pcSEt8sHk20RutE eXBfAJAfUXswERK5NlT7z4G10Z+bh+OVqnn1hQLyfPUVbDx25f5Ka2xks1X6OyYF J/chAoIBAACrZmRsav/20yHu35FpQooODPnFxuQbddeX4guPbKwhghdbet2uH5Ym /jGffD/GRIPcS1/8zg6qI18ij9laXt8YdNG/MBPogEjjLwYJxw+e/KPHFelNxCDi Yi2t8wTuPYqBgJSATRhZkko6rVoVOTZhUn1YdIONEDGIMZvNDkkei9EmYrZxdPCt Ckm9Bad0IK4mZmjIzuIDMypXVQ3kKXizNZAfIL8sW7HS/Lh8xL47QDYNeqhCO1kO DRawb2an34IDYOTMuSWurSzOLrHP1wFGG7TkmfePA7S+BsNzLr8bWiIBOULLZgMU 5tChYrmVPyp9Sgh95deqSYMrdwcQe5UCggEAQVre47OnY6SET5v4nAVvsmPJ00U8 xsTTzZMkJq6dMdUNnmt90nGwreAhp4SopdryejiqSHQTE9mDWGV62TT5/m62vZKS goI/gyz+arJ9LATKJBZ8p827dBx84BB87XoXCBDD4M0xdNq408bUtuewIummZcdp T+9I/Fnf6fFPhwO0ZaL536+NqZA1uwBNzvkxlfam3CxotU/vevmhbSzBlR1EggqU EWwgn0aBVKiWaLaMQ34JLC4GcLmwFV+lHfQl+yb3JsSK9o/fzizXveG0ytBX5vYv gM29WxybaXHan3Wwn9s2F+80Joh1XvRgKYknBH+DRRygJeKqqVlybnp26g== -----END RSA PRIVATE KEY----- request-2.88.1/tests/ssl/npm-ca.crt000066400000000000000000000016501333333633500171410ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIChzCCAfACCQDauvz/KHp8ejANBgkqhkiG9w0BAQUFADCBhzELMAkGA1UEBhMC VVMxCzAJBgNVBAgTAkNBMRAwDgYDVQQHEwdPYWtsYW5kMQwwCgYDVQQKEwNucG0x IjAgBgNVBAsTGW5wbSBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxDjAMBgNVBAMTBW5w bUNBMRcwFQYJKoZIhvcNAQkBFghpQGl6cy5tZTAeFw0xMTA5MDUwMTQ3MTdaFw0y MTA5MDIwMTQ3MTdaMIGHMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExEDAOBgNV BAcTB09ha2xhbmQxDDAKBgNVBAoTA25wbTEiMCAGA1UECxMZbnBtIENlcnRpZmlj YXRlIEF1dGhvcml0eTEOMAwGA1UEAxMFbnBtQ0ExFzAVBgkqhkiG9w0BCQEWCGlA aXpzLm1lMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDLI4tIqPpRW+ACw9GE OgBlJZwK5f8nnKCLK629Pv5yJpQKs3DENExAyOgDcyaF0HD0zk8zTp+ZsLaNdKOz Gn2U181KGprGKAXP6DU6ByOJDWmTlY6+Ad1laYT0m64fERSpHw/hjD3D+iX4aMOl y0HdbT5m1ZGh6SJz3ZqxavhHLQIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAC4ySDbC l7W1WpLmtLGEQ/yuMLUf6Jy/vr+CRp4h+UzL+IQpCv8FfxsYE7dhf/bmWTEupBkv yNL18lipt2jSvR3v6oAHAReotvdjqhxddpe5Holns6EQd1/xEZ7sB1YhQKJtvUrl ZNufy1Jf1r0ldEGeA+0ISck7s+xSh9rQD2Op -----END CERTIFICATE----- request-2.88.1/tests/ssl/test.crt000066400000000000000000000015171333333633500167470ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIICQzCCAawCCQCO/XWtRFck1jANBgkqhkiG9w0BAQUFADBmMQswCQYDVQQGEwJU SDEQMA4GA1UECBMHQmFuZ2tvazEOMAwGA1UEBxMFU2lsb20xGzAZBgNVBAoTElRo ZSBSZXF1ZXN0IE1vZHVsZTEYMBYGA1UEAxMPcmVxdWVzdC5leGFtcGxlMB4XDTEx MTIwMzAyMjkyM1oXDTIxMTEzMDAyMjkyM1owZjELMAkGA1UEBhMCVEgxEDAOBgNV BAgTB0Jhbmdrb2sxDjAMBgNVBAcTBVNpbG9tMRswGQYDVQQKExJUaGUgUmVxdWVz dCBNb2R1bGUxGDAWBgNVBAMTD3JlcXVlc3QuZXhhbXBsZTCBnzANBgkqhkiG9w0B AQEFAAOBjQAwgYkCgYEAwmctddZqlA48+NXs0yOy92DijcQV1jf87zMiYAIlNUto wghVbTWgJU5r0pdKrD16AptnWJTzKanhItEX8XCCPgsNkq1afgTtJP7rNkwu3xcj eIMkhJg/ay4ZnkbnhYdsii5VTU5prix6AqWRAhbkBgoA+iVyHyof8wvZyKBoFTMC AwEAATANBgkqhkiG9w0BAQUFAAOBgQB6BybMJbpeiABgihDfEVBcAjDoQ8gUMgwV l4NulugfKTDmArqnR9aPd4ET5jX5dkMP4bwCHYsvrcYDeWEQy7x5WWuylOdKhua4 L4cEi2uDCjqEErIG3cc1MCOk6Cl6Ld6tkIzQSf953qfdEACRytOeUqLNQcrXrqeE c7U8F6MWLQ== -----END CERTIFICATE----- request-2.88.1/tests/ssl/test.key000066400000000000000000000015731333333633500167510ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIICXgIBAAKBgQDCZy111mqUDjz41ezTI7L3YOKNxBXWN/zvMyJgAiU1S2jCCFVt NaAlTmvSl0qsPXoCm2dYlPMpqeEi0RfxcII+Cw2SrVp+BO0k/us2TC7fFyN4gySE mD9rLhmeRueFh2yKLlVNTmmuLHoCpZECFuQGCgD6JXIfKh/zC9nIoGgVMwIDAQAB AoGBALXFwfUf8vHTSmGlrdZS2AGFPvEtuvldyoxi9K5u8xmdFCvxnOcLsF2RsTHt Mu5QYWhUpNJoG+IGLTPf7RJdj/kNtEs7xXqWy4jR36kt5z5MJzqiK+QIgiO9UFWZ fjUb6oeDnTIJA9YFBdYi97MDuL89iU/UK3LkJN3hd4rciSbpAkEA+MCkowF5kSFb rkOTBYBXZfiAG78itDXN6DXmqb9XYY+YBh3BiQM28oxCeQYyFy6pk/nstnd4TXk6 V/ryA2g5NwJBAMgRKTY9KvxJWbESeMEFe2iBIV0c26/72Amgi7ZKUCLukLfD4tLF +WSZdmTbbqI1079YtwaiOVfiLm45Q/3B0eUCQAaQ/0eWSGE+Yi8tdXoVszjr4GXb G81qBi91DMu6U1It+jNfIba+MPsiHLcZJMVb4/oWBNukN7bD1nhwFWdlnu0CQQCf Is9WHkdvz2RxbZDxb8verz/7kXXJQJhx5+rZf7jIYFxqX3yvTNv3wf2jcctJaWlZ fVZwB193YSivcgt778xlAkEAprYUz3jczjF5r2hrgbizPzPDR94tM5BTO3ki2v3w kbf+j2g7FNAx6kZiVN8XwfLc8xEeUGiPKwtq3ddPDFh17w== -----END RSA PRIVATE KEY----- request-2.88.1/tests/test-agent.js000066400000000000000000000047751333333633500170770ustar00rootroot00000000000000'use strict' var request = require('../index') var version = require('../lib/helpers').version var http = require('http') var ForeverAgent = require('forever-agent') var tape = require('tape') var s = http.createServer(function (req, res) { res.statusCode = 200 res.end() }) tape('setup', function (t) { s.listen(0, function () { s.port = this.address().port s.url = 'http://localhost:' + s.port t.end() }) }) function httpAgent (t, options, req) { var r = (req || request)(options, function (_err, res, body) { t.ok(r.agent instanceof http.Agent, 'is http.Agent') t.equal(r.agent.options.keepAlive, true, 'is keepAlive') t.equal(Object.keys(r.agent.sockets).length, 1, '1 socket name') var name = (typeof r.agent.getName === 'function') ? r.agent.getName({port: s.port}) : 'localhost:' + s.port // node 0.10- t.equal(r.agent.sockets[name].length, 1, '1 open socket') var socket = r.agent.sockets[name][0] socket.on('close', function () { t.equal(Object.keys(r.agent.sockets).length, 0, '0 open sockets') t.end() }) socket.end() }) } function foreverAgent (t, options, req) { var r = (req || request)(options, function (_err, res, body) { t.ok(r.agent instanceof ForeverAgent, 'is ForeverAgent') t.equal(Object.keys(r.agent.sockets).length, 1, '1 socket name') var name = 'localhost:' + s.port // node 0.10- t.equal(r.agent.sockets[name].length, 1, '1 open socket') var socket = r.agent.sockets[name][0] socket.on('close', function () { t.equal(Object.keys(r.agent.sockets[name]).length, 0, '0 open sockets') t.end() }) socket.end() }) } // http.Agent tape('options.agent', function (t) { httpAgent(t, { uri: s.url, agent: new http.Agent({keepAlive: true}) }) }) tape('options.agentClass + options.agentOptions', function (t) { httpAgent(t, { uri: s.url, agentClass: http.Agent, agentOptions: {keepAlive: true} }) }) // forever-agent tape('options.forever = true', function (t) { var v = version() var options = { uri: s.url, forever: true } if (v.major === 0 && v.minor <= 10) { foreverAgent(t, options) } else { httpAgent(t, options) } }) tape('forever() method', function (t) { var v = version() var options = { uri: s.url } var r = request.forever({maxSockets: 1}) if (v.major === 0 && v.minor <= 10) { foreverAgent(t, options, r) } else { httpAgent(t, options, r) } }) tape('cleanup', function (t) { s.close(function () { t.end() }) }) request-2.88.1/tests/test-agentOptions.js000066400000000000000000000023411333333633500204360ustar00rootroot00000000000000'use strict' // test-agent.js modifies the process state // causing these tests to fail when running under single process via tape if (!process.env.running_under_istanbul) { var request = require('../index') var http = require('http') var server = require('./server') var tape = require('tape') var s = server.createServer() s.on('/', function (req, resp) { resp.statusCode = 200 resp.end('') }) tape('setup', function (t) { s.listen(0, function () { t.end() }) }) tape('without agentOptions should use global agent', function (t) { var r = request(s.url, function (err, res, body) { t.equal(err, null) t.equal(res.statusCode, 200) t.deepEqual(r.agent, http.globalAgent) t.equal(Object.keys(r.pool).length, 0) t.end() }) }) tape('with agentOptions should apply to new agent in pool', function (t) { var r = request(s.url, { agentOptions: { foo: 'bar' } }, function (err, res, body) { t.equal(err, null) t.equal(res.statusCode, 200) t.equal(r.agent.options.foo, 'bar') t.equal(Object.keys(r.pool).length, 1) t.end() }) }) tape('cleanup', function (t) { s.close(function () { t.end() }) }) } request-2.88.1/tests/test-api.js000066400000000000000000000011671333333633500165420ustar00rootroot00000000000000'use strict' var http = require('http') var request = require('../index') var tape = require('tape') var server tape('setup', function (t) { server = http.createServer() server.on('request', function (req, res) { res.writeHead(202) req.pipe(res) }) server.listen(0, function () { server.url = 'http://localhost:' + this.address().port t.end() }) }) tape('callback option', function (t) { request({ url: server.url, callback: function (err, res, body) { t.error(err) t.equal(res.statusCode, 202) t.end() } }) }) tape('cleanup', function (t) { server.close(t.end) }) request-2.88.1/tests/test-aws.js000066400000000000000000000045511333333633500165630ustar00rootroot00000000000000'use strict' var request = require('../index') var server = require('./server') var tape = require('tape') var s = server.createServer() var path = '/aws.json' s.on(path, function (req, res) { res.writeHead(200, { 'Content-Type': 'application/json' }) res.end(JSON.stringify(req.headers)) }) tape('setup', function (t) { s.listen(0, function () { t.end() }) }) tape('default behaviour: aws-sign2 without sign_version key', function (t) { var options = { url: s.url + path, aws: { key: 'my_key', secret: 'my_secret' }, json: true } request(options, function (err, res, body) { t.error(err) t.ok(body.authorization) t.notOk(body['x-amz-date']) t.end() }) }) tape('aws-sign4 options', function (t) { var options = { url: s.url + path, aws: { key: 'my_key', secret: 'my_secret', sign_version: 4 }, json: true } request(options, function (err, res, body) { t.error(err) t.ok(body.authorization) t.ok(body['x-amz-date']) t.notok(body['x-amz-security-token']) t.end() }) }) tape('aws-sign4 options with session token', function (t) { var options = { url: s.url + path, aws: { key: 'my_key', secret: 'my_secret', session: 'session', sign_version: 4 }, json: true } request(options, function (err, res, body) { t.error(err) t.ok(body.authorization) t.ok(body['x-amz-date']) t.ok(body['x-amz-security-token']) t.end() }) }) tape('aws-sign4 options with service', function (t) { var serviceName = 'UNIQUE_SERVICE_NAME' var options = { url: s.url + path, aws: { key: 'my_key', secret: 'my_secret', sign_version: 4, service: serviceName }, json: true } request(options, function (err, res, body) { t.error(err) t.ok(body.authorization.includes(serviceName)) t.end() }) }) tape('aws-sign4 with additional headers', function (t) { var options = { url: s.url + path, headers: { 'X-Custom-Header': 'custom' }, aws: { key: 'my_key', secret: 'my_secret', sign_version: 4 }, json: true } request(options, function (err, res, body) { t.error(err) t.ok(body.authorization.includes('x-custom-header')) t.end() }) }) tape('cleanup', function (t) { s.close(function () { t.end() }) }) request-2.88.1/tests/test-baseUrl.js000066400000000000000000000066471333333633500173760ustar00rootroot00000000000000'use strict' var http = require('http') var request = require('../index') var tape = require('tape') var url = require('url') var s = http.createServer(function (req, res) { if (req.url === '/redirect/') { res.writeHead(302, { location: '/' }) } else { res.statusCode = 200 res.setHeader('X-PATH', req.url) } res.end('ok') }) function addTest (baseUrl, uri, expected) { tape('test baseurl="' + baseUrl + '" uri="' + uri + '"', function (t) { request(uri, { baseUrl: baseUrl }, function (err, resp, body) { t.equal(err, null) t.equal(body, 'ok') t.equal(resp.headers['x-path'], expected) t.end() }) }) } function addTests () { addTest(s.url, '', '/') addTest(s.url + '/', '', '/') addTest(s.url, '/', '/') addTest(s.url + '/', '/', '/') addTest(s.url + '/api', '', '/api') addTest(s.url + '/api/', '', '/api/') addTest(s.url + '/api', '/', '/api/') addTest(s.url + '/api/', '/', '/api/') addTest(s.url + '/api', 'resource', '/api/resource') addTest(s.url + '/api/', 'resource', '/api/resource') addTest(s.url + '/api', '/resource', '/api/resource') addTest(s.url + '/api/', '/resource', '/api/resource') addTest(s.url + '/api', 'resource/', '/api/resource/') addTest(s.url + '/api/', 'resource/', '/api/resource/') addTest(s.url + '/api', '/resource/', '/api/resource/') addTest(s.url + '/api/', '/resource/', '/api/resource/') } tape('setup', function (t) { s.listen(0, function () { s.url = 'http://localhost:' + this.address().port addTests() tape('cleanup', function (t) { s.close(function () { t.end() }) }) t.end() }) }) tape('baseUrl', function (t) { request('resource', { baseUrl: s.url }, function (err, resp, body) { t.equal(err, null) t.equal(body, 'ok') t.end() }) }) tape('baseUrl defaults', function (t) { var withDefaults = request.defaults({ baseUrl: s.url }) withDefaults('resource', function (err, resp, body) { t.equal(err, null) t.equal(body, 'ok') t.end() }) }) tape('baseUrl and redirects', function (t) { request('/', { baseUrl: s.url + '/redirect' }, function (err, resp, body) { t.equal(err, null) t.equal(body, 'ok') t.equal(resp.headers['x-path'], '/') t.end() }) }) tape('error when baseUrl is not a String', function (t) { request('resource', { baseUrl: url.parse(s.url + '/path') }, function (err, resp, body) { t.notEqual(err, null) t.equal(err.message, 'options.baseUrl must be a string') t.end() }) }) tape('error when uri is not a String', function (t) { request(url.parse('resource'), { baseUrl: s.url + '/path' }, function (err, resp, body) { t.notEqual(err, null) t.equal(err.message, 'options.uri must be a string when using options.baseUrl') t.end() }) }) tape('error on baseUrl and uri with scheme', function (t) { request(s.url + '/path/ignoring/baseUrl', { baseUrl: s.url + '/path/' }, function (err, resp, body) { t.notEqual(err, null) t.equal(err.message, 'options.uri must be a path when using options.baseUrl') t.end() }) }) tape('error on baseUrl and uri with scheme-relative url', function (t) { request(s.url.slice('http:'.length) + '/path/ignoring/baseUrl', { baseUrl: s.url + '/path/' }, function (err, resp, body) { t.notEqual(err, null) t.equal(err.message, 'options.uri must be a path when using options.baseUrl') t.end() }) }) request-2.88.1/tests/test-basic-auth.js000066400000000000000000000125651333333633500200150ustar00rootroot00000000000000'use strict' var assert = require('assert') var http = require('http') var request = require('../index') var tape = require('tape') var numBasicRequests = 0 var basicServer tape('setup', function (t) { basicServer = http.createServer(function (req, res) { numBasicRequests++ var ok if (req.headers.authorization) { if (req.headers.authorization === 'Basic ' + Buffer.from('user:pass').toString('base64')) { ok = true } else if (req.headers.authorization === 'Basic ' + Buffer.from('user:').toString('base64')) { ok = true } else if (req.headers.authorization === 'Basic ' + Buffer.from(':pass').toString('base64')) { ok = true } else if (req.headers.authorization === 'Basic ' + Buffer.from('user:pâss').toString('base64')) { ok = true } else { // Bad auth header, don't send back WWW-Authenticate header ok = false } } else { // No auth header, send back WWW-Authenticate header ok = false res.setHeader('www-authenticate', 'Basic realm="Private"') } if (req.url === '/post/') { var expectedContent = 'key=value' req.on('data', function (data) { assert.equal(data, expectedContent) }) assert.equal(req.method, 'POST') assert.equal(req.headers['content-length'], '' + expectedContent.length) assert.equal(req.headers['content-type'], 'application/x-www-form-urlencoded') } if (ok) { res.end('ok') } else { res.statusCode = 401 res.end('401') } }).listen(0, function () { basicServer.port = this.address().port basicServer.url = 'http://localhost:' + basicServer.port t.end() }) }) tape('sendImmediately - false', function (t) { var r = request({ 'method': 'GET', 'uri': basicServer.url + '/test/', 'auth': { 'user': 'user', 'pass': 'pass', 'sendImmediately': false } }, function (error, res, body) { t.error(error) t.equal(r._auth.user, 'user') t.equal(res.statusCode, 200) t.equal(numBasicRequests, 2) t.end() }) }) tape('sendImmediately - true', function (t) { // If we don't set sendImmediately = false, request will send basic auth var r = request({ 'method': 'GET', 'uri': basicServer.url + '/test2/', 'auth': { 'user': 'user', 'pass': 'pass' } }, function (error, res, body) { t.error(error) t.equal(r._auth.user, 'user') t.equal(res.statusCode, 200) t.equal(numBasicRequests, 3) t.end() }) }) tape('credentials in url', function (t) { var r = request({ 'method': 'GET', 'uri': basicServer.url.replace(/:\/\//, '$&user:pass@') + '/test2/' }, function (error, res, body) { t.error(error) t.equal(r._auth.user, 'user') t.equal(res.statusCode, 200) t.equal(numBasicRequests, 4) t.end() }) }) tape('POST request', function (t) { var r = request({ 'method': 'POST', 'form': { 'key': 'value' }, 'uri': basicServer.url + '/post/', 'auth': { 'user': 'user', 'pass': 'pass', 'sendImmediately': false } }, function (error, res, body) { t.error(error) t.equal(r._auth.user, 'user') t.equal(res.statusCode, 200) t.equal(numBasicRequests, 6) t.end() }) }) tape('user - empty string', function (t) { t.doesNotThrow(function () { var r = request({ 'method': 'GET', 'uri': basicServer.url + '/allow_empty_user/', 'auth': { 'user': '', 'pass': 'pass', 'sendImmediately': false } }, function (error, res, body) { t.error(error) t.equal(r._auth.user, '') t.equal(res.statusCode, 200) t.equal(numBasicRequests, 8) t.end() }) }) }) tape('pass - undefined', function (t) { t.doesNotThrow(function () { var r = request({ 'method': 'GET', 'uri': basicServer.url + '/allow_undefined_password/', 'auth': { 'user': 'user', 'pass': undefined, 'sendImmediately': false } }, function (error, res, body) { t.error(error) t.equal(r._auth.user, 'user') t.equal(res.statusCode, 200) t.equal(numBasicRequests, 10) t.end() }) }) }) tape('pass - utf8', function (t) { t.doesNotThrow(function () { var r = request({ 'method': 'GET', 'uri': basicServer.url + '/allow_undefined_password/', 'auth': { 'user': 'user', 'pass': 'pâss', 'sendImmediately': false } }, function (error, res, body) { t.error(error) t.equal(r._auth.user, 'user') t.equal(r._auth.pass, 'pâss') t.equal(res.statusCode, 200) t.equal(numBasicRequests, 12) t.end() }) }) }) tape('auth method', function (t) { var r = request .get(basicServer.url + '/test/') .auth('user', '', false) .on('response', function (res) { t.equal(r._auth.user, 'user') t.equal(res.statusCode, 200) t.equal(numBasicRequests, 14) t.end() }) }) tape('get method', function (t) { var r = request.get(basicServer.url + '/test/', { auth: { user: 'user', pass: '', sendImmediately: false } }, function (err, res) { t.equal(r._auth.user, 'user') t.equal(err, null) t.equal(res.statusCode, 200) t.equal(numBasicRequests, 16) t.end() }) }) tape('cleanup', function (t) { basicServer.close(function () { t.end() }) }) request-2.88.1/tests/test-bearer-auth.js000066400000000000000000000104301333333633500201610ustar00rootroot00000000000000'use strict' var assert = require('assert') var http = require('http') var request = require('../index') var tape = require('tape') var numBearerRequests = 0 var bearerServer tape('setup', function (t) { bearerServer = http.createServer(function (req, res) { numBearerRequests++ var ok if (req.headers.authorization) { if (req.headers.authorization === 'Bearer theToken') { ok = true } else { // Bad auth header, don't send back WWW-Authenticate header ok = false } } else { // No auth header, send back WWW-Authenticate header ok = false res.setHeader('www-authenticate', 'Bearer realm="Private"') } if (req.url === '/post/') { var expectedContent = 'data_key=data_value' req.on('data', function (data) { assert.equal(data, expectedContent) }) assert.equal(req.method, 'POST') assert.equal(req.headers['content-length'], '' + expectedContent.length) assert.equal(req.headers['content-type'], 'application/x-www-form-urlencoded') } if (ok) { res.end('ok') } else { res.statusCode = 401 res.end('401') } }).listen(0, function () { bearerServer.url = 'http://localhost:' + this.address().port t.end() }) }) tape('bearer auth', function (t) { request({ 'method': 'GET', 'uri': bearerServer.url + '/test/', 'auth': { 'bearer': 'theToken', 'sendImmediately': false } }, function (error, res, body) { t.error(error) t.equal(res.statusCode, 200) t.equal(numBearerRequests, 2) t.end() }) }) tape('bearer auth with default sendImmediately', function (t) { // If we don't set sendImmediately = false, request will send bearer auth request({ 'method': 'GET', 'uri': bearerServer.url + '/test2/', 'auth': { 'bearer': 'theToken' } }, function (error, res, body) { t.error(error) t.equal(res.statusCode, 200) t.equal(numBearerRequests, 3) t.end() }) }) tape('', function (t) { request({ 'method': 'POST', 'form': { 'data_key': 'data_value' }, 'uri': bearerServer.url + '/post/', 'auth': { 'bearer': 'theToken', 'sendImmediately': false } }, function (error, res, body) { t.error(error) t.equal(res.statusCode, 200) t.equal(numBearerRequests, 5) t.end() }) }) tape('using .auth, sendImmediately = false', function (t) { request .get(bearerServer.url + '/test/') .auth(null, null, false, 'theToken') .on('response', function (res) { t.equal(res.statusCode, 200) t.equal(numBearerRequests, 7) t.end() }) }) tape('using .auth, sendImmediately = true', function (t) { request .get(bearerServer.url + '/test/') .auth(null, null, true, 'theToken') .on('response', function (res) { t.equal(res.statusCode, 200) t.equal(numBearerRequests, 8) t.end() }) }) tape('bearer is a function', function (t) { request({ 'method': 'GET', 'uri': bearerServer.url + '/test/', 'auth': { 'bearer': function () { return 'theToken' }, 'sendImmediately': false } }, function (error, res, body) { t.error(error) t.equal(res.statusCode, 200) t.equal(numBearerRequests, 10) t.end() }) }) tape('bearer is a function, path = test2', function (t) { // If we don't set sendImmediately = false, request will send bearer auth request({ 'method': 'GET', 'uri': bearerServer.url + '/test2/', 'auth': { 'bearer': function () { return 'theToken' } } }, function (error, res, body) { t.error(error) t.equal(res.statusCode, 200) t.equal(numBearerRequests, 11) t.end() }) }) tape('no auth method', function (t) { request({ 'method': 'GET', 'uri': bearerServer.url + '/test2/', 'auth': { 'bearer': undefined } }, function (error, res, body) { t.equal(error.message, 'no auth mechanism defined') t.end() }) }) tape('null bearer', function (t) { request({ 'method': 'GET', 'uri': bearerServer.url + '/test2/', 'auth': { 'bearer': null } }, function (error, res, body) { t.error(error) t.equal(res.statusCode, 401) t.equal(numBearerRequests, 13) t.end() }) }) tape('cleanup', function (t) { bearerServer.close(function () { t.end() }) }) request-2.88.1/tests/test-body.js000066400000000000000000000075601333333633500167310ustar00rootroot00000000000000'use strict' var server = require('./server') var request = require('../index') var tape = require('tape') var http = require('http') var s = server.createServer() tape('setup', function (t) { s.listen(0, function () { t.end() }) }) function addTest (name, data) { tape('test ' + name, function (t) { s.on('/' + name, data.resp) data.uri = s.url + '/' + name request(data, function (err, resp, body) { t.equal(err, null) if (data.expectBody && Buffer.isBuffer(data.expectBody)) { t.deepEqual(data.expectBody.toString(), body.toString()) } else if (data.expectBody) { t.deepEqual(data.expectBody, body) } t.end() }) }) } addTest('testGet', { resp: server.createGetResponse('TESTING!'), expectBody: 'TESTING!' }) addTest('testGetChunkBreak', { resp: server.createChunkResponse( [ Buffer.from([239]), Buffer.from([163]), Buffer.from([191]), Buffer.from([206]), Buffer.from([169]), Buffer.from([226]), Buffer.from([152]), Buffer.from([131]) ]), expectBody: '\uF8FF\u03A9\u2603' }) addTest('testGetBuffer', { resp: server.createGetResponse(Buffer.from('TESTING!')), encoding: null, expectBody: Buffer.from('TESTING!') }) addTest('testGetEncoding', { resp: server.createGetResponse(Buffer.from('efa3bfcea9e29883', 'hex')), encoding: 'hex', expectBody: 'efa3bfcea9e29883' }) addTest('testGetUTF', { resp: server.createGetResponse(Buffer.from([0xEF, 0xBB, 0xBF, 226, 152, 131])), encoding: 'utf8', expectBody: '\u2603' }) addTest('testGetJSON', { resp: server.createGetResponse('{"test":true}', 'application/json'), json: true, expectBody: {'test': true} }) addTest('testPutString', { resp: server.createPostValidator('PUTTINGDATA'), method: 'PUT', body: 'PUTTINGDATA' }) addTest('testPutBuffer', { resp: server.createPostValidator('PUTTINGDATA'), method: 'PUT', body: Buffer.from('PUTTINGDATA') }) addTest('testPutJSON', { resp: server.createPostValidator(JSON.stringify({foo: 'bar'})), method: 'PUT', json: {foo: 'bar'} }) addTest('testPutMultipart', { resp: server.createPostValidator( '--__BOUNDARY__\r\n' + 'content-type: text/html\r\n' + '\r\n' + 'Oh hi.' + '\r\n--__BOUNDARY__\r\n\r\n' + 'Oh hi.' + '\r\n--__BOUNDARY__--' ), method: 'PUT', multipart: [ {'content-type': 'text/html', 'body': 'Oh hi.'}, {'body': 'Oh hi.'} ] }) addTest('testPutMultipartPreambleCRLF', { resp: server.createPostValidator( '\r\n--__BOUNDARY__\r\n' + 'content-type: text/html\r\n' + '\r\n' + 'Oh hi.' + '\r\n--__BOUNDARY__\r\n\r\n' + 'Oh hi.' + '\r\n--__BOUNDARY__--' ), method: 'PUT', preambleCRLF: true, multipart: [ {'content-type': 'text/html', 'body': 'Oh hi.'}, {'body': 'Oh hi.'} ] }) addTest('testPutMultipartPostambleCRLF', { resp: server.createPostValidator( '\r\n--__BOUNDARY__\r\n' + 'content-type: text/html\r\n' + '\r\n' + 'Oh hi.' + '\r\n--__BOUNDARY__\r\n\r\n' + 'Oh hi.' + '\r\n--__BOUNDARY__--' + '\r\n' ), method: 'PUT', preambleCRLF: true, postambleCRLF: true, multipart: [ {'content-type': 'text/html', 'body': 'Oh hi.'}, {'body': 'Oh hi.'} ] }) tape('typed array', function (t) { var server = http.createServer() server.on('request', function (req, res) { req.pipe(res) }) server.listen(0, function () { var data = new Uint8Array([1, 2, 3]) request({ uri: 'http://localhost:' + this.address().port, method: 'POST', body: data, encoding: null }, function (err, res, body) { t.error(err) t.deepEqual(Buffer.from(data), body) server.close(t.end) }) }) }) tape('cleanup', function (t) { s.close(function () { t.end() }) }) request-2.88.1/tests/test-cookies.js000066400000000000000000000057451333333633500174330ustar00rootroot00000000000000'use strict' var http = require('http') var request = require('../index') var tape = require('tape') var validUrl var malformedUrl var invalidUrl var server = http.createServer(function (req, res) { if (req.url === '/valid') { res.setHeader('set-cookie', 'foo=bar') } else if (req.url === '/malformed') { res.setHeader('set-cookie', 'foo') } else if (req.url === '/invalid') { res.setHeader('set-cookie', 'foo=bar; Domain=foo.com') } res.end('okay') }) tape('setup', function (t) { server.listen(0, function () { server.url = 'http://localhost:' + this.address().port validUrl = server.url + '/valid' malformedUrl = server.url + '/malformed' invalidUrl = server.url + '/invalid' t.end() }) }) tape('simple cookie creation', function (t) { var cookie = request.cookie('foo=bar') t.equals(cookie.key, 'foo') t.equals(cookie.value, 'bar') t.end() }) tape('simple malformed cookie creation', function (t) { var cookie = request.cookie('foo') t.equals(cookie.key, '') t.equals(cookie.value, 'foo') t.end() }) tape('after server sends a cookie', function (t) { var jar1 = request.jar() request({ method: 'GET', url: validUrl, jar: jar1 }, function (error, response, body) { t.equal(error, null) t.equal(jar1.getCookieString(validUrl), 'foo=bar') t.equal(body, 'okay') var cookies = jar1.getCookies(validUrl) t.equal(cookies.length, 1) t.equal(cookies[0].key, 'foo') t.equal(cookies[0].value, 'bar') t.end() }) }) tape('after server sends a malformed cookie', function (t) { var jar = request.jar() request({ method: 'GET', url: malformedUrl, jar: jar }, function (error, response, body) { t.equal(error, null) t.equal(jar.getCookieString(malformedUrl), 'foo') t.equal(body, 'okay') var cookies = jar.getCookies(malformedUrl) t.equal(cookies.length, 1) t.equal(cookies[0].key, '') t.equal(cookies[0].value, 'foo') t.end() }) }) tape('after server sends a cookie for a different domain', function (t) { var jar2 = request.jar() request({ method: 'GET', url: invalidUrl, jar: jar2 }, function (error, response, body) { t.equal(error, null) t.equal(jar2.getCookieString(validUrl), '') t.deepEqual(jar2.getCookies(validUrl), []) t.equal(body, 'okay') t.end() }) }) tape('make sure setCookie works', function (t) { var jar3 = request.jar() var err = null try { jar3.setCookie(request.cookie('foo=bar'), validUrl) } catch (e) { err = e } t.equal(err, null) var cookies = jar3.getCookies(validUrl) t.equal(cookies.length, 1) t.equal(cookies[0].key, 'foo') t.equal(cookies[0].value, 'bar') t.end() }) tape('custom store', function (t) { var Store = function () {} var store = new Store() var jar = request.jar(store) t.equals(store, jar._jar.store) t.end() }) tape('cleanup', function (t) { server.close(function () { t.end() }) }) request-2.88.1/tests/test-defaults.js000066400000000000000000000206041333333633500175750ustar00rootroot00000000000000'use strict' var server = require('./server') var request = require('../index') var qs = require('qs') var tape = require('tape') var s = server.createServer() tape('setup', function (t) { s.listen(0, function () { s.on('/', function (req, res) { res.writeHead(200, {'content-type': 'application/json'}) res.end(JSON.stringify({ method: req.method, headers: req.headers, qs: qs.parse(req.url.replace(/.*\?(.*)/, '$1')) })) }) s.on('/head', function (req, res) { res.writeHead(200, {'x-data': JSON.stringify({method: req.method, headers: req.headers})}) res.end() }) s.on('/set-undefined', function (req, res) { var data = '' req.on('data', function (d) { data += d }) req.on('end', function () { res.writeHead(200, {'Content-Type': 'application/json'}) res.end(JSON.stringify({ method: req.method, headers: req.headers, data: JSON.parse(data) })) }) }) t.end() }) }) tape('get(string, function)', function (t) { request.defaults({ headers: { foo: 'bar' } })(s.url + '/', function (e, r, b) { b = JSON.parse(b) t.equal(b.method, 'GET') t.equal(b.headers.foo, 'bar') t.end() }) }) tape('merge headers', function (t) { request.defaults({ headers: { foo: 'bar', merged: 'no' } })(s.url + '/', { headers: { merged: 'yes' }, json: true }, function (e, r, b) { t.equal(b.headers.foo, 'bar') t.equal(b.headers.merged, 'yes') t.end() }) }) tape('deep extend', function (t) { request.defaults({ headers: { a: 1, b: 2 }, qs: { a: 1, b: 2 } })(s.url + '/', { headers: { b: 3, c: 4 }, qs: { b: 3, c: 4 }, json: true }, function (e, r, b) { delete b.headers.host delete b.headers.accept delete b.headers.connection t.deepEqual(b.headers, { a: '1', b: '3', c: '4' }) t.deepEqual(b.qs, { a: '1', b: '3', c: '4' }) t.end() }) }) tape('default undefined header', function (t) { request.defaults({ headers: { foo: 'bar', test: undefined }, json: true })(s.url + '/', function (e, r, b) { t.equal(b.method, 'GET') t.equal(b.headers.foo, 'bar') t.equal(b.headers.test, undefined) t.end() }) }) tape('post(string, object, function)', function (t) { request.defaults({ headers: { foo: 'bar' } }).post(s.url + '/', { json: true }, function (e, r, b) { t.equal(b.method, 'POST') t.equal(b.headers.foo, 'bar') t.equal(b.headers['content-type'], undefined) t.end() }) }) tape('patch(string, object, function)', function (t) { request.defaults({ headers: { foo: 'bar' } }).patch(s.url + '/', { json: true }, function (e, r, b) { t.equal(b.method, 'PATCH') t.equal(b.headers.foo, 'bar') t.equal(b.headers['content-type'], undefined) t.end() }) }) tape('post(string, object, function) with body', function (t) { request.defaults({ headers: { foo: 'bar' } }).post(s.url + '/', { json: true, body: { bar: 'baz' } }, function (e, r, b) { t.equal(b.method, 'POST') t.equal(b.headers.foo, 'bar') t.equal(b.headers['content-type'], 'application/json') t.end() }) }) tape('del(string, function)', function (t) { request.defaults({ headers: {foo: 'bar'}, json: true }).del(s.url + '/', function (e, r, b) { t.equal(b.method, 'DELETE') t.equal(b.headers.foo, 'bar') t.end() }) }) tape('delete(string, function)', function (t) { request.defaults({ headers: {foo: 'bar'}, json: true }).delete(s.url + '/', function (e, r, b) { t.equal(b.method, 'DELETE') t.equal(b.headers.foo, 'bar') t.end() }) }) tape('head(object, function)', function (t) { request.defaults({ headers: { foo: 'bar' } }).head({ uri: s.url + '/head' }, function (e, r, b) { b = JSON.parse(r.headers['x-data']) t.equal(b.method, 'HEAD') t.equal(b.headers.foo, 'bar') t.end() }) }) tape('recursive defaults', function (t) { t.plan(11) var defaultsOne = request.defaults({ headers: { foo: 'bar1' } }) var defaultsTwo = defaultsOne.defaults({ headers: { baz: 'bar2' } }) var defaultsThree = defaultsTwo.defaults({}, function (options, callback) { options.headers = { foo: 'bar3' } defaultsTwo(options, callback) }) defaultsOne(s.url + '/', {json: true}, function (e, r, b) { t.equal(b.method, 'GET') t.equal(b.headers.foo, 'bar1') }) defaultsTwo(s.url + '/', {json: true}, function (e, r, b) { t.equal(b.method, 'GET') t.equal(b.headers.foo, 'bar1') t.equal(b.headers.baz, 'bar2') }) // requester function on recursive defaults defaultsThree(s.url + '/', {json: true}, function (e, r, b) { t.equal(b.method, 'GET') t.equal(b.headers.foo, 'bar3') t.equal(b.headers.baz, 'bar2') }) defaultsTwo.get(s.url + '/', {json: true}, function (e, r, b) { t.equal(b.method, 'GET') t.equal(b.headers.foo, 'bar1') t.equal(b.headers.baz, 'bar2') }) }) tape('recursive defaults requester', function (t) { t.plan(5) var defaultsOne = request.defaults({}, function (options, callback) { var headers = options.headers || {} headers.foo = 'bar1' options.headers = headers request(options, callback) }) var defaultsTwo = defaultsOne.defaults({}, function (options, callback) { var headers = options.headers || {} headers.baz = 'bar2' options.headers = headers defaultsOne(options, callback) }) defaultsOne.get(s.url + '/', {json: true}, function (e, r, b) { t.equal(b.method, 'GET') t.equal(b.headers.foo, 'bar1') }) defaultsTwo.get(s.url + '/', {json: true}, function (e, r, b) { t.equal(b.method, 'GET') t.equal(b.headers.foo, 'bar1') t.equal(b.headers.baz, 'bar2') }) }) tape('test custom request handler function', function (t) { t.plan(3) var requestWithCustomHandler = request.defaults({ headers: { foo: 'bar' }, body: 'TESTING!' }, function (uri, options, callback) { var params = request.initParams(uri, options, callback) params.headers.x = 'y' return request(params.uri, params, params.callback) }) t.throws(function () { requestWithCustomHandler.head(s.url + '/', function (e, r, b) { throw new Error('We should never get here') }) }, /HTTP HEAD requests MUST NOT include a request body/) requestWithCustomHandler.get(s.url + '/', function (e, r, b) { b = JSON.parse(b) t.equal(b.headers.foo, 'bar') t.equal(b.headers.x, 'y') }) }) tape('test custom request handler function without options', function (t) { t.plan(2) var customHandlerWithoutOptions = request.defaults(function (uri, options, callback) { var params = request.initParams(uri, options, callback) var headers = params.headers || {} headers.x = 'y' headers.foo = 'bar' params.headers = headers return request(params.uri, params, params.callback) }) customHandlerWithoutOptions.get(s.url + '/', function (e, r, b) { b = JSON.parse(b) t.equal(b.headers.foo, 'bar') t.equal(b.headers.x, 'y') }) }) tape('test only setting undefined properties', function (t) { request.defaults({ method: 'post', json: true, headers: { 'x-foo': 'bar' } })({ uri: s.url + '/set-undefined', json: {foo: 'bar'}, headers: {'x-foo': 'baz'} }, function (e, r, b) { t.equal(b.method, 'POST') t.equal(b.headers['content-type'], 'application/json') t.equal(b.headers['x-foo'], 'baz') t.deepEqual(b.data, { foo: 'bar' }) t.end() }) }) tape('test only function', function (t) { var post = request.post t.doesNotThrow(function () { post(s.url + '/', function (e, r, b) { t.equal(r.statusCode, 200) t.end() }) }) }) tape('invoke defaults', function (t) { var d = request.defaults({ uri: s.url + '/', headers: { foo: 'bar' } }) d({json: true}, function (e, r, b) { t.equal(b.method, 'GET') t.equal(b.headers.foo, 'bar') t.end() }) }) tape('invoke convenience method from defaults', function (t) { var d = request.defaults({ uri: s.url + '/', headers: { foo: 'bar' } }) d.get({json: true}, function (e, r, b) { t.equal(b.method, 'GET') t.equal(b.headers.foo, 'bar') t.end() }) }) tape('defaults without options', function (t) { var d = request.defaults() d.get(s.url + '/', {json: true}, function (e, r, b) { t.equal(r.statusCode, 200) t.end() }) }) tape('cleanup', function (t) { s.close(function () { t.end() }) }) request-2.88.1/tests/test-digest-auth.js000066400000000000000000000141721333333633500202070ustar00rootroot00000000000000'use strict' var http = require('http') var request = require('../index') var tape = require('tape') var crypto = require('crypto') function makeHeader () { return [].join.call(arguments, ', ') } function makeHeaderRegex () { return new RegExp('^' + makeHeader.apply(null, arguments) + '$') } function md5 (str) { return crypto.createHash('md5').update(str).digest('hex') } var digestServer = http.createServer(function (req, res) { var ok, testHeader if (req.url === '/test/') { if (req.headers.authorization) { testHeader = makeHeaderRegex( 'Digest username="test"', 'realm="Private"', 'nonce="WpcHS2/TBAA=dffcc0dbd5f96d49a5477166649b7c0ae3866a93"', 'uri="/test/"', 'qop=auth', 'response="[a-f0-9]{32}"', 'nc=00000001', 'cnonce="[a-f0-9]{32}"', 'algorithm=MD5', 'opaque="5ccc069c403ebaf9f0171e9517f40e41"' ) if (testHeader.test(req.headers.authorization)) { ok = true } else { // Bad auth header, don't send back WWW-Authenticate header ok = false } } else { // No auth header, send back WWW-Authenticate header ok = false res.setHeader('www-authenticate', makeHeader( 'Digest realm="Private"', 'nonce="WpcHS2/TBAA=dffcc0dbd5f96d49a5477166649b7c0ae3866a93"', 'algorithm=MD5', 'qop="auth"', 'opaque="5ccc069c403ebaf9f0171e9517f40e41"' )) } } else if (req.url === '/test/md5-sess') { // RFC 2716 MD5-sess w/ qop=auth var user = 'test' var realm = 'Private' var pass = 'testing' var nonce = 'WpcHS2/TBAA=dffcc0dbd5f96d49a5477166649b7c0ae3866a93' var nonceCount = '00000001' var qop = 'auth' var algorithm = 'MD5-sess' if (req.headers.authorization) { // HA1=MD5(MD5(username:realm:password):nonce:cnonce) // HA2=MD5(method:digestURI) // response=MD5(HA1:nonce:nonceCount:clientNonce:qop:HA2) var cnonce = /cnonce="(.*)"/.exec(req.headers.authorization)[1] var ha1 = md5(md5(user + ':' + realm + ':' + pass) + ':' + nonce + ':' + cnonce) var ha2 = md5('GET:/test/md5-sess') var response = md5(ha1 + ':' + nonce + ':' + nonceCount + ':' + cnonce + ':' + qop + ':' + ha2) testHeader = makeHeaderRegex( 'Digest username="' + user + '"', 'realm="' + realm + '"', 'nonce="' + nonce + '"', 'uri="/test/md5-sess"', 'qop=' + qop, 'response="' + response + '"', 'nc=' + nonceCount, 'cnonce="' + cnonce + '"', 'algorithm=' + algorithm ) ok = testHeader.test(req.headers.authorization) } else { // No auth header, send back WWW-Authenticate header ok = false res.setHeader('www-authenticate', makeHeader( 'Digest realm="' + realm + '"', 'nonce="' + nonce + '"', 'algorithm=' + algorithm, 'qop="' + qop + '"' )) } } else if (req.url === '/dir/index.html') { // RFC2069-compatible mode // check: http://www.rfc-editor.org/errata_search.php?rfc=2069 if (req.headers.authorization) { testHeader = makeHeaderRegex( 'Digest username="Mufasa"', 'realm="testrealm@host.com"', 'nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093"', 'uri="/dir/index.html"', 'response="[a-f0-9]{32}"', 'opaque="5ccc069c403ebaf9f0171e9517f40e41"' ) if (testHeader.test(req.headers.authorization)) { ok = true } else { // Bad auth header, don't send back WWW-Authenticate header ok = false } } else { // No auth header, send back WWW-Authenticate header ok = false res.setHeader('www-authenticate', makeHeader( 'Digest realm="testrealm@host.com"', 'nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093"', 'opaque="5ccc069c403ebaf9f0171e9517f40e41"' )) } } if (ok) { res.end('ok') } else { res.statusCode = 401 res.end('401') } }) tape('setup', function (t) { digestServer.listen(0, function () { digestServer.url = 'http://localhost:' + this.address().port t.end() }) }) tape('with sendImmediately = false', function (t) { var numRedirects = 0 request({ method: 'GET', uri: digestServer.url + '/test/', auth: { user: 'test', pass: 'testing', sendImmediately: false } }, function (error, response, body) { t.equal(error, null) t.equal(response.statusCode, 200) t.equal(numRedirects, 1) t.end() }).on('redirect', function () { t.equal(this.response.statusCode, 401) numRedirects++ }) }) tape('with MD5-sess algorithm', function (t) { var numRedirects = 0 request({ method: 'GET', uri: digestServer.url + '/test/md5-sess', auth: { user: 'test', pass: 'testing', sendImmediately: false } }, function (error, response, body) { t.equal(error, null) t.equal(response.statusCode, 200) t.equal(numRedirects, 1) t.end() }).on('redirect', function () { t.equal(this.response.statusCode, 401) numRedirects++ }) }) tape('without sendImmediately = false', function (t) { var numRedirects = 0 // If we don't set sendImmediately = false, request will send basic auth request({ method: 'GET', uri: digestServer.url + '/test/', auth: { user: 'test', pass: 'testing' } }, function (error, response, body) { t.equal(error, null) t.equal(response.statusCode, 401) t.equal(numRedirects, 0) t.end() }).on('redirect', function () { t.equal(this.response.statusCode, 401) numRedirects++ }) }) tape('with different credentials', function (t) { var numRedirects = 0 request({ method: 'GET', uri: digestServer.url + '/dir/index.html', auth: { user: 'Mufasa', pass: 'CircleOfLife', sendImmediately: false } }, function (error, response, body) { t.equal(error, null) t.equal(response.statusCode, 200) t.equal(numRedirects, 1) t.end() }).on('redirect', function () { t.equal(this.response.statusCode, 401) numRedirects++ }) }) tape('cleanup', function (t) { digestServer.close(function () { t.end() }) }) request-2.88.1/tests/test-emptyBody.js000066400000000000000000000020371333333633500177420ustar00rootroot00000000000000'use strict' var request = require('../index') var http = require('http') var tape = require('tape') var s = http.createServer(function (req, resp) { resp.statusCode = 200 resp.end('') }) tape('setup', function (t) { s.listen(0, function () { s.url = 'http://localhost:' + this.address().port t.end() }) }) tape('empty body with encoding', function (t) { request(s.url, function (err, res, body) { t.equal(err, null) t.equal(res.statusCode, 200) t.equal(body, '') t.end() }) }) tape('empty body without encoding', function (t) { request({ url: s.url, encoding: null }, function (err, res, body) { t.equal(err, null) t.equal(res.statusCode, 200) t.same(body, Buffer.alloc(0)) t.end() }) }) tape('empty JSON body', function (t) { request({ url: s.url, json: {} }, function (err, res, body) { t.equal(err, null) t.equal(res.statusCode, 200) t.equal(body, undefined) t.end() }) }) tape('cleanup', function (t) { s.close(function () { t.end() }) }) request-2.88.1/tests/test-errors.js000066400000000000000000000042611333333633500173030ustar00rootroot00000000000000'use strict' var request = require('../index') var tape = require('tape') var local = 'http://localhost:0/asdf' tape('without uri', function (t) { t.throws(function () { request({}) }, /^Error: options\.uri is a required argument$/) t.end() }) tape('invalid uri 1', function (t) { t.throws(function () { request({ uri: 'this-is-not-a-valid-uri' }) }, /^Error: Invalid URI/) t.end() }) tape('invalid uri 2', function (t) { t.throws(function () { request({ uri: 'github.com/uri-is-not-valid-without-protocol' }) }, /^Error: Invalid URI/) t.end() }) tape('invalid uri + NO_PROXY', function (t) { process.env.NO_PROXY = 'google.com' t.throws(function () { request({ uri: 'invalid' }) }, /^Error: Invalid URI/) delete process.env.NO_PROXY t.end() }) tape('deprecated unix URL', function (t) { t.throws(function () { request({ uri: 'unix://path/to/socket/and/then/request/path' }) }, /^Error: `unix:\/\/` URL scheme is no longer supported/) t.end() }) tape('invalid body', function (t) { t.throws(function () { request({ uri: local, body: {} }) }, /^Error: Argument error, options\.body\.$/) t.end() }) tape('invalid multipart', function (t) { t.throws(function () { request({ uri: local, multipart: 'foo' }) }, /^Error: Argument error, options\.multipart\.$/) t.end() }) tape('multipart without body 1', function (t) { t.throws(function () { request({ uri: local, multipart: [ {} ] }) }, /^Error: Body attribute missing in multipart\.$/) t.end() }) tape('multipart without body 2', function (t) { t.throws(function () { request(local, { multipart: [ {} ] }) }, /^Error: Body attribute missing in multipart\.$/) t.end() }) tape('head method with a body', function (t) { t.throws(function () { request(local, { method: 'HEAD', body: 'foo' }) }, /HTTP HEAD requests MUST NOT include a request body/) t.end() }) tape('head method with a body 2', function (t) { t.throws(function () { request.head(local, { body: 'foo' }) }, /HTTP HEAD requests MUST NOT include a request body/) t.end() }) request-2.88.1/tests/test-event-forwarding.js000066400000000000000000000013751333333633500212530ustar00rootroot00000000000000'use strict' var server = require('./server') var request = require('../index') var tape = require('tape') var s = server.createServer() tape('setup', function (t) { s.listen(0, function () { s.on('/', function (req, res) { res.writeHead(200, { 'content-type': 'text/plain' }) res.write('waited') res.end() }) t.end() }) }) tape('should emit socket event', function (t) { t.plan(4) var req = request(s.url, function (err, res, body) { t.equal(err, null) t.equal(res.statusCode, 200) t.equal(body, 'waited') }) req.on('socket', function (socket) { var requestSocket = req.req.socket t.equal(requestSocket, socket) }) }) tape('cleanup', function (t) { s.close(function () { t.end() }) }) request-2.88.1/tests/test-follow-all-303.js000066400000000000000000000015661333333633500203470ustar00rootroot00000000000000'use strict' var http = require('http') var request = require('../index') var tape = require('tape') var server = http.createServer(function (req, res) { if (req.method === 'POST') { res.setHeader('location', req.url) res.statusCode = 303 res.end('try again') } else { res.end('ok') } }) tape('setup', function (t) { server.listen(0, function () { server.url = 'http://localhost:' + this.address().port t.end() }) }) tape('followAllRedirects with 303', function (t) { var redirects = 0 request.post({ url: server.url + '/foo', followAllRedirects: true, form: { foo: 'bar' } }, function (err, res, body) { t.equal(err, null) t.equal(body, 'ok') t.equal(redirects, 1) t.end() }).on('redirect', function () { redirects++ }) }) tape('cleanup', function (t) { server.close(function () { t.end() }) }) request-2.88.1/tests/test-follow-all.js000066400000000000000000000020511333333633500200320ustar00rootroot00000000000000'use strict' var http = require('http') var request = require('../index') var tape = require('tape') var server = http.createServer(function (req, res) { // redirect everything 3 times, no matter what. var c = req.headers.cookie if (!c) { c = 0 } else { c = +c.split('=')[1] || 0 } if (c > 3) { res.end('ok') return } res.setHeader('set-cookie', 'c=' + (c + 1)) res.setHeader('location', req.url) res.statusCode = 302 res.end('try again') }) tape('setup', function (t) { server.listen(0, function () { server.url = 'http://localhost:' + this.address().port t.end() }) }) tape('followAllRedirects', function (t) { var redirects = 0 request.post({ url: server.url + '/foo', followAllRedirects: true, jar: true, form: { foo: 'bar' } }, function (err, res, body) { t.equal(err, null) t.equal(body, 'ok') t.equal(redirects, 4) t.end() }).on('redirect', function () { redirects++ }) }) tape('cleanup', function (t) { server.close(function () { t.end() }) }) request-2.88.1/tests/test-form-data-error.js000066400000000000000000000035051333333633500207700ustar00rootroot00000000000000'use strict' var request = require('../index') var server = require('./server') var tape = require('tape') var s = server.createServer() tape('setup', function (t) { s.listen(0, function () { t.end() }) }) tape('re-emit formData errors', function (t) { s.on('/', function (req, res) { res.writeHead(400) res.end() t.fail('The form-data error did not abort the request.') }) request.post(s.url, function (err, res, body) { t.equal(err.message, 'form-data: Arrays are not supported.') setTimeout(function () { t.end() }, 10) }).form().append('field', ['value1', 'value2']) }) tape('omit content-length header if the value is set to NaN', function (t) { // returns chunked HTTP response which is streamed to the 2nd HTTP request in the form data s.on('/chunky', server.createChunkResponse( ['some string', 'some other string' ])) // accepts form data request s.on('/stream', function (req, resp) { req.on('data', function (chunk) { // consume the request body }) req.on('end', function () { resp.writeHead(200) resp.end() }) }) var sendStreamRequest = function (stream) { request.post({ uri: s.url + '/stream', formData: { param: stream } }, function (err, res) { t.error(err, 'request failed') t.end() }) } request.get({ uri: s.url + '/chunky' }).on('response', function (res) { sendStreamRequest(res) }) }) // TODO: remove this test after form-data@2.0 starts stringifying null values tape('form-data should throw on null value', function (t) { t.throws(function () { request({ method: 'POST', url: s.url, formData: { key: null } }) }, TypeError) t.end() }) tape('cleanup', function (t) { s.close(function () { t.end() }) }) request-2.88.1/tests/test-form-data.js000066400000000000000000000103551333333633500176420ustar00rootroot00000000000000'use strict' var http = require('http') var path = require('path') var mime = require('mime-types') var request = require('../index') var fs = require('fs') var tape = require('tape') function runTest (t, options) { var remoteFile = path.join(__dirname, 'googledoodle.jpg') var localFile = path.join(__dirname, 'unicycle.jpg') var multipartFormData = {} var server = http.createServer(function (req, res) { if (req.url === '/file') { res.writeHead(200, {'content-type': 'image/jpg', 'content-length': 7187}) res.end(fs.readFileSync(remoteFile), 'binary') return } if (options.auth) { if (!req.headers.authorization) { res.writeHead(401, {'www-authenticate': 'Basic realm="Private"'}) res.end() return } else { t.ok(req.headers.authorization === 'Basic ' + Buffer.from('user:pass').toString('base64')) } } t.ok(/multipart\/form-data; boundary=--------------------------\d+/ .test(req.headers['content-type'])) // temp workaround var data = '' req.setEncoding('utf8') req.on('data', function (d) { data += d }) req.on('end', function () { // check for the fields' traces // 1st field : my_field t.ok(data.indexOf('form-data; name="my_field"') !== -1) t.ok(data.indexOf(multipartFormData.my_field) !== -1) // 2nd field : my_buffer t.ok(data.indexOf('form-data; name="my_buffer"') !== -1) t.ok(data.indexOf(multipartFormData.my_buffer) !== -1) // 3rd field : my_file t.ok(data.indexOf('form-data; name="my_file"') !== -1) t.ok(data.indexOf('; filename="' + path.basename(multipartFormData.my_file.path) + '"') !== -1) // check for unicycle.jpg traces t.ok(data.indexOf('2005:06:21 01:44:12') !== -1) t.ok(data.indexOf('Content-Type: ' + mime.lookup(multipartFormData.my_file.path)) !== -1) // 4th field : remote_file t.ok(data.indexOf('form-data; name="remote_file"') !== -1) t.ok(data.indexOf('; filename="' + path.basename(multipartFormData.remote_file.path) + '"') !== -1) // 5th field : file with metadata t.ok(data.indexOf('form-data; name="secret_file"') !== -1) t.ok(data.indexOf('Content-Disposition: form-data; name="secret_file"; filename="topsecret.jpg"') !== -1) t.ok(data.indexOf('Content-Type: image/custom') !== -1) // 6th field : batch of files t.ok(data.indexOf('form-data; name="batch"') !== -1) t.ok(data.match(/form-data; name="batch"/g).length === 2) // check for http://localhost:nnnn/file traces t.ok(data.indexOf('Photoshop ICC') !== -1) t.ok(data.indexOf('Content-Type: ' + mime.lookup(remoteFile)) !== -1) res.writeHead(200) res.end(options.json ? JSON.stringify({status: 'done'}) : 'done') }) }) server.listen(0, function () { var url = 'http://localhost:' + this.address().port // @NOTE: multipartFormData properties must be set here so that my_file read stream does not leak in node v0.8 multipartFormData.my_field = 'my_value' multipartFormData.my_buffer = Buffer.from([1, 2, 3]) multipartFormData.my_file = fs.createReadStream(localFile) multipartFormData.remote_file = request(url + '/file') multipartFormData.secret_file = { value: fs.createReadStream(localFile), options: { filename: 'topsecret.jpg', contentType: 'image/custom' } } multipartFormData.batch = [ fs.createReadStream(localFile), fs.createReadStream(localFile) ] var reqOptions = { url: url + '/upload', formData: multipartFormData } if (options.json) { reqOptions.json = true } if (options.auth) { reqOptions.auth = {user: 'user', pass: 'pass', sendImmediately: false} } request.post(reqOptions, function (err, res, body) { t.equal(err, null) t.equal(res.statusCode, 200) t.deepEqual(body, options.json ? {status: 'done'} : 'done') server.close(function () { t.end() }) }) }) } tape('multipart formData', function (t) { runTest(t, {json: false}) }) tape('multipart formData + JSON', function (t) { runTest(t, {json: true}) }) tape('multipart formData + basic auth', function (t) { runTest(t, {json: false, auth: true}) }) request-2.88.1/tests/test-form-urlencoded.js000066400000000000000000000033451333333633500210560ustar00rootroot00000000000000'use strict' var http = require('http') var request = require('../index') var tape = require('tape') function runTest (t, options, index) { var server = http.createServer(function (req, res) { if (index === 0 || index === 3) { t.equal(req.headers['content-type'], 'application/x-www-form-urlencoded') } else { t.equal(req.headers['content-type'], 'application/x-www-form-urlencoded; charset=UTF-8') } t.equal(req.headers['content-length'], '21') t.equal(req.headers.accept, 'application/json') var data = '' req.setEncoding('utf8') req.on('data', function (d) { data += d }) req.on('end', function () { t.equal(data, 'some=url&encoded=data') res.writeHead(200) res.end('done') }) }) server.listen(0, function () { var url = 'http://localhost:' + this.address().port var r = request.post(url, options, function (err, res, body) { t.equal(err, null) t.equal(res.statusCode, 200) t.equal(body, 'done') server.close(function () { t.end() }) }) if (!options.form && !options.body) { r.form({some: 'url', encoded: 'data'}) } }) } var cases = [ { form: {some: 'url', encoded: 'data'}, json: true }, { headers: {'content-type': 'application/x-www-form-urlencoded; charset=UTF-8'}, form: {some: 'url', encoded: 'data'}, json: true }, { headers: {'content-type': 'application/x-www-form-urlencoded; charset=UTF-8'}, body: 'some=url&encoded=data', json: true }, { // body set via .form() method json: true } ] cases.forEach(function (options, index) { tape('application/x-www-form-urlencoded ' + index, function (t) { runTest(t, options, index) }) }) request-2.88.1/tests/test-form.js000066400000000000000000000057641333333633500167430ustar00rootroot00000000000000'use strict' var http = require('http') var path = require('path') var mime = require('mime-types') var request = require('../index') var fs = require('fs') var tape = require('tape') tape('multipart form append', function (t) { var remoteFile = path.join(__dirname, 'googledoodle.jpg') var localFile = path.join(__dirname, 'unicycle.jpg') var totalLength = null var FIELDS = [] var server = http.createServer(function (req, res) { if (req.url === '/file') { res.writeHead(200, {'content-type': 'image/jpg', 'content-length': 7187}) res.end(fs.readFileSync(remoteFile), 'binary') return } t.ok(/multipart\/form-data; boundary=--------------------------\d+/ .test(req.headers['content-type'])) // temp workaround var data = '' req.setEncoding('utf8') req.on('data', function (d) { data += d }) req.on('end', function () { var field // check for the fields' traces // 1st field : my_field field = FIELDS.shift() t.ok(data.indexOf('form-data; name="' + field.name + '"') !== -1) t.ok(data.indexOf(field.value) !== -1) // 2nd field : my_buffer field = FIELDS.shift() t.ok(data.indexOf('form-data; name="' + field.name + '"') !== -1) t.ok(data.indexOf(field.value) !== -1) // 3rd field : my_file field = FIELDS.shift() t.ok(data.indexOf('form-data; name="' + field.name + '"') !== -1) t.ok(data.indexOf('; filename="' + path.basename(field.value.path) + '"') !== -1) // check for unicycle.jpg traces t.ok(data.indexOf('2005:06:21 01:44:12') !== -1) t.ok(data.indexOf('Content-Type: ' + mime.lookup(field.value.path)) !== -1) // 4th field : remote_file field = FIELDS.shift() t.ok(data.indexOf('form-data; name="' + field.name + '"') !== -1) t.ok(data.indexOf('; filename="' + path.basename(field.value.path) + '"') !== -1) // check for http://localhost:nnnn/file traces t.ok(data.indexOf('Photoshop ICC') !== -1) t.ok(data.indexOf('Content-Type: ' + mime.lookup(remoteFile)) !== -1) t.ok(+req.headers['content-length'] === totalLength) res.writeHead(200) res.end('done') t.equal(FIELDS.length, 0) }) }) server.listen(0, function () { var url = 'http://localhost:' + this.address().port FIELDS = [ { name: 'my_field', value: 'my_value' }, { name: 'my_buffer', value: Buffer.from([1, 2, 3]) }, { name: 'my_file', value: fs.createReadStream(localFile) }, { name: 'remote_file', value: request(url + '/file') } ] var req = request.post(url + '/upload', function (err, res, body) { t.equal(err, null) t.equal(res.statusCode, 200) t.equal(body, 'done') server.close(function () { t.end() }) }) var form = req.form() FIELDS.forEach(function (field) { form.append(field.name, field.value) }) form.getLength(function (err, length) { t.equal(err, null) totalLength = length }) }) }) request-2.88.1/tests/test-gzip.js000066400000000000000000000177701333333633500167510ustar00rootroot00000000000000'use strict' var request = require('../index') var http = require('http') var zlib = require('zlib') var assert = require('assert') var bufferEqual = require('buffer-equal') var tape = require('tape') var testContent = 'Compressible response content.\n' var testContentBig var testContentBigGzip var testContentGzip var server = http.createServer(function (req, res) { res.statusCode = 200 res.setHeader('Content-Type', 'text/plain') if (req.method === 'HEAD') { res.setHeader('Content-Encoding', 'gzip') res.end() return } if (req.headers.code) { res.writeHead(req.headers.code, { 'Content-Encoding': 'gzip', code: req.headers.code }) res.end() return } if (/\bgzip\b/i.test(req.headers['accept-encoding'])) { res.setHeader('Content-Encoding', 'gzip') if (req.url === '/error') { // send plaintext instead of gzip (should cause an error for the client) res.end(testContent) } else if (req.url === '/chunks') { res.writeHead(200) res.write(testContentBigGzip.slice(0, 4096)) setTimeout(function () { res.end(testContentBigGzip.slice(4096)) }, 10) } else if (req.url === '/just-slightly-truncated') { zlib.gzip(testContent, function (err, data) { assert.equal(err, null) // truncate the CRC checksum and size check at the end of the stream res.end(data.slice(0, data.length - 8)) }) } else { zlib.gzip(testContent, function (err, data) { assert.equal(err, null) res.end(data) }) } } else if (/\bdeflate\b/i.test(req.headers['accept-encoding'])) { res.setHeader('Content-Encoding', 'deflate') zlib.deflate(testContent, function (err, data) { assert.equal(err, null) res.end(data) }) } else { res.end(testContent) } }) tape('setup', function (t) { // Need big compressed content to be large enough to chunk into gzip blocks. // Want it to be deterministic to ensure test is reliable. // Generate pseudo-random printable ASCII characters using MINSTD var a = 48271 var m = 0x7FFFFFFF var x = 1 testContentBig = Buffer.alloc(10240) for (var i = 0; i < testContentBig.length; ++i) { x = (a * x) & m // Printable ASCII range from 32-126, inclusive testContentBig[i] = (x % 95) + 32 } zlib.gzip(testContent, function (err, data) { t.equal(err, null) testContentGzip = data zlib.gzip(testContentBig, function (err, data2) { t.equal(err, null) testContentBigGzip = data2 server.listen(0, function () { server.url = 'http://localhost:' + this.address().port t.end() }) }) }) }) tape('transparently supports gzip decoding to callbacks', function (t) { var options = { url: server.url + '/foo', gzip: true } request.get(options, function (err, res, body) { t.equal(err, null) t.equal(res.headers['content-encoding'], 'gzip') t.equal(body, testContent) t.end() }) }) tape('supports slightly invalid gzip content', function (t) { var options = { url: server.url + '/just-slightly-truncated', gzip: true } request.get(options, function (err, res, body) { t.equal(err, null) t.equal(res.headers['content-encoding'], 'gzip') t.equal(body, testContent) t.end() }) }) tape('transparently supports gzip decoding to pipes', function (t) { var options = { url: server.url + '/foo', gzip: true } var chunks = [] request.get(options) .on('data', function (chunk) { chunks.push(chunk) }) .on('end', function () { t.equal(Buffer.concat(chunks).toString(), testContent) t.end() }) .on('error', function (err) { t.fail(err) }) }) tape('does not request gzip if user specifies Accepted-Encodings', function (t) { var headers = { 'Accept-Encoding': null } var options = { url: server.url + '/foo', headers: headers, gzip: true } request.get(options, function (err, res, body) { t.equal(err, null) t.equal(res.headers['content-encoding'], undefined) t.equal(body, testContent) t.end() }) }) tape('does not decode user-requested encoding by default', function (t) { var headers = { 'Accept-Encoding': 'gzip' } var options = { url: server.url + '/foo', headers: headers } request.get(options, function (err, res, body) { t.equal(err, null) t.equal(res.headers['content-encoding'], 'gzip') t.equal(body, testContentGzip.toString()) t.end() }) }) tape('supports character encoding with gzip encoding', function (t) { var headers = { 'Accept-Encoding': 'gzip' } var options = { url: server.url + '/foo', headers: headers, gzip: true, encoding: 'utf8' } var strings = [] request.get(options) .on('data', function (string) { t.equal(typeof string, 'string') strings.push(string) }) .on('end', function () { t.equal(strings.join(''), testContent) t.end() }) .on('error', function (err) { t.fail(err) }) }) tape('transparently supports gzip error to callbacks', function (t) { var options = { url: server.url + '/error', gzip: true } request.get(options, function (err, res, body) { t.equal(err.code, 'Z_DATA_ERROR') t.equal(res, undefined) t.equal(body, undefined) t.end() }) }) tape('transparently supports gzip error to pipes', function (t) { var options = { url: server.url + '/error', gzip: true } request.get(options) .on('data', function (chunk) { t.fail('Should not receive data event') }) .on('end', function () { t.fail('Should not receive end event') }) .on('error', function (err) { t.equal(err.code, 'Z_DATA_ERROR') t.end() }) }) tape('pause when streaming from a gzip request object', function (t) { var chunks = [] var paused = false var options = { url: server.url + '/chunks', gzip: true } request.get(options) .on('data', function (chunk) { var self = this t.notOk(paused, 'Only receive data when not paused') chunks.push(chunk) if (chunks.length === 1) { self.pause() paused = true setTimeout(function () { paused = false self.resume() }, 100) } }) .on('end', function () { t.ok(chunks.length > 1, 'Received multiple chunks') t.ok(bufferEqual(Buffer.concat(chunks), testContentBig), 'Expected content') t.end() }) }) tape('pause before streaming from a gzip request object', function (t) { var paused = true var options = { url: server.url + '/foo', gzip: true } var r = request.get(options) r.pause() r.on('data', function (data) { t.notOk(paused, 'Only receive data when not paused') t.equal(data.toString(), testContent) }) r.on('end', t.end.bind(t)) setTimeout(function () { paused = false r.resume() }, 100) }) tape('transparently supports deflate decoding to callbacks', function (t) { var options = { url: server.url + '/foo', gzip: true, headers: { 'Accept-Encoding': 'deflate' } } request.get(options, function (err, res, body) { t.equal(err, null) t.equal(res.headers['content-encoding'], 'deflate') t.equal(body, testContent) t.end() }) }) tape('do not try to pipe HEAD request responses', function (t) { var options = { method: 'HEAD', url: server.url + '/foo', gzip: true } request(options, function (err, res, body) { t.equal(err, null) t.equal(body, '') t.end() }) }) tape('do not try to pipe responses with no body', function (t) { var options = { url: server.url + '/foo', gzip: true } // skip 105 on Node >= v10 var statusCodes = process.version.split('.')[0].slice(1) >= 10 ? [204, 304] : [105, 204, 304] ;(function next (index) { if (index === statusCodes.length) { t.end() return } options.headers = {code: statusCodes[index]} request.post(options, function (err, res, body) { t.equal(err, null) t.equal(res.headers.code, statusCodes[index].toString()) t.equal(body, '') next(++index) }) })(0) }) tape('cleanup', function (t) { server.close(function () { t.end() }) }) request-2.88.1/tests/test-har.js000066400000000000000000000073401333333633500165420ustar00rootroot00000000000000'use strict' var path = require('path') var request = require('..') var tape = require('tape') var fixture = require('./fixtures/har.json') var server = require('./server') var s = server.createEchoServer() tape('setup', function (t) { s.listen(0, function () { t.end() }) }) tape('application-form-encoded', function (t) { var options = { url: s.url, har: fixture['application-form-encoded'] } request(options, function (err, res, body) { var json = JSON.parse(body) t.equal(err, null) t.equal(json.body, 'foo=bar&hello=world') t.end() }) }) tape('application-json', function (t) { var options = { url: s.url, har: fixture['application-json'] } request(options, function (err, res, body) { t.equal(err, null) t.equal(body.body, fixture['application-json'].postData.text) t.end() }) }) tape('cookies', function (t) { var options = { url: s.url, har: fixture.cookies } request(options, function (err, res, body) { var json = JSON.parse(body) t.equal(err, null) t.equal(json.headers.cookie, 'foo=bar; bar=baz') t.end() }) }) tape('custom-method', function (t) { var options = { url: s.url, har: fixture['custom-method'] } request(options, function (err, res, body) { var json = JSON.parse(body) t.equal(err, null) t.equal(json.method, fixture['custom-method'].method) t.end() }) }) tape('headers', function (t) { var options = { url: s.url, har: fixture.headers } request(options, function (err, res, body) { var json = JSON.parse(body) t.equal(err, null) t.equal(json.headers['x-foo'], 'Bar') t.end() }) }) tape('multipart-data', function (t) { var options = { url: s.url, har: fixture['multipart-data'] } request(options, function (err, res, body) { var json = JSON.parse(body) t.equal(err, null) t.ok(~json.headers['content-type'].indexOf('multipart/form-data')) t.ok(~json.body.indexOf('Content-Disposition: form-data; name="foo"; filename="hello.txt"\r\nContent-Type: text/plain\r\n\r\nHello World')) t.end() }) }) tape('multipart-file', function (t) { var options = { url: s.url, har: fixture['multipart-file'] } var absolutePath = path.resolve(__dirname, options.har.postData.params[0].fileName) options.har.postData.params[0].fileName = absolutePath request(options, function (err, res, body) { var json = JSON.parse(body) t.equal(err, null) t.ok(~json.headers['content-type'].indexOf('multipart/form-data')) t.ok(~json.body.indexOf('Content-Disposition: form-data; name="foo"; filename="unicycle.jpg"\r\nContent-Type: image/jpeg')) t.end() }) }) tape('multipart-form-data', function (t) { var options = { url: s.url, har: fixture['multipart-form-data'] } request(options, function (err, res, body) { var json = JSON.parse(body) t.equal(err, null) t.ok(~json.headers['content-type'].indexOf('multipart/form-data')) t.ok(~json.body.indexOf('Content-Disposition: form-data; name="foo"')) t.end() }) }) tape('query', function (t) { var options = { url: s.url + '/?fff=sss', har: fixture.query } request(options, function (err, res, body) { var json = JSON.parse(body) t.equal(err, null) t.equal(json.url, '/?fff=sss&foo%5B0%5D=bar&foo%5B1%5D=baz&baz=abc') t.end() }) }) tape('text/plain', function (t) { var options = { url: s.url, har: fixture['text-plain'] } request(options, function (err, res, body) { var json = JSON.parse(body) t.equal(err, null) t.equal(json.headers['content-type'], 'text/plain') t.equal(json.body, 'Hello World') t.end() }) }) tape('cleanup', function (t) { s.close(function () { t.end() }) }) request-2.88.1/tests/test-hawk.js000066400000000000000000000101401333333633500167120ustar00rootroot00000000000000'use strict' var http = require('http') var request = require('../index') var hawk = require('../lib/hawk') var tape = require('tape') var assert = require('assert') var server = http.createServer(function (req, res) { res.writeHead(200, { 'Content-Type': 'text/plain' }) res.end(authenticate(req)) }) tape('setup', function (t) { server.listen(0, function () { server.url = 'http://localhost:' + this.address().port t.end() }) }) var creds = { key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn', algorithm: 'sha256', id: 'dh37fgj492je' } tape('hawk-get', function (t) { request(server.url, { hawk: { credentials: creds } }, function (err, res, body) { t.equal(err, null) t.equal(res.statusCode, 200) t.equal(body, 'OK') t.end() }) }) tape('hawk-post', function (t) { request.post({ url: server.url, body: 'hello', hawk: { credentials: creds, payload: 'hello' } }, function (err, res, body) { t.equal(err, null) t.equal(res.statusCode, 200) t.equal(body, 'OK') t.end() }) }) tape('hawk-ext', function (t) { request(server.url, { hawk: { credentials: creds, ext: 'test' } }, function (err, res, body) { t.equal(err, null) t.equal(res.statusCode, 200) t.equal(body, 'OK') t.end() }) }) tape('hawk-app', function (t) { request(server.url, { hawk: { credentials: creds, app: 'test' } }, function (err, res, body) { t.equal(err, null) t.equal(res.statusCode, 200) t.equal(body, 'OK') t.end() }) }) tape('hawk-app+dlg', function (t) { request(server.url, { hawk: { credentials: creds, app: 'test', dlg: 'asd' } }, function (err, res, body) { t.equal(err, null) t.equal(res.statusCode, 200) t.equal(body, 'OK') t.end() }) }) tape('hawk-missing-creds', function (t) { request(server.url, { hawk: {} }, function (err, res, body) { t.equal(err, null) t.equal(res.statusCode, 200) t.equal(body, 'FAIL') t.end() }) }) tape('hawk-missing-creds-id', function (t) { request(server.url, { hawk: { credentials: {} } }, function (err, res, body) { t.equal(err, null) t.equal(res.statusCode, 200) t.equal(body, 'FAIL') t.end() }) }) tape('hawk-missing-creds-key', function (t) { request(server.url, { hawk: { credentials: { id: 'asd' } } }, function (err, res, body) { t.equal(err, null) t.equal(res.statusCode, 200) t.equal(body, 'FAIL') t.end() }) }) tape('hawk-missing-creds-algo', function (t) { request(server.url, { hawk: { credentials: { key: '123', id: '123' } } }, function (err, res, body) { t.equal(err, null) t.equal(res.statusCode, 200) t.equal(body, 'FAIL') t.end() }) }) tape('hawk-invalid-creds-algo', function (t) { request(server.url, { hawk: { credentials: { key: '123', id: '123', algorithm: 'xx' } } }, function (err, res, body) { t.equal(err, null) t.equal(res.statusCode, 200) t.equal(body, 'FAIL') t.end() }) }) tape('cleanup', function (t) { server.close(function () { t.end() }) }) function authenticate (req) { if (!req.headers.authorization) { return 'FAIL' } var headerParts = req.headers.authorization.match(/^(\w+)(?:\s+(.*))?$/) assert.equal(headerParts[1], 'Hawk') var attributes = {} headerParts[2].replace(/(\w+)="([^"\\]*)"\s*(?:,\s*|$)/g, function ($0, $1, $2) { attributes[$1] = $2 }) var hostParts = req.headers.host.split(':') const artifacts = { method: req.method, host: hostParts[0], port: (hostParts[1] ? hostParts[1] : (req.connection && req.connection.encrypted ? 443 : 80)), resource: req.url, ts: attributes.ts, nonce: attributes.nonce, hash: attributes.hash, ext: attributes.ext, app: attributes.app, dlg: attributes.dlg, mac: attributes.mac, id: attributes.id } assert.equal(attributes.id, 'dh37fgj492je') var credentials = { key: 'werxhqb98rpaxn39848xrunpaw3489ruxnpa98w4rxn', algorithm: 'sha256', user: 'Steve' } const mac = hawk.calculateMac(credentials, artifacts) assert.equal(mac, attributes.mac) return 'OK' } request-2.88.1/tests/test-headers.js000066400000000000000000000172151333333633500174050ustar00rootroot00000000000000'use strict' var server = require('./server') var request = require('../index') var util = require('util') var tape = require('tape') var url = require('url') var os = require('os') var interfaces = os.networkInterfaces() var loopbackKeyTest = os.platform() === 'win32' ? /Loopback Pseudo-Interface/ : /lo/ var hasIPv6interface = Object.keys(interfaces).some(function (name) { return loopbackKeyTest.test(name) && interfaces[name].some(function (info) { return info.family === 'IPv6' }) }) var s = server.createServer() s.on('/redirect/from', function (req, res) { res.writeHead(301, { location: '/redirect/to' }) res.end() }) s.on('/redirect/to', function (req, res) { res.end('ok') }) s.on('/headers.json', function (req, res) { res.writeHead(200, { 'Content-Type': 'application/json' }) res.end(JSON.stringify(req.headers)) }) function runTest (name, path, requestObj, serverAssertFn) { tape(name, function (t) { s.on('/' + path, function (req, res) { serverAssertFn(t, req, res) res.writeHead(200) res.end() }) requestObj.url = s.url + '/' + path request(requestObj, function (err, res, body) { t.equal(err, null) t.equal(res.statusCode, 200) t.end() }) }) } function addTests () { runTest( '#125: headers.cookie with no cookie jar', 'no-jar', {headers: {cookie: 'foo=bar'}}, function (t, req, res) { t.equal(req.headers.cookie, 'foo=bar') }) var jar = request.jar() jar.setCookie('quux=baz', s.url) runTest( '#125: headers.cookie + cookie jar', 'header-and-jar', {jar: jar, headers: {cookie: 'foo=bar'}}, function (t, req, res) { t.equal(req.headers.cookie, 'foo=bar; quux=baz') }) var jar2 = request.jar() jar2.setCookie('quux=baz; Domain=foo.bar.com', s.url, {ignoreError: true}) runTest( '#794: ignore cookie parsing and domain errors', 'ignore-errors', {jar: jar2, headers: {cookie: 'foo=bar'}}, function (t, req, res) { t.equal(req.headers.cookie, 'foo=bar') }) runTest( '#784: override content-type when json is used', 'json', { json: true, method: 'POST', headers: { 'content-type': 'application/json; charset=UTF-8' }, body: { hello: 'my friend' }}, function (t, req, res) { t.equal(req.headers['content-type'], 'application/json; charset=UTF-8') } ) runTest( 'neither headers.cookie nor a cookie jar is specified', 'no-cookie', {}, function (t, req, res) { t.equal(req.headers.cookie, undefined) }) } tape('setup', function (t) { s.listen(0, function () { addTests() tape('cleanup', function (t) { s.close(function () { t.end() }) }) t.end() }) }) tape('upper-case Host header and redirect', function (t) { // Horrible hack to observe the raw data coming to the server (before Node // core lower-cases the headers) var rawData = '' s.on('connection', function (socket) { if (socket.ondata) { var ondata = socket.ondata } function handledata (d, start, end) { if (ondata) { rawData += d.slice(start, end).toString() return ondata.apply(this, arguments) } else { rawData += d } } socket.on('data', handledata) socket.ondata = handledata }) function checkHostHeader (host) { t.ok( new RegExp('^Host: ' + host + '$', 'm').test(rawData), util.format( 'Expected "Host: %s" in data "%s"', host, rawData.trim().replace(/\r?\n/g, '\\n'))) rawData = '' } var redirects = 0 request({ url: s.url + '/redirect/from', headers: { Host: '127.0.0.1' } }, function (err, res, body) { t.equal(err, null) t.equal(res.statusCode, 200) t.equal(body, 'ok') t.equal(redirects, 1) // XXX should the host header change like this after a redirect? checkHostHeader('localhost:' + s.port) t.end() }).on('redirect', function () { redirects++ t.equal(this.uri.href, s.url + '/redirect/to') checkHostHeader('127.0.0.1') }) }) tape('undefined headers', function (t) { request({ url: s.url + '/headers.json', headers: { 'X-TEST-1': 'test1', 'X-TEST-2': undefined }, json: true }, function (err, res, body) { t.equal(err, null) t.equal(body['x-test-1'], 'test1') t.equal(typeof body['x-test-2'], 'undefined') t.end() }) }) tape('preserve port in host header if non-standard port', function (t) { var r = request({ url: s.url + '/headers.json' }, function (err, res, body) { t.equal(err, null) t.equal(r.originalHost, 'localhost:' + s.port) t.end() }) }) tape('strip port in host header if explicit standard port (:80) & protocol (HTTP)', function (t) { var r = request({ url: 'http://localhost:80/headers.json' }, function (_err, res, body) { t.equal(r.req.socket._host, 'localhost') t.end() }) }) tape('strip port in host header if explicit standard port (:443) & protocol (HTTPS)', function (t) { var r = request({ url: 'https://localhost:443/headers.json' }, function (_err, res, body) { t.equal(r.req.socket._host, 'localhost') t.end() }) }) tape('strip port in host header if implicit standard port & protocol (HTTP)', function (t) { var r = request({ url: 'http://localhost/headers.json' }, function (_err, res, body) { t.equal(r.req.socket._host, 'localhost') t.end() }) }) tape('strip port in host header if implicit standard port & protocol (HTTPS)', function (t) { var r = request({ url: 'https://localhost/headers.json' }, function (_err, res, body) { t.equal(r.req.socket._host, 'localhost') t.end() }) }) var isExpectedHeaderCharacterError = function (headerName, err) { return err.message === 'The header content contains invalid characters' || err.message === ('Invalid character in header content ["' + headerName + '"]') } tape('catch invalid characters error - GET', function (t) { request({ url: s.url + '/headers.json', headers: { 'test': 'אבגד' } }, function (err, res, body) { t.true(isExpectedHeaderCharacterError('test', err)) }) .on('error', function (err) { t.true(isExpectedHeaderCharacterError('test', err)) t.end() }) }) tape('catch invalid characters error - POST', function (t) { request({ method: 'POST', url: s.url + '/headers.json', headers: { 'test': 'אבגד' }, body: 'beep' }, function (err, res, body) { t.true(isExpectedHeaderCharacterError('test', err)) }) .on('error', function (err) { t.true(isExpectedHeaderCharacterError('test', err)) t.end() }) }) if (hasIPv6interface) { tape('IPv6 Host header', function (t) { // Horrible hack to observe the raw data coming to the server var rawData = '' s.on('connection', function (socket) { if (socket.ondata) { var ondata = socket.ondata } function handledata (d, start, end) { if (ondata) { rawData += d.slice(start, end).toString() return ondata.apply(this, arguments) } else { rawData += d } } socket.on('data', handledata) socket.ondata = handledata }) function checkHostHeader (host) { t.ok( new RegExp('^Host: ' + host + '$', 'im').test(rawData), util.format( 'Expected "Host: %s" in data "%s"', host, rawData.trim().replace(/\r?\n/g, '\\n'))) rawData = '' } request({ url: s.url.replace(url.parse(s.url).hostname, '[::1]') + '/headers.json' }, function (err, res, body) { t.equal(err, null) t.equal(res.statusCode, 200) checkHostHeader('\\[::1\\]:' + s.port) t.end() }) }) } request-2.88.1/tests/test-http-signature.js000066400000000000000000000102401333333633500207370ustar00rootroot00000000000000'use strict' var http = require('http') var request = require('../index') var httpSignature = require('http-signature') var tape = require('tape') var privateKeyPEMs = {} privateKeyPEMs['key-1'] = '-----BEGIN RSA PRIVATE KEY-----\n' + 'MIIEpAIBAAKCAQEAzWSJl+Z9Bqv00FVL5N3+JCUoqmQPjIlya1BbeqQroNQ5yG1i\n' + 'VbYTTnMRa1zQtR6r2fNvWeg94DvxivxIG9diDMnrzijAnYlTLOl84CK2vOxkj5b6\n' + '8zrLH9b/Gd6NOHsywo8IjvXvCeTfca5WUHcuVi2lT9VjygFs1ILG4RyeX1BXUumu\n' + 'Y8fzmposxLYdMxCqUTzAn0u9Saq2H2OVj5u114wS7OQPigu6G99dpn/iPHa3zBm8\n' + '7baBWDbqZWRW0BP3K6eqq8sut1+NLhNW8ADPTdnO/SO+kvXy7fqd8atSn+HlQcx6\n' + 'tW42dhXf3E9uE7K78eZtW0KvfyNGAjsI1Fft2QIDAQABAoIBAG1exe3/LEBrPLfb\n' + 'U8iRdY0lxFvHYIhDgIwohC3wUdMYb5SMurpNdEZn+7Sh/fkUVgp/GKJViu1mvh52\n' + 'bKd2r52DwG9NQBQjVgkqY/auRYSglIPpr8PpYNSZlcneunCDGeqEY9hMmXc5Ssqs\n' + 'PQYoEKKPN+IlDTg6PguDgAfLR4IUvt9KXVvmB/SSgV9tSeTy35LECt1Lq3ozbUgu\n' + '30HZI3U6/7H+X22Pxxf8vzBtzkg5rRCLgv+OeNPo16xMnqbutt4TeqEkxRv5rtOo\n' + '/A1i9khBeki0OJAFJsE82qnaSZodaRsxic59VnN8sWBwEKAt87tEu5A3K3j4XSDU\n' + '/avZxAECgYEA+pS3DvpiQLtHlaO3nAH6MxHRrREOARXWRDe5nUQuUNpS1xq9wte6\n' + 'DkFtba0UCvDLic08xvReTCbo9kH0y6zEy3zMpZuJlKbcWCkZf4S5miYPI0RTZtF8\n' + 'yps6hWqzYFSiO9hMYws9k4OJLxX0x3sLK7iNZ32ujcSrkPBSiBr0gxkCgYEA0dWl\n' + '637K41AJ/zy0FP0syq+r4eIkfqv+/t6y2aQVUBvxJYrj9ci6XHBqoxpDV8lufVYj\n' + 'fUAfeI9/MZaWvQJRbnYLre0I6PJfLuCBIL5eflO77BGso165AF7QJZ+fwtgKv3zv\n' + 'ZX75eudCSS/cFo0po9hlbcLMT4B82zEkgT8E2MECgYEAnz+3/wrdOmpLGiyL2dff\n' + '3GjsqmJ2VfY8z+niSrI0BSpbD11tT9Ct67VlCBjA7hsOH6uRfpd6/kaUMzzDiFVq\n' + 'VDAiFvV8QD6zNkwYalQ9aFvbrvwTTPrBpjl0vamMCiJ/YC0cjq1sGr2zh3sar1Ph\n' + 'S43kP+s97dcZeelhaiJHVrECgYEAsx61q/loJ/LDFeYzs1cLTVn4V7I7hQY9fkOM\n' + 'WM0AhInVqD6PqdfXfeFYpjJdGisQ7l0BnoGGW9vir+nkcyPvb2PFRIr6+B8tsU5j\n' + '7BeVgjDoUfQkcrEBK5fEBtnj/ud9BUkY8oMZZBjVNLRuI7IMwZiPvMp0rcj4zAN/\n' + 'LfUlpgECgYArBvFcBxSkNAzR3Rtteud1YDboSKluRM37Ey5plrn4BS0DD0jm++aD\n' + '0pG2Hsik000hibw92lCkzvvBVAqF8BuAcnPlAeYfsOaa97PGEjSKEN5bJVWZ9/om\n' + '9FV1axotRN2XWlwrhixZLEaagkREXhgQc540FS5O8IaI2Vpa80Atzg==\n' + '-----END RSA PRIVATE KEY-----' var publicKeyPEMs = {} publicKeyPEMs['key-1'] = '-----BEGIN PUBLIC KEY-----\n' + 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzWSJl+Z9Bqv00FVL5N3+\n' + 'JCUoqmQPjIlya1BbeqQroNQ5yG1iVbYTTnMRa1zQtR6r2fNvWeg94DvxivxIG9di\n' + 'DMnrzijAnYlTLOl84CK2vOxkj5b68zrLH9b/Gd6NOHsywo8IjvXvCeTfca5WUHcu\n' + 'Vi2lT9VjygFs1ILG4RyeX1BXUumuY8fzmposxLYdMxCqUTzAn0u9Saq2H2OVj5u1\n' + '14wS7OQPigu6G99dpn/iPHa3zBm87baBWDbqZWRW0BP3K6eqq8sut1+NLhNW8ADP\n' + 'TdnO/SO+kvXy7fqd8atSn+HlQcx6tW42dhXf3E9uE7K78eZtW0KvfyNGAjsI1Fft\n' + '2QIDAQAB\n' + '-----END PUBLIC KEY-----' publicKeyPEMs['key-2'] = '-----BEGIN PUBLIC KEY-----\n' + 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqp04VVr9OThli9b35Omz\n' + 'VqSfWbsoQuRrgyWsrNRn3XkFmbWw4FzZwQ42OgGMzQ84Ta4d9zGKKQyFriTiPjPf\n' + 'xhhrsaJnDuybcpVhcr7UNKjSZ0S59tU3hpRiEz6hO+Nc/OSSLkvalG0VKrxOln7J\n' + 'LK/h3rNS/l6wDZ5S/KqsI6CYtV2ZLpn3ahLrizvEYNY038Qcm38qMWx+VJAvZ4di\n' + 'qqmW7RLIsLT59SWmpXdhFKnkYYGhxrk1Mwl22dBTJNY5SbriU5G3gWgzYkm8pgHr\n' + '6CtrXch9ciJAcDJehPrKXNvNDOdUh8EW3fekNJerF1lWcwQg44/12v8sDPyfbaKB\n' + 'dQIDAQAB\n' + '-----END PUBLIC KEY-----' var server = http.createServer(function (req, res) { var parsed = httpSignature.parseRequest(req) var publicKeyPEM = publicKeyPEMs[parsed.keyId] var verified = httpSignature.verifySignature(parsed, publicKeyPEM) res.writeHead(verified ? 200 : 400) res.end() }) tape('setup', function (t) { server.listen(0, function () { server.url = 'http://localhost:' + this.address().port t.end() }) }) tape('correct key', function (t) { var options = { httpSignature: { keyId: 'key-1', key: privateKeyPEMs['key-1'] } } request(server.url, options, function (err, res, body) { t.equal(err, null) t.equal(200, res.statusCode) t.end() }) }) tape('incorrect key', function (t) { var options = { httpSignature: { keyId: 'key-2', key: privateKeyPEMs['key-1'] } } request(server.url, options, function (err, res, body) { t.equal(err, null) t.equal(400, res.statusCode) t.end() }) }) tape('cleanup', function (t) { server.close(function () { t.end() }) }) request-2.88.1/tests/test-httpModule.js000066400000000000000000000057601333333633500201210ustar00rootroot00000000000000'use strict' var http = require('http') var https = require('https') var destroyable = require('server-destroy') var server = require('./server') var request = require('../index') var tape = require('tape') var fauxRequestsMade function clearFauxRequests () { fauxRequestsMade = { http: 0, https: 0 } } function wrapRequest (name, module) { // Just like the http or https module, but note when a request is made. var wrapped = {} Object.keys(module).forEach(function (key) { var value = module[key] if (key === 'request') { wrapped[key] = function (/* options, callback */) { fauxRequestsMade[name] += 1 return value.apply(this, arguments) } } else { wrapped[key] = value } }) return wrapped } var fauxHTTP = wrapRequest('http', http) var fauxHTTPS = wrapRequest('https', https) var plainServer = server.createServer() var httpsServer = server.createSSLServer() destroyable(plainServer) destroyable(httpsServer) tape('setup', function (t) { plainServer.listen(0, function () { plainServer.on('/plain', function (req, res) { res.writeHead(200) res.end('plain') }) plainServer.on('/to_https', function (req, res) { res.writeHead(301, { 'location': 'https://localhost:' + httpsServer.port + '/https' }) res.end() }) httpsServer.listen(0, function () { httpsServer.on('/https', function (req, res) { res.writeHead(200) res.end('https') }) httpsServer.on('/to_plain', function (req, res) { res.writeHead(302, { 'location': 'http://localhost:' + plainServer.port + '/plain' }) res.end() }) t.end() }) }) }) function runTests (name, httpModules) { tape(name, function (t) { var toHttps = 'http://localhost:' + plainServer.port + '/to_https' var toPlain = 'https://localhost:' + httpsServer.port + '/to_plain' var options = { httpModules: httpModules, strictSSL: false } var modulesTest = httpModules || {} clearFauxRequests() request(toHttps, options, function (err, res, body) { t.equal(err, null) t.equal(res.statusCode, 200) t.equal(body, 'https', 'Received HTTPS server body') t.equal(fauxRequestsMade.http, modulesTest['http:'] ? 1 : 0) t.equal(fauxRequestsMade.https, modulesTest['https:'] ? 1 : 0) request(toPlain, options, function (err, res, body) { t.equal(err, null) t.equal(res.statusCode, 200) t.equal(body, 'plain', 'Received HTTPS server body') t.equal(fauxRequestsMade.http, modulesTest['http:'] ? 2 : 0) t.equal(fauxRequestsMade.https, modulesTest['https:'] ? 2 : 0) t.end() }) }) }) } runTests('undefined') runTests('empty', {}) runTests('http only', { 'http:': fauxHTTP }) runTests('https only', { 'https:': fauxHTTPS }) runTests('http and https', { 'http:': fauxHTTP, 'https:': fauxHTTPS }) tape('cleanup', function (t) { plainServer.destroy(function () { httpsServer.destroy(function () { t.end() }) }) }) request-2.88.1/tests/test-https.js000066400000000000000000000060461333333633500171340ustar00rootroot00000000000000'use strict' // a test where we validate the siguature of the keys // otherwise exactly the same as the ssl test var server = require('./server') var request = require('../index') var fs = require('fs') var path = require('path') var tape = require('tape') var s = server.createSSLServer() var caFile = path.resolve(__dirname, 'ssl/ca/ca.crt') var ca = fs.readFileSync(caFile) var opts = { ciphers: 'AES256-SHA', key: path.resolve(__dirname, 'ssl/ca/server.key'), cert: path.resolve(__dirname, 'ssl/ca/server.crt') } var sStrict = server.createSSLServer(opts) function runAllTests (strict, s) { var strictMsg = (strict ? 'strict ' : 'relaxed ') tape(strictMsg + 'setup', function (t) { s.listen(0, function () { t.end() }) }) function runTest (name, test) { tape(strictMsg + name, function (t) { s.on('/' + name, test.resp) test.uri = s.url + '/' + name if (strict) { test.strictSSL = true test.ca = ca test.headers = { host: 'testing.request.mikealrogers.com' } } else { test.rejectUnauthorized = false } request(test, function (err, resp, body) { t.equal(err, null) if (test.expectBody) { t.deepEqual(test.expectBody, body) } t.end() }) }) } runTest('testGet', { resp: server.createGetResponse('TESTING!'), expectBody: 'TESTING!' }) runTest('testGetChunkBreak', { resp: server.createChunkResponse( [ Buffer.from([239]), Buffer.from([163]), Buffer.from([191]), Buffer.from([206]), Buffer.from([169]), Buffer.from([226]), Buffer.from([152]), Buffer.from([131]) ]), expectBody: '\uf8ff\u03a9\u2603' }) runTest('testGetJSON', { resp: server.createGetResponse('{"test":true}', 'application/json'), json: true, expectBody: {'test': true} }) runTest('testPutString', { resp: server.createPostValidator('PUTTINGDATA'), method: 'PUT', body: 'PUTTINGDATA' }) runTest('testPutBuffer', { resp: server.createPostValidator('PUTTINGDATA'), method: 'PUT', body: Buffer.from('PUTTINGDATA') }) runTest('testPutJSON', { resp: server.createPostValidator(JSON.stringify({foo: 'bar'})), method: 'PUT', json: {foo: 'bar'} }) runTest('testPutMultipart', { resp: server.createPostValidator( '--__BOUNDARY__\r\n' + 'content-type: text/html\r\n' + '\r\n' + 'Oh hi.' + '\r\n--__BOUNDARY__\r\n\r\n' + 'Oh hi.' + '\r\n--__BOUNDARY__--' ), method: 'PUT', multipart: [ {'content-type': 'text/html', 'body': 'Oh hi.'}, {'body': 'Oh hi.'} ] }) tape(strictMsg + 'cleanup', function (t) { s.close(function () { t.end() }) }) } runAllTests(false, s) if (!process.env.running_under_istanbul) { // somehow this test modifies the process state // test coverage runs all tests in a single process via tape // executing this test causes one of the tests in test-tunnel.js to throw runAllTests(true, sStrict) } request-2.88.1/tests/test-isUrl.js000066400000000000000000000043631333333633500170700ustar00rootroot00000000000000'use strict' var http = require('http') var request = require('../index') var tape = require('tape') var s = http.createServer(function (req, res) { res.statusCode = 200 res.end('ok') }) tape('setup', function (t) { s.listen(0, function () { s.port = this.address().port s.url = 'http://localhost:' + s.port t.end() }) }) tape('lowercase', function (t) { request(s.url, function (err, resp, body) { t.equal(err, null) t.equal(body, 'ok') t.end() }) }) tape('uppercase', function (t) { request(s.url.replace('http', 'HTTP'), function (err, resp, body) { t.equal(err, null) t.equal(body, 'ok') t.end() }) }) tape('mixedcase', function (t) { request(s.url.replace('http', 'HtTp'), function (err, resp, body) { t.equal(err, null) t.equal(body, 'ok') t.end() }) }) tape('hostname and port', function (t) { request({ uri: { protocol: 'http:', hostname: 'localhost', port: s.port } }, function (err, res, body) { t.equal(err, null) t.equal(body, 'ok') t.end() }) }) tape('hostname and port 1', function (t) { request({ uri: { protocol: 'http:', hostname: 'localhost', port: s.port } }, function (err, res, body) { t.equal(err, null) t.equal(body, 'ok') t.end() }) }) tape('hostname and port 2', function (t) { request({ protocol: 'http:', hostname: 'localhost', port: s.port }, { // need this empty options object, otherwise request thinks no uri was set }, function (err, res, body) { t.equal(err, null) t.equal(body, 'ok') t.end() }) }) tape('hostname and port 3', function (t) { request({ protocol: 'http:', hostname: 'localhost', port: s.port }, function (err, res, body) { t.notEqual(err, null) t.equal(err.message, 'options.uri is a required argument') t.equal(body, undefined) t.end() }) }) tape('hostname and query string', function (t) { request({ uri: { protocol: 'http:', hostname: 'localhost', port: s.port }, qs: { test: 'test' } }, function (err, res, body) { t.equal(err, null) t.equal(body, 'ok') t.end() }) }) tape('cleanup', function (t) { s.close(function () { t.end() }) }) request-2.88.1/tests/test-json-request.js000066400000000000000000000060411333333633500204240ustar00rootroot00000000000000'use strict' var server = require('./server') var request = require('../index') var tape = require('tape') var s = server.createServer() tape('setup', function (t) { s.listen(0, function () { t.end() }) }) function testJSONValue (testId, value) { tape('test ' + testId, function (t) { var testUrl = '/' + testId s.on(testUrl, server.createPostJSONValidator(value, 'application/json')) var opts = { method: 'PUT', uri: s.url + testUrl, json: true, body: value } request(opts, function (err, resp, body) { t.equal(err, null) t.equal(resp.statusCode, 200) t.deepEqual(body, value) t.end() }) }) } function testJSONValueReviver (testId, value, reviver, revivedValue) { tape('test ' + testId, function (t) { var testUrl = '/' + testId s.on(testUrl, server.createPostJSONValidator(value, 'application/json')) var opts = { method: 'PUT', uri: s.url + testUrl, json: true, jsonReviver: reviver, body: value } request(opts, function (err, resp, body) { t.equal(err, null) t.equal(resp.statusCode, 200) t.deepEqual(body, revivedValue) t.end() }) }) } function testJSONValueReplacer (testId, value, replacer, replacedValue) { tape('test ' + testId, function (t) { var testUrl = '/' + testId s.on(testUrl, server.createPostJSONValidator(replacedValue, 'application/json')) var opts = { method: 'PUT', uri: s.url + testUrl, json: true, jsonReplacer: replacer, body: value } request(opts, function (err, resp, body) { t.equal(err, null) t.equal(resp.statusCode, 200) t.deepEqual(body, replacedValue) t.end() }) }) } testJSONValue('jsonNull', null) testJSONValue('jsonTrue', true) testJSONValue('jsonFalse', false) testJSONValue('jsonNumber', -289365.2938) testJSONValue('jsonString', 'some string') testJSONValue('jsonArray', ['value1', 2, null, 8925.53289, true, false, ['array'], { object: 'property' }]) testJSONValue('jsonObject', { trueProperty: true, falseProperty: false, numberProperty: -98346.34698, stringProperty: 'string', nullProperty: null, arrayProperty: ['array'], objectProperty: { object: 'property' } }) testJSONValueReviver('jsonReviver', -48269.592, function (k, v) { return v * -1 }, 48269.592) testJSONValueReviver('jsonReviverInvalid', -48269.592, 'invalid reviver', -48269.592) testJSONValueReplacer('jsonReplacer', -48269.592, function (k, v) { return v * -1 }, 48269.592) testJSONValueReplacer('jsonReplacerInvalid', -48269.592, 'invalid replacer', -48269.592) testJSONValueReplacer('jsonReplacerObject', {foo: 'bar'}, function (k, v) { return v.toUpperCase ? v.toUpperCase() : v }, {foo: 'BAR'}) tape('missing body', function (t) { s.on('/missing-body', function (req, res) { t.equal(req.headers['content-type'], undefined) res.end() }) request({url: s.url + '/missing-body', json: true}, function () { t.end() }) }) tape('cleanup', function (t) { s.close(function () { t.end() }) }) request-2.88.1/tests/test-localAddress.js000066400000000000000000000024101333333633500203610ustar00rootroot00000000000000'use strict' var request = require('../index') var tape = require('tape') tape('bind to invalid address', function (t) { request.get({ uri: 'http://www.google.com', localAddress: '1.2.3.4' }, function (err, res) { t.notEqual(err, null) t.equal(true, /bind EADDRNOTAVAIL/.test(err.message)) t.equal(res, undefined) t.end() }) }) tape('bind to local address', function (t) { request.get({ uri: 'http://www.google.com', localAddress: '127.0.0.1' }, function (err, res) { t.notEqual(err, null) t.equal(res, undefined) t.end() }) }) tape('bind to local address on redirect', function (t) { var os = require('os') var localInterfaces = os.networkInterfaces() var localIPS = [] Object.keys(localInterfaces).forEach(function (ifname) { localInterfaces[ifname].forEach(function (iface) { if (iface.family !== 'IPv4' || iface.internal !== false) { // skip over internal (i.e. 127.0.0.1) and non-ipv4 addresses return } localIPS.push(iface.address) }) }) request.get({ uri: 'http://google.com', // redirects to 'http://google.com' localAddress: localIPS[0] }, function (err, res) { t.equal(err, null) t.equal(res.request.localAddress, localIPS[0]) t.end() }) }) request-2.88.1/tests/test-multipart-encoding.js000066400000000000000000000074041333333633500215760ustar00rootroot00000000000000'use strict' var http = require('http') var path = require('path') var request = require('../index') var fs = require('fs') var tape = require('tape') var localFile = path.join(__dirname, 'unicycle.jpg') var cases = { // based on body type '+array -stream': { options: { multipart: [{name: 'field', body: 'value'}] }, expected: {chunked: false} }, '+array +stream': { options: { multipart: [{name: 'file', body: null}] }, expected: {chunked: true} }, // encoding overrides body value '+array +encoding': { options: { headers: {'transfer-encoding': 'chunked'}, multipart: [{name: 'field', body: 'value'}] }, expected: {chunked: true} }, // based on body type '+object -stream': { options: { multipart: {data: [{name: 'field', body: 'value'}]} }, expected: {chunked: false} }, '+object +stream': { options: { multipart: {data: [{name: 'file', body: null}]} }, expected: {chunked: true} }, // encoding overrides body value '+object +encoding': { options: { headers: {'transfer-encoding': 'chunked'}, multipart: {data: [{name: 'field', body: 'value'}]} }, expected: {chunked: true} }, // based on body type '+object -chunked -stream': { options: { multipart: {chunked: false, data: [{name: 'field', body: 'value'}]} }, expected: {chunked: false} }, '+object -chunked +stream': { options: { multipart: {chunked: false, data: [{name: 'file', body: null}]} }, expected: {chunked: true} }, // chunked overrides body value '+object +chunked -stream': { options: { multipart: {chunked: true, data: [{name: 'field', body: 'value'}]} }, expected: {chunked: true} }, // encoding overrides chunked '+object +encoding -chunked': { options: { headers: {'transfer-encoding': 'chunked'}, multipart: {chunked: false, data: [{name: 'field', body: 'value'}]} }, expected: {chunked: true} } } function runTest (t, test) { var server = http.createServer(function (req, res) { t.ok(req.headers['content-type'].match(/^multipart\/related; boundary=[^\s;]+$/)) if (test.expected.chunked) { t.ok(req.headers['transfer-encoding'] === 'chunked') t.notOk(req.headers['content-length']) } else { t.ok(req.headers['content-length']) t.notOk(req.headers['transfer-encoding']) } // temp workaround var data = '' req.setEncoding('utf8') req.on('data', function (d) { data += d }) req.on('end', function () { // check for the fields traces if (test.expected.chunked && data.indexOf('name: file') !== -1) { // file t.ok(data.indexOf('name: file') !== -1) // check for unicycle.jpg traces t.ok(data.indexOf('2005:06:21 01:44:12') !== -1) } else { // field t.ok(data.indexOf('name: field') !== -1) var parts = test.options.multipart.data || test.options.multipart t.ok(data.indexOf(parts[0].body) !== -1) } res.writeHead(200) res.end() }) }) server.listen(0, function () { var url = 'http://localhost:' + this.address().port // @NOTE: multipartData properties must be set here // so that file read stream does not leak in node v0.8 var parts = test.options.multipart.data || test.options.multipart if (parts[0].name === 'file') { parts[0].body = fs.createReadStream(localFile) } request.post(url, test.options, function (err, res, body) { t.equal(err, null) t.equal(res.statusCode, 200) server.close(function () { t.end() }) }) }) } Object.keys(cases).forEach(function (name) { tape('multipart-encoding ' + name, function (t) { runTest(t, cases[name]) }) }) request-2.88.1/tests/test-multipart.js000066400000000000000000000070341333333633500200110ustar00rootroot00000000000000'use strict' var http = require('http') var path = require('path') var request = require('../index') var fs = require('fs') var tape = require('tape') function runTest (t, a) { var remoteFile = path.join(__dirname, 'googledoodle.jpg') var localFile = path.join(__dirname, 'unicycle.jpg') var multipartData = [] var server = http.createServer(function (req, res) { if (req.url === '/file') { res.writeHead(200, {'content-type': 'image/jpg'}) res.end(fs.readFileSync(remoteFile), 'binary') return } if (a.header) { if (a.header.indexOf('mixed') !== -1) { t.ok(req.headers['content-type'].match(/^multipart\/mixed; boundary=[^\s;]+$/)) } else { t.ok(req.headers['content-type'] .match(/^multipart\/related; boundary=XXX; type=text\/xml; start=""$/)) } } else { t.ok(req.headers['content-type'].match(/^multipart\/related; boundary=[^\s;]+$/)) } // temp workaround var data = '' req.setEncoding('utf8') req.on('data', function (d) { data += d }) req.on('end', function () { // check for the fields traces // my_field t.ok(data.indexOf('name: my_field') !== -1) t.ok(data.indexOf(multipartData[0].body) !== -1) // my_number t.ok(data.indexOf('name: my_number') !== -1) t.ok(data.indexOf(multipartData[1].body) !== -1) // my_buffer t.ok(data.indexOf('name: my_buffer') !== -1) t.ok(data.indexOf(multipartData[2].body) !== -1) // my_file t.ok(data.indexOf('name: my_file') !== -1) // check for unicycle.jpg traces t.ok(data.indexOf('2005:06:21 01:44:12') !== -1) // remote_file t.ok(data.indexOf('name: remote_file') !== -1) // check for http://localhost:nnnn/file traces t.ok(data.indexOf('Photoshop ICC') !== -1) if (a.header && a.header.indexOf('boundary=XXX') !== -1) { t.ok(data.indexOf('--XXX') !== -1) } res.writeHead(200) res.end(a.json ? JSON.stringify({status: 'done'}) : 'done') }) }) server.listen(0, function () { var url = 'http://localhost:' + this.address().port // @NOTE: multipartData properties must be set here so that my_file read stream does not leak in node v0.8 multipartData = [ {name: 'my_field', body: 'my_value'}, {name: 'my_number', body: 1000}, {name: 'my_buffer', body: Buffer.from([1, 2, 3])}, {name: 'my_file', body: fs.createReadStream(localFile)}, {name: 'remote_file', body: request(url + '/file')} ] var reqOptions = { url: url + '/upload', multipart: multipartData } if (a.header) { reqOptions.headers = { 'content-type': a.header } } if (a.json) { reqOptions.json = true } request[a.method](reqOptions, function (err, res, body) { t.equal(err, null) t.equal(res.statusCode, 200) t.deepEqual(body, a.json ? {status: 'done'} : 'done') server.close(function () { t.end() }) }) }) } var testHeaders = [ null, 'multipart/mixed', 'multipart/related; boundary=XXX; type=text/xml; start=""' ] var methods = ['post', 'get'] methods.forEach(function (method) { testHeaders.forEach(function (header) { [true, false].forEach(function (json) { var name = [ 'multipart-related', method.toUpperCase(), (header || 'default'), (json ? '+' : '-') + 'json' ].join(' ') tape(name, function (t) { runTest(t, {method: method, header: header, json: json}) }) }) }) }) request-2.88.1/tests/test-node-debug.js000066400000000000000000000050201333333633500177720ustar00rootroot00000000000000'use strict' var request = require('../index') var http = require('http') var tape = require('tape') var s = http.createServer(function (req, res) { res.statusCode = 200 res.end('') }) var stderr = [] var prevStderrLen = 0 tape('setup', function (t) { process.stderr._oldWrite = process.stderr.write process.stderr.write = function (string, encoding, fd) { stderr.push(string) } s.listen(0, function () { s.url = 'http://localhost:' + this.address().port t.end() }) }) tape('a simple request should not fail with debugging enabled', function (t) { request.debug = true t.equal(request.Request.debug, true, 'request.debug sets request.Request.debug') t.equal(request.debug, true, 'request.debug gets request.Request.debug') stderr = [] request(s.url, function (err, res, body) { t.ifError(err, 'the request did not fail') t.ok(res, 'the request did not fail') t.ok(stderr.length, 'stderr has some messages') var url = s.url.replace(/\//g, '\\/') var patterns = [ /^REQUEST { uri: /, new RegExp('^REQUEST make request ' + url + '/\n$'), /^REQUEST onRequestResponse /, /^REQUEST finish init /, /^REQUEST response end /, /^REQUEST end event /, /^REQUEST emitting complete / ] patterns.forEach(function (pattern) { var found = false stderr.forEach(function (msg) { if (pattern.test(msg)) { found = true } }) t.ok(found, 'a log message matches ' + pattern) }) prevStderrLen = stderr.length t.end() }) }) tape('there should be no further lookups on process.env', function (t) { process.env.NODE_DEBUG = '' stderr = [] request(s.url, function (err, res, body) { t.ifError(err, 'the request did not fail') t.ok(res, 'the request did not fail') t.equal(stderr.length, prevStderrLen, 'env.NODE_DEBUG is not retested') t.end() }) }) tape('it should be possible to disable debugging at runtime', function (t) { request.debug = false t.equal(request.Request.debug, false, 'request.debug sets request.Request.debug') t.equal(request.debug, false, 'request.debug gets request.Request.debug') stderr = [] request(s.url, function (err, res, body) { t.ifError(err, 'the request did not fail') t.ok(res, 'the request did not fail') t.equal(stderr.length, 0, 'debugging can be disabled') t.end() }) }) tape('cleanup', function (t) { process.stderr.write = process.stderr._oldWrite delete process.stderr._oldWrite s.close(function () { t.end() }) }) request-2.88.1/tests/test-oauth.js000066400000000000000000000572241333333633500171160ustar00rootroot00000000000000'use strict' var oauth = require('oauth-sign') var qs = require('querystring') var fs = require('fs') var path = require('path') var request = require('../index') var tape = require('tape') var http = require('http') function getSignature (r) { var sign r.headers.Authorization.slice('OAuth '.length).replace(/, /g, ',').split(',').forEach(function (v) { if (v.slice(0, 'oauth_signature="'.length) === 'oauth_signature="') { sign = v.slice('oauth_signature="'.length, -1) } }) return decodeURIComponent(sign) } // Tests from Twitter documentation https://dev.twitter.com/docs/auth/oauth var hmacsign = oauth.hmacsign var hmacsign256 = oauth.hmacsign256 var rsasign = oauth.rsasign var rsaPrivatePEM = fs.readFileSync(path.join(__dirname, 'ssl', 'test.key')) var reqsign var reqsign256 var reqsignRSA var accsign var accsign256 var accsignRSA var upsign var upsign256 var upsignRSA tape('reqsign', function (t) { reqsign = hmacsign('POST', 'https://api.twitter.com/oauth/request_token', { oauth_callback: 'http://localhost:3005/the_dance/process_callback?service_provider_id=11', oauth_consumer_key: 'GDdmIQH6jhtmLUypg82g', oauth_nonce: 'QP70eNmVz8jvdPevU3oJD2AfF7R7odC2XJcn4XlZJqk', oauth_signature_method: 'HMAC-SHA1', oauth_timestamp: '1272323042', oauth_version: '1.0' }, 'MCD8BKwGdgPHvAuvgvz4EQpqDAtx89grbuNMRd7Eh98') t.equal(reqsign, '8wUi7m5HFQy76nowoCThusfgB+Q=') t.end() }) tape('reqsign256', function (t) { reqsign256 = hmacsign256('POST', 'https://api.twitter.com/oauth/request_token', { oauth_callback: 'http://localhost:3005/the_dance/process_callback?service_provider_id=11', oauth_consumer_key: 'GDdmIQH6jhtmLUypg82g', oauth_nonce: 'QP70eNmVz8jvdPevU3oJD2AfF7R7odC2XJcn4XlZJqk', oauth_signature_method: 'HMAC-SHA256', oauth_timestamp: '1272323042', oauth_version: '1.0' }, 'MCD8BKwGdgPHvAuvgvz4EQpqDAtx89grbuNMRd7Eh98') t.equal(reqsign256, 'N0KBpiPbuPIMx2B77eIg7tNfGNF81iq3bcO9RO6lH+k=') t.end() }) tape('reqsignRSA', function (t) { reqsignRSA = rsasign('POST', 'https://api.twitter.com/oauth/request_token', { oauth_callback: 'http://localhost:3005/the_dance/process_callback?service_provider_id=11', oauth_consumer_key: 'GDdmIQH6jhtmLUypg82g', oauth_nonce: 'QP70eNmVz8jvdPevU3oJD2AfF7R7odC2XJcn4XlZJqk', oauth_signature_method: 'RSA-SHA1', oauth_timestamp: '1272323042', oauth_version: '1.0' }, rsaPrivatePEM, 'this parameter is not used for RSA signing') t.equal(reqsignRSA, 'MXdzEnIrQco3ACPoVWxCwv5pxYrm5MFRXbsP3LfRV+zfcRr+WMp/dOPS/3r+Wcb+17Z2IK3uJ8dMHfzb5LiDNCTUIj7SWBrbxOpy3Y6SA6z3vcrtjSekkTHLek1j+mzxOi3r3fkbYaNwjHx3PyoFSazbEstnkQQotbITeFt5FBE=') t.end() }) tape('accsign', function (t) { accsign = hmacsign('POST', 'https://api.twitter.com/oauth/access_token', { oauth_consumer_key: 'GDdmIQH6jhtmLUypg82g', oauth_nonce: '9zWH6qe0qG7Lc1telCn7FhUbLyVdjEaL3MO5uHxn8', oauth_signature_method: 'HMAC-SHA1', oauth_token: '8ldIZyxQeVrFZXFOZH5tAwj6vzJYuLQpl0WUEYtWc', oauth_timestamp: '1272323047', oauth_verifier: 'pDNg57prOHapMbhv25RNf75lVRd6JDsni1AJJIDYoTY', oauth_version: '1.0' }, 'MCD8BKwGdgPHvAuvgvz4EQpqDAtx89grbuNMRd7Eh98', 'x6qpRnlEmW9JbQn4PQVVeVG8ZLPEx6A0TOebgwcuA') t.equal(accsign, 'PUw/dHA4fnlJYM6RhXk5IU/0fCc=') t.end() }) tape('accsign256', function (t) { accsign256 = hmacsign256('POST', 'https://api.twitter.com/oauth/access_token', { oauth_consumer_key: 'GDdmIQH6jhtmLUypg82g', oauth_nonce: '9zWH6qe0qG7Lc1telCn7FhUbLyVdjEaL3MO5uHxn8', oauth_signature_method: 'HMAC-SHA256', oauth_token: '8ldIZyxQeVrFZXFOZH5tAwj6vzJYuLQpl0WUEYtWc', oauth_timestamp: '1272323047', oauth_verifier: 'pDNg57prOHapMbhv25RNf75lVRd6JDsni1AJJIDYoTY', oauth_version: '1.0' }, 'MCD8BKwGdgPHvAuvgvz4EQpqDAtx89grbuNMRd7Eh98', 'x6qpRnlEmW9JbQn4PQVVeVG8ZLPEx6A0TOebgwcuA') t.equal(accsign256, 'y7S9eUhA0tC9/YfRzCPqkg3/bUdYRDpZ93Xi51AvhjQ=') t.end() }) tape('accsignRSA', function (t) { accsignRSA = rsasign('POST', 'https://api.twitter.com/oauth/access_token', { oauth_consumer_key: 'GDdmIQH6jhtmLUypg82g', oauth_nonce: '9zWH6qe0qG7Lc1telCn7FhUbLyVdjEaL3MO5uHxn8', oauth_signature_method: 'RSA-SHA1', oauth_token: '8ldIZyxQeVrFZXFOZH5tAwj6vzJYuLQpl0WUEYtWc', oauth_timestamp: '1272323047', oauth_verifier: 'pDNg57prOHapMbhv25RNf75lVRd6JDsni1AJJIDYoTY', oauth_version: '1.0' }, rsaPrivatePEM) t.equal(accsignRSA, 'gZrMPexdgGMVUl9H6RxK0MbR6Db8tzf2kIIj52kOrDFcMgh4BunMBgUZAO1msUwz6oqZIvkVqyfyDAOP2wIrpYem0mBg3vqwPIroSE1AlUWo+TtQxOTuqrU+3kDcXpdvJe7CAX5hUx9Np/iGRqaCcgByqb9DaCcQ9ViQ+0wJiXI=') t.end() }) tape('upsign', function (t) { upsign = hmacsign('POST', 'http://api.twitter.com/1/statuses/update.json', { oauth_consumer_key: 'GDdmIQH6jhtmLUypg82g', oauth_nonce: 'oElnnMTQIZvqvlfXM56aBLAf5noGD0AQR3Fmi7Q6Y', oauth_signature_method: 'HMAC-SHA1', oauth_token: '819797-Jxq8aYUDRmykzVKrgoLhXSq67TEa5ruc4GJC2rWimw', oauth_timestamp: '1272325550', oauth_version: '1.0', status: 'setting up my twitter 私のさえずりを設定する' }, 'MCD8BKwGdgPHvAuvgvz4EQpqDAtx89grbuNMRd7Eh98', 'J6zix3FfA9LofH0awS24M3HcBYXO5nI1iYe8EfBA') t.equal(upsign, 'yOahq5m0YjDDjfjxHaXEsW9D+X0=') t.end() }) tape('upsign256', function (t) { upsign256 = hmacsign256('POST', 'http://api.twitter.com/1/statuses/update.json', { oauth_consumer_key: 'GDdmIQH6jhtmLUypg82g', oauth_nonce: 'oElnnMTQIZvqvlfXM56aBLAf5noGD0AQR3Fmi7Q6Y', oauth_signature_method: 'HMAC-SHA256', oauth_token: '819797-Jxq8aYUDRmykzVKrgoLhXSq67TEa5ruc4GJC2rWimw', oauth_timestamp: '1272325550', oauth_version: '1.0', status: 'setting up my twitter 私のさえずりを設定する' }, 'MCD8BKwGdgPHvAuvgvz4EQpqDAtx89grbuNMRd7Eh98', 'J6zix3FfA9LofH0awS24M3HcBYXO5nI1iYe8EfBA') t.equal(upsign256, 'xYhKjozxc3NYef7C26WU+gORdhEURdZRxSDzRttEKH0=') t.end() }) tape('upsignRSA', function (t) { upsignRSA = rsasign('POST', 'http://api.twitter.com/1/statuses/update.json', { oauth_consumer_key: 'GDdmIQH6jhtmLUypg82g', oauth_nonce: 'oElnnMTQIZvqvlfXM56aBLAf5noGD0AQR3Fmi7Q6Y', oauth_signature_method: 'RSA-SHA1', oauth_token: '819797-Jxq8aYUDRmykzVKrgoLhXSq67TEa5ruc4GJC2rWimw', oauth_timestamp: '1272325550', oauth_version: '1.0', status: 'setting up my twitter 私のさえずりを設定する' }, rsaPrivatePEM) t.equal(upsignRSA, 'fF4G9BNzDxPu/htctzh9CWzGhtXo9DYYl+ZyRO1/QNOhOZPqnWVUOT+CGUKxmAeJSzLKMAH8y/MFSHI0lzihqwgfZr7nUhTx6kH7lUChcVasr+TZ4qPqvGGEhfJ8Av8D5dF5fytfCSzct62uONU0iHYVqainP+zefk1K7Ptb6hI=') t.end() }) tape('rsign', function (t) { var rsign = request.post( { url: 'https://api.twitter.com/oauth/request_token', oauth: { callback: 'http://localhost:3005/the_dance/process_callback?service_provider_id=11', consumer_key: 'GDdmIQH6jhtmLUypg82g', nonce: 'QP70eNmVz8jvdPevU3oJD2AfF7R7odC2XJcn4XlZJqk', timestamp: '1272323042', version: '1.0', consumer_secret: 'MCD8BKwGdgPHvAuvgvz4EQpqDAtx89grbuNMRd7Eh98' } }) process.nextTick(function () { t.equal(reqsign, getSignature(rsign)) rsign.abort() t.end() }) }) tape('rsign_rsa', function (t) { var rsignRSA = request.post( { url: 'https://api.twitter.com/oauth/request_token', oauth: { callback: 'http://localhost:3005/the_dance/process_callback?service_provider_id=11', consumer_key: 'GDdmIQH6jhtmLUypg82g', nonce: 'QP70eNmVz8jvdPevU3oJD2AfF7R7odC2XJcn4XlZJqk', timestamp: '1272323042', version: '1.0', private_key: rsaPrivatePEM, signature_method: 'RSA-SHA1' } }) process.nextTick(function () { t.equal(reqsignRSA, getSignature(rsignRSA)) rsignRSA.abort() t.end() }) }) tape('raccsign', function (t) { var raccsign = request.post( { url: 'https://api.twitter.com/oauth/access_token', oauth: { consumer_key: 'GDdmIQH6jhtmLUypg82g', nonce: '9zWH6qe0qG7Lc1telCn7FhUbLyVdjEaL3MO5uHxn8', signature_method: 'HMAC-SHA1', token: '8ldIZyxQeVrFZXFOZH5tAwj6vzJYuLQpl0WUEYtWc', timestamp: '1272323047', verifier: 'pDNg57prOHapMbhv25RNf75lVRd6JDsni1AJJIDYoTY', version: '1.0', consumer_secret: 'MCD8BKwGdgPHvAuvgvz4EQpqDAtx89grbuNMRd7Eh98', token_secret: 'x6qpRnlEmW9JbQn4PQVVeVG8ZLPEx6A0TOebgwcuA' } }) process.nextTick(function () { t.equal(accsign, getSignature(raccsign)) raccsign.abort() t.end() }) }) tape('raccsignRSA', function (t) { var raccsignRSA = request.post( { url: 'https://api.twitter.com/oauth/access_token', oauth: { consumer_key: 'GDdmIQH6jhtmLUypg82g', nonce: '9zWH6qe0qG7Lc1telCn7FhUbLyVdjEaL3MO5uHxn8', signature_method: 'RSA-SHA1', token: '8ldIZyxQeVrFZXFOZH5tAwj6vzJYuLQpl0WUEYtWc', timestamp: '1272323047', verifier: 'pDNg57prOHapMbhv25RNf75lVRd6JDsni1AJJIDYoTY', version: '1.0', private_key: rsaPrivatePEM, token_secret: 'x6qpRnlEmW9JbQn4PQVVeVG8ZLPEx6A0TOebgwcuA' } }) process.nextTick(function () { t.equal(accsignRSA, getSignature(raccsignRSA)) raccsignRSA.abort() t.end() }) }) tape('rupsign', function (t) { var rupsign = request.post( { url: 'http://api.twitter.com/1/statuses/update.json', oauth: { consumer_key: 'GDdmIQH6jhtmLUypg82g', nonce: 'oElnnMTQIZvqvlfXM56aBLAf5noGD0AQR3Fmi7Q6Y', signature_method: 'HMAC-SHA1', token: '819797-Jxq8aYUDRmykzVKrgoLhXSq67TEa5ruc4GJC2rWimw', timestamp: '1272325550', version: '1.0', consumer_secret: 'MCD8BKwGdgPHvAuvgvz4EQpqDAtx89grbuNMRd7Eh98', token_secret: 'J6zix3FfA9LofH0awS24M3HcBYXO5nI1iYe8EfBA' }, form: {status: 'setting up my twitter 私のさえずりを設定する'} }) process.nextTick(function () { t.equal(upsign, getSignature(rupsign)) rupsign.abort() t.end() }) }) tape('rupsignRSA', function (t) { var rupsignRSA = request.post( { url: 'http://api.twitter.com/1/statuses/update.json', oauth: { consumer_key: 'GDdmIQH6jhtmLUypg82g', nonce: 'oElnnMTQIZvqvlfXM56aBLAf5noGD0AQR3Fmi7Q6Y', signature_method: 'RSA-SHA1', token: '819797-Jxq8aYUDRmykzVKrgoLhXSq67TEa5ruc4GJC2rWimw', timestamp: '1272325550', version: '1.0', private_key: rsaPrivatePEM, token_secret: 'J6zix3FfA9LofH0awS24M3HcBYXO5nI1iYe8EfBA' }, form: {status: 'setting up my twitter 私のさえずりを設定する'} }) process.nextTick(function () { t.equal(upsignRSA, getSignature(rupsignRSA)) rupsignRSA.abort() t.end() }) }) tape('rfc5849 example', function (t) { var rfc5849 = request.post( { url: 'http://example.com/request?b5=%3D%253D&a3=a&c%40=&a2=r%20b', oauth: { consumer_key: '9djdj82h48djs9d2', nonce: '7d8f3e4a', signature_method: 'HMAC-SHA1', token: 'kkk9d7dh3k39sjv7', timestamp: '137131201', consumer_secret: 'j49sk3j29djd', token_secret: 'dh893hdasih9', realm: 'Example' }, form: { c2: '', a3: '2 q' } }) process.nextTick(function () { // different signature in rfc5849 because request sets oauth_version by default t.equal('OB33pYjWAnf+xtOHN4Gmbdil168=', getSignature(rfc5849)) rfc5849.abort() t.end() }) }) tape('rfc5849 RSA example', function (t) { var rfc5849RSA = request.post( { url: 'http://example.com/request?b5=%3D%253D&a3=a&c%40=&a2=r%20b', oauth: { consumer_key: '9djdj82h48djs9d2', nonce: '7d8f3e4a', signature_method: 'RSA-SHA1', token: 'kkk9d7dh3k39sjv7', timestamp: '137131201', private_key: rsaPrivatePEM, token_secret: 'dh893hdasih9', realm: 'Example' }, form: { c2: '', a3: '2 q' } }) process.nextTick(function () { // different signature in rfc5849 because request sets oauth_version by default t.equal('ThNYfZhYogcAU6rWgI3ZFlPEhoIXHMZcuMzl+jykJZW/ab+AxyefS03dyd64CclIZ0u8JEW64TQ5SHthoQS8aM8qir4t+t88lRF3LDkD2KtS1krgCZTUQxkDL5BO5pxsqAQ2Zfdcrzaxb6VMGD1Hf+Pno+fsHQo/UUKjq4V3RMo=', getSignature(rfc5849RSA)) rfc5849RSA.abort() t.end() }) }) tape('plaintext signature method', function (t) { var plaintext = request.post( { url: 'https://dummy.com', oauth: { consumer_secret: 'consumer_secret', token_secret: 'token_secret', signature_method: 'PLAINTEXT' } }) process.nextTick(function () { t.equal('consumer_secret&token_secret', getSignature(plaintext)) plaintext.abort() t.end() }) }) tape('invalid transport_method', function (t) { t.throws( function () { request.post( { url: 'http://example.com/', oauth: { transport_method: 'headerquery' } }) }, /transport_method invalid/) t.end() }) tape("invalid method while using transport_method 'body'", function (t) { t.throws( function () { request.get( { url: 'http://example.com/', headers: { 'content-type': 'application/x-www-form-urlencoded; charset=UTF-8' }, oauth: { transport_method: 'body' } }) }, /requires POST/) t.end() }) tape("invalid content-type while using transport_method 'body'", function (t) { t.throws( function () { request.post( { url: 'http://example.com/', headers: { 'content-type': 'application/json; charset=UTF-8' }, oauth: { transport_method: 'body' } }) }, /requires POST/) t.end() }) tape('query transport_method', function (t) { var r = request.post( { url: 'https://api.twitter.com/oauth/access_token', oauth: { consumer_key: 'GDdmIQH6jhtmLUypg82g', nonce: '9zWH6qe0qG7Lc1telCn7FhUbLyVdjEaL3MO5uHxn8', signature_method: 'HMAC-SHA1', token: '8ldIZyxQeVrFZXFOZH5tAwj6vzJYuLQpl0WUEYtWc', timestamp: '1272323047', verifier: 'pDNg57prOHapMbhv25RNf75lVRd6JDsni1AJJIDYoTY', version: '1.0', consumer_secret: 'MCD8BKwGdgPHvAuvgvz4EQpqDAtx89grbuNMRd7Eh98', token_secret: 'x6qpRnlEmW9JbQn4PQVVeVG8ZLPEx6A0TOebgwcuA', transport_method: 'query' } }) process.nextTick(function () { t.notOk(r.headers.Authorization, "oauth Authorization header should not be present with transport_method 'query'") t.equal(r.uri.path, r.path, 'r.uri.path should equal r.path') t.ok(r.path.match(/^\/oauth\/access_token\?/), 'path should contain path + query') t.deepEqual(qs.parse(r.uri.query), { oauth_consumer_key: 'GDdmIQH6jhtmLUypg82g', oauth_nonce: '9zWH6qe0qG7Lc1telCn7FhUbLyVdjEaL3MO5uHxn8', oauth_signature_method: 'HMAC-SHA1', oauth_timestamp: '1272323047', oauth_token: '8ldIZyxQeVrFZXFOZH5tAwj6vzJYuLQpl0WUEYtWc', oauth_verifier: 'pDNg57prOHapMbhv25RNf75lVRd6JDsni1AJJIDYoTY', oauth_version: '1.0', oauth_signature: accsign }) r.abort() t.end() }) }) tape('query transport_method + form option + url params', function (t) { var r = request.post( { url: 'http://example.com/request?b5=%3D%253D&a3=a&c%40=&a2=r%20b', oauth: { consumer_key: '9djdj82h48djs9d2', nonce: '7d8f3e4a', signature_method: 'HMAC-SHA1', token: 'kkk9d7dh3k39sjv7', timestamp: '137131201', consumer_secret: 'j49sk3j29djd', token_secret: 'dh893hdasih9', realm: 'Example', transport_method: 'query' }, form: { c2: '', a3: '2 q' } }) process.nextTick(function () { t.notOk(r.headers.Authorization, "oauth Authorization header should not be present with transport_method 'query'") t.equal(r.uri.path, r.path, 'r.uri.path should equal r.path') t.ok(r.path.match(/^\/request\?/), 'path should contain path + query') t.deepEqual(qs.parse(r.uri.query), { b5: '=%3D', a3: 'a', 'c@': '', a2: 'r b', realm: 'Example', oauth_consumer_key: '9djdj82h48djs9d2', oauth_nonce: '7d8f3e4a', oauth_signature_method: 'HMAC-SHA1', oauth_timestamp: '137131201', oauth_token: 'kkk9d7dh3k39sjv7', oauth_version: '1.0', oauth_signature: 'OB33pYjWAnf+xtOHN4Gmbdil168=' }) r.abort() t.end() }) }) tape('query transport_method + qs option + url params', function (t) { var r = request.post( { url: 'http://example.com/request?a2=r%20b', oauth: { consumer_key: '9djdj82h48djs9d2', nonce: '7d8f3e4a', signature_method: 'HMAC-SHA1', token: 'kkk9d7dh3k39sjv7', timestamp: '137131201', consumer_secret: 'j49sk3j29djd', token_secret: 'dh893hdasih9', realm: 'Example', transport_method: 'query' }, qs: { b5: '=%3D', a3: ['a', '2 q'], 'c@': '', c2: '' } }) process.nextTick(function () { t.notOk(r.headers.Authorization, "oauth Authorization header should not be present with transport_method 'query'") t.equal(r.uri.path, r.path, 'r.uri.path should equal r.path') t.ok(r.path.match(/^\/request\?/), 'path should contain path + query') t.deepEqual(qs.parse(r.uri.query), { a2: 'r b', b5: '=%3D', 'a3[0]': 'a', 'a3[1]': '2 q', 'c@': '', c2: '', realm: 'Example', oauth_consumer_key: '9djdj82h48djs9d2', oauth_nonce: '7d8f3e4a', oauth_signature_method: 'HMAC-SHA1', oauth_timestamp: '137131201', oauth_token: 'kkk9d7dh3k39sjv7', oauth_version: '1.0', oauth_signature: 'OB33pYjWAnf+xtOHN4Gmbdil168=' }) r.abort() t.end() }) }) tape('body transport_method', function (t) { var r = request.post( { url: 'https://api.twitter.com/oauth/access_token', headers: { 'content-type': 'application/x-www-form-urlencoded; charset=UTF-8' }, oauth: { consumer_key: 'GDdmIQH6jhtmLUypg82g', nonce: '9zWH6qe0qG7Lc1telCn7FhUbLyVdjEaL3MO5uHxn8', signature_method: 'HMAC-SHA1', token: '8ldIZyxQeVrFZXFOZH5tAwj6vzJYuLQpl0WUEYtWc', timestamp: '1272323047', verifier: 'pDNg57prOHapMbhv25RNf75lVRd6JDsni1AJJIDYoTY', version: '1.0', consumer_secret: 'MCD8BKwGdgPHvAuvgvz4EQpqDAtx89grbuNMRd7Eh98', token_secret: 'x6qpRnlEmW9JbQn4PQVVeVG8ZLPEx6A0TOebgwcuA', transport_method: 'body' } }) process.nextTick(function () { t.notOk(r.headers.Authorization, "oauth Authorization header should not be present with transport_method 'body'") t.deepEqual(qs.parse(r.body), { oauth_consumer_key: 'GDdmIQH6jhtmLUypg82g', oauth_nonce: '9zWH6qe0qG7Lc1telCn7FhUbLyVdjEaL3MO5uHxn8', oauth_signature_method: 'HMAC-SHA1', oauth_timestamp: '1272323047', oauth_token: '8ldIZyxQeVrFZXFOZH5tAwj6vzJYuLQpl0WUEYtWc', oauth_verifier: 'pDNg57prOHapMbhv25RNf75lVRd6JDsni1AJJIDYoTY', oauth_version: '1.0', oauth_signature: accsign }) r.abort() t.end() }) }) tape('body transport_method + form option + url params', function (t) { var r = request.post( { url: 'http://example.com/request?b5=%3D%253D&a3=a&c%40=&a2=r%20b', oauth: { consumer_key: '9djdj82h48djs9d2', nonce: '7d8f3e4a', signature_method: 'HMAC-SHA1', token: 'kkk9d7dh3k39sjv7', timestamp: '137131201', consumer_secret: 'j49sk3j29djd', token_secret: 'dh893hdasih9', realm: 'Example', transport_method: 'body' }, form: { c2: '', a3: '2 q' } }) process.nextTick(function () { t.notOk(r.headers.Authorization, "oauth Authorization header should not be present with transport_method 'body'") t.deepEqual(qs.parse(r.body), { c2: '', a3: '2 q', realm: 'Example', oauth_consumer_key: '9djdj82h48djs9d2', oauth_nonce: '7d8f3e4a', oauth_signature_method: 'HMAC-SHA1', oauth_timestamp: '137131201', oauth_token: 'kkk9d7dh3k39sjv7', oauth_version: '1.0', oauth_signature: 'OB33pYjWAnf+xtOHN4Gmbdil168=' }) r.abort() t.end() }) }) tape('body_hash manually set', function (t) { var r = request.post( { url: 'http://example.com', oauth: { consumer_secret: 'consumer_secret', body_hash: 'ManuallySetHash' }, json: {foo: 'bar'} }) process.nextTick(function () { var hash = r.headers.Authorization.replace(/.*oauth_body_hash="([^"]+)".*/, '$1') t.equal('ManuallySetHash', hash) r.abort() t.end() }) }) tape('body_hash automatically built for string', function (t) { var r = request.post( { url: 'http://example.com', oauth: { consumer_secret: 'consumer_secret', body_hash: true }, body: 'Hello World!' }) process.nextTick(function () { var hash = r.headers.Authorization.replace(/.*oauth_body_hash="([^"]+)".*/, '$1') // from https://tools.ietf.org/id/draft-eaton-oauth-bodyhash-00.html#anchor15 t.equal('Lve95gjOVATpfV8EL5X4nxwjKHE%3D', hash) r.abort() t.end() }) }) tape('body_hash automatically built for JSON', function (t) { var r = request.post( { url: 'http://example.com', oauth: { consumer_secret: 'consumer_secret', body_hash: true }, json: {foo: 'bar'} }) process.nextTick(function () { var hash = r.headers.Authorization.replace(/.*oauth_body_hash="([^"]+)".*/, '$1') t.equal('pedE0BZFQNM7HX6mFsKPL6l%2BdUo%3D', hash) r.abort() t.end() }) }) tape('body_hash PLAINTEXT signature_method', function (t) { t.throws(function () { request.post( { url: 'http://example.com', oauth: { consumer_secret: 'consumer_secret', body_hash: true, signature_method: 'PLAINTEXT' }, json: {foo: 'bar'} }) }, /oauth: PLAINTEXT signature_method not supported with body_hash signing/) t.end() }) tape('refresh oauth_nonce on redirect', function (t) { var oauthNonce1 var oauthNonce2 var url var s = http.createServer(function (req, res) { if (req.url === '/redirect') { oauthNonce1 = req.headers.authorization.replace(/.*oauth_nonce="([^"]+)".*/, '$1') res.writeHead(302, {location: url + '/response'}) res.end() } else if (req.url === '/response') { oauthNonce2 = req.headers.authorization.replace(/.*oauth_nonce="([^"]+)".*/, '$1') res.writeHead(200, {'content-type': 'text/plain'}) res.end() } }) s.listen(0, function () { url = 'http://localhost:' + this.address().port request.get( { url: url + '/redirect', oauth: { consumer_key: 'consumer_key', consumer_secret: 'consumer_secret', token: 'token', token_secret: 'token_secret' } }, function (err, res, body) { t.equal(err, null) t.notEqual(oauthNonce1, oauthNonce2) s.close(function () { t.end() }) }) }) }) tape('no credentials on external redirect', function (t) { var s2 = http.createServer(function (req, res) { res.writeHead(200, {'content-type': 'text/plain'}) res.end() }) var s1 = http.createServer(function (req, res) { res.writeHead(302, {location: s2.url}) res.end() }) s1.listen(0, function () { s1.url = 'http://localhost:' + this.address().port s2.listen(0, function () { s2.url = 'http://127.0.0.1:' + this.address().port request.get( { url: s1.url, oauth: { consumer_key: 'consumer_key', consumer_secret: 'consumer_secret', token: 'token', token_secret: 'token_secret' } }, function (err, res, body) { t.equal(err, null) t.equal(res.request.headers.Authorization, undefined) s1.close(function () { s2.close(function () { t.end() }) }) }) }) }) }) request-2.88.1/tests/test-onelineproxy.js000066400000000000000000000025101333333633500205150ustar00rootroot00000000000000'use strict' var http = require('http') var assert = require('assert') var request = require('../index') var tape = require('tape') var server = http.createServer(function (req, resp) { resp.statusCode = 200 if (req.url === '/get') { assert.equal(req.method, 'GET') resp.write('content') resp.end() return } if (req.url === '/put') { var x = '' assert.equal(req.method, 'PUT') req.on('data', function (chunk) { x += chunk }) req.on('end', function () { assert.equal(x, 'content') resp.write('success') resp.end() }) return } if (req.url === '/proxy') { assert.equal(req.method, 'PUT') req.pipe(request(server.url + '/put')).pipe(resp) return } if (req.url === '/test') { request(server.url + '/get').pipe(request.put(server.url + '/proxy')).pipe(resp) return } throw new Error('Unknown url', req.url) }) tape('setup', function (t) { server.listen(0, function () { server.url = 'http://localhost:' + this.address().port t.end() }) }) tape('chained one-line proxying', function (t) { request(server.url + '/test', function (err, res, body) { t.equal(err, null) t.equal(res.statusCode, 200) t.equal(body, 'success') t.end() }) }) tape('cleanup', function (t) { server.close(function () { t.end() }) }) request-2.88.1/tests/test-option-reuse.js000066400000000000000000000020251333333633500204140ustar00rootroot00000000000000'use strict' var request = require('../index') var http = require('http') var tape = require('tape') var methodsSeen = { head: 0, get: 0 } var s = http.createServer(function (req, res) { res.statusCode = 200 res.end('ok') methodsSeen[req.method.toLowerCase()]++ }) tape('setup', function (t) { s.listen(0, function () { s.url = 'http://localhost:' + this.address().port t.end() }) }) tape('options object is not mutated', function (t) { var url = s.url var options = { url: url } request.head(options, function (err, resp, body) { t.equal(err, null) t.equal(body, '') t.equal(Object.keys(options).length, 1) t.equal(options.url, url) request.get(options, function (err, resp, body) { t.equal(err, null) t.equal(body, 'ok') t.equal(Object.keys(options).length, 1) t.equal(options.url, url) t.equal(methodsSeen.head, 1) t.equal(methodsSeen.get, 1) t.end() }) }) }) tape('cleanup', function (t) { s.close(function () { t.end() }) }) request-2.88.1/tests/test-options-convenience-method.js000066400000000000000000000020541333333633500232300ustar00rootroot00000000000000'use strict' var server = require('./server') var request = require('../index') var tape = require('tape') var destroyable = require('server-destroy') var s = server.createServer() destroyable(s) tape('setup', function (t) { s.listen(0, function () { s.on('/options', function (req, res) { res.writeHead(200, { 'x-original-method': req.method, 'allow': 'OPTIONS, GET, HEAD' }) res.end() }) t.end() }) }) tape('options(string, function)', function (t) { request.options(s.url + '/options', function (err, res) { t.equal(err, null) t.equal(res.statusCode, 200) t.equal(res.headers['x-original-method'], 'OPTIONS') t.end() }) }) tape('options(object, function)', function (t) { request.options({ url: s.url + '/options', headers: { foo: 'bar' } }, function (err, res) { t.equal(err, null) t.equal(res.statusCode, 200) t.equal(res.headers['x-original-method'], 'OPTIONS') t.end() }) }) tape('cleanup', function (t) { s.destroy(function () { t.end() }) }) request-2.88.1/tests/test-params.js000066400000000000000000000042571333333633500172570ustar00rootroot00000000000000'use strict' var server = require('./server') var request = require('../index') var tape = require('tape') var s = server.createServer() function runTest (name, test) { tape(name, function (t) { s.on('/' + name, test.resp) request(s.url + '/' + name, test, function (err, resp, body) { t.equal(err, null) if (test.expectBody) { if (Buffer.isBuffer(test.expectBody)) { t.equal(test.expectBody.toString(), body.toString()) } else { t.deepEqual(test.expectBody, body) } } t.end() }) }) } tape('setup', function (t) { s.listen(0, function () { t.end() }) }) runTest('testGet', { resp: server.createGetResponse('TESTING!'), expectBody: 'TESTING!' }) runTest('testGetChunkBreak', { resp: server.createChunkResponse( [ Buffer.from([239]), Buffer.from([163]), Buffer.from([191]), Buffer.from([206]), Buffer.from([169]), Buffer.from([226]), Buffer.from([152]), Buffer.from([131]) ]), expectBody: '\uf8ff\u03a9\u2603' }) runTest('testGetBuffer', { resp: server.createGetResponse(Buffer.from('TESTING!')), encoding: null, expectBody: Buffer.from('TESTING!') }) runTest('testGetJSON', { resp: server.createGetResponse('{"test":true}', 'application/json'), json: true, expectBody: {'test': true} }) runTest('testPutString', { resp: server.createPostValidator('PUTTINGDATA'), method: 'PUT', body: 'PUTTINGDATA' }) runTest('testPutBuffer', { resp: server.createPostValidator('PUTTINGDATA'), method: 'PUT', body: Buffer.from('PUTTINGDATA') }) runTest('testPutJSON', { resp: server.createPostValidator(JSON.stringify({foo: 'bar'})), method: 'PUT', json: {foo: 'bar'} }) runTest('testPutMultipart', { resp: server.createPostValidator( '--__BOUNDARY__\r\n' + 'content-type: text/html\r\n' + '\r\n' + 'Oh hi.' + '\r\n--__BOUNDARY__\r\n\r\n' + 'Oh hi.' + '\r\n--__BOUNDARY__--' ), method: 'PUT', multipart: [ {'content-type': 'text/html', 'body': 'Oh hi.'}, {'body': 'Oh hi.'} ] }) tape('cleanup', function (t) { s.close(function () { t.end() }) }) request-2.88.1/tests/test-piped-redirect.js000066400000000000000000000020611333333633500206630ustar00rootroot00000000000000'use strict' var http = require('http') var request = require('../index') var tape = require('tape') var port1 var port2 var s1 = http.createServer(function (req, resp) { if (req.url === '/original') { resp.writeHeader(302, { 'location': '/redirected' }) resp.end() } else if (req.url === '/redirected') { resp.writeHeader(200, { 'content-type': 'text/plain' }) resp.write('OK') resp.end() } }) var s2 = http.createServer(function (req, resp) { var x = request('http://localhost:' + port1 + '/original') req.pipe(x) x.pipe(resp) }) tape('setup', function (t) { s1.listen(0, function () { port1 = this.address().port s2.listen(0, function () { port2 = this.address().port t.end() }) }) }) tape('piped redirect', function (t) { request('http://localhost:' + port2 + '/original', function (err, res, body) { t.equal(err, null) t.equal(body, 'OK') t.end() }) }) tape('cleanup', function (t) { s1.close(function () { s2.close(function () { t.end() }) }) }) request-2.88.1/tests/test-pipes.js000066400000000000000000000215021333333633500171040ustar00rootroot00000000000000'use strict' var server = require('./server') var stream = require('stream') var fs = require('fs') var request = require('../index') var path = require('path') var util = require('util') var tape = require('tape') var s = server.createServer() s.on('/cat', function (req, res) { if (req.method === 'GET') { res.writeHead(200, { 'content-type': 'text/plain-test', 'content-length': 4 }) res.end('asdf') } else if (req.method === 'PUT') { var body = '' req.on('data', function (chunk) { body += chunk }).on('end', function () { res.writeHead(201) res.end() s.emit('catDone', req, res, body) }) } }) s.on('/doodle', function (req, res) { if (req.headers['x-oneline-proxy']) { res.setHeader('x-oneline-proxy', 'yup') } res.writeHead('200', { 'content-type': 'image/jpeg' }) fs.createReadStream(path.join(__dirname, 'googledoodle.jpg')).pipe(res) }) function ValidationStream (t, str) { this.str = str this.buf = '' this.on('data', function (data) { this.buf += data }) this.on('end', function () { t.equal(this.str, this.buf) }) this.writable = true } util.inherits(ValidationStream, stream.Stream) ValidationStream.prototype.write = function (chunk) { this.emit('data', chunk) } ValidationStream.prototype.end = function (chunk) { if (chunk) { this.emit('data', chunk) } this.emit('end') } tape('setup', function (t) { s.listen(0, function () { t.end() }) }) tape('piping to a request object', function (t) { s.once('/push', server.createPostValidator('mydata')) var mydata = new stream.Stream() mydata.readable = true var r1 = request.put({ url: s.url + '/push' }, function (err, res, body) { t.equal(err, null) t.equal(res.statusCode, 200) t.equal(body, 'mydata') t.end() }) mydata.pipe(r1) mydata.emit('data', 'mydata') mydata.emit('end') }) tape('piping to a request object with invalid uri', function (t) { var mybodydata = new stream.Stream() mybodydata.readable = true var r2 = request.put({ url: '/bad-uri', json: true }, function (err, res, body) { t.ok(err instanceof Error) t.equal(err.message, 'Invalid URI "/bad-uri"') t.end() }) mybodydata.pipe(r2) mybodydata.emit('data', JSON.stringify({ foo: 'bar' })) mybodydata.emit('end') }) tape('piping to a request object with a json body', function (t) { var obj = {foo: 'bar'} var json = JSON.stringify(obj) s.once('/push-json', server.createPostValidator(json, 'application/json')) var mybodydata = new stream.Stream() mybodydata.readable = true var r2 = request.put({ url: s.url + '/push-json', json: true }, function (err, res, body) { t.equal(err, null) t.equal(res.statusCode, 200) t.deepEqual(body, obj) t.end() }) mybodydata.pipe(r2) mybodydata.emit('data', JSON.stringify({ foo: 'bar' })) mybodydata.emit('end') }) tape('piping from a request object', function (t) { s.once('/pull', server.createGetResponse('mypulldata')) var mypulldata = new stream.Stream() mypulldata.writable = true request({ url: s.url + '/pull' }).pipe(mypulldata) var d = '' mypulldata.write = function (chunk) { d += chunk } mypulldata.end = function () { t.equal(d, 'mypulldata') t.end() } }) tape('pause when piping from a request object', function (t) { s.once('/chunks', function (req, res) { res.writeHead(200, { 'content-type': 'text/plain' }) res.write('Chunk 1') setTimeout(function () { res.end('Chunk 2') }, 10) }) var chunkNum = 0 var paused = false request({ url: s.url + '/chunks' }) .on('data', function (chunk) { var self = this t.notOk(paused, 'Only receive data when not paused') ++chunkNum if (chunkNum === 1) { t.equal(chunk.toString(), 'Chunk 1') self.pause() paused = true setTimeout(function () { paused = false self.resume() }, 100) } else { t.equal(chunk.toString(), 'Chunk 2') } }) .on('end', t.end.bind(t)) }) tape('pause before piping from a request object', function (t) { s.once('/pause-before', function (req, res) { res.writeHead(200, { 'content-type': 'text/plain' }) res.end('Data') }) var paused = true var r = request({ url: s.url + '/pause-before' }) r.pause() r.on('data', function (data) { t.notOk(paused, 'Only receive data when not paused') t.equal(data.toString(), 'Data') }) r.on('end', t.end.bind(t)) setTimeout(function () { paused = false r.resume() }, 100) }) var fileContents = fs.readFileSync(__filename) function testPipeFromFile (testName, hasContentLength) { tape(testName, function (t) { s.once('/pushjs', function (req, res) { if (req.method === 'PUT') { t.equal(req.headers['content-type'], 'application/javascript') t.equal( req.headers['content-length'], (hasContentLength ? '' + fileContents.length : undefined)) var body = '' req.setEncoding('utf8') req.on('data', function (data) { body += data }) req.on('end', function () { res.end() t.equal(body, fileContents.toString()) t.end() }) } else { res.end() } }) var r = request.put(s.url + '/pushjs') fs.createReadStream(__filename).pipe(r) if (hasContentLength) { r.setHeader('content-length', fileContents.length) } }) } // TODO Piping from a file does not send content-length header testPipeFromFile('piping from a file', false) testPipeFromFile('piping from a file with content-length', true) tape('piping to and from same URL', function (t) { s.once('catDone', function (req, res, body) { t.equal(req.headers['content-type'], 'text/plain-test') t.equal(req.headers['content-length'], '4') t.equal(body, 'asdf') t.end() }) request.get(s.url + '/cat') .pipe(request.put(s.url + '/cat')) }) tape('piping between urls', function (t) { s.once('/catresp', function (req, res) { request.get(s.url + '/cat').pipe(res) }) request.get(s.url + '/catresp', function (err, res, body) { t.equal(err, null) t.equal(res.headers['content-type'], 'text/plain-test') t.equal(res.headers['content-length'], '4') t.end() }) }) tape('writing to file', function (t) { var doodleWrite = fs.createWriteStream(path.join(__dirname, 'test.jpg')) request.get(s.url + '/doodle').pipe(doodleWrite) doodleWrite.on('close', function () { t.deepEqual( fs.readFileSync(path.join(__dirname, 'googledoodle.jpg')), fs.readFileSync(path.join(__dirname, 'test.jpg'))) fs.unlinkSync(path.join(__dirname, 'test.jpg')) t.end() }) }) tape('one-line proxy', function (t) { s.once('/onelineproxy', function (req, res) { var x = request(s.url + '/doodle') req.pipe(x) x.pipe(res) }) request.get({ uri: s.url + '/onelineproxy', headers: { 'x-oneline-proxy': 'nope' } }, function (err, res, body) { t.equal(err, null) t.equal(res.headers['x-oneline-proxy'], 'yup') t.end() }) }) tape('piping after response', function (t) { s.once('/afterresponse', function (req, res) { res.write('d') res.end() }) var rAfterRes = request.post(s.url + '/afterresponse') rAfterRes.on('response', function () { var v = new ValidationStream(t, 'd') rAfterRes.pipe(v) v.on('end', function () { t.end() }) }) }) tape('piping through a redirect', function (t) { s.once('/forward1', function (req, res) { res.writeHead(302, { location: '/forward2' }) res.end() }) s.once('/forward2', function (req, res) { res.writeHead('200', { 'content-type': 'image/png' }) res.write('d') res.end() }) var validateForward = new ValidationStream(t, 'd') request.get(s.url + '/forward1').pipe(validateForward) validateForward.on('end', function () { t.end() }) }) tape('pipe options', function (t) { s.once('/opts', server.createGetResponse('opts response')) var optsStream = new stream.Stream() var optsData = '' optsStream.writable = true optsStream.write = function (buf) { optsData += buf if (optsData === 'opts response') { setTimeout(function () { t.end() }, 10) } } optsStream.end = function () { t.fail('end called') } request({ url: s.url + '/opts' }).pipe(optsStream, { end: false }) }) tape('request.pipefilter is called correctly', function (t) { s.once('/pipefilter', function (req, res) { res.end('d') }) var validatePipeFilter = new ValidationStream(t, 'd') var r3 = request.get(s.url + '/pipefilter') r3.pipe(validatePipeFilter) r3.pipefilter = function (res, dest) { t.equal(res, r3.response) t.equal(dest, validatePipeFilter) t.end() } }) tape('cleanup', function (t) { s.close(function () { t.end() }) }) request-2.88.1/tests/test-pool.js000066400000000000000000000063411333333633500167410ustar00rootroot00000000000000'use strict' var request = require('../index') var http = require('http') var tape = require('tape') var s = http.createServer(function (req, res) { res.statusCode = 200 res.end('asdf') }) tape('setup', function (t) { s.listen(0, function () { s.url = 'http://localhost:' + this.address().port t.end() }) }) tape('pool', function (t) { request({ url: s.url, pool: false }, function (err, res, body) { t.equal(err, null) t.equal(res.statusCode, 200) t.equal(body, 'asdf') var agent = res.request.agent t.equal(agent, false) t.end() }) }) tape('forever', function (t) { var r = request({ url: s.url, forever: true, pool: {maxSockets: 1024} }, function (err, res, body) { // explicitly shut down the agent if (typeof r.agent.destroy === 'function') { r.agent.destroy() } else { // node < 0.12 Object.keys(r.agent.sockets).forEach(function (name) { r.agent.sockets[name].forEach(function (socket) { socket.end() }) }) } t.equal(err, null) t.equal(res.statusCode, 200) t.equal(body, 'asdf') var agent = res.request.agent t.equal(agent.maxSockets, 1024) t.end() }) }) tape('forever, should use same agent in sequential requests', function (t) { var r = request.defaults({ forever: true }) var req1 = r(s.url) var req2 = r(s.url + '/somepath') req1.abort() req2.abort() if (typeof req1.agent.destroy === 'function') { req1.agent.destroy() } if (typeof req2.agent.destroy === 'function') { req2.agent.destroy() } t.equal(req1.agent, req2.agent) t.end() }) tape('forever, should use same agent in sequential requests(with pool.maxSockets)', function (t) { var r = request.defaults({ forever: true, pool: {maxSockets: 1024} }) var req1 = r(s.url) var req2 = r(s.url + '/somepath') req1.abort() req2.abort() if (typeof req1.agent.destroy === 'function') { req1.agent.destroy() } if (typeof req2.agent.destroy === 'function') { req2.agent.destroy() } t.equal(req1.agent.maxSockets, 1024) t.equal(req1.agent, req2.agent) t.end() }) tape('forever, should use same agent in request() and request.verb', function (t) { var r = request.defaults({ forever: true, pool: {maxSockets: 1024} }) var req1 = r(s.url) var req2 = r.get(s.url) req1.abort() req2.abort() if (typeof req1.agent.destroy === 'function') { req1.agent.destroy() } if (typeof req2.agent.destroy === 'function') { req2.agent.destroy() } t.equal(req1.agent.maxSockets, 1024) t.equal(req1.agent, req2.agent) t.end() }) tape('should use different agent if pool option specified', function (t) { var r = request.defaults({ forever: true, pool: {maxSockets: 1024} }) var req1 = r(s.url) var req2 = r.get({ url: s.url, pool: {maxSockets: 20} }) req1.abort() req2.abort() if (typeof req1.agent.destroy === 'function') { req1.agent.destroy() } if (typeof req2.agent.destroy === 'function') { req2.agent.destroy() } t.equal(req1.agent.maxSockets, 1024) t.equal(req2.agent.maxSockets, 20) t.notEqual(req1.agent, req2.agent) t.end() }) tape('cleanup', function (t) { s.close(function () { t.end() }) }) request-2.88.1/tests/test-promise.js000066400000000000000000000021431333333633500174420ustar00rootroot00000000000000'use strict' var http = require('http') var request = require('../index') var tape = require('tape') var Promise = require('bluebird') var s = http.createServer(function (req, res) { res.writeHead(200, {}) res.end('ok') }) tape('setup', function (t) { s.listen(0, function () { s.url = 'http://localhost:' + this.address().port t.end() }) }) tape('promisify convenience method', function (t) { var get = request.get var p = Promise.promisify(get, {multiArgs: true}) p(s.url) .then(function (results) { var res = results[0] t.equal(res.statusCode, 200) t.end() }) }) tape('promisify request function', function (t) { var p = Promise.promisify(request, {multiArgs: true}) p(s.url) .spread(function (res, body) { t.equal(res.statusCode, 200) t.end() }) }) tape('promisify all methods', function (t) { Promise.promisifyAll(request, {multiArgs: true}) request.getAsync(s.url) .spread(function (res, body) { t.equal(res.statusCode, 200) t.end() }) }) tape('cleanup', function (t) { s.close(function () { t.end() }) }) request-2.88.1/tests/test-proxy-connect.js000066400000000000000000000035411333333633500205770ustar00rootroot00000000000000'use strict' var request = require('../index') var tape = require('tape') var called = false var proxiedHost = 'google.com' var data = '' var s = require('net').createServer(function (sock) { called = true sock.once('data', function (c) { data += c sock.write('HTTP/1.1 200 OK\r\n\r\n') sock.once('data', function (c) { data += c sock.write('HTTP/1.1 200 OK\r\n') sock.write('content-type: text/plain\r\n') sock.write('content-length: 5\r\n') sock.write('\r\n') sock.end('derp\n') }) }) }) tape('setup', function (t) { s.listen(0, function () { s.url = 'http://localhost:' + this.address().port t.end() }) }) tape('proxy', function (t) { request({ tunnel: true, url: 'http://' + proxiedHost, proxy: s.url, headers: { 'Proxy-Authorization': 'Basic dXNlcjpwYXNz', 'authorization': 'Token deadbeef', 'dont-send-to-proxy': 'ok', 'dont-send-to-dest': 'ok', 'accept': 'yo', 'user-agent': 'just another foobar' }, proxyHeaderExclusiveList: ['Dont-send-to-dest'] }, function (err, res, body) { t.equal(err, null) t.equal(res.statusCode, 200) t.equal(body, 'derp\n') var re = new RegExp([ 'CONNECT google.com:80 HTTP/1.1', 'Proxy-Authorization: Basic dXNlcjpwYXNz', 'dont-send-to-dest: ok', 'accept: yo', 'user-agent: just another foobar', 'host: google.com:80', 'Connection: close', '', 'GET / HTTP/1.1', 'authorization: Token deadbeef', 'dont-send-to-proxy: ok', 'accept: yo', 'user-agent: just another foobar', 'host: google.com' ].join('\r\n')) t.equal(true, re.test(data)) t.equal(called, true, 'the request must be made to the proxy server') t.end() }) }) tape('cleanup', function (t) { s.close(function () { t.end() }) }) request-2.88.1/tests/test-proxy.js000066400000000000000000000171751333333633500171600ustar00rootroot00000000000000'use strict' var server = require('./server') var request = require('../index') var tape = require('tape') var s = server.createServer() var currResponseHandler ['http://google.com/', 'https://google.com/'].forEach(function (url) { s.on(url, function (req, res) { currResponseHandler(req, res) res.writeHeader(200) res.end('ok') }) }) var proxyEnvVars = [ 'http_proxy', 'HTTP_PROXY', 'https_proxy', 'HTTPS_PROXY', 'no_proxy', 'NO_PROXY' ] // Set up and run a proxy test. All environment variables pertaining to // proxies will be deleted before each test. Specify environment variables as // `options.env`; all other keys on `options` will be passed as additional // options to `request`. // // If `responseHandler` is a function, it should perform asserts on the server // response. It will be called with parameters (t, req, res). Otherwise, // `responseHandler` should be truthy to indicate that the proxy should be used // for this request, or falsy to indicate that the proxy should not be used for // this request. function runTest (name, options, responseHandler) { tape(name, function (t) { proxyEnvVars.forEach(function (v) { delete process.env[v] }) if (options.env) { for (var v in options.env) { process.env[v] = options.env[v] } delete options.env } var called = false currResponseHandler = function (req, res) { if (responseHandler) { called = true t.equal(req.headers.host, 'google.com') if (typeof responseHandler === 'function') { responseHandler(t, req, res) } } else { t.fail('proxy response should not be called') } } options.url = options.url || 'http://google.com' request(options, function (err, res, body) { if (responseHandler && !called) { t.fail('proxy response should be called') } t.equal(err, null) t.equal(res.statusCode, 200) if (responseHandler) { if (body.length > 100) { body = body.substring(0, 100) } t.equal(body, 'ok') } else { t.equal(/^/i.test(body), true) } t.end() }) }) } function addTests () { // If the `runTest` function is changed, run the following command and make // sure both of these tests fail: // // TEST_PROXY_HARNESS=y node tests/test-proxy.js if (process.env.TEST_PROXY_HARNESS) { runTest('should fail with "proxy response should not be called"', { proxy: s.url }, false) runTest('should fail with "proxy response should be called"', { proxy: null }, true) } else { // Run the real tests runTest('basic proxy', { proxy: s.url, headers: { 'proxy-authorization': 'Token Fooblez' } }, function (t, req, res) { t.equal(req.headers['proxy-authorization'], 'Token Fooblez') }) runTest('proxy auth without uri auth', { proxy: 'http://user:pass@localhost:' + s.port }, function (t, req, res) { t.equal(req.headers['proxy-authorization'], 'Basic dXNlcjpwYXNz') }) // http: urls and basic proxy settings runTest('HTTP_PROXY environment variable and http: url', { env: { HTTP_PROXY: s.url } }, true) runTest('http_proxy environment variable and http: url', { env: { http_proxy: s.url } }, true) runTest('HTTPS_PROXY environment variable and http: url', { env: { HTTPS_PROXY: s.url } }, false) runTest('https_proxy environment variable and http: url', { env: { https_proxy: s.url } }, false) // https: urls and basic proxy settings runTest('HTTP_PROXY environment variable and https: url', { env: { HTTP_PROXY: s.url }, url: 'https://google.com', tunnel: false, pool: false }, true) runTest('http_proxy environment variable and https: url', { env: { http_proxy: s.url }, url: 'https://google.com', tunnel: false }, true) runTest('HTTPS_PROXY environment variable and https: url', { env: { HTTPS_PROXY: s.url }, url: 'https://google.com', tunnel: false }, true) runTest('https_proxy environment variable and https: url', { env: { https_proxy: s.url }, url: 'https://google.com', tunnel: false }, true) runTest('multiple environment variables and https: url', { env: { HTTPS_PROXY: s.url, HTTP_PROXY: 'http://localhost:0/' }, url: 'https://google.com', tunnel: false }, true) // no_proxy logic runTest('NO_PROXY hostnames are case insensitive', { env: { HTTP_PROXY: s.url, NO_PROXY: 'GOOGLE.COM' } }, false) runTest('NO_PROXY hostnames are case insensitive 2', { env: { http_proxy: s.url, NO_PROXY: 'GOOGLE.COM' } }, false) runTest('NO_PROXY hostnames are case insensitive 3', { env: { HTTP_PROXY: s.url, no_proxy: 'GOOGLE.COM' } }, false) runTest('NO_PROXY ignored with explicit proxy passed', { env: { NO_PROXY: '*' }, proxy: s.url }, true) runTest('NO_PROXY overrides HTTP_PROXY for specific hostname', { env: { HTTP_PROXY: s.url, NO_PROXY: 'google.com' } }, false) runTest('no_proxy overrides HTTP_PROXY for specific hostname', { env: { HTTP_PROXY: s.url, no_proxy: 'google.com' } }, false) runTest('NO_PROXY does not override HTTP_PROXY if no hostnames match', { env: { HTTP_PROXY: s.url, NO_PROXY: 'foo.bar,bar.foo' } }, true) runTest('NO_PROXY overrides HTTP_PROXY if a hostname matches', { env: { HTTP_PROXY: s.url, NO_PROXY: 'foo.bar,google.com' } }, false) runTest('NO_PROXY allows an explicit port', { env: { HTTP_PROXY: s.url, NO_PROXY: 'google.com:80' } }, false) runTest('NO_PROXY only overrides HTTP_PROXY if the port matches', { env: { HTTP_PROXY: s.url, NO_PROXY: 'google.com:1234' } }, true) runTest('NO_PROXY=* should override HTTP_PROXY for all hosts', { env: { HTTP_PROXY: s.url, NO_PROXY: '*' } }, false) runTest('NO_PROXY should override HTTP_PROXY for all subdomains', { env: { HTTP_PROXY: s.url, NO_PROXY: 'google.com' }, headers: { host: 'www.google.com' } }, false) runTest('NO_PROXY should not override HTTP_PROXY for partial domain matches', { env: { HTTP_PROXY: s.url, NO_PROXY: 'oogle.com' } }, true) runTest('NO_PROXY with port should not override HTTP_PROXY for partial domain matches', { env: { HTTP_PROXY: s.url, NO_PROXY: 'oogle.com:80' } }, true) // misc // this fails if the check 'isMatchedAt > -1' in lib/getProxyFromURI.js is // missing or broken runTest('http_proxy with length of one more than the URL', { env: { HTTP_PROXY: s.url, NO_PROXY: 'elgoog1.com' // one more char than google.com } }, true) runTest('proxy: null should override HTTP_PROXY', { env: { HTTP_PROXY: s.url }, proxy: null, timeout: 500 }, false) runTest('uri auth without proxy auth', { url: 'http://user:pass@google.com', proxy: s.url }, function (t, req, res) { t.equal(req.headers['proxy-authorization'], undefined) t.equal(req.headers.authorization, 'Basic dXNlcjpwYXNz') }) } } tape('setup', function (t) { s.listen(0, function () { addTests() tape('cleanup', function (t) { s.close(function () { t.end() }) }) t.end() }) }) request-2.88.1/tests/test-qs.js000066400000000000000000000071561333333633500164200ustar00rootroot00000000000000'use strict' var request = require('../index') var tape = require('tape') // Run a querystring test. `options` can have the following keys: // - suffix : a string to be added to the URL // - qs : an object to be passed to request's `qs` option // - qsParseOptions : an object to be passed to request's `qsParseOptions` option // - qsStringifyOptions : an object to be passed to request's `qsStringifyOptions` option // - afterRequest : a function to execute after creating the request // - expected : the expected path of the request // - expectedQuerystring : expected path when using the querystring library function runTest (name, options) { var uri = 'http://www.google.com' + (options.suffix || '') var opts = { uri: uri, qsParseOptions: options.qsParseOptions, qsStringifyOptions: options.qsStringifyOptions } if (options.qs) { opts.qs = options.qs } tape(name + ' - using qs', function (t) { var r = request.get(opts) if (typeof options.afterRequest === 'function') { options.afterRequest(r) } process.nextTick(function () { t.equal(r.path, options.expected) r.abort() t.end() }) }) tape(name + ' - using querystring', function (t) { opts.useQuerystring = true var r = request.get(opts) if (typeof options.afterRequest === 'function') { options.afterRequest(r) } process.nextTick(function () { t.equal(r.path, options.expectedQuerystring || options.expected) r.abort() t.end() }) }) } function esc (str) { return str .replace(/\[/g, '%5B') .replace(/\]/g, '%5D') } runTest('adding a querystring', { qs: { q: 'search' }, expected: '/?q=search' }) runTest('replacing a querystring value', { suffix: '?q=abc', qs: { q: 'search' }, expected: '/?q=search' }) runTest('appending a querystring value to the ones present in the uri', { suffix: '?x=y', qs: { q: 'search' }, expected: '/?x=y&q=search' }) runTest('leaving a querystring alone', { suffix: '?x=y', expected: '/?x=y' }) runTest('giving empty qs property', { qs: {}, expected: '/' }) runTest('modifying the qs after creating the request', { qs: {}, afterRequest: function (r) { r.qs({ q: 'test' }) }, expected: '/?q=test' }) runTest('a query with an object for a value', { qs: { where: { foo: 'bar' } }, expected: esc('/?where[foo]=bar'), expectedQuerystring: '/?where=' }) runTest('a query with an array for a value', { qs: { order: ['bar', 'desc'] }, expected: esc('/?order[0]=bar&order[1]=desc'), expectedQuerystring: '/?order=bar&order=desc' }) runTest('pass options to the qs module via the qsParseOptions key', { suffix: '?a=1;b=2', qs: {}, qsParseOptions: { delimiter: ';' }, qsStringifyOptions: { delimiter: ';' }, expected: esc('/?a=1;b=2'), expectedQuerystring: '/?a=1%3Bb%3D2' }) runTest('pass options to the qs module via the qsStringifyOptions key', { qs: { order: ['bar', 'desc'] }, qsStringifyOptions: { arrayFormat: 'brackets' }, expected: esc('/?order[]=bar&order[]=desc'), expectedQuerystring: '/?order=bar&order=desc' }) runTest('pass options to the querystring module via the qsParseOptions key', { suffix: '?a=1;b=2', qs: {}, qsParseOptions: { sep: ';' }, qsStringifyOptions: { sep: ';' }, expected: esc('/?a=1%3Bb%3D2'), expectedQuerystring: '/?a=1;b=2' }) runTest('pass options to the querystring module via the qsStringifyOptions key', { qs: { order: ['bar', 'desc'] }, qsStringifyOptions: { sep: ';' }, expected: esc('/?order[0]=bar&order[1]=desc'), expectedQuerystring: '/?order=bar;order=desc' }) request-2.88.1/tests/test-redirect-auth.js000066400000000000000000000065441333333633500205350ustar00rootroot00000000000000'use strict' var server = require('./server') var request = require('../index') var util = require('util') var tape = require('tape') var destroyable = require('server-destroy') var s = server.createServer() var ss = server.createSSLServer() destroyable(s) destroyable(ss) // always send basic auth and allow non-strict SSL request = request.defaults({ auth: { user: 'test', pass: 'testing' }, rejectUnauthorized: false }) // redirect.from(proto, host).to(proto, host) returns an object with keys: // src : source URL // dst : destination URL var redirect = { from: function (fromProto, fromHost) { return { to: function (toProto, toHost) { var fromPort = (fromProto === 'http' ? s.port : ss.port) var toPort = (toProto === 'http' ? s.port : ss.port) return { src: util.format( '%s://%s:%d/to/%s/%s', fromProto, fromHost, fromPort, toProto, toHost), dst: util.format( '%s://%s:%d/from/%s/%s', toProto, toHost, toPort, fromProto, fromHost) } } } } } function handleRequests (srv) { ['http', 'https'].forEach(function (proto) { ['localhost', '127.0.0.1'].forEach(function (host) { srv.on(util.format('/to/%s/%s', proto, host), function (req, res) { var r = redirect .from(srv.protocol, req.headers.host.split(':')[0]) .to(proto, host) res.writeHead(301, { location: r.dst }) res.end() }) srv.on(util.format('/from/%s/%s', proto, host), function (req, res) { res.end('auth: ' + (req.headers.authorization || '(nothing)')) }) }) }) } handleRequests(s) handleRequests(ss) function runTest (name, redir, expectAuth) { tape('redirect to ' + name, function (t) { request(redir.src, function (err, res, body) { t.equal(err, null) t.equal(res.request.uri.href, redir.dst) t.equal(res.statusCode, 200) t.equal(body, expectAuth ? 'auth: Basic dGVzdDp0ZXN0aW5n' : 'auth: (nothing)') t.end() }) }) } function addTests () { runTest('same host and protocol', redirect.from('http', 'localhost').to('http', 'localhost'), true) runTest('same host different protocol', redirect.from('http', 'localhost').to('https', 'localhost'), true) runTest('different host same protocol', redirect.from('https', '127.0.0.1').to('https', 'localhost'), false) runTest('different host and protocol', redirect.from('http', 'localhost').to('https', '127.0.0.1'), false) } tape('setup', function (t) { s.listen(0, function () { ss.listen(0, function () { addTests() tape('cleanup', function (t) { s.destroy(function () { ss.destroy(function () { t.end() }) }) }) t.end() }) }) }) tape('redirect URL helper', function (t) { t.deepEqual( redirect.from('http', 'localhost').to('https', '127.0.0.1'), { src: util.format('http://localhost:%d/to/https/127.0.0.1', s.port), dst: util.format('https://127.0.0.1:%d/from/http/localhost', ss.port) }) t.deepEqual( redirect.from('https', 'localhost').to('http', 'localhost'), { src: util.format('https://localhost:%d/to/http/localhost', ss.port), dst: util.format('http://localhost:%d/from/https/localhost', s.port) }) t.end() }) request-2.88.1/tests/test-redirect-complex.js000066400000000000000000000033721333333633500212370ustar00rootroot00000000000000'use strict' var server = require('./server') var request = require('../index') var events = require('events') var tape = require('tape') var destroyable = require('server-destroy') var s = server.createServer() var ss = server.createSSLServer() var e = new events.EventEmitter() destroyable(s) destroyable(ss) function bouncy (s, serverUrl) { var redirs = { a: 'b', b: 'c', c: 'd', d: 'e', e: 'f', f: 'g', g: 'h', h: 'end' } var perm = true Object.keys(redirs).forEach(function (p) { var t = redirs[p] // switch type each time var type = perm ? 301 : 302 perm = !perm s.on('/' + p, function (req, res) { setTimeout(function () { res.writeHead(type, { location: serverUrl + '/' + t }) res.end() }, Math.round(Math.random() * 25)) }) }) s.on('/end', function (req, res) { var key = req.headers['x-test-key'] e.emit('hit-' + key, key) res.writeHead(200) res.end(key) }) } tape('setup', function (t) { s.listen(0, function () { ss.listen(0, function () { bouncy(s, ss.url) bouncy(ss, s.url) t.end() }) }) }) tape('lots of redirects', function (t) { var n = 10 t.plan(n * 4) function doRedirect (i) { var key = 'test_' + i request({ url: (i % 2 ? s.url : ss.url) + '/a', headers: { 'x-test-key': key }, rejectUnauthorized: false }, function (err, res, body) { t.equal(err, null) t.equal(res.statusCode, 200) t.equal(body, key) }) e.once('hit-' + key, function (v) { t.equal(v, key) }) } for (var i = 0; i < n; i++) { doRedirect(i) } }) tape('cleanup', function (t) { s.destroy(function () { ss.destroy(function () { t.end() }) }) }) request-2.88.1/tests/test-redirect.js000066400000000000000000000301661333333633500175730ustar00rootroot00000000000000'use strict' var server = require('./server') var assert = require('assert') var request = require('../index') var tape = require('tape') var http = require('http') var destroyable = require('server-destroy') var s = server.createServer() var ss = server.createSSLServer() var hits = {} var jar = request.jar() destroyable(s) destroyable(ss) s.on('/ssl', function (req, res) { res.writeHead(302, { location: ss.url + '/' }) res.end() }) ss.on('/', function (req, res) { res.writeHead(200) res.end('SSL') }) function createRedirectEndpoint (code, label, landing) { s.on('/' + label, function (req, res) { hits[label] = true res.writeHead(code, { 'location': s.url + '/' + landing, 'set-cookie': 'ham=eggs' }) res.end() }) } function createLandingEndpoint (landing) { s.on('/' + landing, function (req, res) { // Make sure the cookie doesn't get included twice, see #139: // Make sure cookies are set properly after redirect assert.equal(req.headers.cookie, 'foo=bar; quux=baz; ham=eggs') hits[landing] = true res.writeHead(200, {'x-response': req.method.toUpperCase() + ' ' + landing}) res.end(req.method.toUpperCase() + ' ' + landing) }) } function bouncer (code, label, hops) { var hop var landing = label + '_landing' var currentLabel var currentLanding hops = hops || 1 if (hops === 1) { createRedirectEndpoint(code, label, landing) } else { for (hop = 0; hop < hops; hop++) { currentLabel = (hop === 0) ? label : label + '_' + (hop + 1) currentLanding = (hop === hops - 1) ? landing : label + '_' + (hop + 2) createRedirectEndpoint(code, currentLabel, currentLanding) } } createLandingEndpoint(landing) } tape('setup', function (t) { s.listen(0, function () { ss.listen(0, function () { bouncer(301, 'temp') bouncer(301, 'double', 2) bouncer(301, 'treble', 3) bouncer(302, 'perm') bouncer(302, 'nope') bouncer(307, 'fwd') t.end() }) }) }) tape('permanent bounce', function (t) { jar.setCookie('quux=baz', s.url) hits = {} request({ uri: s.url + '/perm', jar: jar, headers: { cookie: 'foo=bar' } }, function (err, res, body) { t.equal(err, null) t.equal(res.statusCode, 200) t.ok(hits.perm, 'Original request is to /perm') t.ok(hits.perm_landing, 'Forward to permanent landing URL') t.equal(body, 'GET perm_landing', 'Got permanent landing content') t.end() }) }) tape('preserve HEAD method when using followAllRedirects', function (t) { jar.setCookie('quux=baz', s.url) hits = {} request({ method: 'HEAD', uri: s.url + '/perm', followAllRedirects: true, jar: jar, headers: { cookie: 'foo=bar' } }, function (err, res, body) { t.equal(err, null) t.equal(res.statusCode, 200) t.ok(hits.perm, 'Original request is to /perm') t.ok(hits.perm_landing, 'Forward to permanent landing URL') t.equal(res.headers['x-response'], 'HEAD perm_landing', 'Got permanent landing content') t.end() }) }) tape('temporary bounce', function (t) { hits = {} request({ uri: s.url + '/temp', jar: jar, headers: { cookie: 'foo=bar' } }, function (err, res, body) { t.equal(err, null) t.equal(res.statusCode, 200) t.ok(hits.temp, 'Original request is to /temp') t.ok(hits.temp_landing, 'Forward to temporary landing URL') t.equal(body, 'GET temp_landing', 'Got temporary landing content') t.end() }) }) tape('prevent bouncing', function (t) { hits = {} request({ uri: s.url + '/nope', jar: jar, headers: { cookie: 'foo=bar' }, followRedirect: false }, function (err, res, body) { t.equal(err, null) t.equal(res.statusCode, 302) t.ok(hits.nope, 'Original request to /nope') t.ok(!hits.nope_landing, 'No chasing the redirect') t.equal(res.statusCode, 302, 'Response is the bounce itself') t.end() }) }) tape('should not follow post redirects by default', function (t) { hits = {} request.post(s.url + '/temp', { jar: jar, headers: { cookie: 'foo=bar' } }, function (err, res, body) { t.equal(err, null) t.equal(res.statusCode, 301) t.ok(hits.temp, 'Original request is to /temp') t.ok(!hits.temp_landing, 'No chasing the redirect when post') t.equal(res.statusCode, 301, 'Response is the bounce itself') t.end() }) }) tape('should follow post redirects when followallredirects true', function (t) { hits = {} request.post({ uri: s.url + '/temp', followAllRedirects: true, jar: jar, headers: { cookie: 'foo=bar' } }, function (err, res, body) { t.equal(err, null) t.equal(res.statusCode, 200) t.ok(hits.temp, 'Original request is to /temp') t.ok(hits.temp_landing, 'Forward to temporary landing URL') t.equal(body, 'GET temp_landing', 'Got temporary landing content') t.end() }) }) tape('should follow post redirects when followallredirects true and followOriginalHttpMethod is enabled', function (t) { hits = {} request.post({ uri: s.url + '/temp', followAllRedirects: true, followOriginalHttpMethod: true, jar: jar, headers: { cookie: 'foo=bar' } }, function (err, res, body) { t.equal(err, null) t.equal(res.statusCode, 200) t.ok(hits.temp, 'Original request is to /temp') t.ok(hits.temp_landing, 'Forward to temporary landing URL') t.equal(body, 'POST temp_landing', 'Got temporary landing content') t.end() }) }) tape('should not follow post redirects when followallredirects false', function (t) { hits = {} request.post({ uri: s.url + '/temp', followAllRedirects: false, jar: jar, headers: { cookie: 'foo=bar' } }, function (err, res, body) { t.equal(err, null) t.equal(res.statusCode, 301) t.ok(hits.temp, 'Original request is to /temp') t.ok(!hits.temp_landing, 'No chasing the redirect') t.equal(res.statusCode, 301, 'Response is the bounce itself') t.end() }) }) tape('should not follow delete redirects by default', function (t) { hits = {} request.del(s.url + '/temp', { jar: jar, headers: { cookie: 'foo=bar' } }, function (err, res, body) { t.equal(err, null) t.ok(res.statusCode >= 301 && res.statusCode < 400, 'Status is a redirect') t.ok(hits.temp, 'Original request is to /temp') t.ok(!hits.temp_landing, 'No chasing the redirect when delete') t.equal(res.statusCode, 301, 'Response is the bounce itself') t.end() }) }) tape('should not follow delete redirects even if followredirect is set to true', function (t) { hits = {} request.del(s.url + '/temp', { followRedirect: true, jar: jar, headers: { cookie: 'foo=bar' } }, function (err, res, body) { t.equal(err, null) t.equal(res.statusCode, 301) t.ok(hits.temp, 'Original request is to /temp') t.ok(!hits.temp_landing, 'No chasing the redirect when delete') t.equal(res.statusCode, 301, 'Response is the bounce itself') t.end() }) }) tape('should follow delete redirects when followallredirects true', function (t) { hits = {} request.del(s.url + '/temp', { followAllRedirects: true, jar: jar, headers: { cookie: 'foo=bar' } }, function (err, res, body) { t.equal(err, null) t.equal(res.statusCode, 200) t.ok(hits.temp, 'Original request is to /temp') t.ok(hits.temp_landing, 'Forward to temporary landing URL') t.equal(body, 'GET temp_landing', 'Got temporary landing content') t.end() }) }) tape('should follow 307 delete redirects when followallredirects true', function (t) { hits = {} request.del(s.url + '/fwd', { followAllRedirects: true, jar: jar, headers: { cookie: 'foo=bar' } }, function (err, res, body) { t.equal(err, null) t.equal(res.statusCode, 200) t.ok(hits.fwd, 'Original request is to /fwd') t.ok(hits.fwd_landing, 'Forward to temporary landing URL') t.equal(body, 'DELETE fwd_landing', 'Got temporary landing content') t.end() }) }) tape('double bounce', function (t) { hits = {} request({ uri: s.url + '/double', jar: jar, headers: { cookie: 'foo=bar' } }, function (err, res, body) { t.equal(err, null) t.equal(res.statusCode, 200) t.ok(hits.double, 'Original request is to /double') t.ok(hits.double_2, 'Forward to temporary landing URL') t.ok(hits.double_landing, 'Forward to landing URL') t.equal(body, 'GET double_landing', 'Got temporary landing content') t.end() }) }) tape('double bounce terminated after first redirect', function (t) { function filterDouble (response) { return (response.headers.location || '').indexOf('double_2') === -1 } hits = {} request({ uri: s.url + '/double', jar: jar, headers: { cookie: 'foo=bar' }, followRedirect: filterDouble }, function (err, res, body) { t.equal(err, null) t.equal(res.statusCode, 301) t.ok(hits.double, 'Original request is to /double') t.equal(res.headers.location, s.url + '/double_2', 'Current location should be ' + s.url + '/double_2') t.end() }) }) tape('triple bounce terminated after second redirect', function (t) { function filterTreble (response) { return (response.headers.location || '').indexOf('treble_3') === -1 } hits = {} request({ uri: s.url + '/treble', jar: jar, headers: { cookie: 'foo=bar' }, followRedirect: filterTreble }, function (err, res, body) { t.equal(err, null) t.equal(res.statusCode, 301) t.ok(hits.treble, 'Original request is to /treble') t.equal(res.headers.location, s.url + '/treble_3', 'Current location should be ' + s.url + '/treble_3') t.end() }) }) tape('http to https redirect', function (t) { hits = {} request.get({ uri: require('url').parse(s.url + '/ssl'), rejectUnauthorized: false }, function (err, res, body) { t.equal(err, null) t.equal(res.statusCode, 200) t.equal(body, 'SSL', 'Got SSL redirect') t.end() }) }) tape('should have referer header by default when following redirect', function (t) { request.post({ uri: s.url + '/temp', jar: jar, followAllRedirects: true, headers: { cookie: 'foo=bar' } }, function (err, res, body) { t.equal(err, null) t.equal(res.statusCode, 200) t.end() }) .on('redirect', function () { t.equal(this.headers.referer, s.url + '/temp') }) }) tape('should not have referer header when removeRefererHeader is true', function (t) { request.post({ uri: s.url + '/temp', jar: jar, followAllRedirects: true, removeRefererHeader: true, headers: { cookie: 'foo=bar' } }, function (err, res, body) { t.equal(err, null) t.equal(res.statusCode, 200) t.end() }) .on('redirect', function () { t.equal(this.headers.referer, undefined) }) }) tape('should preserve referer header set in the initial request when removeRefererHeader is true', function (t) { request.post({ uri: s.url + '/temp', jar: jar, followAllRedirects: true, removeRefererHeader: true, headers: { cookie: 'foo=bar', referer: 'http://awesome.com' } }, function (err, res, body) { t.equal(err, null) t.equal(res.statusCode, 200) t.end() }) .on('redirect', function () { t.equal(this.headers.referer, 'http://awesome.com') }) }) tape('should use same agent class on redirect', function (t) { var agent var calls = 0 var agentOptions = {} function FakeAgent (agentOptions) { var createConnection agent = new http.Agent(agentOptions) createConnection = agent.createConnection agent.createConnection = function () { calls++ return createConnection.apply(agent, arguments) } return agent } hits = {} request.get({ uri: s.url + '/temp', jar: jar, headers: { cookie: 'foo=bar' }, agentOptions: agentOptions, agentClass: FakeAgent }, function (err, res, body) { t.equal(err, null) t.equal(res.statusCode, 200) t.equal(body, 'GET temp_landing', 'Got temporary landing content') t.equal(calls, 2) t.ok(this.agent === agent, 'Reinstantiated the user-specified agent') t.ok(this.agentOptions === agentOptions, 'Reused agent options') t.end() }) }) tape('cleanup', function (t) { s.destroy(function () { ss.destroy(function () { t.end() }) }) }) request-2.88.1/tests/test-rfc3986.js000066400000000000000000000041401333333633500170670ustar00rootroot00000000000000'use strict' var http = require('http') var request = require('../index') var tape = require('tape') function runTest (t, options) { var server = http.createServer(function (req, res) { var data = '' req.setEncoding('utf8') req.on('data', function (d) { data += d }) req.on('end', function () { if (options.qs) { t.equal(req.url, '/?rfc3986=%21%2A%28%29%27') } t.equal(data, options._expectBody) res.writeHead(200) res.end('done') }) }) server.listen(0, function () { var port = this.address().port request.post('http://localhost:' + port, options, function (err, res, body) { t.equal(err, null) server.close(function () { t.end() }) }) }) } var bodyEscaped = 'rfc3986=%21%2A%28%29%27' var bodyJson = '{"rfc3986":"!*()\'"}' var cases = [ { _name: 'qs', qs: {rfc3986: "!*()'"}, _expectBody: '' }, { _name: 'qs + json', qs: {rfc3986: "!*()'"}, json: true, _expectBody: '' }, { _name: 'form', form: {rfc3986: "!*()'"}, _expectBody: bodyEscaped }, { _name: 'form + json', form: {rfc3986: "!*()'"}, json: true, _expectBody: bodyEscaped }, { _name: 'qs + form', qs: {rfc3986: "!*()'"}, form: {rfc3986: "!*()'"}, _expectBody: bodyEscaped }, { _name: 'qs + form + json', qs: {rfc3986: "!*()'"}, form: {rfc3986: "!*()'"}, json: true, _expectBody: bodyEscaped }, { _name: 'body + header + json', headers: {'content-type': 'application/x-www-form-urlencoded; charset=UTF-8'}, body: "rfc3986=!*()'", json: true, _expectBody: bodyEscaped }, { _name: 'body + json', body: {rfc3986: "!*()'"}, json: true, _expectBody: bodyJson }, { _name: 'json object', json: {rfc3986: "!*()'"}, _expectBody: bodyJson } ] var libs = ['qs', 'querystring'] libs.forEach(function (lib) { cases.forEach(function (options) { options.useQuerystring = (lib === 'querystring') tape(lib + ' rfc3986 ' + options._name, function (t) { runTest(t, options) }) }) }) request-2.88.1/tests/test-stream.js000066400000000000000000000014511333333633500172600ustar00rootroot00000000000000var fs = require('fs') var path = require('path') var http = require('http') var tape = require('tape') var request = require('../') var server tape('before', function (t) { server = http.createServer() server.on('request', function (req, res) { req.pipe(res) }) server.listen(0, function () { server.url = 'http://localhost:' + this.address().port t.end() }) }) tape('request body stream', function (t) { var fpath = path.join(__dirname, 'unicycle.jpg') var input = fs.createReadStream(fpath, {highWaterMark: 1000}) request({ uri: server.url, method: 'POST', body: input, encoding: null }, function (err, res, body) { t.error(err) t.equal(body.length, fs.statSync(fpath).size) t.end() }) }) tape('after', function (t) { server.close(t.end) }) request-2.88.1/tests/test-timeout.js000066400000000000000000000145701333333633500174610ustar00rootroot00000000000000'use strict' function checkErrCode (t, err) { t.notEqual(err, null) t.ok(err.code === 'ETIMEDOUT' || err.code === 'ESOCKETTIMEDOUT', 'Error ETIMEDOUT or ESOCKETTIMEDOUT') } function checkEventHandlers (t, socket) { var connectListeners = socket.listeners('connect') var found = false for (var i = 0; i < connectListeners.length; ++i) { var fn = connectListeners[i] if (typeof fn === 'function' && fn.name === 'onReqSockConnect') { found = true break } } t.ok(!found, 'Connect listener should not exist') } var server = require('./server') var request = require('../index') var tape = require('tape') var s = server.createServer() // Request that waits for 200ms s.on('/timeout', function (req, res) { setTimeout(function () { res.writeHead(200, {'content-type': 'text/plain'}) res.write('waited') res.end() }, 200) }) tape('setup', function (t) { s.listen(0, function () { t.end() }) }) tape('should timeout', function (t) { var shouldTimeout = { url: s.url + '/timeout', timeout: 100 } request(shouldTimeout, function (err, res, body) { checkErrCode(t, err) t.end() }) }) tape('should set connect to false', function (t) { var shouldTimeout = { url: s.url + '/timeout', timeout: 100 } request(shouldTimeout, function (err, res, body) { checkErrCode(t, err) t.ok(err.connect === false, 'Read Timeout Error should set \'connect\' property to false') t.end() }) }) tape('should timeout with events', function (t) { t.plan(3) var shouldTimeoutWithEvents = { url: s.url + '/timeout', timeout: 100 } var eventsEmitted = 0 request(shouldTimeoutWithEvents) .on('error', function (err) { eventsEmitted++ t.equal(1, eventsEmitted) checkErrCode(t, err) }) }) tape('should not timeout', function (t) { var shouldntTimeout = { url: s.url + '/timeout', timeout: 1200 } var socket request(shouldntTimeout, function (err, res, body) { t.equal(err, null) t.equal(body, 'waited') checkEventHandlers(t, socket) t.end() }).on('socket', function (socket_) { socket = socket_ }) }) tape('no timeout', function (t) { var noTimeout = { url: s.url + '/timeout' } request(noTimeout, function (err, res, body) { t.equal(err, null) t.equal(body, 'waited') t.end() }) }) tape('negative timeout', function (t) { // should be treated a zero or the minimum delay var negativeTimeout = { url: s.url + '/timeout', timeout: -1000 } request(negativeTimeout, function (err, res, body) { // Only verify error if it is set, since using a timeout value of 0 can lead // to inconsistent results, depending on a variety of factors if (err) { checkErrCode(t, err) } t.end() }) }) tape('float timeout', function (t) { // should be rounded by setTimeout anyway var floatTimeout = { url: s.url + '/timeout', timeout: 100.76 } request(floatTimeout, function (err, res, body) { checkErrCode(t, err) t.end() }) }) // We need a destination that will not immediately return a TCP Reset // packet. StackOverflow suggests these hosts: // (https://stackoverflow.com/a/904609/329700) var nonRoutable = [ '10.255.255.1', '10.0.0.0', '192.168.0.0', '192.168.255.255', '172.16.0.0', '172.31.255.255' ] var nrIndex = 0 function getNonRoutable () { var ip = nonRoutable[nrIndex] if (!ip) { throw new Error('No more non-routable addresses') } ++nrIndex return ip } tape('connect timeout', function tryConnect (t) { var tarpitHost = 'http://' + getNonRoutable() var shouldConnectTimeout = { url: tarpitHost + '/timeout', timeout: 100 } var socket request(shouldConnectTimeout, function (err) { t.notEqual(err, null) if (err.code === 'ENETUNREACH' && nrIndex < nonRoutable.length) { // With some network configurations, some addresses will be reported as // unreachable immediately (before the timeout occurs). In those cases, // try other non-routable addresses before giving up. return tryConnect(t) } checkErrCode(t, err) t.ok(err.connect === true, 'Connect Timeout Error should set \'connect\' property to true') checkEventHandlers(t, socket) nrIndex = 0 t.end() }).on('socket', function (socket_) { socket = socket_ }) }) tape('connect timeout with non-timeout error', function tryConnect (t) { var tarpitHost = 'http://' + getNonRoutable() var shouldConnectTimeout = { url: tarpitHost + '/timeout', timeout: 1000 } var socket request(shouldConnectTimeout, function (err) { t.notEqual(err, null) if (err.code === 'ENETUNREACH' && nrIndex < nonRoutable.length) { // With some network configurations, some addresses will be reported as // unreachable immediately (before the timeout occurs). In those cases, // try other non-routable addresses before giving up. return tryConnect(t) } // Delay the check since the 'connect' handler is removed in a separate // 'error' handler which gets triggered after this callback setImmediate(function () { checkEventHandlers(t, socket) nrIndex = 0 t.end() }) }).on('socket', function (socket_) { socket = socket_ setImmediate(function () { socket.emit('error', new Error('Fake Error')) }) }) }) tape('request timeout with keep-alive connection', function (t) { var Agent = require('http').Agent var agent = new Agent({ keepAlive: true }) var firstReq = { url: s.url + '/timeout', agent: agent } request(firstReq, function (err) { // We should now still have a socket open. For the second request we should // see a request timeout on the active socket ... t.equal(err, null) var shouldReqTimeout = { url: s.url + '/timeout', timeout: 100, agent: agent } request(shouldReqTimeout, function (err) { checkErrCode(t, err) t.ok(err.connect === false, 'Error should have been a request timeout error') t.end() }).on('socket', function (socket) { var isConnecting = socket._connecting || socket.connecting t.ok(isConnecting !== true, 'Socket should already be connected') }) }).on('socket', function (socket) { var isConnecting = socket._connecting || socket.connecting t.ok(isConnecting === true, 'Socket should be new') }) }) tape('cleanup', function (t) { s.close(function () { t.end() }) }) request-2.88.1/tests/test-timing.js000066400000000000000000000116061333333633500172570ustar00rootroot00000000000000'use strict' var server = require('./server') var request = require('../index') var tape = require('tape') var http = require('http') var plainServer = server.createServer() var redirectMockTime = 10 tape('setup', function (t) { plainServer.listen(0, function () { plainServer.on('/', function (req, res) { res.writeHead(200) res.end('plain') }) plainServer.on('/redir', function (req, res) { // fake redirect delay to ensure strong signal for rollup check setTimeout(function () { res.writeHead(301, { 'location': 'http://localhost:' + plainServer.port + '/' }) res.end() }, redirectMockTime) }) t.end() }) }) tape('non-redirected request is timed', function (t) { var options = {time: true} var start = new Date().getTime() var r = request('http://localhost:' + plainServer.port + '/', options, function (err, res, body) { var end = new Date().getTime() t.equal(err, null) t.equal(typeof res.elapsedTime, 'number') t.equal(typeof res.responseStartTime, 'number') t.equal(typeof res.timingStart, 'number') t.equal((res.timingStart >= start), true) t.equal(typeof res.timings, 'object') t.equal((res.elapsedTime > 0), true) t.equal((res.elapsedTime <= (end - start)), true) t.equal((res.responseStartTime > r.startTime), true) t.equal((res.timings.socket >= 0), true) t.equal((res.timings.lookup >= res.timings.socket), true) t.equal((res.timings.connect >= res.timings.lookup), true) t.equal((res.timings.response >= res.timings.connect), true) t.equal((res.timings.end >= res.timings.response), true) t.equal(typeof res.timingPhases, 'object') t.equal((res.timingPhases.wait >= 0), true) t.equal((res.timingPhases.dns >= 0), true) t.equal((res.timingPhases.tcp >= 0), true) t.equal((res.timingPhases.firstByte > 0), true) t.equal((res.timingPhases.download > 0), true) t.equal((res.timingPhases.total > 0), true) t.equal((res.timingPhases.total <= (end - start)), true) // validate there are no unexpected properties var propNames = [] for (var propName in res.timings) { if (res.timings.hasOwnProperty(propName)) { propNames.push(propName) } } t.deepEqual(propNames, ['socket', 'lookup', 'connect', 'response', 'end']) propNames = [] for (propName in res.timingPhases) { if (res.timingPhases.hasOwnProperty(propName)) { propNames.push(propName) } } t.deepEqual(propNames, ['wait', 'dns', 'tcp', 'firstByte', 'download', 'total']) t.end() }) }) tape('redirected request is timed with rollup', function (t) { var options = {time: true} var r = request('http://localhost:' + plainServer.port + '/redir', options, function (err, res, body) { t.equal(err, null) t.equal(typeof res.elapsedTime, 'number') t.equal(typeof res.responseStartTime, 'number') t.equal((res.elapsedTime > 0), true) t.equal((res.responseStartTime > 0), true) t.equal((res.elapsedTime > redirectMockTime), true) t.equal((res.responseStartTime > r.startTime), true) t.end() }) }) tape('keepAlive is timed', function (t) { var agent = new http.Agent({ keepAlive: true }) var options = { time: true, agent: agent } var start1 = new Date().getTime() request('http://localhost:' + plainServer.port + '/', options, function (err1, res1, body1) { var end1 = new Date().getTime() // ensure the first request's timestamps look ok t.equal((res1.timingStart >= start1), true) t.equal((start1 <= end1), true) t.equal((res1.timings.socket >= 0), true) t.equal((res1.timings.lookup >= res1.timings.socket), true) t.equal((res1.timings.connect >= res1.timings.lookup), true) t.equal((res1.timings.response >= res1.timings.connect), true) // open a second request with the same agent so we re-use the same connection var start2 = new Date().getTime() request('http://localhost:' + plainServer.port + '/', options, function (err2, res2, body2) { var end2 = new Date().getTime() // ensure the second request's timestamps look ok t.equal((res2.timingStart >= start2), true) t.equal((start2 <= end2), true) // ensure socket==lookup==connect for the second request t.equal((res2.timings.socket >= 0), true) t.equal((res2.timings.lookup === res2.timings.socket), true) t.equal((res2.timings.connect === res2.timings.lookup), true) t.equal((res2.timings.response >= res2.timings.connect), true) // explicitly shut down the agent if (typeof agent.destroy === 'function') { agent.destroy() } else { // node < 0.12 Object.keys(agent.sockets).forEach(function (name) { agent.sockets[name].forEach(function (socket) { socket.end() }) }) } t.end() }) }) }) tape('cleanup', function (t) { plainServer.close(function () { t.end() }) }) request-2.88.1/tests/test-toJSON.js000066400000000000000000000016551333333633500171070ustar00rootroot00000000000000'use strict' var request = require('../index') var http = require('http') var tape = require('tape') var s = http.createServer(function (req, resp) { resp.statusCode = 200 resp.end('asdf') }) tape('setup', function (t) { s.listen(0, function () { s.url = 'http://localhost:' + this.address().port t.end() }) }) tape('request().toJSON()', function (t) { var r = request({ url: s.url, headers: { foo: 'bar' } }, function (err, res) { var jsonR = JSON.parse(JSON.stringify(r)) var jsonRes = JSON.parse(JSON.stringify(res)) t.equal(err, null) t.equal(jsonR.uri.href, r.uri.href) t.equal(jsonR.method, r.method) t.equal(jsonR.headers.foo, r.headers.foo) t.equal(jsonRes.statusCode, res.statusCode) t.equal(jsonRes.body, res.body) t.equal(jsonRes.headers.date, res.headers.date) t.end() }) }) tape('cleanup', function (t) { s.close(function () { t.end() }) }) request-2.88.1/tests/test-tunnel.js000066400000000000000000000247721333333633500173050ustar00rootroot00000000000000'use strict' var server = require('./server') var tape = require('tape') var request = require('../index') var https = require('https') var net = require('net') var fs = require('fs') var path = require('path') var util = require('util') var url = require('url') var destroyable = require('server-destroy') var events = [] var caFile = path.resolve(__dirname, 'ssl/ca/ca.crt') var ca = fs.readFileSync(caFile) var clientCert = fs.readFileSync(path.resolve(__dirname, 'ssl/ca/client.crt')) var clientKey = fs.readFileSync(path.resolve(__dirname, 'ssl/ca/client-enc.key')) var clientPassword = 'password' var sslOpts = { key: path.resolve(__dirname, 'ssl/ca/localhost.key'), cert: path.resolve(__dirname, 'ssl/ca/localhost.crt') } var mutualSSLOpts = { key: path.resolve(__dirname, 'ssl/ca/localhost.key'), cert: path.resolve(__dirname, 'ssl/ca/localhost.crt'), ca: caFile, requestCert: true, rejectUnauthorized: true } // this is needed for 'https over http, tunnel=false' test // from https://github.com/coolaj86/node-ssl-root-cas/blob/v1.1.9-beta/ssl-root-cas.js#L4267-L4281 var httpsOpts = https.globalAgent.options httpsOpts.ca = httpsOpts.ca || [] httpsOpts.ca.push(ca) var s = server.createServer() var ss = server.createSSLServer(sslOpts) var ss2 = server.createSSLServer(mutualSSLOpts) // XXX when tunneling https over https, connections get left open so the server // doesn't want to close normally (and same issue with http server on v0.8.x) destroyable(s) destroyable(ss) destroyable(ss2) function event () { events.push(util.format.apply(null, arguments)) } function setListeners (server, type) { server.on('/', function (req, res) { event('%s response', type) res.end(type + ' ok') }) server.on('request', function (req, res) { if (/^https?:/.test(req.url)) { // This is a proxy request var dest = req.url.split(':')[0] // Is it a redirect? var match = req.url.match(/\/redirect\/(https?)$/) if (match) { dest += '->' + match[1] } event('%s proxy to %s', type, dest) request(req.url, { followRedirect: false }).pipe(res) } }) server.on('/redirect/http', function (req, res) { event('%s redirect to http', type) res.writeHead(301, { location: s.url }) res.end() }) server.on('/redirect/https', function (req, res) { event('%s redirect to https', type) res.writeHead(301, { location: ss.url }) res.end() }) server.on('connect', function (req, client, head) { var u = url.parse(req.url) var server = net.connect(u.host, u.port, function () { event('%s connect to %s', type, req.url) client.write('HTTP/1.1 200 Connection established\r\n\r\n') client.pipe(server) server.write(head) server.pipe(client) }) }) } setListeners(s, 'http') setListeners(ss, 'https') setListeners(ss2, 'https') // monkey-patch since you can't set a custom certificate authority for the // proxy in tunnel-agent (this is necessary for "* over https" tests) var customCaCount = 0 var httpsRequestOld = https.request https.request = function (options) { if (customCaCount) { options.ca = ca customCaCount-- } return httpsRequestOld.apply(this, arguments) } function runTest (name, opts, expected) { tape(name, function (t) { opts.ca = ca if (opts.proxy === ss.url) { customCaCount = (opts.url === ss.url ? 2 : 1) } request(opts, function (err, res, body) { event(err ? 'err ' + err.message : res.statusCode + ' ' + body) t.deepEqual(events, expected) events = [] t.end() }) }) } function addTests () { // HTTP OVER HTTP runTest('http over http, tunnel=true', { url: s.url, proxy: s.url, tunnel: true }, [ 'http connect to localhost:' + s.port, 'http response', '200 http ok' ]) runTest('http over http, tunnel=false', { url: s.url, proxy: s.url, tunnel: false }, [ 'http proxy to http', 'http response', '200 http ok' ]) runTest('http over http, tunnel=default', { url: s.url, proxy: s.url }, [ 'http proxy to http', 'http response', '200 http ok' ]) // HTTP OVER HTTPS runTest('http over https, tunnel=true', { url: s.url, proxy: ss.url, tunnel: true }, [ 'https connect to localhost:' + s.port, 'http response', '200 http ok' ]) runTest('http over https, tunnel=false', { url: s.url, proxy: ss.url, tunnel: false }, [ 'https proxy to http', 'http response', '200 http ok' ]) runTest('http over https, tunnel=default', { url: s.url, proxy: ss.url }, [ 'https proxy to http', 'http response', '200 http ok' ]) // HTTPS OVER HTTP runTest('https over http, tunnel=true', { url: ss.url, proxy: s.url, tunnel: true }, [ 'http connect to localhost:' + ss.port, 'https response', '200 https ok' ]) runTest('https over http, tunnel=false', { url: ss.url, proxy: s.url, tunnel: false }, [ 'http proxy to https', 'https response', '200 https ok' ]) runTest('https over http, tunnel=default', { url: ss.url, proxy: s.url }, [ 'http connect to localhost:' + ss.port, 'https response', '200 https ok' ]) // HTTPS OVER HTTPS runTest('https over https, tunnel=true', { url: ss.url, proxy: ss.url, tunnel: true }, [ 'https connect to localhost:' + ss.port, 'https response', '200 https ok' ]) runTest('https over https, tunnel=false', { url: ss.url, proxy: ss.url, tunnel: false, pool: false // must disable pooling here or Node.js hangs }, [ 'https proxy to https', 'https response', '200 https ok' ]) runTest('https over https, tunnel=default', { url: ss.url, proxy: ss.url }, [ 'https connect to localhost:' + ss.port, 'https response', '200 https ok' ]) // HTTP->HTTP OVER HTTP runTest('http->http over http, tunnel=true', { url: s.url + '/redirect/http', proxy: s.url, tunnel: true }, [ 'http connect to localhost:' + s.port, 'http redirect to http', 'http connect to localhost:' + s.port, 'http response', '200 http ok' ]) runTest('http->http over http, tunnel=false', { url: s.url + '/redirect/http', proxy: s.url, tunnel: false }, [ 'http proxy to http->http', 'http redirect to http', 'http proxy to http', 'http response', '200 http ok' ]) runTest('http->http over http, tunnel=default', { url: s.url + '/redirect/http', proxy: s.url }, [ 'http proxy to http->http', 'http redirect to http', 'http proxy to http', 'http response', '200 http ok' ]) // HTTP->HTTPS OVER HTTP runTest('http->https over http, tunnel=true', { url: s.url + '/redirect/https', proxy: s.url, tunnel: true }, [ 'http connect to localhost:' + s.port, 'http redirect to https', 'http connect to localhost:' + ss.port, 'https response', '200 https ok' ]) runTest('http->https over http, tunnel=false', { url: s.url + '/redirect/https', proxy: s.url, tunnel: false }, [ 'http proxy to http->https', 'http redirect to https', 'http proxy to https', 'https response', '200 https ok' ]) runTest('http->https over http, tunnel=default', { url: s.url + '/redirect/https', proxy: s.url }, [ 'http proxy to http->https', 'http redirect to https', 'http connect to localhost:' + ss.port, 'https response', '200 https ok' ]) // HTTPS->HTTP OVER HTTP runTest('https->http over http, tunnel=true', { url: ss.url + '/redirect/http', proxy: s.url, tunnel: true }, [ 'http connect to localhost:' + ss.port, 'https redirect to http', 'http connect to localhost:' + s.port, 'http response', '200 http ok' ]) runTest('https->http over http, tunnel=false', { url: ss.url + '/redirect/http', proxy: s.url, tunnel: false }, [ 'http proxy to https->http', 'https redirect to http', 'http proxy to http', 'http response', '200 http ok' ]) runTest('https->http over http, tunnel=default', { url: ss.url + '/redirect/http', proxy: s.url }, [ 'http connect to localhost:' + ss.port, 'https redirect to http', 'http proxy to http', 'http response', '200 http ok' ]) // HTTPS->HTTPS OVER HTTP runTest('https->https over http, tunnel=true', { url: ss.url + '/redirect/https', proxy: s.url, tunnel: true }, [ 'http connect to localhost:' + ss.port, 'https redirect to https', 'http connect to localhost:' + ss.port, 'https response', '200 https ok' ]) runTest('https->https over http, tunnel=false', { url: ss.url + '/redirect/https', proxy: s.url, tunnel: false }, [ 'http proxy to https->https', 'https redirect to https', 'http proxy to https', 'https response', '200 https ok' ]) runTest('https->https over http, tunnel=default', { url: ss.url + '/redirect/https', proxy: s.url }, [ 'http connect to localhost:' + ss.port, 'https redirect to https', 'http connect to localhost:' + ss.port, 'https response', '200 https ok' ]) // MUTUAL HTTPS OVER HTTP runTest('mutual https over http, tunnel=true', { url: ss2.url, proxy: s.url, tunnel: true, cert: clientCert, key: clientKey, passphrase: clientPassword }, [ 'http connect to localhost:' + ss2.port, 'https response', '200 https ok' ]) // XXX causes 'Error: socket hang up' // runTest('mutual https over http, tunnel=false', { // url : ss2.url, // proxy : s.url, // tunnel : false, // cert : clientCert, // key : clientKey, // passphrase : clientPassword // }, [ // 'http connect to localhost:' + ss2.port, // 'https response', // '200 https ok' // ]) runTest('mutual https over http, tunnel=default', { url: ss2.url, proxy: s.url, cert: clientCert, key: clientKey, passphrase: clientPassword }, [ 'http connect to localhost:' + ss2.port, 'https response', '200 https ok' ]) } tape('setup', function (t) { s.listen(0, function () { ss.listen(0, function () { ss2.listen(0, 'localhost', function () { addTests() tape('cleanup', function (t) { s.destroy(function () { ss.destroy(function () { ss2.destroy(function () { t.end() }) }) }) }) t.end() }) }) }) }) request-2.88.1/tests/test-unix.js000066400000000000000000000036511333333633500167540ustar00rootroot00000000000000'use strict' var request = require('../index') var http = require('http') var fs = require('fs') var rimraf = require('rimraf') var assert = require('assert') var tape = require('tape') var url = require('url') var rawPath = [null, 'raw', 'path'].join('/') var queryPath = [null, 'query', 'path'].join('/') var searchString = '?foo=bar' var socket = [__dirname, 'tmp-socket'].join('/') var expectedBody = 'connected' var statusCode = 200 rimraf.sync(socket) var s = http.createServer(function (req, res) { var incomingUrl = url.parse(req.url) switch (incomingUrl.pathname) { case rawPath: assert.equal(incomingUrl.pathname, rawPath, 'requested path is sent to server') break case queryPath: assert.equal(incomingUrl.pathname, queryPath, 'requested path is sent to server') assert.equal(incomingUrl.search, searchString, 'query string is sent to server') break default: assert(false, 'A valid path was requested') } res.statusCode = statusCode res.end(expectedBody) }) tape('setup', function (t) { s.listen(socket, function () { t.end() }) }) tape('unix socket connection', function (t) { request('http://unix:' + socket + ':' + rawPath, function (err, res, body) { t.equal(err, null, 'no error in connection') t.equal(res.statusCode, statusCode, 'got HTTP 200 OK response') t.equal(body, expectedBody, 'expected response body is received') t.end() }) }) tape('unix socket connection with qs', function (t) { request({ uri: 'http://unix:' + socket + ':' + queryPath, qs: { foo: 'bar' } }, function (err, res, body) { t.equal(err, null, 'no error in connection') t.equal(res.statusCode, statusCode, 'got HTTP 200 OK response') t.equal(body, expectedBody, 'expected response body is received') t.end() }) }) tape('cleanup', function (t) { s.close(function () { fs.unlink(socket, function () { t.end() }) }) }) request-2.88.1/tests/unicycle.jpg000066400000000000000000000465361333333633500170040ustar00rootroot00000000000000JFIFHH(ICC_PROFILEappl scnrRGB XYZ acspAPPLappl-appl rXYZgXYZbXYZ0wtptDchadX,rTRCgTRCbTRCdesc=cprtAdscmXYZ tK>XYZ Zs&XYZ (W3XYZ Rsf32 B&lcurv3descCamera RGB ProfileCamera RGB Profilemluc enUS$esES,LdaDK4deDE,fiFI(frFU<itIT,rnlNL$noNO xptBR(JsvSE*jaJPkoKR2zhTW2zhCNKameran RGB-profiiliRGB-profil fr Kamera000 RGB 000000exOMvj_ RGB r_icϏPerfil RGB para CmaraRGB-kameraprofilRGB-Profil fr Kamerasvg: RGB cϏeNRGB-beskrivelse til KameraRGB-profiel CameratT| RGB \ |Perfil RGB de CmeraProfilo RGB FotocameraCamera RGB ProfileProfil RVB de l appareil-phototextCopyright 2003 Apple Computer Inc., all rights reserved.ExifMM* z(12iCASIO COMPUTER CO.,LTD QV-R41 HH1.00 2005:06:21 01:44:12&"0221.BV ^f  n0100v'  2005:06:21 01:44:122005:06:21 01:44:12wC     C  " }!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz w!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ?4J;k2;qggyzPnaoTzU1He8aqZx\ sH.֦:Ÿr)iA ڝRN<+cE|~*W&UGK8 Oaq%[[Q\MΙrq>H= \t@Zgj>VfT}@6m#RҮIE{ q4D2+'ƪ}-|c=empG>߅~-W TMSX~= Լ-;Ơ>{ L['eSЍЭd`sz,x1nGFWF)# _ĭK¹cxn~e3Z3#] ֮:sR* `QךOϳ[NF<k|CIERw(`OC]GϋVJ]SsX|0YJ9C",UAQRVhᱰs׿^-f=>Tܴ.>s\أ@ڐ%d!cSө\^ x׽d&r^`xV?v6]lc'xέT9P͌6xY<%YX1TU85mw_%te<}4= Xn>XkM{v7 sm5kɵV_$&H2ϵt 5Ñm+u&H$1N o4{%k n,8Vr:phc<%doZdrx|>˞P}-#9 e@+T:u퟈4X0$]D)Ł\'~c6 ??o8+"8ngѡ3YIgrڥFo@$zrZCĤ (u78ҶbojjuؠlmyesHutخOO3]x<LNjfUk*{+Qԉzkî~"iSэnGd-W+/%VƷqs$mSaz/"ikQULCk2 =܁ξy?-tx%༚w8'eh؎Y KxM,-ȟqcױOU]7Z[ VݝC 9$W4t-'ϕ 'vr@<`׍*nNZTD-ޣld@d]]#&S td$eH^SPY a̅fܤc+dɢ\dmsxVJ#)Ez6|2(ez7T ?9cK!WϫǧZG֦)sYys9qIj4F_ #|[۟NXb/i a`u(|2Njy7зpzmH80Z֗7-WSzh"Iyn!|CȯEPVCw eΏOQCsvψVmm>TRGsծThv"-Ju5o=tRyC꧷ӥz?ÏNy\@0pN/p~t j^Z3s(?)T1mV(I$yqc 9;x\DTU#>|ygN;db7ұ- Ǫ,+Ef H^-)ӵ+8 i6zMEea4; eJ m~|C9E'}O|/YVmӢ'nW n?g/xnhx^oaeI( lqW3׵Cykc4;@^q|TZ=Ob%tޭ B"tEi2nc ~"k$7OȇBZ2n<x ax6~_[<ߥ׏)9:_׿-%ZRךv:.hR# )-"I֍ky.v[>VJ)$D}3#Vs俏>!5]ZXIbϖ=$xZz<6:3,eBȅK>R_"e,hLs!s޽ÒNInq 꽖5mNo34+a PS'tO|o6a sœ+ҽ cOEYx*K-`6k)#^kukTΔő; Oְ[|-upTVΏi{j֗Aw m rbR$r:r[-d}0|#Ծ>5[4JH, `y;^٫Ǟ4oᘖyM!\A?/x_V<+ Fӡq(O|jN*TNVkxE}*i1Q"kC5͞,[Gw!l$y+JDm bmG$t+{ T)qhđH6yjU'$r|UHsׇ:Jǚךͤp[[Z_n%[2an~^ska(R^[X2/;]/., ЪjTNm  [*+˵ k6ޑt0ܪsBT3קT>flCtytuدdeky#;Xҥ]RQ]2yxZ44"mBnϜ4@pO}9KOxMa{-2lHdĕsNr c_٧x襯b?^'SQ>"uX5F,d(x[[>PkH *ez;;0oD9+ J:x?5fG㧁9: I^W%|nO¡_2=Bp)O{5?w6%T:lA R Z#-{V\o2Ism?"hY?#R0}MG+&Qmtm?j̴b~7،yFc?wҴIǡWBz|ՕeOT~h.oNJ>$6Ye5E#lnَԂ=|ӼIk}sF`a` a+FH2:J*c;wX3nZznxNo,-W"4AQE ϠS^ Kwg ˹m,ԳUgzZ.mwdld-/q܊쯾g s j*pH#gxY)|ጣr..#1 eqBq;rvq'ԏǚK񯌬t>/j̑ȋiurȥ* _ 藰E 6/&I30|X&LMK[{>2މ9)xwfĤ h3mz@+2 sWK'+t2n bP+|Y=GP.!!r}[>WȠH;H :okقlX ?CW-< ,TϽ}[q1lMO‰A+azxG)|LG+ H1=3ׁ!Q~$Ixa!qo$|F?\G_? ?|LOoG&x_L_/-$F0d8?;!D>ث!';u <ȶ&J?_ <5({Cƫ*{q??@hZu>gc#5 }58fRc y4۟r4K| ɒ9+tUWZ%q!x*OvŪNJ5q= /ua3 i~Qm$de }*R@{U[h=<9cm-Hd$U'gqL$ž6a'}J* ¯ qISgEa ~{Vމkeqszम34ͻ{ܓ~]$|e/y֧/ ǧ2+5l/'9o|e[Kn,toY{u>2 kddy6耪+l/^ҿh C/h0iv! }r1$.}0~ҵ3sLڗlZDRebL2:c= fTG=ji~cXto$.ّ_'i\V煮>8N%܎]Ā3Th_7Unm\n/.M>lW/~jwa'[ +tZG- ZG lH$Wº<:Z<&(b y#͟ \.Zu)$poz?6Y~'-.FKYˍ)P9 _7~(i{~'!ѼIM-V) mrݫ3VJmgpݟֱ\ &T<8ledYVdzqvƩ;iO yCͳG"ܴD#WϪjZMbU\KpӹiWtp8=+ښ]Ou ;g<"ĉ֔^)sM8u:)B[ |Z?rB r8֨rP}:zV5DL  F;匑R_a4}?ɣGS8nLTb&Q6cig>ߟYv>3,B78ZǨ6Q+CjmawSzv4i:$q^{Ū<+fWhl>yo%UI0@=xаMˣ.vt>[K(͟<($yѥٱa}kb,Y|X&Kr7sW "Fk;FHU(#uNꖅZb. Rk̯r۩.I(~xš}m!4@:漷ƛtK4Okfoa0/XHgƽ6fM!qu{V|ijqy: 85ۈqG5:?`.ol[꺝%؈"7^'Oعͣ^Gd,\o_$O=Rbm;KkaȀyakY[ړziЮ4DUaVPqGNkJ :oZ+WѯE?Jy! k}Q2|4$SDPݸҫì^iumjDv!Qw >^:)I3fYK 8w:MsR ?Ig&nwB5 =k>:~d%Ƥۏ8*@0s+dU gR{g]SN[mF) w[J`XMK?=?g"~QMB _Rh:n E3^z !bP"27{ҾP7s> Z`y( c5i?~:/ux~Ѧ~DžE2}nFCʫn_\𳪽_GSzC=hnL6VPr1XPx7I5,4Ůyeg vݻ{q-|bŏ*PY*J9G)}?w gr>vnj~^"KK#|ij)E;)ag ;7Z>.iv&wqD'ˌ1+̓]SM[IWҢSJg <E [l6`Ue3ǵs|}2 hA;d\m`qWGv^5ھ/`nMEb6qY}_kF(,vlp@kkZf,Y9ݹOsYW:]F,HN;H8 E{t6Puf{_Kg[nɠPydž}xo ޿&xc5K;iZ 36 3хM29ibpJ>74}B{<{l(Ưx+[fF\gnt8?VnNwuԊ4K%gh;lĞy$fƳӁY؁@@׫9ګ"*gs-O#!>⻿xv\F Pcd*ٓIŨ۷@A+_cӖ2wm\fƤUGPn)G'mkֺߊ5B7@mоv-կGK u@ݹoA2nT1\m_,%$ͳ|zחQsٹdфcSwC3674+KS{Jxw_©OZv} ԙ疩4Y4//EsOB0 ڹvkXK} 7nY~R_Q/_V΃%&j mܬ)<8qlfp۔b${"[7LIr)<P #בF2sV=Җ]]M_gLdybsGJijj7rZɴ& *Xq2VUN1,pFx9YuԺg9㸕[ ;=zW9J:m+=Zv(jTw44Eir[M$=_ kϭ^ۨK60PNG>fzk~kܮWogw>$i%M݌Q@fܸ6Yt=\3Q%yJ-G[kWasjkm+ȯdP?{vھoym]˅bj6|52mM0vCՎ;q^}~{ i bku!EoЌ!*OԮ$]b-g{g|Y޷wPmxv;{ZM kKZvuϥbx GR8 "8a2pk223ھ^j o/Aܒ.AU6173YpۿWY"nz5VK!f囖WGR՞ νNXW>qs_lKHZ98>%m[Դ+{U'-frLyU⾐)"RR8#:l5Euښ0CGehYg^ b¾ ӛ.l#/[`w/#r+ԗ~V}Ԓ;;k 4m-lrX%9*x!{՚Li\cy5v}Gk |*|cou{._·Hm,F`*& ʉG^e 9Oe/q6< 1>?u ƺ9SK4+|qU{R|?p\*"ZE!{ߵtFsS_Ye-#NP|ɲ*˱KJ$zhc+y~#NIBQI{m4nu5cH#l-ʑ(;Jm?Z<5j#!MOA%%K"g+q :+Ɓ-;S1\ ӧ.5g\6# PѤh98!+zM;>O[ N?GkDû`#E~ixQ~(}$Iovƀ|aԚ ;C=ИY"rd%'3$#bOP__3Qc}I֧V>ዻK b=8=2 d ^o[Ȃ8!Ms&[ea<盖-`jgs>dO9g}kjW=FL<Ui5M]ݴ=.$ZvwR{mlax{e:\fҤExrnjq_GO7v3έͯ+o#RɄ)nIoq.c'MgyK[Hӓ_wK' 52b;mG(CJ[;Hb J0xX-q0UcV6rL򿃾 .<[k3\[k%FW5LT4?b刖")GOr\ww&8"xJK-6Vr+` `w9?gMX\OmiqtĴsl]G8ǽt>3x'/ص G)ϺD} tӓo/絗Qͼ, ' j|DʼZ5%mV%/G2]Ќuw]sgs33 " %c ذn.~PէXg–sݐe1\~F|7:z&]DhYK K(K+*כ :I?XVcR>T)>fڽoE}z/cq[]2Vkxs2xEҵkVּζ[<YA&/AҮ>C])NK4#qctnc5ͦEr<-^ q!3} fuƔi>Xϳ|󺕥UJڏnk?a}{=mI3BUF@ppA'َJ֣V5l6ӑnXgWֽՆ二J ͵#ǻnXF̎(릿_AMgz[լzf@_FX)y8 4Zohx;~ۑc[h1)bnܪ@\s%;V Dl1<ܬG͌ϭ};[xb]G>K NU#]\_-X}{?Zxi6ge$,vHf吖F>5]Yq<2 R;_W_K+llIt8j9î^S&)5%]`״gUL,$dLӟʪxe -,8g޾l]v{- xiRO㡯IU['Q-q͚Ƭ**9<}6,, >[[?'3sb0*wVzWk^ +ĄQhVX^ 7 f̹\1N]lUfqtv^3}+"F鉕=X=`qELR֪9±XZ6vq@u6]隸${EٍڀׯJ_7%ŏtGzqyMmf0ˏxJvK"K;fK KуBz1S0=; P9\Z'miw<leυ~-5\ǧ&A<>#39O5QkEvp2GZ)aq}g]KKb5TӤLwOp$LU%NV+^ќ 6׳ [4*rwzv4gi7o+iں)rnch@'y40U}~}kroo9t [Ygv@0hI`N5\#QS[8Ճ2pS|VhTN3WwO!~45+ }vt}BǷz Wֱ\I5q:;=|i*aSGn^TIu[[̇KosXZXy$; ̬:OBj 8qҼtxmv?  %z`i]y3> eQ-V6tTkΚp+Ru#zsmȇ$)ԱK8guFGӊ4k%\۴( N ([HkdIp{utԞ |)TqM+~'ul < 66Zb)1/֠}$'tiqțpۑnUS~)nŨB =Zqc+#=:(tH m9?HmCy8O&<Yyt&xq' |?59'k?)U5gĨxW83_Q O޼)]y/oSkZ(%OaC{/2i+Ws>N1_xbz>k~3"09r5ZKW_N^FI46.%r;_sKfhMcԦap=\_ h^,wV2n}s޾ү!ִK'QIG,6S] @۵%y{WK1u0 C_Jm-֠zx:όn$i- FTG!A~xnu)bB>{#Mg᷋|e5޵;{5Ҡ ARz?SS,g{q1#B,7Smߐf1T+%Ӣ>/95rk¢[jՆȮ.m_'?Eo}M-٤2*nvcS^7Oђ=.L`t^f]J.e90`[[?0okwG=1*pߕmj:hܷkBOUeC~+8=HG3m]0(?luZiy3FO'|ys+{KMFħMEvp@{W5wr-) ~f>13_hj_@&[X:O(Kpm3)wMbUU;T[ZRI ]>Mg_H$e#wS׆Zu[;m#T ႗_UyᾒZ Fa2yM[x:oBѡ60Lν%wMz=S_qɌ˽fɢGO&kgwmh^*ljQYCjQ[:hm zZ+|C:-9ui蠞V9>ʓtz439N3nIYM|<3VjzMUk7Oi xHԺucUL>!Qv&{-m)&ɐ?Xj1 ׅ@0V &s6`~_@w⹏_Z⾑ <0*ַ#8I'٧ ʬ8kתkdG^쭶wUDe<-jH b>9V_6z_X}ľ  }j@ ,eE Nsx<+sL$^t2XKʽ~PznC?{$^߳ǁ:,v0<[2qp1I|H/Kڬ bhWn3͚=Շuilt/oBڄ2pr_hPRZ3}:=zױjrP}7A$NG|+u6f3$L.z֥\4_H49!Ip H-ƿٶ7P ڇͺꚘ#OU~ڟTiϞ>uxwAc=C #)m yqМr鋨&WzmιeCvX2D~5PSOkpOp.nLt9{|