-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathnanorequest.ts
124 lines (106 loc) · 3.13 KB
/
nanorequest.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
import http from 'http'
import https from 'https'
import url, { Url } from 'url'
import { Stream } from 'stream'
interface NROpts extends http.RequestOptions{
url?: string | Url
body?: any
}
function json (obj:NROpts):boolean {
return !!(obj.headers && (String(obj.headers['content-type']).indexOf('application/json') > -1))
}
function text (obj:NROpts):boolean {
return !!(obj.headers && (String(obj.headers['content-type']).indexOf('text') > -1))
}
interface NRError extends Error {
res?: http.IncomingMessage
body?: body
}
type body = Buffer | string | {[key:string]: any}
type NRCB = (err:NRError | null, res:http.IncomingMessage, body:body ) => void
export function nanorequest (_opts:NROpts, cb?:NRCB):Promise<{res:http.IncomingMessage, body:body}> | http.ClientRequest {
let _promise = false
return new Promise((resolve, reject) => {
if (!cb || typeof cb !== 'function') {
_promise = true
cb = (err:NRError | null, res:http.IncomingMessage, body:any) => {
if (err) {
err.res = res
err.body = body
return reject(err)
}
resolve({res, body})
}
}
const opts = Object.assign({}, _opts)
let reqBody = ''
if (typeof opts.url === 'string') {
Object.assign(opts, url.parse(opts.url))
opts.url = void 0
}
if (opts.body) {
if (json(opts) && typeof opts.body !== 'string') {
try {
reqBody = JSON.stringify(opts.body)
} catch (err) {
return reject(err)
}
} else {
reqBody = opts.body
}
if (!opts.headers) {
opts.headers = {}
}
opts.headers['content-length'] = Buffer.byteLength(reqBody, 'utf8')
}
let req
if (opts.protocol === 'https:') {
req = https.request(opts, response)
} else {
req = http.request(opts, response)
}
req
.on('error', handleError)
.end(reqBody)
if (!_promise) return req
function response (res:http.IncomingMessage):void {
const len = parseInt(res.headers['content-length'] || '', 10) || 0
let bufs:Buffer[] = []
res
.on('error', handleError)
.on('data', (chunk:Buffer) => {
bufs.push(chunk)
})
.on('end', () => {
const len = bufs.reduce((acc, buf) => (acc = acc + buf.length), 0)
const buf = Buffer.concat(bufs, len)
end(res, buf)
})
}
function end (res:http.IncomingMessage, buf:Buffer):void {
let err = null
let body:body = buf
if (json(res)) {
try {
body = JSON.parse(buf.toString('utf8'))
} catch (err) {
return handleError(err, res)
}
}
if (text(res)) {
body = buf.toString('utf8')
}
if (res.statusCode && res.statusCode > 299) {
err = new Error(`${res.statusCode}: ${res.statusMessage || 'error'}`)
}
if (cb) cb(err, res, body)
}
function handleError (err:NRError, res:http.IncomingMessage) {
if (!(err instanceof Error)) {
err = new Error(err)
}
res = res || {statusCode: null}
if (cb) cb(err, res, err.message)
}
})
}