@datanimbus/postman-request 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,112 @@
1
+ 'use strict'
2
+
3
+ var uuid = require('uuid').v4
4
+ var CombinedStream = require('combined-stream')
5
+ var isstream = require('isstream')
6
+ var Buffer = require('safe-buffer').Buffer
7
+
8
+ function Multipart (request) {
9
+ this.request = request
10
+ this.boundary = uuid()
11
+ this.chunked = false
12
+ this.body = null
13
+ }
14
+
15
+ Multipart.prototype.isChunked = function (options) {
16
+ var self = this
17
+ var chunked = false
18
+ var parts = options.data || options
19
+
20
+ if (!parts.forEach) {
21
+ self.request.emit('error', new Error('Argument error, options.multipart.'))
22
+ }
23
+
24
+ if (options.chunked !== undefined) {
25
+ chunked = options.chunked
26
+ }
27
+
28
+ if (self.request.getHeader('transfer-encoding') === 'chunked') {
29
+ chunked = true
30
+ }
31
+
32
+ if (!chunked) {
33
+ parts.forEach(function (part) {
34
+ if (typeof part.body === 'undefined') {
35
+ self.request.emit('error', new Error('Body attribute missing in multipart.'))
36
+ }
37
+ if (isstream(part.body)) {
38
+ chunked = true
39
+ }
40
+ })
41
+ }
42
+
43
+ return chunked
44
+ }
45
+
46
+ Multipart.prototype.setHeaders = function (chunked) {
47
+ var self = this
48
+
49
+ if (chunked && !self.request.hasHeader('transfer-encoding')) {
50
+ self.request.setHeader('Transfer-Encoding', 'chunked')
51
+ }
52
+
53
+ var header = self.request.getHeader('content-type')
54
+
55
+ if (!header || header.indexOf('multipart') === -1) {
56
+ self.request.setHeader('Content-Type', 'multipart/related; boundary=' + self.boundary)
57
+ } else {
58
+ if (header.indexOf('boundary') !== -1) {
59
+ self.boundary = header.replace(/.*boundary=([^\s;]+).*/, '$1')
60
+ } else {
61
+ self.request.setHeader('Content-Type', header + '; boundary=' + self.boundary)
62
+ }
63
+ }
64
+ }
65
+
66
+ Multipart.prototype.build = function (parts, chunked) {
67
+ var self = this
68
+ var body = chunked ? new CombinedStream() : []
69
+
70
+ function add (part) {
71
+ if (typeof part === 'number') {
72
+ part = part.toString()
73
+ }
74
+ return chunked ? body.append(part) : body.push(Buffer.from(part))
75
+ }
76
+
77
+ if (self.request.preambleCRLF) {
78
+ add('\r\n')
79
+ }
80
+
81
+ parts.forEach(function (part) {
82
+ var preamble = '--' + self.boundary + '\r\n'
83
+ Object.keys(part).forEach(function (key) {
84
+ if (key === 'body') { return }
85
+ preamble += key + ': ' + part[key] + '\r\n'
86
+ })
87
+ preamble += '\r\n'
88
+ add(preamble)
89
+ add(part.body)
90
+ add('\r\n')
91
+ })
92
+ add('--' + self.boundary + '--')
93
+
94
+ if (self.request.postambleCRLF) {
95
+ add('\r\n')
96
+ }
97
+
98
+ return body
99
+ }
100
+
101
+ Multipart.prototype.onRequest = function (options) {
102
+ var self = this
103
+
104
+ var chunked = self.isChunked(options)
105
+ var parts = options.data || options
106
+
107
+ self.setHeaders(chunked)
108
+ self.chunked = chunked
109
+ self.body = self.build(parts, chunked)
110
+ }
111
+
112
+ exports.Multipart = Multipart
package/lib/oauth.js ADDED
@@ -0,0 +1,147 @@
1
+ 'use strict'
2
+
3
+ var qs = require('qs')
4
+ var caseless = require('caseless')
5
+ var uuid = require('uuid').v4
6
+ var oauth = require('oauth-sign')
7
+ var crypto = require('crypto')
8
+ var Buffer = require('safe-buffer').Buffer
9
+
10
+ function OAuth (request) {
11
+ this.request = request
12
+ this.params = null
13
+ }
14
+
15
+ OAuth.prototype.buildParams = function (_oauth, uri, method, query, form, qsLib) {
16
+ var oa = {}
17
+ for (var i in _oauth) {
18
+ oa['oauth_' + i] = _oauth[i]
19
+ }
20
+ if (!oa.oauth_version) {
21
+ oa.oauth_version = '1.0'
22
+ }
23
+ if (!oa.oauth_timestamp) {
24
+ oa.oauth_timestamp = Math.floor(Date.now() / 1000).toString()
25
+ }
26
+ if (!oa.oauth_nonce) {
27
+ oa.oauth_nonce = uuid().replace(/-/g, '')
28
+ }
29
+ if (!oa.oauth_signature_method) {
30
+ oa.oauth_signature_method = 'HMAC-SHA1'
31
+ }
32
+
33
+ var consumer_secret_or_private_key = oa.oauth_consumer_secret || oa.oauth_private_key // eslint-disable-line camelcase
34
+ delete oa.oauth_consumer_secret
35
+ delete oa.oauth_private_key
36
+
37
+ var token_secret = oa.oauth_token_secret // eslint-disable-line camelcase
38
+ delete oa.oauth_token_secret
39
+
40
+ var realm = oa.oauth_realm
41
+ delete oa.oauth_realm
42
+ delete oa.oauth_transport_method
43
+
44
+ var baseurl = uri.protocol + '//' + uri.host + uri.pathname
45
+ var params = qsLib.parse([].concat(query, form, qsLib.stringify(oa)).join('&'))
46
+
47
+ oa.oauth_signature = oauth.sign(
48
+ oa.oauth_signature_method,
49
+ method,
50
+ baseurl,
51
+ params,
52
+ consumer_secret_or_private_key, // eslint-disable-line camelcase
53
+ token_secret // eslint-disable-line camelcase
54
+ )
55
+
56
+ if (realm) {
57
+ oa.realm = realm
58
+ }
59
+
60
+ return oa
61
+ }
62
+
63
+ OAuth.prototype.buildBodyHash = function (_oauth, body) {
64
+ if (['HMAC-SHA1', 'RSA-SHA1'].indexOf(_oauth.signature_method || 'HMAC-SHA1') < 0) {
65
+ this.request.emit('error', new Error('oauth: ' + _oauth.signature_method +
66
+ ' signature_method not supported with body_hash signing.'))
67
+ }
68
+
69
+ var shasum = crypto.createHash('sha1')
70
+ shasum.update(body || '')
71
+ var sha1 = shasum.digest('hex')
72
+
73
+ return Buffer.from(sha1, 'hex').toString('base64')
74
+ }
75
+
76
+ OAuth.prototype.concatParams = function (oa, sep, wrap) {
77
+ wrap = wrap || ''
78
+
79
+ var params = Object.keys(oa).filter(function (i) {
80
+ return i !== 'realm' && i !== 'oauth_signature'
81
+ }).sort()
82
+
83
+ if (oa.realm) {
84
+ params.splice(0, 0, 'realm')
85
+ }
86
+ params.push('oauth_signature')
87
+
88
+ return params.map(function (i) {
89
+ return i + '=' + wrap + oauth.rfc3986(oa[i]) + wrap
90
+ }).join(sep)
91
+ }
92
+
93
+ OAuth.prototype.onRequest = function (_oauth) {
94
+ var self = this
95
+ self.params = _oauth
96
+
97
+ var uri = self.request.uri || {}
98
+ var method = self.request.method || ''
99
+ var headers = caseless(self.request.headers)
100
+ var body = self.request.body || ''
101
+ var qsLib = self.request.qsLib || qs
102
+
103
+ var form
104
+ var query
105
+ var contentType = headers.get('content-type') || ''
106
+ var formContentType = 'application/x-www-form-urlencoded'
107
+ var transport = _oauth.transport_method || 'header'
108
+
109
+ if (contentType.slice(0, formContentType.length) === formContentType) {
110
+ contentType = formContentType
111
+ form = body
112
+ }
113
+ if (uri.query) {
114
+ query = uri.query
115
+ }
116
+ if (transport === 'body' && (method !== 'POST' || contentType !== formContentType)) {
117
+ self.request.emit('error', new Error('oauth: transport_method of body requires POST ' +
118
+ 'and content-type ' + formContentType))
119
+ }
120
+
121
+ if (!form && typeof _oauth.body_hash === 'boolean') {
122
+ _oauth.body_hash = self.buildBodyHash(_oauth, self.request.body.toString())
123
+ }
124
+
125
+ var oa = self.buildParams(_oauth, uri, method, query, form, qsLib)
126
+
127
+ switch (transport) {
128
+ case 'header':
129
+ self.request.setHeader('Authorization', 'OAuth ' + self.concatParams(oa, ',', '"'))
130
+ break
131
+
132
+ case 'query':
133
+ var href = self.request.uri.href += (query ? '&' : '?') + self.concatParams(oa, '&')
134
+ self.request.uri = self.request.urlParser.parse(href)
135
+ self.request.path = self.request.uri.path
136
+ break
137
+
138
+ case 'body':
139
+ self.request.body = (form ? form + '&' : '') + self.concatParams(oa, '&')
140
+ break
141
+
142
+ default:
143
+ self.request.emit('error', new Error('oauth: transport_method invalid'))
144
+ }
145
+ }
146
+
147
+ exports.OAuth = OAuth
@@ -0,0 +1,50 @@
1
+ 'use strict'
2
+
3
+ var qs = require('qs')
4
+ var querystring = require('querystring')
5
+
6
+ function Querystring (request) {
7
+ this.request = request
8
+ this.lib = null
9
+ this.useQuerystring = null
10
+ this.parseOptions = null
11
+ this.stringifyOptions = null
12
+ }
13
+
14
+ Querystring.prototype.init = function (options) {
15
+ if (this.lib) { return }
16
+
17
+ this.useQuerystring = options.useQuerystring
18
+ this.lib = (this.useQuerystring ? querystring : qs)
19
+
20
+ this.parseOptions = options.qsParseOptions || {}
21
+ this.stringifyOptions = options.qsStringifyOptions || {}
22
+ }
23
+
24
+ Querystring.prototype.stringify = function (obj) {
25
+ return (this.useQuerystring)
26
+ ? this.rfc3986(this.lib.stringify(obj,
27
+ this.stringifyOptions.sep || null,
28
+ this.stringifyOptions.eq || null,
29
+ this.stringifyOptions))
30
+ : this.lib.stringify(obj, this.stringifyOptions)
31
+ }
32
+
33
+ Querystring.prototype.parse = function (str) {
34
+ return (this.useQuerystring)
35
+ ? this.lib.parse(str,
36
+ this.parseOptions.sep || null,
37
+ this.parseOptions.eq || null,
38
+ this.parseOptions)
39
+ : this.lib.parse(str, this.parseOptions)
40
+ }
41
+
42
+ Querystring.prototype.rfc3986 = function (str) {
43
+ return str.replace(/[!'()*]/g, function (c) {
44
+ return '%' + c.charCodeAt(0).toString(16).toUpperCase()
45
+ })
46
+ }
47
+
48
+ Querystring.prototype.unescape = querystring.unescape
49
+
50
+ exports.Querystring = Querystring
@@ -0,0 +1,218 @@
1
+ 'use strict'
2
+
3
+ var fs = require('fs')
4
+ var isUrl = /^https?:/
5
+
6
+ function Redirect (request) {
7
+ this.request = request
8
+ this.followRedirect = true
9
+ this.followRedirects = true
10
+ this.followAllRedirects = false
11
+ this.followOriginalHttpMethod = false
12
+ this.followAuthorizationHeader = false
13
+ this.allowRedirect = function () { return true }
14
+ this.maxRedirects = 10
15
+ this.redirects = []
16
+ this.redirectsFollowed = 0
17
+ this.removeRefererHeader = false
18
+ }
19
+
20
+ Redirect.prototype.onRequest = function (options) {
21
+ var self = this
22
+
23
+ if (options.maxRedirects !== undefined) {
24
+ self.maxRedirects = options.maxRedirects
25
+ }
26
+ if (typeof options.followRedirect === 'function') {
27
+ self.allowRedirect = options.followRedirect
28
+ }
29
+ if (options.followRedirect !== undefined) {
30
+ self.followRedirects = !!options.followRedirect
31
+ }
32
+ if (options.followAllRedirects !== undefined) {
33
+ self.followAllRedirects = options.followAllRedirects
34
+ }
35
+ if (self.followRedirects || self.followAllRedirects) {
36
+ self.redirects = self.redirects || []
37
+ }
38
+ if (options.removeRefererHeader !== undefined) {
39
+ self.removeRefererHeader = options.removeRefererHeader
40
+ }
41
+ if (options.followOriginalHttpMethod !== undefined) {
42
+ self.followOriginalHttpMethod = options.followOriginalHttpMethod
43
+ }
44
+ if (options.followAuthorizationHeader !== undefined) {
45
+ self.followAuthorizationHeader = options.followAuthorizationHeader
46
+ }
47
+ }
48
+
49
+ Redirect.prototype.redirectTo = function (response) {
50
+ var self = this
51
+ var request = self.request
52
+
53
+ var redirectTo = null
54
+ if (response.statusCode >= 300 && response.statusCode < 400 && response.caseless.has('location')) {
55
+ var location = response.caseless.get('location')
56
+ request.debug('redirect', location)
57
+
58
+ if (self.followAllRedirects) {
59
+ redirectTo = location
60
+ } else if (self.followRedirects) {
61
+ switch (request.method) {
62
+ case 'PATCH':
63
+ case 'PUT':
64
+ case 'POST':
65
+ case 'DELETE':
66
+ // Do not follow redirects
67
+ break
68
+ default:
69
+ redirectTo = location
70
+ break
71
+ }
72
+ }
73
+ } else if (response.statusCode === 401) {
74
+ // retry the request with the new Authorization header value using
75
+ // WWW-Authenticate response header.
76
+ // https://tools.ietf.org/html/rfc7235#section-3.1
77
+ var authHeader = request._auth.onResponse(response)
78
+ if (authHeader) {
79
+ request.setHeader('Authorization', authHeader)
80
+ redirectTo = request.uri
81
+ }
82
+ }
83
+ return redirectTo
84
+ }
85
+
86
+ Redirect.prototype.onResponse = function (response) {
87
+ var self = this
88
+ var request = self.request
89
+ var urlParser = request.urlParser
90
+ var options = {}
91
+
92
+ var redirectTo = self.redirectTo(response)
93
+ if (!redirectTo || !self.allowRedirect.call(request, response)) {
94
+ return false
95
+ }
96
+
97
+ request.debug('redirect to', redirectTo)
98
+
99
+ // ignore any potential response body. it cannot possibly be useful
100
+ // to us at this point.
101
+ // response.resume should be defined, but check anyway before calling. Workaround for browserify.
102
+ if (response.resume) {
103
+ response.resume()
104
+ }
105
+
106
+ if (self.redirectsFollowed >= self.maxRedirects) {
107
+ request.emit('error', new Error('Exceeded maxRedirects. Probably stuck in a redirect loop ' + request.uri.href))
108
+ return false
109
+ }
110
+ self.redirectsFollowed += 1
111
+
112
+ if (!isUrl.test(redirectTo)) {
113
+ redirectTo = urlParser.resolve(request.uri.href, redirectTo)
114
+ }
115
+
116
+ var uriPrev = request.uri
117
+ request.uri = urlParser.parse(redirectTo)
118
+
119
+ // handle the case where we change protocol from https to http or vice versa
120
+ if (request.uri.protocol !== uriPrev.protocol) {
121
+ delete request.agent
122
+ }
123
+
124
+ self.redirects.push({ statusCode: response.statusCode, redirectUri: redirectTo })
125
+
126
+ // if the redirect hostname (not just port or protocol) is changed:
127
+ // 1. remove host header, the new host will be populated on request.init
128
+ // 2. remove authorization header, avoid authentication leak
129
+ // @note: This is done because of security reasons, irrespective of the
130
+ // status code or request method used.
131
+ if (request.headers && uriPrev.hostname !== request.uri.hostname) {
132
+ request.removeHeader('host')
133
+
134
+ // use followAuthorizationHeader option to retain authorization header
135
+ if (!self.followAuthorizationHeader) {
136
+ request.removeHeader('authorization')
137
+ }
138
+ }
139
+
140
+ delete request.src
141
+ delete request.req
142
+ delete request._started
143
+
144
+ // Switch request method to GET
145
+ // - if followOriginalHttpMethod is not set [OVERRIDE]
146
+ // - or, statusCode code is not 401, 307 or 308 [STANDARD]
147
+ // - also, remove request body for the GET redirect [STANDARD]
148
+ // @note: when followOriginalHttpMethod is set,
149
+ // it will always retain the request body irrespective of the method (say GET) or status code (any 3XX).
150
+ if (!self.followOriginalHttpMethod &&
151
+ response.statusCode !== 401 && response.statusCode !== 307 && response.statusCode !== 308) {
152
+ // force all redirects to use GET (legacy reasons)
153
+ // but, HEAD is considered as a safe method so, the method is retained.
154
+ if (request.method !== 'HEAD') {
155
+ request.method = 'GET'
156
+ }
157
+
158
+ // Remove parameters from the previous response, unless this is the second request
159
+ // for a server that requires digest authentication.
160
+ delete request.body
161
+ delete request._form
162
+ delete request._multipart
163
+ if (request.headers) {
164
+ request.removeHeader('content-type')
165
+ request.removeHeader('content-length')
166
+ }
167
+ }
168
+
169
+ // Restore form-data stream if request body is retained
170
+ if (request.formData &&
171
+ // make sure _form is released and there's no pending _streams left
172
+ // which will be the case for 401 redirects. so, reuse _form on redirect
173
+ // @note: multiple form-param / file-streams may cause following issue:
174
+ // https://github.com/request/request/issues/887
175
+ // @todo: expose stream errors as events
176
+ request._form && request._form._released &&
177
+ request._form._streams && !request._form._streams.length) {
178
+ // reinitialize FormData stream for 307 or 308 redirects
179
+ delete request._form
180
+ // remove content-type header for new boundary
181
+ request.removeHeader('content-type')
182
+ // remove content-length header since FormValue may be dropped if its not a file stream
183
+ request.removeHeader('content-length')
184
+
185
+ var formData = []
186
+ var resetFormData = function (key, value, paramOptions) {
187
+ // if `value` is of type stream
188
+ if (typeof (value && value.pipe) === 'function') {
189
+ // bail out if not a file stream
190
+ if (!(value.hasOwnProperty('fd') && value.path)) return
191
+ // create new file stream
192
+ value = fs.createReadStream(value.path)
193
+ }
194
+
195
+ formData.push({key: key, value: value, options: paramOptions})
196
+ }
197
+ for (var i = 0, ii = request.formData.length; i < ii; i++) {
198
+ var formParam = request.formData[i]
199
+ if (!formParam) { continue }
200
+ resetFormData(formParam.key, formParam.value, formParam.options)
201
+ }
202
+
203
+ // setting `options.formData` will reinitialize FormData in `request.init`
204
+ options.formData = formData
205
+ }
206
+
207
+ if (!self.removeRefererHeader) {
208
+ request.setHeader('Referer', uriPrev.href)
209
+ }
210
+
211
+ request.emit('redirect')
212
+
213
+ request.init(options)
214
+
215
+ return true
216
+ }
217
+
218
+ exports.Redirect = Redirect
package/lib/socks.js ADDED
@@ -0,0 +1,45 @@
1
+ 'use strict'
2
+
3
+ var { SocksProxyAgent } = require('socks-proxy-agent')
4
+ var ALLOWED_PROTOCOLS = ['socks4:', 'socks4a:', 'socks5:', 'socks5h:', 'socks:']
5
+
6
+ function SocksProxy (request) {
7
+ this.request = request
8
+ }
9
+
10
+ SocksProxy.prototype.isEnabled = function () {
11
+ var self = this
12
+ var request = self.request
13
+
14
+ if (typeof request.proxy === 'string') {
15
+ request.proxy = request.urlParser.parse(request.proxy)
16
+ }
17
+
18
+ if (!request.proxy) {
19
+ return false
20
+ }
21
+
22
+ return request.proxy.href && ALLOWED_PROTOCOLS.includes(request.proxy.protocol)
23
+ }
24
+
25
+ SocksProxy.prototype.setup = function () {
26
+ var self = this
27
+ var request = self.request
28
+
29
+ if (!self.isEnabled()) {
30
+ return false
31
+ }
32
+
33
+ var proxyUrl = request.proxy.href
34
+
35
+ // Handle authentication from proxy.auth if not already in URL
36
+ if (request.proxy.auth && proxyUrl.indexOf('@') === -1) {
37
+ proxyUrl = request.proxy.protocol + '//' + request.proxy.auth + '@' + request.proxy.host
38
+ }
39
+
40
+ request.agent = new SocksProxyAgent(proxyUrl)
41
+
42
+ return true
43
+ }
44
+
45
+ exports.SocksProxy = SocksProxy