diff --git a/fetch.bs b/fetch.bs index 1668dda27..c4a4f1339 100755 --- a/fetch.bs +++ b/fetch.bs @@ -8079,18 +8079,20 @@ steps:
Set action to this step: run the
- multipart/form-data encoding algorithm, with object's
+
Let (boundary, chunks) be the result of running the
+ multipart/form-data chunk serializer with object's
entry list and UTF-8.
+
Set stream to the result of creating a multipart/form-data
+ readable stream from chunks.
+
Set source to object. -
Set length to unclear, see - html/6424 for improving this. +
Set length to the length of a
+ multipart/form-data payload given chunks.
-
Set type to `multipart/form-data; boundary=`, followed by the
- multipart/form-data boundary string generated by the
- multipart/form-data encoding algorithm.
+
Set type to `multipart/form-data; boundary=`, followed by
+ boundary.
multipart/form-data"
Parse bytes, using the value of the `boundary` parameter from
- mimeType, per the rules set forth in
- Returning Values from Forms: multipart/form-data. [[!RFC7578]]
-
-
Each part whose `Content-Disposition` header contains a
- `filename` parameter must be parsed into an entry whose
- value is a {{File}} object whose contents are the contents of the part. The {{File/name}}
- attribute of the {{File}} object must have the value of the `filename` parameter
- of the part. The {{Blob/type}} attribute of the {{File}} object must have the value of the
- `Content-Type` header of the part if the part has such header, and
- `text/plain` (the default defined by [[!RFC7578]] section 4.4) otherwise.
-
-
Each part whose `Content-Disposition` header does not contain a
- `filename` parameter must be parsed into an entry whose
- value is the UTF-8 decoded without BOM content of the
- part. This is done regardless of the presence or the value of a
- `Content-Type` header and regardless of the presence or the value of a
- `charset` parameter.
-
-
A part whose `Content-Disposition` header contains a
- `name` parameter whose value is `_charset_` is parsed like any other
- part. It does not change the encoding.
-
-
If that fails for some reason, then throw a {{TypeError}}. - -
Return a new {{FormData}} object, appending each entry, - resulting from the parsing operation, to its entry list. -
Let entryList be the result of running the
+ multipart/form-data parser given bytes and mimeType.
-
The above is a rough approximation of what is needed for
- `multipart/form-data`, a more detailed parsing specification is to be written.
- Volunteers welcome.
+
If entryList is failure, then throw a {{TypeError}}. + +
Return a new {{FormData}} object whose entry list is + entryList. +
application/x-www-form-urlencoded"
multipart/form-dataA multipart/form-data
+boundary is a byte sequence such that:
+
+
its length is greater than 26 and less than 71, and + +
it is composed by bytes in the ranges 0x30 to 0x39, 0x41 to 0x5A, or 0x61 to 0x7A, + inclusive (ASCII alphanumeric), or which are 0x27 ('), 0x2D (-) or 0x5F (_). +
To generate a
+multipart/form-data boundary, return an
+implementation-defined byte sequence which fulfills the conditions for
+boundaries, such that part of it is randomly generated, with a minimum entropy of 95 bits.
+
Previous definitions of multipart/form-data specified that the
+multipart/form-data boundary associated
+with a multipart/form-data payload not be present anywhere in the payload other than as
+a delimiter, although they allow for generating the
+multipart/form-data boundary
+probabilistically. Since this generation algorithm is separate from a payload, however, it has to
+specify a minimum entropy instead. [[RFC7578]] [[RFC2046]]
+
+
If a user agent generates multipart/form-data boundaries with a length
+of 27 and an entropy of 95 bits, given a payload made specifically to generate collisions with that
+user agent's boundaries, the expected length of the payload before a collision is found is well over
+a yottabyte.
+
+
To escape a multipart/form-data name with a string
+name, an optional encoding encoding (default UTF-8) and an
+optional boolean isFilename (default false):
+
+
If isFilename is true, then set name to the result of + converting name. + +
Otherwise: + +
Assert: name is a scalar value string. + +
Replace every occurrence of U+000D (CR) not followed by U+000A (LF), and every occurrence + of U+000A (LF) not preceded by U+000D (CR), in name, by a string consisting of + U+000D (CR) and U+000A (LF). +
Let encoded be the result of encoding name with + encoding. + +
Replace every 0x0A (LF) byte in encoded with the byte sequence
+ `%0A`, 0x0D (CR) with `%0D` and 0x22 (") with `%22`.
+
+
Return encoded. +
The multipart/form-data chunk serializer takes an
+entry list entries and an optional encoding
+encoding (default UTF-8), and returns a tuple of a
+multipart/form-data boundary and a list
+of chunks, each of which can be either a byte sequence or a {{File}}:
+
+
Set encoding to the result of getting an output encoding from + encoding. + +
Let boundary be the result of
+ generating a
+ multipart/form-data boundary.
+
+
Let outputChunks be an empty list. + +
For each entry of entries: + +
Let chunk be a byte sequence containing `--`,
+ followed by boundary, followed by 0x0D 0x0A (CR LF).
+
+
Append `Content-Disposition: form-data; name="`, followed by the result of
+ escaping a multipart/form-data name given entry's
+ name and encoding, followed by 0x22 ("), to
+ chunk.
+
+
Let value be entry's value. + +
If value is a string: + +
Append 0x0D 0x0A 0x0D 0x0A (CR LF CR LF) to chunk. + +
Replace every occurrence of U+000D (CR) not followed by U+000A (LF), and every + occurrence of U+000A (LF) not preceded by U+000D (CR), in value, by a string + consisting of U+000D (CR) and U+000A (LF). + +
Append the result of encoding value with + encoding to chunk. + +
Append 0x0D 0x0A (CR LF) to chunk. + +
Append chunk to outputChunks. +
Otherwise: + +
Assert: value is a {{File}}. + +
Append `; filename="`, followed by the result of
+ escaping a multipart/form-data name given value's {{File/name}}
+ with encoding and isFilename set to true,
+ followed by 0x22 0x0D 0x0A (" CR LF), to chunk.
+
+
Let type be value's {{Blob/type}}, if it is not the empty string,
+ or "application/octet-stream" otherwise.
+
+
Append `Content-Type: `, followed by the result of
+ isomorphic encoding type, to chunk.
+
+
Append 0x0D 0x0A 0x0D 0x0A (CR LF CR LF) to chunk. + +
Append chunk, followed by value, followed by the + byte sequence 0x0D 0x0A (CR LF), to outputChunks. +
Append the byte sequence containing `--`,
+ followed by boundary, followed by `--`, followed by 0x0D 0x0A (CR LF), to
+ outputChunks.
+
+
Return the tuple (boundary, outputChunks). +
The length of a multipart/form-data
+payload, given a list of chunks chunks which can be either byte sequences or
+{{File}} objects, is the result of running the following steps:
+
+
Let length be 0. + +
For each chunk of chunks: + +
If chunk is a byte sequence: + +
Increase length by chunk's length. +
Otherwise: + +
Assert: chunk is a {{File}}. + +
Increase length by chunk's {{Blob/size}}. +
Return length. +
To create a multipart/form-data readable stream from a list of
+chunks chunks which can be either byte sequences or {{File}} objects:
+
+
Let fileStream be null. + +
Let stream be a new {{ReadableStream}}. + +
Let pullAlgorithm be an algorithm that runs the following steps: + +
If chunks[0] is a byte sequence, then + enqueue the result of + [=ArrayBufferView/create|creating=] a {{Uint8Array}} from chunks[0] into + stream. + +
Otherwise: + +
Assert: chunks[0] is a {{File}} object. + +
Set fileStream to the result of running chunks[0]'s + {{Blob/stream}} method. + +
Run pullAlgorithm. +
Remove the first item from chunks. +
Close stream. +
Let readRequest be a new read request with the following + items: + +
Set fileStream to null. + +
Run pullAlgorithm. +
Error stream with e. +
Let reader be the result of getting a reader for + fileStream. + +
Read a chunk from reader with + readRequest. +
Let cancelAlgorithm be an algorithm that runs the following steps, given + reason: + +
If fileStream is not null, then cancel + fileStream with reason. +
Set up stream with + pullAlgorithm set to pullAlgorithm and + cancelAlgorithm set to cancelAlgorithm. + +
Return stream. +
The multipart/form-data parser takes a byte sequence
+input and a MIME type mimeType, and returns either an
+entry list or failure:
+
+
If mimeType's parameters["boundary"] does not
+ exist, then return failure.
+
+
Let boundary be the result of UTF-8 encoding mimeType's
+ parameters["boundary"].
+
+
The definition of MIME type in Mime Sniffing has the + parameter values being ASCII strings, but the + parse a MIME type algorithm can create + MIME type records containing non-ASCII parameter values. See + whatwg/mimesniff#141. + +
Let entryList be an empty entry list. + +
Let position be a pointer to a byte in input, initially pointing at + the first byte. + +
While true: + +
If position does not point to a sequence of bytes starting with 0x2D 0x2D
+ (`--`) followed by boundary, then return failure.
+
+
Advance position by 2 + the length of boundary. + +
Collect a sequence of bytes that are HTTP tab or space bytes given + position. (Do nothing with those bytes.) + +
If position points to a sequence of bytes starting with 0x2D 0x2D
+ (`--`):
+
+
If position + 2 points to the end of input, or + position + 2 points to a sequence of bytes starting with 0x0D 0x0A (CR LF), then + return entryList. +
If position does not point to a sequence of bytes starting with 0x0D 0x0A + (CR LF), then return failure. + +
Advance position by 2. (This skips past the newline.) + +
Let result be the result of parsing multipart/form-data
+ headers on input and position.
+
+
If result is failure, then return failure. + +
Let (name, filename, contentType) be + result. + +
Advance position by 2. (This skips past the empty line that marks the end of + the headers.) + +
Let body be the empty byte sequence. + +
Body loop: While position is not past the end of input: + +
Append the byte at position to body. + +
Advance position by 1. + +
If body ends with boundary: + +
Remove the last 4 + (length of boundary) bytes from body. + +
Decrease position by 4 + (length of boundary). + +
Break out of body loop. +
If position does not point to a sequence of bytes starting with 0x0D 0x0A + (CR LF), then return failure. + +
Advance position by 2. + +
If filename is not null: + +
If contentType is null, then set contentType to
+ "text/plain".
+
+
If contentType is not an ASCII string, then set contentType to + the empty string. + +
Let value be a new {{File}} object with name filename, type + contentType, and body body. +
Otherwise: + +
Let value be the UTF-8 decode without BOM of body. +
Assert: name is a scalar value string and + value is either a scalar value string or a {{File}} object. + +
Create an entry with name and value, and + append it to entryList. +
To parse multipart/form-data headers, given a
+byte sequence input and a pointer into it position:
+
+
Let name, filename and contentType be null. + +
While true: + +
If position points to a sequence of bytes starting with 0x0D 0x0A (CR LF): + +
If name is null, then return failure. + +
Return (name, filename, contentType). +
Let headerName be the result of collecting a sequence of bytes that are not + 0x0A (LF), 0x0D (CR) or 0x3A (:), given position. + +
Remove any HTTP tab or space bytes from the start or end of + headerName. + +
If headerName does not match the field-name token + production, then return failure. + +
If the byte at position is not 0x3A (:), then return failure. + +
Advance position by 1. + +
Collect a sequence of bytes that are HTTP tab or space bytes given + position. (Do nothing with those bytes.) + +
Byte-lowercase headerName and switch on the result: + +
content-disposition`
+ Set name and filename to null. + +
If position does not point to a sequence of bytes starting with
+ `form-data; name="`, then return failure.
+
+
Advance position so it points at the byte after the next 0x22 (") byte (the + one in the sequence of bytes matched above). + +
Set name to the result of parsing a multipart/form-data
+ name given input and position.
+
+
If name is failure, then return failure. + +
If position points to a sequence of bytes starting with
+ `; filename="`:
+
+
Advance position so it points at the byte after the next 0x22 (") byte + (the one in the sequence of bytes matched above). + +
Set filename to the result of
+ parsing a multipart/form-data name given input and
+ position.
+
+
If filename is failure, then return failure. +
content-type`
+ Let headerValue be the result of collecting a sequence of bytes that are + not 0x0A (LF) or 0x0D (CR), given position. + +
Remove any HTTP tab or space bytes from the end of headerValue. + +
Set contentType to the isomorphic decoding of + headerValue. +
Collect a sequence of bytes that are not 0x0A (LF) or 0x0D (CR), given + position. (Do nothing with those bytes.) +
If position does not point to a sequence of bytes starting with 0x0D 0x0A + (CR LF), then return failure. + +
Advance position by 2. (This skips past the newline.) +
To parse a multipart/form-data name, given a
+byte sequence input and a pointer into it position:
+
+
Assert: The byte at (position − 1) is 0x22 ("). + +
Let name be the result of collecting a sequence of bytes that are not 0x0A (LF), + 0x0D (CR) or 0x22 ("), given position. + +
If the byte at position is not 0x22 ("), then return failure. + +
Advance position by 1. + +
Replace any occurrence of the following subsequences in name with the given byte: + +
%0A`
+ 0x0A (LF) + +
%0D`
+ 0x0D (CR) + +
%22`
+ 0x22 (") +
Return the UTF-8 decode without BOM of name. +