@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,529 @@
|
|
|
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, original copyright, see LICENSE.chromium
|
|
6
|
+
// Copyright (c) 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 "absl/cleanup/cleanup.h"
|
|
11
|
+
#include "src/http3server.h"
|
|
12
|
+
#include "src/http3dispatcher.h"
|
|
13
|
+
#include "src/http3wtsessionvisitor.h"
|
|
14
|
+
#include "quiche/quic/core/quic_default_packet_writer.h"
|
|
15
|
+
#include "quiche/quic/core/quic_default_connection_helper.h"
|
|
16
|
+
#include "quiche/quic/core/quic_default_clock.h"
|
|
17
|
+
#include "quiche/quic/tools/quic_simple_crypto_server_stream_helper.h"
|
|
18
|
+
#include "quiche/quic/core/crypto/proof_source_x509.h"
|
|
19
|
+
#include "quiche/common/platform/api/quiche_reference_counted.h"
|
|
20
|
+
|
|
21
|
+
using namespace Napi;
|
|
22
|
+
|
|
23
|
+
namespace quic
|
|
24
|
+
{
|
|
25
|
+
|
|
26
|
+
const size_t kNumSessionsToCreatePerSocketEvent = 16;
|
|
27
|
+
|
|
28
|
+
Http3Server::Http3Server(Http3ServerJS *js, std::unique_ptr<ProofSource> proof_source,
|
|
29
|
+
const char *secret, QuicConfig config)
|
|
30
|
+
: config_(config),
|
|
31
|
+
http3_server_backend_(),
|
|
32
|
+
packet_reader_(new QuicPacketReader()),
|
|
33
|
+
packets_dropped_(0),
|
|
34
|
+
version_manager_({ParsedQuicVersion::RFCv1()}),
|
|
35
|
+
crypto_config_(secret,
|
|
36
|
+
QuicRandom::GetInstance(),
|
|
37
|
+
std::move(proof_source),
|
|
38
|
+
KeyExchangeSource::Default()),
|
|
39
|
+
expected_server_connection_id_length_(kQuicDefaultConnectionIdLength),
|
|
40
|
+
js_(js),
|
|
41
|
+
connection_id_generator_(expected_server_connection_id_length_)
|
|
42
|
+
{
|
|
43
|
+
// may be put somewhereelse
|
|
44
|
+
dispatcher_.reset(CreateQuicDispatcher());
|
|
45
|
+
dispatcher_->InitializeWithWriter(new SocketJSWriter(getJS()));
|
|
46
|
+
// may be put somewhereelse
|
|
47
|
+
const uint32_t kInitialSessionFlowControlWindow = 1 * 1024 * 1024; // 1 MB
|
|
48
|
+
const uint32_t kInitialStreamFlowControlWindow = 64 * 1024; // 64 KB
|
|
49
|
+
if (config_.GetInitialStreamFlowControlWindowToSend() ==
|
|
50
|
+
kDefaultFlowControlSendWindow)
|
|
51
|
+
{
|
|
52
|
+
config_.SetInitialStreamFlowControlWindowToSend(
|
|
53
|
+
kInitialStreamFlowControlWindow);
|
|
54
|
+
}
|
|
55
|
+
if (config_.GetInitialSessionFlowControlWindowToSend() ==
|
|
56
|
+
kDefaultFlowControlSendWindow)
|
|
57
|
+
{
|
|
58
|
+
config_.SetInitialSessionFlowControlWindowToSend(
|
|
59
|
+
kInitialSessionFlowControlWindow);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
Http3Server::~Http3Server()
|
|
64
|
+
{
|
|
65
|
+
// printf("server destruct %x\n", this);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// TODO make call from JS
|
|
69
|
+
void Http3Server::Destroy()
|
|
70
|
+
{
|
|
71
|
+
|
|
72
|
+
// if (!silent_close_) {
|
|
73
|
+
// Before we shut down the epoll server, give all active sessions a chance
|
|
74
|
+
// to notify clients that they're closing.
|
|
75
|
+
dispatcher_->Shutdown();
|
|
76
|
+
//}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
QuicDispatcher *Http3Server::CreateQuicDispatcher()
|
|
80
|
+
{
|
|
81
|
+
http3_server_backend_.setServer(this);
|
|
82
|
+
return new Http3Dispatcher(
|
|
83
|
+
&config_, &crypto_config_, &version_manager_,
|
|
84
|
+
std::unique_ptr<QuicDefaultConnectionHelper>(new QuicDefaultConnectionHelper()),
|
|
85
|
+
std::unique_ptr<QuicCryptoServerStreamBase::Helper>(
|
|
86
|
+
new QuicSimpleCryptoServerStreamHelper()),
|
|
87
|
+
std::unique_ptr<QuicAlarmFactory>(new NapiAlarmFactory(QuicDefaultClock::Get(), getJS())),
|
|
88
|
+
&http3_server_backend_, expected_server_connection_id_length_, connection_id_generator_);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
Http3ServerJS::Http3ServerJS(const Napi::CallbackInfo &info) : Napi::ObjectWrap<Http3ServerJS>(info)
|
|
92
|
+
{
|
|
93
|
+
std::string secret;
|
|
94
|
+
std::string cert;
|
|
95
|
+
std::string privkey;
|
|
96
|
+
|
|
97
|
+
QuicConfig sconfig;
|
|
98
|
+
if (!info[0].IsUndefined())
|
|
99
|
+
{
|
|
100
|
+
Napi::Object lobj = info[0].ToObject();
|
|
101
|
+
if (!lobj.IsEmpty())
|
|
102
|
+
{
|
|
103
|
+
if (lobj.Has("secret") && !(lobj).Get("secret").IsEmpty())
|
|
104
|
+
{
|
|
105
|
+
Napi::Value secretValue = (lobj).Get("secret");
|
|
106
|
+
secret = secretValue.ToString().Utf8Value();
|
|
107
|
+
}
|
|
108
|
+
else
|
|
109
|
+
{
|
|
110
|
+
Napi::Error::New(Env(), "No secret set for Http3Server").ThrowAsJavaScriptException();
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if (lobj.Has("cert") && !(lobj).Get("cert").IsEmpty())
|
|
114
|
+
{
|
|
115
|
+
Napi::Value certValue = (lobj).Get("cert");
|
|
116
|
+
cert = certValue.ToString().Utf8Value();
|
|
117
|
+
}
|
|
118
|
+
else
|
|
119
|
+
{
|
|
120
|
+
Napi::Error::New(Env(), "No cert set for Http3Server").ThrowAsJavaScriptException();
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
if (lobj.Has("privKey") && !(lobj).Get("privKey").IsEmpty())
|
|
124
|
+
{
|
|
125
|
+
Napi::Value keyValue = (lobj).Get("privKey");
|
|
126
|
+
privkey = keyValue.ToString().Utf8Value();
|
|
127
|
+
}
|
|
128
|
+
else
|
|
129
|
+
{
|
|
130
|
+
Napi::Error::New(Env(), "No privKey set for Http3Server").ThrowAsJavaScriptException();
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
if (lobj.Has("maxConnections") && !(lobj).Get("maxConnections").IsEmpty())
|
|
134
|
+
{
|
|
135
|
+
Napi::Value maxconnValue = (lobj).Get("maxConnections");
|
|
136
|
+
int maxconn = maxconnValue.As<Napi::Number>().Int32Value();
|
|
137
|
+
sconfig.SetMaxBidirectionalStreamsToSend(maxconn);
|
|
138
|
+
sconfig.SetMaxUnidirectionalStreamsToSend(maxconn);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (lobj.Has("initialStreamFlowControlWindow") && !(lobj).Get("initialStreamFlowControlWindow").IsEmpty())
|
|
142
|
+
{
|
|
143
|
+
Napi::Value initialStreamFlowControlWindowValue = (lobj).Get("initialStreamFlowControlWindow");
|
|
144
|
+
int initialStreamFlowControlWindow = initialStreamFlowControlWindowValue.As<Napi::Number>().Int32Value();
|
|
145
|
+
sconfig.SetInitialStreamFlowControlWindowToSend(initialStreamFlowControlWindow);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (lobj.Has("initialSessionFlowControlWindow") && !(lobj).Get("initialSessionFlowControlWindow").IsEmpty())
|
|
149
|
+
{
|
|
150
|
+
Napi::Value initialSessionFlowControlWindowValue = (lobj).Get("initialSessionFlowControlWindow");
|
|
151
|
+
int initialSessionFlowControlWindow = initialSessionFlowControlWindowValue.As<Napi::Number>().Int32Value();
|
|
152
|
+
sconfig.SetInitialSessionFlowControlWindowToSend(initialSessionFlowControlWindow);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (lobj.Has("streamFlowControlWindowSizeLimit") && !(lobj).Get("streamFlowControlWindowSizeLimit").IsEmpty())
|
|
156
|
+
{
|
|
157
|
+
Napi::Value streamFlowControlWindowSizeLimitValue = (lobj).Get("streamFlowControlWindowSizeLimit");
|
|
158
|
+
int streamFlowControlWindowSizeLimitWindow = streamFlowControlWindowSizeLimitValue.As<Napi::Number>().Int32Value();
|
|
159
|
+
sconfig.SetInitialMaxStreamDataBytesOutgoingBidirectionalToSend(streamFlowControlWindowSizeLimitWindow);
|
|
160
|
+
sconfig.SetInitialMaxStreamDataBytesIncomingBidirectionalToSend(streamFlowControlWindowSizeLimitWindow);
|
|
161
|
+
sconfig.SetInitialMaxStreamDataBytesUnidirectionalToSend(streamFlowControlWindowSizeLimitWindow);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
// Callback *callback, int port, std::unique_ptr<ProofSource> proof_source, const char *secret
|
|
165
|
+
|
|
166
|
+
std::stringstream certstream(cert, std::ios_base::in);
|
|
167
|
+
quiche::QuicheReferenceCountedPointer<ProofSource::Chain> chain(new ProofSource::Chain(CertificateView::LoadPemFromStream(&certstream)));
|
|
168
|
+
|
|
169
|
+
std::stringstream privkeystream(privkey, std::ios_base::in);
|
|
170
|
+
|
|
171
|
+
auto certprivkey = CertificatePrivateKey::LoadPemFromStream(&privkeystream);
|
|
172
|
+
if (certprivkey == nullptr)
|
|
173
|
+
{
|
|
174
|
+
Napi::Error::New(Env(), "LoadPemFromStream privKey failed for Http3Server").ThrowAsJavaScriptException();
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
std::unique_ptr<ProofSourceX509> proofsource = ProofSourceX509::Create(chain, std::move(*certprivkey));
|
|
179
|
+
if (proofsource == nullptr)
|
|
180
|
+
{
|
|
181
|
+
Napi::Error::New(Env(), "LoadPemFromStream cert failed for Http3Server").ThrowAsJavaScriptException();
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
server_ = std::make_unique<Http3Server>(this, std::move(proofsource), secret.c_str(), sconfig);
|
|
186
|
+
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
else
|
|
190
|
+
{
|
|
191
|
+
Napi::Error::New(Env(), "No arguments passed to Http3Server").ThrowAsJavaScriptException();
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
Http3ServerJS::~Http3ServerJS()
|
|
197
|
+
{
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
void Http3ServerJS::destroy(const Napi::CallbackInfo &info)
|
|
201
|
+
{
|
|
202
|
+
server_->Destroy();
|
|
203
|
+
server_.reset();
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
Napi::Value Http3ServerJS::recvPaket(const Napi::CallbackInfo &info)
|
|
207
|
+
{
|
|
208
|
+
QuicTime now = QuicDefaultClock::Get()->Now();
|
|
209
|
+
// Got a packet replace OnSocketEvent for readable
|
|
210
|
+
if (info[0].IsUndefined())
|
|
211
|
+
{
|
|
212
|
+
Napi::Error::New(Env(), "No obj passed to recvPaket").ThrowAsJavaScriptException();
|
|
213
|
+
return Env().Undefined();
|
|
214
|
+
}
|
|
215
|
+
Napi::Object lobj = info[0].ToObject();
|
|
216
|
+
if (lobj.IsEmpty())
|
|
217
|
+
{
|
|
218
|
+
Napi::Error::New(Env(), "Obj for recvPaket is empty").ThrowAsJavaScriptException();
|
|
219
|
+
return Env().Undefined();
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (!lobj.Has("selfaddress"))
|
|
223
|
+
{
|
|
224
|
+
Napi::Error::New(Env(), "No Selfaddress for recvPaket").ThrowAsJavaScriptException();
|
|
225
|
+
return Env().Undefined();
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
Napi::Object selfaddress = (lobj).Get("selfaddress").As<Napi::Object>();
|
|
229
|
+
if (selfaddress.IsEmpty())
|
|
230
|
+
{
|
|
231
|
+
Napi::Error::New(Env(), "Selfaddress for recvPaket empty").ThrowAsJavaScriptException();
|
|
232
|
+
return Env().Undefined();
|
|
233
|
+
}
|
|
234
|
+
int port = selfaddress.Get("port").As<Napi::Number>().Int32Value();
|
|
235
|
+
std::string selfipaddress = selfaddress.Get("address").As<Napi::String>();
|
|
236
|
+
|
|
237
|
+
QuicIpAddress self_ip;
|
|
238
|
+
self_ip.FromString(selfipaddress);
|
|
239
|
+
QuicSocketAddress self_address(self_ip, port);
|
|
240
|
+
|
|
241
|
+
if (!lobj.Has("rinfo"))
|
|
242
|
+
{
|
|
243
|
+
Napi::Error::New(Env(), "No rinfo for recvPaket").ThrowAsJavaScriptException();
|
|
244
|
+
return Env().Undefined();
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
Napi::Object rinfo = (lobj).Get("rinfo").As<Napi::Object>();
|
|
248
|
+
if (rinfo.IsEmpty())
|
|
249
|
+
{
|
|
250
|
+
Napi::Error::New(Env(), "Rinfo for recvPaket empty").ThrowAsJavaScriptException();
|
|
251
|
+
return Env().Undefined();
|
|
252
|
+
}
|
|
253
|
+
int peerport = rinfo.Get("port").As<Napi::Number>().Int32Value();
|
|
254
|
+
std::string peeripaddress = rinfo.Get("address").As<Napi::String>();
|
|
255
|
+
|
|
256
|
+
QuicIpAddress peer_ip;
|
|
257
|
+
peer_ip.FromString(peeripaddress);
|
|
258
|
+
QuicSocketAddress peer_address(peer_ip, peerport);
|
|
259
|
+
|
|
260
|
+
Napi::Object bufferlocal = lobj.Get("msg").As<Napi::Object>();
|
|
261
|
+
|
|
262
|
+
QuicReceivedPacket packet(
|
|
263
|
+
bufferlocal.As<Napi::Buffer<char>>().Data(), rinfo.Get("size").As<Napi::Number>().Uint32Value(), now,
|
|
264
|
+
/*owns_buffer=*/false, 0 /*ttl*/, false /*has_ttl*/, nullptr /*headers*/, 0 /*headers_length*/,
|
|
265
|
+
/*owns_header_buffer=*/false, ECN_NOT_ECT);
|
|
266
|
+
|
|
267
|
+
Http3Server *obj = getObj();
|
|
268
|
+
bool hasbufferedchlos = obj->ProcessPacket(self_address, peer_address, packet);
|
|
269
|
+
return Napi::Boolean::New(Env(), hasbufferedchlos);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
bool Http3Server::ProcessPacket(const QuicSocketAddress &self_address,
|
|
273
|
+
const QuicSocketAddress &peer_address,
|
|
274
|
+
const QuicReceivedPacket &packet)
|
|
275
|
+
{
|
|
276
|
+
dispatcher_.get()->ProcessPacket(self_address, peer_address, packet);
|
|
277
|
+
return dispatcher_->HasChlosBuffered();
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
void Http3ServerJS::processBufferedChlos(const Napi::CallbackInfo &info)
|
|
281
|
+
{
|
|
282
|
+
Http3Server *obj = getObj();
|
|
283
|
+
obj->ProcessBufferedChlos();
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
void Http3Server::ProcessBufferedChlos()
|
|
287
|
+
{
|
|
288
|
+
dispatcher_->ProcessBufferedChlos(kNumSessionsToCreatePerSocketEvent);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
void Http3ServerJS::onCanWrite(const Napi::CallbackInfo &info)
|
|
294
|
+
{
|
|
295
|
+
server_->OnCanWrite();
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
void Http3Server::OnCanWrite()
|
|
299
|
+
{
|
|
300
|
+
dispatcher_->OnCanWrite();
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/*void Http3Server::OnSocketEvent(QuicEventLoop *event_loop, QuicUdpSocketFd fd,
|
|
304
|
+
QuicSocketEventMask events)
|
|
305
|
+
{
|
|
306
|
+
// QUICHE_DCHECK_EQ(fd, fd_);
|
|
307
|
+
|
|
308
|
+
/* if (events & kSocketEventReadable)
|
|
309
|
+
{
|
|
310
|
+
QUIC_DVLOG(1) << "kSocketEventReadable";
|
|
311
|
+
|
|
312
|
+
dispatcher_->ProcessBufferedChlos(kNumSessionsToCreatePerSocketEvent);
|
|
313
|
+
|
|
314
|
+
bool more_to_read = true;
|
|
315
|
+
while (more_to_read)
|
|
316
|
+
{
|
|
317
|
+
more_to_read = packet_reader_->ReadAndDispatchPackets(
|
|
318
|
+
fd_, port_, *QuicDefaultClock::Get(), dispatcher_.get(),
|
|
319
|
+
overflow_supported_ ? &packets_dropped_ : nullptr);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (dispatcher_->HasChlosBuffered())
|
|
323
|
+
{
|
|
324
|
+
// Register EPOLLIN event to consume buffered CHLO(s).
|
|
325
|
+
bool success =
|
|
326
|
+
event_loop->ArtificiallyNotifyEvent(fd, kSocketEventReadable);
|
|
327
|
+
QUICHE_DCHECK(success);
|
|
328
|
+
}
|
|
329
|
+
else if (!event_loop->SupportsEdgeTriggered())
|
|
330
|
+
{
|
|
331
|
+
bool success = event_loop->RearmSocket(fd, kSocketEventReadable);
|
|
332
|
+
QUICHE_DCHECK(success);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
if (events & kSocketEventWritable)
|
|
336
|
+
{
|
|
337
|
+
dispatcher_->OnCanWrite();
|
|
338
|
+
if (!event_loop->SupportsEdgeTriggered() &&
|
|
339
|
+
dispatcher_->HasPendingWrites())
|
|
340
|
+
{
|
|
341
|
+
bool success = event_loop->RearmSocket(fd, kSocketEventWritable);
|
|
342
|
+
QUICHE_DCHECK(success);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}*/
|
|
346
|
+
|
|
347
|
+
void Http3ServerJS::processNewSession(Http3WTSession *session, const std::string &path, Napi::Reference<Napi::Value> *header)
|
|
348
|
+
{
|
|
349
|
+
Napi::HandleScope scope(Env());
|
|
350
|
+
|
|
351
|
+
Http3Constructors *constr = Env().GetInstanceData<Http3Constructors>();
|
|
352
|
+
Napi::Object sessionobj = constr->session.New({});
|
|
353
|
+
Http3WTSessionJS *sessionjs = Napi::ObjectWrap<Http3WTSessionJS>::Unwrap(sessionobj);
|
|
354
|
+
sessionjs->setObj(session);
|
|
355
|
+
sessionjs->Ref();
|
|
356
|
+
session->setJS(sessionjs);
|
|
357
|
+
|
|
358
|
+
Napi::Object objVal = Value().Get("jsobj").As<Napi::Object>();
|
|
359
|
+
|
|
360
|
+
Napi::Object retObj = Napi::Object::New(Env());
|
|
361
|
+
retObj.Set("session", sessionobj);
|
|
362
|
+
retObj.Set("path", path);
|
|
363
|
+
if (header)
|
|
364
|
+
{
|
|
365
|
+
retObj.Set("header", header->Value());
|
|
366
|
+
header->Unref();
|
|
367
|
+
}
|
|
368
|
+
objVal.Get("onHttpWTSessionVisitor")
|
|
369
|
+
.As<Napi::Function>()
|
|
370
|
+
.Call(objVal, {retObj});
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
void Http3ServerJS::processNewSessionRequest(WebTransportSession *session, const spdy::Http2HeaderBlock &reqhead, WebTransportRespPromisePtr *promise)
|
|
374
|
+
{
|
|
375
|
+
Napi::HandleScope scope(Env());
|
|
376
|
+
|
|
377
|
+
Napi::Object objVal = Value().Get("jsobj").As<Napi::Object>();
|
|
378
|
+
|
|
379
|
+
Napi::Object retObj = Napi::Object::New(Env());
|
|
380
|
+
|
|
381
|
+
// header
|
|
382
|
+
|
|
383
|
+
Napi::Object headObj = Napi::Object::New(Env());
|
|
384
|
+
for (auto pair : reqhead)
|
|
385
|
+
{
|
|
386
|
+
// we iterate over all header fields
|
|
387
|
+
headObj.Set(std::string(pair.first), std::string(pair.second));
|
|
388
|
+
}
|
|
389
|
+
retObj.Set("header", headObj);
|
|
390
|
+
// delete reqheadcopy; // we own it and must free it!
|
|
391
|
+
|
|
392
|
+
// promise
|
|
393
|
+
Napi::External<WebTransportRespPromisePtr> promObj =
|
|
394
|
+
Napi::External<WebTransportRespPromisePtr>::New(Env(), promise,
|
|
395
|
+
[](Napi::Env /*env*/, WebTransportRespPromisePtr *ref)
|
|
396
|
+
{
|
|
397
|
+
delete ref; // we own it and must delete it
|
|
398
|
+
});
|
|
399
|
+
retObj.Set("promise", promObj);
|
|
400
|
+
|
|
401
|
+
Napi::External<WebTransportSession> wtsObj =
|
|
402
|
+
Napi::External<WebTransportSession>::New(Env(), session,
|
|
403
|
+
[](Napi::Env /*env*/, WebTransportSession *ref)
|
|
404
|
+
{
|
|
405
|
+
// we do not own it! And do not delete it.
|
|
406
|
+
// does it outlife everything?
|
|
407
|
+
});
|
|
408
|
+
retObj.Set("session", wtsObj);
|
|
409
|
+
|
|
410
|
+
objVal.Get("onSessionRequest")
|
|
411
|
+
.As<Napi::Function>()
|
|
412
|
+
.Call(objVal, {retObj});
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
void Http3ServerJS::addPath(const Napi::CallbackInfo &info)
|
|
416
|
+
{
|
|
417
|
+
Http3Server *obj = getObj();
|
|
418
|
+
|
|
419
|
+
if (!info[0].IsUndefined())
|
|
420
|
+
{
|
|
421
|
+
|
|
422
|
+
std::string lpath(info[0].ToString().Utf8Value());
|
|
423
|
+
obj->http3_server_backend_.addPath(lpath);
|
|
424
|
+
}
|
|
425
|
+
else
|
|
426
|
+
return Napi::Error::New(Env(), "No path set for addPath").ThrowAsJavaScriptException();
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
void Http3ServerJS::finishSessionRequest(const Napi::CallbackInfo &info)
|
|
430
|
+
{
|
|
431
|
+
Http3Server *obj = getObj();
|
|
432
|
+
|
|
433
|
+
if (!info[0].IsUndefined())
|
|
434
|
+
{
|
|
435
|
+
// needs two properties
|
|
436
|
+
int status = -1;
|
|
437
|
+
std::string path = "";
|
|
438
|
+
Napi::Object lobj = info[0].ToObject();
|
|
439
|
+
if (!lobj.IsEmpty())
|
|
440
|
+
{
|
|
441
|
+
if (lobj.Has("status") && !(lobj).Get("status").IsEmpty())
|
|
442
|
+
{
|
|
443
|
+
Napi::Value statusValue = (lobj).Get("status");
|
|
444
|
+
status = statusValue.As<Napi::Number>().Int32Value();
|
|
445
|
+
}
|
|
446
|
+
else
|
|
447
|
+
return Napi::Error::New(Env(), "No status code passed for finishSessionRequest").ThrowAsJavaScriptException();
|
|
448
|
+
|
|
449
|
+
if (lobj.Has("path") && !(lobj).Get("path").IsEmpty())
|
|
450
|
+
{
|
|
451
|
+
Napi::Value pathValue = (lobj).Get("path");
|
|
452
|
+
path = pathValue.ToString().Utf8Value();
|
|
453
|
+
}
|
|
454
|
+
else
|
|
455
|
+
return Napi::Error::New(Env(), "No status code passed for finishSessionRequest").ThrowAsJavaScriptException();
|
|
456
|
+
WebTransportSession *session = nullptr;
|
|
457
|
+
if (lobj.Has("session") && !(lobj).Get("session").IsEmpty())
|
|
458
|
+
{
|
|
459
|
+
Napi::Value sessionVal = (lobj).Get("session");
|
|
460
|
+
if (!sessionVal.IsExternal())
|
|
461
|
+
return Napi::Error::New(Env(), "Session is not external for finishSessionRequest").ThrowAsJavaScriptException();
|
|
462
|
+
Napi::External<WebTransportSession> sessionExt = sessionVal.As<Napi::External<WebTransportSession>>();
|
|
463
|
+
session = sessionExt.Data();
|
|
464
|
+
}
|
|
465
|
+
else
|
|
466
|
+
return Napi::Error::New(Env(), "No session passed for finishSessionRequest").ThrowAsJavaScriptException();
|
|
467
|
+
|
|
468
|
+
if (lobj.Has("promise") && !(lobj).Get("promise").IsEmpty())
|
|
469
|
+
{
|
|
470
|
+
Napi::Value promiseVal = (lobj).Get("promise");
|
|
471
|
+
if (!promiseVal.IsExternal())
|
|
472
|
+
return Napi::Error::New(Env(), "Promise is not external for finishSessionRequest").ThrowAsJavaScriptException();
|
|
473
|
+
Napi::External<Http3ServerBackend::WebTransportRespPromisePtr> promise = promiseVal.As<Napi::External<Http3ServerBackend::WebTransportRespPromisePtr>>();
|
|
474
|
+
|
|
475
|
+
Http3ServerBackend::WebTransportRespPromisePtr *prom = promise.Data();
|
|
476
|
+
Napi::Reference<Napi::Value> *headerValue = nullptr;
|
|
477
|
+
if (status == 200)
|
|
478
|
+
{
|
|
479
|
+
|
|
480
|
+
if (lobj.Has("header") && !(lobj).Get("header").IsEmpty())
|
|
481
|
+
{
|
|
482
|
+
napi_ref ref;
|
|
483
|
+
napi_status status = napi_create_reference(Env(), lobj.Get("header"), 1, &ref);
|
|
484
|
+
NAPI_THROW_IF_FAILED(Env(), status, Reference<Napi::Value>());
|
|
485
|
+
headerValue = new Napi::Reference<Napi::Value>(Env(), ref);
|
|
486
|
+
}
|
|
487
|
+
else
|
|
488
|
+
return Napi::Error::New(Env(), "No status code passed for finishSessionRequest").ThrowAsJavaScriptException();
|
|
489
|
+
}
|
|
490
|
+
if (status != 200)
|
|
491
|
+
{
|
|
492
|
+
std::unique_ptr<Http3ServerBackend::WebTransportResponse> response = std::make_unique<Http3ServerBackend::WebTransportResponse>();
|
|
493
|
+
response->response_headers[":status"] = std::to_string(status);
|
|
494
|
+
(*prom)->resolve(std::move(response));
|
|
495
|
+
}
|
|
496
|
+
else
|
|
497
|
+
{
|
|
498
|
+
std::unique_ptr<Http3ServerBackend::WebTransportResponse> response = std::make_unique<Http3ServerBackend::WebTransportResponse>();
|
|
499
|
+
response->response_headers[":status"] = std::to_string(status);
|
|
500
|
+
Http3WTSession *wtsession = new Http3WTSession();
|
|
501
|
+
wtsession->init(session);
|
|
502
|
+
response->visitor =
|
|
503
|
+
std::make_unique<Http3WTSession::Visitor>(wtsession);
|
|
504
|
+
processNewSession(static_cast<Http3WTSession *>(wtsession), path, headerValue);
|
|
505
|
+
(*prom)->resolve(std::move(response));
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
else
|
|
509
|
+
return Napi::Error::New(Env(), "No promise passed for finishSessionRequest").ThrowAsJavaScriptException();
|
|
510
|
+
}
|
|
511
|
+
else
|
|
512
|
+
return Napi::Error::New(Env(), "No object passed for finishSessionRequest").ThrowAsJavaScriptException();
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
void Http3ServerJS::setJSRequestHandler(const Napi::CallbackInfo &info)
|
|
517
|
+
{
|
|
518
|
+
Http3Server *obj = getObj();
|
|
519
|
+
|
|
520
|
+
if (!info[0].IsUndefined())
|
|
521
|
+
{
|
|
522
|
+
|
|
523
|
+
bool hashandler = info[0].ToBoolean();
|
|
524
|
+
obj->http3_server_backend_.setJSHandler(hashandler);
|
|
525
|
+
}
|
|
526
|
+
else
|
|
527
|
+
return Napi::Error::New(Env(), "No bool set for setJSRequestHandler").ThrowAsJavaScriptException();
|
|
528
|
+
}
|
|
529
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
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, original copyright, see LICENSE.chromium
|
|
6
|
+
// Copyright (c) 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
|
+
#ifndef WT_HTTP3_SERVER_H
|
|
11
|
+
#define WT_HTTP3_SERVER_H
|
|
12
|
+
|
|
13
|
+
#include <memory>
|
|
14
|
+
|
|
15
|
+
#include <napi.h>
|
|
16
|
+
|
|
17
|
+
#include "src/librarymain.h"
|
|
18
|
+
#include "src/http3serverbackend.h"
|
|
19
|
+
#include "src/napialarmfactory.h"
|
|
20
|
+
#include "src/socketjswriter.h"
|
|
21
|
+
#include "quiche/quic/core/crypto/quic_crypto_server_config.h"
|
|
22
|
+
#include "quiche/quic/core/deterministic_connection_id_generator.h"
|
|
23
|
+
#include "quiche/quic/core/quic_udp_socket.h"
|
|
24
|
+
#include "quiche/quic/core/quic_dispatcher.h"
|
|
25
|
+
#include "quiche/quic/core/quic_packet_reader.h"
|
|
26
|
+
#include "quiche/quic/platform/api/quic_socket_address.h"
|
|
27
|
+
|
|
28
|
+
using namespace Napi;
|
|
29
|
+
|
|
30
|
+
namespace quic
|
|
31
|
+
{
|
|
32
|
+
|
|
33
|
+
class Http3EventLoop;
|
|
34
|
+
|
|
35
|
+
class Http3Server;
|
|
36
|
+
class Http3ServerJS;
|
|
37
|
+
class Http3WTSession;
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class Http3ServerJS : public Napi::ObjectWrap<Http3ServerJS>,
|
|
42
|
+
public EnvGetter
|
|
43
|
+
{
|
|
44
|
+
friend class Http3Server;
|
|
45
|
+
|
|
46
|
+
public:
|
|
47
|
+
using WebTransportRespPromise = JSlikePromise<Http3ServerBackend::WebTransportResponse>;
|
|
48
|
+
using WebTransportRespPromisePtr = std::shared_ptr<Http3ServerBackend::WebTransportRespPromise>;
|
|
49
|
+
Http3ServerJS(const Napi::CallbackInfo &info);
|
|
50
|
+
~Http3ServerJS();
|
|
51
|
+
|
|
52
|
+
Http3Server *getObj()
|
|
53
|
+
{
|
|
54
|
+
return server_.get();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
Napi::Env getEnv() override
|
|
58
|
+
{
|
|
59
|
+
return Env();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
Napi::Object getValue() override
|
|
63
|
+
{
|
|
64
|
+
return Value();
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
void destroy(const Napi::CallbackInfo &info);
|
|
68
|
+
|
|
69
|
+
void addPath(const Napi::CallbackInfo &info);
|
|
70
|
+
|
|
71
|
+
Napi::Value recvPaket(const Napi::CallbackInfo &info);
|
|
72
|
+
|
|
73
|
+
void onCanWrite(const Napi::CallbackInfo &info);
|
|
74
|
+
|
|
75
|
+
void finishSessionRequest(const Napi::CallbackInfo &info);
|
|
76
|
+
|
|
77
|
+
void setJSRequestHandler(const Napi::CallbackInfo &info);
|
|
78
|
+
|
|
79
|
+
void processBufferedChlos(const Napi::CallbackInfo &info);
|
|
80
|
+
|
|
81
|
+
static void InitExports(Napi::Env env, Napi::Object exports)
|
|
82
|
+
{
|
|
83
|
+
Napi::Function tplsrv = DefineClass(env, "Http3WebTransportServer", {InstanceMethod<&Http3ServerJS::destroy>("destroy", static_cast<napi_property_attributes>(napi_writable | napi_configurable)), InstanceMethod<&Http3ServerJS::addPath>("addPath", static_cast<napi_property_attributes>(napi_writable | napi_configurable)), InstanceMethod<&Http3ServerJS::recvPaket>("recvPaket", static_cast<napi_property_attributes>(napi_writable | napi_configurable)), InstanceMethod<&Http3ServerJS::processBufferedChlos>("processBufferedChlos", static_cast<napi_property_attributes>(napi_writable | napi_configurable)), InstanceMethod<&Http3ServerJS::onCanWrite>("onCanWrite", static_cast<napi_property_attributes>(napi_writable | napi_configurable)), InstanceMethod<&Http3ServerJS::finishSessionRequest>("finishSessionRequest", static_cast<napi_property_attributes>(napi_writable | napi_configurable)), InstanceMethod<&Http3ServerJS::setJSRequestHandler>("setJSRequestHandler", static_cast<napi_property_attributes>(napi_writable | napi_configurable))});
|
|
84
|
+
exports.Set("Http3WebTransportServer", tplsrv);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
void processNewSession(Http3WTSession *session, const std::string &path, Napi::Reference<Napi::Value> *header);
|
|
88
|
+
void processNewSessionRequest(WebTransportSession *session, const spdy::Http2HeaderBlock &reqheadcopy, WebTransportRespPromisePtr *promise);
|
|
89
|
+
|
|
90
|
+
protected:
|
|
91
|
+
std::unique_ptr<Http3Server> server_;
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
class Http3Server
|
|
95
|
+
{
|
|
96
|
+
friend class Http3ServerJS;
|
|
97
|
+
|
|
98
|
+
public:
|
|
99
|
+
Http3Server(Http3ServerJS *js,
|
|
100
|
+
std::unique_ptr<ProofSource> proof_source,
|
|
101
|
+
const char *secret,
|
|
102
|
+
QuicConfig config);
|
|
103
|
+
|
|
104
|
+
Http3Server(const Http3Server &) = delete;
|
|
105
|
+
Http3Server &operator=(const Http3Server &) = delete;
|
|
106
|
+
|
|
107
|
+
~Http3Server();
|
|
108
|
+
|
|
109
|
+
void Destroy();
|
|
110
|
+
|
|
111
|
+
bool ProcessPacket(const QuicSocketAddress &self_address,
|
|
112
|
+
const QuicSocketAddress &peer_address,
|
|
113
|
+
const QuicReceivedPacket &packet);
|
|
114
|
+
void ProcessBufferedChlos();
|
|
115
|
+
|
|
116
|
+
void OnCanWrite();
|
|
117
|
+
|
|
118
|
+
Http3ServerJS *getJS() { return js_; };
|
|
119
|
+
|
|
120
|
+
private:
|
|
121
|
+
Http3ServerJS *js_;
|
|
122
|
+
|
|
123
|
+
QuicPacketCount packets_dropped_;
|
|
124
|
+
std::unique_ptr<QuicPacketReader> packet_reader_;
|
|
125
|
+
std::unique_ptr<QuicDispatcher> dispatcher_;
|
|
126
|
+
// config_ contains non-crypto parameters that are negotiated in the crypto
|
|
127
|
+
// handshake.
|
|
128
|
+
QuicConfig config_;
|
|
129
|
+
// crypto_config_ contains crypto parameters for the handshake.
|
|
130
|
+
QuicCryptoServerConfig crypto_config_;
|
|
131
|
+
// crypto_config_options_ contains crypto parameters for the handshake.
|
|
132
|
+
QuicCryptoServerConfig::ConfigOptions crypto_config_options_;
|
|
133
|
+
|
|
134
|
+
// Used to generate current supported versions.
|
|
135
|
+
QuicVersionManager version_manager_;
|
|
136
|
+
|
|
137
|
+
Http3ServerBackend http3_server_backend_; // unowned.
|
|
138
|
+
|
|
139
|
+
// Connection ID length expected to be read on incoming IETF short headers.
|
|
140
|
+
uint8_t expected_server_connection_id_length_;
|
|
141
|
+
|
|
142
|
+
QuicDispatcher *CreateQuicDispatcher();
|
|
143
|
+
|
|
144
|
+
DeterministicConnectionIdGenerator connection_id_generator_;
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
#endif
|