-
Notifications
You must be signed in to change notification settings - Fork 63
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
Failing client requests #122
Comments
I had some issues with this before, it might mean that the headers frame was crafted before the handshake was performed and might be in an invalid state, but hard to say without a small repro code. On another note, I've created this gem, which aims at supporting HTTP/2 and HTTP/1 and multiple concurrent requests, if you want to have a look. Maybe it solves your problem already. |
I have a repro care to take a look at it with me? |
@HoneyryderChuck Are you able to join https://gitter.im/socketry/async to discuss? |
Here is the repro
|
I'm testing using
It seems the only difference is in the broken case, don't send settings frame like so:
|
Hmmm....
https://http2.github.io/http2-spec/#SETTINGS So, it seems like a bug? |
I'll only be able to look into it later in the day, however, don't you have a small script that fits in a comment? Would greatly reduce the overhead of building a full project.
Looking at your example and nghttp's, nghttp receives and processes the sender settings before sending their own. http-2 crafts the settings frame and buffers it first, but none of them are wrong spec-wise (it is the first frame being sent in both cases). You'd have to see internally why the protocol error is being thrown. You could also try the examples script in order to reproduce it (if that script fails, it is definitely a bug). I'd suggest you give httpx a try though. I've been using it to test a ruby server implementation, and you might not need the low-level details of HTTP/2 frames. But if you really must, I'll see if I can have a look at it later. |
@HoneyryderChuck Take a look at those examples again. nghttp generates the following settings frame:
Such a frame is never generated by |
I suspect what is happening is that the SETTINGS ACK is masking the generation of a proper settings frame, there is a pseudo-race condition in the logic of http-2, I'll see if I can make a PR. |
Again, I'd have to see that small repro script. I see two different dumps from your example, One sending two frames before the read loop, and and the second one without those. Did you omit those two frames from the second dump, or are they really not being generated? The second example might mean that you're reading frames from the socket and sending them to the connection before initializing your own. This gem expects you to initialize the request before exchanging frames. So, if you are not building frames from your request before exchanging, you should consider initializing it yourself |
meaning: connection = HTTP2::Client.new
# set up all your callbacks here
connection.send_connection_preface |
The initial settings frame is not being generated.
I will investigate this, but my usage is very similar to the README. |
I see there is some kind of state management bug. Here, it has a state transition from Lines 50 to 51 in 16dec9f
But this transition can also be made here: http-2/lib/http/2/connection.rb Line 424 in 16dec9f
So it seems that depending on the order of operations, part of the connection is not initialised. |
If you followed the README example, there's your issue (@igrigorik , maybe consider changing it?). You should instead follow the example get, or as a quick fix, apply the thing I suggested in my last comment. |
Okay, so adding a call to |
@HoneyryderChuck Thanks so much for your help you've been awesome and patient :) |
No worries :) . It's kind of by design. Most of the examples here craft the frames from the client fully before doing network stuff (nghttp works the other way round, I suppose). This gem also doesn't control what you do regarding network, so all bets are off there. A higher level abstraction is also better, which is why I created |
Fair enough, it still seems like a race condition in the state machine. |
It's not a race condition, it's just an unhandled state transition. A connection implements However, I assume that it would have some impact in the tests, so I'll defer to @igrigorik on whether this can/should be done. |
Fair enough. The way it's patched in the The problem is the object is not fully constructed after calling
What about
At least this way it's possible to fully construct the object and make sure it's in a valid state. |
You could even make a dedicated method if you don't want to change the API, e.g. |
I'm not the best person to answer this, as this was @igrigorik design. But I don't find it bad, and one doesn't need to provide n ways to do the same if there is no tangible advantage. And this is supposed to be a lower level lib, not exactly user facing, so DSL ergonomics count less. |
Those are all good points and mostly subjective. Some objective ones: it's not documented clearly; should be in the constructor documentation I think, and perhaps in the README. There are performance implications regarding latency and doing unnecessary work on the critical path. However, it's Ruby so Thanks for your continued input. |
Hey guys, trying to catch up here.. apologies ahead of time if I'm rehashing what you already covered. I'm still a little puzzled as to what the bug is here:
What's missing in above? Can we write a failing spec test for this? |
If you call |
Ahh.. Ok, that's an oversight — let's fix that. To start, would you be willing to put together a failing spec? :) |
Sure. |
Then part of the associated PR will have to make it a private method (I thought that this was public API and have advised @ioquatix in this regard). |
#44 discusses the same problem. |
I've worked around this issue. I may supply a PR to rework how Recently I've added a PR which specs the behaviour as it stands, so I'm happy with that. |
I'm using the latest published gem (0.9.0).
I'm working on an asynchronous implementation which has feature parity with HTTP1 (connection per request and sequential connections using keep-alive). I'm working on the HTTP2 implementation which for the most part appears to work except the functionality depends on when I start the read loop.
Here are two dumps from my client:
It appears that depending on when the read loop is started (i.e. before
new_stream
or after) changes the behaviour of the client connection, and in this case it fails:I'm not absolutely certain what the issue is yet but I thought I'd report my initial findings. I'll follow up with more details as they become available.
The text was updated successfully, but these errors were encountered: