Socket
Socket
Sign inDemoInstall

http-call

Package Overview
Dependencies
Maintainers
4
Versions
62
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

http-call - npm Package Compare versions

Comparing version 2.1.6 to 3.0.0

287

lib/http.js

@@ -45,3 +45,3 @@ 'use strict';

const debug = require('debug')('http-call');
const debug = require('debug')('http');

@@ -66,2 +66,34 @@ function concat(stream) {

function caseInsensitiveObject() {
let lowercaseKey = k => typeof k === 'string' ? k.toLowerCase() : k;
return new Proxy({}, {
get: (t, k) => {
k = lowercaseKey(k);
return t[k];
},
set: (t, k, v) => {
k = lowercaseKey(k);
t[k] = v;
return true;
},
deleteProperty: (t, k) => {
k = lowercaseKey(k);
if (k in t) return false;
return delete t[k];
},
has: function (t, k) {
k = lowercaseKey(k);
return k in t;
}
});
}
function lowercaseHeaders(headers) {
let newHeaders = caseInsensitiveObject();
for (let [k, v] of Object.entries(headers)) {
newHeaders[k] = v;
}
return newHeaders;
}
/**

@@ -83,6 +115,4 @@ * Utility for simple HTTP calls

*/
static async get(url, options = {}) {
options.method = 'GET';
let http = await this.request(url, options);
return this._getNextBody(http);
static get(url, options = {}) {
return this.request(url, { ...options, method: 'GET' });
}

@@ -101,6 +131,4 @@

*/
static async post(url, options = {}) {
options.method = 'POST';
let http = await this.request(url, options);
return http.body;
static post(url, options = {}) {
return this.request(url, { ...options, method: 'POST' });
}

@@ -119,6 +147,4 @@

*/
static async put(url, options = {}) {
options.method = 'PUT';
let http = await this.request(url, options);
return http.body;
static put(url, options = {}) {
return this.request(url, { ...options, method: 'PUT' });
}

@@ -138,5 +164,3 @@

static async patch(url, options = {}) {
options.method = 'PATCH';
let http = await this.request(url, options);
return http.body;
return this.request(url, { ...options, method: 'PATCH' });
}

@@ -156,5 +180,3 @@

static async delete(url, options = {}) {
options.method = 'DELETE';
let http = await this.request(url, options);
return http.body;
return this.request(url, { ...options, method: 'DELETE' });
}

@@ -174,7 +196,4 @@

*/
static async stream(url, options = {}) {
options.method = options.method || 'GET';
options.raw = true;
let http = await this.request(url, options);
return http.response;
static stream(url, options = {}) {
return this.request(url, { ...options, raw: true });
}

@@ -188,46 +207,102 @@

static defaults(options = {}) {
return class CustomHTTP extends HTTP {
get defaultOptions() {
return {
...super.defaultOptions,
...options
};
}
};
}
// instance properties
get method() {
return this.options.method;
}
get statusCode() {
if (!this.response) return 0;
return this.response.statusCode;
}
get secure() {
return this.options.protocol === 'https:';
}
get url() {
return `${this.options.protocol}//${this.options.host}${this.options.path}`;
}
set url(input) {
let u = _url2.default.parse(input);
this.options.protocol = u.protocol || this.options.protocol;
this.options.host = u.hostname || this.defaultOptions.host || 'localhost';
this.options.path = u.path || '/';
this.options.agent = this.options.agent || _proxy2.default.agent(this.secure);
this.options.port = parseInt(u.port || this.defaultOptions.port || (this.secure ? 443 : 80));
}
get headers() {
if (!this.response) return {};
return this.response.headers;
}
get partial() {
if (this.method !== 'GET' || this.options.partial) return true;
return !(this.headers['next-range'] && this.body instanceof Array);
}
get defaultOptions() {
return {
method: 'GET',
host: 'localhost',
protocol: 'https:',
path: '/',
raw: false,
partial: false,
headers: {
'user-agent': `${_package2.default.name}/${_package2.default.version} node-${process.version}`
}
};
}
constructor(url, options = {}) {
this.method = 'GET';
this.host = 'localhost';
this.port = 0;
this.protocol = 'https:';
this.path = '/';
this.raw = false;
this.partial = false;
this.headers = {
'user-agent': `${_package2.default.name}/${_package2.default.version} node-${process.version}`
this.options = {
...this.defaultOptions,
...options,
headers: lowercaseHeaders({
...this.defaultOptions.headers,
...options.headers
})
};
if (!url) throw new Error('no url provided');
this.options = options;
let headers = Object.assign(this.headers, options.headers);
Object.assign(this, options);
this.headers = headers;
let u = _url2.default.parse(url);
this.protocol = u.protocol || this.protocol;
this.host = u.hostname || this.host;
this.port = u.port || this.port || (this.protocol === 'https:' ? 443 : 80);
this.path = u.path || this.path;
if (options.body) this.parseBody(options.body);
this.body = undefined;
this.agent = _proxy2.default.agent(this.protocol === 'https:');
if (this.agent) debug('proxy: %j', this.agent.options);
this.url = url;
if (this.options.body) this._parseBody(this.options.body);
}
async _request(retries = 0) {
async _request() {
this._debugRequest();
try {
debug(`--> ${this.method} ${this.url}`);
this.response = await this.performRequest();
debug(`<-- ${this.method} ${this.url} ${this.response.statusCode}`);
this.response = await this._performRequest();
} catch (err) {
return this.maybeRetry(err, retries);
return this._maybeRetry(err);
}
if (this.response.statusCode >= 200 && this.response.statusCode < 300) {
if (!this.raw) this.body = await this.parse(this.response);
} else throw new HTTPError(this, (await this.parse(this.response)));
if (this._shouldParseResponseBody) await this._parse();
this._debugResponse();
if (this._responseRedirect) return this._redirect();
if (!this._responseOK) {
throw new HTTPError(this);
}
if (!this.partial) await this._getNextRange();
}
async maybeRetry(err, retries) {
async _redirect() {
if (!this._redirectRetries) this._redirectRetries = 0;
this._redirectRetries++;
if (this._redirectRetries > 10) throw new Error(`Redirect loop at ${this.url}`);
if (!this.headers.location) throw new Error('Redirect with no location header');
this.url = this.headers.location;
await this._request();
}
async _maybeRetry(err) {
if (!this._errorRetries) this._errorRetries = 0;
this._errorRetries++;
const allowed = err => {
if (retries >= 5) return false;
if (this._errorRetries > 5) return false;
if (!err.code) return false;

@@ -239,4 +314,4 @@ if (err.code === 'ENOTFOUND') return true;

let noise = Math.random() * 100;
await this._wait((1 << retries) * 1000 + noise);
await this._request(retries + 1);
await this._wait((1 << this._errorRetries) * 1000 + noise);
await this._request();
return;

@@ -247,18 +322,23 @@ }

get http() {
return this.protocol === 'https:' ? _https2.default : _http2.default;
_debugRequest() {
if (this.options.agent) debug('proxy: %o', this.options.agent.options);
debug('--> %s %s %O', this.options.method, this.url, this._redactedHeaders(this.options.headers));
}
get url() {
return `${this.protocol}//${this.host}${this.path}`;
_debugResponse() {
if (this.body) {
debug('<-- %s %s %s\nHeaders: %O\nBody: %O', this.method, this.url, this.statusCode, this._redactedHeaders(this.headers), this.body);
} else {
debug('<-- %s %s %s\nHeaders: %O\nBody: %O', this.method, this.url, this.statusCode, this._redactedHeaders(this.headers), this.body);
}
}
performRequest() {
_performRequest() {
return new Promise((resolve, reject) => {
this.request = this.http.request(this, resolve);
this.request = this._http.request(this.options, resolve);
this.request.on('error', reject);
if (_isStream2.default.readable(this.requestBody)) {
this.requestBody.pipe(this.request);
if (this.options.body && _isStream2.default.readable(this.options.body)) {
this.options.body.pipe(this.request);
} else {
this.request.end(this.requestBody);
this.request.end(this.options.body);
}

@@ -268,33 +348,56 @@ });

async parse(response) {
let body = await concat(response);
return response.headers['content-type'] === 'application/json' ? JSON.parse(body) : body;
async _parse() {
this.body = await concat(this.response);
let json = this.headers['content-type'] === 'application/json';
if (json) this.body = JSON.parse(this.body);
}
parseBody(body) {
_parseBody(body) {
if (_isStream2.default.readable(body)) {
this.requestBody = body;
this.options.body = body;
return;
}
if (!this.headers['Content-Type']) {
this.headers['Content-Type'] = 'application/json';
if (!this.options.headers['content-type']) {
this.options.headers['content-type'] = 'application/json';
}
if (this.headers['Content-Type'] === 'application/json') {
this.requestBody = JSON.stringify(body);
if (this.options.headers['content-type'] === 'application/json') {
this.options.body = JSON.stringify(body);
} else {
this.requestBody = body;
this.options.body = body;
}
this.headers['Content-Length'] = Buffer.byteLength(this.requestBody).toString();
this.options.headers['content-length'] = Buffer.byteLength(this.options.body).toString();
}
static async _getNextBody(http) {
if (http.partial || !http.response.headers['next-range'] || !(http.body instanceof Array)) return http.body;
let opts = { headers: {} };
opts = Object.assign(opts, http.options);
opts.headers['range'] = http.response.headers['next-range'];
let next = await this.get(http.url, opts);
return http.body.concat(next);
async _getNextRange() {
this.options.headers['range'] = this.headers['next-range'];
let prev = this.body;
await this._request();
this.body = prev.concat(this.body);
}
_redactedHeaders(headers) {
headers = { ...headers };
if (headers.authorization) headers.authorization = '[REDACTED]';
return headers;
}
get _http() {
return this.secure ? _https2.default : _http2.default;
}
get _responseOK() {
if (!this.response) return false;
return this.statusCode >= 200 && this.statusCode < 300;
}
get _responseRedirect() {
if (!this.response) return false;
return this.statusCode >= 300 && this.statusCode < 400;
}
get _shouldParseResponseBody() {
return !this._responseOK || !this.options.raw && this._responseOK;
}
_wait(ms) {

@@ -308,14 +411,14 @@ return new Promise(resolve => setTimeout(resolve, ms));

constructor(http, body) {
constructor(http) {
let message;
if (typeof body === 'string' || typeof body.message === 'string') message = body.message || body;else message = _util2.default.inspect(body);
super(`HTTP Error ${http.response.statusCode} for ${http.method} ${http.url}\n${message}`);
if (typeof http.body === 'string' || typeof http.body.message === 'string') message = http.body.message || http.body;else message = _util2.default.inspect(http.body);
super(`HTTP Error ${http.statusCode} for ${http.method} ${http.url}\n${message}`);
this.__httpcall = true;
this.statusCode = http.response.statusCode;
this.statusCode = http.statusCode;
this.http = http;
this.body = body;
this.body = http.body;
}
}
exports.HTTPError = HTTPError; // commonjs helpers
exports.HTTPError = HTTPError; // common/s helpers

@@ -322,0 +425,0 @@ function get(url, options = {}) {

{
"name": "http-call",
"description": "make http requests",
"version": "2.1.6",
"version": "3.0.0",
"author": "Jeff Dickey @dickeyxxx",
"bugs": "https://github.com/dickeyxxx/http-call/issues",
"dependencies": {
"debug": "^3.0.0",
"debug": "^3.0.1",
"is-retry-allowed": "^1.1.0",

@@ -14,11 +14,12 @@ "is-stream": "^1.1.0",

"devDependencies": {
"babel-cli": "6.24.1",
"babel-core": "^6.25.0",
"babel-cli": "^6.26.0",
"babel-core": "^6.26.0",
"babel-eslint": "7.2.3",
"babel-jest": "20.0.3",
"babel-plugin-syntax-object-rest-spread": "^6.13.0",
"babel-plugin-transform-class-properties": "6.24.1",
"babel-plugin-transform-es2015-modules-commonjs": "6.24.1",
"babel-plugin-transform-es2015-modules-commonjs": "^6.26.0",
"babel-plugin-transform-flow-strip-types": "6.22.0",
"flow-bin": "^0.52.0",
"flow-copy-source": "^1.2.0",
"flow-bin": "0.52.0",
"flow-copy-source": "^1.2.1",
"flow-typed": "^2.1.5",

@@ -33,3 +34,3 @@ "jest": "20.0.4",

"engines": {
"node": ">=7.6.0"
"node": ">=8.3.0"
},

@@ -40,7 +41,2 @@ "files": [

"homepage": "https://github.com/dickeyxxx/http-call",
"jest": {
"testEnvironment": "node",
"coverageDirectory": "./coverage/",
"collectCoverage": true
},
"keywords": [

@@ -72,3 +68,4 @@ "http",

"$Diff",
"$Shape"
"$Shape",
"Class"
],

@@ -75,0 +72,0 @@ "ignore": [

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc