multipartkit/form
Types
Opaque builder for multipart/form-data messages.
Form is constructed via new and accumulated with add_field /
add_file / add_file_auto / unsafe_add_part. Read it back as
List(Part) via parts. The boundary is generated lazily by
encode_form and is not part of Form’s observable state.
pub opaque type Form
Reasons the strict form-builder variants reject input.
The non-strict add_field / add_file / add_file_auto /
add_file_auto_with silently strip CR / LF / NUL bytes from
the values that flow into header lines (sealing the #28 header
injection vector). The silent strip is data loss the caller
cannot observe — add_field("name\n", _) produces
name="", and add_file(_, "fi\nle.png", _, _) concatenates
the two halves into a different valid filename. The *_strict
variants surface this as a typed error so callers can render
“field name foo\\nbar contains forbidden control bytes”
rather than silently producing the wrong wire. (#40, #41)
pub type FormError {
NameContainsControlBytes(value: String)
FilenameContainsControlBytes(value: String)
ContentTypeContainsControlBytes(value: String)
}
Constructors
-
NameContainsControlBytes(value: String)add_field_strictsaw CR / LF / NUL bytes in the field name. Carries the original (un-sanitized) value. -
FilenameContainsControlBytes(value: String)add_file_strictsaw CR / LF / NUL bytes in the file’s filename. Carries the original (un-sanitized) value. -
ContentTypeContainsControlBytes(value: String)add_file_strictsaw CR / LF / NUL bytes in the file’s content type. Carries the original (un-sanitized) value.
Values
pub fn add_field(
form: Form,
name name: String,
value value: String,
) -> Form
Append a text field. value is encoded as UTF-8 in the part body. No
filename is set.
Carriage returns, line feeds, and NUL bytes in name are silently
stripped to prevent header injection. The cached name on the resulting
Part reflects the sanitized value, matching what a parse-after-encode
round-trip would produce. The strip is data loss the caller cannot
observe — add_field("name\n", _) produces a part with name="" —
so callers passing user-typed or upstream data into name should
prefer add_field_strict, which surfaces the bad input as
Error(NameContainsControlBytes(value:)) instead. Use
unsafe_add_part if byte-exact preservation of arbitrary header
values is required.
pub fn add_field_strict(
form: Form,
name name: String,
value value: String,
) -> Result(Form, FormError)
Strict counterpart of add_field: rejects names containing CR /
LF / NUL bytes with Error(NameContainsControlBytes(value:)).
The non-strict add_field silently strips these bytes (so
add_field("name\n", _) produces a part with name=""). For
callers that pass user-typed or upstream data into name and
want to surface bad inputs as a typed error rather than silent
data loss, use this variant. The value payload carries the
caller’s original input so the error renders as
“field name foo\\nbar contains forbidden control bytes”. (#40)
pub fn add_file(
form: Form,
name name: String,
filename filename: String,
content_type content_type: String,
body body: BitArray,
) -> Form
Append a file part with an explicit content type.
Carriage returns, line feeds, and NUL bytes in name, filename, and
content_type are silently stripped to prevent header injection. The
cached name, filename, and content_type on the resulting Part
reflect the sanitized values. The strip on filename is especially
dangerous — add_file(_, "fi\nle.png", _, _) concatenates the two
halves into the different valid filename "file.png", which can
change authorisation-relevant identifiers. Callers passing user-typed
or upstream data should prefer add_file_strict, which surfaces the
bad input as Error(NameContainsControlBytes(value:)) /
Error(FilenameContainsControlBytes(value:)) /
Error(ContentTypeContainsControlBytes(value:)). Use
unsafe_add_part if byte-exact preservation of arbitrary header
values is required.
pub fn add_file_auto(
form: Form,
name: String,
filename: String,
body: BitArray,
) -> Form
Append a file part using the default (no-op) inferer.
Equivalent to add_file_auto_with(form, name, filename, body, infer.default_inferer()). The default inferer returns None from both
helpers in v0.1.0, so this falls through to application/octet-stream
unless you call add_file_auto_with with a real inferer.
pub fn add_file_auto_with(
form: Form,
name: String,
filename: String,
body: BitArray,
inferer: infer.Inferer,
) -> Form
Append a file part, inferring the content type via the supplied
Inferer.
Inference precedence:
inferer.from_filename(filename)inferer.from_bytes(body)application/octet-stream
The inferred content type is sanitized (CR / LF / NUL stripped) before being written to the header.
pub fn add_file_strict(
form: Form,
name name: String,
filename filename: String,
content_type content_type: String,
body body: BitArray,
) -> Result(Form, FormError)
Strict counterpart of add_file: rejects names, filenames, and
content types containing CR / LF / NUL bytes with the matching
FormError variant.
The non-strict add_file silently strips these bytes. For
name the strip leaves an empty string (loud round-trip
failure); for filename it concatenates the two halves into a
different valid filename, which can change
authorisation-relevant identifiers (the attacker shape
described in #41). The strict variant catches both at the
builder boundary so the wrong wire never gets produced. (#41)
pub fn unsafe_add_part(form: Form, the_part: part.Part) -> Form
Append a pre-built Part without validation or normalisation.
The caller is responsible for keeping headers, name, filename, and
content_type mutually consistent. Prefer add_field / add_file /
add_file_auto for library-maintained consistency.