• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 // A metadata source that deletes itself upon completion.
23 class SelfDeletingMetadataSource : public MetadataSource {
24  public:
SelfDeletingMetadataSource(std::unique_ptr<MetadataSource> source)25   explicit SelfDeletingMetadataSource(std::unique_ptr<MetadataSource> source)
26       : source_(std::move(source)) {}
27 
NumFrames(size_t max_frame_size) const28   size_t NumFrames(size_t max_frame_size) const override {
29     return source_->NumFrames(max_frame_size);
30   }
31 
Pack(uint8_t * dest,size_t dest_len)32   std::pair<int64_t, bool> Pack(uint8_t* dest, size_t dest_len) override {
33     const auto result = source_->Pack(dest, dest_len);
34     if (result.first < 0 || result.second) {
35       delete this;
36     }
37     return result;
38   }
39 
OnFailure()40   void OnFailure() override {
41     source_->OnFailure();
42     delete this;
43   }
44 
45  private:
46   std::unique_ptr<MetadataSource> source_;
47 };
48 
49 }  // anonymous namespace
50 
51 /* static */
CreateClientAdapter(Http2VisitorInterface & visitor,const nghttp2_option * options)52 std::unique_ptr<NgHttp2Adapter> NgHttp2Adapter::CreateClientAdapter(
53     Http2VisitorInterface& visitor, const nghttp2_option* options) {
54   auto adapter = new NgHttp2Adapter(visitor, Perspective::kClient, options);
55   adapter->Initialize();
56   return absl::WrapUnique(adapter);
57 }
58 
59 /* static */
CreateServerAdapter(Http2VisitorInterface & visitor,const nghttp2_option * options)60 std::unique_ptr<NgHttp2Adapter> NgHttp2Adapter::CreateServerAdapter(
61     Http2VisitorInterface& visitor, const nghttp2_option* options) {
62   auto adapter = new NgHttp2Adapter(visitor, Perspective::kServer, options);
63   adapter->Initialize();
64   return absl::WrapUnique(adapter);
65 }
66 
IsServerSession() const67 bool NgHttp2Adapter::IsServerSession() const {
68   int result = nghttp2_session_check_server_session(session_->raw_ptr());
69   QUICHE_DCHECK_EQ(perspective_ == Perspective::kServer, result > 0);
70   return result > 0;
71 }
72 
ProcessBytes(absl::string_view bytes)73 int64_t NgHttp2Adapter::ProcessBytes(absl::string_view bytes) {
74   const int64_t processed_bytes = session_->ProcessBytes(bytes);
75   if (processed_bytes < 0) {
76     visitor_.OnConnectionError(ConnectionError::kParseError);
77   }
78   return processed_bytes;
79 }
80 
SubmitSettings(absl::Span<const Http2Setting> settings)81 void NgHttp2Adapter::SubmitSettings(absl::Span<const Http2Setting> settings) {
82   // Submit SETTINGS, converting each Http2Setting to an nghttp2_settings_entry.
83   std::vector<nghttp2_settings_entry> nghttp2_settings;
84   absl::c_transform(settings, std::back_inserter(nghttp2_settings),
85                     [](const Http2Setting& setting) {
86                       return nghttp2_settings_entry{setting.id, setting.value};
87                     });
88   nghttp2_submit_settings(session_->raw_ptr(), NGHTTP2_FLAG_NONE,
89                           nghttp2_settings.data(), nghttp2_settings.size());
90 }
91 
SubmitPriorityForStream(Http2StreamId stream_id,Http2StreamId parent_stream_id,int weight,bool exclusive)92 void NgHttp2Adapter::SubmitPriorityForStream(Http2StreamId stream_id,
93                                              Http2StreamId parent_stream_id,
94                                              int weight, bool exclusive) {
95   nghttp2_priority_spec priority_spec;
96   nghttp2_priority_spec_init(&priority_spec, parent_stream_id, weight,
97                              static_cast<int>(exclusive));
98   nghttp2_submit_priority(session_->raw_ptr(), NGHTTP2_FLAG_NONE, stream_id,
99                           &priority_spec);
100 }
101 
SubmitPing(Http2PingId ping_id)102 void NgHttp2Adapter::SubmitPing(Http2PingId ping_id) {
103   uint8_t opaque_data[8] = {};
104   Http2PingId ping_id_to_serialize = quiche::QuicheEndian::HostToNet64(ping_id);
105   std::memcpy(opaque_data, &ping_id_to_serialize, sizeof(Http2PingId));
106   nghttp2_submit_ping(session_->raw_ptr(), NGHTTP2_FLAG_NONE, opaque_data);
107 }
108 
SubmitShutdownNotice()109 void NgHttp2Adapter::SubmitShutdownNotice() {
110   nghttp2_submit_shutdown_notice(session_->raw_ptr());
111 }
112 
SubmitGoAway(Http2StreamId last_accepted_stream_id,Http2ErrorCode error_code,absl::string_view opaque_data)113 void NgHttp2Adapter::SubmitGoAway(Http2StreamId last_accepted_stream_id,
114                                   Http2ErrorCode error_code,
115                                   absl::string_view opaque_data) {
116   nghttp2_submit_goaway(session_->raw_ptr(), NGHTTP2_FLAG_NONE,
117                         last_accepted_stream_id,
118                         static_cast<uint32_t>(error_code),
119                         ToUint8Ptr(opaque_data.data()), opaque_data.size());
120 }
121 
SubmitWindowUpdate(Http2StreamId stream_id,int window_increment)122 void NgHttp2Adapter::SubmitWindowUpdate(Http2StreamId stream_id,
123                                         int window_increment) {
124   nghttp2_submit_window_update(session_->raw_ptr(), NGHTTP2_FLAG_NONE,
125                                stream_id, window_increment);
126 }
127 
SubmitMetadata(Http2StreamId stream_id,size_t max_frame_size,std::unique_ptr<MetadataSource> source)128 void NgHttp2Adapter::SubmitMetadata(Http2StreamId stream_id,
129                                     size_t max_frame_size,
130                                     std::unique_ptr<MetadataSource> source) {
131   auto* wrapped_source = new SelfDeletingMetadataSource(std::move(source));
132   const size_t num_frames = wrapped_source->NumFrames(max_frame_size);
133   size_t num_successes = 0;
134   for (size_t i = 1; i <= num_frames; ++i) {
135     const int result = nghttp2_submit_extension(
136         session_->raw_ptr(), kMetadataFrameType,
137         i == num_frames ? kMetadataEndFlag : 0, stream_id, wrapped_source);
138     if (result != 0) {
139       QUICHE_LOG(DFATAL) << "Failed to submit extension frame " << i << " of "
140                          << num_frames;
141       break;
142     }
143     ++num_successes;
144   }
145   if (num_successes == 0) {
146     delete wrapped_source;
147   }
148 }
149 
Send()150 int NgHttp2Adapter::Send() {
151   const int result = nghttp2_session_send(session_->raw_ptr());
152   if (result != 0) {
153     QUICHE_VLOG(1) << "nghttp2_session_send returned " << result;
154     visitor_.OnConnectionError(ConnectionError::kSendError);
155   }
156   return result;
157 }
158 
GetSendWindowSize() const159 int NgHttp2Adapter::GetSendWindowSize() const {
160   return session_->GetRemoteWindowSize();
161 }
162 
GetStreamSendWindowSize(Http2StreamId stream_id) const163 int NgHttp2Adapter::GetStreamSendWindowSize(Http2StreamId stream_id) const {
164   return nghttp2_session_get_stream_remote_window_size(session_->raw_ptr(),
165                                                        stream_id);
166 }
167 
GetStreamReceiveWindowLimit(Http2StreamId stream_id) const168 int NgHttp2Adapter::GetStreamReceiveWindowLimit(Http2StreamId stream_id) const {
169   return nghttp2_session_get_stream_effective_local_window_size(
170       session_->raw_ptr(), stream_id);
171 }
172 
GetStreamReceiveWindowSize(Http2StreamId stream_id) const173 int NgHttp2Adapter::GetStreamReceiveWindowSize(Http2StreamId stream_id) const {
174   return nghttp2_session_get_stream_local_window_size(session_->raw_ptr(),
175                                                       stream_id);
176 }
177 
GetReceiveWindowSize() const178 int NgHttp2Adapter::GetReceiveWindowSize() const {
179   return nghttp2_session_get_local_window_size(session_->raw_ptr());
180 }
181 
GetHpackEncoderDynamicTableSize() const182 int NgHttp2Adapter::GetHpackEncoderDynamicTableSize() const {
183   return nghttp2_session_get_hd_deflate_dynamic_table_size(session_->raw_ptr());
184 }
185 
GetHpackDecoderDynamicTableSize() const186 int NgHttp2Adapter::GetHpackDecoderDynamicTableSize() const {
187   return nghttp2_session_get_hd_inflate_dynamic_table_size(session_->raw_ptr());
188 }
189 
GetHighestReceivedStreamId() const190 Http2StreamId NgHttp2Adapter::GetHighestReceivedStreamId() const {
191   return nghttp2_session_get_last_proc_stream_id(session_->raw_ptr());
192 }
193 
MarkDataConsumedForStream(Http2StreamId stream_id,size_t num_bytes)194 void NgHttp2Adapter::MarkDataConsumedForStream(Http2StreamId stream_id,
195                                                size_t num_bytes) {
196   int rc = session_->Consume(stream_id, num_bytes);
197   if (rc != 0) {
198     QUICHE_LOG(ERROR) << "Error " << rc << " marking " << num_bytes
199                       << " bytes consumed for stream " << stream_id;
200   }
201 }
202 
SubmitRst(Http2StreamId stream_id,Http2ErrorCode error_code)203 void NgHttp2Adapter::SubmitRst(Http2StreamId stream_id,
204                                Http2ErrorCode error_code) {
205   int status =
206       nghttp2_submit_rst_stream(session_->raw_ptr(), NGHTTP2_FLAG_NONE,
207                                 stream_id, static_cast<uint32_t>(error_code));
208   if (status < 0) {
209     QUICHE_LOG(WARNING) << "Reset stream failed: " << stream_id
210                         << " with status code " << status;
211   }
212 }
213 
SubmitRequest(absl::Span<const Header> headers,std::unique_ptr<DataFrameSource> data_source,void * stream_user_data)214 int32_t NgHttp2Adapter::SubmitRequest(
215     absl::Span<const Header> headers,
216     std::unique_ptr<DataFrameSource> data_source, void* stream_user_data) {
217   auto nvs = GetNghttp2Nvs(headers);
218   std::unique_ptr<nghttp2_data_provider> provider =
219       MakeDataProvider(data_source.get());
220 
221   int32_t stream_id =
222       nghttp2_submit_request(session_->raw_ptr(), nullptr, nvs.data(),
223                              nvs.size(), provider.get(), stream_user_data);
224   sources_.emplace(stream_id, std::move(data_source));
225   QUICHE_VLOG(1) << "Submitted request with " << nvs.size()
226                  << " request headers and user data " << stream_user_data
227                  << "; resulted in stream " << stream_id;
228   return stream_id;
229 }
230 
SubmitResponse(Http2StreamId stream_id,absl::Span<const Header> headers,std::unique_ptr<DataFrameSource> data_source)231 int NgHttp2Adapter::SubmitResponse(
232     Http2StreamId stream_id, absl::Span<const Header> headers,
233     std::unique_ptr<DataFrameSource> data_source) {
234   auto nvs = GetNghttp2Nvs(headers);
235   std::unique_ptr<nghttp2_data_provider> provider =
236       MakeDataProvider(data_source.get());
237 
238   sources_.emplace(stream_id, std::move(data_source));
239 
240   int result = nghttp2_submit_response(session_->raw_ptr(), stream_id,
241                                        nvs.data(), nvs.size(), provider.get());
242   QUICHE_VLOG(1) << "Submitted response with " << nvs.size()
243                  << " response headers; result = " << result;
244   return result;
245 }
246 
SubmitTrailer(Http2StreamId stream_id,absl::Span<const Header> trailers)247 int NgHttp2Adapter::SubmitTrailer(Http2StreamId stream_id,
248                                   absl::Span<const Header> trailers) {
249   auto nvs = GetNghttp2Nvs(trailers);
250   int result = nghttp2_submit_trailer(session_->raw_ptr(), stream_id,
251                                       nvs.data(), nvs.size());
252   QUICHE_VLOG(1) << "Submitted trailers with " << nvs.size()
253                  << " response trailers; result = " << result;
254   return result;
255 }
256 
SetStreamUserData(Http2StreamId stream_id,void * stream_user_data)257 void NgHttp2Adapter::SetStreamUserData(Http2StreamId stream_id,
258                                        void* stream_user_data) {
259   nghttp2_session_set_stream_user_data(session_->raw_ptr(), stream_id,
260                                        stream_user_data);
261 }
262 
GetStreamUserData(Http2StreamId stream_id)263 void* NgHttp2Adapter::GetStreamUserData(Http2StreamId stream_id) {
264   return nghttp2_session_get_stream_user_data(session_->raw_ptr(), stream_id);
265 }
266 
ResumeStream(Http2StreamId stream_id)267 bool NgHttp2Adapter::ResumeStream(Http2StreamId stream_id) {
268   return 0 == nghttp2_session_resume_data(session_->raw_ptr(), stream_id);
269 }
270 
RemoveStream(Http2StreamId stream_id)271 void NgHttp2Adapter::RemoveStream(Http2StreamId stream_id) {
272   sources_.erase(stream_id);
273 }
274 
NgHttp2Adapter(Http2VisitorInterface & visitor,Perspective perspective,const nghttp2_option * options)275 NgHttp2Adapter::NgHttp2Adapter(Http2VisitorInterface& visitor,
276                                Perspective perspective,
277                                const nghttp2_option* options)
278     : Http2Adapter(visitor),
279       visitor_(visitor),
280       options_(options),
281       perspective_(perspective) {}
282 
~NgHttp2Adapter()283 NgHttp2Adapter::~NgHttp2Adapter() {}
284 
Initialize()285 void NgHttp2Adapter::Initialize() {
286   nghttp2_option* owned_options = nullptr;
287   if (options_ == nullptr) {
288     nghttp2_option_new(&owned_options);
289     // Set some common options for compatibility.
290     nghttp2_option_set_no_closed_streams(owned_options, 1);
291     nghttp2_option_set_no_auto_window_update(owned_options, 1);
292     nghttp2_option_set_max_send_header_block_length(owned_options, 0x2000000);
293     nghttp2_option_set_max_outbound_ack(owned_options, 10000);
294     nghttp2_option_set_user_recv_extension_type(owned_options,
295                                                 kMetadataFrameType);
296     options_ = owned_options;
297   }
298 
299   session_ =
300       std::make_unique<NgHttp2Session>(perspective_, callbacks::Create(),
301                                        options_, static_cast<void*>(&visitor_));
302   if (owned_options != nullptr) {
303     nghttp2_option_del(owned_options);
304   }
305   options_ = nullptr;
306 }
307 
308 }  // namespace adapter
309 }  // namespace http2
310