commit 956c16e48c8dc665b7bec470298b0c991011028d Author: AJ ONeal Date: Mon Jun 18 19:39:19 2018 -0600 initial commit diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..693448a --- /dev/null +++ b/LICENSE @@ -0,0 +1,41 @@ +Copyright 2018 AJ ONeal + +This is open source software; you can redistribute it and/or modify it under the +terms of either: + + a) the "MIT License" + b) the "Apache-2.0 License" + +MIT License + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE. + +Apache-2.0 License Summary + + 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. diff --git a/README.md b/README.md new file mode 100644 index 0000000..86eade6 --- /dev/null +++ b/README.md @@ -0,0 +1,16 @@ +# µRequest - Minimalist HTTP client + +A minimal drop-in replacement for request with 0 dependenciese. + +## Super simple to use + +µRequest is designed to be a minimal drop-in replacement for request. + +```js +var request = require('urequest'); +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. +}); +``` diff --git a/examples/debug.js b/examples/debug.js new file mode 100644 index 0000000..eb512cf --- /dev/null +++ b/examples/debug.js @@ -0,0 +1,7 @@ +'use strict'; + +// if NODE_DEBUG contains 'urequest' then this should print out 'DEBUG ON for urequest' +// NODE_DEBUG="urequest" node examples/debug.js +// +//require('urequest'); +require('../'); diff --git a/examples/follow-redirect.js b/examples/follow-redirect.js new file mode 100644 index 0000000..ebeb03a --- /dev/null +++ b/examples/follow-redirect.js @@ -0,0 +1,15 @@ +'use strict'; + +//var request = require('urequest'); +var request = require('../'); + +// will redirect to https://www.github.com and then https://github.com +request('http://www.github.com', function (error, response, body) { + if (error) { + console.log('error:', error); // Print the error if one occurred + return; + } + console.log('statusCode:', response.statusCode); // The final statusCode + console.log('Final URI:', response.request.uri); // The final URI + console.log('Body Length:', body.length); // body length +}); diff --git a/examples/http-string.js b/examples/http-string.js new file mode 100644 index 0000000..e263377 --- /dev/null +++ b/examples/http-string.js @@ -0,0 +1,9 @@ +'use strict'; + +//var request = require('urequest'); +var request = require('../'); +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. +}); diff --git a/examples/https-string.js b/examples/https-string.js new file mode 100644 index 0000000..04e4eaa --- /dev/null +++ b/examples/https-string.js @@ -0,0 +1,9 @@ +'use strict'; + +//var request = require('urequest'); +var request = require('../'); +request('https://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. +}); diff --git a/examples/no-follow-redirect.js b/examples/no-follow-redirect.js new file mode 100644 index 0000000..003ab3b --- /dev/null +++ b/examples/no-follow-redirect.js @@ -0,0 +1,16 @@ +'use strict'; + +//var request = require('urequest'); +var request = require('../'); + +// would normally redirect to https://www.github.com and then https://github.com +request({ uri: 'https://www.github.com', followRedirect: false }, function (error, response, body) { + if (error) { + console.log('error:', error); // Print the error if one occurred + return; + } + console.log('URI:', response.request.uri); // The final URI + console.log('statusCode:', response.statusCode); // Should be 301 or 302 + console.log('Location:', response.headers.location); // The redirect + console.log('Body:', body || JSON.stringify(body)); +}); diff --git a/index.js b/index.js new file mode 100644 index 0000000..7509cd4 --- /dev/null +++ b/index.js @@ -0,0 +1,196 @@ +'use strict'; + +var http = require('http'); +var https = require('https'); +var url = require('url'); + +function debug() { + if (module.exports.debug) { + console.log.apply(console, arguments); + } +} + +function applyUrl(opts, urlstr) { + var urlObj = url.parse(urlstr); + opts.url = opts.uri = urlstr; + Object.keys(urlObj).forEach(function (key) { + opts[key] = urlObj[key]; + }); + return opts; +} + +function mergeOrDelete(defaults, updates) { + Object.keys(defaults).forEach(function (key) { + if (!(key in updates)) { + updates[key] = defaults[key]; + return; + } + + // neither accept the prior default nor define an explicit value + // CRDT probs... + if ('undefined' === typeof updates[key]) { + delete updates[key]; + } else if ('object' === typeof defaults[key] && 'object' === typeof updates[key]) { + updates[key] = mergeOrDelete(defaults[key], updates[key]); + } + }); + + return updates; +} + +function setDefaults(defs) { + defs = defs || {}; + + function urequest(opts, cb) { + var req; + // request.js behavior: + // encoding: null + json ? unknown + // json => attempt to parse, fail silently + // encoding => buffer.toString(encoding) + // null === encoding => Buffer.concat(buffers) + if ('string' === typeof opts) { + opts = { url: opts }; + } + if (!opts.method) { + opts.method = 'GET'; + } + if (!opts.method) { + opts.method = 'GET'; + } + if (opts.url || opts.uri) { + opts = applyUrl(opts, opts.url || opts.uri); + } + function onResponse(resp) { + var encoding = opts.encoding || defs.encoding; + var followRedirect; + Object.keys(defs).forEach(function (key) { + if (key in opts && 'undefined' !== typeof opts[key]) { + return; + } + opts[key] = defs[key]; + }); + followRedirect = opts.followRedirect; + + resp.request = req; + resp.request.uri = opts.uri; + if (resp.headers.location && opts.followRedirect) { + debug('Following redirect: ' + resp.headers.location); + if (opts.followAllRedirects || -1 !== [ 301, 302 ].indexOf(resp.statusCode)) { + followRedirect = true; + } + if (opts._redirectCount >= opts.maxRedirects) { + followRedirect = false; + } + if ('function' === opts.followRedirect) { + if (!opts.followRedirect(resp)) { + followRedirect = false; + } + } + if (!opts.followOriginalHttpMethod) { + opts.method = 'GET'; + opts.body = null; + } + if (followRedirect) { + opts.url = resp.headers.location; + return urequest(opts, cb); + } + if (opts.removeRefererHeader && opts.headers) { + delete opts.headers.referer; + } + } + if (null === encoding) { + resp._body = []; + } else { + resp.body = ''; + } + resp._bodyLength = 0; + resp.on('data', function (chunk) { + if ('string' === typeof resp.body) { + resp.body += chunk.toString(encoding); + } else { + resp._body.push(chunk); + resp._bodyLength += chunk.length; + } + }); + resp.on('end', function () { + if ('string' !== typeof resp.body) { + if (1 === resp._body.length) { + resp.body = resp._body[0]; + } else { + resp.body = Buffer.concat(resp._body, resp._bodyLength); + } + resp._body = null; + } + if (opts.json && 'string' === typeof resp.body) { + // TODO I would parse based on Content-Type + // but request.js doesn't do that. + try { + resp.body = JSON.parse(resp.body); + } catch(e) { + // ignore + } + } + cb(null, resp, resp.body); + }); + } + + if (!opts.protocol) { + opts.protocol = 'https:'; + } + if ('https:' === opts.protocol) { + req = https.request(opts, onResponse); + } else if ('http:' === opts.protocol) { + req = http.request(opts, onResponse); + } else { + throw new Error("unknown protocol: '" + opts.protocol + "'"); + } + req.on('error', function (e) { + cb(e); + }); + if (opts.body) { + if (true === opts.json) { + req.end(JSON.stringify(opts.json)); + } else { + req.end(opts.body); + } + } else if (opts.json && true !== opts.json) { + req.end(JSON.stringify(opts.json)); + } else { + req.end(); + } + } + + urequest.defaults = function (_defs) { + _defs = mergeOrDelete(defs, _defs); + return setDefaults(_defs); + }; + [ 'get', 'put', 'post', 'patch', 'delete', 'head', 'options' ].forEach(function (method) { + urequest[method] = function (obj) { + if ('string' === typeof obj) { + obj = { url: obj }; + } + obj.method = method.toUpperCase(); + urequest(obj); + }; + }); + urequest.del = urequest.delete; + + return urequest; +} + +module.exports = setDefaults({ + sendImmediately: true +, method: 'GET' +, headers: {} +, useQuerystring: false +, followRedirect: true +, followAllRedirects: false +, followOriginalHttpMethod: false +, maxRedirects: 10 +, removeRefererHeader: false +//, encoding: undefined +, gzip: false +}); +module.exports.debug = (-1 !== (process.env.NODE_DEBUG||'').split(/\s+/g).indexOf('urequest')); + +debug("DEBUG ON for urequest"); diff --git a/package.json b/package.json new file mode 100644 index 0000000..f5f737b --- /dev/null +++ b/package.json @@ -0,0 +1,26 @@ +{ + "name": "urequest", + "version": "1.0.0", + "description": "A lightweight drop-in replacement for request", + "main": "index.js", + "directories": { + "example": "examples" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "https://git.ppl.family/ppl/urequest.js.git" + }, + "keywords": [ + "request", + "lightweight", + "alternative", + "http", + "https", + "call" + ], + "author": "AJ ONeal (https://coolaj86.com/)", + "license": "(MIT OR Apache-2.0)" +}