-
Notifications
You must be signed in to change notification settings - Fork 311
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Does FetchBodyStream need to be split #413
Comments
What are the use-cases? Is this to explain something like EventSource? |
The problem is that the IO streams specification only acknowledges input and output streams. Not a concept that is both simultaneously, which is more or less what we need. So we need to make this an object that exposes both or some such. |
So the issue is that there is no such thing as a stream that is both readable and writable. That concept is represented by a pair Last time we discussed this my conclusion was that doing things "the Node way," where you have a ClientRequest that is writable (for performing uploads) and a ServerRequest that is readable (for receiving uploads) was too painful for the common use cases we were seeing in service worker and related code, e.g. wanting to get an incoming "server" request and then pass it to fetch as a "client" request. The code would end up looking something like this, which is similar to the kind of code you write in Node, and just as painful. That leaves us wanting to combine both concepts into a single Request class, which contains a pass-through stream (i.e. a readable + writable pair where the write side feeds directly into the read side; also known as, an identity transform stream). For client requests, the developer will write to the writable stream and the browser will read from the readable side. For server requests, the developer will read from the readable side as the browser writes into the writable side. API-wise, the impact is that, when we expose the underlying stream machinery, you'd probably do things like // I'm on the server!
self.onfetch = ({ request: req }) => {
req.body.input.pipeTo(aWritableDestination);
req.body.input.read(); // reads a chunk if available
req.body.input.readInto(arrayBuffer, 0, 1024); // reads up to 1024 bytes into arrayBuffer starting at position 0
}; // I'm on the client!
var req = new Request("http://example.com", {
method: "POST",
headers: { "Content-Length": 1024 }
});
fetch(req).then(...);
req.body.output.write(new ArrayBuffer(512));
aReadableSource.pipeTo(req.body.output); (Here I am using the version from the above-linked bug where Considerations impacting MVP:
|
Isn't it just: // I'm on the client!
var requestBody = new ReadableStream(function() {
start: function(enqueue, close, error) {
enqueue(new ArrayBuffer(512));
}
});
var req = new Request("http://example.com", {
method: "POST",
headers: { "Content-Length": 1024 },
body: requestBody
});
// or
var req = new Request("http://example.com", {
method: "POST",
headers: { "Content-Length": 1024 },
body: aReadableSource
}); |
That adds layers of indirection between the actual writable socket representing the outgoing HTTP request, and the user. The goal of the low-level stream API is to expose that socket, along with its state, backpressure signals, kernel-level buffering, etc. as something you can write to. If you have a source of data you want to write to that socket, the way to express that is by piping the readable stream representing that source to the writable stream representing that sink: i.e., there is already an idiomatic way to express such an operation, that makes it clear what is going on, instead of making it implicit that when you pass a readable stream to the Request constructor, it will consume it implicitly. |
After some chatting on IRC, I think we're leaning towards a function that would reveal a writable stream for request/response. new Request(url, {
method: 'POST',
streamer: function(writableStream) {
// ...
}
}); If you had a readable stream & wanted to use that as a request/response body, you can still do: new Request(url, {
method: 'POST',
body: readableStream
}); The issues are:
We could continue to overload @domenic is that a fair roundup? |
I like this idea. For example, thinking of whatwg/streams#97, I think we basically want the
{reveal|expose|create}Body{Stream|StreamWriter|WritableStream}?
Maybe initialize the stream with the data provided by the
Hmm, are we sure that there won't be any more type in the future that also needs the revealing pattern? |
@jakearchibald ya that's a fair roundup indeed.
I'm leaning toward something like {start|init|with}BodyStream?
TypeError for now seems best; we could relax it later if there's a compelling use-case, but I kind of doubt there would be.
I was only tempted to do this when I couldn't think of a name, but @tyoshino got my thoughts flowing again in that regard and so I don't think it's worth it. |
Dumb lurker questions:
|
So you can pipe data to a document from a service worker. |
I read and re-read this a few times, and I think I understand the implication here. Now that I think about it, it kinda makes sense. I believe the implication is you have a Request/Response pair from the Document, and the SW has a separate Request/Response pair from a fetch() (or a Cache or what not), and you want to pipe from the SW fetch()'s Response to the Document's Response. Did I get that right? Is that the implication? If so, that's fine, although I have to say, it's initially confusing that a Response would be writable. But if you reuse the same interface/object, for consuming as well as producing Responses, then I guess that makes sense. I kinda like how the Streams approach explicitly separated the producer/consumer into different interfaces (writable vs readable streams), but I don't know anything about web interfaces so I'll shut up now. Thanks for the explanation. |
The document creates a |
@willchan in terms of the API, you'll only get the writable stream via function passed to the constructor |
@jakearchibald Ah, perfect! It all makes sense now :) |
As {init|with}Body{Writable|Writing}Stream Too long? |
I think the input should still be |
You could create a null transform stream & use that as |
@tyoshino I see what you mean. But perhaps FetchBodyStream just needs renaming to FetchBody or similar. That said, @annevk but you (the browser, I mean) can't read from a writable stream. The creator of the writable stream needs to hook things up to the underlying sink for you ahead of time. @jakearchibald's identity transform stream idea is closer: you create a paired set of readable/writable streams, and pass the readable one as It still seems fine to do |
@domenic OK. If such renaming happens, yes |
I think #372 (comment) gets us out of "impacts MVP" realm. |
Yeah, at this point streams can be sorted out behind the scenes and plugged into |
There's a separate discussion going on already on streams integration in #452. Closing this. |
See discussion in f4609c9 and w3c/ServiceWorker#413.
Per @domenic we might need both a read and write a stream rather than just one. That would change how Request/Response work pretty drastically.
The text was updated successfully, but these errors were encountered: