quicsilver 0.5.0-x86_64-linux

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 (107) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/ci.yml +49 -0
  3. data/.github/workflows/cibuildgem.yaml +117 -0
  4. data/.gitignore +21 -0
  5. data/.gitmodules +3 -0
  6. data/.ruby-version +1 -0
  7. data/CHANGELOG.md +175 -0
  8. data/Gemfile +6 -0
  9. data/Gemfile.lock +67 -0
  10. data/LICENSE +21 -0
  11. data/README.md +261 -0
  12. data/Rakefile +130 -0
  13. data/benchmarks/baseline.rb +175 -0
  14. data/benchmarks/compare.rb +398 -0
  15. data/benchmarks/components.rb +191 -0
  16. data/benchmarks/helpers.rb +162 -0
  17. data/benchmarks/latency.rb +128 -0
  18. data/benchmarks/loss.rb +193 -0
  19. data/benchmarks/mobile.rb +125 -0
  20. data/bin/console +14 -0
  21. data/bin/setup +8 -0
  22. data/docs/connection_identity.md +141 -0
  23. data/examples/README.md +58 -0
  24. data/examples/benchmark.rb +191 -0
  25. data/examples/connection_pool_demo.rb +47 -0
  26. data/examples/example_helper.rb +69 -0
  27. data/examples/falcon_middleware.rb +44 -0
  28. data/examples/feature_demo.rb +125 -0
  29. data/examples/grpc_style.rb +97 -0
  30. data/examples/latency_aware_routing.rb +382 -0
  31. data/examples/lifecycle_drain.rb +264 -0
  32. data/examples/minimal_http3_server.rb +14 -0
  33. data/examples/priorities.rb +60 -0
  34. data/examples/protocol_http_server.rb +31 -0
  35. data/examples/rack_http3_server.rb +42 -0
  36. data/examples/rails_feature_test.rb +280 -0
  37. data/examples/server_stats.rb +117 -0
  38. data/examples/simple_client_test.rb +26 -0
  39. data/examples/streaming_sse.rb +33 -0
  40. data/examples/trailers.rb +69 -0
  41. data/examples/webtransport_smoke.rb +546 -0
  42. data/examples/worker_load_balancing.rb +425 -0
  43. data/ext/quicsilver/extconf.rb +124 -0
  44. data/ext/quicsilver/include/msquic.h +2226 -0
  45. data/ext/quicsilver/include/msquic_posix.h +696 -0
  46. data/ext/quicsilver/include/quic_sal_stub.h +314 -0
  47. data/ext/quicsilver/msquic_patch.rb +68 -0
  48. data/ext/quicsilver/patches/reliable-reset.patch +723 -0
  49. data/ext/quicsilver/patches/stream-final-size.patch +47 -0
  50. data/ext/quicsilver/quicsilver.c +2741 -0
  51. data/lib/quicsilver/3.4/quicsilver.so +0 -0
  52. data/lib/quicsilver/4.0/quicsilver.so +0 -0
  53. data/lib/quicsilver/client/client.rb +708 -0
  54. data/lib/quicsilver/client/connection_pool.rb +202 -0
  55. data/lib/quicsilver/client/request.rb +190 -0
  56. data/lib/quicsilver/errors.rb +39 -0
  57. data/lib/quicsilver/libmsquic.so.2 +0 -0
  58. data/lib/quicsilver/protocol/adapter.rb +196 -0
  59. data/lib/quicsilver/protocol/capsule.rb +38 -0
  60. data/lib/quicsilver/protocol/connect_stream_decoder.rb +75 -0
  61. data/lib/quicsilver/protocol/control_stream_parser.rb +117 -0
  62. data/lib/quicsilver/protocol/datagram.rb +40 -0
  63. data/lib/quicsilver/protocol/frame_parser.rb +141 -0
  64. data/lib/quicsilver/protocol/frame_reader.rb +84 -0
  65. data/lib/quicsilver/protocol/frames.rb +454 -0
  66. data/lib/quicsilver/protocol/priority.rb +65 -0
  67. data/lib/quicsilver/protocol/qpack/decoder.rb +127 -0
  68. data/lib/quicsilver/protocol/qpack/encoder.rb +217 -0
  69. data/lib/quicsilver/protocol/qpack/header_block_decoder.rb +140 -0
  70. data/lib/quicsilver/protocol/qpack/huffman.rb +459 -0
  71. data/lib/quicsilver/protocol/request_encoder.rb +50 -0
  72. data/lib/quicsilver/protocol/request_parser.rb +289 -0
  73. data/lib/quicsilver/protocol/response_encoder.rb +107 -0
  74. data/lib/quicsilver/protocol/response_parser.rb +147 -0
  75. data/lib/quicsilver/protocol/stream_input.rb +98 -0
  76. data/lib/quicsilver/protocol/stream_output.rb +59 -0
  77. data/lib/quicsilver/protocol/unidirectional_stream.rb +12 -0
  78. data/lib/quicsilver/protocol/web_transport.rb +214 -0
  79. data/lib/quicsilver/rack/context.rb +33 -0
  80. data/lib/quicsilver/response.rb +40 -0
  81. data/lib/quicsilver/server/listener_data.rb +14 -0
  82. data/lib/quicsilver/server/rack_adapter.rb +77 -0
  83. data/lib/quicsilver/server/receive_queue.rb +106 -0
  84. data/lib/quicsilver/server/request_handler.rb +149 -0
  85. data/lib/quicsilver/server/request_registry.rb +63 -0
  86. data/lib/quicsilver/server/scheduler.rb +50 -0
  87. data/lib/quicsilver/server/schedulers/fiber_scheduler.rb +79 -0
  88. data/lib/quicsilver/server/schedulers/thread_scheduler.rb +76 -0
  89. data/lib/quicsilver/server/server.rb +1080 -0
  90. data/lib/quicsilver/server/web_transport_flow_control.rb +270 -0
  91. data/lib/quicsilver/server/web_transport_manager.rb +281 -0
  92. data/lib/quicsilver/server/web_transport_registry.rb +40 -0
  93. data/lib/quicsilver/server/web_transport_session.rb +812 -0
  94. data/lib/quicsilver/server/web_transport_stream.rb +326 -0
  95. data/lib/quicsilver/transport/configuration.rb +234 -0
  96. data/lib/quicsilver/transport/connection.rb +594 -0
  97. data/lib/quicsilver/transport/connection_stats.rb +53 -0
  98. data/lib/quicsilver/transport/event_loop.rb +74 -0
  99. data/lib/quicsilver/transport/inbound_stream.rb +53 -0
  100. data/lib/quicsilver/transport/stream.rb +43 -0
  101. data/lib/quicsilver/transport/stream_event.rb +49 -0
  102. data/lib/quicsilver/transport/stream_id.rb +26 -0
  103. data/lib/quicsilver/version.rb +3 -0
  104. data/lib/quicsilver.rb +96 -0
  105. data/lib/rackup/handler/quicsilver.rb +96 -0
  106. data/quicsilver.gemspec +66 -0
  107. metadata +362 -0
