xref: /aosp_15_r20/external/cronet/net/third_party/quiche/src/quiche/http2/adapter/nghttp2_adapter.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 #include "quiche/http2/adapter/nghttp2_adapter.h"
2 
3 #include <memory>
4 
5 #include "absl/algorithm/container.h"
6 #include "absl/strings/str_cat.h"
7 #include "absl/strings/string_view.h"
8 #include "quiche/http2/adapter/http2_visitor_interface.h"
9 #include "quiche/http2/adapter/nghttp2.h"
10 #include "quiche/http2/adapter/nghttp2_callbacks.h"
11 #include "quiche/http2/adapter/nghttp2_data_provider.h"
12 #include "quiche/common/platform/api/quiche_logging.h"
13 #include "quiche/common/quiche_endian.h"
14 
15 namespace http2 {
16 namespace adapter {
17 
18 namespace {
19 
20 using ConnectionError = Http2VisitorInterface::ConnectionError;
21 
22 }  // anonymous namespace
23 
24 // A metadata source that notifies the owning NgHttp2Adapter upon completion or
25 // failure.
26 class NgHttp2Adapter::NotifyingMetadataSource : public MetadataSource {
27  public:
NotifyingMetadataSource(NgHttp2Adapter * adapter,Http2StreamId stream_id,std::unique_ptr<MetadataSource> source)28   explicit NotifyingMetadataSource(NgHttp2Adapter* adapter,
29                                    Http2StreamId stream_id,
30                                    std::unique_ptr<MetadataSource> source)
31       : adapter_(adapter), stream_id_(stream_id), source_(std::move(source)) {}
32 
NumFrames(size_t max_frame_size) const33   size_t NumFrames(size_t max_frame_size) const override {
34     return source_->NumFrames(max_frame_size);
35   }
36 
Pack(uint8_t * dest,size_t dest_len)37   std::pair<int64_t, bool> Pack(uint8_t* dest, size_t dest_len) override {
38     const auto result = source_->Pack(dest, dest_len);
39     if (result.first < 0 || result.second) {
40       adapter_->RemovePendingMetadata(stream_id_);
41     }
42     return result;
43   }
44 
OnFailure()45   void OnFailure() override {
46     source_->OnFailure();
47     adapter_->RemovePendingMetadata(stream_id_);
48   }
49 
50  private:
51   NgHttp2Adapter* const adapter_;
52   const Http2StreamId stream_id_;
53   std::unique_ptr<MetadataSource> source_;
54 };
55 
56 /* static */
CreateClientAdapter(Http2VisitorInterface & visitor,const nghttp2_option * options)57 std::unique_ptr<NgHttp2Adapter> NgHttp2Adapter::CreateClientAdapter(
58     Http2VisitorInterface& visitor, const nghttp2_option* options) {
59   auto adapter = new NgHttp2Adapter(visitor, Perspective::kClient, options);
60   adapter->Initialize();
61   return absl::WrapUnique(adapter);
62 }
63 
64 /* static */
CreateServerAdapter(Http2VisitorInterface & visitor,const nghttp2_option * options)65 std::unique_ptr<NgHttp2Adapter> NgHttp2Adapter::CreateServerAdapter(
66     Http2VisitorInterface& visitor, const nghttp2_option* options) {
67   auto adapter = new NgHttp2Adapter(visitor, Perspective::kServer, options);
68   adapter->Initialize();
69   return absl::WrapUnique(adapter);
70 }
71 
IsServerSession() const72 bool NgHttp2Adapter::IsServerSession() const {
73   int result = nghttp2_session_check_server_session(session_->raw_ptr());
74   QUICHE_DCHECK_EQ(perspective_ == Perspective::kServer, result > 0);
75   return result > 0;
76 }
77 
ProcessBytes(absl::string_view bytes)78 int64_t NgHttp2Adapter::ProcessBytes(absl::string_view bytes) {
79   const int64_t processed_bytes = session_->ProcessBytes(bytes);
80   if (processed_bytes < 0) {
81     visitor_.OnConnectionError(ConnectionError::kParseError);
82   }
83   return processed_bytes;
84 }
85 
SubmitSettings(absl::Span<const Http2Setting> settings)86 void NgHttp2Adapter::SubmitSettings(absl::Span<const Http2Setting> settings) {
87   // Submit SETTINGS, converting each Http2Setting to an nghttp2_settings_entry.
88   std::vector<nghttp2_settings_entry> nghttp2_settings;
89   absl::c_transform(settings, std::back_inserter(nghttp2_settings),
90                     [](const Http2Setting& setting) {
91                       return nghttp2_settings_entry{setting.id, setting.value};
92                     });
93   nghttp2_submit_settings(session_->raw_ptr(), NGHTTP2_FLAG_NONE,
94                           nghttp2_settings.data(), nghttp2_settings.size());
95 }
96 
SubmitPriorityForStream(Http2StreamId stream_id,Http2StreamId parent_stream_id,int weight,bool exclusive)97 void NgHttp2Adapter::SubmitPriorityForStream(Http2StreamId stream_id,
98                                              Http2StreamId parent_stream_id,
99                                              int weight, bool exclusive) {
100   nghttp2_priority_spec priority_spec;
101   nghttp2_priority_spec_init(&priority_spec, parent_stream_id, weight,
102                              static_cast<int>(exclusive));
103   nghttp2_submit_priority(session_->raw_ptr(), NGHTTP2_FLAG_NONE, stream_id,
104                           &priority_spec);
105 }
106 
SubmitPing(Http2PingId ping_id)107 void NgHttp2Adapter::SubmitPing(Http2PingId ping_id) {
108   uint8_t opaque_data[8] = {};
109   Http2PingId ping_id_to_serialize = quiche::QuicheEndian::HostToNet64(ping_id);
110   std::memcpy(opaque_data, &ping_id_to_serialize, sizeof(Http2PingId));
111   nghttp2_submit_ping(session_->raw_ptr(), NGHTTP2_FLAG_NONE, opaque_data);
112 }
113 
SubmitShutdownNotice()114 void NgHttp2Adapter::SubmitShutdownNotice() {
115   nghttp2_submit_shutdown_notice(session_->raw_ptr());
116 }
117 
SubmitGoAway(Http2StreamId last_accepted_stream_id,Http2ErrorCode error_code,absl::string_view opaque_data)118 void NgHttp2Adapter::SubmitGoAway(Http2StreamId last_accepted_stream_id,
119                                   Http2ErrorCode error_code,
120                                   absl::string_view opaque_data) {
121   nghttp2_submit_goaway(session_->raw_ptr(), NGHTTP2_FLAG_NONE,
122                         last_accepted_stream_id,
123                         static_cast<uint32_t>(error_code),
124                         ToUint8Ptr(opaque_data.data()), opaque_data.size());
125 }
126 
SubmitWindowUpdate(Http2StreamId stream_id,int window_increment)127 void NgHttp2Adapter::SubmitWindowUpdate(Http2StreamId stream_id,
128                                         int window_increment) {
129   nghttp2_submit_window_update(session_->raw_ptr(), NGHTTP2_FLAG_NONE,
130                                stream_id, window_increment);
131 }
132 
SubmitMetadata(Http2StreamId stream_id,size_t max_frame_size,std::unique_ptr<MetadataSource> source)133 void NgHttp2Adapter::SubmitMetadata(Http2StreamId stream_id,
134                                     size_t max_frame_size,
135                                     std::unique_ptr<MetadataSource> source) {
136   auto wrapped_source = std::make_unique<NotifyingMetadataSource>(
137       this, stream_id, std::move(source));
138   const size_t num_frames = wrapped_source->NumFrames(max_frame_size);
139   size_t num_successes = 0;
140   for (size_t i = 1; i <= num_frames; ++i) {
141     const int result =
142         nghttp2_submit_extension(session_->raw_ptr(), kMetadataFrameType,
143                                  i == num_frames ? kMetadataEndFlag : 0,
144                                  stream_id, wrapped_source.get());
145     if (result != 0) {
146       QUICHE_LOG(DFATAL) << "Failed to submit extension frame " << i << " of "
147                          << num_frames;
148       break;
149     }
150     ++num_successes;
151   }
152   if (num_successes > 0) {
153     // Finds the MetadataSourceVec for `stream_id` or inserts a new one if not
154     // present.
155     auto [it, _] = stream_metadata_.insert({stream_id, MetadataSourceVec{}});
156     it->second.push_back(std::move(wrapped_source));
157   }
158 }
159 
Send()160 int NgHttp2Adapter::Send() {
161   const int result = nghttp2_session_send(session_->raw_ptr());
162   if (result != 0) {
163     QUICHE_VLOG(1) << "nghttp2_session_send returned " << result;
164     visitor_.OnConnectionError(ConnectionError::kSendError);
165   }
166   return result;
167 }
168 
GetSendWindowSize() const169 int NgHttp2Adapter::GetSendWindowSize() const {
170   return session_->GetRemoteWindowSize();
171 }
172 
GetStreamSendWindowSize(Http2StreamId stream_id) const173 int NgHttp2Adapter::GetStreamSendWindowSize(Http2StreamId stream_id) const {
174   return nghttp2_session_get_stream_remote_window_size(session_->raw_ptr(),
175                                                        stream_id);
176 }
177 
GetStreamReceiveWindowLimit(Http2StreamId stream_id) const178 int NgHttp2Adapter::GetStreamReceiveWindowLimit(Http2StreamId stream_id) const {
179   return nghttp2_session_get_stream_effective_local_window_size(
180       session_->raw_ptr(), stream_id);
181 }
182 
GetStreamReceiveWindowSize(Http2StreamId stream_id) const183 int NgHttp2Adapter::GetStreamReceiveWindowSize(Http2StreamId stream_id) const {
184   return nghttp2_session_get_stream_local_window_size(session_->raw_ptr(),
185                                                       stream_id);
186 }
187 
GetReceiveWindowSize() const188 int NgHttp2Adapter::GetReceiveWindowSize() const {
189   return nghttp2_session_get_local_window_size(session_->raw_ptr());
190 }
191 
GetHpackEncoderDynamicTableSize() const192 int NgHttp2Adapter::GetHpackEncoderDynamicTableSize() const {
193   return nghttp2_session_get_hd_deflate_dynamic_table_size(session_->raw_ptr());
194 }
195 
GetHpackDecoderDynamicTableSize() const196 int NgHttp2Adapter::GetHpackDecoderDynamicTableSize() const {
197   return nghttp2_session_get_hd_inflate_dynamic_table_size(session_->raw_ptr());
198 }
199 
GetHighestReceivedStreamId() const200 Http2StreamId NgHttp2Adapter::GetHighestReceivedStreamId() const {
201   return nghttp2_session_get_last_proc_stream_id(session_->raw_ptr());
202 }
203 
MarkDataConsumedForStream(Http2StreamId stream_id,size_t num_bytes)204 void NgHttp2Adapter::MarkDataConsumedForStream(Http2StreamId stream_id,
205                                                size_t num_bytes) {
206   int rc = session_->Consume(stream_id, num_bytes);
207   if (rc != 0) {
208     QUICHE_LOG(ERROR) << "Error " << rc << " marking " << num_bytes
209                       << " bytes consumed for stream " << stream_id;
210   }
211 }
212 
SubmitRst(Http2StreamId stream_id,Http2ErrorCode error_code)213 void NgHttp2Adapter::SubmitRst(Http2StreamId stream_id,
214                                Http2ErrorCode error_code) {
215   int status =
216       nghttp2_submit_rst_stream(session_->raw_ptr(), NGHTTP2_FLAG_NONE,
217                                 stream_id, static_cast<uint32_t>(error_code));
218   if (status < 0) {
219     QUICHE_LOG(WARNING) << "Reset stream failed: " << stream_id
220                         << " with status code " << status;
221   }
222 }
223 
SubmitRequest(absl::Span<const Header> headers,std::unique_ptr<DataFrameSource> data_source,void * stream_user_data)224 int32_t NgHttp2Adapter::SubmitRequest(
225     absl::Span<const Header> headers,
226     std::unique_ptr<DataFrameSource> data_source, void* stream_user_data) {
227   auto nvs = GetNghttp2Nvs(headers);
228   std::unique_ptr<nghttp2_data_provider> provider =
229       MakeDataProvider(data_source.get());
230 
231   int32_t stream_id =
232       nghttp2_submit_request(session_->raw_ptr(), nullptr, nvs.data(),
233                              nvs.size(), provider.get(), stream_user_data);
234   sources_.emplace(stream_id, std::move(data_source));
235   QUICHE_VLOG(1) << "Submitted request with " << nvs.size()
236                  << " request headers and user data " << stream_user_data
237                  << "; resulted in stream " << stream_id;
238   return stream_id;
239 }
240 
SubmitResponse(Http2StreamId stream_id,absl::Span<const Header> headers,std::unique_ptr<DataFrameSource> data_source)241 int NgHttp2Adapter::SubmitResponse(
242     Http2StreamId stream_id, absl::Span<const Header> headers,
243     std::unique_ptr<DataFrameSource> data_source) {
244   auto nvs = GetNghttp2Nvs(headers);
245   std::unique_ptr<nghttp2_data_provider> provider =
246       MakeDataProvider(data_source.get());
247 
248   sources_.emplace(stream_id, std::move(data_source));
249 
250   int result = nghttp2_submit_response(session_->raw_ptr(), stream_id,
251                                        nvs.data(), nvs.size(), provider.get());
252   QUICHE_VLOG(1) << "Submitted response with " << nvs.size()
253                  << " response headers; result = " << result;
254   return result;
255 }
256 
SubmitTrailer(Http2StreamId stream_id,absl::Span<const Header> trailers)257 int NgHttp2Adapter::SubmitTrailer(Http2StreamId stream_id,
258                                   absl::Span<const Header> trailers) {
259   auto nvs = GetNghttp2Nvs(trailers);
260   int result = nghttp2_submit_trailer(session_->raw_ptr(), stream_id,
261                                       nvs.data(), nvs.size());
262   QUICHE_VLOG(1) << "Submitted trailers with " << nvs.size()
263                  << " response trailers; result = " << result;
264   return result;
265 }
266 
SetStreamUserData(Http2StreamId stream_id,void * stream_user_data)267 void NgHttp2Adapter::SetStreamUserData(Http2StreamId stream_id,
268                                        void* stream_user_data) {
269   nghttp2_session_set_stream_user_data(session_->raw_ptr(), stream_id,
270                                        stream_user_data);
271 }
272 
GetStreamUserData(Http2StreamId stream_id)273 void* NgHttp2Adapter::GetStreamUserData(Http2StreamId stream_id) {
274   return nghttp2_session_get_stream_user_data(session_->raw_ptr(), stream_id);
275 }
276 
ResumeStream(Http2StreamId stream_id)277 bool NgHttp2Adapter::ResumeStream(Http2StreamId stream_id) {
278   return 0 == nghttp2_session_resume_data(session_->raw_ptr(), stream_id);
279 }
280 
FrameNotSent(Http2StreamId stream_id,uint8_t frame_type)281 void NgHttp2Adapter::FrameNotSent(Http2StreamId stream_id, uint8_t frame_type) {
282   if (frame_type == kMetadataFrameType) {
283     RemovePendingMetadata(stream_id);
284   }
285 }
286 
RemoveStream(Http2StreamId stream_id)287 void NgHttp2Adapter::RemoveStream(Http2StreamId stream_id) {
288   sources_.erase(stream_id);
289 }
290 
NgHttp2Adapter(Http2VisitorInterface & visitor,Perspective perspective,const nghttp2_option * options)291 NgHttp2Adapter::NgHttp2Adapter(Http2VisitorInterface& visitor,
292                                Perspective perspective,
293                                const nghttp2_option* options)
294     : Http2Adapter(visitor),
295       visitor_(visitor),
296       options_(options),
297       perspective_(perspective) {}
298 
~NgHttp2Adapter()299 NgHttp2Adapter::~NgHttp2Adapter() {}
300 
Initialize()301 void NgHttp2Adapter::Initialize() {
302   nghttp2_option* owned_options = nullptr;
303   if (options_ == nullptr) {
304     nghttp2_option_new(&owned_options);
305     // Set some common options for compatibility.
306     nghttp2_option_set_no_closed_streams(owned_options, 1);
307     nghttp2_option_set_no_auto_window_update(owned_options, 1);
308     nghttp2_option_set_max_send_header_block_length(owned_options, 0x2000000);
309     nghttp2_option_set_max_outbound_ack(owned_options, 10000);
310     nghttp2_option_set_user_recv_extension_type(owned_options,
311                                                 kMetadataFrameType);
312     options_ = owned_options;
313   }
314 
315   session_ =
316       std::make_unique<NgHttp2Session>(perspective_, callbacks::Create(),
317                                        options_, static_cast<void*>(&visitor_));
318   if (owned_options != nullptr) {
319     nghttp2_option_del(owned_options);
320   }
321   options_ = nullptr;
322 }
323 
RemovePendingMetadata(Http2StreamId stream_id)324 void NgHttp2Adapter::RemovePendingMetadata(Http2StreamId stream_id) {
325   auto it = stream_metadata_.find(stream_id);
326   if (it != stream_metadata_.end()) {
327     it->second.erase(it->second.begin());
328     if (it->second.empty()) {
329       stream_metadata_.erase(it);
330     }
331   }
332 }
333 
334 }  // namespace adapter
335 }  // namespace http2
336