Files
wumpfetch/lib/model/WumpRequest.js
David Ralph d360bdcc07 More Improvements (see full info)
Here's what I did and why I did it:
* Remove README.md information about why use Wumpfetch - Users already know it's light from the start and from package phobia, no point adding extra unneeded information.
* Made gulpfile.js lighter - Made it like the other files
* Edit LICENSE - There was an unneeded gap
* Index.js modifications -  No longer does it hard code the package name and URL, making life easier for people forking it or using it in business. Also changed request to req to make it lighter!
* .gitignore edit - Removed unneeded "/"
* WumpRequest.js Improvements - Package name and URL are also no longer hard coded here, renamed consts to decrease file size.

Now under 10kb!
2019-04-11 18:28:38 +01:00

172 lines
5.2 KiB
JavaScript

const h = require('http');
const hs = require('https');
const u = require('url');
const pa = require('path');
const qs = require('querystring');
const zl = require('zlib');
const WumpRes = require('./WumpResponse');
const pkg = require('../package.json');
const w = require('../index');
const c = [ 'gzip', 'deflate' ];
module.exports = class WumpRequest {
constructor (url = {}, method = {}) {
const o = (typeof url === 'string' ? method : url);
const obj = (typeof o === 'object');
if (typeof url !== 'string' && !o.hasOwnProperty('url')) throw new Error('Missing URL parameter');
this.o = {
'm': typeof o === 'string' ? o : obj && o.method ? o.method : 'GET',
'url': typeof url === 'string' ? new u.URL(url) : obj && typeof o.url === 'string' ? new u.URL(o.url) : url,
'SDA': obj && typeof o.sendDataAs === 'string' ? o.sendDataAs : obj && o.data && typeof o.data === 'object' ? 'json' : undefined,
'data': obj && o.body ? o.body : obj && o.data ? o.data : obj && o.json ? o.json : obj && o.form ? qs.stringify(o.form) : undefined,
'parse': obj && o.parse ? o.parse : undefined,
'follow': !!(obj && o.followRedirects),
'rHeaders': obj && typeof o.headers === 'object' ? o.headers : {},
'streamed': !!(obj && o.streamed),
'compressed': !!(obj && o.compressed),
'timeoutTime': obj && typeof o.timeout === 'number' ? o.timeout : null,
'coreOptions': obj && typeof o.coreOptions === 'object' ? o.coreOptions : {}
};
if (typeof o.core === 'object') Object.keys(o.core).forEach((v) => this.option(v, o.core[v]));
return this;
}
query (a, b) {
if (typeof a === 'object') Object.keys(a).forEach((v) => this.o.url.searchParams.append(v, a[v]));
else this.o.url.searchParams.append(a, b);
return this;
}
body (data, SA) {
this.o.SDA = typeof data === 'object' && !SA && !Buffer.isBuffer(data) ? 'json' : (SA ? SA.toLowerCase() : 'buffer');
this.o.data = this.SDA === 'form' ? qs.stringify(data) : (this.SDA === 'json' ? JSON.stringify(data) : data);
return this;
}
header (a, b) {
if (typeof a === 'object') Object.keys(a).forEach((v) => this.o.rHeaders[v.toLowerCase()] = a[v]);
else this.o.rHeaders[a.toLowerCase()] = b;
return this;
}
compress () {
this.compressed = true;
if (!this.o.rHeaders['accept-encoding']) this.o.rHeaders['accept-encoding'] = c.join(', ');
return this;
}
path (p) {
this.o.url.pathname = pa.join(this.o.url.pathname, p);
return this;
}
stream () {
this.o.streamed = true;
return this;
}
option (n, v) {
this.o.coreOptions[n] = v;
return this;
}
timeout (timeout) {
this.o.timeoutTime = timeout;
return this;
}
send () {
return new Promise((resolve, reject) => {
if (this.o.data) {
if (!this.o.rHeaders.hasOwnProperty('user-agent')) this.o.rHeaders['User-Agent'] = w.userAgent || `${pkg.name}/${pkg.version} (${pkg.git.repository})`;
if (this.o.SDA === 'json' && !this.o.rHeaders.hasOwnProperty('content-type')) this.o.rHeaders['Content-Type'] = 'application/json';
if (this.o.SDA === 'form') {
if (!this.o.rHeaders.hasOwnProperty('content-type')) this.o.rHeaders['Content-Type'] = 'application/x-www-form-urlencoded';
if (!this.o.rHeaders.hasOwnProperty('content-length')) this.o.rHeaders['Content-Length'] = Buffer.byteLength(this.o.data);
}
}
let req;
const options = {
'host': this.o.url.hostname,
'port': this.o.url.port,
'path': this.o.url.pathname + this.o.url.search,
'method': this.o.m,
'headers': this.o.rHeaders,
'protocol': this.o.url.protocol,
...this.o.coreOptions
}
const handler = async (res) => {
let stream = res;
if (this.o.compressed) {
if (res.headers['content-encoding'] === 'gzip') stream = res.pipe(zl.createGunzip());
else if (res.headers[ 'content-encoding'] === 'deflate') stream = res.pipe(zl.createInflate());
}
let wumpRes;
if (this.o.streamed) resolve(stream);
else {
wumpRes = new WumpRes(res);
if (res.headers.hasOwnProperty('location') && this.o.follow) {
this.o.url = (new u.URL(res.headers['location'], this.o.url)).toString();
return await w(this.o);
}
stream.on('error', (e) => reject(e));
stream.on('data', (c) => wumpRes._addChunk(c));
stream.on('end', () => {
if (this.o.parse) {
if (this.o.parse === 'json') wumpRes.body = JSON.parse(wumpRes.body);
else if (this.o.parse === 'text') wumpRes.body = wumpRes.body.toString();
else wumpRes.body = wumpRes.body;
}
resolve(wumpRes);
});
}
};
if (this.o.url.protocol === 'http:') req = h.request(options, handler);
else if (this.o.url.protocol === 'https:') req = hs.request(options, handler);
else throw new Error(`Bad URL protocol: ${this.o.url.protocol}`);
if (this.o.timeoutTime) {
req.setTimeout(this.o.timeoutTime, () => {
req.abort();
if (!this.o.streamed) reject(new Error('Timeout reached'));
});
}
req.on('error', (e) => reject(e));
if (this.o.data) {
if (this.o.SDA === 'json') req.write(JSON.stringify(this.o.data));
else {
if (typeof this.o.data === 'object') req.write(JSON.stringify(this.o.data));
else req.write(this.o.data);
}
}
req.end();
});
}
};