diff --git a/fetch.bs b/fetch.bs index fa6deae20..c5eb40d98 100755 --- a/fetch.bs +++ b/fetch.bs @@ -1651,6 +1651,162 @@ these steps: +
A trailer state is a struct used to represent +HTTP trailer fields +([[HTTP]], Section 6.5) +associated with a request or response. +It has: + +
A header list (a header list), + initially « ». + +
A state
+ ("pending", "complete", or "errored"),
+ initially "pending".
+
+
An error (null or a value), initially null. + +
An observers (a list of + algorithms), initially « ». +
A trailer state is shared by reference between a +request or response and its +clones. + +
To complete a trailer state given a trailer state +trailerState: + +
+To error a trailer state given a trailer state +trailerState and a value reason: + +
+To process a trailers init given a trailer state +trailerState, a {{Promise}} trailersPromise, a +headers guard guard, and a header list +headerList: + +
Upon fulfillment of trailersPromise with + init: + +
Let trailerHeaders be a new {{Headers}} object whose + guard is guard. + +
Fill trailerHeaders with init. + +
Let declaredNames be the result of
+ getting, decoding, and splitting
+ `Trailer` from headerList.
+
+
If declaredNames is null, then set declaredNames + to « ». + +
Let lowercasedDeclaredNames be a new list. + +
For each name of declaredNames, + append name, byte-lowercased, to + lowercasedDeclaredNames. + +
Let namesToRemove be a new list. + +
For each header of trailerHeaders's + header list: + +
+ +For each name of namesToRemove, + delete name from + trailerHeaders's header list. + +
Set trailerState's header list + to trailerHeaders's header list. + +
Complete a trailer state given trailerState. +
Upon rejection of trailersPromise with + reason: + +
Error a trailer state given trailerState and + reason. +
The guard applied to trailerHeaders
+ensures that forbidden request-header names (for requests) or
+forbidden response-header names (for responses) are excluded via the
+standard {{Headers}} filtering before the `Trailer` declaration
+filter is applied. Trailer field names present in the resolved {{HeadersInit}}
+but not pre-declared in the `Trailer` header are silently dropped.
+
To ignore a trailers init given a {{Promise}} +trailersPromise: + +
Mark as handled trailersPromise. +
This algorithm is provided for use by specifications that extend +Fetch and need to discard a user-provided {{RequestInit/trailers}} or +{{ResponseInit/trailers}} promise without triggering unhandled rejection +warnings. Within this specification, process a trailers init handles +promise rejection directly. +
This section documents how requests work in detail. To get started, see @@ -2305,6 +2461,17 @@ Unless stated otherwise, it is unset.
A request has an associated done flag. Unless stated otherwise, it is unset. +
A request has an associated
+trailer state
+(a trailer state). Unless stated otherwise it is a new trailer state
+whose state is "complete".
+
+
A request's trailer state defaults to
+"complete" with an empty header list,
+representing a request with no trailer fields to send. When the
+{{RequestInit/trailers}} option is provided, the trailer state
+starts as "pending" and is completed when the promise resolves.
+
A request has an associated timing allow failed flag. Unless stated otherwise, it is unset. @@ -2579,6 +2746,18 @@ message as HTTP/2 does not support them.
The source and length concepts of a network's response's body are always null. +
A response has an associated +trailer state +(a trailer state). Unless stated otherwise it is a new trailer state. + +
A response's +trailer header list +is its trailer state's header list. + +
The trailer state is shared by reference between a +response and its clones (see clone), ensuring trailer fields +received after cloning are visible to all copies. +
A response has an associated
cache state (the empty string,
"local", or "validated"). Unless stated otherwise, it is the empty
@@ -2692,17 +2871,23 @@ of defining the concrete types of filtered responses.)
A basic filtered response is a
filtered response whose
-type is "basic" and
+type is "basic",
header list excludes any
headers in
internal response's
header list whose
name is a
+forbidden response-header name, and
+trailer header list excludes any
+headers in
+internal response's
+trailer header list whose
+name is a
forbidden response-header name.
A CORS filtered response is a
filtered response whose
-type is "cors" and
+type is "cors",
header list excludes any
headers in
internal response's
@@ -2710,6 +2895,14 @@ of defining the concrete types of filtered responses.)
name is not a
CORS-safelisted response-header name, given
internal response's
+CORS-exposed header-name list, and
+trailer header list excludes any
+headers in
+internal response's
+trailer header list whose
+name is not a
+CORS-safelisted response-header name, given
+internal response's
CORS-exposed header-name list.
An opaque filtered response is a @@ -2719,6 +2912,7 @@ of defining the concrete types of filtered responses.) status is 0, status message is the empty byte sequence, header list is « », +trailer header list is « », body is null, and body info is a new response body info. @@ -2729,6 +2923,7 @@ is a filtered response whose status is 0, status message is the empty byte sequence, header list is « », +trailer header list is « », body is null, and body info is a new response body info. @@ -2781,7 +2976,10 @@ console.log((await fetch("/surprise-me", { redirect: "manual" })).type); // "opa internal response.
Let newResponse be a copy of response, except for its - body. + body and trailer state. + +
Set newResponse's trailer state to + response's trailer state.
If response's body is non-null, then set newResponse's body to the result of cloning @@ -5310,6 +5508,9 @@ steps:
Complete a trailer state given response's + trailer state. +
If fetchParams's process response end-of-body is non-null, then run fetchParams's process response end-of-body given response. @@ -5360,13 +5561,34 @@ steps: flushAlgorithm set to processResponseEndOfBody. -
Set internalResponse's body's stream to the - result of internalResponse's body's stream - piped through transformStream. +
Let readable be transformStream's + readable. + +
Let pipePromise be the result of + piping internalResponse's + body's stream to + transformStream's writable. + +
Upon rejection of pipePromise with + reason: + +
Error a trailer state given response's + trailer state and reason. +
This {{TransformStream}} is needed for the purpose of receiving a notification when - the stream reaches its end, and is otherwise an identity transform stream. +
The {{TransformStream}} receives a notification when the body + stream reaches its end (via the flush algorithm) or when it errors (via the + pipe promise rejection). On successful completion, + processResponseEndOfBody completes + the trailer state. On error, the trailer state is + errored, which rejects any pending + {{Response/trailers}} promises.
If fetchParams's process response consume body is @@ -6760,6 +6982,44 @@ optional boolean forceNewConnection (default false), run these steps:
Break. + +
If request's trailer state's
+ state is "pending", the
+ implementation MAY, after the request body has been fully transmitted,
+ observe request's trailer state and send
+ trailer fields from its header list once it
+ is completed. Implementations that
+ do not support sending trailer fields MAY ignore the trailer state
+ entirely.
+
+
Sending trailer fields is entirely optional. + Implementations that do not support sending trailer fields are fully + compliant with this specification. The {{RequestInit/trailers}} option + exists to provide a standardized interface for implementations that do + support sending trailer fields. Promise rejection is handled by the + process a trailers init algorithm (which is called during + request construction), so implementations that ignore the trailer state + do not cause unhandled promise rejections. + Whether or not to actually forward trailer fields is an implementation + decision, consistent with how HTTP intermediaries may forward, discard, + or store trailer fields + ([[HTTP]], Section 6.5). + +
If the HTTP response includes trailer fields + ([[HTTP]], Section 6.5), + then, after the response body has been fully transmitted, set + response's trailer header list to a + header list containing each received trailer field as a + header. + +
Trailer fields are received after the response body. The HTTP + layer is responsible for omitting trailer fields prohibited by HTTP semantics + (e.g., fields used in transfer codings, content framing, or routing). The + trailer header list is populated before + completing the trailer state + in the fetch response handover steps.
The exact layering between Fetch and HTTP still needs to be sorted through and @@ -8616,6 +8876,7 @@ interface Request { readonly attribute boolean isHistoryNavigation; readonly attribute AbortSignal signal; readonly attribute RequestDuplex duplex; + readonly attribute Promise<Headers> trailers; [NewObject] Request clone(); }; @@ -8636,6 +8897,7 @@ dictionary RequestInit { AbortSignal? signal; RequestDuplex duplex; RequestPriority priority; + Promise<HeadersInit> trailers; any window; // can only be set to null }; @@ -8663,6 +8925,9 @@ used or observed from JavaScript.
A {{Request}} object has an associated signal (null or an {{AbortSignal}} object), initially null. +
A {{Request}} object has an associated +trailers promise (null or a {{Promise}}), initially null. +
A {{Request}} object's body is its request's body. @@ -8742,6 +9007,12 @@ object), initially null.
Trailer` header;
+ undeclared trailer fields are silently dropped. Implementations are not required to send
+ trailer fields and may ignore this option entirely.
request . method
@@ -8815,6 +9086,23 @@ object), initially null.
See issue #1254 for
defining "full".
+ request . trailers
+ Returns a promise that resolves to immutable {{Headers}} containing the request's + trailer fields. + +
If the {{RequestInit/trailers}} option was provided, the promise resolves when
+ the provided promise fulfills, with trailer fields filtered by the `Trailer`
+ header declaration. If no {{RequestInit/trailers}} option was provided, the
+ promise resolves immediately to empty immutable {{Headers}}.
+
+
The resolution timing of this promise reflects the timing of the + user-provided {{RequestInit/trailers}} promise, not the timing of body + transmission. This may indirectly reveal when payload processing completes if + the caller ties the promise to body consumption (e.g., computing a digest over + the body stream), but this timing is controlled by the caller, not the + implementation. +
request . clone()
Returns a clone of request. @@ -9218,6 +9506,21 @@ constructor steps are:
If init["{{RequestInit/trailers}}"] exists, then: + +
Let trailerState be a new trailer state. + +
Set this's request's trailer state + to trailerState. + +
Process a trailers init given trailerState, + init["{{RequestInit/trailers}}"], + this's headers's guard, and + this's request's header list. +
The duplex getter steps are to return
"half".
+
The trailers getter steps are:
+
+
If this's trailers promise is null, then: + +
Let trailerState be this's request's + trailer state. + +
Let promise be a new promise. + +
If trailerState's state is
+ "complete", then:
+
+
Let headers be a new {{Headers}} object whose
+ header list is trailerState's
+ header list and whose guard is
+ "immutable".
+
+
Resolve promise with headers. +
Otherwise, if trailerState's state is
+ "errored", then reject promise with
+ trailerState's error.
+
+
Otherwise (trailerState's state is
+ "pending"):
+
+
Append the following steps to trailerState's + observers: + +
If trailerState's state is
+ "complete", then:
+
+
Let headers be a new {{Headers}} object whose
+ header list is trailerState's
+ header list and whose guard is
+ "immutable".
+
+
Resolve promise with headers. +
Set this's trailers promise to promise. +
Return this's trailers promise. +
When no {{RequestInit/trailers}} option is provided, the
+trailer state is immediately "complete" with an
+empty header list, so the {{Request/trailers}} promise
+resolves to empty immutable {{Headers}}. When a {{RequestInit/trailers}} option is
+provided, the promise resolves to the filtered trailer fields once the provided
+promise fulfills.
+
A {{Response}} object also has an associated headers (null or a {{Headers}} object), initially null. +
A {{Response}} object has an associated +trailers promise (null or a {{Promise}}), initially null. +
A {{Response}} object's body is its response's body. @@ -9394,8 +9773,26 @@ enum ResponseType { "basic", "cors", "default", "error", "opaque", "opaqueredire
response . headers
Returns response's headers as {{Headers}}. +
response . trailers
+ Returns a promise that resolves to {{Headers}} containing the response's trailer fields. + +
For responses obtained from {{WindowOrWorkerGlobalScope/fetch()}}, the promise resolves after the + response body has been fully received. For synthetically constructed responses (via + {{Response/error()}}, {{Response/redirect()}}, or + {{Response/json()}} and the {{Response/Response()}} constructor without a + {{ResponseInit/trailers}} option), the promise resolves immediately to empty immutable + {{Headers}}. + +
If the {{ResponseInit/trailers}} option was provided (to the {{Response/Response()}}
+ constructor or {{Response/json()}}), the promise resolves when the provided promise fulfills,
+ with trailer fields filtered by the `Trailer` header declaration.
+
+
If the response body stream errors, the promise is rejected. +
response . clone()
- Returns a clone of response. +
Returns a clone of response. The clone shares the same + trailer state, so both will receive the same trailer fields.
Perform initialize a response given this, init, and bodyWithType. + +
If init["{{ResponseInit/trailers}}"] exists, then + process a trailers init given this's response's + trailer state, + init["{{ResponseInit/trailers}}"], + this's headers's guard, and + this's response's header list. + +
Otherwise, complete a trailer state given this's + response's trailer state.
The static error() method steps are to return the
-result of creating a {{Response}} object, given a new network error,
-"immutable", and the current realm.
+
The static error() method steps are:
+
+
Let responseObject be the result of creating a {{Response}}
+ object, given a new network error, "immutable", and the current realm.
+
+
Complete a trailer state given responseObject's + response's trailer state. + +
Return responseObject. +
The static @@ -9518,6 +9937,9 @@ are:
Append (`Location`, value) to
responseObject's response's header list.
+
Complete a trailer state given responseObject's + response's trailer state. +
Return responseObject.
Perform initialize a response given responseObject, init, and
(body, "application/json").
+
If init["{{ResponseInit/trailers}}"] exists, then + process a trailers init given responseObject's + response's trailer state, + init["{{ResponseInit/trailers}}"], + responseObject's headers's guard, and + responseObject's response's header list. + +
Otherwise, complete a trailer state given responseObject's + response's trailer state. +
Return responseObject. @@ -9574,6 +10008,72 @@ otherwise false.
The headers getter steps are to return
this's headers.
+
The trailers getter steps are:
+
+
If this's trailers promise is null, then: + +
Let trailerState be response's + trailer state. + +
Let promise be a new promise. + +
If trailerState's state is
+ "complete", then:
+
+
Let headers be a new {{Headers}} object whose
+ header list is response's
+ trailer header list and whose guard is
+ "immutable".
+
+
Resolve promise with headers. +
Otherwise, if trailerState's state is
+ "errored", then reject promise with
+ trailerState's error.
+
+
Otherwise (trailerState's state is
+ "pending"):
+
+
Append the following steps to trailerState's + observers: + +
If trailerState's state is
+ "complete", then:
+
+
Let headers be a new {{Headers}} object whose
+ header list is response's
+ trailer header list and whose guard is
+ "immutable".
+
+
Resolve promise with headers. +
Set this's trailers promise to promise. +
Return this's trailers promise. +
If response's body is non-null and is readable, then error response's body with error. + +
Error a trailer state given response's + trailer state and error.