xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/http2/adapter/nghttp2_util.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 #include "quiche/http2/adapter/nghttp2_util.h"
2 
3 #include <cstdint>
4 #include <memory>
5 
6 #include "absl/strings/str_join.h"
7 #include "absl/strings/string_view.h"
8 #include "absl/types/span.h"
9 #include "quiche/http2/adapter/http2_protocol.h"
10 #include "quiche/common/platform/api/quiche_logging.h"
11 #include "quiche/common/quiche_endian.h"
12 
13 namespace http2 {
14 namespace adapter {
15 
16 namespace {
17 
18 using InvalidFrameError = Http2VisitorInterface::InvalidFrameError;
19 
DeleteCallbacks(nghttp2_session_callbacks * callbacks)20 void DeleteCallbacks(nghttp2_session_callbacks* callbacks) {
21   if (callbacks) {
22     nghttp2_session_callbacks_del(callbacks);
23   }
24 }
25 
DeleteSession(nghttp2_session * session)26 void DeleteSession(nghttp2_session* session) {
27   if (session) {
28     nghttp2_session_del(session);
29   }
30 }
31 
32 }  // namespace
33 
MakeCallbacksPtr(nghttp2_session_callbacks * callbacks)34 nghttp2_session_callbacks_unique_ptr MakeCallbacksPtr(
35     nghttp2_session_callbacks* callbacks) {
36   return nghttp2_session_callbacks_unique_ptr(callbacks, &DeleteCallbacks);
37 }
38 
MakeSessionPtr(nghttp2_session * session)39 nghttp2_session_unique_ptr MakeSessionPtr(nghttp2_session* session) {
40   return nghttp2_session_unique_ptr(session, &DeleteSession);
41 }
42 
ToUint8Ptr(char * str)43 uint8_t* ToUint8Ptr(char* str) { return reinterpret_cast<uint8_t*>(str); }
ToUint8Ptr(const char * str)44 uint8_t* ToUint8Ptr(const char* str) {
45   return const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(str));
46 }
47 
ToStringView(nghttp2_rcbuf * rc_buffer)48 absl::string_view ToStringView(nghttp2_rcbuf* rc_buffer) {
49   nghttp2_vec buffer = nghttp2_rcbuf_get_buf(rc_buffer);
50   return absl::string_view(reinterpret_cast<const char*>(buffer.base),
51                            buffer.len);
52 }
53 
ToStringView(uint8_t * pointer,size_t length)54 absl::string_view ToStringView(uint8_t* pointer, size_t length) {
55   return absl::string_view(reinterpret_cast<const char*>(pointer), length);
56 }
57 
ToStringView(const uint8_t * pointer,size_t length)58 absl::string_view ToStringView(const uint8_t* pointer, size_t length) {
59   return absl::string_view(reinterpret_cast<const char*>(pointer), length);
60 }
61 
GetNghttp2Nvs(absl::Span<const Header> headers)62 std::vector<nghttp2_nv> GetNghttp2Nvs(absl::Span<const Header> headers) {
63   const int num_headers = headers.size();
64   std::vector<nghttp2_nv> nghttp2_nvs;
65   nghttp2_nvs.reserve(num_headers);
66   for (int i = 0; i < num_headers; ++i) {
67     nghttp2_nv header;
68     uint8_t flags = NGHTTP2_NV_FLAG_NONE;
69 
70     const auto [name, no_copy_name] = GetStringView(headers[i].first);
71     header.name = ToUint8Ptr(name.data());
72     header.namelen = name.size();
73     if (no_copy_name) {
74       flags |= NGHTTP2_NV_FLAG_NO_COPY_NAME;
75     }
76     const auto [value, no_copy_value] = GetStringView(headers[i].second);
77     header.value = ToUint8Ptr(value.data());
78     header.valuelen = value.size();
79     if (no_copy_value) {
80       flags |= NGHTTP2_NV_FLAG_NO_COPY_VALUE;
81     }
82     header.flags = flags;
83     nghttp2_nvs.push_back(std::move(header));
84   }
85 
86   return nghttp2_nvs;
87 }
88 
GetResponseNghttp2Nvs(const spdy::Http2HeaderBlock & headers,absl::string_view response_code)89 std::vector<nghttp2_nv> GetResponseNghttp2Nvs(
90     const spdy::Http2HeaderBlock& headers, absl::string_view response_code) {
91   // Allocate enough for all headers and also the :status pseudoheader.
92   const int num_headers = headers.size();
93   std::vector<nghttp2_nv> nghttp2_nvs;
94   nghttp2_nvs.reserve(num_headers + 1);
95 
96   // Add the :status pseudoheader first.
97   nghttp2_nv status;
98   status.name = ToUint8Ptr(kHttp2StatusPseudoHeader);
99   status.namelen = strlen(kHttp2StatusPseudoHeader);
100   status.value = ToUint8Ptr(response_code.data());
101   status.valuelen = response_code.size();
102   status.flags = NGHTTP2_FLAG_NONE;
103   nghttp2_nvs.push_back(std::move(status));
104 
105   // Add the remaining headers.
106   for (const auto& header_pair : headers) {
107     nghttp2_nv header;
108     header.name = ToUint8Ptr(header_pair.first.data());
109     header.namelen = header_pair.first.size();
110     header.value = ToUint8Ptr(header_pair.second.data());
111     header.valuelen = header_pair.second.size();
112     header.flags = NGHTTP2_FLAG_NONE;
113     nghttp2_nvs.push_back(std::move(header));
114   }
115 
116   return nghttp2_nvs;
117 }
118 
ToHttp2ErrorCode(uint32_t wire_error_code)119 Http2ErrorCode ToHttp2ErrorCode(uint32_t wire_error_code) {
120   if (wire_error_code > static_cast<int>(Http2ErrorCode::MAX_ERROR_CODE)) {
121     return Http2ErrorCode::INTERNAL_ERROR;
122   }
123   return static_cast<Http2ErrorCode>(wire_error_code);
124 }
125 
ToNgHttp2ErrorCode(InvalidFrameError error)126 int ToNgHttp2ErrorCode(InvalidFrameError error) {
127   switch (error) {
128     case InvalidFrameError::kProtocol:
129       return NGHTTP2_ERR_PROTO;
130     case InvalidFrameError::kRefusedStream:
131       return NGHTTP2_ERR_REFUSED_STREAM;
132     case InvalidFrameError::kHttpHeader:
133       return NGHTTP2_ERR_HTTP_HEADER;
134     case InvalidFrameError::kHttpMessaging:
135       return NGHTTP2_ERR_HTTP_MESSAGING;
136     case InvalidFrameError::kFlowControl:
137       return NGHTTP2_ERR_FLOW_CONTROL;
138     case InvalidFrameError::kStreamClosed:
139       return NGHTTP2_ERR_STREAM_CLOSED;
140   }
141   return NGHTTP2_ERR_PROTO;
142 }
143 
ToInvalidFrameError(int error)144 InvalidFrameError ToInvalidFrameError(int error) {
145   switch (error) {
146     case NGHTTP2_ERR_PROTO:
147       return InvalidFrameError::kProtocol;
148     case NGHTTP2_ERR_REFUSED_STREAM:
149       return InvalidFrameError::kRefusedStream;
150     case NGHTTP2_ERR_HTTP_HEADER:
151       return InvalidFrameError::kHttpHeader;
152     case NGHTTP2_ERR_HTTP_MESSAGING:
153       return InvalidFrameError::kHttpMessaging;
154     case NGHTTP2_ERR_FLOW_CONTROL:
155       return InvalidFrameError::kFlowControl;
156     case NGHTTP2_ERR_STREAM_CLOSED:
157       return InvalidFrameError::kStreamClosed;
158   }
159   return InvalidFrameError::kProtocol;
160 }
161 
162 class Nghttp2DataFrameSource : public DataFrameSource {
163  public:
Nghttp2DataFrameSource(nghttp2_data_provider provider,nghttp2_send_data_callback send_data,void * user_data)164   Nghttp2DataFrameSource(nghttp2_data_provider provider,
165                          nghttp2_send_data_callback send_data, void* user_data)
166       : provider_(std::move(provider)),
167         send_data_(std::move(send_data)),
168         user_data_(user_data) {}
169 
SelectPayloadLength(size_t max_length)170   std::pair<int64_t, bool> SelectPayloadLength(size_t max_length) override {
171     const int32_t stream_id = 0;
172     uint32_t data_flags = 0;
173     int64_t result = provider_.read_callback(
174         nullptr /* session */, stream_id, nullptr /* buf */, max_length,
175         &data_flags, &provider_.source, nullptr /* user_data */);
176     if (result == NGHTTP2_ERR_DEFERRED) {
177       return {kBlocked, false};
178     } else if (result < 0) {
179       return {kError, false};
180     } else if ((data_flags & NGHTTP2_DATA_FLAG_NO_COPY) == 0) {
181       QUICHE_LOG(ERROR) << "Source did not use the zero-copy API!";
182       return {kError, false};
183     } else {
184       const bool eof = data_flags & NGHTTP2_DATA_FLAG_EOF;
185       if (eof && (data_flags & NGHTTP2_DATA_FLAG_NO_END_STREAM) == 0) {
186         send_fin_ = true;
187       }
188       return {result, eof};
189     }
190   }
191 
Send(absl::string_view frame_header,size_t payload_length)192   bool Send(absl::string_view frame_header, size_t payload_length) override {
193     nghttp2_frame frame;
194     frame.hd.type = 0;
195     frame.hd.length = payload_length;
196     frame.hd.flags = 0;
197     frame.hd.stream_id = 0;
198     frame.data.padlen = 0;
199     const int result = send_data_(
200         nullptr /* session */, &frame, ToUint8Ptr(frame_header.data()),
201         payload_length, &provider_.source, user_data_);
202     QUICHE_LOG_IF(ERROR, result < 0 && result != NGHTTP2_ERR_WOULDBLOCK)
203         << "Unexpected error code from send: " << result;
204     return result == 0;
205   }
206 
send_fin() const207   bool send_fin() const override { return send_fin_; }
208 
209  private:
210   nghttp2_data_provider provider_;
211   nghttp2_send_data_callback send_data_;
212   void* user_data_;
213   bool send_fin_ = false;
214 };
215 
MakeZeroCopyDataFrameSource(nghttp2_data_provider provider,void * user_data,nghttp2_send_data_callback send_data)216 std::unique_ptr<DataFrameSource> MakeZeroCopyDataFrameSource(
217     nghttp2_data_provider provider, void* user_data,
218     nghttp2_send_data_callback send_data) {
219   return std::make_unique<Nghttp2DataFrameSource>(
220       std::move(provider), std::move(send_data), user_data);
221 }
222 
ErrorString(uint32_t error_code)223 absl::string_view ErrorString(uint32_t error_code) {
224   return Http2ErrorCodeToString(static_cast<Http2ErrorCode>(error_code));
225 }
226 
PaddingLength(uint8_t flags,size_t padlen)227 size_t PaddingLength(uint8_t flags, size_t padlen) {
228   return (flags & PADDED_FLAG ? 1 : 0) + padlen;
229 }
230 
231 struct NvFormatter {
operator ()http2::adapter::NvFormatter232   void operator()(std::string* out, const nghttp2_nv& nv) {
233     absl::StrAppend(out, ToStringView(nv.name, nv.namelen), ": ",
234                     ToStringView(nv.value, nv.valuelen));
235   }
236 };
237 
NvsAsString(nghttp2_nv * nva,size_t nvlen)238 std::string NvsAsString(nghttp2_nv* nva, size_t nvlen) {
239   return absl::StrJoin(absl::MakeConstSpan(nva, nvlen), ", ", NvFormatter());
240 }
241 
242 #define HTTP2_FRAME_SEND_LOG QUICHE_VLOG(1)
243 
LogBeforeSend(const nghttp2_frame & frame)244 void LogBeforeSend(const nghttp2_frame& frame) {
245   switch (static_cast<FrameType>(frame.hd.type)) {
246     case FrameType::DATA:
247       HTTP2_FRAME_SEND_LOG << "Sending DATA on stream " << frame.hd.stream_id
248                            << " with length "
249                            << frame.hd.length - PaddingLength(frame.hd.flags,
250                                                               frame.data.padlen)
251                            << " and padding "
252                            << PaddingLength(frame.hd.flags, frame.data.padlen);
253       break;
254     case FrameType::HEADERS:
255       HTTP2_FRAME_SEND_LOG << "Sending HEADERS on stream " << frame.hd.stream_id
256                            << " with headers ["
257                            << NvsAsString(frame.headers.nva,
258                                           frame.headers.nvlen)
259                            << "]";
260       break;
261     case FrameType::PRIORITY:
262       HTTP2_FRAME_SEND_LOG << "Sending PRIORITY";
263       break;
264     case FrameType::RST_STREAM:
265       HTTP2_FRAME_SEND_LOG << "Sending RST_STREAM on stream "
266                            << frame.hd.stream_id << " with error code "
267                            << ErrorString(frame.rst_stream.error_code);
268       break;
269     case FrameType::SETTINGS:
270       HTTP2_FRAME_SEND_LOG << "Sending SETTINGS with " << frame.settings.niv
271                            << " entries, is_ack: "
272                            << (frame.hd.flags & ACK_FLAG);
273       break;
274     case FrameType::PUSH_PROMISE:
275       HTTP2_FRAME_SEND_LOG << "Sending PUSH_PROMISE";
276       break;
277     case FrameType::PING: {
278       Http2PingId ping_id;
279       std::memcpy(&ping_id, frame.ping.opaque_data, sizeof(Http2PingId));
280       HTTP2_FRAME_SEND_LOG << "Sending PING with unique_id "
281                            << quiche::QuicheEndian::NetToHost64(ping_id)
282                            << ", is_ack: " << (frame.hd.flags & ACK_FLAG);
283       break;
284     }
285     case FrameType::GOAWAY:
286       HTTP2_FRAME_SEND_LOG << "Sending GOAWAY with last_stream: "
287                            << frame.goaway.last_stream_id << " and error "
288                            << ErrorString(frame.goaway.error_code);
289       break;
290     case FrameType::WINDOW_UPDATE:
291       HTTP2_FRAME_SEND_LOG << "Sending WINDOW_UPDATE on stream "
292                            << frame.hd.stream_id << " with update delta "
293                            << frame.window_update.window_size_increment;
294       break;
295     case FrameType::CONTINUATION:
296       HTTP2_FRAME_SEND_LOG << "Sending CONTINUATION, which is unexpected";
297       break;
298   }
299 }
300 
301 #undef HTTP2_FRAME_SEND_LOG
302 
303 }  // namespace adapter
304 }  // namespace http2
305