@wiajs/request 3.0.0 → 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/.github/ISSUE_TEMPLATE.md +56 -0
- package/.github/PULL_REQUEST_TEMPLATE.md +13 -0
- package/.github/stale.yml +19 -0
- package/.swcrc +57 -0
- package/.travis.yml +21 -0
- package/CHANGELOG.md +717 -0
- package/CONTRIBUTING.md +81 -0
- package/biome.json +44 -0
- package/codecov.yml +2 -0
- package/disabled.appveyor.yml +36 -0
- package/dist/request.cjs +1476 -0
- package/dist/request.mjs +1474 -0
- package/examples/README.md +135 -0
- package/gulpfile.js +71 -0
- package/package.json +1 -2
- package/release.sh +45 -0
- package/tests/browser/karma.conf.js +57 -0
- package/tests/browser/ssl/ca.crt +14 -0
- package/tests/browser/ssl/server.crt +14 -0
- package/tests/browser/ssl/server.key +15 -0
- package/tests/browser/start.js +37 -0
- package/tests/browser/test.js +34 -0
- package/tests/fixtures/har.json +158 -0
- package/tests/googledoodle.jpg +0 -0
- package/tests/server.js +142 -0
- package/tests/squid.conf +76 -0
- package/tests/ssl/ca/README.md +8 -0
- package/tests/ssl/ca/ca.cnf +20 -0
- package/tests/ssl/ca/ca.crl +0 -0
- package/tests/ssl/ca/ca.crt +17 -0
- package/tests/ssl/ca/ca.csr +13 -0
- package/tests/ssl/ca/ca.key +18 -0
- package/tests/ssl/ca/ca.srl +1 -0
- package/tests/ssl/ca/client-enc.key +30 -0
- package/tests/ssl/ca/client.cnf +20 -0
- package/tests/ssl/ca/client.crt +20 -0
- package/tests/ssl/ca/client.csr +18 -0
- package/tests/ssl/ca/client.key +27 -0
- package/tests/ssl/ca/gen-all-certs.sh +6 -0
- package/tests/ssl/ca/gen-client.sh +25 -0
- package/tests/ssl/ca/gen-localhost.sh +22 -0
- package/tests/ssl/ca/gen-server.sh +18 -0
- package/tests/ssl/ca/localhost.cnf +20 -0
- package/tests/ssl/ca/localhost.crt +20 -0
- package/tests/ssl/ca/localhost.csr +18 -0
- package/tests/ssl/ca/localhost.js +33 -0
- package/tests/ssl/ca/localhost.key +27 -0
- package/tests/ssl/ca/server.cnf +19 -0
- package/tests/ssl/ca/server.crt +25 -0
- package/tests/ssl/ca/server.csr +29 -0
- package/tests/ssl/ca/server.js +34 -0
- package/tests/ssl/ca/server.key +51 -0
- package/tests/ssl/npm-ca.crt +16 -0
- package/tests/ssl/test.crt +15 -0
- package/tests/ssl/test.key +15 -0
- package/tests/test-agent.js +102 -0
- package/tests/test-agentOptions.js +51 -0
- package/tests/test-api.js +33 -0
- package/tests/test-aws.js +123 -0
- package/tests/test-baseUrl.js +133 -0
- package/tests/test-basic-auth.js +221 -0
- package/tests/test-bearer-auth.js +187 -0
- package/tests/test-body.js +154 -0
- package/tests/test-cookies.js +130 -0
- package/tests/test-defaults.js +340 -0
- package/tests/test-digest-auth.js +232 -0
- package/tests/test-emptyBody.js +56 -0
- package/tests/test-errors.js +108 -0
- package/tests/test-event-forwarding.js +39 -0
- package/tests/test-follow-all-303.js +45 -0
- package/tests/test-follow-all.js +57 -0
- package/tests/test-form-data-error.js +85 -0
- package/tests/test-form-data.js +133 -0
- package/tests/test-form-urlencoded.js +73 -0
- package/tests/test-form.js +101 -0
- package/tests/test-gzip.js +296 -0
- package/tests/test-har.js +175 -0
- package/tests/test-hawk.js +187 -0
- package/tests/test-headers.js +305 -0
- package/tests/test-http-signature.js +110 -0
- package/tests/test-httpModule.js +112 -0
- package/tests/test-https.js +116 -0
- package/tests/test-isUrl.js +120 -0
- package/tests/test-json-request.js +117 -0
- package/tests/test-localAddress.js +49 -0
- package/tests/test-multipart-encoding.js +147 -0
- package/tests/test-multipart.js +129 -0
- package/tests/test-node-debug.js +95 -0
- package/tests/test-oauth.js +721 -0
- package/tests/test-onelineproxy.js +61 -0
- package/tests/test-option-reuse.js +54 -0
- package/tests/test-options-convenience-method.js +52 -0
- package/tests/test-params.js +101 -0
- package/tests/test-piped-redirect.js +55 -0
- package/tests/test-pipes.js +383 -0
- package/tests/test-pool.js +148 -0
- package/tests/test-promise.js +53 -0
- package/tests/test-proxy-connect.js +80 -0
- package/tests/test-proxy.js +304 -0
- package/tests/test-qs.js +135 -0
- package/tests/test-redirect-auth.js +131 -0
- package/tests/test-redirect-complex.js +93 -0
- package/tests/test-redirect.js +449 -0
- package/tests/test-rfc3986.js +106 -0
- package/tests/test-stream.js +36 -0
- package/tests/test-timeout.js +260 -0
- package/tests/test-timing.js +147 -0
- package/tests/test-toJSON.js +45 -0
- package/tests/test-tunnel.js +466 -0
- package/tests/test-unix.js +74 -0
- package/tests/unicycle.jpg +0 -0
- package/request.js +0 -1553
- package/src/ZlibTransform.js +0 -27
- package/src/caseless.js +0 -118
- package/src/index.js +0 -122
- package/src/request.js +0 -967
- package/src/utils.js +0 -274
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
var request = require('../index')
|
|
4
|
+
var tape = require('tape')
|
|
5
|
+
|
|
6
|
+
var local = 'http://localhost:0/asdf'
|
|
7
|
+
|
|
8
|
+
tape('without uri', function (t) {
|
|
9
|
+
t.throws(function () {
|
|
10
|
+
request({})
|
|
11
|
+
}, /^Error: options\.uri is a required argument$/)
|
|
12
|
+
t.end()
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
tape('invalid uri 1', function (t) {
|
|
16
|
+
t.throws(function () {
|
|
17
|
+
request({
|
|
18
|
+
uri: 'this-is-not-a-valid-uri'
|
|
19
|
+
})
|
|
20
|
+
}, /^Error: Invalid URI/)
|
|
21
|
+
t.end()
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
tape('invalid uri 2', function (t) {
|
|
25
|
+
t.throws(function () {
|
|
26
|
+
request({
|
|
27
|
+
uri: 'github.com/uri-is-not-valid-without-protocol'
|
|
28
|
+
})
|
|
29
|
+
}, /^Error: Invalid URI/)
|
|
30
|
+
t.end()
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
tape('invalid uri + NO_PROXY', function (t) {
|
|
34
|
+
process.env.NO_PROXY = 'google.com'
|
|
35
|
+
t.throws(function () {
|
|
36
|
+
request({
|
|
37
|
+
uri: 'invalid'
|
|
38
|
+
})
|
|
39
|
+
}, /^Error: Invalid URI/)
|
|
40
|
+
delete process.env.NO_PROXY
|
|
41
|
+
t.end()
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
tape('deprecated unix URL', function (t) {
|
|
45
|
+
t.throws(function () {
|
|
46
|
+
request({
|
|
47
|
+
uri: 'unix://path/to/socket/and/then/request/path'
|
|
48
|
+
})
|
|
49
|
+
}, /^Error: `unix:\/\/` URL scheme is no longer supported/)
|
|
50
|
+
t.end()
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
tape('invalid body', function (t) {
|
|
54
|
+
t.throws(function () {
|
|
55
|
+
request({
|
|
56
|
+
uri: local, body: {}
|
|
57
|
+
})
|
|
58
|
+
}, /^Error: Argument error, options\.body\.$/)
|
|
59
|
+
t.end()
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
tape('invalid multipart', function (t) {
|
|
63
|
+
t.throws(function () {
|
|
64
|
+
request({
|
|
65
|
+
uri: local,
|
|
66
|
+
multipart: 'foo'
|
|
67
|
+
})
|
|
68
|
+
}, /^Error: Argument error, options\.multipart\.$/)
|
|
69
|
+
t.end()
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
tape('multipart without body 1', function (t) {
|
|
73
|
+
t.throws(function () {
|
|
74
|
+
request({
|
|
75
|
+
uri: local,
|
|
76
|
+
multipart: [ {} ]
|
|
77
|
+
})
|
|
78
|
+
}, /^Error: Body attribute missing in multipart\.$/)
|
|
79
|
+
t.end()
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
tape('multipart without body 2', function (t) {
|
|
83
|
+
t.throws(function () {
|
|
84
|
+
request(local, {
|
|
85
|
+
multipart: [ {} ]
|
|
86
|
+
})
|
|
87
|
+
}, /^Error: Body attribute missing in multipart\.$/)
|
|
88
|
+
t.end()
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
tape('head method with a body', function (t) {
|
|
92
|
+
t.throws(function () {
|
|
93
|
+
request(local, {
|
|
94
|
+
method: 'HEAD',
|
|
95
|
+
body: 'foo'
|
|
96
|
+
})
|
|
97
|
+
}, /HTTP HEAD requests MUST NOT include a request body/)
|
|
98
|
+
t.end()
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
tape('head method with a body 2', function (t) {
|
|
102
|
+
t.throws(function () {
|
|
103
|
+
request.head(local, {
|
|
104
|
+
body: 'foo'
|
|
105
|
+
})
|
|
106
|
+
}, /HTTP HEAD requests MUST NOT include a request body/)
|
|
107
|
+
t.end()
|
|
108
|
+
})
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
var server = require('./server')
|
|
4
|
+
var request = require('../index')
|
|
5
|
+
var tape = require('tape')
|
|
6
|
+
|
|
7
|
+
var s = server.createServer()
|
|
8
|
+
|
|
9
|
+
tape('setup', function (t) {
|
|
10
|
+
s.listen(0, function () {
|
|
11
|
+
s.on('/', function (req, res) {
|
|
12
|
+
res.writeHead(200, { 'content-type': 'text/plain' })
|
|
13
|
+
res.write('waited')
|
|
14
|
+
res.end()
|
|
15
|
+
})
|
|
16
|
+
t.end()
|
|
17
|
+
})
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
tape('should emit socket event', function (t) {
|
|
21
|
+
t.plan(4)
|
|
22
|
+
|
|
23
|
+
var req = request(s.url, function (err, res, body) {
|
|
24
|
+
t.equal(err, null)
|
|
25
|
+
t.equal(res.statusCode, 200)
|
|
26
|
+
t.equal(body, 'waited')
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
req.on('socket', function (socket) {
|
|
30
|
+
var requestSocket = req.req.socket
|
|
31
|
+
t.equal(requestSocket, socket)
|
|
32
|
+
})
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
tape('cleanup', function (t) {
|
|
36
|
+
s.close(function () {
|
|
37
|
+
t.end()
|
|
38
|
+
})
|
|
39
|
+
})
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
var http = require('http')
|
|
4
|
+
var request = require('../index')
|
|
5
|
+
var tape = require('tape')
|
|
6
|
+
|
|
7
|
+
var server = http.createServer(function (req, res) {
|
|
8
|
+
if (req.method === 'POST') {
|
|
9
|
+
res.setHeader('location', req.url)
|
|
10
|
+
res.statusCode = 303
|
|
11
|
+
res.end('try again')
|
|
12
|
+
} else {
|
|
13
|
+
res.end('ok')
|
|
14
|
+
}
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
tape('setup', function (t) {
|
|
18
|
+
server.listen(0, function () {
|
|
19
|
+
server.url = 'http://localhost:' + this.address().port
|
|
20
|
+
t.end()
|
|
21
|
+
})
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
tape('followAllRedirects with 303', function (t) {
|
|
25
|
+
var redirects = 0
|
|
26
|
+
|
|
27
|
+
request.post({
|
|
28
|
+
url: server.url + '/foo',
|
|
29
|
+
followAllRedirects: true,
|
|
30
|
+
form: { foo: 'bar' }
|
|
31
|
+
}, function (err, res, body) {
|
|
32
|
+
t.equal(err, null)
|
|
33
|
+
t.equal(body, 'ok')
|
|
34
|
+
t.equal(redirects, 1)
|
|
35
|
+
t.end()
|
|
36
|
+
}).on('redirect', function () {
|
|
37
|
+
redirects++
|
|
38
|
+
})
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
tape('cleanup', function (t) {
|
|
42
|
+
server.close(function () {
|
|
43
|
+
t.end()
|
|
44
|
+
})
|
|
45
|
+
})
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
var http = require('http')
|
|
4
|
+
var request = require('../index')
|
|
5
|
+
var tape = require('tape')
|
|
6
|
+
|
|
7
|
+
var server = http.createServer(function (req, res) {
|
|
8
|
+
// redirect everything 3 times, no matter what.
|
|
9
|
+
var c = req.headers.cookie
|
|
10
|
+
|
|
11
|
+
if (!c) {
|
|
12
|
+
c = 0
|
|
13
|
+
} else {
|
|
14
|
+
c = +c.split('=')[1] || 0
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
if (c > 3) {
|
|
18
|
+
res.end('ok')
|
|
19
|
+
return
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
res.setHeader('set-cookie', 'c=' + (c + 1))
|
|
23
|
+
res.setHeader('location', req.url)
|
|
24
|
+
res.statusCode = 302
|
|
25
|
+
res.end('try again')
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
tape('setup', function (t) {
|
|
29
|
+
server.listen(0, function () {
|
|
30
|
+
server.url = 'http://localhost:' + this.address().port
|
|
31
|
+
t.end()
|
|
32
|
+
})
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
tape('followAllRedirects', function (t) {
|
|
36
|
+
var redirects = 0
|
|
37
|
+
|
|
38
|
+
request.post({
|
|
39
|
+
url: server.url + '/foo',
|
|
40
|
+
followAllRedirects: true,
|
|
41
|
+
jar: true,
|
|
42
|
+
form: { foo: 'bar' }
|
|
43
|
+
}, function (err, res, body) {
|
|
44
|
+
t.equal(err, null)
|
|
45
|
+
t.equal(body, 'ok')
|
|
46
|
+
t.equal(redirects, 4)
|
|
47
|
+
t.end()
|
|
48
|
+
}).on('redirect', function () {
|
|
49
|
+
redirects++
|
|
50
|
+
})
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
tape('cleanup', function (t) {
|
|
54
|
+
server.close(function () {
|
|
55
|
+
t.end()
|
|
56
|
+
})
|
|
57
|
+
})
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
var request = require('../index')
|
|
4
|
+
var server = require('./server')
|
|
5
|
+
var tape = require('tape')
|
|
6
|
+
|
|
7
|
+
var s = server.createServer()
|
|
8
|
+
|
|
9
|
+
tape('setup', function (t) {
|
|
10
|
+
s.listen(0, function () {
|
|
11
|
+
t.end()
|
|
12
|
+
})
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
tape('re-emit formData errors', function (t) {
|
|
16
|
+
s.on('/', function (req, res) {
|
|
17
|
+
res.writeHead(400)
|
|
18
|
+
res.end()
|
|
19
|
+
t.fail('The form-data error did not abort the request.')
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
request.post(s.url, function (err, res, body) {
|
|
23
|
+
t.equal(err.message, 'form-data: Arrays are not supported.')
|
|
24
|
+
setTimeout(function () {
|
|
25
|
+
t.end()
|
|
26
|
+
}, 10)
|
|
27
|
+
}).form().append('field', ['value1', 'value2'])
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
tape('omit content-length header if the value is set to NaN', function (t) {
|
|
31
|
+
// returns chunked HTTP response which is streamed to the 2nd HTTP request in the form data
|
|
32
|
+
s.on('/chunky', server.createChunkResponse(
|
|
33
|
+
['some string',
|
|
34
|
+
'some other string'
|
|
35
|
+
]))
|
|
36
|
+
|
|
37
|
+
// accepts form data request
|
|
38
|
+
s.on('/stream', function (req, resp) {
|
|
39
|
+
req.on('data', function (chunk) {
|
|
40
|
+
// consume the request body
|
|
41
|
+
})
|
|
42
|
+
req.on('end', function () {
|
|
43
|
+
resp.writeHead(200)
|
|
44
|
+
resp.end()
|
|
45
|
+
})
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
var sendStreamRequest = function (stream) {
|
|
49
|
+
request.post({
|
|
50
|
+
uri: s.url + '/stream',
|
|
51
|
+
formData: {
|
|
52
|
+
param: stream
|
|
53
|
+
}
|
|
54
|
+
}, function (err, res) {
|
|
55
|
+
t.error(err, 'request failed')
|
|
56
|
+
t.end()
|
|
57
|
+
})
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
request.get({
|
|
61
|
+
uri: s.url + '/chunky'
|
|
62
|
+
}).on('response', function (res) {
|
|
63
|
+
sendStreamRequest(res)
|
|
64
|
+
})
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
// TODO: remove this test after form-data@2.0 starts stringifying null values
|
|
68
|
+
tape('form-data should throw on null value', function (t) {
|
|
69
|
+
t.throws(function () {
|
|
70
|
+
request({
|
|
71
|
+
method: 'POST',
|
|
72
|
+
url: s.url,
|
|
73
|
+
formData: {
|
|
74
|
+
key: null
|
|
75
|
+
}
|
|
76
|
+
})
|
|
77
|
+
}, TypeError)
|
|
78
|
+
t.end()
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
tape('cleanup', function (t) {
|
|
82
|
+
s.close(function () {
|
|
83
|
+
t.end()
|
|
84
|
+
})
|
|
85
|
+
})
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
var http = require('http')
|
|
4
|
+
var path = require('path')
|
|
5
|
+
var mime = require('mime-types')
|
|
6
|
+
var request = require('../index')
|
|
7
|
+
var fs = require('fs')
|
|
8
|
+
var tape = require('tape')
|
|
9
|
+
|
|
10
|
+
function runTest (t, options) {
|
|
11
|
+
var remoteFile = path.join(__dirname, 'googledoodle.jpg')
|
|
12
|
+
var localFile = path.join(__dirname, 'unicycle.jpg')
|
|
13
|
+
var multipartFormData = {}
|
|
14
|
+
|
|
15
|
+
var server = http.createServer(function (req, res) {
|
|
16
|
+
if (req.url === '/file') {
|
|
17
|
+
res.writeHead(200, {'content-type': 'image/jpg', 'content-length': 7187})
|
|
18
|
+
res.end(fs.readFileSync(remoteFile), 'binary')
|
|
19
|
+
return
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
if (options.auth) {
|
|
23
|
+
if (!req.headers.authorization) {
|
|
24
|
+
res.writeHead(401, {'www-authenticate': 'Basic realm="Private"'})
|
|
25
|
+
res.end()
|
|
26
|
+
return
|
|
27
|
+
} else {
|
|
28
|
+
t.ok(req.headers.authorization === 'Basic ' + Buffer.from('user:pass').toString('base64'))
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
t.ok(/multipart\/form-data; boundary=--------------------------\d+/
|
|
33
|
+
.test(req.headers['content-type']))
|
|
34
|
+
|
|
35
|
+
// temp workaround
|
|
36
|
+
var data = ''
|
|
37
|
+
req.setEncoding('utf8')
|
|
38
|
+
|
|
39
|
+
req.on('data', function (d) {
|
|
40
|
+
data += d
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
req.on('end', function () {
|
|
44
|
+
// check for the fields' traces
|
|
45
|
+
|
|
46
|
+
// 1st field : my_field
|
|
47
|
+
t.ok(data.indexOf('form-data; name="my_field"') !== -1)
|
|
48
|
+
t.ok(data.indexOf(multipartFormData.my_field) !== -1)
|
|
49
|
+
|
|
50
|
+
// 2nd field : my_buffer
|
|
51
|
+
t.ok(data.indexOf('form-data; name="my_buffer"') !== -1)
|
|
52
|
+
t.ok(data.indexOf(multipartFormData.my_buffer) !== -1)
|
|
53
|
+
|
|
54
|
+
// 3rd field : my_file
|
|
55
|
+
t.ok(data.indexOf('form-data; name="my_file"') !== -1)
|
|
56
|
+
t.ok(data.indexOf('; filename="' + path.basename(multipartFormData.my_file.path) + '"') !== -1)
|
|
57
|
+
// check for unicycle.jpg traces
|
|
58
|
+
t.ok(data.indexOf('2005:06:21 01:44:12') !== -1)
|
|
59
|
+
t.ok(data.indexOf('Content-Type: ' + mime.lookup(multipartFormData.my_file.path)) !== -1)
|
|
60
|
+
|
|
61
|
+
// 4th field : remote_file
|
|
62
|
+
t.ok(data.indexOf('form-data; name="remote_file"') !== -1)
|
|
63
|
+
t.ok(data.indexOf('; filename="' + path.basename(multipartFormData.remote_file.path) + '"') !== -1)
|
|
64
|
+
|
|
65
|
+
// 5th field : file with metadata
|
|
66
|
+
t.ok(data.indexOf('form-data; name="secret_file"') !== -1)
|
|
67
|
+
t.ok(data.indexOf('Content-Disposition: form-data; name="secret_file"; filename="topsecret.jpg"') !== -1)
|
|
68
|
+
t.ok(data.indexOf('Content-Type: image/custom') !== -1)
|
|
69
|
+
|
|
70
|
+
// 6th field : batch of files
|
|
71
|
+
t.ok(data.indexOf('form-data; name="batch"') !== -1)
|
|
72
|
+
t.ok(data.match(/form-data; name="batch"/g).length === 2)
|
|
73
|
+
|
|
74
|
+
// check for http://localhost:nnnn/file traces
|
|
75
|
+
t.ok(data.indexOf('Photoshop ICC') !== -1)
|
|
76
|
+
t.ok(data.indexOf('Content-Type: ' + mime.lookup(remoteFile)) !== -1)
|
|
77
|
+
|
|
78
|
+
res.writeHead(200)
|
|
79
|
+
res.end(options.json ? JSON.stringify({status: 'done'}) : 'done')
|
|
80
|
+
})
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
server.listen(0, function () {
|
|
84
|
+
var url = 'http://localhost:' + this.address().port
|
|
85
|
+
// @NOTE: multipartFormData properties must be set here so that my_file read stream does not leak in node v0.8
|
|
86
|
+
multipartFormData.my_field = 'my_value'
|
|
87
|
+
multipartFormData.my_buffer = Buffer.from([1, 2, 3])
|
|
88
|
+
multipartFormData.my_file = fs.createReadStream(localFile)
|
|
89
|
+
multipartFormData.remote_file = request(url + '/file')
|
|
90
|
+
multipartFormData.secret_file = {
|
|
91
|
+
value: fs.createReadStream(localFile),
|
|
92
|
+
options: {
|
|
93
|
+
filename: 'topsecret.jpg',
|
|
94
|
+
contentType: 'image/custom'
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
multipartFormData.batch = [
|
|
98
|
+
fs.createReadStream(localFile),
|
|
99
|
+
fs.createReadStream(localFile)
|
|
100
|
+
]
|
|
101
|
+
|
|
102
|
+
var reqOptions = {
|
|
103
|
+
url: url + '/upload',
|
|
104
|
+
formData: multipartFormData
|
|
105
|
+
}
|
|
106
|
+
if (options.json) {
|
|
107
|
+
reqOptions.json = true
|
|
108
|
+
}
|
|
109
|
+
if (options.auth) {
|
|
110
|
+
reqOptions.auth = {user: 'user', pass: 'pass', sendImmediately: false}
|
|
111
|
+
}
|
|
112
|
+
request.post(reqOptions, function (err, res, body) {
|
|
113
|
+
t.equal(err, null)
|
|
114
|
+
t.equal(res.statusCode, 200)
|
|
115
|
+
t.deepEqual(body, options.json ? {status: 'done'} : 'done')
|
|
116
|
+
server.close(function () {
|
|
117
|
+
t.end()
|
|
118
|
+
})
|
|
119
|
+
})
|
|
120
|
+
})
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
tape('multipart formData', function (t) {
|
|
124
|
+
runTest(t, {json: false})
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
tape('multipart formData + JSON', function (t) {
|
|
128
|
+
runTest(t, {json: true})
|
|
129
|
+
})
|
|
130
|
+
|
|
131
|
+
tape('multipart formData + basic auth', function (t) {
|
|
132
|
+
runTest(t, {json: false, auth: true})
|
|
133
|
+
})
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
var http = require('http')
|
|
4
|
+
var request = require('../index')
|
|
5
|
+
var tape = require('tape')
|
|
6
|
+
|
|
7
|
+
function runTest (t, options, index) {
|
|
8
|
+
var server = http.createServer(function (req, res) {
|
|
9
|
+
if (index === 0 || index === 3) {
|
|
10
|
+
t.equal(req.headers['content-type'], 'application/x-www-form-urlencoded')
|
|
11
|
+
} else {
|
|
12
|
+
t.equal(req.headers['content-type'], 'application/x-www-form-urlencoded; charset=UTF-8')
|
|
13
|
+
}
|
|
14
|
+
t.equal(req.headers['content-length'], '21')
|
|
15
|
+
t.equal(req.headers.accept, 'application/json')
|
|
16
|
+
|
|
17
|
+
var data = ''
|
|
18
|
+
req.setEncoding('utf8')
|
|
19
|
+
|
|
20
|
+
req.on('data', function (d) {
|
|
21
|
+
data += d
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
req.on('end', function () {
|
|
25
|
+
t.equal(data, 'some=url&encoded=data')
|
|
26
|
+
|
|
27
|
+
res.writeHead(200)
|
|
28
|
+
res.end('done')
|
|
29
|
+
})
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
server.listen(0, function () {
|
|
33
|
+
var url = 'http://localhost:' + this.address().port
|
|
34
|
+
var r = request.post(url, options, function (err, res, body) {
|
|
35
|
+
t.equal(err, null)
|
|
36
|
+
t.equal(res.statusCode, 200)
|
|
37
|
+
t.equal(body, 'done')
|
|
38
|
+
server.close(function () {
|
|
39
|
+
t.end()
|
|
40
|
+
})
|
|
41
|
+
})
|
|
42
|
+
if (!options.form && !options.body) {
|
|
43
|
+
r.form({some: 'url', encoded: 'data'})
|
|
44
|
+
}
|
|
45
|
+
})
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
var cases = [
|
|
49
|
+
{
|
|
50
|
+
form: {some: 'url', encoded: 'data'},
|
|
51
|
+
json: true
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
headers: {'content-type': 'application/x-www-form-urlencoded; charset=UTF-8'},
|
|
55
|
+
form: {some: 'url', encoded: 'data'},
|
|
56
|
+
json: true
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
headers: {'content-type': 'application/x-www-form-urlencoded; charset=UTF-8'},
|
|
60
|
+
body: 'some=url&encoded=data',
|
|
61
|
+
json: true
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
// body set via .form() method
|
|
65
|
+
json: true
|
|
66
|
+
}
|
|
67
|
+
]
|
|
68
|
+
|
|
69
|
+
cases.forEach(function (options, index) {
|
|
70
|
+
tape('application/x-www-form-urlencoded ' + index, function (t) {
|
|
71
|
+
runTest(t, options, index)
|
|
72
|
+
})
|
|
73
|
+
})
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
var http = require('http')
|
|
4
|
+
var path = require('path')
|
|
5
|
+
var mime = require('mime-types')
|
|
6
|
+
var request = require('../index')
|
|
7
|
+
var fs = require('fs')
|
|
8
|
+
var tape = require('tape')
|
|
9
|
+
|
|
10
|
+
tape('multipart form append', function (t) {
|
|
11
|
+
var remoteFile = path.join(__dirname, 'googledoodle.jpg')
|
|
12
|
+
var localFile = path.join(__dirname, 'unicycle.jpg')
|
|
13
|
+
var totalLength = null
|
|
14
|
+
var FIELDS = []
|
|
15
|
+
|
|
16
|
+
var server = http.createServer(function (req, res) {
|
|
17
|
+
if (req.url === '/file') {
|
|
18
|
+
res.writeHead(200, {'content-type': 'image/jpg', 'content-length': 7187})
|
|
19
|
+
res.end(fs.readFileSync(remoteFile), 'binary')
|
|
20
|
+
return
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
t.ok(/multipart\/form-data; boundary=--------------------------\d+/
|
|
24
|
+
.test(req.headers['content-type']))
|
|
25
|
+
|
|
26
|
+
// temp workaround
|
|
27
|
+
var data = ''
|
|
28
|
+
req.setEncoding('utf8')
|
|
29
|
+
|
|
30
|
+
req.on('data', function (d) {
|
|
31
|
+
data += d
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
req.on('end', function () {
|
|
35
|
+
var field
|
|
36
|
+
// check for the fields' traces
|
|
37
|
+
|
|
38
|
+
// 1st field : my_field
|
|
39
|
+
field = FIELDS.shift()
|
|
40
|
+
t.ok(data.indexOf('form-data; name="' + field.name + '"') !== -1)
|
|
41
|
+
t.ok(data.indexOf(field.value) !== -1)
|
|
42
|
+
|
|
43
|
+
// 2nd field : my_buffer
|
|
44
|
+
field = FIELDS.shift()
|
|
45
|
+
t.ok(data.indexOf('form-data; name="' + field.name + '"') !== -1)
|
|
46
|
+
t.ok(data.indexOf(field.value) !== -1)
|
|
47
|
+
|
|
48
|
+
// 3rd field : my_file
|
|
49
|
+
field = FIELDS.shift()
|
|
50
|
+
t.ok(data.indexOf('form-data; name="' + field.name + '"') !== -1)
|
|
51
|
+
t.ok(data.indexOf('; filename="' + path.basename(field.value.path) + '"') !== -1)
|
|
52
|
+
// check for unicycle.jpg traces
|
|
53
|
+
t.ok(data.indexOf('2005:06:21 01:44:12') !== -1)
|
|
54
|
+
t.ok(data.indexOf('Content-Type: ' + mime.lookup(field.value.path)) !== -1)
|
|
55
|
+
|
|
56
|
+
// 4th field : remote_file
|
|
57
|
+
field = FIELDS.shift()
|
|
58
|
+
t.ok(data.indexOf('form-data; name="' + field.name + '"') !== -1)
|
|
59
|
+
t.ok(data.indexOf('; filename="' + path.basename(field.value.path) + '"') !== -1)
|
|
60
|
+
// check for http://localhost:nnnn/file traces
|
|
61
|
+
t.ok(data.indexOf('Photoshop ICC') !== -1)
|
|
62
|
+
t.ok(data.indexOf('Content-Type: ' + mime.lookup(remoteFile)) !== -1)
|
|
63
|
+
|
|
64
|
+
t.ok(+req.headers['content-length'] === totalLength)
|
|
65
|
+
|
|
66
|
+
res.writeHead(200)
|
|
67
|
+
res.end('done')
|
|
68
|
+
|
|
69
|
+
t.equal(FIELDS.length, 0)
|
|
70
|
+
})
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
server.listen(0, function () {
|
|
74
|
+
var url = 'http://localhost:' + this.address().port
|
|
75
|
+
FIELDS = [
|
|
76
|
+
{ name: 'my_field', value: 'my_value' },
|
|
77
|
+
{ name: 'my_buffer', value: Buffer.from([1, 2, 3]) },
|
|
78
|
+
{ name: 'my_file', value: fs.createReadStream(localFile) },
|
|
79
|
+
{ name: 'remote_file', value: request(url + '/file') }
|
|
80
|
+
]
|
|
81
|
+
|
|
82
|
+
var req = request.post(url + '/upload', function (err, res, body) {
|
|
83
|
+
t.equal(err, null)
|
|
84
|
+
t.equal(res.statusCode, 200)
|
|
85
|
+
t.equal(body, 'done')
|
|
86
|
+
server.close(function () {
|
|
87
|
+
t.end()
|
|
88
|
+
})
|
|
89
|
+
})
|
|
90
|
+
var form = req.form()
|
|
91
|
+
|
|
92
|
+
FIELDS.forEach(function (field) {
|
|
93
|
+
form.append(field.name, field.value)
|
|
94
|
+
})
|
|
95
|
+
|
|
96
|
+
form.getLength(function (err, length) {
|
|
97
|
+
t.equal(err, null)
|
|
98
|
+
totalLength = length
|
|
99
|
+
})
|
|
100
|
+
})
|
|
101
|
+
})
|