Move Datagram-Flow-Id header to SpdyUtils
This code is not used in production.
PiperOrigin-RevId: 359912077
Change-Id: Ifdf163e92b6f184f0e5f8bc9c6629d3c2805d9c3
diff --git a/quic/core/http/spdy_utils.cc b/quic/core/http/spdy_utils.cc
index a3d800a..d3a3c92 100644
--- a/quic/core/http/spdy_utils.cc
+++ b/quic/core/http/spdy_utils.cc
@@ -12,6 +12,7 @@
#include "absl/strings/str_cat.h"
#include "absl/strings/str_split.h"
#include "absl/strings/string_view.h"
+#include "absl/types/optional.h"
#include "quic/platform/api/quic_flag_utils.h"
#include "quic/platform/api/quic_flags.h"
#include "quic/platform/api/quic_logging.h"
@@ -153,4 +154,45 @@
return true;
}
+// static
+absl::optional<QuicDatagramFlowId> SpdyUtils::ParseDatagramFlowIdHeader(
+ const spdy::SpdyHeaderBlock& headers) {
+ auto flow_id_pair = headers.find("datagram-flow-id");
+ if (flow_id_pair == headers.end()) {
+ return absl::nullopt;
+ }
+ std::vector<absl::string_view> flow_id_strings =
+ absl::StrSplit(flow_id_pair->second, ',');
+ absl::optional<QuicDatagramFlowId> first_named_flow_id;
+ for (absl::string_view flow_id_string : flow_id_strings) {
+ std::vector<absl::string_view> flow_id_components =
+ absl::StrSplit(flow_id_string, ';');
+ if (flow_id_components.empty()) {
+ continue;
+ }
+ absl::string_view flow_id_value_string = flow_id_components[0];
+ quiche::QuicheTextUtils::RemoveLeadingAndTrailingWhitespace(
+ &flow_id_value_string);
+ QuicDatagramFlowId flow_id;
+ if (!absl::SimpleAtoi(flow_id_value_string, &flow_id)) {
+ continue;
+ }
+ if (flow_id_components.size() == 1) {
+ // This flow ID is unnamed, return this one.
+ return flow_id;
+ }
+ // Otherwise this is a named flow ID.
+ if (!first_named_flow_id.has_value()) {
+ first_named_flow_id = flow_id;
+ }
+ }
+ return first_named_flow_id;
+}
+
+// static
+void SpdyUtils::AddDatagramFlowIdHeader(spdy::SpdyHeaderBlock* headers,
+ QuicDatagramFlowId flow_id) {
+ (*headers)["datagram-flow-id"] = absl::StrCat(flow_id);
+}
+
} // namespace quic
diff --git a/quic/core/http/spdy_utils.h b/quic/core/http/spdy_utils.h
index a3b3134..e3a8a97 100644
--- a/quic/core/http/spdy_utils.h
+++ b/quic/core/http/spdy_utils.h
@@ -9,6 +9,7 @@
#include <cstdint>
#include <string>
+#include "absl/types/optional.h"
#include "quic/core/http/http_constants.h"
#include "quic/core/http/quic_header_list.h"
#include "quic/core/quic_packets.h"
@@ -51,6 +52,15 @@
// which must be fully-qualified.
static bool PopulateHeaderBlockFromUrl(const std::string url,
spdy::SpdyHeaderBlock* headers);
+
+ // Parses the "datagram-flow-id" header, returns the flow ID on success, or
+ // returns absl::nullopt if the header was not present or failed to parse.
+ static absl::optional<QuicDatagramFlowId> ParseDatagramFlowIdHeader(
+ const spdy::SpdyHeaderBlock& headers);
+
+ // Adds the "datagram-flow-id" header.
+ static void AddDatagramFlowIdHeader(spdy::SpdyHeaderBlock* headers,
+ QuicDatagramFlowId flow_id);
};
} // namespace quic
diff --git a/quic/core/http/spdy_utils_test.cc b/quic/core/http/spdy_utils_test.cc
index 5cb391d..c7207f3 100644
--- a/quic/core/http/spdy_utils_test.cc
+++ b/quic/core/http/spdy_utils_test.cc
@@ -33,6 +33,14 @@
return headers;
}
+static void ValidateDatagramFlowId(
+ const std::string& header_value,
+ absl::optional<QuicDatagramFlowId> expected_flow_id) {
+ SpdyHeaderBlock headers;
+ headers["datagram-flow-id"] = header_value;
+ ASSERT_EQ(SpdyUtils::ParseDatagramFlowIdHeader(headers), expected_flow_id);
+}
+
} // anonymous namespace
using CopyAndValidateHeaders = QuicTest;
@@ -377,5 +385,29 @@
SpdyUtils::PopulateHeaderBlockFromUrl("www.google.com/", &headers));
}
+using DatagramFlowIdTest = QuicTest;
+
+TEST_F(DatagramFlowIdTest, DatagramFlowId) {
+ // Test missing header.
+ SpdyHeaderBlock headers;
+ EXPECT_EQ(SpdyUtils::ParseDatagramFlowIdHeader(headers), absl::nullopt);
+ // Add header and verify it parses.
+ QuicDatagramFlowId flow_id = 123;
+ SpdyUtils::AddDatagramFlowIdHeader(&headers, flow_id);
+ EXPECT_EQ(SpdyUtils::ParseDatagramFlowIdHeader(headers), flow_id);
+ // Test empty header.
+ ValidateDatagramFlowId("", absl::nullopt);
+ // Test invalid header.
+ ValidateDatagramFlowId("M4SQU3", absl::nullopt);
+ // Test simple header.
+ ValidateDatagramFlowId("42", 42);
+ // Test with parameter.
+ ValidateDatagramFlowId("42; abc=def", 42);
+ // Test list.
+ ValidateDatagramFlowId("42, 44; ecn-ect0, 46; ecn-ect1, 48; ecn-ce", 42);
+ // Test reordered list.
+ ValidateDatagramFlowId("44; ecn-ect0, 42, 48; ecn-ce, 46; ecn-ect1", 42);
+}
+
} // namespace test
} // namespace quic
diff --git a/quic/masque/masque_client_session.cc b/quic/masque/masque_client_session.cc
index e158125..79ab509 100644
--- a/quic/masque/masque_client_session.cc
+++ b/quic/masque/masque_client_session.cc
@@ -4,6 +4,7 @@
#include "quic/masque/masque_client_session.h"
#include "absl/algorithm/container.h"
+#include "quic/core/http/spdy_utils.h"
#include "quic/core/quic_data_reader.h"
#include "common/platform/api/quiche_text_utils.h"
@@ -99,7 +100,7 @@
headers[":scheme"] = "masque";
headers[":path"] = "/";
headers[":authority"] = target_server_address.ToString();
- headers["datagram-flow-id"] = absl::StrCat(flow_id);
+ SpdyUtils::AddDatagramFlowIdHeader(&headers, flow_id);
size_t bytes_sent =
stream->SendRequest(std::move(headers), /*body=*/"", /*fin=*/false);
if (bytes_sent == 0) {
diff --git a/quic/masque/masque_server_session.cc b/quic/masque/masque_server_session.cc
index 1e420b9..4f065ee 100644
--- a/quic/masque/masque_server_session.cc
+++ b/quic/masque/masque_server_session.cc
@@ -8,6 +8,8 @@
#include "absl/strings/str_cat.h"
#include "absl/strings/string_view.h"
+#include "absl/types/optional.h"
+#include "quic/core/http/spdy_utils.h"
#include "quic/core/quic_data_reader.h"
#include "quic/core/quic_udp_socket.h"
#include "quic/tools/quic_url.h"
@@ -174,12 +176,10 @@
auto path_pair = request_headers.find(":path");
auto scheme_pair = request_headers.find(":scheme");
auto method_pair = request_headers.find(":method");
- auto flow_id_pair = request_headers.find("datagram-flow-id");
auto authority_pair = request_headers.find(":authority");
if (path_pair == request_headers.end() ||
scheme_pair == request_headers.end() ||
method_pair == request_headers.end() ||
- flow_id_pair == request_headers.end() ||
authority_pair == request_headers.end()) {
QUIC_DLOG(ERROR) << "MASQUE request is missing required headers";
return CreateBackendErrorResponse("400", "Missing required headers");
@@ -187,7 +187,6 @@
absl::string_view path = path_pair->second;
absl::string_view scheme = scheme_pair->second;
absl::string_view method = method_pair->second;
- absl::string_view flow_id_str = flow_id_pair->second;
absl::string_view authority = authority_pair->second;
if (path.empty()) {
QUIC_DLOG(ERROR) << "MASQUE request with empty path";
@@ -201,11 +200,13 @@
QUIC_DLOG(ERROR) << "MASQUE request with bad method \"" << method << "\"";
return CreateBackendErrorResponse("400", "Bad method");
}
- QuicDatagramFlowId flow_id;
- if (!absl::SimpleAtoi(flow_id_str, &flow_id)) {
- QUIC_DLOG(ERROR) << "MASQUE request with bad flow_id \"" << flow_id_str
- << "\"";
- return CreateBackendErrorResponse("400", "Bad flow ID");
+ absl::optional<QuicDatagramFlowId> flow_id =
+ SpdyUtils::ParseDatagramFlowIdHeader(request_headers);
+ if (!flow_id.has_value()) {
+ QUIC_DLOG(ERROR)
+ << "MASQUE request with bad or missing DatagramFlowId header";
+ return CreateBackendErrorResponse("400",
+ "Bad or missing DatagramFlowId header");
}
QuicUrl url(absl::StrCat("https://", authority));
if (!url.IsValid() || url.PathParamsQuery() != "/") {
@@ -232,7 +233,7 @@
info_list, freeaddrinfo);
QuicSocketAddress target_server_address(info_list->ai_addr,
info_list->ai_addrlen);
- QUIC_DLOG(INFO) << "Got CONNECT_UDP request flow_id=" << flow_id
+ QUIC_DLOG(INFO) << "Got CONNECT_UDP request flow_id=" << *flow_id
<< " target_server_address=\"" << target_server_address
<< "\"";
@@ -250,12 +251,12 @@
epoll_server_->RegisterFDForRead(fd_wrapper.fd(), this);
connect_udp_server_states_.emplace_back(ConnectUdpServerState(
- flow_id, request_handler->stream_id(), target_server_address,
+ *flow_id, request_handler->stream_id(), target_server_address,
fd_wrapper.extract_fd(), this));
spdy::Http2HeaderBlock response_headers;
response_headers[":status"] = "200";
- response_headers["datagram-flow-id"] = absl::StrCat(flow_id);
+ SpdyUtils::AddDatagramFlowIdHeader(&response_headers, *flow_id);
auto response = std::make_unique<QuicBackendResponse>();
response->set_response_type(QuicBackendResponse::INCOMPLETE_RESPONSE);
response->set_headers(std::move(response_headers));