[http1] fix HttpRequest to support query params (#38099)
Also add trace logging for HTTP requests and responses.
Closes #38099
COPYBARA_INTEGRATE_REVIEW=https://github.com/grpc/grpc/pull/38099 from markdroth:gcp_auth_trace_logging d3afa34b6bfdb153ed47bfdf058c5781160b333d
PiperOrigin-RevId: 695779969
diff --git a/src/core/util/http_client/httpcli.cc b/src/core/util/http_client/httpcli.cc
index 0eb2292..20e7768 100644
--- a/src/core/util/http_client/httpcli.cc
+++ b/src/core/util/http_client/httpcli.cc
@@ -79,8 +79,9 @@
}
std::string name =
absl::StrFormat("HTTP:GET:%s:%s", uri.authority(), uri.path());
- const grpc_slice request_text = grpc_httpcli_format_get_request(
- request, uri.authority().c_str(), uri.path().c_str());
+ const grpc_slice request_text =
+ grpc_httpcli_format_get_request(request, uri.authority().c_str(),
+ uri.EncodedPathAndQueryParams().c_str());
return MakeOrphanable<HttpRequest>(
std::move(uri), request_text, response, deadline, channel_args, on_done,
pollent, name.c_str(), std::move(test_only_generate_response),
@@ -103,8 +104,9 @@
}
std::string name =
absl::StrFormat("HTTP:POST:%s:%s", uri.authority(), uri.path());
- const grpc_slice request_text = grpc_httpcli_format_post_request(
- request, uri.authority().c_str(), uri.path().c_str());
+ const grpc_slice request_text =
+ grpc_httpcli_format_post_request(request, uri.authority().c_str(),
+ uri.EncodedPathAndQueryParams().c_str());
return MakeOrphanable<HttpRequest>(
std::move(uri), request_text, response, deadline, channel_args, on_done,
pollent, name.c_str(), std::move(test_only_generate_response),
@@ -127,8 +129,9 @@
}
std::string name =
absl::StrFormat("HTTP:PUT:%s:%s", uri.authority(), uri.path());
- const grpc_slice request_text = grpc_httpcli_format_put_request(
- request, uri.authority().c_str(), uri.path().c_str());
+ const grpc_slice request_text =
+ grpc_httpcli_format_put_request(request, uri.authority().c_str(),
+ uri.EncodedPathAndQueryParams().c_str());
return MakeOrphanable<HttpRequest>(
std::move(uri), request_text, response, deadline, channel_args, on_done,
pollent, name.c_str(), std::move(test_only_generate_response),
@@ -241,6 +244,8 @@
void HttpRequest::OnReadInternal(grpc_error_handle error) {
for (size_t i = 0; i < incoming_.count; i++) {
+ GRPC_TRACE_LOG(http1, INFO)
+ << "HTTP response data: " << StringViewFromSlice(incoming_.slices[i]);
if (GRPC_SLICE_LENGTH(incoming_.slices[i])) {
have_read_byte_ = 1;
grpc_error_handle err =
@@ -275,6 +280,8 @@
}
void HttpRequest::StartWrite() {
+ GRPC_TRACE_LOG(http1, INFO)
+ << "Sending HTTP1 request: " << StringViewFromSlice(request_text_);
CSliceRef(request_text_);
grpc_slice_buffer_add(&outgoing_, request_text_);
Ref().release(); // ref held by pending write
diff --git a/src/core/util/uri.cc b/src/core/util/uri.cc
index e6a94e5..e7b8224 100644
--- a/src/core/util/uri.cc
+++ b/src/core/util/uri.cc
@@ -352,6 +352,16 @@
parts.emplace_back("//");
parts.emplace_back(PercentEncode(authority_, IsAuthorityChar));
}
+ parts.emplace_back(EncodedPathAndQueryParams());
+ if (!fragment_.empty()) {
+ parts.push_back("#");
+ parts.push_back(PercentEncode(fragment_, IsQueryOrFragmentChar));
+ }
+ return absl::StrJoin(parts, "");
+}
+
+std::string URI::EncodedPathAndQueryParams() const {
+ std::vector<std::string> parts;
if (!path_.empty()) {
parts.emplace_back(PercentEncode(path_, IsPathChar));
}
@@ -360,10 +370,6 @@
parts.push_back(
absl::StrJoin(query_parameter_pairs_, "&", QueryParameterFormatter()));
}
- if (!fragment_.empty()) {
- parts.push_back("#");
- parts.push_back(PercentEncode(fragment_, IsQueryOrFragmentChar));
- }
return absl::StrJoin(parts, "");
}
diff --git a/src/core/util/uri.h b/src/core/util/uri.h
index 14e9274..3f9dc7d 100644
--- a/src/core/util/uri.h
+++ b/src/core/util/uri.h
@@ -76,7 +76,7 @@
return query_parameter_map_;
}
// A vector of key:value query parameter pairs, kept in order of appearance
- // within the URI search string. Repeated keys are represented as separate
+ // within the URI string. Repeated keys are represented as separate
// key:value elements.
const std::vector<QueryParam>& query_parameter_pairs() const {
return query_parameter_pairs_;
@@ -85,6 +85,10 @@
std::string ToString() const;
+ // Returns the encoded path and query params, such as would be used on
+ // the wire in an HTTP request.
+ std::string EncodedPathAndQueryParams() const;
+
private:
URI(std::string scheme, std::string authority, std::string path,
std::vector<QueryParam> query_parameter_pairs, std::string fragment);
diff --git a/test/core/http/httpcli_test.cc b/test/core/http/httpcli_test.cc
index 0a3c508..ba3dc9d 100644
--- a/test/core/http/httpcli_test.cc
+++ b/test/core/http/httpcli_test.cc
@@ -193,8 +193,10 @@
std::string host = absl::StrFormat("localhost:%d", g_server_port);
LOG(INFO) << "requesting from " << host;
memset(&req, 0, sizeof(req));
- auto uri = grpc_core::URI::Create("http", host, "/get", {} /* query params */,
- "" /* fragment */);
+ auto uri = grpc_core::URI::Create(
+ "http", host, "/get",
+ /*query_parameter_pairs=*/{{"foo", "bar"}, {"baz", "quux"}},
+ /*fragment=*/"");
CHECK(uri.ok());
grpc_core::OrphanablePtr<grpc_core::HttpRequest> http_request =
grpc_core::HttpRequest::Get(
@@ -219,8 +221,10 @@
memset(&req, 0, sizeof(req));
req.body = const_cast<char*>("hello");
req.body_length = 5;
- auto uri = grpc_core::URI::Create("http", host, "/post",
- {} /* query params */, "" /* fragment */);
+ auto uri = grpc_core::URI::Create(
+ "http", host, "/post",
+ /*query_parameter_pairs=*/{{"foo", "bar"}, {"mumble", "frotz"}},
+ /*fragment=*/"");
CHECK(uri.ok());
grpc_core::OrphanablePtr<grpc_core::HttpRequest> http_request =
grpc_core::HttpRequest::Post(
diff --git a/test/core/http/httpscli_test.cc b/test/core/http/httpscli_test.cc
index 88c764b..8c8c8bf 100644
--- a/test/core/http/httpscli_test.cc
+++ b/test/core/http/httpscli_test.cc
@@ -191,8 +191,10 @@
const_cast<char*>(GRPC_SSL_TARGET_NAME_OVERRIDE_ARG),
const_cast<char*>("foo.test.google.fr"));
grpc_channel_args args = {1, &ssl_override_arg};
- auto uri = grpc_core::URI::Create("https", host, "/get",
- {} /* query params */, "" /* fragment */);
+ auto uri = grpc_core::URI::Create(
+ "https", host, "/get",
+ /*query_parameter_pairs=*/{{"foo", "bar"}, {"baz", "quux"}},
+ /*fragment=*/"");
CHECK(uri.ok());
grpc_core::OrphanablePtr<grpc_core::HttpRequest> http_request =
grpc_core::HttpRequest::Get(
@@ -219,8 +221,10 @@
const_cast<char*>(GRPC_SSL_TARGET_NAME_OVERRIDE_ARG),
const_cast<char*>("foo.test.google.fr"));
grpc_channel_args args = {1, &ssl_override_arg};
- auto uri = grpc_core::URI::Create("https", host, "/post",
- {} /* query params */, "" /* fragment */);
+ auto uri = grpc_core::URI::Create(
+ "https", host, "/post",
+ /*query_parameter_pairs=*/{{"foo", "bar"}, {"mumble", "frotz"}},
+ /*fragment=*/"");
CHECK(uri.ok());
grpc_core::OrphanablePtr<grpc_core::HttpRequest> http_request =
grpc_core::HttpRequest::Post(
diff --git a/test/core/http/test_server.py b/test/core/http/test_server.py
index 6da64fc..34cb717 100755
--- a/test/core/http/test_server.py
+++ b/test/core/http/test_server.py
@@ -59,13 +59,13 @@
)
def do_GET(self):
- if self.path == "/get":
+ if self.path == "/get?foo=bar&baz=quux":
self.good()
def do_POST(self):
content_len = self.headers.get("content-length")
content = self.rfile.read(int(content_len)).decode("ascii")
- if self.path == "/post" and content == "hello":
+ if self.path == "/post?foo=bar&mumble=frotz" and content == "hello":
self.good()