Last active
February 23, 2021 04:29
-
-
Save alex-taxiera/98d9fc332746d2442e9d65c0ba487d49 to your computer and use it in GitHub Desktop.
node http request wrapper (JS and TS)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"name": "request", | |
"version": "1.0.0", | |
"main": "request.js" | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import https from 'https' | |
import http from 'http' | |
import { URL } from 'url' | |
import zlib from 'zlib' | |
import FormData from 'form-data' | |
export const requesters = { | |
'http:': http, | |
'https:': https, | |
} | |
export const decompressors = { | |
deflate: zlib.createInflate, | |
gzip: zlib.createGunzip, | |
} | |
export default function request ( | |
url, | |
options = {}, | |
) { | |
const { | |
method = 'GET', | |
body, | |
timeout, | |
...reqOptions, | |
} = options | |
if (!http.METHODS.includes(method.toUpperCase())) { | |
return Promise.reject(Error(`INVALID METHOD: ${method.toUpperCase()}`)) | |
} | |
const address = new URL(url) | |
const protocol = requesters[address.protocol] | |
if (!protocol) { | |
return Promise.reject(Error(`INVALID PROTOCOL: ${address.protocol}`)) | |
} | |
return new Promise((resolve, reject) => { | |
const req = protocol.request(address, { ...reqOptions, method }) | |
if (body) { | |
if (body instanceof FormData) { | |
body.pipe(req) | |
} else { | |
req.write(body) | |
req.end() | |
} | |
} else { | |
req.end() | |
} | |
if (timeout) { | |
req.setTimeout(timeout) | |
} | |
req | |
.once('error', reject) | |
.once('timeout', () => req.destroy(new Error('REQUEST TIMED OUT'))) | |
.once('response', (res) => { | |
const chunks = [] | |
const decompressor = decompressors[res.headers['content-encoding']] | |
const stream = decompressor ? res.pipe(decompressor()) : res | |
stream | |
.once('aborted', reject) | |
.once('error', reject) | |
.on('data', (chunk) => chunks.push(chunk)) | |
.once('end', () => { | |
if (res.complete) { | |
resolve({ | |
req, res, body: Buffer.concat(chunks), | |
}) | |
} else { | |
reject(Error('REQUEST NOT COMPLETED')) | |
} | |
}) | |
}) | |
}) | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import https from 'https' | |
import http from 'http' | |
import { URL } from 'url' | |
import zlib from 'zlib' | |
import FormData from 'form-data' | |
export const requesters = { | |
'http:': http, | |
'https:': https, | |
} | |
export const decompressors = { | |
deflate: zlib.createInflate, | |
gzip: zlib.createGunzip, | |
} | |
export interface Response { | |
req: http.ClientRequest | |
res: http.IncomingMessage | |
body: Buffer | |
} | |
export interface Options extends http.RequestOptions { | |
body?: string | FormData | |
timeout?: number | |
} | |
export default function request ( | |
url: string, | |
options: Options = {}, | |
): Promise<Response> { | |
const { | |
method = 'GET', | |
body, | |
timeout, | |
...reqOptions, | |
} = options | |
if (!http.METHODS.includes(method.toUpperCase())) { | |
return Promise.reject(Error(`INVALID METHOD: ${method.toUpperCase()}`)) | |
} | |
const address = new URL(url) | |
const protocol = requesters[address.protocol as keyof typeof requesters] | |
if (!protocol) { | |
return Promise.reject(Error(`INVALID PROTOCOL: ${address.protocol}`)) | |
} | |
return new Promise((resolve, reject) => { | |
const req = protocol.request(address, { ...reqOptions, method }) | |
if (body) { | |
if (body instanceof FormData) { | |
body.pipe(req) | |
} else { | |
req.write(body) | |
req.end() | |
} | |
} else { | |
req.end() | |
} | |
if (timeout) { | |
req.setTimeout(timeout) | |
} | |
req | |
.once('error', reject) | |
.once('timeout', () => req.destroy(new Error('REQUEST TIMED OUT'))) | |
.once('response', (res) => { | |
const chunks: Array<Buffer> = [] | |
const decompressor = decompressors[ | |
res.headers['content-encoding'] as keyof typeof decompressors | |
] | |
const stream = decompressor ? res.pipe(decompressor()) : res | |
stream | |
.once('aborted', reject) | |
.once('error', reject) | |
.on('data', (chunk: Buffer) => chunks.push(chunk)) | |
.once('end', () => { | |
if (res.complete) { | |
resolve({ | |
req, res, body: Buffer.concat(chunks), | |
}) | |
} else { | |
reject(Error('REQUEST NOT COMPLETED')) | |
} | |
}) | |
}) | |
}) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment