@fails-components/webtransport-transport-http3-quiche 1.0.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/CMakeLists.txt +978 -0
  2. package/LICENSE +39 -0
  3. package/LICENSE.chromium +27 -0
  4. package/LICENSE.envoy +207 -0
  5. package/build.js +315 -0
  6. package/lib/clientsocket.js +80 -0
  7. package/lib/index.js +47 -0
  8. package/lib/serversocket.js +80 -0
  9. package/lib/socket.js +118 -0
  10. package/lib/types.ts +15 -0
  11. package/lib/utils.js +12 -0
  12. package/package.json +68 -0
  13. package/platform/icufix/fakedata.cpp +8 -0
  14. package/platform/quiche/quic/core/io/socket_win.inc +3 -0
  15. package/platform/quiche_platform_impl/quiche_bug_tracker_impl.h +7 -0
  16. package/platform/quiche_platform_impl/quiche_client_stats_impl.h +7 -0
  17. package/platform/quiche_platform_impl/quiche_command_line_flags_impl.h +7 -0
  18. package/platform/quiche_platform_impl/quiche_containers_impl.h +7 -0
  19. package/platform/quiche_platform_impl/quiche_event_loop_impl.h +7 -0
  20. package/platform/quiche_platform_impl/quiche_export_impl.h +6 -0
  21. package/platform/quiche_platform_impl/quiche_flag_utils_impl.h +11 -0
  22. package/platform/quiche_platform_impl/quiche_flags_impl.h +8 -0
  23. package/platform/quiche_platform_impl/quiche_googleurl_impl.h +8 -0
  24. package/platform/quiche_platform_impl/quiche_iovec_impl.h +10 -0
  25. package/platform/quiche_platform_impl/quiche_logging_impl.h +21 -0
  26. package/platform/quiche_platform_impl/quiche_mem_slice_impl.h +2 -0
  27. package/platform/quiche_platform_impl/quiche_mutex_impl.h +8 -0
  28. package/platform/quiche_platform_impl/quiche_prefetch_impl.h +11 -0
  29. package/platform/quiche_platform_impl/quiche_reference_counted_impl.h +16 -0
  30. package/platform/quiche_platform_impl/quiche_server_stats_impl.h +7 -0
  31. package/platform/quiche_platform_impl/quiche_stack_trace_impl.h +12 -0
  32. package/platform/quiche_platform_impl/quiche_stream_buffer_allocator_impl.h +8 -0
  33. package/platform/quiche_platform_impl/quiche_testvalue_impl.h +1 -0
  34. package/platform/quiche_platform_impl/quiche_thread_local_impl.h +6 -0
  35. package/platform/quiche_platform_impl/quiche_time_utils_impl.h +13 -0
  36. package/platform/quiche_platform_impl/quiche_udp_socket_platform_impl.h +39 -0
  37. package/readme.md +20 -0
  38. package/src/http3backendresponse.cc +36 -0
  39. package/src/http3backendresponse.h +109 -0
  40. package/src/http3client.cc +1402 -0
  41. package/src/http3client.h +531 -0
  42. package/src/http3clientsession.cc +65 -0
  43. package/src/http3clientsession.h +58 -0
  44. package/src/http3clientstream.cc +52 -0
  45. package/src/http3clientstream.h +42 -0
  46. package/src/http3dispatcher.cc +62 -0
  47. package/src/http3dispatcher.h +57 -0
  48. package/src/http3server.cc +529 -0
  49. package/src/http3server.h +149 -0
  50. package/src/http3serverbackend.cc +104 -0
  51. package/src/http3serverbackend.h +96 -0
  52. package/src/http3serversession.cc +123 -0
  53. package/src/http3serversession.h +95 -0
  54. package/src/http3serverstream.cc +463 -0
  55. package/src/http3serverstream.h +114 -0
  56. package/src/http3sessioncache.cc +85 -0
  57. package/src/http3sessioncache.h +59 -0
  58. package/src/http3wtsessionvisitor.cc +312 -0
  59. package/src/http3wtsessionvisitor.h +257 -0
  60. package/src/http3wtstreamvisitor.cc +286 -0
  61. package/src/http3wtstreamvisitor.h +323 -0
  62. package/src/librarymain.cc +86 -0
  63. package/src/librarymain.h +42 -0
  64. package/src/napialarmfactory.cc +51 -0
  65. package/src/napialarmfactory.h +165 -0
  66. package/src/socketjswriter.cc +46 -0
  67. package/src/socketjswriter.h +100 -0
@@ -0,0 +1,1402 @@
1
+ // Copyright (c) 2022 Marten Richter or other contributers (see commit). 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
+ // portions taken from libquiche or Chromium, original copyright, see LICENSE.chromium
6
+ // Copyright (c) 2012 The Chromium Authors. All rights reserved.
7
+ // Use of this source code is governed by a BSD-style license that can be
8
+ // found in the LICENSE file.
9
+
10
+ #include "src/http3client.h"
11
+ #include "src/http3clientsession.h"
12
+ #include "src/http3wtsessionvisitor.h"
13
+ #include "src/http3sessioncache.h"
14
+
15
+ #include <memory>
16
+ #include <utility>
17
+ #include <vector>
18
+
19
+ #include "absl/strings/match.h"
20
+ #include "absl/strings/string_view.h"
21
+ #include "absl/cleanup/cleanup.h"
22
+ #include "openssl/x509.h"
23
+ #include "quiche/quic/core/crypto/proof_verifier.h"
24
+ #include "quiche/quic/core/http/quic_spdy_client_stream.h"
25
+ #include "quiche/quic/core/http/spdy_utils.h"
26
+ #include "quiche/quic/core/http/web_transport_http3.h"
27
+ #include "quiche/quic/core/web_transport_interface.h"
28
+ #include "quiche/quic/core/quic_default_packet_writer.h"
29
+ #include "quiche/quic/core/quic_default_connection_helper.h"
30
+ #include "quiche/quic/core/quic_default_clock.h"
31
+ #include "quiche/quic/core/quic_packet_writer_wrapper.h"
32
+ #include "quiche/quic/core/quic_server_id.h"
33
+ #include "quiche/quic/core/quic_utils.h"
34
+ #include "quiche/quic/platform/api/quic_flags.h"
35
+ #include "quiche/quic/platform/api/quic_logging.h"
36
+ #include "quiche/quic/platform/api/quic_stack_trace.h"
37
+ // #include "quiche/quic/test_tools/crypto_test_utils.h"
38
+ #include "quiche/quic/core/quic_udp_socket.h"
39
+ #include "quiche/quic/tools/quic_url.h"
40
+ #include "quiche/common/quiche_text_utils.h"
41
+
42
+ using spdy::Http2HeaderBlock;
43
+
44
+ namespace quic
45
+ {
46
+
47
+ // taken from chromium to behave like the browser
48
+ // A version of WebTransportFingerprintProofVerifier that enforces
49
+ // Chromium-specific policies.
50
+ class ChromiumWebTransportFingerprintProofVerifier
51
+ : public quic::WebTransportFingerprintProofVerifier
52
+ {
53
+ public:
54
+ using WebTransportFingerprintProofVerifier::
55
+ WebTransportFingerprintProofVerifier;
56
+
57
+ protected:
58
+ bool IsKeyTypeAllowedByPolicy(
59
+ const quic::CertificateView &certificate) override
60
+ {
61
+ if (certificate.public_key_type() == quic::PublicKeyType::kRsa)
62
+ {
63
+ return false;
64
+ }
65
+ return WebTransportFingerprintProofVerifier::IsKeyTypeAllowedByPolicy(
66
+ certificate);
67
+ }
68
+ };
69
+
70
+ class NodeJSProofVerifier : public quic::ProofVerifier
71
+ {
72
+ public:
73
+
74
+ NodeJSProofVerifier(EnvGetter *envg) : envg_(envg)
75
+ {
76
+
77
+ }
78
+
79
+ QuicAsyncStatus VerifyProof(
80
+ const std::string& hostname, const uint16_t port,
81
+ const std::string& server_config, QuicTransportVersion transport_version,
82
+ absl::string_view chlo_hash, const std::vector<std::string>& certs,
83
+ const std::string& cert_sct, const std::string& signature,
84
+ const ProofVerifyContext* context, std::string* error_details,
85
+ std::unique_ptr<ProofVerifyDetails>* details,
86
+ std::unique_ptr<ProofVerifierCallback> callback) {
87
+ auto env = envg_->getEnv();
88
+ Napi::HandleScope scope(env);
89
+
90
+ Napi::Object retObj = Napi::Object::New(env);
91
+ retObj.Set("hostname", hostname);
92
+ retObj.Set("port", port);
93
+ retObj.Set("serverconfig", server_config);
94
+ // todo certs
95
+
96
+ Napi::Array jscerts = Napi::Array::New(env, certs.size());
97
+ for (size_t i = 0; i < certs.size(); i++) {
98
+ jscerts.Set(i, certs[i]);
99
+ }
100
+ retObj.Set("signature", signature);
101
+
102
+ Napi::Value verifyres = env.Global().Get("FAILSVerifyProof").As<Napi::Function>().Call({
103
+ retObj });
104
+ if (verifyres.As<Napi::Boolean>().Value()) {
105
+ return QUIC_SUCCESS;
106
+
107
+ } else {
108
+ return QUIC_FAILURE;
109
+ }
110
+ }
111
+
112
+ QuicAsyncStatus VerifyCertChain(
113
+ const std::string& hostname, const uint16_t port,
114
+ const std::vector<std::string>& certs, const std::string& ocsp_response,
115
+ const std::string& cert_sct, const ProofVerifyContext* context,
116
+ std::string* error_details, std::unique_ptr<ProofVerifyDetails>* details,
117
+ uint8_t* out_alert, std::unique_ptr<ProofVerifierCallback> callback) {
118
+ auto env = envg_->getEnv();
119
+ Napi::HandleScope scope(env);
120
+
121
+ Napi::Object retObj = Napi::Object::New(env);
122
+ retObj.Set("hostname", hostname);
123
+ retObj.Set("port", port);
124
+ // todo certs
125
+
126
+ Napi::Array jscerts = Napi::Array::New(env, certs.size());
127
+ for (size_t i = 0; i < certs.size(); i++) {
128
+ jscerts.Set(i, certs[i]);
129
+ }
130
+ Napi::Value verifyres = env.Global().Get("FAILSVerifyProof").As<Napi::Function>().Call({
131
+ retObj });
132
+ if (verifyres.As<Napi::Boolean>().Value()) {
133
+ return QUIC_SUCCESS;
134
+
135
+ } else {
136
+ return QUIC_FAILURE;
137
+ }
138
+ }
139
+
140
+ std::unique_ptr<ProofVerifyContext> CreateDefaultContext() {
141
+ return nullptr;
142
+ }
143
+
144
+ private:
145
+ EnvGetter *envg_;
146
+ };
147
+
148
+ QuicStreamId GetNthClientInitiatedBidirectionalStreamId(
149
+ QuicTransportVersion version, int n)
150
+ {
151
+ int num = n;
152
+ if (!VersionUsesHttp3(version))
153
+ {
154
+ num++;
155
+ }
156
+ return QuicUtils::GetFirstBidirectionalStreamId(version,
157
+ Perspective::IS_CLIENT) +
158
+ QuicUtils::StreamIdDelta(version) * num;
159
+ }
160
+
161
+ Http3Client::Http3Client(Http3ClientJS *js,
162
+ std::unique_ptr<ProofVerifier> proof_verifier,
163
+ std::unique_ptr<SessionCache> session_cache,
164
+ std::unique_ptr<QuicConnectionHelperInterface> helper,
165
+ QuicConfig config) :
166
+ initialized_(false),
167
+ store_response_(false),
168
+ latest_response_code_(-1),
169
+ overflow_supported_(false),
170
+ packets_dropped_(0),
171
+ packet_reader_(new QuicPacketReader()),
172
+ config_(config),
173
+ crypto_config_(std::move(proof_verifier), std::move(session_cache)),
174
+ helper_(std::move(helper)),
175
+ alarm_factory_(new NapiAlarmFactory(QuicDefaultClock::Get(), js)),
176
+ supported_versions_({ParsedQuicVersion::RFCv1()}),
177
+ initial_max_packet_length_(0),
178
+ num_sent_client_hellos_(0),
179
+ js_(js),
180
+ connection_error_(QUIC_NO_ERROR),
181
+ connected_or_attempting_connect_(false),
182
+ server_connection_id_length_(kQuicDefaultConnectionIdLength),
183
+ client_connection_id_length_(0),
184
+ max_reads_per_loop_(std::numeric_limits<int>::max()),
185
+ wait_for_encryption_(false),
186
+ connection_in_progress_(false),
187
+ connectionrecheck_(false),
188
+ num_attempts_connect_(0),
189
+ webtransport_server_support_inform_(false),
190
+ connection_debug_visitor_(nullptr),
191
+ priority_(HttpStreamPriority())
192
+ {
193
+
194
+ }
195
+
196
+ Http3Client::~Http3Client()
197
+ {
198
+ // printf("client destruct %x\n", this);
199
+ }
200
+
201
+ void Http3Client::setHostname(QuicSocketAddress server_address, const std::string &server_hostname)
202
+ {
203
+ server_id_ = QuicServerId(server_hostname, server_address.port(), false);
204
+ set_server_address(server_address);
205
+ Initialize();
206
+ }
207
+
208
+ bool Http3Client::closeClientInt()
209
+ {
210
+ for (std::pair<QuicStreamId, QuicSpdyClientStream *> stream : open_streams_)
211
+ {
212
+ stream.second->set_visitor(nullptr);
213
+ }
214
+ if (connected())
215
+ {
216
+ session_->connection()->CloseConnection(
217
+ QUIC_PEER_GOING_AWAY, "Client being torn down",
218
+ ConnectionCloseBehavior::SEND_CONNECTION_CLOSE_PACKET);
219
+ }
220
+ // We own the push promise index. We need to explicitly kill
221
+ // the session before the push promise index goes out of scope.
222
+ ResetSession();
223
+
224
+ this->getJS()->Unref();
225
+ return true;
226
+ }
227
+
228
+ void Http3Client::Initialize()
229
+ {
230
+ priority_ = QuicStreamPriority(HttpStreamPriority());
231
+ connect_attempted_ = false;
232
+ auto_reconnect_ = false;
233
+ buffer_body_ = true;
234
+ num_requests_ = 0;
235
+ num_responses_ = 0;
236
+ ClearPerConnectionState();
237
+ // As chrome will generally do this, we want it to be the default when it's
238
+ // not overridden.
239
+ if (!config_.HasSetBytesForConnectionIdToSend())
240
+ {
241
+ config_.SetBytesForConnectionIdToSend(0);
242
+ }
243
+ }
244
+
245
+ void Http3Client::SetUserAgentID(const std::string &user_agent_id)
246
+ {
247
+ crypto_config_.set_user_agent_id(user_agent_id);
248
+ }
249
+
250
+ void Http3Client::SendRequest(const std::string &uri)
251
+ {
252
+ spdy::Http2HeaderBlock headers;
253
+ if (!PopulateHeaderBlockFromUrl(uri, &headers))
254
+ {
255
+ return;
256
+ }
257
+ SendMessageAsync(headers, "");
258
+ }
259
+
260
+ void Http3Client::SendRequestAndRstTogether(const std::string &uri)
261
+ {
262
+ spdy::Http2HeaderBlock headers;
263
+ if (!PopulateHeaderBlockFromUrl(uri, &headers))
264
+ {
265
+ return;
266
+ }
267
+
268
+ QuicSpdyClientSession *session = session_.get();
269
+ QuicConnection::ScopedPacketFlusher flusher(session->connection());
270
+ SendMessageAsync(headers, "", /*fin=*/true);
271
+
272
+ QuicStreamId stream_id = GetNthClientInitiatedBidirectionalStreamId(
273
+ session->transport_version(), 0);
274
+ session->ResetStream(stream_id, QUIC_STREAM_CANCELLED);
275
+ }
276
+
277
+ void Http3Client::GetOrCreateStreamAndSendRequest(
278
+ const spdy::Http2HeaderBlock *headers, absl::string_view body, bool fin)
279
+ {
280
+ std::shared_ptr<spdy::Http2HeaderBlock> spdy_headers;
281
+ bool hasheaders = false;
282
+ if (headers != nullptr)
283
+ {
284
+ spdy_headers = std::make_shared<spdy::Http2HeaderBlock>(headers->Clone());
285
+ hasheaders = true;
286
+ }
287
+
288
+ // Maybe it's better just to overload this. it's just that we need
289
+ // for the GetOrCreateStream function to call something else...which
290
+ // is icky and complicated, but maybe not worse than this.
291
+ RunOnStreamMaybeCreateStream(
292
+ [spdy_headers, hasheaders, body, fin, this](QuicSpdyClientStream *stream)
293
+ {
294
+ if (stream == nullptr)
295
+ {
296
+ return;
297
+ }
298
+ // QuicSpdyStreamPeer::set_ack_listener(stream, ack_listener);
299
+
300
+ size_t ret = 0;
301
+ if (hasheaders)
302
+ {
303
+ if ((*spdy_headers.get())[":authority"].as_string().empty())
304
+ {
305
+ (*spdy_headers.get())[":authority"] = server_id_.host();
306
+ }
307
+ ret = stream->SendRequest(std::move(*spdy_headers.get()), body, fin);
308
+ ++num_requests_;
309
+ }
310
+ else
311
+ {
312
+ stream->WriteOrBufferBody(std::string(body), fin);
313
+ ret = body.length();
314
+ }
315
+ });
316
+ }
317
+
318
+ void Http3Client::SendMessageAsync(const spdy::Http2HeaderBlock &headers,
319
+ absl::string_view body)
320
+ {
321
+ return SendMessageAsync(headers, body, /*fin=*/true);
322
+ }
323
+
324
+ void Http3Client::SendMessageAsync(const spdy::Http2HeaderBlock &headers,
325
+ absl::string_view body, bool fin)
326
+ {
327
+ // Always force creation of a stream for SendMessage.
328
+ latest_created_stream_ = nullptr;
329
+
330
+ GetOrCreateStreamAndSendRequest(&headers, body, fin);
331
+ }
332
+
333
+ bool Http3Client::response_complete() const { return response_complete_; }
334
+
335
+ int64_t Http3Client::response_body_size() const
336
+ {
337
+ return response_body_size_;
338
+ }
339
+
340
+ bool Http3Client::buffer_body() const { return buffer_body_; }
341
+
342
+ void Http3Client::set_buffer_body(bool buffer_body)
343
+ {
344
+ buffer_body_ = buffer_body;
345
+ }
346
+
347
+ const std::string &Http3Client::response_body() const { return response_; }
348
+
349
+ void Http3Client::SendConnectivityProbing()
350
+ {
351
+ QuicConnection *connection = session_->connection();
352
+ connection->SendConnectivityProbingPacket(connection->writer(),
353
+ connection->peer_address());
354
+ }
355
+
356
+ void Http3Client::SetLatestCreatedStream(QuicSpdyClientStream *stream)
357
+ {
358
+ latest_created_stream_ = stream;
359
+ if (latest_created_stream_ != nullptr)
360
+ {
361
+ open_streams_[stream->id()] = stream;
362
+ stream->set_visitor(this);
363
+ }
364
+ }
365
+
366
+ void Http3Client::CreateClientStream(std::function<void(QuicSpdyClientStream *)> finish)
367
+ {
368
+ if (!connected())
369
+ {
370
+ finish(nullptr);
371
+ }
372
+ finish_stream_open_.push(finish);
373
+
374
+ checkSession(); // check if it is already available
375
+ /*
376
+ if (VersionHasIetfQuicFrames(session_->transport_version()))
377
+ {
378
+ // Process MAX_STREAMS from peer or wait for liveness testing succeeds.
379
+ while (!session_->CanOpenNextOutgoingBidirectionalStream())
380
+ {
381
+ RunEventLoop();
382
+ }
383
+ }
384
+ auto *stream = static_cast<QuicSpdyClientStream *>(
385
+ session_->CreateOutgoingBidirectionalStream());
386
+ if (stream)
387
+ {
388
+ stream->set_visitor(this);
389
+ }
390
+ return stream; */
391
+ }
392
+
393
+ void Http3Client::RunOnStreamMaybeCreateStream(std::function<void(QuicSpdyClientStream *)> finish)
394
+ {
395
+ if (!connect_attempted_ || auto_reconnect_)
396
+ {
397
+ if (!connected())
398
+ {
399
+ Connect();
400
+ }
401
+ if (!connected())
402
+ {
403
+ finish(nullptr);
404
+ return;
405
+ }
406
+ }
407
+ if (open_streams_.empty())
408
+ {
409
+ ClearPerConnectionState();
410
+ }
411
+ if (!latest_created_stream_)
412
+ {
413
+ CreateClientStream(
414
+ [this, finish](QuicSpdyClientStream *stream)
415
+ {
416
+ SetLatestCreatedStream(stream);
417
+ if (latest_created_stream_)
418
+ {
419
+ latest_created_stream_->SetPriority(priority_);
420
+ }
421
+ finish(latest_created_stream_);
422
+ });
423
+ }
424
+ else
425
+ {
426
+ finish(latest_created_stream_);
427
+ }
428
+ }
429
+
430
+ QuicErrorCode Http3Client::connection_error() const
431
+ {
432
+ // Return the high-level error if there was one. Otherwise, return the
433
+ // connection error from the last session.
434
+ if (connection_error_ != QUIC_NO_ERROR)
435
+ {
436
+ return connection_error_;
437
+ }
438
+ if (session_ == nullptr)
439
+ {
440
+ return QUIC_NO_ERROR;
441
+ }
442
+ return session_->error();
443
+ }
444
+
445
+ const QuicTagValueMap &Http3Client::GetServerConfig()
446
+ {
447
+ const QuicCryptoClientConfig::CachedState *state =
448
+ crypto_config_.LookupOrCreate(server_id_);
449
+ const CryptoHandshakeMessage *handshake_msg = state->GetServerConfig();
450
+ return handshake_msg->tag_value_map();
451
+ }
452
+
453
+ bool Http3Client::connected() const
454
+ {
455
+ return session_.get() && session_->connection() &&
456
+ session_->connection()->connected();
457
+ }
458
+
459
+ bool Http3Client::clientInitialize()
460
+ {
461
+ num_sent_client_hellos_ = 0;
462
+ connection_error_ = QUIC_NO_ERROR;
463
+ connected_or_attempting_connect_ = false;
464
+
465
+ // If an initial flow control window has not explicitly been set, then use the
466
+ // same values that Chrome uses.
467
+ const uint32_t kSessionMaxRecvWindowSize = 15 * 1024 * 1024; // 15 MB
468
+ const uint32_t kStreamMaxRecvWindowSize = 6 * 1024 * 1024; // 6 MB
469
+ if (config_.GetInitialStreamFlowControlWindowToSend() ==
470
+ kDefaultFlowControlSendWindow)
471
+ {
472
+ config_.SetInitialStreamFlowControlWindowToSend(kStreamMaxRecvWindowSize);
473
+ }
474
+ if (config_.GetInitialSessionFlowControlWindowToSend() ==
475
+ kDefaultFlowControlSendWindow)
476
+ {
477
+ config_.SetInitialSessionFlowControlWindowToSend(
478
+ kSessionMaxRecvWindowSize);
479
+ }
480
+
481
+ initialized_ = true;
482
+ return true;
483
+ }
484
+
485
+ void Http3Client::Connect()
486
+ {
487
+ if (connected())
488
+ {
489
+ QUIC_BUG(quic_bug_10133_1) << "Cannot connect already-connected client";
490
+ return;
491
+ }
492
+ if (!connect_attempted_)
493
+ {
494
+ clientInitialize();
495
+ }
496
+
497
+ // If we've been asked to override SNI, set it now
498
+ if (override_sni_set_)
499
+ {
500
+ // This should only be set before the initial Connect()
501
+ server_id_ = QuicServerId(override_sni_, address().port(), false);
502
+ }
503
+ connection_in_progress_ = true;
504
+ connectionrecheck_ = true;
505
+ wait_for_encryption_ = false;
506
+ }
507
+
508
+ bool Http3Client::handleConnecting()
509
+ {
510
+ bool recheck = false;
511
+ if (connection_in_progress_)
512
+ {
513
+ if (!wait_for_encryption_)
514
+ {
515
+ if (!connected() && num_attempts_connect_ <= QuicCryptoClientStream::kMaxClientHellos)
516
+ {
517
+ if (!connect_attempted_)
518
+ {
519
+ clientInitialize();
520
+ }
521
+ StartConnect();
522
+ wait_for_encryption_ = true;
523
+ }
524
+ else if (session_ == nullptr && num_attempts_connect_ > QuicCryptoClientStream::kMaxClientHellos)
525
+ {
526
+ connection_in_progress_ = false;
527
+ connect_attempted_ = true;
528
+ QUIC_BUG(quic_bug_10906_1) << "Missing session after Connect";
529
+ getJS()->processClientConnected(false);
530
+ }
531
+ }
532
+ if (wait_for_encryption_)
533
+ {
534
+ if (EncryptionBeingEstablished())
535
+ return false;
536
+ wait_for_encryption_ = false;
537
+ ParsedQuicVersion version = UnsupportedQuicVersion();
538
+ if (session_ != nullptr && !CanReconnectWithDifferentVersion(&version) && !session_->connection()->connected())
539
+ {
540
+ // We've successfully created a session but we're not connected, and we
541
+ // cannot reconnect with a different version. Give up trying.
542
+ connection_in_progress_ = false;
543
+ connect_attempted_ = true;
544
+ getJS()->processClientConnected(false);
545
+ }
546
+ else if (session_ != nullptr && session_->connection()->connected())
547
+ {
548
+ connect_attempted_ = true;
549
+ connection_in_progress_ = false;
550
+ getJS()->processClientConnected(true);
551
+ webtransport_server_support_inform_ = true;
552
+ recheck = true;
553
+ }
554
+ else
555
+ {
556
+ num_attempts_connect_++;
557
+ recheck = true;
558
+ }
559
+ }
560
+ }
561
+ if (webtransport_server_support_inform_ && connected())
562
+ {
563
+ if (session_->SupportsWebTransport())
564
+ {
565
+ getJS()->processClientWebtransportSupport();
566
+ webtransport_server_support_inform_ = false;
567
+ }
568
+ else
569
+ recheck = true;
570
+ }
571
+
572
+ if (checkSession())
573
+ {
574
+ recheck = true;
575
+ }
576
+
577
+ return recheck;
578
+ }
579
+
580
+ bool Http3Client::checkSession()
581
+ {
582
+ while (finish_stream_open_.size() > 0 && session_->CanOpenNextOutgoingBidirectionalStream())
583
+ {
584
+ auto *stream = static_cast<QuicSpdyClientStream *>(
585
+ session_->CreateOutgoingBidirectionalStream());
586
+ if (stream)
587
+ {
588
+ stream->set_visitor(this);
589
+ }
590
+ finish_stream_open_.front()(stream);
591
+ if (stream != nullptr)
592
+ {
593
+ if (stream->web_transport() != nullptr)
594
+ {
595
+ WebTransportSessionId id = stream->id();
596
+ WebTransportHttp3 *wtsession = session_->GetWebTransportSession(id);
597
+ if (wtsession == nullptr)
598
+ {
599
+ getJS()->processNewClientSession(nullptr);
600
+ // may be throw error
601
+ }
602
+ else
603
+ {
604
+ // ok we have our session, do we wait for session ready, no set visitor immediatele
605
+ Http3WTSession *wtsessionobj =
606
+ new Http3WTSession();
607
+ wtsessionobj->init(
608
+ static_cast<WebTransportSession *>(wtsession));
609
+ getJS()->processNewClientSession(wtsessionobj);
610
+ auto visitor = std::make_unique<Http3WTSession::Visitor>(wtsessionobj);
611
+ wtsession->SetVisitor(std::move(visitor));
612
+ }
613
+ }
614
+ }
615
+ finish_stream_open_.pop();
616
+ }
617
+
618
+ return finish_stream_open_.size() > 0;
619
+ }
620
+
621
+ void Http3Client::openWTSessionInt(absl::string_view path)
622
+ {
623
+ spdy::Http2HeaderBlock headers;
624
+ headers[":scheme"] = "https";
625
+ headers[":authority"] = "localhost";
626
+ headers[":path"] = path;
627
+ headers[":method"] = "CONNECT";
628
+ headers[":protocol"] = "webtransport";
629
+
630
+ SendMessageAsync(headers, "", /*fin=*/false);
631
+ }
632
+
633
+ void Http3Client::StartConnect()
634
+ {
635
+ QUICHE_DCHECK(initialized_);
636
+ QUICHE_DCHECK(!connected());
637
+ QuicPacketWriter *writer = new SocketJSWriter(getJS());
638
+ ParsedQuicVersion mutual_version = UnsupportedQuicVersion();
639
+ const bool can_reconnect_with_different_version =
640
+ CanReconnectWithDifferentVersion(&mutual_version);
641
+ if (connected_or_attempting_connect_)
642
+ {
643
+ // Clear queued up data if client can not try to connect with a different
644
+ // version.
645
+ if (!can_reconnect_with_different_version)
646
+ {
647
+ ClearDataToResend();
648
+ }
649
+ // Before we destroy the last session and create a new one, gather its stats
650
+ // and update the stats for the overall connection.
651
+ // no stats for me!
652
+ // UpdateStats();
653
+ }
654
+ QuicConnectionId newconnid = QuicUtils::CreateRandomConnectionId(server_connection_id_length_);
655
+
656
+ const quic::ParsedQuicVersionVector client_supported_versions =
657
+ can_reconnect_with_different_version
658
+ ? ParsedQuicVersionVector{mutual_version}
659
+ : supported_versions_;
660
+
661
+ session_ = std::make_unique<Http3ClientSession>(
662
+ config_, client_supported_versions, new QuicConnection(newconnid, QuicSocketAddress(), server_address_, helper_.get(), alarm_factory_.get(), writer,
663
+ /* owns_writer= */ false, Perspective::IS_CLIENT, client_supported_versions, connection_id_generator_),
664
+ server_id_, &crypto_config_,
665
+ false /*drop_response_body_*/, true /* enable_web_transport */);
666
+
667
+ if (can_reconnect_with_different_version)
668
+ {
669
+ session_->set_client_original_supported_versions(supported_versions_);
670
+ }
671
+ if (connection_debug_visitor_ != nullptr)
672
+ {
673
+ session_->connection()->set_debug_visitor(connection_debug_visitor_);
674
+ }
675
+ session_->connection()->set_client_connection_id(
676
+ QuicUtils::CreateRandomConnectionId(client_connection_id_length_));
677
+ if (initial_max_packet_length_ != 0)
678
+ {
679
+ session_->connection()->SetMaxPacketLength(initial_max_packet_length_);
680
+ }
681
+ // Reset |writer()| after |session()| so that the old writer outlives the old
682
+ // session.
683
+ if (writer_.get() != writer)
684
+ {
685
+ writer_.reset(writer);
686
+ }
687
+ // set_writer(writer);
688
+ InitializeSession();
689
+ if (can_reconnect_with_different_version)
690
+ {
691
+ // This is a reconnect using server supported |mutual_version|.
692
+ session_->connection()->SetVersionNegotiated();
693
+ }
694
+ connected_or_attempting_connect_ = true;
695
+ }
696
+
697
+ void Http3Client::InitializeSession()
698
+ {
699
+ if (max_inbound_header_list_size_ > 0)
700
+ {
701
+ session_->set_max_inbound_header_list_size(
702
+ max_inbound_header_list_size_);
703
+ }
704
+ session_->Initialize();
705
+ session_->CryptoConnect();
706
+ }
707
+
708
+ bool Http3Client::CanReconnectWithDifferentVersion(
709
+ ParsedQuicVersion *version) const
710
+ {
711
+ if (session_ == nullptr || session_->connection() == nullptr ||
712
+ session_->error() != QUIC_INVALID_VERSION)
713
+ {
714
+ return false;
715
+ }
716
+
717
+ const auto &server_supported_versions =
718
+ session_->connection()->server_supported_versions();
719
+ if (server_supported_versions.empty())
720
+ {
721
+ return false;
722
+ }
723
+
724
+ for (const auto &client_version : supported_versions_)
725
+ {
726
+ if (std::find(server_supported_versions.begin(),
727
+ server_supported_versions.end(),
728
+ client_version) != server_supported_versions.end())
729
+ {
730
+ *version = client_version;
731
+ return true;
732
+ }
733
+ }
734
+ return false;
735
+ }
736
+
737
+ bool Http3Client::EncryptionBeingEstablished()
738
+ {
739
+ return !session_->IsEncryptionEstablished() &&
740
+ session_->connection()->connected();
741
+ }
742
+
743
+ bool Http3Client::HasActiveRequests()
744
+ {
745
+ return session_->HasActiveRequestStreams();
746
+ }
747
+
748
+ void Http3Client::ClearPerRequestState()
749
+ {
750
+ stream_error_ = QUIC_STREAM_NO_ERROR;
751
+ response_ = "";
752
+ response_complete_ = false;
753
+ response_headers_complete_ = false;
754
+ response_headers_.clear();
755
+ response_trailers_.clear();
756
+ bytes_read_ = 0;
757
+ bytes_written_ = 0;
758
+ response_body_size_ = 0;
759
+ }
760
+
761
+ void Http3Client::ClearDataToResend()
762
+ {
763
+ data_to_resend_on_connect_.clear();
764
+ }
765
+
766
+ bool Http3Client::HaveActiveStream()
767
+ {
768
+ return !open_streams_.empty();
769
+ }
770
+
771
+ void Http3ClientJS::recvPaket(const Napi::CallbackInfo &info)
772
+ {
773
+ QuicTime now = QuicDefaultClock::Get()->Now();
774
+ // Got a packet replace OnSocketEvent for readable
775
+ if (info[0].IsUndefined())
776
+ {
777
+ Napi::Error::New(Env(), "No obj passed to recvPaket").ThrowAsJavaScriptException();
778
+ }
779
+ Napi::Object lobj = info[0].ToObject();
780
+ if (lobj.IsEmpty())
781
+ {
782
+ Napi::Error::New(Env(), "Obj for recvPaket is empty").ThrowAsJavaScriptException();
783
+ }
784
+ if (!lobj.Has("selfaddress"))
785
+ {
786
+ Napi::Error::New(Env(), "No Selfaddress for recvPaket").ThrowAsJavaScriptException();
787
+ }
788
+ Napi::Object selfaddress = (lobj).Get("selfaddress").As<Napi::Object>();
789
+ if (selfaddress.IsEmpty())
790
+ {
791
+ Napi::Error::New(Env(), "Selfaddress for recvPaket empty").ThrowAsJavaScriptException();
792
+ }
793
+ int port = selfaddress.Get("port").As<Napi::Number>().Int32Value();
794
+ std::string selfipaddress = selfaddress.Get("address").As<Napi::String>();
795
+
796
+ QuicIpAddress self_ip;
797
+ self_ip.FromString(selfipaddress);
798
+ QuicSocketAddress self_address(self_ip, port);
799
+
800
+ if (!lobj.Has("rinfo"))
801
+ {
802
+ Napi::Error::New(Env(), "No rinfo for recvPaket").ThrowAsJavaScriptException();
803
+ }
804
+
805
+ Napi::Object rinfo = (lobj).Get("rinfo").As<Napi::Object>();
806
+ if (rinfo.IsEmpty())
807
+ {
808
+ Napi::Error::New(Env(), "Rinfo for recvPaket empty").ThrowAsJavaScriptException();
809
+ }
810
+ int peerport = rinfo.Get("port").As<Napi::Number>().Int32Value();
811
+ std::string peeripaddress = rinfo.Get("address").As<Napi::String>();
812
+
813
+ QuicIpAddress peer_ip;
814
+ peer_ip.FromString(peeripaddress);
815
+ QuicSocketAddress peer_address(peer_ip, peerport);
816
+
817
+ Napi::Object bufferlocal = lobj.Get("msg").As<Napi::Object>();
818
+
819
+ QuicReceivedPacket packet(
820
+ bufferlocal.As<Napi::Buffer<char>>().Data(), rinfo.Get("size").As<Napi::Number>().Uint32Value(), now,
821
+ /*owns_buffer=*/false, 0 /*ttl*/, false /*has_ttl*/, nullptr /*headers*/, 0 /*headers_length*/,
822
+ /*owns_header_buffer=*/false, ECN_NOT_ECT);
823
+
824
+ client_->ProcessPacket(self_address, peer_address, packet);
825
+
826
+ if (client_->connectionrecheck_)
827
+ {
828
+ client_->connectionrecheck_ = !client_->handleConnecting();
829
+ }
830
+ }
831
+
832
+ void Http3ClientJS::onCanWrite(const Napi::CallbackInfo &info)
833
+ {
834
+ if (client_->connectionrecheck_)
835
+ {
836
+ client_->connectionrecheck_ = !client_->handleConnecting();
837
+ }
838
+ client_->OnCanWrite();
839
+ }
840
+
841
+ void Http3Client::OnCanWrite()
842
+ {
843
+ writer_->SetWritable();
844
+ if (connected())
845
+ {
846
+ session_->connection()->OnCanWrite();
847
+ }
848
+ }
849
+
850
+ /*
851
+ void Http3Client::OnSocketEvent(QuicEventLoop *event_loop, QuicUdpSocketFd fd,
852
+ QuicSocketEventMask events)
853
+ {
854
+ /*if (events & kSocketEventReadable)
855
+ {
856
+ QUIC_DVLOG(1) << "Read packets on kSocketEventReadable";
857
+ int times_to_read = max_reads_per_loop_;
858
+ bool more_to_read = true;
859
+ QuicPacketCount packets_dropped = 0;
860
+ while (connected() && more_to_read && times_to_read > 0)
861
+ {
862
+ more_to_read = packet_reader_->ReadAndDispatchPackets(
863
+ fd, GetLatestClientAddress().port(), *helper_->GetClock(),
864
+ this, overflow_supported_ ? &packets_dropped : nullptr);
865
+ --times_to_read;
866
+ }
867
+ if (packets_dropped_ < packets_dropped)
868
+ {
869
+ QUIC_LOG(ERROR)
870
+ << packets_dropped - packets_dropped_
871
+ << " more packets are dropped in the socket receive buffer.";
872
+ packets_dropped_ = packets_dropped;
873
+ }
874
+ if (connected() && more_to_read)
875
+ {
876
+ // Register EPOLLIN event to consume buffered CHLO(s).
877
+ bool success =
878
+ event_loop->ArtificiallyNotifyEvent(fd, kSocketEventReadable);
879
+ QUICHE_DCHECK(success);
880
+ }
881
+ else if (!event_loop->SupportsEdgeTriggered())
882
+ {
883
+ bool success = event_loop->RearmSocket(fd, kSocketEventReadable);
884
+ QUICHE_DCHECK(success);
885
+ }
886
+ }*/
887
+ /*if (connected() && (events & kSocketEventWritable))
888
+ {
889
+ writer_->SetWritable();
890
+ session_->connection()->OnCanWrite();
891
+ }
892
+
893
+ if (handleConnecting())
894
+ {
895
+ bool success =
896
+ event_loop->ArtificiallyNotifyEvent(fd, kSocketEventReadable);
897
+ QUICHE_DCHECK(success);
898
+ }
899
+ }*/
900
+
901
+ void Http3Client::ProcessPacket(
902
+ const QuicSocketAddress &self_address,
903
+ const QuicSocketAddress &peer_address, const QuicReceivedPacket &packet)
904
+ {
905
+ if (connected()) session_->ProcessUdpPacket(self_address, peer_address, packet);
906
+ }
907
+
908
+ bool Http3Client::response_headers_complete() const
909
+ {
910
+ for (std::pair<QuicStreamId, QuicSpdyClientStream *> stream : open_streams_)
911
+ {
912
+ if (stream.second->headers_decompressed())
913
+ {
914
+ return true;
915
+ }
916
+ }
917
+ return response_headers_complete_;
918
+ }
919
+
920
+ const spdy::Http2HeaderBlock *Http3Client::response_headers() const
921
+ {
922
+ for (std::pair<QuicStreamId, QuicSpdyClientStream *> stream : open_streams_)
923
+ {
924
+ if (stream.second->headers_decompressed())
925
+ {
926
+ response_headers_ = stream.second->response_headers().Clone();
927
+ break;
928
+ }
929
+ }
930
+ return &response_headers_;
931
+ }
932
+
933
+ const spdy::Http2HeaderBlock &Http3Client::response_trailers() const
934
+ {
935
+ return response_trailers_;
936
+ }
937
+
938
+ int64_t Http3Client::response_size() const { return bytes_read(); }
939
+
940
+ size_t Http3Client::bytes_read() const
941
+ {
942
+ for (std::pair<QuicStreamId, QuicSpdyClientStream *> stream : open_streams_)
943
+ {
944
+ size_t bytes_read = stream.second->total_body_bytes_read() +
945
+ stream.second->header_bytes_read();
946
+ if (bytes_read > 0)
947
+ {
948
+ return bytes_read;
949
+ }
950
+ }
951
+ return bytes_read_;
952
+ }
953
+
954
+ size_t Http3Client::bytes_written() const
955
+ {
956
+ for (std::pair<QuicStreamId, QuicSpdyClientStream *> stream : open_streams_)
957
+ {
958
+ size_t bytes_written = stream.second->stream_bytes_written() +
959
+ stream.second->header_bytes_written();
960
+ if (bytes_written > 0)
961
+ {
962
+ return bytes_written;
963
+ }
964
+ }
965
+ return bytes_written_;
966
+ }
967
+
968
+ void Http3Client::OnCompleteResponse(
969
+ QuicStreamId id, const spdy::Http2HeaderBlock &response_headers,
970
+ const absl::string_view &response_body)
971
+ {
972
+ // implement
973
+ }
974
+
975
+ void Http3Client::OnClose(QuicSpdyStream *stream)
976
+ {
977
+ if (stream == nullptr)
978
+ {
979
+ return;
980
+ }
981
+ // Always close the stream, regardless of whether it was the last stream
982
+ // written.
983
+ QUICHE_DCHECK(stream != nullptr);
984
+ QuicSpdyClientStream *client_stream =
985
+ static_cast<QuicSpdyClientStream *>(stream);
986
+
987
+ const Http2HeaderBlock &response_headers = client_stream->response_headers();
988
+
989
+ OnCompleteResponse(stream->id(), response_headers, client_stream->data());
990
+
991
+ // Store response headers and body.
992
+ if (store_response_)
993
+ {
994
+ auto status = response_headers.find(":status");
995
+ if (status == response_headers.end())
996
+ {
997
+ QUIC_LOG(ERROR) << "Missing :status response header";
998
+ }
999
+ else if (!absl::SimpleAtoi(status->second, &latest_response_code_))
1000
+ {
1001
+ QUIC_LOG(ERROR) << "Invalid :status response header: " << status->second;
1002
+ }
1003
+ latest_response_headers_ = response_headers.DebugString();
1004
+ for (const Http2HeaderBlock &headers :
1005
+ client_stream->preliminary_headers())
1006
+ {
1007
+ absl::StrAppend(&preliminary_response_headers_, headers.DebugString());
1008
+ }
1009
+ latest_response_header_block_ = response_headers.Clone();
1010
+ latest_response_body_ = client_stream->data();
1011
+ latest_response_trailers_ =
1012
+ client_stream->received_trailers().DebugString();
1013
+ }
1014
+ ++num_responses_;
1015
+ if (open_streams_.find(stream->id()) == open_streams_.end())
1016
+ {
1017
+ return;
1018
+ }
1019
+ if (latest_created_stream_ == stream)
1020
+ {
1021
+ latest_created_stream_ = nullptr;
1022
+ }
1023
+
1024
+ QuicStreamId id = client_stream->id();
1025
+ closed_stream_states_.insert(std::make_pair(
1026
+ id,
1027
+ PerStreamState(
1028
+ // Set response_complete to true iff stream is closed while connected.
1029
+ client_stream->stream_error(), connected(),
1030
+ client_stream->headers_decompressed(),
1031
+ client_stream->response_headers(),
1032
+ (buffer_body() ? client_stream->data() : ""),
1033
+ client_stream->received_trailers(),
1034
+ // Use NumBytesConsumed to avoid counting retransmitted stream frames.
1035
+ client_stream->total_body_bytes_read() +
1036
+ client_stream->header_bytes_read(),
1037
+ client_stream->stream_bytes_written() +
1038
+ client_stream->header_bytes_written(),
1039
+ client_stream->data().size())));
1040
+ open_streams_.erase(id);
1041
+ }
1042
+
1043
+ const QuicSocketAddress &Http3Client::address() const
1044
+ {
1045
+ return server_address_;
1046
+ }
1047
+
1048
+ Http3Client::Http3ClientDataToResend::Http3ClientDataToResend(
1049
+ std::unique_ptr<spdy::Http2HeaderBlock> headers, absl::string_view body,
1050
+ bool fin, Http3Client *client)
1051
+ : headers_(std::move(headers)), body_(body), fin_(fin),
1052
+ client_(client) {}
1053
+
1054
+ Http3Client::Http3ClientDataToResend::~Http3ClientDataToResend() = default;
1055
+
1056
+ void Http3Client::Http3ClientDataToResend::Resend()
1057
+ {
1058
+ client_->GetOrCreateStreamAndSendRequest(headers_.get(), body_, fin_);
1059
+ headers_.reset();
1060
+ }
1061
+
1062
+ Http3Client::PerStreamState::PerStreamState(const PerStreamState &other)
1063
+ : stream_error(other.stream_error),
1064
+ response_complete(other.response_complete),
1065
+ response_headers_complete(other.response_headers_complete),
1066
+ response_headers(other.response_headers.Clone()),
1067
+ response(other.response),
1068
+ response_trailers(other.response_trailers.Clone()),
1069
+ bytes_read(other.bytes_read),
1070
+ bytes_written(other.bytes_written),
1071
+ response_body_size(other.response_body_size) {}
1072
+
1073
+ Http3Client::PerStreamState::PerStreamState(
1074
+ QuicRstStreamErrorCode stream_error, bool response_complete,
1075
+ bool response_headers_complete,
1076
+ const spdy::Http2HeaderBlock &response_headers,
1077
+ const absl::string_view response, const spdy::Http2HeaderBlock &response_trailers,
1078
+ uint64_t bytes_read, uint64_t bytes_written, int64_t response_body_size)
1079
+ : stream_error(stream_error),
1080
+ response_complete(response_complete),
1081
+ response_headers_complete(response_headers_complete),
1082
+ response_headers(response_headers.Clone()),
1083
+ response(response),
1084
+ response_trailers(response_trailers.Clone()),
1085
+ bytes_read(bytes_read),
1086
+ bytes_written(bytes_written),
1087
+ response_body_size(response_body_size) {}
1088
+
1089
+ Http3Client::PerStreamState::~PerStreamState() = default;
1090
+
1091
+ bool Http3Client::PopulateHeaderBlockFromUrl(
1092
+ const std::string &uri, spdy::Http2HeaderBlock *headers)
1093
+ {
1094
+ std::string url;
1095
+ if (absl::StartsWith(uri, "https://") || absl::StartsWith(uri, "http://"))
1096
+ {
1097
+ url = uri;
1098
+ }
1099
+ else if (uri[0] == '/')
1100
+ {
1101
+ url = "https://" + server_id_.host() + uri;
1102
+ }
1103
+ else
1104
+ {
1105
+ url = "https://" + uri;
1106
+ }
1107
+ return SpdyUtils::PopulateHeaderBlockFromUrl(url, headers);
1108
+ }
1109
+
1110
+ /*
1111
+ void Http3Client::ReadNextResponse()
1112
+ {
1113
+ if (closed_stream_states_.empty())
1114
+ {
1115
+ return;
1116
+ }
1117
+
1118
+ PerStreamState state(closed_stream_states_.front().second);
1119
+
1120
+ stream_error_ = state.stream_error;
1121
+ response_ = state.response;
1122
+ response_complete_ = state.response_complete;
1123
+ response_headers_complete_ = state.response_headers_complete;
1124
+ response_headers_ = state.response_headers.Clone();
1125
+ response_trailers_ = state.response_trailers.Clone();
1126
+ bytes_read_ = state.bytes_read;
1127
+ bytes_written_ = state.bytes_written;
1128
+ response_body_size_ = state.response_body_size;
1129
+
1130
+ closed_stream_states_.pop_front();
1131
+ }
1132
+ */
1133
+
1134
+ void Http3Client::ClearPerConnectionState()
1135
+ {
1136
+ ClearPerRequestState();
1137
+ open_streams_.clear();
1138
+ closed_stream_states_.clear();
1139
+ latest_created_stream_ = nullptr;
1140
+ }
1141
+
1142
+ Http3ClientJS::Http3ClientJS(const Napi::CallbackInfo &info) : Napi::ObjectWrap<Http3ClientJS>(info)
1143
+ {
1144
+ bool allowPooling = false;
1145
+ std::vector<WebTransportHash> serverCertificateHashes;
1146
+ std::string privkey;
1147
+ QuicConfig cconfig;
1148
+ auto env = info.Env();
1149
+ if (!info[0].IsUndefined())
1150
+ {
1151
+ Napi::Object lobj = info[0].ToObject();
1152
+ if (!lobj.IsEmpty())
1153
+ {
1154
+
1155
+ if (lobj.Has("allowPooling") && !(lobj).Get("allowPooling").IsEmpty())
1156
+ {
1157
+ Napi::Value poolValue = (lobj).Get("allowPooling");
1158
+ allowPooling = poolValue.As<Napi::Boolean>().Value();
1159
+ }
1160
+
1161
+ if (lobj.Has("serverCertificateHashes") && !(lobj).Get("serverCertificateHashes").IsEmpty())
1162
+ {
1163
+ Napi::Value hashValue = (lobj).Get("serverCertificateHashes");
1164
+ if (hashValue.IsArray())
1165
+ {
1166
+ Napi::Array hashArray = hashValue.As<Napi::Array>();
1167
+ int length = hashArray.Length();
1168
+
1169
+ for (unsigned int i = 0; i < length; i++)
1170
+ {
1171
+ WebTransportHash curhash;
1172
+ Napi::Value hash = hashArray.Get(i);
1173
+ Napi::Object hashobj = hash.ToObject();
1174
+ if (hashobj.Has("value") && !(hashobj).Get("value").IsEmpty())
1175
+ {
1176
+ Napi::Object bufferlocal = (hashobj).Get("value").ToObject();
1177
+ char *buffer = bufferlocal.As<Napi::Buffer<char>>().Data();
1178
+ size_t len = bufferlocal.As<Napi::Buffer<char>>().Length();
1179
+ curhash.value = std::string(buffer, len);
1180
+ }
1181
+ else
1182
+ {
1183
+ Napi::Error::New(env, "serverCertificateHashes wrong format").ThrowAsJavaScriptException();
1184
+ return;
1185
+ }
1186
+ if (hashobj.Has("algorithm") && !(hashobj).Get("algorithm").IsEmpty())
1187
+ {
1188
+ Napi::Value algorithmValue = (hashobj).Get("algorithm");
1189
+ curhash.algorithm = algorithmValue.ToString().Utf8Value();
1190
+ }
1191
+ else
1192
+ {
1193
+ Napi::Error::New(env, "serverCertificateHashes wrong format").ThrowAsJavaScriptException();
1194
+ return;
1195
+ }
1196
+ if (curhash.algorithm.compare(WebTransportHash::kSha256) != 0)
1197
+ {
1198
+ Napi::Error::New(env, "serverCertificateHashes unknown algorithm").ThrowAsJavaScriptException();
1199
+ return;
1200
+ }
1201
+ serverCertificateHashes.push_back(curhash);
1202
+ }
1203
+ }
1204
+ else
1205
+ {
1206
+ Napi::Error::New(env, "serverCertificateHashes is not an array").ThrowAsJavaScriptException();
1207
+ return;
1208
+ }
1209
+ }
1210
+ if (lobj.Has("initialStreamFlowControlWindow") && !(lobj).Get("initialStreamFlowControlWindow").IsEmpty())
1211
+ {
1212
+ Napi::Value initialStreamFlowControlWindowValue = (lobj).Get("initialStreamFlowControlWindow");
1213
+ int initialStreamFlowControlWindow = initialStreamFlowControlWindowValue.As<Napi::Number>().Int32Value();
1214
+ cconfig.SetInitialStreamFlowControlWindowToSend(initialStreamFlowControlWindow);
1215
+ }
1216
+
1217
+ if (lobj.Has("initialSessionFlowControlWindow") && !(lobj).Get("initialSessionFlowControlWindow").IsEmpty())
1218
+ {
1219
+ Napi::Value initialSessionFlowControlWindowValue = (lobj).Get("initialSessionFlowControlWindow");
1220
+ int initialSessionFlowControlWindow = initialSessionFlowControlWindowValue.As<Napi::Number>().Int32Value();
1221
+ cconfig.SetInitialSessionFlowControlWindowToSend(initialSessionFlowControlWindow);
1222
+ }
1223
+
1224
+ if (lobj.Has("streamFlowControlWindowSizeLimit") && !(lobj).Get("streamFlowControlWindowSizeLimit").IsEmpty())
1225
+ {
1226
+ Napi::Value streamFlowControlWindowSizeLimitValue = (lobj).Get("streamFlowControlWindowSizeLimit");
1227
+ int streamFlowControlWindowSizeLimitWindow = streamFlowControlWindowSizeLimitValue.As<Napi::Number>().Int32Value();
1228
+ cconfig.SetInitialMaxStreamDataBytesOutgoingBidirectionalToSend(streamFlowControlWindowSizeLimitWindow);
1229
+ cconfig.SetInitialMaxStreamDataBytesIncomingBidirectionalToSend(streamFlowControlWindowSizeLimitWindow);
1230
+ cconfig.SetInitialMaxStreamDataBytesUnidirectionalToSend(streamFlowControlWindowSizeLimitWindow);
1231
+ }
1232
+ }
1233
+ }
1234
+
1235
+ // Callback *callback, int port, std::unique_ptr<ProofSource> proof_source, const char *secret
1236
+
1237
+ std::unique_ptr<QuicConnectionHelperInterface> helper =
1238
+ std::make_unique<QuicDefaultConnectionHelper>();
1239
+
1240
+ std::unique_ptr<ProofVerifier> verifier;
1241
+
1242
+ if (serverCertificateHashes.size() > 0)
1243
+ {
1244
+ verifier = std::make_unique<ChromiumWebTransportFingerprintProofVerifier>(helper->GetClock(), 14);
1245
+
1246
+ for (auto cur = serverCertificateHashes.begin();
1247
+ cur != serverCertificateHashes.end(); cur++)
1248
+ {
1249
+ if (!static_cast<ChromiumWebTransportFingerprintProofVerifier*>(verifier.get())->AddFingerprint(*cur))
1250
+ {
1251
+ Napi::Error::New(env, "serverCertificateHashes is not valid fingerprint").ThrowAsJavaScriptException();
1252
+ return;
1253
+ }
1254
+ }
1255
+ }
1256
+ else
1257
+ {
1258
+ verifier = std::make_unique<NodeJSProofVerifier>(this);
1259
+ }
1260
+
1261
+ std::unique_ptr<Http3SessionCache> cache;
1262
+
1263
+ client_ = std::make_unique<Http3Client>(this, std::move(verifier), std::move(cache), std::move(helper), cconfig);
1264
+ client_->SetUserAgentID("fails-components/webtransport");
1265
+
1266
+ Ref(); // do not garbage collect
1267
+
1268
+ client_->Connect();
1269
+ return;
1270
+ }
1271
+
1272
+ void Http3ClientJS::setHostname(const Napi::CallbackInfo &info) {
1273
+ int port = 443;
1274
+ std::string serveraddress;
1275
+ std::string hostname = "localhost";
1276
+ auto env = info.Env();
1277
+ if (!info[0].IsUndefined())
1278
+ {
1279
+ Napi::Object lobj = info[0].ToObject();
1280
+ if (!lobj.IsEmpty())
1281
+ {
1282
+
1283
+
1284
+
1285
+ if (lobj.Has("port") && !(lobj).Get("port").IsEmpty())
1286
+ {
1287
+ Napi::Value portValue = (lobj).Get("port");
1288
+ port = portValue.As<Napi::Number>().Int32Value();
1289
+ }
1290
+ else
1291
+ {
1292
+ Napi::Error::New(env, "no port specified").ThrowAsJavaScriptException();
1293
+ return;
1294
+ }
1295
+
1296
+ if (lobj.Has("host") && !(lobj).Get("host").IsEmpty())
1297
+ {
1298
+ Napi::Value hostnameValue = (lobj).Get("host");
1299
+ hostname = hostnameValue.ToString().Utf8Value();
1300
+ }
1301
+ else
1302
+ {
1303
+ Napi::Error::New(env, "no hostname specified").ThrowAsJavaScriptException();
1304
+ return;
1305
+ }
1306
+
1307
+ if (lobj.Has("serveraddress") && !(lobj).Get("serveraddress").IsEmpty())
1308
+ {
1309
+ Napi::Value serveraddressValue = (lobj).Get("serveraddress");
1310
+ serveraddress = serveraddressValue.ToString().Utf8Value();
1311
+ }
1312
+ else
1313
+ {
1314
+ Napi::Error::New(env, "no serveraddress specified").ThrowAsJavaScriptException();
1315
+ return;
1316
+ }
1317
+ }
1318
+ }
1319
+
1320
+ QuicIpAddress server_ip;
1321
+ server_ip.FromString(serveraddress);
1322
+ QuicSocketAddress address(server_ip, port);
1323
+
1324
+ client_ ->setHostname(address, hostname);
1325
+ }
1326
+
1327
+ void Http3ClientJS::openWTSession(const Napi::CallbackInfo &info)
1328
+ {
1329
+ Http3Client *obj = getObj();
1330
+ // got the object we can now start the server
1331
+
1332
+ if (!info[0].IsUndefined())
1333
+ {
1334
+ std::string lpath(info[0].ToString().Utf8Value());
1335
+
1336
+ obj->openWTSessionInt(lpath);
1337
+ }
1338
+ else
1339
+ {
1340
+ Napi::Error::New(info.Env(), "openWTSession without path").ThrowAsJavaScriptException();
1341
+ return;
1342
+ }
1343
+ }
1344
+
1345
+ void Http3ClientJS::closeClient(const Napi::CallbackInfo &info)
1346
+ {
1347
+ Http3Client *obj = getObj();
1348
+ // got the object we can now start the server
1349
+
1350
+ if (!obj->closeClientInt())
1351
+ {
1352
+ printf("closeClientInt failed for Http3Client");
1353
+ return;
1354
+ }
1355
+ }
1356
+
1357
+ void Http3ClientJS::processClientConnected(bool success)
1358
+ {
1359
+ Napi::HandleScope scope(Env());
1360
+
1361
+ Napi::Object objVal = Value().Get("jsobj").As<Napi::Object>();
1362
+ Napi::Object retObj = Napi::Object::New(Env());
1363
+ retObj.Set("success", success);
1364
+
1365
+ objVal.Get("onClientConnected")
1366
+ .As<Napi::Function>()
1367
+ .Call(objVal, {retObj.As<Napi::Value>()});
1368
+ }
1369
+
1370
+ void Http3ClientJS::processClientWebtransportSupport()
1371
+ {
1372
+ Napi::HandleScope scope(Env());
1373
+ Napi::Object objVal = Value().Get("jsobj").As<Napi::Object>();
1374
+ Napi::Object retObj = Napi::Object::New(Env());
1375
+
1376
+ objVal.Get("onClientWebTransportSupport")
1377
+ .As<Napi::Function>()
1378
+ .Call(objVal, {retObj});
1379
+ }
1380
+
1381
+ void Http3ClientJS::processNewClientSession(Http3WTSession *session)
1382
+ {
1383
+ Napi::HandleScope scope(Env());
1384
+
1385
+ Napi::Object retObj = Napi::Object::New(Env());
1386
+ if (session != nullptr)
1387
+ {
1388
+ Http3Constructors *constr = Env().GetInstanceData<Http3Constructors>();
1389
+ Napi::Object sessionobj = constr->session.New({});
1390
+ Http3WTSessionJS *sessionjs = Napi::ObjectWrap<Http3WTSessionJS>::Unwrap(sessionobj);
1391
+ sessionjs->setObj(session);
1392
+ sessionjs->Ref();
1393
+ session->setJS(sessionjs);
1394
+ Napi::Object objVal = Value().Get("jsobj").As<Napi::Object>();
1395
+ retObj.Set("session", sessionobj);
1396
+ objVal.Get("onHttpWTSessionVisitor")
1397
+ .As<Napi::Function>()
1398
+ .Call(objVal, {retObj});
1399
+ }
1400
+ }
1401
+
1402
+ } // namespace quic