Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@i110
Copy link
Contributor

@i110 i110 commented Mar 18, 2019

Currently there're the following problems in streaming request body if h2o gets an early response (i.e. the upstream sends the response unless waiting whole request body).

  • h2o doesn't immediately forward early response to the client, and wait the whole request body to be sent to the upstream
  • h2o returns 502 error if the upstream closes the connection after sending an early response
  • h2o doesn't close the h1 upstream connection after sending request body even if it got an early response, that causes HTTP/1 framing error

This PR aims to fix these issues.

TODOs

  • fix http2client
  • consider timeouts
  • consider mruby's http_request
  • write tests

Copy link
Member

@kazuho kazuho left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the PR.

I like the how you refactor the HTTP/2 server stack. The state machine remains the same, however the state transitions happen regardless of if the request body is still streaming. The approach seems ideal to me.

OTOH, I am not sure if I like the introduction of on_finish callback in the httpclient API. IMO, it would be preferable to take the same approach that we have taken for the server-side code. I mean, can't we communicate the abrupt closure of the request body stream by using the proceed_req callback (possibly by extending the third argument to a tri-state value like h2o_send_state_t)?
Doing so would isolate the handling of streaming request body outside of the main state transition path (i.e. send headers -> receive headers -> receive body).

@i110
Copy link
Contributor Author

i110 commented Mar 20, 2019

@kazuho Thank you for your comment. I addressed the issues

Copy link
Member

@kazuho kazuho left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the changes.

Below are my comments on the changes to lib/common/http1client.c. I think they are mostly fine, the comments are mostly seeking for readablity improvements.

This is unrelated to the proposed change, but I might suggest renaming on_req_chunked and on_send_request. The former is a function that is called for processing the response (hence "req" is misleading). The latter is called when the sending of a request is complete (hence "send" is misleading; maybe something like on_whole_request_sent is more appropriate).

#include "h2o/token.h"

enum enum_h2o_http1client_stream_state {
STREAM_STATE_IDLE,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we might prefer omitting the prefix (i.e. h2o_http1client_ from both the typename and the names of the constants), or having prefix in both of them. They have the same visibility.

I also think that it might make sense to rename these states "~BEFORE_HEAD", "~IN_BODY", "~CLOSED". "Idle" and "open" do not naturally reflect what they are, and I was confused by the fact that you could call on_head when in "IDLE" state (i.e., can I call a callback before the stream becomes open?).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done by 572fab5

}

static void on_body_error(struct st_h2o_http1client_t *client, const char *errstr)
static void close_client(struct st_h2o_http1client_t *client)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To me it seems that the function is actually closing the response. Am I correct?

If that is the case, I think we should rename the function to something that better reflects what the function is doing. A close_client functions sometimes just stopping the read-side of the connection to the origin is confusing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done by 572fab5

@i110 i110 changed the title [WIP] early response with streaming body early response with streaming body Apr 22, 2019
lib/core/proxy.c Outdated
self->client = NULL;
detach_client(self);
h2o_req_log_error(req, "lib/core/proxy.c", "%s", "invalid response from upstream (malformed content-length)");
h2o_send_error_502(req, "Gateway Error", "invalid response from upstream", H2O_SEND_ERROR_HTTP1_CLOSE_CONNECTION);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's become now unnecessary to close the connection, the flag field can be set to 0 again. This is true for the three calls to h2o_send_error_502 in the file

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reverted by e708379

@i110
Copy link
Contributor Author

i110 commented Apr 23, 2019

@kazuho I addessed the remaining issues (http2client and mruby). could you review when you have the chance?

@i110
Copy link
Contributor Author

i110 commented May 11, 2019

@kazuho @deweerdt Finally all testes pass! Please review when you have the time.

@deweerdt
Copy link
Member

@i110 is the failure in CI relevant to this PR? I can't tell.

@i110
Copy link
Contributor Author

i110 commented May 17, 2019

@deweerdt
I think it isn't relevant to this PR because that test case are using neither body streaming nor early response. In the log, timeout at t/50mruby-middleware.t line 54. means that the request sent from curl to h2o was timed out, so I'm guessing there's some kind of timing issue in this test case, otherwise a rare error of ci environment hit us. This is first time for me to see this error. I'll dive deep if the failure continues.

@kazuho kazuho mentioned this pull request May 31, 2019
10 tasks
@kazuho
Copy link
Member

kazuho commented May 31, 2019

Thank you for the patience. I am looking forward to reviewing this PR next week by using it as the basis of request body handling in #1846.

@kazuho
Copy link
Member

kazuho commented Jun 21, 2019

Regarding "request body streaming," I think we should try to become consistent in referring to the feature as "request streaming".

"body streaming" is confusing because it does not clarify that it is about request body. "Request body streaming" is superfluous because we never stream headers. Hence "request streaming."

@kazuho kazuho changed the title early response with streaming body early response with request streaming Jun 21, 2019
@kazuho kazuho force-pushed the i110/early-response-with-streaming-body branch from d7ca0bd to 8455792 Compare September 5, 2019 07:21
include/h2o.h Outdated
* indicates whether the response is being sent or not
* @param req the request
*/
static int h2o_is_sending_response(h2o_req_t *req);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am going to remove this function, and directly check req->_generator != NULL in http1.c (the only code that uses this function).

The name ("is_sending_response") is inaccurate, as generator is assigned before the emission of the response starts and might remain assigned after the generator goes away (IIRC this is the case when there is a buffering ostream filter).

If a generator has been assigned is something that each protocol implementation (e.g., http1.c) can know about, and this information is only necessary to http1.c, due to HTTP/1 (ab)using an HTTP response to indicate the framing error (HTTP/2, /3 reports framing issues using frame-level errors).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in 829cb56.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually mruby.c also touches req->_generator in several points to know whether it can send the response header or not, and I intended to use this function in there too after this PR gets merged. Touching req->_generator makes me a bit anxious, so we should come up with another nice name, or at least removing _ prefix from _generator if we keep touching that member directly, WDYT?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants