@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.
- package/CMakeLists.txt +978 -0
- package/LICENSE +39 -0
- package/LICENSE.chromium +27 -0
- package/LICENSE.envoy +207 -0
- package/build.js +315 -0
- package/lib/clientsocket.js +80 -0
- package/lib/index.js +47 -0
- package/lib/serversocket.js +80 -0
- package/lib/socket.js +118 -0
- package/lib/types.ts +15 -0
- package/lib/utils.js +12 -0
- package/package.json +68 -0
- package/platform/icufix/fakedata.cpp +8 -0
- package/platform/quiche/quic/core/io/socket_win.inc +3 -0
- package/platform/quiche_platform_impl/quiche_bug_tracker_impl.h +7 -0
- package/platform/quiche_platform_impl/quiche_client_stats_impl.h +7 -0
- package/platform/quiche_platform_impl/quiche_command_line_flags_impl.h +7 -0
- package/platform/quiche_platform_impl/quiche_containers_impl.h +7 -0
- package/platform/quiche_platform_impl/quiche_event_loop_impl.h +7 -0
- package/platform/quiche_platform_impl/quiche_export_impl.h +6 -0
- package/platform/quiche_platform_impl/quiche_flag_utils_impl.h +11 -0
- package/platform/quiche_platform_impl/quiche_flags_impl.h +8 -0
- package/platform/quiche_platform_impl/quiche_googleurl_impl.h +8 -0
- package/platform/quiche_platform_impl/quiche_iovec_impl.h +10 -0
- package/platform/quiche_platform_impl/quiche_logging_impl.h +21 -0
- package/platform/quiche_platform_impl/quiche_mem_slice_impl.h +2 -0
- package/platform/quiche_platform_impl/quiche_mutex_impl.h +8 -0
- package/platform/quiche_platform_impl/quiche_prefetch_impl.h +11 -0
- package/platform/quiche_platform_impl/quiche_reference_counted_impl.h +16 -0
- package/platform/quiche_platform_impl/quiche_server_stats_impl.h +7 -0
- package/platform/quiche_platform_impl/quiche_stack_trace_impl.h +12 -0
- package/platform/quiche_platform_impl/quiche_stream_buffer_allocator_impl.h +8 -0
- package/platform/quiche_platform_impl/quiche_testvalue_impl.h +1 -0
- package/platform/quiche_platform_impl/quiche_thread_local_impl.h +6 -0
- package/platform/quiche_platform_impl/quiche_time_utils_impl.h +13 -0
- package/platform/quiche_platform_impl/quiche_udp_socket_platform_impl.h +39 -0
- package/readme.md +20 -0
- package/src/http3backendresponse.cc +36 -0
- package/src/http3backendresponse.h +109 -0
- package/src/http3client.cc +1402 -0
- package/src/http3client.h +531 -0
- package/src/http3clientsession.cc +65 -0
- package/src/http3clientsession.h +58 -0
- package/src/http3clientstream.cc +52 -0
- package/src/http3clientstream.h +42 -0
- package/src/http3dispatcher.cc +62 -0
- package/src/http3dispatcher.h +57 -0
- package/src/http3server.cc +529 -0
- package/src/http3server.h +149 -0
- package/src/http3serverbackend.cc +104 -0
- package/src/http3serverbackend.h +96 -0
- package/src/http3serversession.cc +123 -0
- package/src/http3serversession.h +95 -0
- package/src/http3serverstream.cc +463 -0
- package/src/http3serverstream.h +114 -0
- package/src/http3sessioncache.cc +85 -0
- package/src/http3sessioncache.h +59 -0
- package/src/http3wtsessionvisitor.cc +312 -0
- package/src/http3wtsessionvisitor.h +257 -0
- package/src/http3wtstreamvisitor.cc +286 -0
- package/src/http3wtstreamvisitor.h +323 -0
- package/src/librarymain.cc +86 -0
- package/src/librarymain.h +42 -0
- package/src/napialarmfactory.cc +51 -0
- package/src/napialarmfactory.h +165 -0
- package/src/socketjswriter.cc +46 -0
- 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
|