@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.
- package/LICENSE +55 -0
- package/README.md +1315 -0
- package/index.js +166 -0
- package/lib/auth.js +167 -0
- package/lib/autohttp/agent.js +206 -0
- package/lib/autohttp/headerValidations.js +40 -0
- package/lib/autohttp/index.js +8 -0
- package/lib/autohttp/request.js +192 -0
- package/lib/autohttp/requestName.js +86 -0
- package/lib/cookies.js +20 -0
- package/lib/getProxyFromURI.js +79 -0
- package/lib/har.js +200 -0
- package/lib/hawk.js +89 -0
- package/lib/helpers.js +91 -0
- package/lib/http2/agent.js +92 -0
- package/lib/http2/index.js +8 -0
- package/lib/http2/request.js +369 -0
- package/lib/inflate.js +64 -0
- package/lib/multipart.js +112 -0
- package/lib/oauth.js +147 -0
- package/lib/querystring.js +50 -0
- package/lib/redirect.js +218 -0
- package/lib/socks.js +45 -0
- package/lib/tunnel.js +175 -0
- package/lib/url-parse.js +187 -0
- package/package.json +77 -0
- package/request.js +2098 -0
package/lib/multipart.js
ADDED
|
@@ -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
|
package/lib/redirect.js
ADDED
|
@@ -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
|