-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.js
139 lines (108 loc) · 3.83 KB
/
main.js
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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
"use-strict";
// node-jsonrequest
const
https = require('https'),
zlib = require('zlib');
// valid options
// hostname : Required.
// method : Defaults to 'post' if payload specified, 'get' otherwise.
// path : Required.
// payload : Defaults to no payload. Object to be JSON encoded.
// data: May be used for non-JSON payloads.
// headers : Optional HTTP headers.
// port : Defaults to 443
module.exports = function(options) {
const jsonString = options.payload ? JSON.stringify(options.payload) : null;
const requestOptions = {
path: options.path,
hostname: options.hostname,
method: (options.method || (jsonString ? 'post' : 'get')).toUpperCase(),
port: options.port || 443,
headers: Object.assign({
'User-Agent': 'node-jsonrequest/0.1',
'Content-Type': 'application/json', // JSON payload
'Accept-Encoding': 'gzip, deflate' // enable compression
}, options.headers)
};
// set Content-Length
if(jsonString) requestOptions.headers['Content-Length'] = Buffer.from(jsonString).length;
else if(options.data) requestOptions.headers['Content-Length'] = Buffer.from(options.data).length;
return new Promise((resolve, reject) => {
const req = https.request(requestOptions, res => {
const contentEncoding = res.headers['content-encoding'];
const compressed = ['deflate', 'gzip'].indexOf(contentEncoding) !== -1;
const encoding = compressed ? 'binary' : 'utf8';
let body = '';
res.setEncoding(encoding);
// request limit reached
if(res.statusCode == 429) {
const timeToWait = res.headers['x-retry-after'];
//console.log('Restarting request call after suggested time');
return setTimeout(() => {
this.run(options)
.then(resolve)
.catch(reject);
}, timeToWait * 1000);
}
res.on('data', chunk => body += chunk);
res.on('end', () => {
//if(res.statusCode >= 400 && res.statusCode <= 600)
// return reject(res.statusCode);
// don't bother reading response
if(options.nodata) return resolve(null);
if(compressed) {
const unzip = contentEncoding === 'deflate' ? zlib.deflate : zlib.gunzip;
// response may be incomplete buffer
// wrapping in try
try {
return unzip(Buffer.from(body, encoding), {finishFlush: zlib.constants.Z_SYNC_FLUSH}, (err, data) => {
if(err) return reject(err);
return parseResponse(res, data.toString('utf8'), resolve, reject);
});
} catch(err) {
// malformed response - retry for now
// this needs to be an option
//console.log('Malformed response. Restarting request.');
return setTimeout(() => {
module.exports(options)
.then(resolve)
.catch(reject);
}, 100);
}
}
return parseResponse(res, body, resolve, reject);
});
});
req.on('error', e => {
//console.log(`\x1b[31;1mJSONRequest: ${e}\x1b[0m`);
// catch a socket disconnect and simply re-run the request
if((""+e).indexOf("socket disconnected") > -1)
return setTimeout(() => {
module.exports(options).then(resolve).catch(reject);
}, 500);
else return reject(e);
});
if(jsonString) req.write(jsonString);
else if(options.data) req.write(options.data);
req.end();
});
};
function parseResponse(res, body, resolve, reject) {
try {
// if not JSON, just return the raw response
if (!/application\/(problem\+)?json/.test(res.headers['content-type']) || body.trim() === '')
return resolve(body);
var data = null;
try { data = JSON.parse(body); } catch(e) { console.log(e); }
if(res.statusCode >= 400)
return reject(data);
// if an error or error collection returned, reject
//if(data.error || data.errors)
//if(data.status >= 400)
// return reject(data);//.error || data.errors);
// return the parsed JSON
return resolve(data);
} catch(e) {
return reject(e);
}
}