123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220 |
- const path = require('path')
- const http = require('http')
- const https = require('https')
- const followRedirects = require('follow-redirects')
- const qs = require('querystring')
- const zlib = require('zlib')
- const {URL} = require('url')
- const CentraResponse = require('./CentraResponse.js')
- const supportedCompressions = ['gzip', 'deflate', 'br']
- const useRequest = (protocol, maxRedirects) => {
- let httpr
- let httpsr
- if (maxRedirects <= 0) {
- httpr = http.request
- httpsr = https.request
- }
- else {
- httpr = followRedirects.http.request
- httpsr = followRedirects.https.request
- }
- if (protocol === 'http:') {
- return httpr
- }
- else if (protocol === 'https:') {
- return httpsr
- }
- else throw new Error('Bad URL protocol: ' + protocol)
- }
- module.exports = class CentraRequest {
- constructor (url, method = 'GET') {
- this.url = typeof url === 'string' ? new URL(url) : url
- this.method = method
- this.data = null
- this.sendDataAs = null
- this.reqHeaders = {}
- this.streamEnabled = false
- this.compressionEnabled = false
- this.timeoutTime = null
- this.coreOptions = {}
- this.maxRedirects = 0
- this.resOptions = {
- 'maxBuffer': 50 * 1000000 // 50 MB
- }
- return this
- }
- followRedirects(n) {
- this.maxRedirects = n
- return this
- }
- query (a1, a2) {
- if (typeof a1 === 'object') {
- Object.keys(a1).forEach((queryKey) => {
- this.url.searchParams.append(queryKey, a1[queryKey])
- })
- }
- else this.url.searchParams.append(a1, a2)
- return this
- }
- path (relativePath) {
- this.url.pathname = path.join(this.url.pathname, relativePath)
- return this
- }
- body (data, sendAs) {
- this.sendDataAs = typeof data === 'object' && !sendAs && !Buffer.isBuffer(data) ? 'json' : (sendAs ? sendAs.toLowerCase() : 'buffer')
- this.data = this.sendDataAs === 'form' ? qs.stringify(data) : (this.sendDataAs === 'json' ? JSON.stringify(data) : data)
- return this
- }
- header (a1, a2) {
- if (typeof a1 === 'object') {
- Object.keys(a1).forEach((headerName) => {
- this.reqHeaders[headerName.toLowerCase()] = a1[headerName]
- })
- }
- else this.reqHeaders[a1.toLowerCase()] = a2
- return this
- }
- timeout (timeout) {
- this.timeoutTime = timeout
- return this
- }
- option (name, value) {
- this.coreOptions[name] = value
- return this
- }
- stream () {
- this.streamEnabled = true
- return this
- }
- compress () {
- this.compressionEnabled = true
- if (!this.reqHeaders['accept-encoding']) this.reqHeaders['accept-encoding'] = supportedCompressions.join(', ')
- return this
- }
- send () {
- return new Promise((resolve, reject) => {
- if (this.data) {
- if (!this.reqHeaders.hasOwnProperty('content-type')) {
- if (this.sendDataAs === 'json') {
- this.reqHeaders['content-type'] = 'application/json'
- }
- else if (this.sendDataAs === 'form') {
- this.reqHeaders['content-type'] = 'application/x-www-form-urlencoded'
- }
- }
- if (!this.reqHeaders.hasOwnProperty('content-length')) {
- this.reqHeaders['content-length'] = Buffer.byteLength(this.data)
- }
- }
- const options = Object.assign({
- 'protocol': this.url.protocol,
- 'host': this.url.hostname.replace('[', '').replace(']', ''),
- 'port': this.url.port,
- 'path': this.url.pathname + (this.url.search === null ? '' : this.url.search),
- 'method': this.method,
- 'headers': this.reqHeaders,
- 'maxRedirects': this.maxRedirects
- }, this.coreOptions)
- let req
- const resHandler = (res) => {
- let stream = res
- if (this.compressionEnabled) {
- if (res.headers['content-encoding'] === 'gzip') {
- stream = res.pipe(zlib.createGunzip())
- }
- else if (res.headers['content-encoding'] === 'deflate') {
- stream = res.pipe(zlib.createInflate())
- }
- else if (res.headers['content-encoding'] === 'br') {
- stream = res.pipe(zlib.createBrotliDecompress())
- }
- }
- let centraRes
- if (this.streamEnabled) {
- resolve(stream)
- }
- else {
- centraRes = new CentraResponse(res, this.resOptions)
- stream.on('error', (err) => {
- reject(err)
- })
- stream.on('aborted', () => {
- reject(new Error('Server aborted request'))
- })
- stream.on('data', (chunk) => {
- centraRes._addChunk(chunk)
- if (this.resOptions.maxBuffer !== null && centraRes.body.length > this.resOptions.maxBuffer) {
- stream.destroy()
- reject('Received a response which was longer than acceptable when buffering. (' + this.body.length + ' bytes)')
- }
- })
- stream.on('end', () => {
- resolve(centraRes)
- })
- }
- }
- const request = useRequest(this.url.protocol, this.maxRedirects)
- req = request(options, resHandler)
- if (this.timeoutTime) {
- req.setTimeout(this.timeoutTime, () => {
- req.abort()
- if (!this.streamEnabled) {
- reject(new Error('Timeout reached'))
- }
- })
- }
- req.on('error', (err) => {
- reject(err)
- })
- if (this.data) req.write(this.data)
- req.end()
- })
- }
- }
|