-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Fix and tune http::request setup by client #2934
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
Changes from all commits
072e86f
1d96dac
b68ed89
c7709fb
4267526
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -111,15 +111,12 @@ future<connection::reply_ptr> connection::maybe_wait_for_continue(const request& | |
| }); | ||
| } | ||
|
|
||
| void connection::setup_request(request& req) { | ||
| static void validate_request(const request& req) { | ||
| if (req._version.empty()) { | ||
| req._version = "1.1"; | ||
| throw std::runtime_error("HTTP version not set"); | ||
| } | ||
| if (req.content_length != 0) { | ||
| if (!req.body_writer && req.content.empty()) { | ||
| throw std::runtime_error("Request body writer not set and content is empty"); | ||
| } | ||
| req._headers["Content-Length"] = to_sstring(req.content_length); | ||
| if (req.content_length != 0 && !req.body_writer && req.content.empty()) { | ||
| throw std::runtime_error("Request body writer not set and content is empty"); | ||
| } | ||
| } | ||
|
|
||
|
|
@@ -156,8 +153,7 @@ future<connection::reply_ptr> connection::recv_reply() { | |
| }); | ||
| } | ||
|
|
||
| future<connection::reply_ptr> connection::do_make_request(request& req) { | ||
| setup_request(req); | ||
| future<connection::reply_ptr> connection::do_make_request(const request& req) { | ||
| return send_request_head(req).then([this, &req] { | ||
| return maybe_wait_for_continue(req).then([this, &req] (reply_ptr cont) { | ||
| if (cont) { | ||
|
|
@@ -174,6 +170,11 @@ future<connection::reply_ptr> connection::do_make_request(request& req) { | |
| } | ||
|
|
||
| future<reply> connection::make_request(request req) { | ||
| try { | ||
| validate_request(req); | ||
| } catch (...) { | ||
| return current_exception_as_future<reply>(); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wow, I completely missed that we switched from the idiom to I'm curious why. We need to update doc/tutorial.md which refers to the old idiom. Sad there is no grant any more forcing us to write such documentation :-( There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks. To be honest, I don't understand the justification in that commit (it's the first time I'm seeing it, maybe I was on vacation in December 2019 ;-)). By "offline", did he mean "out of line"? But in any case, since both functions would be called in an exception handler*, wouldn't they be out-of-line anyway? It's sad that that patch did not:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I see the doxygen in include/seastar/core/future.hh actually says about current_exception_as_future that it is:
I still have no idea why and would have been nice to read about somewhere. We don't pay for doxygen comments by the byte ;-) |
||
| } | ||
| return do_with(std::move(req), [this] (auto& req) { | ||
| return do_make_request(req).then([] (reply_ptr rep) { | ||
| return make_ready_future<reply>(std::move(*rep)); | ||
|
|
@@ -353,7 +354,12 @@ static bool is_retryable_exception(std::exception_ptr ex) { | |
| return false; | ||
| } | ||
|
|
||
| future<> client::make_request(request& req, reply_handler& handle, std::optional<reply::status_type> expected, abort_source* as) { | ||
| future<> client::make_request(const request& req, reply_handler& handle, std::optional<reply::status_type> expected, abort_source* as) { | ||
| try { | ||
| validate_request(req); | ||
| } catch (...) { | ||
| return current_exception_as_future(); | ||
| } | ||
| return with_connection([this, &req, &handle, as, expected] (connection& con) { | ||
| return do_make_request(con, req, handle, as, expected); | ||
| }, as).handle_exception([this, &req, &handle, as, expected] (std::exception_ptr ex) { | ||
|
|
@@ -391,7 +397,7 @@ class skip_body_source : public data_source_impl { | |
| } | ||
| }; | ||
|
|
||
| future<> client::do_make_request(connection& con, request& req, reply_handler& handle, abort_source* as, std::optional<reply::status_type> expected) { | ||
| future<> client::do_make_request(connection& con, const request& req, reply_handler& handle, abort_source* as, std::optional<reply::status_type> expected) { | ||
| auto sub = as ? as->subscribe([&con] () noexcept { con.shutdown(); }) : std::nullopt; | ||
| return con.do_make_request(req).then([this, &con, &req, &handle, expected] (connection::reply_ptr reply) mutable { | ||
| auto& rep = *reply; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I understand correctly, this changes the API - you now absolutely must specify the request's version and can't have it default to 1.1 like it did. I guess it's fine to change the API because it's in the experiemental namespace?
But why do you want version to be explicitly given, since there is a very good default - 1.1 has been the default for almost 20 years now?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It still defaults to 1.1 but in another place -- in the
request::make()helper that's used by all our code anyway.Yes, exactly.
Do I get it correctly that you suggest to always put "1.1" by the client regardless of what's there on the
request::_version? No strong reason. However, someone is working on http/2, presumably (I didn't check that) sending a request withreq._version = "2.0"and still seeing "1.1" string appearing on the socket would be quite surprisingThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I may be asking a stupid question because I don't understand the API. I got the impression that the API looks something like:
In that case, I thought it made sense that before your patch this request will default to 1.1.
Clearly, if someone creates a request explicitly asking for a different version - that version should be used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The API nowadays is
so assigning "_version = 1.1" now happens in
request::make(). If someone needs another version, then it should useauto req = request::make("GET", "google.com", "/foo/bar"); +req._version = "2.0"; co_await client.make_request(req);There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, though I don't understand why we have a "factory function"
request::make(...)instead of an ordinary constructor,request(...). But in any case, that is not new to this patch, so I am only asking for my own education, it's not really related to this review.