117 lines
3.6 KiB
JavaScript
117 lines
3.6 KiB
JavaScript
'use strict'
|
||
|
||
module.exports = httpRequest
|
||
|
||
const url = require('url')
|
||
const http = require('http')
|
||
const https = require('https')
|
||
|
||
const _ = require('lodash')
|
||
const debug = require('debug')('octokit:rest')
|
||
const isArrayBuffer = require('is-array-buffer')
|
||
const isStream = require('is-stream')
|
||
|
||
const HttpError = require('./http-error')
|
||
|
||
function httpRequest (requestOptions) {
|
||
requestOptions = Object.assign(
|
||
url.parse(requestOptions.url),
|
||
_.pick(requestOptions, 'method', 'body', 'headers', 'ca', 'family', 'proxy', 'rejectUnauthorized', 'timeout')
|
||
)
|
||
debug('REQUEST:', requestOptions)
|
||
|
||
// calculate content length unless body is a stream, in which case the
|
||
// content length is already set per option
|
||
if (requestOptions.body && !isStream(requestOptions.body)) {
|
||
// stringify body unless it’s an ArrayBuffer
|
||
if (!isArrayBuffer(requestOptions.body) && !Buffer.isBuffer(requestOptions.body) && typeof requestOptions.body !== 'string') {
|
||
requestOptions.body = JSON.stringify(requestOptions.body)
|
||
}
|
||
|
||
_.defaults(requestOptions.headers, {
|
||
'content-type': 'application/json; charset=utf-8',
|
||
'content-length': Buffer.byteLength(requestOptions.body, 'utf8')
|
||
})
|
||
}
|
||
|
||
// GitHub expects "content-length: 0" header for PUT/PATCH requests without body
|
||
if (['patch', 'put'].indexOf(requestOptions.method) >= 0 &&
|
||
!requestOptions.headers['content-length']) {
|
||
requestOptions.headers['content-length'] = 0
|
||
}
|
||
|
||
if ('content-length' in requestOptions.headers) {
|
||
requestOptions.headers['content-length'] = parseInt(requestOptions.headers['content-length'], 10)
|
||
}
|
||
|
||
const reqModule = requestOptions.protocol === 'http:' ? http : https
|
||
delete requestOptions.protocol
|
||
|
||
return new Promise((resolve, reject) => {
|
||
const request = reqModule.request(requestOptions, (response) => {
|
||
debug('STATUS: ' + response.statusCode)
|
||
debug('HEADERS: ' + JSON.stringify(response.headers))
|
||
|
||
response.setEncoding('utf8')
|
||
let data = ''
|
||
response.on('data', (chunk) => {
|
||
data += chunk
|
||
})
|
||
|
||
/* istanbul ignore next */
|
||
response.on('error', (error) => {
|
||
reject(new HttpError(error.message, 500))
|
||
})
|
||
response.on('end', () => {
|
||
if (response.statusCode !== 304 && response.statusCode >= 301 && response.statusCode <= 307) {
|
||
requestOptions.url = response.headers.location
|
||
httpRequest(requestOptions).then(resolve, reject)
|
||
return
|
||
}
|
||
|
||
if (response.statusCode === 304 || response.statusCode >= 400 || response.statusCode < 10) {
|
||
reject(new HttpError(data, response.statusCode, response.headers))
|
||
return
|
||
}
|
||
|
||
const contentType = response.headers['content-type']
|
||
if (contentType && contentType.indexOf('application/json') !== -1) {
|
||
data = data && JSON.parse(data)
|
||
}
|
||
|
||
resolve({
|
||
meta: response.headers,
|
||
data
|
||
})
|
||
})
|
||
})
|
||
|
||
let aborted
|
||
request.on('error', (error) => {
|
||
if (aborted) return
|
||
debug('REQUEST ERROR: ' + error.message)
|
||
reject(new HttpError(error.message, 500))
|
||
})
|
||
|
||
if (requestOptions.timeout) {
|
||
request.setTimeout(requestOptions.timeout)
|
||
}
|
||
request.on('timeout', () => {
|
||
debug('REQUEST ERROR: timed out')
|
||
request.abort()
|
||
aborted = true
|
||
reject(new HttpError('Request timeout', 504))
|
||
})
|
||
|
||
if (requestOptions.body) {
|
||
if (isStream(requestOptions.body)) {
|
||
return requestOptions.body.pipe(request)
|
||
}
|
||
|
||
request.write(Buffer.from(requestOptions.body))
|
||
}
|
||
|
||
request.end()
|
||
})
|
||
}
|