├── .eslintrc ├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── contributors.md ├── examples.js ├── lib └── httpreq.js ├── package.json └── test ├── tests-async.js ├── tests.js └── testupload.jpg /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "ecmaFeatures": { 3 | "modules": true 4 | }, 5 | 6 | "env": { 7 | "node": true 8 | }, 9 | 10 | "rules": { 11 | "array-bracket-spacing": [2, "never"], 12 | "brace-style": [2, "1tbs", { 13 | "allowSingleLine": true 14 | }], 15 | "camelcase": [2, { 16 | "properties": "never" 17 | }], 18 | "comma-spacing": [2, { 19 | "before": false, 20 | "after": true 21 | }], 22 | "comma-style": [2, "last"], 23 | "comma-dangle": [2, "never"], 24 | "complexity": [1, 8], 25 | "computed-property-spacing": [2, "never"], 26 | "consistent-return": 1, 27 | "curly": [2, "all"], 28 | "default-case": 2, 29 | "dot-notation": [1, { 30 | "allowKeywords": true 31 | }], 32 | "dot-location": [2, "property"], 33 | "eol-last": 2, 34 | "eqeqeq": 2, 35 | "func-style": 0, 36 | "guard-for-in": 0, 37 | "handle-callback-err": [2, "^(e|er|err|error)[0-9]{1,2}?$"], 38 | "indent": [2, 2, { 39 | "SwitchCase": 1 40 | }], 41 | "keyword-spacing": 2, 42 | "key-spacing": [2, { 43 | "beforeColon": false, 44 | "afterColon": true 45 | }], 46 | "lines-around-comment": [2, { 47 | "beforeBlockComment": true, 48 | "afterBlockComment": true, 49 | "beforeLineComment": true, 50 | "afterLineComment": false, 51 | "allowBlockStart": true, 52 | "allowBlockEnd": false 53 | }], 54 | "linebreak-style": [2, "unix"], 55 | "max-nested-callbacks": [1, 3], 56 | "new-cap": 0, 57 | "newline-after-var": [2, "always"], 58 | "no-alert": 2, 59 | "no-caller": 2, 60 | "no-catch-shadow": 2, 61 | "no-delete-var": 2, 62 | "no-div-regex": 2, 63 | "no-duplicate-case": 2, 64 | "no-else-return": 2, 65 | "no-empty": 2, 66 | "no-empty-character-class": 2, 67 | "no-eval": 2, 68 | "no-extend-native": 2, 69 | "no-extra-semi": 2, 70 | "no-fallthrough": 2, 71 | "no-floating-decimal": 2, 72 | "no-func-assign": 2, 73 | "no-implied-eval": 2, 74 | "no-inline-comments": 1, 75 | "no-invalid-regexp": 2, 76 | "no-label-var": 2, 77 | "no-labels": 2, 78 | "no-lone-blocks": 2, 79 | "no-lonely-if": 2, 80 | "no-mixed-requires": 0, 81 | "no-mixed-spaces-and-tabs": 2, 82 | "no-multi-spaces": 2, 83 | "no-multi-str": 2, 84 | "no-multiple-empty-lines": [2, { 85 | "max": 2 86 | }], 87 | "no-native-reassign": 2, 88 | "no-nested-ternary": 2, 89 | "no-new-func": 2, 90 | "no-new-object": 2, 91 | "no-new-wrappers": 2, 92 | "no-octal-escape": 2, 93 | "no-octal": 2, 94 | "no-path-concat": 2, 95 | "no-param-reassign": 0, 96 | "no-process-env": 0, 97 | "no-proto": 2, 98 | "no-redeclare": 2, 99 | "no-reserved-keys": 0, 100 | "no-return-assign": [2, "always"], 101 | "no-self-compare": 2, 102 | "no-sequences": 2, 103 | "no-shadow": 2, 104 | "no-shadow-restricted-names": 2, 105 | "no-spaced-func": 0, 106 | "no-sparse-arrays": 1, 107 | "no-sync": 1, 108 | "no-ternary": 0, 109 | "no-throw-literal": 2, 110 | "no-trailing-spaces": 2, 111 | "no-undef": 2, 112 | "no-undef-init": 2, 113 | "no-undefined": 1, 114 | "no-underscore-dangle": 2, 115 | "no-unexpected-multiline": 2, 116 | "no-unneeded-ternary": 2, 117 | "no-unreachable": 2, 118 | "no-unused-vars": 1, 119 | "no-use-before-define": 2, 120 | "no-useless-concat": 2, 121 | "no-warning-comments": 1, 122 | "no-with": 2, 123 | "no-wrap-func": 0, 124 | "object-curly-spacing": [2, "always", { 125 | "objectsInObjects": false, 126 | "arraysInObjects": false 127 | }], 128 | "one-var": [2, "never"], 129 | "operator-assignment": [2, "always"], 130 | "operator-linebreak": [2, "before"], 131 | "padded-blocks": [2, "never"], 132 | "quote-props": [2, "consistent"], 133 | "quotes": [2, "single", "avoid-escape"], 134 | "radix": 2, 135 | "semi": 2, 136 | "semi-spacing": [2, { 137 | "before": false, 138 | "after": true 139 | }], 140 | "space-before-blocks": [2, "always"], 141 | "space-before-function-paren": [2, "always"], 142 | "space-in-parens": [2, "never"], 143 | "space-infix-ops": 2, 144 | "space-unary-ops": [2, { 145 | "words": true, 146 | "nonwords": false 147 | }], 148 | "spaced-comment": [2, "always"], 149 | "use-isnan": 2, 150 | "valid-typeof": 2, 151 | "vars-on-top": 2, 152 | "wrap-regex": 0, 153 | "yoda": [2, "never"] 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | *.seed 3 | *.log 4 | *.csv 5 | *.dat 6 | *.out 7 | *.pid 8 | *.gz 9 | 10 | pids 11 | logs 12 | results 13 | 14 | node_modules 15 | 16 | npm-debug.log 17 | .DS_Store -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | test 2 | .eslintrc 3 | examples.js 4 | uploads -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Sam Decrock 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | node-httpreq 2 | ============ 3 | 4 | node-httpreq is a node.js library to do HTTP(S) requests the easy way 5 | 6 | Do GET, POST, PUT, PATCH, DELETE, OPTIONS, upload files, use cookies, change headers, ... 7 | 8 | This module helps you fight [TLS fingerprinting](https://httptoolkit.com/blog/tls-fingerprinting-node-js/)! See the `shuffleCiphers` option below. 9 | 10 | ## Donate 11 | 12 | If you've benefited from this module in any way, please consider donating! 13 | 14 | [![](https://neat.be/paypal-donate-button.png)](https://www.paypal.com/donate/?hosted_button_id=2CKNJLZJBW8ZC) 15 | 16 | Thank you for your support! 17 | 18 | 19 | ## Install 20 | 21 | You can install __httpreq__ using the Node Package Manager (npm): 22 | 23 | npm install httpreq 24 | 25 | ## Simple example 26 | ```js 27 | var httpreq = require('httpreq'); 28 | 29 | httpreq.get('http://www.google.com', function (err, res) { 30 | if (err) return console.log(err); 31 | 32 | console.log(res.statusCode); 33 | console.log(res.headers); 34 | console.log(res.body); 35 | console.log(res.cookies); 36 | }); 37 | ``` 38 | 39 | __Using await/async:__ 40 | 41 | ```js 42 | var httpreq = require('httpreq'); 43 | 44 | var res = await httpreq.get('http://www.google.com'); 45 | 46 | console.log(res.statusCode); 47 | console.log(res.headers); 48 | console.log(res.body); 49 | console.log(res.cookies); 50 | ``` 51 | 52 | ## Use with async/await 53 | 54 | This module has been updated to support async/await. 55 | 56 | In the following examples, simply omit the `callback` parameter and prepend it with `await`. 57 | 58 | __Example:__ 59 | 60 | ```js 61 | var httpreq = require('httpreq'); 62 | 63 | var res = await httpreq.post('http://posttestserver.com/post.php', { 64 | parameters: { 65 | name: 'John', 66 | lastname: 'Doe' 67 | } 68 | }); 69 | 70 | console.log(res.body); 71 | ``` 72 | 73 | ## How to use 74 | 75 | * [httpreq.get(url, [options], callback)](#get) 76 | * [httpreq.post(url, [options], callback)](#post) 77 | * [httpreq.put(url, [options], callback)](#put) 78 | * [httpreq.delete(url, [options], callback)](#delete) 79 | * [httpreq.options(url, [options], callback)](#options) 80 | * [Uploading files](#upload) 81 | * [Downloading a binary file](#binary) 82 | * [Downloading a file directly to disk](#download) 83 | * [Sending a custom body](#custombody) 84 | * [Using a http(s) proxy](#proxy) 85 | * [httpreq.doRequest(options, callback)](#dorequest) 86 | 87 | --------------------------------------- 88 | ### httpreq.get(url, [options], callback) 89 | 90 | 91 | __Arguments__ 92 | - url: The url to connect to. Can be http or https. 93 | - options: (all are optional) The following options can be passed: 94 | - parameters: an object of query parameters 95 | - headers: an object of headers 96 | - cookies: an array of cookies 97 | - auth: a string for basic authentication. For example `username:password` 98 | - binary: true/false (default: false), if true, res.body will a buffer containing the binary data 99 | - allowRedirects: (default: __true__ , only with httpreq.get() ), if true, redirects will be followed 100 | - maxRedirects: (default: __10__ ). For example 1 redirect will allow for one normal request and 1 extra redirected request. 101 | - timeout: (default: __none__ ). Adds a timeout to the http(s) request. Should be in milliseconds. 102 | - ciphers: Change the TLS ciphers if needed. 103 | - shuffleCiphers: Set to `true` if you want to shuffle the TLS ciphers to fight [TLS fingerprinting](https://httptoolkit.com/blog/tls-fingerprinting-node-js/). 104 | - proxy, if you want to pass your request through a http(s) proxy server: 105 | - host: eg: "192.168.0.1" 106 | - port: eg: 8888 107 | - protocol: (default: __'http'__ ) can be 'http' or 'https' 108 | - rejectUnauthorized: validate certificate for request with HTTPS. [More here](http://nodejs.org/api/https.html#https_https_request_options_callback) 109 | - callback(err, res): A callback function which is called when the request is complete. __res__ contains the headers ( __res.headers__ ), the http status code ( __res.statusCode__ ) and the body ( __res.body__ ) 110 | 111 | __Example without options__ 112 | 113 | ```js 114 | var httpreq = require('httpreq'); 115 | 116 | httpreq.get('http://www.google.com', function (err, res){ 117 | if (err) return console.log(err); 118 | 119 | console.log(res.statusCode); 120 | console.log(res.headers); 121 | console.log(res.body); 122 | }); 123 | ``` 124 | 125 | __Example with options__ 126 | 127 | ```js 128 | var httpreq = require('httpreq'); 129 | 130 | httpreq.get('http://posttestserver.com/post.php', { 131 | parameters: { 132 | name: 'John', 133 | lastname: 'Doe' 134 | }, 135 | headers:{ 136 | 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:18.0) Gecko/20100101 Firefox/18.0' 137 | }, 138 | cookies: [ 139 | 'token=DGcGUmplWQSjfqEvmu%2BZA%2Fc', 140 | 'id=2' 141 | ] 142 | }, function (err, res){ 143 | if (err){ 144 | console.log(err); 145 | }else{ 146 | console.log(res.body); 147 | } 148 | }); 149 | ``` 150 | --------------------------------------- 151 | ### httpreq.post(url, [options], callback) 152 | 153 | 154 | __Arguments__ 155 | - url: The url to connect to. Can be http or https. 156 | - options: (all are optional) The following options can be passed: 157 | - parameters: an object of post parameters (content-type is set to *application/x-www-form-urlencoded; charset=UTF-8*) 158 | - json: if you want to send json directly (content-type is set to *application/json*) 159 | - files: an object of files to upload (content-type is set to *multipart/form-data; boundary=xxx*) 160 | - body: custom body content you want to send. If used, previous options will be ignored and your custom body will be sent. (content-type will not be set) 161 | - headers: an object of headers 162 | - cookies: an array of cookies 163 | - auth: a string for basic authentication. For example `username:password` 164 | - binary: true/false (default: __false__ ), if true, res.body will be a buffer containing the binary data 165 | - allowRedirects: (default: __false__ ), if true, redirects will be followed 166 | - maxRedirects: (default: __10__ ). For example 1 redirect will allow for one normal request and 1 extra redirected request. 167 | - encodePostParameters: (default: __true__ ), if true, POST/PUT parameters names will be URL encoded. 168 | - timeout: (default: none). Adds a timeout to the http(s) request. Should be in milliseconds. 169 | - proxy, if you want to pass your request through a http(s) proxy server: 170 | - host: eg: "192.168.0.1" 171 | - port: eg: 8888 172 | - protocol: (default: __'http'__ ) can be 'http' or 'https' 173 | - rejectUnauthorized: validate certificate for request with HTTPS. [More here](http://nodejs.org/api/https.html#https_https_request_options_callback) 174 | - callback(err, res): A callback function which is called when the request is complete. __res__ contains the headers ( __res.headers__ ), the http status code ( __res.statusCode__ ) and the body ( __res.body__ ) 175 | 176 | __Example without extra options__ 177 | 178 | ```js 179 | var httpreq = require('httpreq'); 180 | 181 | httpreq.post('http://posttestserver.com/post.php', { 182 | parameters: { 183 | name: 'John', 184 | lastname: 'Doe' 185 | } 186 | }, function (err, res){ 187 | if (err){ 188 | console.log(err); 189 | }else{ 190 | console.log(res.body); 191 | } 192 | }); 193 | ``` 194 | 195 | __Example with options__ 196 | 197 | ```js 198 | var httpreq = require('httpreq'); 199 | 200 | httpreq.post('http://posttestserver.com/post.php', { 201 | parameters: { 202 | name: 'John', 203 | lastname: 'Doe' 204 | }, 205 | headers:{ 206 | 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:18.0) Gecko/20100101 Firefox/18.0' 207 | }, 208 | cookies: [ 209 | 'token=DGcGUmplWQSjfqEvmu%2BZA%2Fc', 210 | 'id=2' 211 | ] 212 | }, function (err, res){ 213 | if (err){ 214 | console.log(err); 215 | }else{ 216 | console.log(res.body); 217 | } 218 | }); 219 | ``` 220 | 221 | --------------------------------------- 222 | ### httpreq.put(url, [options], callback) 223 | 224 | 225 | Same options as [httpreq.post(url, [options], callback)](#post) 226 | 227 | --------------------------------------- 228 | 229 | ### httpreq.delete(url, [options], callback) 230 | 231 | Same options as [httpreq.post(url, [options], callback)](#post) 232 | 233 | --------------------------------------- 234 | 235 | ### httpreq.options(url, [options], callback) 236 | 237 | Same options as [httpreq.get(url, [options], callback)](#get) except for the ability to follow redirects. 238 | 239 | --------------------------------------- 240 | 241 | ### Uploading files 242 | 243 | You can still use ```httpreq.uploadFiles({url: 'url', files: {}}, callback)```, but it's easier to just use POST (or PUT): 244 | 245 | __Example__ 246 | 247 | ```js 248 | var httpreq = require('httpreq'); 249 | 250 | httpreq.post('http://posttestserver.com/upload.php', { 251 | parameters: { 252 | name: 'John', 253 | lastname: 'Doe' 254 | }, 255 | files: { 256 | myfile: __dirname + '/file1.jpg', 257 | myotherfile: __dirname + '/file2.jpg' 258 | } 259 | }, function (err, res){ 260 | if (err) throw err; 261 | }); 262 | ``` 263 | 264 | __Example 2__ 265 | 266 | In case you want to use the same form name for multiple files: 267 | 268 | ```js 269 | var httpreq = require('httpreq'); 270 | 271 | httpreq.post('http://posttestserver.com/upload.php', { 272 | parameters: { 273 | name: 'John', 274 | lastname: 'Doe' 275 | }, 276 | files: { 277 | myfiles: [__dirname + '/file1.jpg', __dirname + '/file.jpg'] 278 | } 279 | }, function (err, res){ 280 | if (err) throw err; 281 | }); 282 | ``` 283 | 284 | --------------------------------------- 285 | 286 | ### Downloading a binary file 287 | To download a binary file, just add __binary: true__ to the options when doing a get or a post. 288 | 289 | __Example__ 290 | 291 | ```js 292 | var httpreq = require('httpreq'); 293 | 294 | httpreq.get('https://ssl.gstatic.com/gb/images/k1_a31af7ac.png', {binary: true}, function (err, res){ 295 | if (err){ 296 | console.log(err); 297 | }else{ 298 | fs.writeFile(__dirname + '/test.png', res.body, function (err) { 299 | if(err) 300 | console.log("error writing file"); 301 | }); 302 | } 303 | }); 304 | ``` 305 | 306 | --------------------------------------- 307 | 308 | ### Downloading a file directly to disk 309 | To download a file directly to disk, use the download method provided. 310 | 311 | Downloading is done using a stream, so the data is not stored in memory and directly saved to file. 312 | 313 | __Example__ 314 | 315 | ```js 316 | var httpreq = require('httpreq'); 317 | 318 | httpreq.download( 319 | 'https://ssl.gstatic.com/gb/images/k1_a31af7ac.png', 320 | __dirname + '/test.png' 321 | , function (err, progress){ 322 | if (err) return console.log(err); 323 | console.log(progress); 324 | }, function (err, res){ 325 | if (err) return console.log(err); 326 | console.log(res); 327 | }); 328 | 329 | ``` 330 | 331 | When specifying the `progress` callback (3th parameter), you cannot use async/await. 332 | 333 | --------------------------------------- 334 | 335 | ### Sending a custom body 336 | Use the body option to send a custom body (eg. an xml post) 337 | 338 | __Example__ 339 | 340 | ```js 341 | var httpreq = require('httpreq'); 342 | 343 | httpreq.post('http://posttestserver.com/post.php',{ 344 | body: '', 345 | headers:{ 346 | 'Content-Type': 'text/xml', 347 | }}, 348 | function (err, res) { 349 | if (err){ 350 | console.log(err); 351 | }else{ 352 | console.log(res.body); 353 | } 354 | } 355 | ); 356 | ``` 357 | 358 | --------------------------------------- 359 | 360 | ### Using a http(s) proxy 361 | 362 | __Example__ 363 | 364 | ```js 365 | var httpreq = require('httpreq'); 366 | 367 | httpreq.post('http://posttestserver.com/post.php', { 368 | proxy: { 369 | host: '10.100.0.126', 370 | port: 8888 371 | } 372 | }, function (err, res){ 373 | if (err){ 374 | console.log(err); 375 | }else{ 376 | console.log(res.body); 377 | } 378 | }); 379 | ``` 380 | 381 | --------------------------------------- 382 | ### httpreq.doRequest(options, callback) 383 | 384 | 385 | httpreq.doRequest is internally used by httpreq.get() and httpreq.post(). You can use this directly. Everything is stays the same as httpreq.get() or httpreq.post() except that the following options MUST be passed: 386 | - url: the url to post the files to 387 | - method: 'GET', 'POST', 'PUT' or 'DELETE' 388 | 389 | ## Run tests 390 | 391 | Install all depedencies with 392 | 393 | ```bash 394 | npm install 395 | ``` 396 | 397 | Install mocha with 398 | 399 | ```bash 400 | npm install mocha -g 401 | ``` 402 | 403 | Run tests: 404 | ```bash 405 | mocha test/tests.js 406 | ``` 407 | 408 | Run the async/await tests: 409 | ```bash 410 | mocha test/tests-async.js 411 | ``` 412 | 413 | -------------------------------------------------------------------------------- /contributors.md: -------------------------------------------------------------------------------- 1 | ###### Contributors 2 | [Sam](https://github.com/SamDecrock) 3 | 74 Commits / 3562++ / 1353-- 4 | 77.08% ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||

5 | [Franklin](https://github.com/fvdm) 6 | 17 Commits / 1729++ / 1393-- 7 | 17.71% ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||

8 | [Dave Preston](https://github.com/davepreston) 9 | 1 Commits / 5++ / 0-- 10 | 01.04% ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||

11 | [Russell Beattie](https://github.com/russellbeattie) 12 | 1 Commits / 55++ / 3-- 13 | 01.04% ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||

14 | [Jason Prickett](https://github.com/jpricket) 15 | 1 Commits / 5++ / 0-- 16 | 01.04% ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||

17 | [MJJ](https://github.com/mjj2000) 18 | 1 Commits / 11++ / 1-- 19 | 01.04% ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||

20 | [ludovic landier](https://github.com/Come2Daddy) 21 | 1 Commits / 1++ / 0-- 22 | 01.04% ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||

23 | ###### [Generated](https://github.com/jakeleboeuf/contributor) on Thu Sep 28 2023 20:56:00 GMT+0200 (Central European Summer Time) -------------------------------------------------------------------------------- /examples.js: -------------------------------------------------------------------------------- 1 | var httpreq = require('./lib/httpreq'); 2 | fs = require('fs') 3 | 4 | 5 | // example1(); // get www.google.com 6 | // example2(); // do some post 7 | // example3(); // same as above + extra headers + cookies 8 | // example4(); // https also works: 9 | // example5(); // uploading some file: 10 | // example6(); // u can use doRequest instead of .get or .post 11 | // example7(); // download a binary file: 12 | // example8(); // send json 13 | // example9(); // send your own body content (eg. xml) 14 | // example10(); // set max redirects: 15 | // example11(); // set timeout 16 | // example12(); // // download file directly to disk 17 | 18 | 19 | // get www.google.com 20 | function example1(){ 21 | httpreq.get('http://www.google.com', function (err, res){ 22 | if (err){ 23 | console.log(err); 24 | }else{ 25 | console.log(res.headers); //headers are stored in res.headers 26 | console.log(res.body); //content of the body is stored in res.body 27 | } 28 | }); 29 | } 30 | 31 | // do some post 32 | function example2(){ 33 | httpreq.post('http://posttestserver.com/post.php', { 34 | parameters: { 35 | name: 'John', 36 | lastname: 'Doe' 37 | } 38 | }, function (err, res){ 39 | if (err){ 40 | console.log(err); 41 | }else{ 42 | console.log(res.body); 43 | } 44 | }); 45 | } 46 | 47 | // same as above + extra headers + cookies 48 | function example3(){ 49 | httpreq.post('http://posttestserver.com/post.php', { 50 | parameters: { 51 | name: 'John', 52 | lastname: 'Doe' 53 | }, 54 | headers:{ 55 | 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:18.0) Gecko/20100101 Firefox/18.0' 56 | }, 57 | cookies: [ 58 | 'token=DGcGUmplWQSjfqEvmu%2BZA%2Fc', 59 | 'id=2' 60 | ] 61 | }, function (err, res){ 62 | if (err){ 63 | console.log(err); 64 | }else{ 65 | console.log(res.body); 66 | } 67 | }); 68 | } 69 | 70 | // https also works: 71 | function example4(){ 72 | httpreq.get('https://graph.facebook.com/19292868552', function (err, res){ 73 | if (err){ 74 | console.log(err); 75 | }else{ 76 | console.log(JSON.parse(res.body)); 77 | } 78 | }); 79 | } 80 | 81 | // uploading some file: 82 | function example5(){ 83 | httpreq.uploadFiles({ 84 | url: "http://rekognition.com/demo/do_upload/", 85 | parameters:{ 86 | name_space : 'something', 87 | }, 88 | files:{ 89 | fileToUpload: __dirname + "/test/testupload.jpg" 90 | }}, 91 | function (err, res){ 92 | if (err) return console.log(err); 93 | console.log(res.body); 94 | }); 95 | } 96 | 97 | // u can use doRequest instead of .get or .post 98 | function example6(){ 99 | httpreq.doRequest({ 100 | url: 'https://graph.facebook.com/19292868552', 101 | method: 'GET', 102 | parameters: { 103 | name: 'test' 104 | } 105 | }, 106 | function (err, res){ 107 | if (err){ 108 | console.log(err); 109 | }else{ 110 | console.log(JSON.parse(res.body)); 111 | } 112 | }); 113 | } 114 | 115 | // download a binary file: 116 | function example7(){ 117 | httpreq.get('https://ssl.gstatic.com/gb/images/k1_a31af7ac.png', { 118 | binary: true, 119 | progressCallback: function (err, progress) { 120 | console.log(progress); 121 | } 122 | }, 123 | function (err, res){ 124 | if (err){ 125 | console.log(err); 126 | }else{ 127 | fs.writeFile(__dirname + '/test.png', res.body, function (err) { 128 | if(err) return console.log("error writing file"); 129 | }); 130 | } 131 | }); 132 | } 133 | 134 | // send json 135 | function example8(){ 136 | httpreq.post('http://posttestserver.com/post.php',{ 137 | json: {name: 'John', lastname: 'Do'}, 138 | headers:{ 139 | 'Content-Type': 'text/xml', 140 | }}, 141 | function (err, res) { 142 | if (err){ 143 | console.log(err); 144 | }else{ 145 | console.log(res.body); 146 | } 147 | } 148 | ); 149 | } 150 | 151 | // send your own body content (eg. xml): 152 | function example9(){ 153 | httpreq.post('http://posttestserver.com/post.php',{ 154 | body: '', 155 | headers:{ 156 | 'Content-Type': 'text/xml', 157 | }}, 158 | function (err, res) { 159 | if (err){ 160 | console.log(err); 161 | }else{ 162 | console.log(res.body); 163 | } 164 | } 165 | ); 166 | } 167 | 168 | // set max redirects: 169 | function example10(){ 170 | httpreq.get('http://scobleizer.com/feed/',{ 171 | maxRedirects: 2, // default is 10 172 | headers:{ 173 | 'User-Agent': 'Magnet', //for some reason causes endless redirects on this site... 174 | }}, 175 | function (err, res) { 176 | if (err){ 177 | console.log(err); 178 | }else{ 179 | console.log(res.body); 180 | } 181 | } 182 | ); 183 | } 184 | 185 | // set timeout 186 | function example11(){ 187 | httpreq.get('http://localhost:3000/',{ 188 | timeout: (5 * 1000) // timeout in milliseconds 189 | }, 190 | function (err, res) { 191 | if (err){ 192 | console.log(err); 193 | }else{ 194 | console.log(res.body); 195 | } 196 | } 197 | ); 198 | } 199 | 200 | // download file directly to disk: 201 | function example12 () { 202 | httpreq.download( 203 | 'https://ssl.gstatic.com/gb/images/k1_a31af7ac.png', 204 | __dirname + '/test.png' 205 | , function (err, progress){ 206 | if (err) return console.log(err); 207 | console.log(progress); 208 | }, function (err, res){ 209 | if (err) return console.log(err); 210 | console.log(res); 211 | }); 212 | } 213 | 214 | 215 | -------------------------------------------------------------------------------- /lib/httpreq.js: -------------------------------------------------------------------------------- 1 | /* 2 | Copyright (c) 2013 Sam Decrock 3 | 4 | MIT License 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | var querystring = require ('querystring'); 26 | var https = require ('https'); 27 | var http = require ('http'); 28 | var url = require ('url'); 29 | var fs = require ('fs'); 30 | var tls = require('tls'); 31 | var defaultCiphers = tls.DEFAULT_CIPHERS.split(':'); 32 | 33 | /** 34 | * Generate multipart boundary 35 | * 36 | * @returns {string} 37 | */ 38 | 39 | function generateBoundary () { 40 | var boundary = '---------------------------'; 41 | var charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; 42 | 43 | for (var i = 0; i < 29; i++) { 44 | boundary += charset.charAt (Math.floor (Math.random () * charset.length)); 45 | } 46 | 47 | return boundary; 48 | } 49 | 50 | 51 | /** 52 | * Extract cookies from headers 53 | * 54 | * @param headers {object} - Response headers 55 | * @returns {array} - Extracted cookie strings 56 | */ 57 | 58 | function extractCookies (headers) { 59 | var rawcookies = headers['set-cookie']; 60 | 61 | if (!rawcookies) { 62 | return []; 63 | } 64 | 65 | if (rawcookies == []) { 66 | return []; 67 | } 68 | 69 | var cookies = []; 70 | for (var i = 0; i < rawcookies.length; i++) { 71 | var rawcookie = rawcookies[i].split (';'); 72 | if (rawcookie[0]) { 73 | cookies.push (rawcookie[0]); 74 | } 75 | } 76 | return cookies; 77 | } 78 | 79 | 80 | /** 81 | * Custom HTTP request 82 | * 83 | * @callback callback 84 | * @param o {object} - Request options 85 | * @param callback [function] - Process response 86 | * @returns {void} 87 | */ 88 | 89 | function doRequest (o, callback) { 90 | // support promises and async/await: 91 | if (callback === undefined) { 92 | return new Promise((resolve, reject) => { 93 | doRequest(o, (err, res) => { 94 | err ? reject(err) : resolve(res); 95 | }); 96 | }); 97 | } 98 | 99 | // prevent multiple callbacks 100 | var finalCallbackDone = false; 101 | function finalCallback (err, res) { 102 | if (!finalCallbackDone) { 103 | finalCallbackDone = true; 104 | callback (err, res); 105 | } 106 | } 107 | 108 | if (o.maxRedirects === undefined) { 109 | o.maxRedirects = 10; 110 | } 111 | 112 | if (o.encodePostParameters === undefined) { 113 | o.encodePostParameters = true; 114 | } 115 | 116 | var chunks = []; 117 | var body; // Buffer 118 | var contentType; 119 | 120 | var port; 121 | var host; 122 | var path; 123 | var isHttps = false; 124 | 125 | if (o.proxy) { 126 | port = o.proxy.port; 127 | host = o.proxy.host; 128 | path = o.url; // complete url 129 | 130 | if (o.proxy.protocol && o.proxy.protocol.match (/https/)) { 131 | isHttps = true; 132 | } 133 | } else { 134 | var reqUrl = url.parse (o.url); 135 | host = reqUrl.hostname; 136 | path = reqUrl.path; 137 | 138 | if (reqUrl.protocol === 'https:') { 139 | isHttps = true; 140 | } 141 | 142 | if (reqUrl.port) { 143 | port = reqUrl.port; 144 | } else if (isHttps) { 145 | port = 443; 146 | } else { 147 | port = 80; 148 | } 149 | } 150 | 151 | if (o.files && o.files.length > 0 && o.method === 'GET') { 152 | var err = new Error ('Can\'t send files using GET'); 153 | err.code = 'CANT_SEND_FILES_USING_GET'; 154 | return finalCallback (err); 155 | } 156 | 157 | if (o.parameters) { 158 | if (o.method === 'GET') { 159 | path += '?' + querystring.stringify (o.parameters); 160 | } else { 161 | body = Buffer.from (querystring.stringify (o.parameters), 'utf8'); 162 | contentType = 'application/x-www-form-urlencoded; charset=UTF-8'; 163 | } 164 | } 165 | 166 | if (o.json) { 167 | body = Buffer.from (JSON.stringify (o.json), 'utf8'); 168 | contentType = 'application/json'; 169 | } 170 | 171 | if (o.files) { 172 | var crlf = '\r\n'; 173 | var boundary = generateBoundary (); 174 | var separator = '--' + boundary; 175 | var bodyArray = new Array (); // temporary body array 176 | 177 | // if the user wants to POST/PUT files, other parameters need to be encoded using 'Content-Disposition' 178 | for (var key in o.parameters) { 179 | // According to RFC 2388 (https://www.ietf.org/rfc/rfc2388.txt) 180 | // "Field names originally in non-ASCII character sets MAY be encoded 181 | // within the value of the "name" parameter using the standard method 182 | // described in RFC 2047." 183 | // -- encodePostParameters -- true by default and MAY be changed by the user 184 | var headerKey = o.encodePostParameters ? encodeURIComponent (key) : key; 185 | var encodedParameter = separator + crlf 186 | + 'Content-Disposition: form-data; name="' + headerKey + '"' + crlf 187 | + crlf 188 | + o.parameters[key] + crlf; 189 | bodyArray.push (Buffer.from (encodedParameter)); 190 | } 191 | 192 | // now for the files: 193 | var haveAlreadyAddedAFile = false; 194 | 195 | for (var name in o.files) { 196 | var files = o.files[name]; 197 | if(!Array.isArray(files)) files = [files]; // files can either be stored directly as a string or as an array of strings 198 | 199 | for(var file of files) { 200 | var filename = file.replace (/\\/g, '/').replace (/.*\//, ''); 201 | 202 | var encodedFile = separator + crlf 203 | + 'Content-Disposition: form-data; name="' + name + '"; filename="' + filename + '"' + crlf 204 | + 'Content-Type: application/octet-stream' + crlf 205 | + crlf; 206 | 207 | // add crlf before separator if we have already added a file 208 | if (haveAlreadyAddedAFile) { 209 | encodedFile = crlf + encodedFile; 210 | } 211 | 212 | bodyArray.push (Buffer.from (encodedFile)); 213 | 214 | // add binary file: 215 | bodyArray.push (require ('fs').readFileSync (file)); 216 | 217 | haveAlreadyAddedAFile = true; 218 | } 219 | } 220 | 221 | var footer = crlf + separator + '--' + crlf; 222 | bodyArray.push (Buffer.from (footer)); 223 | 224 | // set body and contentType: 225 | body = Buffer.concat (bodyArray); 226 | contentType = 'multipart/form-data; boundary=' + boundary; 227 | } 228 | 229 | // overwrites the body if the user passes a body: 230 | // clears the content-type 231 | if (o.body) { 232 | body = Buffer.from (o.body, 'utf8'); 233 | contentType = null; 234 | } 235 | 236 | 237 | var requestoptions = { 238 | host: host, 239 | port: port, 240 | path: path, 241 | method: o.method, 242 | headers: {} 243 | }; 244 | 245 | if (!o.redirectCount) { 246 | o.redirectCount = 0; 247 | } 248 | 249 | if (body) { 250 | requestoptions.headers['Content-Length'] = body.length; 251 | } 252 | 253 | if (contentType) { 254 | requestoptions.headers['Content-Type'] = contentType; 255 | } 256 | 257 | if (o.cookies) { 258 | requestoptions.headers.Cookie = o.cookies.join ('; '); 259 | } 260 | 261 | if (o.rejectUnauthorized !== undefined && isHttps) { 262 | requestoptions.rejectUnauthorized = o.rejectUnauthorized; 263 | } 264 | 265 | if (isHttps && o.key) { 266 | requestoptions.key = o.key; 267 | } 268 | 269 | if (isHttps && o.cert) { 270 | requestoptions.cert = o.cert; 271 | } 272 | 273 | if (isHttps && o.secureProtocol) { 274 | requestoptions.secureProtocol = o.secureProtocol; 275 | } 276 | 277 | if (isHttps && o.ciphers) { 278 | requestoptions.ciphers = o.ciphers; 279 | } 280 | 281 | if (isHttps && o.shuffleCiphers) { 282 | var shuffledChiphers = defaultCiphers.slice(0); 283 | for (let i = shuffledChiphers.length - 1; i > 0; i--) { 284 | const j = Math.floor(Math.random() * (i + 1)); 285 | [shuffledChiphers[i], shuffledChiphers[j]] = [shuffledChiphers[j], shuffledChiphers[i]]; 286 | } 287 | requestoptions.ciphers = shuffledChiphers.join(':'); 288 | } 289 | 290 | if (isHttps && o.passphrase) { 291 | requestoptions.passphrase = o.passphrase; 292 | } 293 | 294 | if (isHttps && o.pfx) { 295 | requestoptions.pfx = o.pfx; 296 | } 297 | 298 | if (isHttps && o.ca) { 299 | requestoptions.ca = o.ca; 300 | } 301 | 302 | // add custom headers: 303 | if (o.headers) { 304 | for (var headerkey in o.headers) { 305 | requestoptions.headers[headerkey] = o.headers[headerkey]; 306 | } 307 | } 308 | 309 | if (o.agent) { 310 | requestoptions.agent = o.agent; 311 | } 312 | 313 | if (o.auth) { 314 | requestoptions.auth = o.auth; 315 | } 316 | 317 | if (o.localAddress) { 318 | requestoptions.localAddress = o.localAddress; 319 | } 320 | 321 | if (o.secureOptions) { 322 | requestoptions.secureOptions = o.secureOptions; 323 | } 324 | 325 | 326 | /** 327 | * Process request response 328 | * 329 | * @param res {object} - Response details 330 | * @returns {void} 331 | */ 332 | 333 | function requestResponse (res) { 334 | var ended = false; 335 | var currentsize = 0; 336 | 337 | var downloadstream = null; 338 | if (o.downloadlocation) { 339 | downloadstream = fs.createWriteStream (o.downloadlocation); 340 | } 341 | 342 | res.on ('data', function (chunk) { 343 | if (o.downloadlocation) { 344 | downloadstream.write (chunk); //write it to disk, not to memory 345 | } else { 346 | chunks.push (chunk); 347 | } 348 | 349 | if (o.progressCallback) { 350 | var totalsize = res.headers['content-length']; 351 | if (totalsize) { 352 | currentsize += chunk.length; 353 | 354 | o.progressCallback (null, { 355 | url: o.url, 356 | totalsize: totalsize, 357 | currentsize: currentsize, 358 | percentage: currentsize * 100 / totalsize 359 | }); 360 | } else { 361 | o.progressCallback (new Error ('no content-length specified for file, so no progress monitoring possible')); 362 | } 363 | } 364 | }); 365 | 366 | res.on ('end', function (err) { 367 | ended = true; 368 | 369 | // check for redirects 370 | if (res.headers.location && o.allowRedirects) { 371 | // Close any open file 372 | if (o.downloadlocation) { 373 | downloadstream.end (); 374 | } 375 | 376 | if (o.redirectCount < o.maxRedirects) { 377 | o.redirectCount++; 378 | o.url = (new URL(res.headers.location, o.url)).href; // location can be the path only (no base url present) 379 | o.cookies = extractCookies (res.headers); 380 | return doRequest (o, finalCallback); 381 | } else { 382 | var err = new Error ('Too many redirects (> ' + o.maxRedirects + ')'); 383 | err.code = 'TOOMANYREDIRECTS'; 384 | err.redirects = o.maxRedirects; 385 | return finalCallback (err); 386 | } 387 | } 388 | 389 | if (!o.downloadlocation) { 390 | var responsebody = Buffer.concat (chunks); 391 | if (!o.binary) { 392 | responsebody = responsebody.toString ('utf8'); 393 | } 394 | 395 | finalCallback (null, { 396 | headers: res.headers, 397 | statusCode: res.statusCode, 398 | body: responsebody, 399 | cookies: extractCookies (res.headers) 400 | }); 401 | } else { 402 | downloadstream.end (null, null, function () { 403 | finalCallback (null, { 404 | headers: res.headers, 405 | statusCode: res.statusCode, 406 | downloadlocation: o.downloadlocation, 407 | cookies: extractCookies (res.headers) 408 | }); 409 | }); 410 | } 411 | }); 412 | 413 | res.on ('close', function () { 414 | if (!ended) { 415 | finalCallback (new Error ('Request aborted')); 416 | } 417 | }); 418 | } 419 | 420 | var request; 421 | 422 | // remove headers with undefined keys or values 423 | // else we get an error in Node 0.12.0 about "setHeader ()" 424 | for (var headerName in requestoptions.headers) { 425 | var headerValue = requestoptions.headers[headerName]; 426 | if (!headerName || !headerValue) { 427 | delete requestoptions.headers[headerName]; 428 | } 429 | } 430 | 431 | if (isHttps) { 432 | request = https.request (requestoptions, requestResponse); 433 | } else { 434 | request = http.request (requestoptions, requestResponse); 435 | } 436 | 437 | if (o.timeout) { 438 | request.setTimeout (parseInt (o.timeout, 10), function () { 439 | var err = new Error ('request timed out'); 440 | err.code = 'TIMEOUT'; 441 | finalCallback (err); 442 | request.abort (); 443 | }); 444 | } 445 | 446 | request.on ('error', function (err) { 447 | finalCallback (err); 448 | }); 449 | 450 | if (body) { 451 | request.write (body); 452 | } 453 | 454 | request.end (); 455 | } 456 | 457 | exports.doRequest = doRequest; 458 | 459 | 460 | /** 461 | * HTTP GET method 462 | * 463 | * @callback callback 464 | * @param url {string} - Request URL 465 | * @param [options] {object} - Request options 466 | * @param callback [function] - Process response 467 | * @returns {void} 468 | */ 469 | 470 | exports.get = function (url, options, callback) { 471 | if (callback === undefined && options && typeof options === 'function') { 472 | callback = options; 473 | } 474 | 475 | if (options === undefined) { 476 | options = {}; 477 | } 478 | 479 | var moreOptions = options; 480 | moreOptions.url = url; 481 | moreOptions.method = 'GET'; 482 | 483 | if (moreOptions.allowRedirects === undefined) { 484 | moreOptions.allowRedirects = true; 485 | } 486 | 487 | return doRequest (moreOptions, callback); 488 | }; 489 | 490 | 491 | /** 492 | * HTTP OPTIONS method 493 | * 494 | * @callback callback 495 | * @param url {string} - Request URL 496 | * @param [options] {object} - Request options 497 | * @param callback [function] - Process response 498 | * @returns {void} 499 | */ 500 | 501 | exports.options = function (url, options, callback) { 502 | if (callback === undefined && options && typeof options === 'function') { 503 | callback = options; 504 | } 505 | 506 | if (options === undefined) { 507 | options = {}; 508 | } 509 | 510 | var moreOptions = options; 511 | moreOptions.url = url; 512 | moreOptions.method = 'OPTIONS'; 513 | return doRequest (moreOptions, callback); 514 | }; 515 | 516 | 517 | /** 518 | * HTTP POST method 519 | * 520 | * @callback callback 521 | * @param url {string} - Request URL 522 | * @param [options] {object} - Request options 523 | * @param callback [function] - Process response 524 | * @returns {void} 525 | */ 526 | 527 | exports.post = function (url, options, callback) { 528 | if (callback === undefined && options && typeof options === 'function') { 529 | callback = options; 530 | } 531 | 532 | if (options === undefined) { 533 | options = {}; 534 | } 535 | 536 | var moreOptions = options; 537 | moreOptions.url = url; 538 | moreOptions.method = 'POST'; 539 | return doRequest (moreOptions, callback); 540 | }; 541 | 542 | 543 | /** 544 | * HTTP PUT method 545 | * 546 | * @callback callback 547 | * @param url {string} - Request URL 548 | * @param [options] {object} - Request options 549 | * @param callback [function] - Process response 550 | * @returns {void} 551 | */ 552 | 553 | exports.put = function (url, options, callback) { 554 | if (callback === undefined && options && typeof options === 'function') { 555 | callback = options; 556 | } 557 | 558 | if (options === undefined) { 559 | options = {}; 560 | } 561 | 562 | var moreOptions = options; 563 | moreOptions.url = url; 564 | moreOptions.method = 'PUT'; 565 | return doRequest (moreOptions, callback); 566 | }; 567 | 568 | 569 | /** 570 | * HTTP PATCH method 571 | * 572 | * @callback callback 573 | * @param url {string} - Request URL 574 | * @param [options] {object} - Request options 575 | * @param callback [function] - Process response 576 | * @returns {void} 577 | */ 578 | 579 | exports.patch = function (url, options, callback) { 580 | if (callback === undefined && options && typeof options === 'function') { 581 | callback = options; 582 | } 583 | 584 | if (options === undefined) { 585 | options = {}; 586 | } 587 | 588 | var moreOptions = options; 589 | moreOptions.url = url; 590 | moreOptions.method = 'PATCH'; 591 | return doRequest (moreOptions, callback); 592 | }; 593 | 594 | 595 | /** 596 | * HTTP DELETE method 597 | * 598 | * @callback callback 599 | * @param url {string} - Request URL 600 | * @param [options] {object} - Request options 601 | * @param callback [function] - Process response 602 | * @returns {void} 603 | */ 604 | 605 | exports.delete = function (url, options, callback) { 606 | if (callback === undefined && options && typeof options === 'function') { 607 | callback = options; 608 | } 609 | 610 | if (options === undefined) { 611 | options = {}; 612 | } 613 | 614 | var moreOptions = options; 615 | moreOptions.url = url; 616 | moreOptions.method = 'DELETE'; 617 | return doRequest (moreOptions, callback); 618 | }; 619 | 620 | 621 | /** 622 | * HTTP HEAD method 623 | * 624 | * @callback callback 625 | * @param url {string} - Request URL 626 | * @param [options] {object} - Request options 627 | * @param callback [function] - Process response 628 | * @returns {void} 629 | */ 630 | 631 | exports.head = function (url, options, callback) { 632 | if (callback === undefined && options && typeof options === 'function') { 633 | callback = options; 634 | } 635 | 636 | if (options === undefined) { 637 | options = {}; 638 | } 639 | 640 | var moreOptions = options; 641 | moreOptions.url = url; 642 | moreOptions.method = 'HEAD'; 643 | 644 | if (moreOptions.allowRedirects === undefined) { 645 | moreOptions.allowRedirects = true; 646 | } 647 | 648 | return doRequest (moreOptions, callback); 649 | }; 650 | 651 | 652 | /** 653 | * Download a file 654 | * 655 | * @callback callback 656 | * @param url {string} - Request URL 657 | * @param downloadlocation {string} - Path where to store file 658 | * @param [progressCallback] {function} - Called multiple times during download 659 | * @param callback {function} - Called once when download ends 660 | * @returns {void} 661 | */ 662 | 663 | exports.download = function (url, downloadlocation, progressCallback, callback) { 664 | var options = {}; 665 | options.url = url; 666 | options.method = 'GET'; 667 | options.downloadlocation = downloadlocation; 668 | options.allowRedirects = true; 669 | 670 | // if only 3 args are provided, we assume no progressCallback 671 | if (callback === undefined && progressCallback && typeof progressCallback === 'function') { 672 | callback = progressCallback; 673 | } else { 674 | options.progressCallback = progressCallback; 675 | } 676 | 677 | return doRequest (options, callback); 678 | }; 679 | 680 | 681 | /** 682 | * Upload files 683 | * old function, can still be used 684 | * 685 | * @callback callback 686 | * @param options {object} - Request options 687 | * @param callback [function] - Process response 688 | * @returns {void} 689 | */ 690 | 691 | exports.uploadFiles = function (options, callback) { 692 | var moreOptions = options; 693 | moreOptions.method = 'POST'; 694 | return doRequest (moreOptions, callback); 695 | }; 696 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "httpreq", 3 | "description": "node-httpreq is a node.js library to do HTTP(S) requests the easy way", 4 | "version": "1.1.1", 5 | "author": { 6 | "name": "Sam Decrock", 7 | "url": "https://github.com/SamDecrock/" 8 | }, 9 | "bugs": { 10 | "url": "https://github.com/SamDecrock/node-httpreq/issues" 11 | }, 12 | "engines": { 13 | "node": ">= 6.15.1" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git://github.com/SamDecrock/node-httpreq.git" 18 | }, 19 | "main": "./lib/httpreq", 20 | "license": "MIT", 21 | "devDependencies": { 22 | "chai": "~1.9.1", 23 | "mocha": "~1.20.1", 24 | "express": "^4.17.1", 25 | "body-parser": "^1.19.0", 26 | "multer": "^1.4.2" 27 | }, 28 | "contributors": [ 29 | { 30 | "name": "ludovic landier", 31 | "email": null, 32 | "url": "https://github.com/Come2Daddy", 33 | "contributions": 1, 34 | "additions": 1, 35 | "deletions": 0, 36 | "hireable": null 37 | }, 38 | { 39 | "name": "MJJ", 40 | "email": null, 41 | "url": "https://github.com/mjj2000", 42 | "contributions": 1, 43 | "additions": 11, 44 | "deletions": 1, 45 | "hireable": true 46 | }, 47 | { 48 | "name": "Jason Prickett", 49 | "email": null, 50 | "url": "https://github.com/jpricket", 51 | "contributions": 1, 52 | "additions": 5, 53 | "deletions": 0, 54 | "hireable": null 55 | }, 56 | { 57 | "name": "Russell Beattie", 58 | "email": null, 59 | "url": "https://github.com/russellbeattie", 60 | "contributions": 1, 61 | "additions": 55, 62 | "deletions": 3, 63 | "hireable": null 64 | }, 65 | { 66 | "name": "Sam", 67 | "email": null, 68 | "url": "https://github.com/SamDecrock", 69 | "contributions": 74, 70 | "additions": 3562, 71 | "deletions": 1353, 72 | "hireable": true 73 | }, 74 | { 75 | "name": "Dave Preston", 76 | "email": null, 77 | "url": "https://github.com/davepreston", 78 | "contributions": 1, 79 | "additions": 5, 80 | "deletions": 0, 81 | "hireable": null 82 | }, 83 | { 84 | "name": "Franklin", 85 | "email": null, 86 | "url": "https://github.com/fvdm", 87 | "contributions": 17, 88 | "additions": 1729, 89 | "deletions": 1393, 90 | "hireable": null 91 | } 92 | ] 93 | } -------------------------------------------------------------------------------- /test/tests-async.js: -------------------------------------------------------------------------------- 1 | const httpreq = require('../lib/httpreq'); 2 | const assert = require("assert"); 3 | const expect = require("chai").expect; 4 | const fs = require('fs'); 5 | const express = require('express'); 6 | const bodyParser = require('body-parser'); 7 | const multer = require('multer') 8 | const upload = multer({ dest: 'uploads/' }) 9 | 10 | 11 | describe("httpreq", function(){ 12 | 13 | var port, app, webserver, endpointroot; 14 | 15 | before(function (done) { 16 | port = Math.floor( Math.random() * (65535 - 1025) + 1025 ); 17 | 18 | endpointroot = 'http://localhost:' + port; 19 | 20 | app = express(); 21 | 22 | // parse application/x-www-form-urlencoded 23 | app.use(bodyParser.urlencoded({ extended: true })); 24 | 25 | // parse application/json 26 | app.use(bodyParser.json()); 27 | 28 | // serve static files 29 | app.use('/static', express.static(__dirname)) 30 | 31 | webserver = app.listen(port, function(){ 32 | console.log("web server listening on port " + port); 33 | done(); 34 | }); 35 | 36 | }); 37 | 38 | after(function () { 39 | webserver.close(); 40 | }); 41 | 42 | 43 | describe("get", () => { 44 | it("should do a simple GET request", async () => { 45 | 46 | var path = '/get'; // make sure this is unique when writing tests 47 | var jsonData = {some: 'data'}; 48 | 49 | app.get(path, function (req, res) { 50 | res.json(jsonData); 51 | }); 52 | 53 | var res = await httpreq.get(endpointroot + path); 54 | expect(JSON.parse(res.body)).to.deep.equal(jsonData); 55 | }); 56 | 57 | it("should do a HTTPS GET request and shuffle the ciphers", async () => { 58 | var res = await httpreq.get('https://www.reichelt.com/be/nl/raspberry-pi-afstandsboutset-30mm-rpi-mountingkit3-p162090.html', {shuffleCiphers: true}); 59 | expect(res.statusCode).to.deep.equal(200); 60 | }); 61 | }); 62 | 63 | describe("head", () => { 64 | 65 | it("should do a simple HEAD request", async () => { 66 | 67 | var path = '/head'; 68 | var headerValue = 'abcd'; 69 | 70 | app.head(path, function (req, res) { 71 | res.set('x-user', headerValue); 72 | res.sendStatus(200); 73 | }); 74 | 75 | var res = await httpreq.head(endpointroot + path); 76 | expect(res.headers['x-user']).to.deep.equal(headerValue); 77 | }); 78 | 79 | }); 80 | 81 | describe("post", () => { 82 | 83 | it("should do a simple POST request with parameters", (done) => { 84 | 85 | var parameters = { 86 | name: 'John', 87 | lastname: 'Doe' 88 | }; 89 | 90 | var path = '/post'; 91 | 92 | // set up webserver endpoint: 93 | app.post(path, function (req, res) { 94 | res.send('ok'); 95 | 96 | expect(req.body).to.deep.equal(parameters); 97 | 98 | done(); 99 | }); 100 | 101 | // post parameters to webserver endpoint: 102 | httpreq.post(endpointroot + path, { 103 | parameters: parameters 104 | }); 105 | 106 | }); 107 | 108 | it("should do a simple POST request with parameters without callback function", (done) => { 109 | 110 | var parameters = { 111 | name: 'John', 112 | lastname: 'Doe' 113 | }; 114 | 115 | var path = '/postnocallback'; 116 | 117 | // set up webserver endpoint: 118 | app.post(path, function (req, res) { 119 | res.send('ok'); 120 | 121 | expect(req.body).to.deep.equal(parameters); 122 | 123 | done(); 124 | }); 125 | 126 | // post parameters to webserver endpoint: 127 | httpreq.post(endpointroot + path, { 128 | parameters: parameters 129 | }); 130 | 131 | }); 132 | 133 | 134 | it("should do a simple POST request and check the response", async () => { 135 | 136 | var jsonData = {some: 'data'}; 137 | 138 | var path = '/postwithresponse'; 139 | 140 | // set up webserver endpoint: 141 | app.post(path, function (req, res) { 142 | res.json(jsonData); 143 | }); 144 | 145 | // post parameters to webserver endpoint: 146 | var res = await httpreq.post(endpointroot + path); 147 | expect(JSON.parse(res.body)).to.deep.equal(jsonData); 148 | 149 | }); 150 | 151 | it("should do a simple POST request with parameters and cookies", (done) => { 152 | 153 | var parameters = { 154 | name: 'John', 155 | lastname: 'Doe' 156 | }; 157 | 158 | var cookies = [ 159 | 'token=DGcGUmplWQSjfqEvmu%2BZA%2Fc', 160 | 'id=2' 161 | ]; 162 | 163 | var path = '/postcookies'; 164 | 165 | // set up webserver endpoint: 166 | app.post(path, function (req, res) { 167 | res.send('ok'); 168 | 169 | expect(req.body).to.deep.equal(parameters); 170 | expect(req.headers.cookie).to.equal(cookies.join('; ')); 171 | 172 | done(); 173 | }); 174 | 175 | // post testdata to webserver endpoint: 176 | httpreq.post(endpointroot + path, { 177 | parameters: parameters, 178 | cookies: cookies 179 | }); 180 | 181 | }); 182 | 183 | it("should do a simple POST request with parameters and custom headers", (done) => { 184 | 185 | var parameters = { 186 | name: 'John', 187 | lastname: 'Doe' 188 | }; 189 | 190 | var headers = { 191 | 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:18.0) Gecko/20100101 Firefox/18.0' 192 | }; 193 | 194 | var path = '/postheaders'; 195 | 196 | // set up webserver endpoint: 197 | app.post(path, function (req, res) { 198 | res.send('ok'); 199 | 200 | expect(req.body).to.deep.equal(parameters); 201 | expect(req.headers).to.have.a.property('user-agent', headers['User-Agent']); 202 | 203 | done(); 204 | }); 205 | 206 | // post testdata to webserver endpoint: 207 | httpreq.post(endpointroot + path, { 208 | parameters: parameters, 209 | headers: headers 210 | }); 211 | 212 | }); 213 | 214 | }); 215 | 216 | 217 | describe("put", function(){ 218 | it("should do a simple PUT request with parameters", (done) => { 219 | 220 | var parameters = { 221 | name: 'John', 222 | lastname: 'Doe' 223 | }; 224 | 225 | var path = '/put'; 226 | 227 | // set up webserver endpoint: 228 | app.put(path, function (req, res) { 229 | res.send('ok'); 230 | 231 | expect(req.body).to.deep.equal(parameters); 232 | 233 | done(); 234 | }); 235 | 236 | // put parameters to webserver endpoint: 237 | httpreq.put(endpointroot + path, { 238 | parameters: parameters 239 | }); 240 | 241 | }); 242 | }); 243 | 244 | describe("patch", () => { 245 | it("should do a simple PATCH request with parameters", (done) => { 246 | 247 | var parameters = { 248 | name: 'John', 249 | lastname: 'Doe' 250 | }; 251 | 252 | var path = '/patch'; 253 | 254 | // set up webserver endpoint: 255 | app.patch(path, function (req, res) { 256 | res.send('ok'); 257 | 258 | expect(req.body).to.deep.equal(parameters); 259 | 260 | done(); 261 | }); 262 | 263 | // patch parameters to webserver endpoint: 264 | httpreq.patch(endpointroot + path, { 265 | parameters: parameters 266 | }); 267 | 268 | }); 269 | }); 270 | 271 | describe("delete", () => { 272 | it("should do a simple DELETE request", (done) => { 273 | 274 | var path = '/delete'; 275 | 276 | // set up webserver endpoint: 277 | app.delete(path, function (req, res) { 278 | res.send('ok'); 279 | 280 | done(); 281 | }); 282 | 283 | // send delete request: 284 | httpreq.delete(endpointroot + path); 285 | }); 286 | }); 287 | 288 | 289 | describe("post json", () => { 290 | it('should POST some json', (done) => { 291 | var somejson = { 292 | name: 'John', 293 | lastname: 'Doe' 294 | }; 295 | 296 | var path = '/postjson'; 297 | 298 | // set up webserver endpoint: 299 | app.post(path, function (req, res) { 300 | res.send('ok'); 301 | 302 | expect(req.body).to.deep.equal(somejson); 303 | 304 | done(); 305 | }); 306 | 307 | httpreq.post(endpointroot + path, { 308 | json: somejson 309 | }); 310 | }); 311 | }); 312 | 313 | 314 | describe("upload file", () => { 315 | it('should upload 1 file (old way)', (done) => { 316 | 317 | var testparams = { 318 | name: 'John', 319 | lastname: 'Doe' 320 | }; 321 | 322 | var testfile = __dirname + "/testupload.jpg"; 323 | 324 | var path = '/uploadfile_old'; 325 | 326 | // set up webserver endpoint: 327 | app.post(path, upload.single('myfile'), function (req, res) { 328 | res.send('ok'); 329 | 330 | expect(req.body).to.deep.equal(testparams); 331 | 332 | comparefiles(req.file.path, testfile, done); 333 | }); 334 | 335 | httpreq.uploadFiles({ 336 | url: endpointroot + path, 337 | parameters: testparams, 338 | files:{ 339 | myfile: testfile 340 | } 341 | }); 342 | }); 343 | 344 | it('should upload 2 files (using POST)', (done) => { 345 | 346 | var testparams = { 347 | name: 'John', 348 | lastname: 'Doe' 349 | }; 350 | 351 | var testfile = __dirname + "/testupload.jpg"; 352 | 353 | var path = '/uploadfiles'; 354 | 355 | // set up webserver endpoint: 356 | app.post(path, upload.fields([{name: 'myfile'}, {name: 'myotherfile'}]), function (req, res) { 357 | res.send('ok'); 358 | 359 | expect(req.body).to.deep.equal(testparams); 360 | 361 | comparefiles(req.files['myfile'][0].path, testfile, function () { 362 | comparefiles(req.files['myotherfile'][0].path, testfile, function () { 363 | done(); 364 | }); 365 | }); 366 | }); 367 | 368 | httpreq.post(endpointroot + path, { 369 | parameters: testparams, 370 | files:{ 371 | myfile: testfile, 372 | myotherfile: testfile 373 | } 374 | }); 375 | }); 376 | 377 | it('should upload 2 files (as array, using POST)', (done) => { 378 | 379 | var testparams = { 380 | name: 'John', 381 | lastname: 'Doe' 382 | }; 383 | 384 | var testfile = __dirname + "/testupload.jpg"; 385 | 386 | var path = '/uploadfiles_array'; 387 | 388 | // set up webserver endpoint: 389 | app.post(path, upload.fields([{name: 'myfiles'}, {name: 'myotherfile'}]), function (req, res) { 390 | res.send('ok'); 391 | 392 | expect(req.body).to.deep.equal(testparams); 393 | 394 | comparefiles(req.files['myfiles'][0].path, testfile, function () { 395 | comparefiles(req.files['myfiles'][1].path, testfile, function () { 396 | done(); 397 | }); 398 | }); 399 | }); 400 | 401 | httpreq.post(endpointroot + path, { 402 | parameters: testparams, 403 | files:{ 404 | myfiles: [testfile, testfile] 405 | } 406 | }); 407 | }); 408 | 409 | it('should upload 2 files (using PUT)', (done) => { 410 | 411 | var testparams = { 412 | name: 'John', 413 | lastname: 'Doe' 414 | }; 415 | 416 | var testfile = __dirname + "/testupload.jpg"; 417 | 418 | var path = '/uploadfiles_put'; 419 | 420 | // set up webserver endpoint: 421 | app.put(path, upload.fields([{name: 'myfile'}, {name: 'myotherfile'}]), function (req, res) { 422 | res.send('ok'); 423 | 424 | expect(req.body).to.deep.equal(testparams); 425 | 426 | comparefiles(req.files['myfile'][0].path, testfile, function () { 427 | comparefiles(req.files['myotherfile'][0].path, testfile, function () { 428 | done(); 429 | }); 430 | }); 431 | }); 432 | 433 | httpreq.put(endpointroot + path, { 434 | parameters: testparams, 435 | files:{ 436 | myfile: testfile, 437 | myotherfile: testfile 438 | } 439 | }); 440 | }); 441 | }); 442 | 443 | describe("download", () => { 444 | it('should download 1 file using get()', async () => { 445 | 446 | var testfile = __dirname + "/testupload.jpg"; 447 | 448 | // no need to set up webserver response, webserver is serving this folder as static files 449 | 450 | var res = await httpreq.get(endpointroot + "/static/testupload.jpg", {binary: true}); 451 | var testfileData = fs.readFileSync(testfile); 452 | expect(res.body).to.deep.equal(testfileData); 453 | }); 454 | 455 | it('should download 1 file directly to disk', async () => { 456 | 457 | var testfile = __dirname + "/testupload.jpg"; 458 | 459 | // no need to set up webserver response, webserver is serving this folder as static files 460 | 461 | var res = await httpreq.download( 462 | endpointroot + "/static/testupload.jpg", 463 | __dirname + "/" + Date.now() + ".jpg" 464 | ); 465 | 466 | console.log('res:', res); 467 | 468 | var testfileData = fs.readFileSync(testfile); 469 | var downloadedFile = fs.readFileSync(res.downloadlocation); 470 | expect(downloadedFile).to.deep.equal(testfileData); 471 | }); 472 | }); 473 | 474 | }); 475 | 476 | 477 | function comparefiles (file1, file2, callback) { 478 | fs.readFile(file1, function (err, file1data) { 479 | if(err) throw err; 480 | 481 | fs.readFile(file2, function (err, file2data) { 482 | if(err) throw err; 483 | 484 | expect(file1data).to.deep.equal(file2data); 485 | 486 | callback(); 487 | }); 488 | }); 489 | } -------------------------------------------------------------------------------- /test/tests.js: -------------------------------------------------------------------------------- 1 | const httpreq = require('../lib/httpreq'); 2 | const assert = require("assert"); 3 | const expect = require("chai").expect; 4 | const fs = require('fs'); 5 | const express = require('express'); 6 | const bodyParser = require('body-parser'); 7 | const multer = require('multer') 8 | const upload = multer({ dest: 'uploads/' }) 9 | 10 | 11 | describe("httpreq", function(){ 12 | 13 | var port, app, webserver, endpointroot; 14 | 15 | before(function (done) { 16 | port = Math.floor( Math.random() * (65535 - 1025) + 1025 ); 17 | 18 | endpointroot = 'http://localhost:' + port; 19 | 20 | app = express(); 21 | 22 | // parse application/x-www-form-urlencoded 23 | app.use(bodyParser.urlencoded({ extended: true })); 24 | 25 | // parse application/json 26 | app.use(bodyParser.json()); 27 | 28 | // serve static files 29 | app.use('/static', express.static(__dirname)) 30 | 31 | webserver = app.listen(port, function(){ 32 | console.log("web server listening on port " + port); 33 | done(); 34 | }); 35 | 36 | }); 37 | 38 | after(function () { 39 | webserver.close(); 40 | }); 41 | 42 | 43 | describe("get", function(){ 44 | 45 | it("should do a simple GET request", function (done){ 46 | 47 | var path = '/get'; // make sure this is unique when writing tests 48 | var jsonData = {some: 'data'}; 49 | 50 | app.get(path, function (req, res) { 51 | res.json(jsonData); 52 | }); 53 | 54 | httpreq.get(endpointroot + path, function (err, res) { 55 | if (err) throw err; 56 | expect(JSON.parse(res.body)).to.deep.equal(jsonData); 57 | done(); 58 | }); 59 | 60 | }); 61 | 62 | it("should do a HTTPS GET request and shuffle the ciphers", function (done){ 63 | httpreq.get('https://www.reichelt.com/be/nl/raspberry-pi-afstandsboutset-30mm-rpi-mountingkit3-p162090.html', {shuffleCiphers: true}, function (err, res) { 64 | if (err) throw err; 65 | 66 | expect(res.statusCode).to.deep.equal(200); 67 | done(); 68 | }); 69 | }); 70 | }); 71 | 72 | 73 | 74 | 75 | describe("head", function(){ 76 | 77 | it("should do a simple HEAD request", function (done){ 78 | 79 | var path = '/head'; 80 | var headerValue = 'abcd'; 81 | 82 | app.head(path, function (req, res) { 83 | res.set('x-user', headerValue); 84 | res.sendStatus(200); 85 | }); 86 | 87 | httpreq.head(endpointroot + path, function (err, res) { 88 | if (err) throw err; 89 | expect(res.headers['x-user']).to.deep.equal(headerValue); 90 | done(); 91 | }); 92 | 93 | }); 94 | 95 | }); 96 | 97 | describe("post", function(){ 98 | 99 | it("should do a simple POST request with parameters", function (done){ 100 | 101 | var parameters = { 102 | name: 'John', 103 | lastname: 'Doe' 104 | }; 105 | 106 | var path = '/post'; 107 | 108 | // set up webserver endpoint: 109 | app.post(path, function (req, res) { 110 | res.send('ok'); 111 | 112 | expect(req.body).to.deep.equal(parameters); 113 | 114 | done(); 115 | }); 116 | 117 | // post parameters to webserver endpoint: 118 | httpreq.post(endpointroot + path, { 119 | parameters: parameters 120 | }, function (err, res){ 121 | if (err) throw err; 122 | }); 123 | 124 | }); 125 | 126 | it("should do a simple POST request with parameters without callback function", function (done){ 127 | 128 | var parameters = { 129 | name: 'John', 130 | lastname: 'Doe' 131 | }; 132 | 133 | var path = '/postnocallback'; 134 | 135 | // set up webserver endpoint: 136 | app.post(path, function (req, res) { 137 | res.send('ok'); 138 | 139 | expect(req.body).to.deep.equal(parameters); 140 | 141 | done(); 142 | }); 143 | 144 | // post parameters to webserver endpoint: 145 | httpreq.post(endpointroot + path, { 146 | parameters: parameters 147 | }); 148 | 149 | }); 150 | 151 | it("should do a simple POST request and check the response", function (done){ 152 | 153 | var jsonData = {some: 'data'}; 154 | 155 | var path = '/postwithresponse'; 156 | 157 | // set up webserver endpoint: 158 | app.post(path, function (req, res) { 159 | res.json(jsonData); 160 | }); 161 | 162 | // post parameters to webserver endpoint: 163 | httpreq.post(endpointroot + path, function (err, res){ 164 | if (err) throw err; 165 | expect(JSON.parse(res.body)).to.deep.equal(jsonData); 166 | done(); 167 | }); 168 | 169 | }); 170 | 171 | it("should do a simple POST request with parameters and cookies", function (done){ 172 | 173 | var parameters = { 174 | name: 'John', 175 | lastname: 'Doe' 176 | }; 177 | 178 | var cookies = [ 179 | 'token=DGcGUmplWQSjfqEvmu%2BZA%2Fc', 180 | 'id=2' 181 | ]; 182 | 183 | var path = '/postcookies'; 184 | 185 | // set up webserver endpoint: 186 | app.post(path, function (req, res) { 187 | res.send('ok'); 188 | 189 | expect(req.body).to.deep.equal(parameters); 190 | expect(req.headers.cookie).to.equal(cookies.join('; ')); 191 | 192 | done(); 193 | }); 194 | 195 | // post testdata to webserver endpoint: 196 | httpreq.post(endpointroot + path, { 197 | parameters: parameters, 198 | cookies: cookies 199 | }, function (err, res){ 200 | if (err) throw err; 201 | }); 202 | 203 | }); 204 | 205 | it("should do a simple POST request with parameters and custom headers", function (done){ 206 | 207 | var parameters = { 208 | name: 'John', 209 | lastname: 'Doe' 210 | }; 211 | 212 | var headers = { 213 | 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10.7; rv:18.0) Gecko/20100101 Firefox/18.0' 214 | }; 215 | 216 | var path = '/postheaders'; 217 | 218 | // set up webserver endpoint: 219 | app.post(path, function (req, res) { 220 | res.send('ok'); 221 | 222 | expect(req.body).to.deep.equal(parameters); 223 | expect(req.headers).to.have.a.property('user-agent', headers['User-Agent']); 224 | 225 | done(); 226 | }); 227 | 228 | // post testdata to webserver endpoint: 229 | httpreq.post(endpointroot + path, { 230 | parameters: parameters, 231 | headers: headers 232 | }, function (err, res){ 233 | if (err) throw err; 234 | }); 235 | 236 | }); 237 | 238 | }); 239 | 240 | 241 | describe("put", function(){ 242 | it("should do a simple PUT request with parameters", function (done){ 243 | 244 | var parameters = { 245 | name: 'John', 246 | lastname: 'Doe' 247 | }; 248 | 249 | var path = '/put'; 250 | 251 | // set up webserver endpoint: 252 | app.put(path, function (req, res) { 253 | res.send('ok'); 254 | 255 | expect(req.body).to.deep.equal(parameters); 256 | 257 | done(); 258 | }); 259 | 260 | // put parameters to webserver endpoint: 261 | httpreq.put(endpointroot + path, { 262 | parameters: parameters 263 | }, function (err, res){ 264 | if (err) throw err; 265 | }); 266 | 267 | }); 268 | }); 269 | 270 | describe("patch", function(){ 271 | it("should do a simple PATCH request with parameters", function (done){ 272 | 273 | var parameters = { 274 | name: 'John', 275 | lastname: 'Doe' 276 | }; 277 | 278 | var path = '/patch'; 279 | 280 | // set up webserver endpoint: 281 | app.patch(path, function (req, res) { 282 | res.send('ok'); 283 | 284 | expect(req.body).to.deep.equal(parameters); 285 | 286 | done(); 287 | }); 288 | 289 | // patch parameters to webserver endpoint: 290 | httpreq.patch(endpointroot + path, { 291 | parameters: parameters 292 | }, function (err, res){ 293 | if (err) throw err; 294 | }); 295 | 296 | }); 297 | }); 298 | 299 | describe("delete", function(){ 300 | it("should do a simple DELETE request", function (done){ 301 | 302 | var path = '/delete'; 303 | 304 | // set up webserver endpoint: 305 | app.delete(path, function (req, res) { 306 | res.send('ok'); 307 | 308 | done(); 309 | }); 310 | 311 | // send delete request: 312 | httpreq.delete(endpointroot + path, function (err, res){ 313 | if (err) throw err; 314 | }); 315 | 316 | }); 317 | }); 318 | 319 | 320 | describe("post json", function () { 321 | it('should POST some json', function (done) { 322 | var somejson = { 323 | name: 'John', 324 | lastname: 'Doe' 325 | }; 326 | 327 | var path = '/postjson'; 328 | 329 | // set up webserver endpoint: 330 | app.post(path, function (req, res) { 331 | res.send('ok'); 332 | 333 | expect(req.body).to.deep.equal(somejson); 334 | 335 | done(); 336 | }); 337 | 338 | httpreq.post(endpointroot + path, { 339 | json: somejson 340 | }, function (err, res){ 341 | if (err) throw err; 342 | }); 343 | }); 344 | }); 345 | 346 | 347 | describe("upload file", function () { 348 | it('should upload 1 file (old way)', function (done) { 349 | 350 | var testparams = { 351 | name: 'John', 352 | lastname: 'Doe' 353 | }; 354 | 355 | var testfile = __dirname + "/testupload.jpg"; 356 | 357 | var path = '/uploadfile_old'; 358 | 359 | // set up webserver endpoint: 360 | app.post(path, upload.single('myfile'), function (req, res) { 361 | res.send('ok'); 362 | 363 | expect(req.body).to.deep.equal(testparams); 364 | 365 | comparefiles(req.file.path, testfile, done); 366 | }); 367 | 368 | httpreq.uploadFiles({ 369 | url: endpointroot + path, 370 | parameters: testparams, 371 | files:{ 372 | myfile: testfile 373 | } 374 | }, function (err, res){ 375 | if (err) throw err; 376 | }); 377 | }); 378 | 379 | it('should upload 2 files (using POST)', function (done) { 380 | 381 | var testparams = { 382 | name: 'John', 383 | lastname: 'Doe' 384 | }; 385 | 386 | var testfile = __dirname + "/testupload.jpg"; 387 | 388 | var path = '/uploadfiles'; 389 | 390 | // set up webserver endpoint: 391 | app.post(path, upload.fields([{name: 'myfile'}, {name: 'myotherfile'}]), function (req, res) { 392 | res.send('ok'); 393 | 394 | expect(req.body).to.deep.equal(testparams); 395 | 396 | comparefiles(req.files['myfile'][0].path, testfile, function () { 397 | comparefiles(req.files['myotherfile'][0].path, testfile, function () { 398 | done(); 399 | }); 400 | }); 401 | }); 402 | 403 | httpreq.post(endpointroot + path, { 404 | parameters: testparams, 405 | files:{ 406 | myfile: testfile, 407 | myotherfile: testfile 408 | } 409 | }, function (err, res){ 410 | if (err) throw err; 411 | }); 412 | }); 413 | 414 | it('should upload 2 files (as array, using POST)', function (done) { 415 | 416 | var testparams = { 417 | name: 'John', 418 | lastname: 'Doe' 419 | }; 420 | 421 | var testfile = __dirname + "/testupload.jpg"; 422 | 423 | var path = '/uploadfiles_array'; 424 | 425 | // set up webserver endpoint: 426 | app.post(path, upload.fields([{name: 'myfiles'}]), function (req, res) { 427 | res.send('ok'); 428 | 429 | expect(req.body).to.deep.equal(testparams); 430 | 431 | comparefiles(req.files['myfiles'][0].path, testfile, function () { 432 | comparefiles(req.files['myfiles'][1].path, testfile, function () { 433 | done(); 434 | }); 435 | }); 436 | }); 437 | 438 | httpreq.post(endpointroot + path, { 439 | parameters: testparams, 440 | files:{ 441 | myfiles: [testfile, testfile] 442 | } 443 | }, function (err, res){ 444 | if (err) throw err; 445 | }); 446 | }); 447 | 448 | it('should upload 2 files (using PUT)', function (done) { 449 | 450 | var testparams = { 451 | name: 'John', 452 | lastname: 'Doe' 453 | }; 454 | 455 | var testfile = __dirname + "/testupload.jpg"; 456 | 457 | var path = '/uploadfiles_put'; 458 | 459 | // set up webserver endpoint: 460 | app.put(path, upload.fields([{name: 'myfile'}, {name: 'myotherfile'}]), function (req, res) { 461 | res.send('ok'); 462 | 463 | expect(req.body).to.deep.equal(testparams); 464 | 465 | comparefiles(req.files['myfile'][0].path, testfile, function () { 466 | comparefiles(req.files['myotherfile'][0].path, testfile, function () { 467 | done(); 468 | }); 469 | }); 470 | }); 471 | 472 | httpreq.put(endpointroot + path, { 473 | parameters: testparams, 474 | files:{ 475 | myfile: testfile, 476 | myotherfile: testfile 477 | } 478 | }, function (err, res){ 479 | if (err) throw err; 480 | }); 481 | }); 482 | }); 483 | 484 | describe("download", function () { 485 | it('should download 1 file using get()', function (done) { 486 | 487 | var testfile = __dirname + "/testupload.jpg"; 488 | 489 | // no need to set up webserver response, webserver is serving this folder as static files 490 | 491 | httpreq.get(endpointroot + "/static/testupload.jpg", {binary: true}, function (err, res) { 492 | if (err) throw err; 493 | 494 | fs.readFile(testfile, function (err, testfileData) { 495 | if(err) throw err; 496 | 497 | expect(res.body).to.deep.equal(testfileData); 498 | done(); 499 | }); 500 | }); 501 | 502 | }); 503 | 504 | it('should download 1 file directly to disk', function (done) { 505 | 506 | var testfile = __dirname + "/testupload.jpg"; 507 | 508 | // no need to set up webserver response, webserver is serving this folder as static files 509 | 510 | httpreq.download( 511 | endpointroot + "/static/testupload.jpg", 512 | __dirname + "/" + Date.now() + ".jpg" 513 | , function (err, progress){ 514 | if (err) throw err; 515 | console.log('download progress:', progress.percentage); 516 | }, function (err, res){ 517 | if (err) throw err; 518 | comparefiles(res.downloadlocation, testfile, function () { 519 | done(); 520 | }); 521 | }); 522 | 523 | }); 524 | }); 525 | 526 | }); 527 | 528 | 529 | function comparefiles (file1, file2, callback) { 530 | fs.readFile(file1, function (err, file1data) { 531 | if(err) throw err; 532 | 533 | fs.readFile(file2, function (err, file2data) { 534 | if(err) throw err; 535 | 536 | expect(file1data).to.deep.equal(file2data); 537 | 538 | callback(); 539 | }); 540 | }); 541 | } -------------------------------------------------------------------------------- /test/testupload.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SamDecrock/node-httpreq/ada2be73ada26d2af716283564fab3a6273573c3/test/testupload.jpg --------------------------------------------------------------------------------