quicsilver 0.1.0 → 0.2.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.
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "quicsilver"
4
+ require "rackup/handler"
5
+ require "localhost"
6
+
7
+ module Quicsilver
8
+ module RackHandler
9
+ DEFAULT_OPTIONS = {
10
+ Host: "0.0.0.0",
11
+ Port: 4433,
12
+ }
13
+
14
+ def self.run(app, **options)
15
+ normalized_options = {
16
+ host: options[:Host] || options[:host] || DEFAULT_OPTIONS[:Host],
17
+ port: (options[:Port] || options[:port] || DEFAULT_OPTIONS[:Port]).to_i,
18
+ }
19
+
20
+ cert_file = options[:cert_file]
21
+ key_file = options[:key_file]
22
+
23
+ if cert_file.nil? && key_file.nil?
24
+ env = options[:environment] || ENV['RACK_ENV'] || ENV['RAILS_ENV'] || 'development'
25
+
26
+ if env == 'production'
27
+ raise ArgumentError, "cert_file and key_file are required in production"
28
+ else
29
+ require 'localhost/authority'
30
+ authority = Localhost::Authority.fetch
31
+ cert_file = authority.certificate_path
32
+ key_file = authority.key_path
33
+ Quicsilver.logger.info("Using auto-generated certificates for localhost")
34
+ Quicsilver.logger.info(" Cert: #{cert_file}")
35
+ Quicsilver.logger.info(" Key: #{key_file}")
36
+ end
37
+ end
38
+
39
+ config = ::Quicsilver::ServerConfiguration.new(cert_file, key_file)
40
+
41
+ server = ::Quicsilver::Server.new(
42
+ normalized_options[:port],
43
+ address: normalized_options[:host],
44
+ app: app,
45
+ server_configuration: config
46
+ )
47
+
48
+ yield server if block_given?
49
+
50
+ server.start
51
+ end
52
+
53
+ def self.valid_options
54
+ {
55
+ "Host=HOST" => "Hostname to listen on (default: 0.0.0.0)",
56
+ "Port=PORT" => "Port to listen on (default: 4433)",
57
+ "cert_file=PATH" => "Path to TLS certificate file (required)",
58
+ "key_file=PATH" => "Path to TLS key file (required)"
59
+ }
60
+ end
61
+
62
+ end
63
+ end
64
+
65
+ module Rackup
66
+ module Handler
67
+ module Quicsilver
68
+ def self.run(app, **options, &block)
69
+ ::Quicsilver::RackHandler.run(app, **options, &block)
70
+ end
71
+
72
+ def self.valid_options
73
+ ::Quicsilver::RackHandler.valid_options
74
+ end
75
+ end
76
+ register :quicsilver, Quicsilver
77
+ end
78
+ end
data/quicsilver.gemspec CHANGED
@@ -8,8 +8,8 @@ Gem::Specification.new do |spec|
8
8
  spec.authors = ["Haroon Ahmed"]
9
9
  spec.email = ["haroon.ahmed25@gmail.com"]
10
10
 
