Last active
October 28, 2019 10:42
-
-
Save davalapar/a80605f62c4ec194ebff7a7924045a2b to your computer and use it in GitHub Desktop.
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
const fs = require('fs'); | |
const p = require('path'); | |
const http = require('http'); | |
const https = require('https'); | |
const qs = require('querystring'); | |
const request = (config) => { | |
if (typeof config !== 'object' || config === null) { | |
throw Error('invalid non-object config'); | |
} | |
let url; | |
if (config.url === undefined) { | |
throw Error('invalid undefined config.url'); | |
} else if (typeof config.url !== 'string') { | |
throw Error('invalid non-string config.url'); | |
} else { | |
url = config.url; | |
} | |
let agent; | |
if (url.includes('https://') === true) { | |
agent = https; | |
} else if (url.includes('http://') === true) { | |
agent = http; | |
} else { | |
throw Error('invalid non-http non-https protocols'); | |
} | |
let path; | |
if (config.path !== undefined) { | |
if (typeof config.path !== 'string') { | |
throw Error('invalid non-string config.path'); | |
} else { | |
path = config.path; | |
} | |
} | |
let method; | |
if (config.method !== undefined) { | |
if (typeof config.method !== 'string') { | |
throw Error('invalid non-string config.method'); | |
} else if (config.method !== 'GET' && config.method !== 'POST') { | |
throw Error('invalid non-GET & non-POST config.method'); | |
} else { | |
method = config.method; | |
} | |
} else { | |
method = 'GET'; | |
} | |
let data; | |
if (config.data !== undefined) { | |
if (config.method !== 'POST') { | |
throw Error('invalid non-POST config.method with non-undefined config.data'); | |
} else if (typeof data !== 'object' || data === null) { | |
throw Error('invalid non-object config.data'); | |
} else { | |
data = config.data; | |
} | |
} | |
let timeout; | |
if (config.timeout !== undefined) { | |
if (typeof config.timeout !== 'number') { | |
throw Error('invalid non-number config.timeout'); | |
} else if (Number.isNaN(config.timeout) === true) { | |
throw Error('invalid NaN config.timeout'); | |
} else if (Number.isFinite(config.timeout) === false) { | |
throw Error('invalid non-finite config.timeout'); | |
} else { | |
timeout = config.timeout; | |
} | |
} | |
return new Promise((resolve, reject) => { | |
let timeoutObject; | |
const req = agent.request( | |
new URL(url), | |
(res) => { | |
const cType = res.headers['content-type']; | |
const cLength = Number(res.headers['content-length']); | |
if (Number.isNaN(cLength) === true) { | |
reject(new Error('RES_CLENGTH_NAN')); | |
} | |
if (res.statusCode === 200) { | |
if (path === undefined) { | |
if (timeout !== undefined) { | |
clearTimeout(timeoutObject); | |
timeoutObject = setTimeout(() => { | |
console.log('res :: timeout'); | |
req.abort(); | |
res.removeAllListeners(); | |
res.destroy(); | |
reject(new Error(`RES_TIMEOUT_${timeout}`)); | |
}, timeout); | |
} | |
let buffer = Buffer.alloc(0); | |
res.on('data', (chunk) => { | |
console.log('res :: data', chunk.byteLength, buffer.byteLength, cLength, (buffer.byteLength / cLength) * 100); | |
buffer = Buffer.concat([buffer, chunk]); | |
}); | |
res.on('end', () => { | |
console.log('res :: end'); | |
if (timeout !== undefined) { | |
clearTimeout(timeoutObject); | |
} | |
if (cType.includes('application/json') === true) { | |
try { | |
resolve(JSON.parse(buffer.toString('utf8'))); | |
} catch (e) { | |
reject(e); | |
} | |
} else if (cType.includes('text/plain') === true) { | |
try { | |
resolve(buffer.toString('utf8')); | |
} catch (e) { | |
reject(e); | |
} | |
} else { | |
resolve(buffer); | |
} | |
}); | |
} else { | |
const dir = p.dirname(path); | |
if (fs.existsSync(dir) === false) { | |
fs.mkdirSync(dir, { recursive: true }); | |
} | |
if (fs.existsSync(path) === true) { | |
fs.unlinkSync(path); | |
} | |
const fws = fs.createWriteStream(path, { flags: 'wx' }); | |
if (timeout !== undefined) { | |
clearTimeout(timeoutObject); | |
timeoutObject = setTimeout(() => { | |
req.abort(); | |
res.removeAllListeners(); | |
res.destroy(); | |
fws.close(); | |
if (fs.existsSync(path) === true) { | |
fs.unlinkSync(path); | |
} | |
reject(new Error(`RES_TIMEOUT_${timeout}`)); | |
}, timeout); | |
} | |
res.pipe(fws); | |
fws.on('error', (e) => { | |
console.log('fws :: error'); | |
if (timeout !== undefined) { | |
clearTimeout(timeoutObject); | |
} | |
req.abort(); | |
res.removeAllListeners(); | |
res.destroy(); | |
fws.close(); | |
if (fs.existsSync(path) === true) { | |
fs.unlinkSync(path); | |
} | |
reject(e); | |
}); | |
fws.on('finish', () => { | |
console.log('fws :: finish'); | |
if (timeout !== undefined) { | |
clearTimeout(timeoutObject); | |
} | |
resolve(cLength); | |
}); | |
} | |
} else { | |
req.abort(); | |
res.removeAllListeners(); | |
res.destroy(); | |
reject(new Error(`RES_UNEXPECTED_${res.statusCode}`)); | |
} | |
}, | |
); | |
if (timeout !== undefined) { | |
timeoutObject = setTimeout(() => { | |
console.log('req :: timeout'); | |
req.abort(); | |
reject(new Error(`REQ_TIMEOUT_${timeout}`)); | |
}, timeout); | |
} | |
req.on('error', (e) => { | |
console.log('req :: error'); | |
if (timeout !== undefined) { | |
clearTimeout(timeoutObject); | |
} | |
reject(e); | |
}); | |
if (method === 'POST') { | |
req.write(qs.stringify(data)); | |
} | |
req.end(); | |
}); | |
}; | |
(async () => { | |
/** | |
* - if path is specified, request returns undefined. | |
* - if response headers contains application/json, we parse it | |
* - if response headers contains text/plain, we return it | |
* - if timeout is specified, and triggered | |
*/ | |
request({ | |
// large file 50mb | |
url: 'http://212.183.159.230/50MB.zip', | |
// image/jpeg | |
// url: 'http://i3.ytimg.com/vi/J---aiyznGQ/mqdefault.jpg', | |
// application/json | |
// url: 'https://jsonplaceholder.typicode.com/todos/1', | |
// text/plain | |
// url: 'https://stackoverflow.com/robots.txt', | |
// path: './a/b/test.jpg', | |
timeout: 30000, | |
}) | |
.then((result) => { | |
console.log({ result }); | |
}) | |
.catch((error) => { | |
console.error({ error }); | |
}); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment