@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.
Files changed (78) hide show
  1. package/CMakeLists.txt +1 -0
  2. package/dist/lib/client.d.ts +74 -0
  3. package/dist/lib/client.d.ts.map +1 -0
  4. package/dist/lib/dom.d.ts +70 -0
  5. package/dist/lib/dom.d.ts.map +1 -0
  6. package/dist/lib/event-loop.d.ts +26 -0
  7. package/dist/lib/event-loop.d.ts.map +1 -0
  8. package/dist/lib/index.d.ts +79 -0
  9. package/dist/lib/index.d.ts.map +1 -0
  10. package/dist/lib/native.d.ts +2 -0
  11. package/dist/lib/native.d.ts.map +1 -0
  12. package/dist/lib/server.d.ts +88 -0
  13. package/dist/lib/server.d.ts.map +1 -0
  14. package/dist/lib/session.d.ts +225 -0
  15. package/dist/lib/session.d.ts.map +1 -0
  16. package/dist/lib/stream.d.ts +116 -0
  17. package/dist/lib/stream.d.ts.map +1 -0
  18. package/dist/lib/transport.d.ts +25 -0
  19. package/dist/lib/transport.d.ts.map +1 -0
  20. package/dist/lib/types.d.ts +164 -0
  21. package/dist/lib/types.d.ts.map +1 -0
  22. package/dist/lib/utils.d.ts +6 -0
  23. package/dist/lib/utils.d.ts.map +1 -0
  24. package/dist/lib/webtransport.d.ts +35 -0
  25. package/dist/lib/webtransport.d.ts.map +1 -0
  26. package/dist/test/bidirectional-streams.spec.d.ts +5 -0
  27. package/dist/test/bidirectional-streams.spec.d.ts.map +1 -0
  28. package/dist/test/datagrams.spec.d.ts +5 -0
  29. package/dist/test/datagrams.spec.d.ts.map +1 -0
  30. package/dist/test/echoclient.d.ts +2 -0
  31. package/dist/test/echoclient.d.ts.map +1 -0
  32. package/dist/test/echoserver.d.ts +2 -0
  33. package/dist/test/echoserver.d.ts.map +1 -0
  34. package/dist/test/event-loop.spec.d.ts +2 -0
  35. package/dist/test/event-loop.spec.d.ts.map +1 -0
  36. package/dist/test/fixtures/certificate.d.ts +21 -0
  37. package/dist/test/fixtures/certificate.d.ts.map +1 -0
  38. package/dist/test/fixtures/chai.d.ts +2 -0
  39. package/dist/test/fixtures/chai.d.ts.map +1 -0
  40. package/dist/test/fixtures/read-stream.d.ts +10 -0
  41. package/dist/test/fixtures/read-stream.d.ts.map +1 -0
  42. package/dist/test/fixtures/reader-value.d.ts +7 -0
  43. package/dist/test/fixtures/reader-value.d.ts.map +1 -0
  44. package/dist/test/fixtures/server.d.ts +10 -0
  45. package/dist/test/fixtures/server.d.ts.map +1 -0
  46. package/dist/test/fixtures/write-stream.d.ts +10 -0
  47. package/dist/test/fixtures/write-stream.d.ts.map +1 -0
  48. package/dist/test/test.d.ts +2 -0
  49. package/dist/test/test.d.ts.map +1 -0
  50. package/dist/test/testsuite.d.ts +25 -0
  51. package/dist/test/testsuite.d.ts.map +1 -0
  52. package/dist/test/unidirectional-streams.spec.d.ts +5 -0
  53. package/dist/test/unidirectional-streams.spec.d.ts.map +1 -0
  54. package/lib/index.js +1 -0
  55. package/lib/server.js +44 -4
  56. package/lib/session.js +3 -0
  57. package/lib/types.ts +16 -0
  58. package/lib/utils.js +1 -2
  59. package/package.json +5 -2
  60. package/src/http3eventloop.cc +124 -32
  61. package/src/http3eventloop.h +30 -17
  62. package/src/http3server.cc +111 -0
  63. package/src/http3server.h +14 -6
  64. package/src/http3serverbackend.cc +31 -18
  65. package/src/http3serverbackend.h +38 -3
  66. package/src/http3serverstream.cc +19 -6
  67. package/src/http3serverstream.h +2 -0
  68. package/src/http3wtstreamvisitor.cc +3 -0
  69. package/src/http3wtstreamvisitor.h +3 -1
  70. package/test/bidirectional-streams.spec.js +3 -2
  71. package/test/datagrams.spec.js +3 -2
  72. package/test/{event-loop.specoff.js → event-loop.spec.js} +7 -6
  73. package/test/fixtures/certificate.js +9 -9
  74. package/test/fixtures/chai.js +6 -0
  75. package/test/fixtures/reader-value.js +1 -2
  76. package/test/fixtures/write-stream.js +1 -2
  77. package/test/unidirectional-streams.spec.js +54 -1
  78. package/test/suite.specoff.js +0 -9
@@ -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
- ServerStatusDetails* details = new ServerStatusDetails();
100
- details->host = host_;
101
- details->port = port_;
102
- return details;
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::WebTransportResponse
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.response_headers[":status"] = "400";
57
- return response;
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.response_headers[":status"] = "400";
65
- return response;
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 * wtsession = new Http3WTSession();
73
+ std::unique_ptr<WebTransportResponse> response = std::make_unique<WebTransportResponse>();
74
+ Http3WTSession *wtsession = new Http3WTSession();
73
75
  wtsession->init(session, eventloop_);
74
- response.response_headers[":status"] = "200";
75
- response.visitor =
76
- std::make_unique<Http3WTSession::Visitor>(wtsession);
77
- eventloop_->informAboutNewSession(server_, static_cast<Http3WTSession *>(wtsession), path);
78
- return response;
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.response_headers[":status"] = "404";
83
- return response;
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()
@@ -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
- server_(nullptr) {}
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
- WebTransportResponse ProcessWebTransportRequest(
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_; // unowned
90
+ Http3Server *server_; // unowned
57
91
  Http3EventLoop *eventloop_; // unowned
92
+ bool jshandlerequesthandler_;
58
93
  std::set<std::string> paths_;
59
94
  };
60
95
 
@@ -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
- Http3ServerBackend::WebTransportResponse response =
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
- if (response.response_headers[":status"] == "200")
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(response.response_headers), false, nullptr);
220
- if (response.visitor != nullptr)
231
+ WriteHeaders(std::move(resp->response_headers), false, nullptr);
232
+ if (resp->visitor != nullptr)
221
233
  {
222
- web_transport()->SetVisitor(std::move(response.visitor));
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(response.response_headers), true, nullptr);
240
+ WriteHeaders(std::move(resp->response_headers), true, nullptr);
229
241
  }
242
+ return; });
230
243
  return;
231
244
  }
232
245
 
@@ -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-disable no-undef */
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-disable no-undef */
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-disable no-undef */
2
- import { expect } from 'chai'
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
- let keyPair = await crypto.subtle.generateKey(
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
- let privateKey = crypto.subtle.exportKey('pkcs8', keyPair.privateKey)
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(
@@ -0,0 +1,6 @@
1
+ import chai from 'chai'
2
+ import dirtyChai from 'dirty-chai'
3
+
4
+ chai.use(dirtyChai)
5
+
6
+ export const expect = chai.expect
@@ -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 (readableStream) {
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
- export async function writeStream (writable, input) {
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-disable no-undef */
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
  })
@@ -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
- })