@fails-components/webtransport 0.1.0-macarmbuild.5 → 0.1.0
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 +1 -0
- package/dist/lib/client.d.ts +74 -0
- package/dist/lib/client.d.ts.map +1 -0
- package/dist/lib/dom.d.ts +70 -0
- package/dist/lib/dom.d.ts.map +1 -0
- package/dist/lib/event-loop.d.ts +26 -0
- package/dist/lib/event-loop.d.ts.map +1 -0
- package/dist/lib/index.d.ts +79 -0
- package/dist/lib/index.d.ts.map +1 -0
- package/dist/lib/native.d.ts +2 -0
- package/dist/lib/native.d.ts.map +1 -0
- package/dist/lib/server.d.ts +88 -0
- package/dist/lib/server.d.ts.map +1 -0
- package/dist/lib/session.d.ts +225 -0
- package/dist/lib/session.d.ts.map +1 -0
- package/dist/lib/stream.d.ts +116 -0
- package/dist/lib/stream.d.ts.map +1 -0
- package/dist/lib/transport.d.ts +25 -0
- package/dist/lib/transport.d.ts.map +1 -0
- package/dist/lib/types.d.ts +164 -0
- package/dist/lib/types.d.ts.map +1 -0
- package/dist/lib/utils.d.ts +6 -0
- package/dist/lib/utils.d.ts.map +1 -0
- package/dist/lib/webtransport.d.ts +35 -0
- package/dist/lib/webtransport.d.ts.map +1 -0
- package/dist/test/bidirectional-streams.spec.d.ts +5 -0
- package/dist/test/bidirectional-streams.spec.d.ts.map +1 -0
- package/dist/test/datagrams.spec.d.ts +5 -0
- package/dist/test/datagrams.spec.d.ts.map +1 -0
- package/dist/test/echoclient.d.ts +2 -0
- package/dist/test/echoclient.d.ts.map +1 -0
- package/dist/test/echoserver.d.ts +2 -0
- package/dist/test/echoserver.d.ts.map +1 -0
- package/dist/test/event-loop.spec.d.ts +2 -0
- package/dist/test/event-loop.spec.d.ts.map +1 -0
- package/dist/test/fixtures/certificate.d.ts +21 -0
- package/dist/test/fixtures/certificate.d.ts.map +1 -0
- package/dist/test/fixtures/chai.d.ts +2 -0
- package/dist/test/fixtures/chai.d.ts.map +1 -0
- package/dist/test/fixtures/read-stream.d.ts +10 -0
- package/dist/test/fixtures/read-stream.d.ts.map +1 -0
- package/dist/test/fixtures/reader-value.d.ts +7 -0
- package/dist/test/fixtures/reader-value.d.ts.map +1 -0
- package/dist/test/fixtures/server.d.ts +10 -0
- package/dist/test/fixtures/server.d.ts.map +1 -0
- package/dist/test/fixtures/write-stream.d.ts +10 -0
- package/dist/test/fixtures/write-stream.d.ts.map +1 -0
- package/dist/test/test.d.ts +2 -0
- package/dist/test/test.d.ts.map +1 -0
- package/dist/test/testsuite.d.ts +25 -0
- package/dist/test/testsuite.d.ts.map +1 -0
- package/dist/test/unidirectional-streams.spec.d.ts +5 -0
- package/dist/test/unidirectional-streams.spec.d.ts.map +1 -0
- package/lib/index.js +1 -0
- package/lib/server.js +44 -4
- package/lib/session.js +3 -0
- package/lib/types.ts +16 -0
- package/lib/utils.js +1 -2
- package/package.json +5 -2
- package/src/http3eventloop.cc +124 -32
- package/src/http3eventloop.h +30 -17
- package/src/http3server.cc +111 -0
- package/src/http3server.h +14 -6
- package/src/http3serverbackend.cc +31 -18
- package/src/http3serverbackend.h +38 -3
- package/src/http3serverstream.cc +19 -6
- package/src/http3serverstream.h +2 -0
- package/src/http3wtstreamvisitor.cc +3 -0
- package/src/http3wtstreamvisitor.h +3 -1
- package/test/bidirectional-streams.spec.js +3 -2
- package/test/datagrams.spec.js +3 -2
- package/test/{event-loop.specoff.js → event-loop.spec.js} +7 -6
- package/test/fixtures/certificate.js +9 -9
- package/test/fixtures/chai.js +6 -0
- package/test/fixtures/reader-value.js +1 -2
- package/test/fixtures/write-stream.js +1 -2
- package/test/unidirectional-streams.spec.js +54 -1
- package/test/suite.specoff.js +0 -9
package/src/http3server.cc
CHANGED
|
@@ -400,5 +400,116 @@ namespace quic
|
|
|
400
400
|
};
|
|
401
401
|
obj->eventloop_->Schedule(task);
|
|
402
402
|
}
|
|
403
|
+
else
|
|
404
|
+
return Napi::Error::New(Env(), "No path set for addPath").ThrowAsJavaScriptException();
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
void Http3ServerJS::finishSessionRequest(const Napi::CallbackInfo &info)
|
|
408
|
+
{
|
|
409
|
+
Http3Server *obj = getObj();
|
|
410
|
+
|
|
411
|
+
if (!info[0].IsUndefined())
|
|
412
|
+
{
|
|
413
|
+
// needs two properties
|
|
414
|
+
int status = -1;
|
|
415
|
+
std::string path = "";
|
|
416
|
+
Napi::Object lobj = info[0].ToObject();
|
|
417
|
+
if (!lobj.IsEmpty())
|
|
418
|
+
{
|
|
419
|
+
if (lobj.Has("status") && !(lobj).Get("status").IsEmpty())
|
|
420
|
+
{
|
|
421
|
+
Napi::Value statusValue = (lobj).Get("status");
|
|
422
|
+
status = statusValue.As<Napi::Number>().Int32Value();
|
|
423
|
+
}
|
|
424
|
+
else
|
|
425
|
+
return Napi::Error::New(Env(), "No status code passed for finishSessionRequest").ThrowAsJavaScriptException();
|
|
426
|
+
|
|
427
|
+
if (lobj.Has("path") && !(lobj).Get("path").IsEmpty())
|
|
428
|
+
{
|
|
429
|
+
Napi::Value pathValue = (lobj).Get("path");
|
|
430
|
+
path = pathValue.ToString().Utf8Value();
|
|
431
|
+
}
|
|
432
|
+
else
|
|
433
|
+
return Napi::Error::New(Env(), "No status code passed for finishSessionRequest").ThrowAsJavaScriptException();
|
|
434
|
+
WebTransportSession *session = nullptr;
|
|
435
|
+
if (lobj.Has("session") && !(lobj).Get("session").IsEmpty())
|
|
436
|
+
{
|
|
437
|
+
Napi::Value sessionVal = (lobj).Get("session");
|
|
438
|
+
if (!sessionVal.IsExternal())
|
|
439
|
+
return Napi::Error::New(Env(), "Session is not external for finishSessionRequest").ThrowAsJavaScriptException();
|
|
440
|
+
Napi::External<WebTransportSession> sessionExt = sessionVal.As<Napi::External<WebTransportSession>>();
|
|
441
|
+
session = sessionExt.Data();
|
|
442
|
+
}
|
|
443
|
+
else
|
|
444
|
+
return Napi::Error::New(Env(), "No session passed for finishSessionRequest").ThrowAsJavaScriptException();
|
|
445
|
+
|
|
446
|
+
if (lobj.Has("promise") && !(lobj).Get("promise").IsEmpty())
|
|
447
|
+
{
|
|
448
|
+
Napi::Value promiseVal = (lobj).Get("promise");
|
|
449
|
+
if (!promiseVal.IsExternal())
|
|
450
|
+
return Napi::Error::New(Env(), "Promise is not external for finishSessionRequest").ThrowAsJavaScriptException();
|
|
451
|
+
Napi::External<Http3ServerBackend::WebTransportRespPromisePtr> promise = promiseVal.As<Napi::External<Http3ServerBackend::WebTransportRespPromisePtr>>();
|
|
452
|
+
|
|
453
|
+
Http3ServerBackend::WebTransportRespPromisePtr *prom = promise.Data();
|
|
454
|
+
Napi::Reference<Napi::Value> *headerValue = nullptr;
|
|
455
|
+
if (status == 200)
|
|
456
|
+
{
|
|
457
|
+
|
|
458
|
+
if (lobj.Has("header") && !(lobj).Get("header").IsEmpty())
|
|
459
|
+
{
|
|
460
|
+
napi_ref ref;
|
|
461
|
+
napi_status status = napi_create_reference(Env(), lobj.Get("header"), 1, &ref);
|
|
462
|
+
NAPI_THROW_IF_FAILED(Env(), status, Reference<Napi::Value>());
|
|
463
|
+
headerValue = new Napi::Reference<Napi::Value>(Env(), ref);
|
|
464
|
+
}
|
|
465
|
+
else
|
|
466
|
+
return Napi::Error::New(Env(), "No status code passed for finishSessionRequest").ThrowAsJavaScriptException();
|
|
467
|
+
}
|
|
468
|
+
std::function<void()> task = [status, prom, path, headerValue, session, obj, this]()
|
|
469
|
+
{
|
|
470
|
+
if (status != 200)
|
|
471
|
+
{
|
|
472
|
+
std::unique_ptr<Http3ServerBackend::WebTransportResponse> response = std::make_unique<Http3ServerBackend::WebTransportResponse>();
|
|
473
|
+
response->response_headers[":status"] = std::to_string(status);
|
|
474
|
+
(*prom)->resolve(std::move(response));
|
|
475
|
+
}
|
|
476
|
+
else
|
|
477
|
+
{
|
|
478
|
+
std::unique_ptr<Http3ServerBackend::WebTransportResponse> response = std::make_unique<Http3ServerBackend::WebTransportResponse>();
|
|
479
|
+
response->response_headers[":status"] = std::to_string(status);
|
|
480
|
+
Http3WTSession *wtsession = new Http3WTSession();
|
|
481
|
+
wtsession->init(session, obj->eventloop_);
|
|
482
|
+
response->visitor =
|
|
483
|
+
std::make_unique<Http3WTSession::Visitor>(wtsession);
|
|
484
|
+
obj->eventloop_->informAboutNewSession(obj, static_cast<Http3WTSession *>(wtsession), path, headerValue);
|
|
485
|
+
(*prom)->resolve(std::move(response));
|
|
486
|
+
}
|
|
487
|
+
};
|
|
488
|
+
obj->eventloop_->Schedule(task);
|
|
489
|
+
}
|
|
490
|
+
else
|
|
491
|
+
return Napi::Error::New(Env(), "No promise passed for finishSessionRequest").ThrowAsJavaScriptException();
|
|
492
|
+
}
|
|
493
|
+
else
|
|
494
|
+
return Napi::Error::New(Env(), "No object passed for finishSessionRequest").ThrowAsJavaScriptException();
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
void Http3ServerJS::setJSRequestHandler(const Napi::CallbackInfo &info)
|
|
499
|
+
{
|
|
500
|
+
Http3Server *obj = getObj();
|
|
501
|
+
|
|
502
|
+
if (!info[0].IsUndefined())
|
|
503
|
+
{
|
|
504
|
+
|
|
505
|
+
bool hashandler = info[0].ToBoolean();
|
|
506
|
+
std::function<void()> task = [obj, hashandler]()
|
|
507
|
+
{
|
|
508
|
+
obj->http3_server_backend_.setJSHandler(hashandler);
|
|
509
|
+
};
|
|
510
|
+
obj->eventloop_->Schedule(task);
|
|
511
|
+
}
|
|
512
|
+
else
|
|
513
|
+
return Napi::Error::New(Env(), "No bool set for setJSRequestHandler").ThrowAsJavaScriptException();
|
|
403
514
|
}
|
|
404
515
|
}
|
package/src/http3server.h
CHANGED
|
@@ -50,6 +50,9 @@ namespace quic
|
|
|
50
50
|
|
|
51
51
|
void addPath(const Napi::CallbackInfo &info);
|
|
52
52
|
|
|
53
|
+
void finishSessionRequest(const Napi::CallbackInfo &info);
|
|
54
|
+
|
|
55
|
+
void setJSRequestHandler(const Napi::CallbackInfo &info);
|
|
53
56
|
|
|
54
57
|
static void InitExports(Napi::Env env, Napi::Object exports)
|
|
55
58
|
{
|
|
@@ -59,7 +62,11 @@ namespace quic
|
|
|
59
62
|
InstanceMethod<&Http3ServerJS::stopServer>("stopServer",
|
|
60
63
|
static_cast<napi_property_attributes>(napi_writable | napi_configurable)),
|
|
61
64
|
InstanceMethod<&Http3ServerJS::addPath>("addPath",
|
|
62
|
-
static_cast<napi_property_attributes>(napi_writable | napi_configurable))
|
|
65
|
+
static_cast<napi_property_attributes>(napi_writable | napi_configurable)),
|
|
66
|
+
InstanceMethod<&Http3ServerJS::finishSessionRequest>("finishSessionRequest",
|
|
67
|
+
static_cast<napi_property_attributes>(napi_writable | napi_configurable)),
|
|
68
|
+
InstanceMethod<&Http3ServerJS::setJSRequestHandler>("setJSRequestHandler",
|
|
69
|
+
static_cast<napi_property_attributes>(napi_writable | napi_configurable))});
|
|
63
70
|
exports.Set("Http3WebTransportServer", tplsrv);
|
|
64
71
|
}
|
|
65
72
|
|
|
@@ -95,11 +102,12 @@ namespace quic
|
|
|
95
102
|
|
|
96
103
|
Http3ServerJS *getJS() { return js_; };
|
|
97
104
|
|
|
98
|
-
ServerStatusDetails *getStatusDetails()
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
105
|
+
ServerStatusDetails *getStatusDetails()
|
|
106
|
+
{
|
|
107
|
+
ServerStatusDetails *details = new ServerStatusDetails();
|
|
108
|
+
details->host = host_;
|
|
109
|
+
details->port = port_;
|
|
110
|
+
return details;
|
|
103
111
|
}
|
|
104
112
|
|
|
105
113
|
private:
|
|
@@ -26,7 +26,6 @@
|
|
|
26
26
|
#include "quiche/common/platform/api/quiche_file_utils.h"
|
|
27
27
|
#include "quiche/common/quiche_text_utils.h"
|
|
28
28
|
|
|
29
|
-
|
|
30
29
|
using spdy::Http2HeaderBlock;
|
|
31
30
|
|
|
32
31
|
namespace quic
|
|
@@ -45,42 +44,56 @@ namespace quic
|
|
|
45
44
|
}
|
|
46
45
|
*/
|
|
47
46
|
|
|
48
|
-
Http3ServerBackend::
|
|
47
|
+
Http3ServerBackend::WebTransportRespPromisePtr
|
|
49
48
|
Http3ServerBackend::ProcessWebTransportRequest(
|
|
50
49
|
const spdy::Http2HeaderBlock &request_headers,
|
|
51
50
|
WebTransportSession *session)
|
|
52
51
|
{
|
|
52
|
+
WebTransportRespPromisePtr promise = std::make_shared<WebTransportRespPromise>();
|
|
53
53
|
if (!SupportsWebTransport())
|
|
54
54
|
{
|
|
55
|
-
WebTransportResponse response;
|
|
56
|
-
response
|
|
57
|
-
|
|
55
|
+
std::unique_ptr<WebTransportResponse> response = std::make_unique<WebTransportResponse>();
|
|
56
|
+
response->response_headers[":status"] = "400";
|
|
57
|
+
promise->resolve(std::move(response));
|
|
58
|
+
return promise;
|
|
58
59
|
}
|
|
59
60
|
|
|
60
61
|
auto path_it = request_headers.find(":path");
|
|
61
62
|
if (path_it == request_headers.end())
|
|
62
63
|
{
|
|
63
|
-
WebTransportResponse response;
|
|
64
|
-
response
|
|
65
|
-
|
|
64
|
+
std::unique_ptr<WebTransportResponse> response = std::make_unique<WebTransportResponse>();
|
|
65
|
+
response->response_headers[":status"] = "400";
|
|
66
|
+
promise->resolve(std::move(response));
|
|
67
|
+
return promise;
|
|
66
68
|
}
|
|
67
69
|
std::string path(path_it->second);
|
|
68
70
|
|
|
69
71
|
if (paths_.find(path) != paths_.end())
|
|
70
72
|
{ // to do handle our web transport paths
|
|
71
|
-
WebTransportResponse response;
|
|
72
|
-
Http3WTSession *
|
|
73
|
+
std::unique_ptr<WebTransportResponse> response = std::make_unique<WebTransportResponse>();
|
|
74
|
+
Http3WTSession *wtsession = new Http3WTSession();
|
|
73
75
|
wtsession->init(session, eventloop_);
|
|
74
|
-
response
|
|
75
|
-
response
|
|
76
|
-
std::make_unique<Http3WTSession::Visitor>(wtsession);
|
|
77
|
-
eventloop_->informAboutNewSession(server_, static_cast<Http3WTSession *>(wtsession), path);
|
|
78
|
-
|
|
76
|
+
response->response_headers[":status"] = "200";
|
|
77
|
+
response->visitor =
|
|
78
|
+
std::make_unique<Http3WTSession::Visitor>(wtsession);
|
|
79
|
+
eventloop_->informAboutNewSession(server_, static_cast<Http3WTSession *>(wtsession), path, nullptr);
|
|
80
|
+
promise->resolve(std::move(response));
|
|
81
|
+
return promise;
|
|
82
|
+
}
|
|
83
|
+
else if (jshandlerequesthandler_)
|
|
84
|
+
{
|
|
85
|
+
// first step pass header block along, due to thread safety, we need to copy
|
|
86
|
+
spdy::Http2HeaderBlock *reqheadcopy = new Http2HeaderBlock(request_headers.Clone());
|
|
87
|
+
|
|
88
|
+
// second step inform the js side
|
|
89
|
+
eventloop_->informAboutNewSessionRequest(server_, session, reqheadcopy, promise);
|
|
90
|
+
return promise;
|
|
79
91
|
}
|
|
80
92
|
|
|
81
|
-
WebTransportResponse response;
|
|
82
|
-
response
|
|
83
|
-
|
|
93
|
+
std::unique_ptr<WebTransportResponse> response = std::make_unique<WebTransportResponse>();
|
|
94
|
+
response->response_headers[":status"] = "404";
|
|
95
|
+
promise->resolve(std::move(response));
|
|
96
|
+
return promise;
|
|
84
97
|
}
|
|
85
98
|
|
|
86
99
|
Http3ServerBackend::~Http3ServerBackend()
|
package/src/http3serverbackend.h
CHANGED
|
@@ -23,6 +23,36 @@ namespace quic
|
|
|
23
23
|
class Http3Server;
|
|
24
24
|
class Http3EventLoop;
|
|
25
25
|
|
|
26
|
+
template <typename T>
|
|
27
|
+
class JSlikePromise
|
|
28
|
+
{
|
|
29
|
+
public:
|
|
30
|
+
void finally(std::function<void(T *)> func)
|
|
31
|
+
{
|
|
32
|
+
T *res = result.get();
|
|
33
|
+
if (res != nullptr)
|
|
34
|
+
func(res);
|
|
35
|
+
else
|
|
36
|
+
finallys.push_back(func);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
void resolve(std::unique_ptr<T> res)
|
|
40
|
+
{
|
|
41
|
+
if (result.get() != nullptr)
|
|
42
|
+
return; // throw std::runtime_error("Promise already settled");
|
|
43
|
+
result = std::move(res);
|
|
44
|
+
for (auto finally : finallys)
|
|
45
|
+
{
|
|
46
|
+
finally(result.get());
|
|
47
|
+
}
|
|
48
|
+
finallys.clear();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
protected:
|
|
52
|
+
std::unique_ptr<T> result;
|
|
53
|
+
std::list<std::function<void(T *)>> finallys;
|
|
54
|
+
};
|
|
55
|
+
|
|
26
56
|
// This interface implements the functionality to fetch a response
|
|
27
57
|
// from the backend (such as cache, http-proxy etc) to serve
|
|
28
58
|
// requests received by a Quic Server
|
|
@@ -36,14 +66,18 @@ namespace quic
|
|
|
36
66
|
std::unique_ptr<WebTransportVisitor> visitor;
|
|
37
67
|
};
|
|
38
68
|
|
|
69
|
+
using WebTransportRespPromise = JSlikePromise<WebTransportResponse>;
|
|
70
|
+
using WebTransportRespPromisePtr = std::shared_ptr<WebTransportRespPromise>;
|
|
71
|
+
|
|
39
72
|
Http3ServerBackend(Http3EventLoop *eventloop) : eventloop_(eventloop),
|
|
40
|
-
|
|
73
|
+
server_(nullptr), jshandlerequesthandler_(false) {}
|
|
41
74
|
|
|
42
75
|
~Http3ServerBackend();
|
|
43
76
|
|
|
44
77
|
void setServer(Http3Server *server) { server_ = server; }
|
|
78
|
+
void setJSHandler(bool on) { jshandlerequesthandler_ = on; }
|
|
45
79
|
|
|
46
|
-
|
|
80
|
+
WebTransportRespPromisePtr ProcessWebTransportRequest(
|
|
47
81
|
const spdy::Http2HeaderBlock & /*request_headers*/,
|
|
48
82
|
WebTransportSession * /*session*/);
|
|
49
83
|
bool SupportsWebTransport() { return true; }
|
|
@@ -53,8 +87,9 @@ namespace quic
|
|
|
53
87
|
void addPath(std::string path) { paths_.insert(path); }
|
|
54
88
|
|
|
55
89
|
protected:
|
|
56
|
-
Http3Server *server_;
|
|
90
|
+
Http3Server *server_; // unowned
|
|
57
91
|
Http3EventLoop *eventloop_; // unowned
|
|
92
|
+
bool jshandlerequesthandler_;
|
|
58
93
|
std::set<std::string> paths_;
|
|
59
94
|
};
|
|
60
95
|
|
package/src/http3serverstream.cc
CHANGED
|
@@ -59,6 +59,13 @@ namespace quic
|
|
|
59
59
|
Http3ServerStream::~Http3ServerStream()
|
|
60
60
|
{
|
|
61
61
|
// http3_server_backend_->CloseBackendResponseStream(this);
|
|
62
|
+
for (auto prom : pending_proms_)
|
|
63
|
+
{
|
|
64
|
+
std::unique_ptr<Http3ServerBackend::WebTransportResponse> response =
|
|
65
|
+
std::make_unique<Http3ServerBackend::WebTransportResponse>();
|
|
66
|
+
response->response_headers[":status"] = "500"; // internal server error, probably implementation on js side
|
|
67
|
+
prom->resolve(std::move(response));
|
|
68
|
+
}
|
|
62
69
|
}
|
|
63
70
|
|
|
64
71
|
void Http3ServerStream::OnInitialHeadersComplete(
|
|
@@ -211,22 +218,28 @@ namespace quic
|
|
|
211
218
|
|
|
212
219
|
if (web_transport() != nullptr)
|
|
213
220
|
{
|
|
214
|
-
|
|
221
|
+
// to do synchronize lifetime of object, so maybe store promises
|
|
222
|
+
Http3ServerBackend::WebTransportRespPromisePtr response =
|
|
215
223
|
http3_server_backend_->ProcessWebTransportRequest(
|
|
216
224
|
request_headers_, web_transport());
|
|
217
|
-
|
|
225
|
+
pending_proms_.insert(response);
|
|
226
|
+
response->finally([this, response](Http3ServerBackend::WebTransportResponse *resp)
|
|
227
|
+
{
|
|
228
|
+
pending_proms_.erase(response);
|
|
229
|
+
if (resp->response_headers[":status"] == "200")
|
|
218
230
|
{
|
|
219
|
-
WriteHeaders(std::move(
|
|
220
|
-
if (
|
|
231
|
+
WriteHeaders(std::move(resp->response_headers), false, nullptr);
|
|
232
|
+
if (resp->visitor != nullptr)
|
|
221
233
|
{
|
|
222
|
-
web_transport()->SetVisitor(std::move(
|
|
234
|
+
web_transport()->SetVisitor(std::move(resp->visitor));
|
|
223
235
|
}
|
|
224
236
|
web_transport()->HeadersReceived(request_headers_);
|
|
225
237
|
}
|
|
226
238
|
else
|
|
227
239
|
{
|
|
228
|
-
WriteHeaders(std::move(
|
|
240
|
+
WriteHeaders(std::move(resp->response_headers), true, nullptr);
|
|
229
241
|
}
|
|
242
|
+
return; });
|
|
230
243
|
return;
|
|
231
244
|
}
|
|
232
245
|
|
package/src/http3serverstream.h
CHANGED
|
@@ -103,6 +103,8 @@ namespace quic
|
|
|
103
103
|
spdy::Http2HeaderBlock request_headers_;
|
|
104
104
|
int64_t content_length_;
|
|
105
105
|
std::string body_;
|
|
106
|
+
// promises not fullfilled from js side or c++ side
|
|
107
|
+
std::set<Http3ServerBackend::WebTransportRespPromisePtr> pending_proms_;
|
|
106
108
|
|
|
107
109
|
private:
|
|
108
110
|
uint64_t generate_bytes_length_;
|
|
@@ -69,7 +69,10 @@ namespace quic
|
|
|
69
69
|
void Http3WTStream::doCanRead()
|
|
70
70
|
{
|
|
71
71
|
if (pause_reading_)
|
|
72
|
+
{
|
|
73
|
+
can_read_pending_ = true;
|
|
72
74
|
return; // back pressure folks!
|
|
75
|
+
}
|
|
73
76
|
// first figure out if we have readable data
|
|
74
77
|
size_t readable = stream_->ReadableBytes();
|
|
75
78
|
// ok create a string obj to hold the data
|
|
@@ -93,8 +93,9 @@ namespace quic
|
|
|
93
93
|
void tryRead()
|
|
94
94
|
{
|
|
95
95
|
pause_reading_ = false;
|
|
96
|
-
if (stream_ && stream_->ReadableBytes() > 0)
|
|
96
|
+
if (stream_ && ((stream_->ReadableBytes() > 0) || can_read_pending_))
|
|
97
97
|
{
|
|
98
|
+
can_read_pending_ = false;
|
|
98
99
|
doCanRead();
|
|
99
100
|
}
|
|
100
101
|
}
|
|
@@ -209,6 +210,7 @@ namespace quic
|
|
|
209
210
|
bool fin_was_sent_ = false;
|
|
210
211
|
bool stop_sending_received_ = false;
|
|
211
212
|
bool pause_reading_ = false;
|
|
213
|
+
bool can_read_pending_ = false;
|
|
212
214
|
std::deque<WChunks> chunks_;
|
|
213
215
|
};
|
|
214
216
|
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
/* eslint-
|
|
1
|
+
/* eslint-env mocha */
|
|
2
|
+
|
|
2
3
|
import { createServer } from './fixtures/server.js'
|
|
3
4
|
import { getReaderValue } from './fixtures/reader-value.js'
|
|
4
5
|
import { WebTransport } from '../lib/index.js'
|
|
5
|
-
import { expect } from 'chai'
|
|
6
|
+
import { expect } from './fixtures/chai.js'
|
|
6
7
|
import { readStream } from './fixtures/read-stream.js'
|
|
7
8
|
import { writeStream } from './fixtures/write-stream.js'
|
|
8
9
|
import { defer } from '../lib/utils.js'
|
package/test/datagrams.spec.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
/* eslint-
|
|
1
|
+
/* eslint-env mocha */
|
|
2
|
+
|
|
2
3
|
import { createServer } from './fixtures/server.js'
|
|
3
4
|
import { getReaderValue } from './fixtures/reader-value.js'
|
|
4
5
|
import { WebTransport } from '../lib/index.js'
|
|
5
|
-
import { expect } from 'chai'
|
|
6
|
+
import { expect } from './fixtures/chai.js'
|
|
6
7
|
import { readStream } from './fixtures/read-stream.js'
|
|
7
8
|
import { writeStream } from './fixtures/write-stream.js'
|
|
8
9
|
import { defer } from '../lib/utils.js'
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
/* eslint-
|
|
2
|
-
|
|
1
|
+
/* eslint-env mocha */
|
|
2
|
+
|
|
3
|
+
import { expect } from './fixtures/chai.js'
|
|
3
4
|
import { Http3EventLoop } from '../lib/event-loop.js'
|
|
4
5
|
|
|
5
|
-
describe('event-loop', () => {
|
|
6
|
+
describe.skip('event-loop', () => {
|
|
6
7
|
beforeEach(() => {
|
|
7
8
|
if (Http3EventLoop.globalLoop != null) {
|
|
8
9
|
// shut down loop, otherwise we have to wait for
|
|
@@ -12,12 +13,12 @@ describe('event-loop', () => {
|
|
|
12
13
|
})
|
|
13
14
|
|
|
14
15
|
it('should start and stop the event loop', () => {
|
|
15
|
-
expect(Http3EventLoop.globalLoop).to.be.null
|
|
16
|
+
expect(Http3EventLoop.globalLoop).to.be.null()
|
|
16
17
|
|
|
17
18
|
Http3EventLoop.createGlobalEventLoop()
|
|
18
|
-
expect(Http3EventLoop.globalLoop).to.not.be.null
|
|
19
|
+
expect(Http3EventLoop.globalLoop).to.not.be.null()
|
|
19
20
|
|
|
20
21
|
Http3EventLoop.globalLoop?.shutdownEventLoop()
|
|
21
|
-
expect(Http3EventLoop.globalLoop).to.be.null
|
|
22
|
+
expect(Http3EventLoop.globalLoop).to.be.null()
|
|
22
23
|
})
|
|
23
24
|
})
|
|
@@ -55,8 +55,7 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
|
|
|
55
55
|
|
|
56
56
|
// @ts-expect-error node-forge has no types and @types/node-forge do not include oids
|
|
57
57
|
import forge from 'node-forge'
|
|
58
|
-
import { webcrypto as crypto } from 'crypto'
|
|
59
|
-
import { X509Certificate } from 'crypto'
|
|
58
|
+
import { webcrypto as crypto, X509Certificate } from 'crypto'
|
|
60
59
|
|
|
61
60
|
const { pki, asn1, oids } = forge
|
|
62
61
|
// taken from node-forge
|
|
@@ -111,8 +110,8 @@ function _dnToAsn1(obj) {
|
|
|
111
110
|
return rval
|
|
112
111
|
}
|
|
113
112
|
|
|
114
|
-
const jan_1_1950 = new Date('1950-01-01T00:00:00Z')
|
|
115
|
-
const jan_1_2050 = new Date('2050-01-01T00:00:00Z')
|
|
113
|
+
const jan_1_1950 = new Date('1950-01-01T00:00:00Z') // eslint-disable-line camelcase
|
|
114
|
+
const jan_1_2050 = new Date('2050-01-01T00:00:00Z') // eslint-disable-line camelcase
|
|
116
115
|
// taken from node-forge almost not modified
|
|
117
116
|
/**
|
|
118
117
|
* Converts a Date object to ASN.1
|
|
@@ -123,6 +122,7 @@ const jan_1_2050 = new Date('2050-01-01T00:00:00Z')
|
|
|
123
122
|
* @return the ASN.1 object representing the date.
|
|
124
123
|
*/
|
|
125
124
|
function _dateToAsn1(date) {
|
|
125
|
+
// eslint-disable-next-line camelcase
|
|
126
126
|
if (date >= jan_1_1950 && date < jan_1_2050) {
|
|
127
127
|
return asn1.create(
|
|
128
128
|
asn1.Class.UNIVERSAL,
|
|
@@ -149,10 +149,10 @@ function _dateToAsn1(date) {
|
|
|
149
149
|
* @return ASN.1 object representing signature parameters
|
|
150
150
|
*/
|
|
151
151
|
function _signatureParametersToAsn1(oid, params) {
|
|
152
|
+
const parts = []
|
|
153
|
+
|
|
152
154
|
switch (oid) {
|
|
153
155
|
case oids['RSASSA-PSS']:
|
|
154
|
-
const parts = []
|
|
155
|
-
|
|
156
156
|
if (params.hash.algorithmOid !== undefined) {
|
|
157
157
|
parts.push(
|
|
158
158
|
asn1.create(asn1.Class.CONTEXT_SPECIFIC, 0, true, [
|
|
@@ -348,7 +348,7 @@ function toPositiveHex(hexString) {
|
|
|
348
348
|
*/
|
|
349
349
|
export async function generateWebTransportCertificate(attrs, options) {
|
|
350
350
|
try {
|
|
351
|
-
|
|
351
|
+
const keyPair = await crypto.subtle.generateKey(
|
|
352
352
|
{
|
|
353
353
|
name: 'ECDSA',
|
|
354
354
|
namedCurve: 'P-256'
|
|
@@ -371,7 +371,7 @@ export async function generateWebTransportCertificate(attrs, options) {
|
|
|
371
371
|
cert.setSubject(attrs)
|
|
372
372
|
cert.setIssuer(attrs)
|
|
373
373
|
|
|
374
|
-
|
|
374
|
+
const privateKey = crypto.subtle.exportKey('pkcs8', keyPair.privateKey)
|
|
375
375
|
const publicKey = (cert.publicKey = await crypto.subtle.exportKey(
|
|
376
376
|
'spki',
|
|
377
377
|
keyPair.publicKey
|
|
@@ -408,7 +408,7 @@ export async function generateWebTransportCertificate(attrs, options) {
|
|
|
408
408
|
oids['1.2.840.10045.4.3.2'] = 'ecdsa-with-sha256'
|
|
409
409
|
oids['ecdsa-with-sha256'] = '1.2.840.10045.4.3.2'
|
|
410
410
|
|
|
411
|
-
cert.siginfo.algorithmOid = cert.signatureOid = '1.2.840.10045.4.3.2' //'ecdsa-with-sha256'
|
|
411
|
+
cert.siginfo.algorithmOid = cert.signatureOid = '1.2.840.10045.4.3.2' // 'ecdsa-with-sha256'
|
|
412
412
|
|
|
413
413
|
cert.tbsCertificate = getTBSCertificate(cert)
|
|
414
414
|
const encoded = Buffer.from(
|
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
|
|
2
1
|
/**
|
|
3
2
|
* @template T
|
|
4
3
|
* @param {ReadableStream<T>} readableStream
|
|
5
4
|
* @returns {Promise<T>}
|
|
6
5
|
*/
|
|
7
|
-
export async function getReaderValue
|
|
6
|
+
export async function getReaderValue(readableStream) {
|
|
8
7
|
const reader = readableStream.getReader()
|
|
9
8
|
|
|
10
9
|
try {
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
|
|
2
1
|
/**
|
|
3
2
|
* Write contents to a stream and close it
|
|
4
3
|
*
|
|
@@ -7,7 +6,7 @@
|
|
|
7
6
|
* @param {T[]} input
|
|
8
7
|
* @returns
|
|
9
8
|
*/
|
|
10
|
-
|
|
9
|
+
export async function writeStream(writable, input) {
|
|
11
10
|
const writer = writable.getWriter()
|
|
12
11
|
|
|
13
12
|
for (const buf of input) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
/* eslint-
|
|
1
|
+
/* eslint-env mocha */
|
|
2
|
+
|
|
2
3
|
import { createServer } from './fixtures/server.js'
|
|
3
4
|
import { getReaderValue } from './fixtures/reader-value.js'
|
|
4
5
|
import { WebTransport } from '../lib/index.js'
|
|
@@ -124,4 +125,56 @@ describe('unidirectional streams', function () {
|
|
|
124
125
|
'Did not receive the same bytes we sent'
|
|
125
126
|
)
|
|
126
127
|
})
|
|
128
|
+
|
|
129
|
+
it('handles fin when paused due to backpressure', async function () {
|
|
130
|
+
this.timeout(10000)
|
|
131
|
+
|
|
132
|
+
/** @type {Deferred<ReadableStream>} */
|
|
133
|
+
const serverStream = defer()
|
|
134
|
+
|
|
135
|
+
Promise.resolve().then(async () => {
|
|
136
|
+
const session = await getReaderValue(server.sessionStream(SERVER_PATH))
|
|
137
|
+
const stream = await getReaderValue(session.incomingUnidirectionalStreams)
|
|
138
|
+
|
|
139
|
+
serverStream.resolve(stream)
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
client = new WebTransport(`${url}${SERVER_PATH}`, {
|
|
143
|
+
serverCertificateHashes: [
|
|
144
|
+
{
|
|
145
|
+
algorithm: 'sha-256',
|
|
146
|
+
value: certificate.hash
|
|
147
|
+
}
|
|
148
|
+
]
|
|
149
|
+
})
|
|
150
|
+
await client.ready
|
|
151
|
+
|
|
152
|
+
const clientStream = await client.createUnidirectionalStream()
|
|
153
|
+
|
|
154
|
+
const input = [
|
|
155
|
+
Uint8Array.from([0, 1, 2, 3, 4]),
|
|
156
|
+
Uint8Array.from([5, 6, 7, 8, 9]),
|
|
157
|
+
Uint8Array.from([10, 11, 12, 13, 14]),
|
|
158
|
+
Uint8Array.from([15, 16, 17, 18, 19]),
|
|
159
|
+
Uint8Array.from([20, 21, 22, 23, 24])
|
|
160
|
+
]
|
|
161
|
+
|
|
162
|
+
const writer = clientStream.getWriter()
|
|
163
|
+
|
|
164
|
+
for (const buf of input) {
|
|
165
|
+
await writer.write(buf)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
await new Promise((resolve) => setTimeout(resolve, 1000))
|
|
169
|
+
|
|
170
|
+
await writer.close()
|
|
171
|
+
|
|
172
|
+
await new Promise((resolve) => setTimeout(resolve, 1000))
|
|
173
|
+
|
|
174
|
+
const received = await readStream(await serverStream.promise)
|
|
175
|
+
expect(received).to.deep.equal(
|
|
176
|
+
input,
|
|
177
|
+
'Did not receive the same bytes we sent'
|
|
178
|
+
)
|
|
179
|
+
})
|
|
127
180
|
})
|
package/test/suite.specoff.js
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { Http3EventLoop } from '../lib/event-loop.js'
|
|
2
|
-
|
|
3
|
-
after(async () => {
|
|
4
|
-
if (Http3EventLoop.globalLoop != null) {
|
|
5
|
-
// shut down loop, otherwise we have to wait for
|
|
6
|
-
// it to time out which takes a long time.
|
|
7
|
-
Http3EventLoop.globalLoop.shutdownEventLoop()
|
|
8
|
-
}
|
|
9
|
-
})
|