Skip to content

Commit

Permalink
wip benchmark
Browse files Browse the repository at this point in the history
  • Loading branch information
talentlessguy committed Sep 29, 2024
1 parent 23a4042 commit ff96362
Show file tree
Hide file tree
Showing 8 changed files with 154 additions and 15 deletions.
1 change: 1 addition & 0 deletions bench/file.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
this is a file that is being sent in a multipart request
17 changes: 17 additions & 0 deletions bench/formidable.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// @ts-check
import { createServer } from 'node:http'
import formidable from 'formidable'
import { createReadStream } from 'node:fs'

const form = formidable({})

const server = createServer((req, res) => {
form.parse(req, (_, __, files) => {
// @ts-expect-error this is JS
const file = createReadStream(files.file[0].filepath)

file.pipe(res)
})
})

server.listen(3005)
22 changes: 18 additions & 4 deletions bench/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ Below is a comparison of body-parser and milliparsec in terms of parsing a reque
- System: Linux 6.9.7
- Machine: Asus ROG Zephyrus G16

### Benchmark command:
### JSON parsing

#### Benchmark command:

```sh
autocannon -b '{"a":1}' -H "Content-Type=application/json" localhost:3002 # or 3003
```

### Results
#### Results

body-parser result:

Expand Down Expand Up @@ -60,6 +62,18 @@ Req/Bytes counts sampled once per second.
697k requests in 11.02s, 85.1 MB rea
```

## Verdict
### Verdict

milliparsec, on average, is ~34% faster.

### Multipart with files

#### Benchmark command:

```sh
autocannon -m POST --form '{ "file": { "type": "file", "path": "./file.txt" } }' localhost:3004
```

#### Results

milliparsec, on average, is ~34% faster.
TBD
41 changes: 41 additions & 0 deletions bench/milliparsec-multipart.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// @ts-check

import { createServer } from 'node:http'
import * as bodyParser from '../dist/index.js'
const mw = bodyParser.multipart()

const server = createServer((req, res) => {
mw(req, res, () => {
/**
* @type {File}
*/
// @ts-ignore
const file = req.body.file

const stream = file.stream()

res.writeHead(200, {
'Content-Type': file.type,
'Content-Disposition': `attachment; filename="${file.name}"`
})

// Pipe the stream to the response
stream.pipeTo(
new WritableStream({
write(chunk) {
res.write(chunk)
},
close() {
res.end()
},
abort(err) {
console.error('Stream error:', err)
res.writeHead(500)
res.end('Error streaming file')
}
})
)
})
})

server.listen(3004)
7 changes: 6 additions & 1 deletion bench/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,19 @@
"description": "",
"main": "index.js",
"scripts": {
"bench": "autocannon -b '{\"a\":1}' -H \"Content-Type=application/json\""
"bench": "autocannon -b '{\"a\":1}' -H \"Content-Type=application/json\"",
"bench:multipart": "autocannon -m POST --form '{ \"file\": { \"type\": \"file\", \"path\": \"./file.txt\" } }'"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"@types/body-parser": "^1.19.5",
"@types/formidable": "^3.4.5",
"autocannon": "^7.15.0",
"body-parser": "^1.20.2"
},
"dependencies": {
"formidable": "^3.5.1"
}
}
54 changes: 54 additions & 0 deletions bench/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

24 changes: 15 additions & 9 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ const parseMultipart = (body: string, boundary: string) => {
if (filename) {
const contentTypeMatch = /Content-Type: (.+)/i.exec(data)!
const fileContent = data.slice(contentTypeMatch[0].length + 2)
// This is a file field

return Object.assign(parsedBody, {
[name]: new File([fileContent], filename[1], { type: contentTypeMatch[1] })
})
Expand All @@ -118,14 +118,20 @@ const parseMultipart = (body: string, boundary: string) => {
return parsedBody
}

const multipart = () => async (req: ReqWithBody, res: Response, next: NextFunction) => {
if (hasBody(req.method!)) {
req.body = await p((x) => {
const boundary = getBoundary(req.headers['content-type']!)
if (boundary) return parseMultipart(x, boundary)
})(req, res, next)
type MultipartOptions = Partial<{
fileCountLimit: number
fileSizeLimit: number
}>

const multipart =
(opts: MultipartOptions = {}) =>
async (req: ReqWithBody, res: Response, next: NextFunction) => {
if (hasBody(req.method!)) {
req.body = await p((x) => {
const boundary = getBoundary(req.headers['content-type']!)
if (boundary) return parseMultipart(x, boundary)
})(req, res, next)
} else next()
}
else next()
}

export { custom, json, raw, text, urlencoded, multipart }
3 changes: 2 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"declaration": true,
"outDir": "dist",
"strict": true,
"removeComments": true
"removeComments": true,
"allowJs": true
}
}

0 comments on commit ff96362

Please sign in to comment.