moonbitstack/moonhttp/mime does not have a README file

    Refused

    pub(all) suberror Refused {
    Parts(limit~ : Int)
    Part(limit~ : Int, got~ : Int)
    Malformed(at~ : Int)
    NotAForm(content_type~ : String)
    } derive(Eq,
    Debug
    )

    Why a body was not read as a form.

    A form over its bounds is refused whole rather than truncated: a handler given the first thousand parts of a larger form would be answering a request nobody sent.

    Refused::equal

    fn Refused::equal(Refused, Refused) -> Bool

    Refused::not_equal

    fn Refused::not_equal(x : Refused, y : Refused) -> Bool

    Refused::to_repr

    Field

    pub(all) struct Field {
    name : String
    value : String
    } derive(Eq,
    Debug
    )

    A named text value from a form.

    Field::equal

    fn Field::equal(Field, Field) -> Bool

    Field::not_equal

    fn Field::not_equal(x : Field, y : Field) -> Bool

    Field::to_repr

    Form

    type Form

    A parsed form: its text fields and its files.

    Form::entries

    fn Form::entries(self : Form) -> Array[Field]

    Every field, in the order they were sent.

    Form::field

    fn Form::field(self : Form, name : StringView) -> String?

    The first value under this name.

    Form::file

    fn Form::file(self : Form, name : StringView) -> Upload?

    The first file under this name.

    Form::is_empty

    fn Form::is_empty(self : Form) -> Bool

    Whether anything was parsed at all.

    Form::uploads

    fn Form::uploads(self : Form) -> Array[Upload]

    Every file, in the order they were sent.

    Form::values

    fn Form::values(self : Form, name : StringView) -> Array[String]

    Every value under this name — a set of checkboxes sends the same name many times, and taking only the first silently drops the rest.

    Limits

    pub(all) struct Limits {
    parts : Int
    part_size : Int
    } derive(Eq,
    Debug
    )

    What a form is allowed to be.

    A body arrives from whoever sent it, so both bounds are needed: a million empty parts and one enormous part are different ways of asking a server to allocate more than it has.

    Limits::equal

    fn Limits::equal(Limits, Limits) -> Bool

    Limits::new

    fn Limits::new(parts? : Int, part_size? : Int) -> Limits

    Build a bound by naming the parts that differ from the default.

    The same thing can be written { ..@mime.limits, parts: 8 }; this form reads better when several parts differ.

    There is no per-call mirror of these two. A bound belongs to an endpoint rather than to a request — an avatar upload and a spreadsheet import are two endpoints, each with its own — so the record is the only place it comes from, and there is nothing to arbitrate.

    Limits::not_equal

    fn Limits::not_equal(x : Limits, y : Limits) -> Bool

    Limits::to_repr

    Upload

    pub(all) struct Upload {
    name : String
    filename : String
    content_type : String
    content : Bytes
    headers : Array[(String, String)]
    } derive(Eq,
    Debug
    )

    A file from a multipart/form-data body.

    The content is held whole. Streaming a part to disk instead of into memory is what limits exists to make unnecessary until it is built.

    Upload::equal

    fn Upload::equal(Upload, Upload) -> Bool

    Upload::header

    fn Upload::header(self : Upload, name : StringView) -> String?

    One of the part's own headers, by lowercased name.

    Upload::not_equal

    fn Upload::not_equal(x : Upload, y : Upload) -> Bool

    Upload::size

    fn Upload::size(self : Upload) -> Int

    The size of the uploaded content in bytes.

    Upload::to_repr

    boundary

    fn boundary(content_type : StringView) -> String?

    The boundary of a multipart/form-data content type.

    limits

    let limits : Limits

    A thousand parts of a megabyte each — what Starlette's max_files and max_fields default to, with python-multipart's part size. Generous for a form and far from what exhausts a server. A caller that knows its own shape should say so.

    multipart

    fn multipart(body : BytesView, boundary : StringView, limits? : Limits, strict? : Bool) -> Form raise Refused

    Parse a multipart/form-data body (RFC 7578) given its boundary.

    A part goes into the files when its Content-Disposition carries a filename and into the fields when it does not, which is the only thing distinguishing them on the wire.

    param

    fn param(header : StringView, key : StringView) -> String?

    A parameter out of a header value: name from form-data; name="file"; filename="a.txt".

    A quoted value ends at the closing quote; a bare one at the next semicolon.

    parse

    fn parse(body : BytesView, content_type : StringView, limits? : Limits, strict? : Bool) -> Form raise Refused

    Parse a body by what its Content-Type says it is.

    A content type that is neither form encoding yields an empty form rather than an error: "this request carried no form" is an answer, not a failure, and a caller asking for a form on a JSON request wants to hear that. strict is for the caller who would rather be told.

    urlencoded

    fn urlencoded(body : BytesView, limits? : Limits) -> Form raise Refused

    Parse an application/x-www-form-urlencoded body (the WHATWG URL standard's form-urlencoded parsing).

    + is a space here and nowhere else in a URL, which is the one thing about this encoding everybody gets wrong.

    Source Files