11
- spec.summary = %q{Minimal HTTP/3 server implementation for Ruby}
12
- spec.description = %q{A minimal HTTP/3 server implementation for Ruby using Microsoft's MSQUIC library.}
11
+ spec.summary = %q{HTTP/3 server implementation for Ruby}
12
+ spec.description = %q{HTTP/3 server implementation for Ruby}
13
13
  spec.homepage = "https://github.com/hahmed/quicsilver"
14
14
 
15
15
  # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
@@ -33,6 +33,7 @@ Gem::Specification.new do |spec|
33
33
  spec.bindir = "exe"
34
34
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
35
35
  spec.require_paths = ["lib"]
36
+ spec.required_ruby_version = ">= 3.2.0"
36
37
 
37
38
  spec.extensions = ['ext/quicsilver/extconf.rb']
38
39
 
@@ -41,4 +42,8 @@ Gem::Specification.new do |spec|
41
42
  spec.add_development_dependency 'rake-compiler', '~> 1.2'
42
43
  spec.add_development_dependency 'rake-compiler-dock', '~> 1.3'
43
44
  spec.add_development_dependency "minitest", "~> 5.0"
45
+ spec.add_development_dependency "minitest-focus", "~> 1.3"
46
+ spec.add_dependency "localhost", "~> 1.6"
47
+ spec.add_dependency "rack", "~> 3.0"
48
+ spec.add_dependency "rackup", "~> 2.0"
44
49
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: quicsilver
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Haroon Ahmed
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-10-28 00:00:00.000000000 Z
10
+ date: 2025-12-17 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: bundler
@@ -79,8 +79,63 @@ dependencies:
79
79
  - - "~>"
80
80
  - !ruby/object:Gem::Version
81
81
  version: '5.0'
82
- description: A minimal HTTP/3 server implementation for Ruby using Microsoft's MSQUIC
83
- library.
82
+ - !ruby/object:Gem::Dependency
83
+ name: minitest-focus
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '1.3'
89
+ type: :development
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '1.3'
96
+ - !ruby/object:Gem::Dependency
97
+ name: localhost
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '1.6'
103
+ type: :runtime
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '1.6'
110
+ - !ruby/object:Gem::Dependency
111
+ name: rack
112
+ requirement: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - "~>"
115
+ - !ruby/object:Gem::Version
116
+ version: '3.0'
117
+ type: :runtime
118
+ prerelease: false
119
+ version_requirements: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - "~>"
122
+ - !ruby/object:Gem::Version
123
+ version: '3.0'
124
+ - !ruby/object:Gem::Dependency
125
+ name: rackup
126
+ requirement: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - "~>"
129
+ - !ruby/object:Gem::Version
130
+ version: '2.0'
131
+ type: :runtime
132
+ prerelease: false
133
+ version_requirements: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - "~>"
136
+ - !ruby/object:Gem::Version
137
+ version: '2.0'
138
+ description: HTTP/3 server implementation for Ruby
84
139
  email:
85
140
  - haroon.ahmed25@gmail.com
86
141
  executables: []
@@ -88,33 +143,43 @@ extensions:
88
143
  - ext/quicsilver/extconf.rb
89
144
  extra_rdoc_files: []
90
145
  files:
146
+ - ".github/workflows/ci.yml"
91
147
  - ".gitignore"
92
148
  - ".gitmodules"
93
149
  - ".ruby-version"
94
150
  - CHANGELOG.md
95
151
  - Gemfile
96
152
  - Gemfile.lock
153
+ - LICENSE
97
154
  - README.md
98
155
  - Rakefile
156
+ - benchmarks/benchmark.rb
157
+ - benchmarks/quicsilver_server.rb
99
158
  - bin/console
100
159
  - bin/setup
101
160
  - examples/README.md
102
- - examples/minimal_http3_client.rb
103
161
  - examples/minimal_http3_server.rb
104
162
  - examples/rack_http3_server.rb
105
163
  - examples/setup_certs.sh
164
+ - examples/simple_client_test.rb
106
165
  - ext/quicsilver/extconf.rb
107
166
  - ext/quicsilver/quicsilver.c
108
167
  - lib/quicsilver.rb
109
168
  - lib/quicsilver/client.rb
169
+ - lib/quicsilver/connection.rb
170
+ - lib/quicsilver/event_loop.rb
110
171
  - lib/quicsilver/http3.rb
111
172
  - lib/quicsilver/http3/request_encoder.rb
112
173
  - lib/quicsilver/http3/request_parser.rb
113
174
  - lib/quicsilver/http3/response_encoder.rb
175
+ - lib/quicsilver/http3/response_parser.rb
114
176
  - lib/quicsilver/listener_data.rb
177
+ - lib/quicsilver/quic_stream.rb
178
+ - lib/quicsilver/request_registry.rb
115
179
  - lib/quicsilver/server.rb
116
180
  - lib/quicsilver/server_configuration.rb
117
181
  - lib/quicsilver/version.rb
182
+ - lib/rackup/handler/quicsilver.rb
118
183
  - quicsilver.gemspec
119
184
  homepage: https://github.com/hahmed/quicsilver
120
185
  licenses: []
@@ -130,7 +195,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
130
195
  requirements:
131
196
  - - ">="
132
197
  - !ruby/object:Gem::Version
133
- version: '0'
198
+ version: 3.2.0
134
199
  required_rubygems_version: !ruby/object:Gem::Requirement
135
200
  requirements:
136
201
  - - ">="
@@ -139,5 +204,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
139
204
  requirements: []
140
205
  rubygems_version: 3.6.2
141
206
  specification_version: 4
142
- summary: Minimal HTTP/3 server implementation for Ruby
207
+ summary: HTTP/3 server implementation for Ruby
143
208
  test_files: []
@@ -1,89 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require "bundler/setup"
4
- require "quicsilver"
5
-
6
- puts "🔌 Minimal HTTP/3 Client Example"
7
- puts "=" * 40
8
-
9
- # Create client
10
- client = Quicsilver::Client.new("127.0.0.1", 4433, unsecure: true)
11
-
12
- puts "🔧 Connecting to server..."
13
- begin
14
- client.connect
15
- puts "✅ Connected successfully!"
16
- puts "📋 Connection info: #{client.connection_info}"
17
-
18
- # HTTP/3 requests using RequestEncoder
19
- require_relative '../lib/quicsilver/http3/request_encoder'
20
-
21
- request1 = Quicsilver::HTTP3::RequestEncoder.new(
22
- method: 'GET',
23
- path: '/api/users',
24
- authority: 'example.com'
25
- )
26
- client.send_data(request1.encode)
27
-
28
- request2 = Quicsilver::HTTP3::RequestEncoder.new(
29
- method: 'GET',
30
- path: '/api/posts/123',
31
- authority: 'example.com'
32
- )
33
- client.send_data(request2.encode)
34
-
35
- request3 = Quicsilver::HTTP3::RequestEncoder.new(
36
- method: 'POST',
37
- path: '/api/messages',
38
- authority: 'example.com',
39
- body: '{"text":"Hello world"}'
40
- )
41
- client.send_data(request3.encode)
42
-
43
- # JSON payloads (API requests) - now as proper HTTP/3 POST requests
44
- request4 = Quicsilver::HTTP3::RequestEncoder.new(
45
- method: 'POST',
46
- path: '/api/subscribe',
47
- authority: 'example.com',
48
- headers: { 'content-type' => 'application/json' },
49
- body: '{"action":"subscribe","channel":"orders"}'
50
- )
51
- client.send_data(request4.encode)
52
-
53
- request5 = Quicsilver::HTTP3::RequestEncoder.new(
54
- method: 'POST',
55
- path: '/api/update',
56
- authority: 'example.com',
57
- headers: { 'content-type' => 'application/json' },
58
- body: '{"action":"update","user_id":42,"status":"online"}'
59
- )
60
- client.send_data(request5.encode)
61
-
62
- # These old manually-crafted requests use incorrect QPACK indices - removed
63
-
64
- # Metrics/telemetry
65
- # client.send_data("METRIC:cpu=45.2,mem=1024,ts=#{Time.now.to_i}")
66
- # client.send_data("EVENT:login,user=alice,ip=192.168.1.100")
67
-
68
- # Large message test - now as HTTP/3 request
69
- request8 = Quicsilver::HTTP3::RequestEncoder.new(
70
- method: 'POST',
71
- path: '/upload',
72
- authority: 'example.com',
73
- body: "X" * 50000 # 50KB
74
- )
75
- client.send_data(request8.encode)
76
-
77
- # Keep connection alive for a bit
78
- puts "⏳ Connection established. Press Enter to disconnect..."
79
- gets
80
-
81
- rescue Quicsilver::ConnectionError => e
82
- puts "❌ Connection failed: #{e.message}"
83
- rescue Quicsilver::TimeoutError => e
84
- puts "⏰ Connection timed out: #{e.message}"
85
- ensure
86
- puts "🔌 Disconnecting..."
87
- client.disconnect
88
- puts "👋 Disconnected"
89
- end