Skip to content

Commit 1d58a23

Browse files
theliukLuca Marzi
and
Luca Marzi
authored
added test for multipart/form-data request with text and file (#121)
* added test for multipart/form-data request with text and file * added to test to check compatibility with fastify-multipart * added warn message when fastify-reply-from is registered with fastify-multipart * added documentation for incompatibility between fastify-reply-from and fastify-multipart * removed unecessary async in test * briefly explained why fastify-reply-from is incompatible with fastify-multipart Co-authored-by: Luca Marzi <luca.marzi@mia-platform.eu>
1 parent f6d1159 commit 1d58a23

File tree

5 files changed

+109
-0
lines changed

5 files changed

+109
-0
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@ HTTP2 to HTTP is supported too.
1212
npm i fastify-reply-from
1313
```
1414

15+
## Compatibility with fastify-multipart
16+
`fastify-reply-from` and [`fastify-multipart`](https://github.com/fastify/fastify-multipart) should not be registered as sibling plugins nor shold be registered in plugins which have a parent-child relationship.<br> The two plugins are incompatible, in the sense that the behavior of `fastify-reply-from` might not be the expected one when the above-mentioned conditions are not respected.<br> This is due to the fact that `fastify-multipart` consumes the multipart content by parsing it, hence this content is not forwarded to the target service by `fastify-reply-from`.<br>
17+
However, the two plugins may be used within the same fastify instance, at the condition that they belong to disjoint branches of the fastify plugins hierarchy tree.
18+
1519
## Usage
1620

1721
The following example set up two fastify servers and forward the request

index.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,13 @@ module.exports = fp(function from (fastify, opts, next) {
133133
return this
134134
})
135135

136+
fastify.addHook('onReady', (done) => {
137+
if (isFastifyMultipartRegistered(fastify)) {
138+
fastify.log.warn('fastify-reply-from might not behave as expected when used with fastify-multipart')
139+
}
140+
done()
141+
})
142+
136143
fastify.onClose((fastify, next) => {
137144
close()
138145
// let the event loop do a full run so that it can
@@ -172,3 +179,7 @@ function requestHeadersNoOp (originalReq, headers) {
172179
function onErrorDefault (reply, { error }) {
173180
reply.send(error)
174181
}
182+
183+
function isFastifyMultipartRegistered (fastify) {
184+
return fastify.hasContentTypeParser('multipart') && fastify.hasRequestDecorator('multipart')
185+
}

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@
3434
"@types/node": "^14.14.2",
3535
"@types/tap": "^14.10.1",
3636
"fastify": "^3.7.0",
37+
"fastify-multipart": "^3.3.1",
38+
"form-data": "^2.3.3",
3739
"got": "^11.8.0",
3840
"h2url": "^0.2.0",
3941
"msgpack5": "^4.2.1",
@@ -42,6 +44,7 @@
4244
"proxyquire": "^2.1.3",
4345
"simple-get": "^4.0.0",
4446
"snazzy": "^9.0.0",
47+
"split2": "^3.2.2",
4548
"standard": "^16.0.3",
4649
"tap": "^14.10.7",
4750
"tsd": "^0.14.0",
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
'use strict'
2+
3+
const fs = require('fs')
4+
const path = require('path')
5+
const t = require('tap')
6+
const Fastify = require('fastify')
7+
const From = require('..')
8+
const Multipart = require('fastify-multipart')
9+
const http = require('http')
10+
const get = require('simple-get').concat
11+
const FormData = require('form-data')
12+
13+
const split = require('split2')
14+
const logStream = split(JSON.parse)
15+
16+
const instance = Fastify({
17+
logger: {
18+
level: 'warn',
19+
stream: logStream
20+
}
21+
})
22+
23+
instance.register(Multipart)
24+
instance.register(From)
25+
26+
t.plan(12)
27+
28+
t.tearDown(instance.close.bind(instance))
29+
30+
const filetPath = path.join(__dirname, 'fixtures', 'file.txt')
31+
const fileContent = fs.readFileSync(filetPath, { encoding: 'utf-8' })
32+
33+
const target = http.createServer((req, res) => {
34+
t.pass('request proxied')
35+
t.equal(req.method, 'POST')
36+
t.match(req.headers['content-type'], /^multipart\/form-data/)
37+
let data = ''
38+
req.setEncoding('utf8')
39+
req.on('data', (d) => {
40+
data += d
41+
})
42+
req.on('end', () => {
43+
t.notMatch(data, 'Content-Disposition: form-data; name="key"')
44+
t.notMatch(data, 'value')
45+
t.notMatch(data, 'Content-Disposition: form-data; name="file"')
46+
t.notMatch(data, fileContent)
47+
res.setHeader('content-type', 'application/json')
48+
res.statusCode = 200
49+
res.end(JSON.stringify({ something: 'else' }))
50+
})
51+
})
52+
53+
instance.post('/', (request, reply) => {
54+
reply.from(`http://localhost:${target.address().port}`)
55+
})
56+
57+
t.tearDown(target.close.bind(target))
58+
59+
instance.listen(0, (err) => {
60+
t.error(err)
61+
62+
logStream.on('data', (log) => {
63+
if (
64+
log.level === 40 &&
65+
log.msg.match(/fastify-reply-from might not behave as expected when used with fastify-multipart/)
66+
) {
67+
t.pass('incompatibility warn message logged')
68+
}
69+
})
70+
71+
target.listen(0, (err) => {
72+
t.error(err)
73+
74+
const form = new FormData()
75+
form.append('key', 'value')
76+
form.append('file', fs.createReadStream(filetPath, { encoding: 'utf-8' }))
77+
78+
get({
79+
url: `http://localhost:${instance.server.address().port}`,
80+
method: 'POST',
81+
headers: {
82+
...form.getHeaders()
83+
},
84+
body: form
85+
}, (err, res, data) => {
86+
t.error(err)
87+
t.deepEqual(JSON.parse(data), { something: 'else' })
88+
})
89+
})
90+
})

test/fixtures/file.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
file content

0 commit comments

Comments
 (0)