@@ -0,0 +1,117 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Demonstrates Quicsilver::Server#stats.
5
+ #
6
+ # ruby examples/server_stats.rb
7
+ # PORT=4444 ruby examples/server_stats.rb
8
+ #
9
+ # The stats snapshot combines:
10
+ # - Quicsilver app-server state: running, connections, active requests, queue.
11
+ # - QUIC transport counters: process-wide connection/stream/packet/worker state.
12
+
13
+ require_relative "example_helper"
14
+ require "json"
15
+
16
+ HOST = ENV.fetch("HOST", "localhost")
17
+ PORT = Integer(ENV.fetch("PORT", "4433"))
18
+
19
+ server = nil
20
+ server_thread = nil
21
+ clients = []
22
+
23
+ begin
24
+ app = ->(env) {
25
+ case env["PATH_INFO"]
26
+ when "/hello"
27
+ [200, { "content-type" => "text/plain" }, ["hello\n"]]
28
+ when %r{\A/slow}
29
+ sleep 0.25
30
+ [200, { "content-type" => "text/plain" }, ["slow response\n"]]
31
+ else
32
+ [404, { "content-type" => "text/plain" }, ["not found\n"]]
33
+ end
34
+ }
35
+
36
+ server = Quicsilver::Server.new(PORT, app: app, server_configuration: EXAMPLE_TLS_CONFIG)
37
+ server_thread = Thread.new { server.start }
38
+
39
+ # Keep examples self-contained without requiring test helpers.
40
+ def wait_until(timeout: 3)
41
+ deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + timeout
42
+ until yield
43
+ raise "timed out waiting" if Process.clock_gettime(Process::CLOCK_MONOTONIC) >= deadline
44
+
45
+ sleep 0.01
46
+ end
47
+ end
48
+
49
+ wait_until { server.running? }
50
+
51
+ # Print the useful subset first, then include the whole transport hash so it is
52
+ # easy to see every counter name currently exposed by Quicsilver.transport_counters.
53
+ def print_stats(title, server)
54
+ stats = server.stats
55
+ transport = stats["transport"] || {}
56
+
57
+ puts "\n#{title}"
58
+ puts "-" * title.length
59
+ puts JSON.pretty_generate(
60
+ "server" => {
61
+ "running" => stats["running"],
62
+ "shutting_down" => stats["shutting_down"],
63
+ "connections" => stats["connections"],
64
+ "requests" => stats["requests"],
65
+ "scheduler" => stats["scheduler"]
66
+ },
67
+ "transport_summary" => {
68
+ "connections_active" => transport["connections_active"],
69
+ "connections_connected" => transport["connections_connected"],
70
+ "streams_active" => transport["streams_active"],
71
+ "connection_queue_depth" => transport["connection_queue_depth"],
72
+ "connection_operations_queue_depth" => transport["connection_operations_queue_depth"],
73
+ "worker_operations_queue_depth" => transport["worker_operations_queue_depth"],
74
+ "connections_load_rejected" => transport["connections_load_rejected"],
75
+ "packets_dropped" => transport["packets_dropped"],
76
+ "app_bytes_received" => transport["app_bytes_received"],
77
+ "app_bytes_sent" => transport["app_bytes_sent"]
78
+ },
79
+ "transport" => transport
80
+ )
81
+ end
82
+
83
+ puts "Quicsilver Server Stats Demo"
84
+ puts "Listening on https://#{HOST}:#{PORT}"
85
+ puts "Note: transport counters are process-wide, so they include this example's clients too."
86
+
87
+ print_stats("1. Fresh server", server)
88
+
89
+ clients = 2.times.map { Quicsilver::Client.new(HOST, PORT, unsecure: true) }
90
+ clients.each_with_index do |client, index|
91
+ response = client.get("/hello")
92
+ puts "client #{index + 1}: GET /hello -> #{response.status}"
93
+ end
94
+
95
+ wait_until { server.stats.dig("connections", "active") >= 2 }
96
+ print_stats("2. After two client connections", server)
97
+
98
+ slow_requests = clients.each_with_index.map do |client, index|
99
+ Thread.new do
100
+ response = client.get("/slow/#{index + 1}")
101
+ puts "client #{index + 1}: GET /slow/#{index + 1} -> #{response.status}"
102
+ end
103
+ end
104
+
105
+ wait_until { server.stats.dig("requests", "active") >= 2 }
106
+ print_stats("3. While slow requests are in flight", server)
107
+
108
+ slow_requests.each(&:join)
109
+ print_stats("4. After slow requests complete", server)
110
+
111
+ puts "\nDone."
112
+ ensure
113
+ clients.each { |client| client.disconnect rescue nil }
114
+ Quicsilver::Client.close_pool rescue nil
115
+ server&.stop rescue nil
116
+ server_thread&.join(2)
117
+ end
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "quicsilver"
5
+
6
+ puts "🔌 Simple HTTP/3 Client Test"
7
+ puts "=" * 40
8
+
9
+ begin
10
+ client = Quicsilver::Client.new("localhost", 4433, unsecure: true)
11
+
12
+ client.connect
13
+
14
+ response = client.get("/")
15
+
16
+ puts "Status: #{response.status}"
17
+ puts "Headers: #{response.headers.inspect}"
18
+ puts "Body: #{response.body}"
19
+
20
+ rescue => e
21
+ puts "❌ Error: #{e.class} - #{e.message}"
22
+ puts e.backtrace.first(10)
23
+ ensure
24
+ client&.disconnect
25
+ puts "👋 Done"
26
+ end
@@ -0,0 +1,33 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Server-Sent Events over HTTP/3.
4
+ #
5
+ # Demonstrates streaming responses — data arrives chunk by chunk,
6
+ # not buffered. Each chunk is sent as a separate HTTP/3 DATA frame.
7
+ #
8
+ # ruby examples/streaming_sse.rb
9
+ # curl --http3-only -k https://localhost:4433/
10
+
11
+ require_relative "example_helper"
12
+
13
+ app = ->(env) {
14
+ body = Enumerator.new do |y|
15
+ 10.times do |i|
16
+ y << "data: {\"count\":#{i},\"time\":\"#{Time.now.iso8601}\"}\n\n"
17
+ sleep 0.2
18
+ end
19
+ y << "data: {\"done\":true}\n\n"
20
+ end
21
+
22
+ [200, { "content-type" => "text/event-stream", "cache-control" => "no-cache" }, body]
23
+ }
24
+
25
+ server = Quicsilver::Server.new(4433, app: app, server_configuration: EXAMPLE_TLS_CONFIG)
26
+
27
+ puts "📡 Streaming SSE over HTTP/3"
28
+ puts " https://localhost:4433"
29
+ puts " curl --http3-only -k https://localhost:4433/"
30
+ puts " Events arrive every 200ms — no buffering."
31
+ puts
32
+
33
+ server.start
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # HTTP/3 Trailers — send headers after the body.
4
+ #
5
+ # Trailers let the server report status after streaming is complete.
6
+ # Useful for checksums, streaming error status, and gRPC.
7
+ #
8
+ # ruby examples/trailers.rb
9
+
10
+ require_relative "example_helper"
11
+ require "digest"
12
+
13
+ PORT = 4433
14
+ HOST = "localhost"
15
+
16
+ app = ->(env) {
17
+ path = env["PATH_INFO"]
18
+
19
+ case path
20
+ when "/stream-with-checksum"
21
+ body = ["chunk1\n", "chunk2\n", "chunk3\n"]
22
+ checksum = Digest::SHA256.hexdigest(body.join)[0..7]
23
+ env["rack.trailers"] = { "x-checksum" => checksum }
24
+ [200, { "content-type" => "text/plain" }, body]
25
+
26
+ when "/grpc-style"
27
+ env["rack.trailers"] = { "grpc-status" => "0", "grpc-message" => "OK" }
28
+ [200, { "content-type" => "application/grpc" }, ['{"result":"processed"}']]
29
+
30
+ when "/grpc-error"
31
+ env["rack.trailers"] = { "grpc-status" => "13", "grpc-message" => "INTERNAL" }
32
+ [200, { "content-type" => "application/grpc" }, ['{"error":"something broke"}']]
33
+
34
+ else
35
+ [200, { "content-type" => "text/plain" }, ["Try /stream-with-checksum, /grpc-style, or /grpc-error"]]
36
+ end
37
+ }
38
+
39
+ server = Quicsilver::Server.new(PORT, app: app, server_configuration: EXAMPLE_TLS_CONFIG)
40
+ server_thread = Thread.new { server.start }
41
+ sleep 0.3
42
+
43
+ puts "HTTP/3 Trailers Demo"
44
+ puts "=" * 50
45
+
46
+ client = Quicsilver::Client.new(HOST, PORT, unsecure: true)
47
+
48
+ puts "\n Streaming with checksum trailer:"
49
+ response = client.get("/stream-with-checksum")
50
+ puts " Status: #{response.status}"
51
+ puts " Body: #{response.body.inspect}"
52
+ puts " Trailers: #{response.trailers}"
53
+
54
+ puts "\n gRPC-style success:"
55
+ response = client.get("/grpc-style")
56
+ puts " Status: #{response.status}"
57
+ puts " Body: #{response.body}"
58
+ puts " Trailers: #{response.trailers}"
59
+
60
+ puts "\n gRPC-style error:"
61
+ response = client.get("/grpc-error")
62
+ puts " Status: #{response.status}"
63
+ puts " Body: #{response.body}"
64
+ puts " Trailers: #{response.trailers}"
65
+
66
+ client.disconnect
67
+ server.stop
68
+ server_thread.join(2)
69
+ puts "\nDone"