xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/quic/core/http/spdy_utils.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "quiche/quic/core/http/spdy_utils.h"
6 
7 #include <memory>
8 #include <optional>
9 #include <string>
10 #include <vector>
11 
12 #include "absl/strings/numbers.h"
13 #include "absl/strings/str_cat.h"
14 #include "absl/strings/str_split.h"
15 #include "absl/strings/string_view.h"
16 #include "quiche/quic/core/quic_versions.h"
17 #include "quiche/quic/platform/api/quic_flag_utils.h"
18 #include "quiche/quic/platform/api/quic_flags.h"
19 #include "quiche/quic/platform/api/quic_logging.h"
20 #include "quiche/common/quiche_text_utils.h"
21 #include "quiche/spdy/core/spdy_protocol.h"
22 
23 using spdy::Http2HeaderBlock;
24 
25 namespace quic {
26 
27 // static
ExtractContentLengthFromHeaders(int64_t * content_length,Http2HeaderBlock * headers)28 bool SpdyUtils::ExtractContentLengthFromHeaders(int64_t* content_length,
29                                                 Http2HeaderBlock* headers) {
30   auto it = headers->find("content-length");
31   if (it == headers->end()) {
32     return false;
33   } else {
34     // Check whether multiple values are consistent.
35     absl::string_view content_length_header = it->second;
36     std::vector<absl::string_view> values =
37         absl::StrSplit(content_length_header, '\0');
38     for (const absl::string_view& value : values) {
39       uint64_t new_value;
40       if (!absl::SimpleAtoi(value, &new_value) ||
41           !quiche::QuicheTextUtils::IsAllDigits(value)) {
42         QUIC_DLOG(ERROR)
43             << "Content length was either unparseable or negative.";
44         return false;
45       }
46       if (*content_length < 0) {
47         *content_length = new_value;
48         continue;
49       }
50       if (new_value != static_cast<uint64_t>(*content_length)) {
51         QUIC_DLOG(ERROR)
52             << "Parsed content length " << new_value << " is "
53             << "inconsistent with previously detected content length "
54             << *content_length;
55         return false;
56       }
57     }
58     return true;
59   }
60 }
61 
CopyAndValidateHeaders(const QuicHeaderList & header_list,int64_t * content_length,Http2HeaderBlock * headers)62 bool SpdyUtils::CopyAndValidateHeaders(const QuicHeaderList& header_list,
63                                        int64_t* content_length,
64                                        Http2HeaderBlock* headers) {
65   for (const auto& p : header_list) {
66     const std::string& name = p.first;
67     if (name.empty()) {
68       QUIC_DLOG(ERROR) << "Header name must not be empty.";
69       return false;
70     }
71 
72     if (quiche::QuicheTextUtils::ContainsUpperCase(name)) {
73       QUIC_DLOG(ERROR) << "Malformed header: Header name " << name
74                        << " contains upper-case characters.";
75       return false;
76     }
77 
78     headers->AppendValueOrAddHeader(name, p.second);
79   }
80 
81   if (headers->contains("content-length") &&
82       !ExtractContentLengthFromHeaders(content_length, headers)) {
83     return false;
84   }
85 
86   QUIC_DVLOG(1) << "Successfully parsed headers: " << headers->DebugString();
87   return true;
88 }
89 
CopyAndValidateTrailers(const QuicHeaderList & header_list,bool expect_final_byte_offset,size_t * final_byte_offset,Http2HeaderBlock * trailers)90 bool SpdyUtils::CopyAndValidateTrailers(const QuicHeaderList& header_list,
91                                         bool expect_final_byte_offset,
92                                         size_t* final_byte_offset,
93                                         Http2HeaderBlock* trailers) {
94   bool found_final_byte_offset = false;
95   for (const auto& p : header_list) {
96     const std::string& name = p.first;
97 
98     // Pull out the final offset pseudo header which indicates the number of
99     // response body bytes expected.
100     if (expect_final_byte_offset && !found_final_byte_offset &&
101         name == kFinalOffsetHeaderKey &&
102         absl::SimpleAtoi(p.second, final_byte_offset)) {
103       found_final_byte_offset = true;
104       continue;
105     }
106 
107     if (name.empty() || name[0] == ':') {
108       QUIC_DLOG(ERROR)
109           << "Trailers must not be empty, and must not contain pseudo-"
110           << "headers. Found: '" << name << "'";
111       return false;
112     }
113 
114     if (quiche::QuicheTextUtils::ContainsUpperCase(name)) {
115       QUIC_DLOG(ERROR) << "Malformed header: Header name " << name
116                        << " contains upper-case characters.";
117       return false;
118     }
119 
120     trailers->AppendValueOrAddHeader(name, p.second);
121   }
122 
123   if (expect_final_byte_offset && !found_final_byte_offset) {
124     QUIC_DLOG(ERROR) << "Required key '" << kFinalOffsetHeaderKey
125                      << "' not present";
126     return false;
127   }
128 
129   // TODO(rjshade): Check for other forbidden keys, following the HTTP/2 spec.
130 
131   QUIC_DVLOG(1) << "Successfully parsed Trailers: " << trailers->DebugString();
132   return true;
133 }
134 
135 // static
136 // TODO(danzh): Move it to quic/tools/ and switch to use GURL.
PopulateHeaderBlockFromUrl(const std::string url,Http2HeaderBlock * headers)137 bool SpdyUtils::PopulateHeaderBlockFromUrl(const std::string url,
138                                            Http2HeaderBlock* headers) {
139   (*headers)[":method"] = "GET";
140   size_t pos = url.find("://");
141   if (pos == std::string::npos) {
142     return false;
143   }
144   (*headers)[":scheme"] = url.substr(0, pos);
145   size_t start = pos + 3;
146   pos = url.find('/', start);
147   if (pos == std::string::npos) {
148     (*headers)[":authority"] = url.substr(start);
149     (*headers)[":path"] = "/";
150     return true;
151   }
152   (*headers)[":authority"] = url.substr(start, pos - start);
153   (*headers)[":path"] = url.substr(pos);
154   return true;
155 }
156 
157 // static
ExtractQuicVersionFromAltSvcEntry(const spdy::SpdyAltSvcWireFormat::AlternativeService & alternative_service_entry,const ParsedQuicVersionVector & supported_versions)158 ParsedQuicVersion SpdyUtils::ExtractQuicVersionFromAltSvcEntry(
159     const spdy::SpdyAltSvcWireFormat::AlternativeService&
160         alternative_service_entry,
161     const ParsedQuicVersionVector& supported_versions) {
162   for (const ParsedQuicVersion& version : supported_versions) {
163     if (version.AlpnDeferToRFCv1()) {
164       // Versions with share an ALPN with v1 are currently unable to be
165       // advertised with Alt-Svc.
166       continue;
167     }
168     if (AlpnForVersion(version) == alternative_service_entry.protocol_id) {
169       return version;
170     }
171   }
172 
173   return ParsedQuicVersion::Unsupported();
174 }
175 
176 }  // namespace quic
177