-
Notifications
You must be signed in to change notification settings - Fork 19
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
Response body that could possibly fail #84
Comments
I am now more and more inclined to put For For Does anyone have an objection? @back2dos @benmerckx @gene-pavlovsky |
Hmm, objection is a bit of a strong word. However it seems to me that regardless of ideal/real stream the container MUST close the connection if content-length was sent and body was too short (could always happen by some mistake). With that behavior in place, we could we could stick with body being |
Sorry for my bad English. (The equivalent word in my primary language isn't so strong, seems like I have literally translated from there) Wouldn't it be easier if the Container gets a RealStream and terminate the connection upon an Error? |
Another reason for the proposed change is that the fetch(request.url, {
method: request.method,
body: source.idealize(e -> Source.EMPTY),
}).all(); The error basically get swallowed in the process, assuming that the remote blindly accepts the early-terminated body. In order to properly capture the error, user would have to write something like this: Future.async(function(cb) {
fetch(request.url, {
method: request.method,
body: source.idealize(e -> {
cb(Failure(e));
Source.EMPTY;
}),
}).all().handle(cb);
}); In short, it is just a bit too cumbersome, which kind of contradicts the philosophy of "don't encourage user to skip error handling" which run though the tink libs. And IdealStream is compatible to RealStream, so one can still opt to handle the error explicitly (thus produce an Ideal stream) even if http accepts the latter |
Nah, your English is just fine! What I meant is that that I have a remark rather than an actual objection ;) So ok, assuming the container gets an error from the outgoing body. What should it do? |
Close the connection. I think there is nothing else the container could do. However, if user has concern on that behaviour, he can still use On the other hand, I doubt if one can really idealize a stream. Take FS read as an example, if for any reason the read halted somewhere in the middle, we might try to open the file again re-read from the last position. But it is still just a RealStream, not ideal. This kind of leads me to a thought that |
Hmm ... well ok, for one idealize should be fixed in many ways:
And all of it needs to be fixed in tink_streams to make matters more complicated. And if properly done, it could be nice. You could recover from an SQL query where the connection goes down while you're in the middle of the result set and then you can just run the same query adjusted so that you get only the rest of the result set (starting from the last received ID or something - which is not necessarily completely accurate though but probably still better than just stopping in the middle I guess?). You could resume an HTTP download if the connection closes. Etc. There are use cases. But it's a headache inducing thing to get right and we should be pragmatic about it. Better to get stable in the foreseeable future and make a major version if anyone can summon the energy to dig through that. As for the matter at hand. Yes, My thinking is that in any case the container, if it has a persistent connection, should close it if |
Now I am starting the doubt the usefulness of converting a RealStream/Source to the Ideal counterpart. Because the outcome is never really ideal. It either involves lost of information (the error) or incorrect information (you think it is gracefully ended but actually not) or both. I am not sure if it is meaningful for anyone to consume such "VirtuallyIdealStream". I am not sure if this is correct, but I am thinking that As for the container, I agree that the container should check the actual body length against the content-length header. But In the case of chunked-encoding, I think having a RealSource might be better. Because the container knows it is an error, so that it can abruptly close the connection (again I think it is the only thing it can do after sending the header) without sending the proper termination bytes. So that the other party at least know something has go wrong. In the case of clients, I will take JsClient as an example. Since XMLHTTPRequest doesn't support streaming, currently we collect all the bytes from the stream and then send them out. If it is IdealSource the client will just collect the incorrect bytes (in case of error) and send them out. If it is RealSource the client would see the error and just not send the bytes altogether. Anyway, I will try to change body to RealStream in a fork and experiment with it. |
Well, there are cases of course. Take an SQL result stream, that you map it through The best solution for HTTP would be to use real streams for the body, always output them with chunked encoding and in case of an error send HTTP trailers to signal the error, but unfortunately support for those is pretty miserable. This would mean that the connection doesn't have to close and the error propagates to the client. |
This is quite a dark forest for me guys. I haven't looked at tink_streams and tink_http really. Well I did read the READMEs but not at how to use this stuff in practice. Any guides/examples for the curious? |
@gene-pavlovsky In general, the most frequent use case for tink_io is file read/write and network send/receive, in streaming (async) manner. Think |
@kevinresol thanks for explanation. Can you give me some examples of practical applications where you needed to do this with streaming? I imagine if I was writing a Haxe version of some UNIX tool (e.g. |
Actually when you write a nodejs application you will (almost) always need streaming in all relevant aspects (file read/write, network read/write, etc). Because you don't want a single IO operation to block your whole application (node is single-threaded). |
Ok that is a fair point, but do you need the streaming exposed to the user? E.g. I need to read a file, I understand that I should do it as an async operation, but do I have to handle the "chunks" manually, or this can be hidden inside the library code? |
Surely you can hide it. You just collect all the bytes and present the result to the user as a promise of a single bytes/chunk object. |
So would you say that these are more suited as a foundation for other libraries/frameworks, rather than to be used directly by application code? Or is there still a direct use case for this in applications? |
It really depends on your use case. For example if you want to report progress of the "data transfer" then you will need streaming not just a promise of the whole chunk. |
I see. I was thinking about progress, indeed |
I was trying to fix the static file middleware, where the file read stream could possibly fail (e.g. someone deleted the file while transferring / the file is in network drive and the network failed). Since the body of OutgoingResponse is an IdealStream, we have to somehow rescue the unsafe file read. But we can't simply rescue with an Empty source because that will possibly fail the client who is expecting
<content-length>
sized body and the transmission will be deemed as "paused" in the middle. The worst case is that the next response will be treated as the continuation of the body, if the connection is reused.There really isn't much we can do after the header is sent out, except forcibly killing the connection. One possible solution is to make the body of OutgoingResponse a RealStream and let the container deal with the failure.
The text was updated successfully, but these errors were encountered: