Skip to content

Commit e0bcbd2

Browse files
committed
fix: use fastify native parser for xml
Signed-off-by: ferhat elmas <elmas.ferhat@gmail.com>
1 parent a9746b0 commit e0bcbd2

File tree

6 files changed

+178
-117
lines changed

6 files changed

+178
-117
lines changed

package-lock.json

Lines changed: 2 additions & 83 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,6 @@
7070
"dotenv": "^16.0.0",
7171
"fastify": "^5.7.4",
7272
"fastify-plugin": "^5.1.0",
73-
"fastify-xml-body-parser": "^2.2.0",
7473
"fs-extra": "^10.0.1",
7574
"fs-xattr": "0.3.1",
7675
"ioredis": "^5.2.4",

src/http/plugins/xml.ts

Lines changed: 74 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,86 @@
11
import accepts from '@fastify/accepts'
22
import { FastifyInstance } from 'fastify'
33
import fastifyPlugin from 'fastify-plugin'
4-
// no types exists for this package
5-
// @ts-ignore
6-
import xmlBodyParser from 'fastify-xml-body-parser'
74
import xml from 'xml2js'
85

6+
type XmlParserOptions = { disableContentParser?: boolean; parseAsArray?: string[] }
7+
type RequestError = Error & { statusCode?: number }
8+
9+
function forcePathAsArray(node: unknown, pathSegments: string[]): void {
10+
if (pathSegments.length === 0 || node === null || node === undefined) {
11+
return
12+
}
13+
14+
if (Array.isArray(node)) {
15+
node.forEach((item) => forcePathAsArray(item, pathSegments))
16+
return
17+
}
18+
19+
if (typeof node !== 'object') {
20+
return
21+
}
22+
23+
const [current, ...rest] = pathSegments
24+
const currentRecord = node as Record<string, unknown>
25+
26+
if (!(current in currentRecord)) {
27+
return
28+
}
29+
30+
if (rest.length === 0) {
31+
const value = currentRecord[current]
32+
if (value !== undefined && !Array.isArray(value)) {
33+
currentRecord[current] = [value]
34+
}
35+
return
36+
}
37+
38+
forcePathAsArray(currentRecord[current], rest)
39+
}
40+
941
export const xmlParser = fastifyPlugin(
10-
async function (
11-
fastify: FastifyInstance,
12-
opts: { disableContentParser?: boolean; parseAsArray?: string[] }
13-
) {
42+
async function (fastify: FastifyInstance, opts: XmlParserOptions) {
1443
fastify.register(accepts)
1544

1645
if (!opts.disableContentParser) {
17-
fastify.register(xmlBodyParser, {
18-
contentType: ['text/xml', 'application/xml'],
19-
isArray: (_: string, jpath: string) => {
20-
return opts.parseAsArray?.includes(jpath)
21-
},
22-
})
46+
fastify.addContentTypeParser(
47+
['text/xml', 'application/xml'],
48+
{ parseAs: 'string' },
49+
(_request, body, done) => {
50+
if (!body) {
51+
done(null, null)
52+
return
53+
}
54+
55+
xml.parseString(
56+
body,
57+
{
58+
explicitArray: false,
59+
trim: true,
60+
valueProcessors: [xml.processors.parseNumbers, xml.processors.parseBooleans],
61+
},
62+
(err: Error | null, parsed: unknown) => {
63+
if (err) {
64+
const parseError: RequestError = new Error(`Invalid XML payload: ${err.message}`)
65+
parseError.statusCode = 400
66+
done(parseError)
67+
return
68+
}
69+
70+
if (parsed && opts.parseAsArray?.length) {
71+
opts.parseAsArray.forEach((path) => {
72+
if (!path) {
73+
return
74+
}
75+
forcePathAsArray(parsed, path.split('.'))
76+
})
77+
}
78+
79+
done(null, parsed)
80+
}
81+
)
82+
}
83+
)
2384
}
2485

2586
fastify.addHook('preSerialization', async (req, res, payload) => {

src/http/routes/object/getObject.ts

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { ERRORS } from '@internal/errors'
22
import { Obj } from '@storage/schemas'
33
import { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify'
4-
import { IncomingMessage, Server, ServerResponse } from 'http'
54
import { FromSchema } from 'json-schema-to-ts'
65
import { getConfig } from '../../../config'
76
import { AuthenticatedRangeRequest } from '../../types'
@@ -30,16 +29,9 @@ interface getObjectRequestInterface extends AuthenticatedRangeRequest {
3029
Querystring: FromSchema<typeof getObjectQuerySchema>
3130
}
3231

33-
async function requestHandler(
34-
request: FastifyRequest<getObjectRequestInterface, Server, IncomingMessage>,
35-
response: FastifyReply<
36-
getObjectRequestInterface,
37-
Server,
38-
IncomingMessage,
39-
ServerResponse,
40-
unknown
41-
>
42-
) {
32+
type GetObjectRequest = FastifyRequest<getObjectRequestInterface>
33+
34+
async function requestHandler(request: GetObjectRequest, response: FastifyReply) {
4335
const { bucketName } = request.params
4436
const { download } = request.query
4537
const objectName = request.params['*']

src/http/routes/object/getObjectInfo.ts

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { ERRORS } from '@internal/errors'
22
import { Obj } from '@storage/schemas'
33
import { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify'
4-
import { IncomingMessage, Server, ServerResponse } from 'http'
54
import { FromSchema } from 'json-schema-to-ts'
65
import { getConfig } from '../../../config'
76
import { AuthenticatedRangeRequest } from '../../types'
@@ -22,15 +21,11 @@ interface getObjectRequestInterface extends AuthenticatedRangeRequest {
2221
Params: FromSchema<typeof getObjectParamsSchema>
2322
}
2423

24+
type GetObjectInfoRequest = FastifyRequest<getObjectRequestInterface>
25+
2526
async function requestHandler(
26-
request: FastifyRequest<getObjectRequestInterface, Server, IncomingMessage>,
27-
response: FastifyReply<
28-
getObjectRequestInterface,
29-
Server,
30-
IncomingMessage,
31-
ServerResponse,
32-
unknown
33-
>,
27+
request: GetObjectInfoRequest,
28+
response: FastifyReply,
3429
publicRoute = false,
3530
method: 'head' | 'info' = 'head'
3631
) {

0 commit comments

Comments
 (0)