In my opinion, there are some less than ideal properties to the Fetch API -- in particular the "cloning" of Request
objects. This plays a role when you want to duplicate requests for re-sending. Most people may not notice the problem because unless a Request
is initialized with [non-null] body
, through the Request
constructor or the fetch
procedure, it's a non issue.
For requests with a body, however, the problem boils down to the fact that you can only clone a Request
, using clone
, if the former uses a body
, a stream, which has not been "disturbed". In other words the stream must be "unused" or "pristine" (never read from), for clone
to succeed, otherwise it will abort with an exception. Fair enough -- you can just clone
early, right? Well, there's more to it: Request
objects may be constructed with an init
argument, and the latter may also be used with fetch
. The argument also allows to specify a body
, thus effectively overriding whatever body
was set on the first argument to the Request
constructor or fetch
, input
as it is called. And for init
specifically, you can't just "clone" it -- clone
is a method of Request
and it accepts no parameters -- it simply clones the Request
(and to reiterate again, with the caveat that the body
stream, if set, is "undisturbed").
So how do you duplicate requests that are effectively specified with input
and init
, where init
may define a stream for body
. That's the crux of it -- that more code is needed, beyond what clone
gives.
In context of the gist, it's really the problem of not being able to rely on just doing new Request(input, init).headers
for accessing headers that are implied in a pair of input
and init
-- if there is some body
on either one of the arguments, which may be a stream (body
of a Request
is always a stream, even if init
body allows for other kinds of values -- the former is, well, initialized from the value), then you get an unusable stream -- you've essentially "used up" input
or init
(specifically the stream on whichever one of these it was available on, with init
taking priority).
This is why e.g. headers
function in the gist was written -- sometimes you just want to know what headers apply for a request expressed with a pair of input
and init
, since the former expression is problematic as described above.
There is a generalization of headers
(the function in the gist) -- it's the request_without_body
function that "idempotently" constructs an actual Request
that is equivalent (in terms of e.g. "sending" with fetch
with the notable exception that there's no body to it) to the pair of input
and init
[it is constructed from], including for accessing the headers of the request. To the best of my current understanding, that is -- there may be other mutable types besides the ReadableStream
for body
, but I don't think there are. When I have the time, I should look closer into the specification of Request
constructor, so that I may be certain the request_without_body
implementation in the gist is sound.