xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/binary_http/binary_http_message.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 #ifndef QUICHE_BINARY_HTTP_BINARY_HTTP_MESSAGE_H_
2 #define QUICHE_BINARY_HTTP_BINARY_HTTP_MESSAGE_H_
3 
4 #include <cstddef>
5 #include <cstdint>
6 #include <functional>
7 #include <memory>
8 #include <ostream>
9 #include <string>
10 #include <utility>
11 #include <vector>
12 
13 #include "absl/container/flat_hash_map.h"
14 #include "absl/status/statusor.h"
15 #include "absl/strings/string_view.h"
16 #include "quiche/common/platform/api/quiche_export.h"
17 #include "quiche/common/quiche_data_writer.h"
18 
19 namespace quiche {
20 
21 // Supports encoding and decoding Binary Http messages.
22 // Currently limited to known-length messages.
23 // https://www.ietf.org/archive/id/draft-ietf-httpbis-binary-message-06.html
24 class QUICHE_EXPORT BinaryHttpMessage {
25  public:
26   // Name value pair of either a header or trailer field.
27   struct QUICHE_EXPORT Field {
28     std::string name;
29     std::string value;
30     bool operator==(const BinaryHttpMessage::Field& rhs) const {
31       return name == rhs.name && value == rhs.value;
32     }
33 
34     bool operator!=(const BinaryHttpMessage::Field& rhs) const {
35       return !(*this == rhs);
36     }
37 
38     std::string DebugString() const;
39   };
40   virtual ~BinaryHttpMessage() = default;
41 
42   // TODO(bschneider): Switch to use existing Http2HeaderBlock
43   BinaryHttpMessage* AddHeaderField(Field header_field);
44 
GetHeaderFields()45   const std::vector<Field>& GetHeaderFields() const {
46     return header_fields_.fields();
47   }
48 
set_body(std::string body)49   BinaryHttpMessage* set_body(std::string body) {
50     body_ = std::move(body);
51     return this;
52   }
53 
swap_body(std::string & body)54   void swap_body(std::string& body) { body_.swap(body); }
set_num_padding_bytes(size_t num_padding_bytes)55   void set_num_padding_bytes(size_t num_padding_bytes) {
56     num_padding_bytes_ = num_padding_bytes;
57   }
num_padding_bytes()58   size_t num_padding_bytes() const { return num_padding_bytes_; }
59 
body()60   absl::string_view body() const { return body_; }
61 
62   // Returns the number of bytes `Serialize` will return, including padding.
63   virtual size_t EncodedSize() const = 0;
64 
65   // Returns the Binary Http formatted message.
66   virtual absl::StatusOr<std::string> Serialize() const = 0;
67   // TODO(bschneider): Add AddTrailerField for chunked messages
68   // TODO(bschneider): Add SetBodyCallback() for chunked messages
69 
70   virtual std::string DebugString() const;
71 
72  protected:
73   class Fields {
74    public:
75     // Appends `field` to list of fields.  Can contain duplicates.
76     void AddField(BinaryHttpMessage::Field field);
77 
fields()78     const std::vector<BinaryHttpMessage::Field>& fields() const {
79       return fields_;
80     }
81 
82     bool operator==(const BinaryHttpMessage::Fields& rhs) const {
83       return fields_ == rhs.fields_;
84     }
85 
86     // Encode fields in insertion order.
87     // https://www.ietf.org/archive/id/draft-ietf-httpbis-binary-message-06.html#name-header-and-trailer-field-li
88     absl::Status Encode(quiche::QuicheDataWriter& writer) const;
89 
90     // The number of returned by EncodedFieldsSize
91     // plus the number of bytes used in the varint holding that value.
92     size_t EncodedSize() const;
93 
94    private:
95     // Number of bytes of just the set of fields.
96     size_t EncodedFieldsSize() const;
97 
98     // Fields in insertion order.
99     std::vector<BinaryHttpMessage::Field> fields_;
100   };
101 
102   // Checks equality excluding padding.
IsPayloadEqual(const BinaryHttpMessage & rhs)103   bool IsPayloadEqual(const BinaryHttpMessage& rhs) const {
104     // `has_host_` is derived from `header_fields_` so it doesn't need to be
105     // tested directly.
106     return body_ == rhs.body_ && header_fields_ == rhs.header_fields_;
107   }
108 
109   absl::Status EncodeKnownLengthFieldsAndBody(
110       quiche::QuicheDataWriter& writer) const;
111   size_t EncodedKnownLengthFieldsAndBodySize() const;
has_host()112   bool has_host() const { return has_host_; }
113 
114  private:
115   std::string body_;
116   Fields header_fields_;
117   bool has_host_ = false;
118   size_t num_padding_bytes_ = 0;
119 };
120 
121 void QUICHE_EXPORT PrintTo(const BinaryHttpMessage::Field& msg,
122                            std::ostream* os);
123 
124 class QUICHE_EXPORT BinaryHttpRequest : public BinaryHttpMessage {
125  public:
126   // HTTP request must have method, scheme, and path fields.
127   // The `authority` field is required unless a `host` header field is added.
128   // If a `host` header field is added, `authority` is serialized as the empty
129   // string.
130   // Some examples are:
131   //   scheme: HTTP
132   //   authority: www.example.com
133   //   path: /index.html
134   struct QUICHE_EXPORT ControlData {
135     std::string method;
136     std::string scheme;
137     std::string authority;
138     std::string path;
139     bool operator==(const BinaryHttpRequest::ControlData& rhs) const {
140       return method == rhs.method && scheme == rhs.scheme &&
141              authority == rhs.authority && path == rhs.path;
142     }
143     bool operator!=(const BinaryHttpRequest::ControlData& rhs) const {
144       return !(*this == rhs);
145     }
146   };
BinaryHttpRequest(ControlData control_data)147   explicit BinaryHttpRequest(ControlData control_data)
148       : control_data_(std::move(control_data)) {}
149 
150   // Deserialize
151   static absl::StatusOr<BinaryHttpRequest> Create(absl::string_view data);
152 
153   size_t EncodedSize() const override;
154   absl::StatusOr<std::string> Serialize() const override;
control_data()155   const ControlData& control_data() const { return control_data_; }
156 
157   virtual std::string DebugString() const override;
158 
159   // Returns true if the contents of the requests are equal, excluding padding.
IsPayloadEqual(const BinaryHttpRequest & rhs)160   bool IsPayloadEqual(const BinaryHttpRequest& rhs) const {
161     return control_data_ == rhs.control_data_ &&
162            BinaryHttpMessage::IsPayloadEqual(rhs);
163   }
164 
165   bool operator==(const BinaryHttpRequest& rhs) const {
166     return IsPayloadEqual(rhs) &&
167            num_padding_bytes() == rhs.num_padding_bytes();
168   }
169 
170   bool operator!=(const BinaryHttpRequest& rhs) const {
171     return !(*this == rhs);
172   }
173 
174  private:
175   absl::Status EncodeControlData(quiche::QuicheDataWriter& writer) const;
176 
177   size_t EncodedControlDataSize() const;
178 
179   // Returns Binary Http known length request formatted request.
180   absl::StatusOr<std::string> EncodeAsKnownLength() const;
181 
182   const ControlData control_data_;
183 };
184 
185 void QUICHE_EXPORT PrintTo(const BinaryHttpRequest& msg, std::ostream* os);
186 
187 class QUICHE_EXPORT BinaryHttpResponse : public BinaryHttpMessage {
188  public:
189   // https://www.ietf.org/archive/id/draft-ietf-httpbis-binary-message-06.html#name-response-control-data
190   // A response can contain 0 to N informational responses.  Each informational
191   // response contains a status code followed by a header field. Valid status
192   // codes are [100,199].
193   class QUICHE_EXPORT InformationalResponse {
194    public:
InformationalResponse(uint16_t status_code)195     explicit InformationalResponse(uint16_t status_code)
196         : status_code_(status_code) {}
InformationalResponse(uint16_t status_code,const std::vector<BinaryHttpMessage::Field> & fields)197     InformationalResponse(uint16_t status_code,
198                           const std::vector<BinaryHttpMessage::Field>& fields)
199         : status_code_(status_code) {
200       for (const BinaryHttpMessage::Field& field : fields) {
201         AddField(field.name, field.value);
202       }
203     }
204 
205     bool operator==(
206         const BinaryHttpResponse::InformationalResponse& rhs) const {
207       return status_code_ == rhs.status_code_ && fields_ == rhs.fields_;
208     }
209 
210     bool operator!=(
211         const BinaryHttpResponse::InformationalResponse& rhs) const {
212       return !(*this == rhs);
213     }
214 
215     // Adds a field with the provided name, converted to lower case.
216     // Fields are in the order they are added.
217     void AddField(absl::string_view name, std::string value);
218 
fields()219     const std::vector<BinaryHttpMessage::Field>& fields() const {
220       return fields_.fields();
221     }
222 
status_code()223     uint16_t status_code() const { return status_code_; }
224 
225     std::string DebugString() const;
226 
227    private:
228     // Give BinaryHttpResponse access to Encoding functionality.
229     friend class BinaryHttpResponse;
230 
231     size_t EncodedSize() const;
232 
233     // Appends the encoded fields and body to `writer`.
234     absl::Status Encode(quiche::QuicheDataWriter& writer) const;
235 
236     const uint16_t status_code_;
237     BinaryHttpMessage::Fields fields_;
238   };
239 
BinaryHttpResponse(uint16_t status_code)240   explicit BinaryHttpResponse(uint16_t status_code)
241       : status_code_(status_code) {}
242 
243   // Deserialize
244   static absl::StatusOr<BinaryHttpResponse> Create(absl::string_view data);
245 
246   size_t EncodedSize() const override;
247   absl::StatusOr<std::string> Serialize() const override;
248 
249   // Informational status codes must be between 100 and 199 inclusive.
250   absl::Status AddInformationalResponse(uint16_t status_code,
251                                         std::vector<Field> header_fields);
252 
status_code()253   uint16_t status_code() const { return status_code_; }
254 
255   // References in the returned `ResponseControlData` are invalidated on
256   // `BinaryHttpResponse` object mutations.
informational_responses()257   const std::vector<InformationalResponse>& informational_responses() const {
258     return informational_response_control_data_;
259   }
260 
261   virtual std::string DebugString() const override;
262 
263   // Returns true if the contents of the requests are equal, excluding padding.
IsPayloadEqual(const BinaryHttpResponse & rhs)264   bool IsPayloadEqual(const BinaryHttpResponse& rhs) const {
265     return informational_response_control_data_ ==
266                rhs.informational_response_control_data_ &&
267            status_code_ == rhs.status_code_ &&
268            BinaryHttpMessage::IsPayloadEqual(rhs);
269   }
270 
271   bool operator==(const BinaryHttpResponse& rhs) const {
272     return IsPayloadEqual(rhs) &&
273            num_padding_bytes() == rhs.num_padding_bytes();
274   }
275 
276   bool operator!=(const BinaryHttpResponse& rhs) const {
277     return !(*this == rhs);
278   }
279 
280  private:
281   // Returns Binary Http known length request formatted response.
282   absl::StatusOr<std::string> EncodeAsKnownLength() const;
283 
284   std::vector<InformationalResponse> informational_response_control_data_;
285   const uint16_t status_code_;
286 };
287 
288 void QUICHE_EXPORT PrintTo(const BinaryHttpResponse& msg, std::ostream* os);
289 }  // namespace quiche
290 
291 #endif  // QUICHE_BINARY_HTTP_BINARY_HTTP_MESSAGE_H_
292