Skip to content

matesedu/fastmulp

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

14 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

fastmulp

High-accuracy, low-allocation multipart/form-data parsing with a zero-copy Rust core and thin targets for Node.js and the browser.

Workspace

  • crates/fastmulp_core: zero-copy parser and native API
  • crates/fastmulp_napi: Node.js bindings built with napi-rs
  • crates/fastmulp_wasm: browser bindings built with wasm-bindgen

Design

  • The Rust core parses against a borrowed &[u8] and returns body ranges instead of copying payload bytes.
  • Content-Disposition is parsed eagerly, and name is enforced for form-data parts.
  • Header storage uses SmallVec so the common case stays stack-friendly.
  • Node.js and browser bindings return metadata plus body ranges, so callers can slice the original buffer themselves.
  • Boundary lines accept RFC 2046 transport padding, plus MIME-style preamble and epilogue.

Spec Notes

For deployed compatibility, fastmulp also accepts filename*= extended parameters even though RFC 7578 Section 4.2 says senders must not generate them.

filename values are untrusted input. Follow the security guidance in RFC 7578 Section 4.2 and avoid using path components blindly.

Rust Example

use fastmulp_core::parse;

let boundary = "demo-boundary";
let body = b"--demo-boundary\r\nContent-Disposition: form-data; name=\"field\"\r\n\r\nhello\r\n--demo-boundary--\r\n";
let multipart = parse(body, boundary.as_bytes())?;

let part = &multipart.parts()[0];
assert_eq!(part.name().and_then(|value| value.as_str().ok()), Some("field"));
assert_eq!(part.body(multipart.body()), b"hello");
# Ok::<(), fastmulp_core::Error>(())

JS Targets

Node.js:

import { parse } from "./fastmulp.node";

const parts = parse(bodyBuffer, boundary);
const fileBytes = bodyBuffer.subarray(parts[0].bodyStart, parts[0].bodyEnd);

Browser:

import { parse } from "fastmulp-wasm";

const parts = parse(formBytes, boundary);
const fieldBytes = formBytes.subarray(parts[0].body_start, parts[0].body_end);

The wasm target still needs one JS-to-wasm copy at the ABI boundary, but it avoids extra copies after parsing by returning ranges instead of materialized part bodies.

Older nested multipart/mixed payloads can be handled by recursively calling parse on a part body after extracting the nested boundary from that part's Content-Type.

Parser Limits

Use parse_with_limits or MultipartParser::new_with_limits when parsing untrusted uploads on a server boundary. ParseLimits can cap the number of parts, the number of headers per part, and the number of header bytes per part. The plain parse API stays unlimited for compatibility and small trusted payloads.

Security Notes

Treat filename as display metadata only. Strip path separators and platform-specific path components before using it in storage, generate your own server-side object names, and keep the original value only as untrusted metadata. Preserve duplicate field names in order unless your application explicitly defines a merge rule. For nested multipart/mixed, extract the nested boundary from that part's Content-Type and parse the nested body with its own limits.

Binding Artifacts

Tag pushes build npm-compatible release artifacts for the JS targets:

  • fastmulp-node-linux-x64-*.tgz: platform-specific Node.js native addon containing fastmulp.node, CommonJS entrypoint, and TypeScript declarations.
  • fastmulp-wasm-*.tgz: browser wasm package containing wasm-bindgen generated JavaScript glue, wasm, and TypeScript declarations.

The Node binding uses camelCase object fields such as bodyStart, bodyEnd, fileName, and contentType. The wasm binding preserves the existing snake_case field names generated by wasm-bindgen.

import { parse as parseNode } from "fastmulp-node-linux-x64";
import { parse as parseWasm } from "fastmulp-wasm";

Release

  • vp run release:patch
  • vp run release:minor
  • vp run release:alpha
  • vp run release:beta

Each release command updates the workspace version in Cargo.toml, creates a release commit, and creates the matching v... git tag. Tag pushes publish fastmulp-core through GitHub Actions trusted publishing and attach Node/wasm binding artifacts to the matching GitHub Release.

Shared Tasks

  • vp run fmt
  • vp run fmt:check
  • vp run ci:local
  • vp run lint
  • vp run check
  • vp run test
  • vp run bench
  • vp run package:core

License

fastmulp is licensed under GPL-3.0-or-later. See LICENSE and the GNU GPL v3 text.

About

⚠️ wip

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors