Skip to content

mates-inc/fastmulp

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

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_start / body_end, 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].body_start, parts[0].body_end);

Browser:

import init, { parse } from "./fastmulp_wasm.js";

await init();
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.

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.

Shared Tasks

  • vp run fmt
  • vp run lint
  • vp run check
  • vp run test includes the release benchmark run
  • vp run bench

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