quicsilver 0.2.0 → 0.4.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 (81) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +4 -5
  3. data/.github/workflows/cibuildgem.yaml +93 -0
  4. data/.gitignore +3 -1
  5. data/CHANGELOG.md +81 -0
  6. data/Gemfile.lock +26 -4
  7. data/README.md +95 -31
  8. data/Rakefile +95 -3
  9. data/benchmarks/components.rb +191 -0
  10. data/benchmarks/concurrent.rb +110 -0
  11. data/benchmarks/helpers.rb +88 -0
  12. data/benchmarks/quicsilver_server.rb +1 -1
  13. data/benchmarks/rails.rb +170 -0
  14. data/benchmarks/throughput.rb +113 -0
  15. data/examples/README.md +44 -91
  16. data/examples/benchmark.rb +111 -0
  17. data/examples/connection_pool_demo.rb +47 -0
  18. data/examples/example_helper.rb +18 -0
  19. data/examples/falcon_middleware.rb +44 -0
  20. data/examples/feature_demo.rb +125 -0
  21. data/examples/grpc_style.rb +97 -0
  22. data/examples/minimal_http3_server.rb +6 -18
  23. data/examples/priorities.rb +60 -0
  24. data/examples/protocol_http_server.rb +31 -0
  25. data/examples/rack_http3_server.rb +8 -20
  26. data/examples/rails_feature_test.rb +260 -0
  27. data/examples/simple_client_test.rb +2 -2
  28. data/examples/streaming_sse.rb +33 -0
  29. data/examples/trailers.rb +69 -0
  30. data/ext/quicsilver/extconf.rb +14 -0
  31. data/ext/quicsilver/quicsilver.c +568 -181
  32. data/lib/quicsilver/client/client.rb +349 -0
  33. data/lib/quicsilver/client/connection_pool.rb +106 -0
  34. data/lib/quicsilver/client/request.rb +98 -0
  35. data/lib/quicsilver/libmsquic.2.dylib +0 -0
  36. data/lib/quicsilver/protocol/adapter.rb +176 -0
  37. data/lib/quicsilver/protocol/control_stream_parser.rb +106 -0
  38. data/lib/quicsilver/protocol/frame_parser.rb +142 -0
  39. data/lib/quicsilver/protocol/frame_reader.rb +55 -0
  40. data/lib/quicsilver/{http3.rb → protocol/frames.rb} +146 -30
  41. data/lib/quicsilver/protocol/priority.rb +56 -0
  42. data/lib/quicsilver/protocol/qpack/decoder.rb +165 -0
  43. data/lib/quicsilver/protocol/qpack/encoder.rb +227 -0
  44. data/lib/quicsilver/protocol/qpack/header_block_decoder.rb +140 -0
  45. data/lib/quicsilver/protocol/qpack/huffman.rb +459 -0
  46. data/lib/quicsilver/protocol/request_encoder.rb +47 -0
  47. data/lib/quicsilver/protocol/request_parser.rb +275 -0
  48. data/lib/quicsilver/protocol/response_encoder.rb +97 -0
  49. data/lib/quicsilver/protocol/response_parser.rb +141 -0
  50. data/lib/quicsilver/protocol/stream_input.rb +98 -0
  51. data/lib/quicsilver/protocol/stream_output.rb +59 -0
  52. data/lib/quicsilver/quicsilver.bundle +0 -0
  53. data/lib/quicsilver/server/listener_data.rb +14 -0
  54. data/lib/quicsilver/server/request_handler.rb +138 -0
  55. data/lib/quicsilver/server/request_registry.rb +50 -0
  56. data/lib/quicsilver/server/server.rb +610 -0
  57. data/lib/quicsilver/transport/configuration.rb +141 -0
  58. data/lib/quicsilver/transport/connection.rb +379 -0
  59. data/lib/quicsilver/transport/event_loop.rb +38 -0
  60. data/lib/quicsilver/transport/inbound_stream.rb +33 -0
  61. data/lib/quicsilver/transport/stream.rb +28 -0
  62. data/lib/quicsilver/transport/stream_event.rb +26 -0
  63. data/lib/quicsilver/version.rb +1 -1
  64. data/lib/quicsilver.rb +55 -14
  65. data/lib/rackup/handler/quicsilver.rb +1 -2
  66. data/quicsilver.gemspec +13 -3
  67. metadata +125 -21
  68. data/benchmarks/benchmark.rb +0 -68
  69. data/examples/setup_certs.sh +0 -57
  70. data/lib/quicsilver/client.rb +0 -261
  71. data/lib/quicsilver/connection.rb +0 -42
  72. data/lib/quicsilver/event_loop.rb +0 -38
  73. data/lib/quicsilver/http3/request_encoder.rb +0 -133
  74. data/lib/quicsilver/http3/request_parser.rb +0 -176
  75. data/lib/quicsilver/http3/response_encoder.rb +0 -186
  76. data/lib/quicsilver/http3/response_parser.rb +0 -160
  77. data/lib/quicsilver/listener_data.rb +0 -29
  78. data/lib/quicsilver/quic_stream.rb +0 -36
  79. data/lib/quicsilver/request_registry.rb +0 -48
  80. data/lib/quicsilver/server.rb +0 -355
  81. data/lib/quicsilver/server_configuration.rb +0 -78
data/lib/quicsilver.rb CHANGED
@@ -2,19 +2,50 @@
2
2
 
3
3
  require "logger"
4
4
  require_relative "quicsilver/version"
5
- require_relative "quicsilver/client"
6
- require_relative "quicsilver/connection"
7
- require_relative "quicsilver/event_loop"
8
- require_relative "quicsilver/quic_stream"
9
- require_relative "quicsilver/listener_data"
10
- require_relative "quicsilver/request_registry"
11
- require_relative "quicsilver/server"
12
- require_relative "quicsilver/server_configuration"
13
- require_relative "quicsilver/http3"
14
- require_relative "quicsilver/http3/request_parser"
15
- require_relative "quicsilver/http3/request_encoder"
16
- require_relative "quicsilver/http3/response_encoder"
17
- require_relative "quicsilver/quicsilver"
5
+
6
+ # Protocol layer
7
+ require_relative "quicsilver/protocol/frames"
8
+ require_relative "quicsilver/protocol/priority"
9
+ require_relative "quicsilver/protocol/qpack/encoder"
10
+ require_relative "quicsilver/protocol/request_parser"
11
+ require_relative "quicsilver/protocol/request_encoder"
12
+ require_relative "quicsilver/protocol/response_parser"
13
+ require_relative "quicsilver/protocol/response_encoder"
14
+ require_relative "quicsilver/protocol/stream_input"
15
+ require_relative "quicsilver/protocol/stream_output"
16
+ require_relative "quicsilver/protocol/adapter"
17
+ require_relative "quicsilver/protocol/control_stream_parser"
18
+ require "protocol/rack"
19
+
20
+ # Transport layer
21
+ require_relative "quicsilver/transport/stream"
22
+ require_relative "quicsilver/transport/stream_event"
23
+ require_relative "quicsilver/transport/inbound_stream"
24
+ require_relative "quicsilver/transport/event_loop"
25
+ require_relative "quicsilver/transport/configuration"
26
+ require_relative "quicsilver/transport/connection"
27
+
28
+ # Server
29
+ require_relative "quicsilver/server/listener_data"
30
+ require_relative "quicsilver/server/request_registry"
31
+ require_relative "quicsilver/server/request_handler"
32
+ require_relative "quicsilver/server/server"
33
+
34
+ # Client
35
+ require_relative "quicsilver/client/request"
36
+ require_relative "quicsilver/client/connection_pool"
37
+ require_relative "quicsilver/client/client"
38
+
39
+ # C extension
40
+ # Load precompiled binary if available, fall back to native extension
41
+ begin
42
+ ruby_version = /(\d+\.\d+)/.match(RUBY_VERSION)
43
+ require_relative "quicsilver/#{ruby_version}/quicsilver"
44
+ rescue LoadError
45
+ require_relative "quicsilver/quicsilver"
46
+ end
47
+
48
+ # Rackup handler
18
49
  require_relative "rackup/handler/quicsilver"
19
50
 
20
51
  module Quicsilver
@@ -41,4 +72,14 @@ module Quicsilver
41
72
  end
42
73
  end
43
74
  end
44
- end
75
+
76
+ # Release pooled client connections on process exit.
77
+ # Closes connection handles so the OS doesn't leak UDP sockets.
78
+ # MsQuic itself is cleaned up by the OS when the process exits.
79
+ at_exit do
80
+ begin
81
+ Client.close_pool
82
+ rescue StandardError # rubocop:disable Lint/SuppressedException
83
+ end
84
+ end
85
+ end
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "quicsilver"
4
3
  require "rackup/handler"
5
4
  require "localhost"
6
5
 
@@ -36,7 +35,7 @@ module Quicsilver
36
35
  end
37
36
  end
38
37
 
39
- config = ::Quicsilver::ServerConfiguration.new(cert_file, key_file)
38
+ config = ::Quicsilver::Transport::Configuration.new(cert_file, key_file)
40
39
 
41
40
  server = ::Quicsilver::Server.new(
42
41
  normalized_options[:port],
data/quicsilver.gemspec CHANGED
@@ -11,6 +11,7 @@ Gem::Specification.new do |spec|
11
11
  spec.summary = %q{HTTP/3 server implementation for Ruby}
12
12
  spec.description = %q{HTTP/3 server implementation for Ruby}
13
13
  spec.homepage = "https://github.com/hahmed/quicsilver"
14
+ spec.license = "MIT"
14
15
 
15
16
  # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
16
17
  # to allow pushing to a single host or delete this section to allow pushing to any host.
@@ -28,21 +29,30 @@ Gem::Specification.new do |spec|
28
29
  # Specify which files should be added to the gem when it is released.
29
30
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
30
31
  spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
31
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
32
+ files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features|vendor)/}) }
33
+ # Include precompiled binaries if present
34
+ files += Dir["lib/quicsilver/libmsquic*"]
35
+ files += Dir["lib/quicsilver/quicsilver.{bundle,so}"]
36
+ files
32
37
  end
33
38
  spec.bindir = "exe"
34
39
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
35
40
  spec.require_paths = ["lib"]
36
- spec.required_ruby_version = ">= 3.2.0"
41
+ spec.required_ruby_version = ">= 3.4.0"
37
42
 
38
43
  spec.extensions = ['ext/quicsilver/extconf.rb']
39
44
 
40
45
  spec.add_development_dependency "bundler", "~> 2.0"
41
- spec.add_development_dependency "rake", "~> 10.0"
46
+ spec.add_development_dependency "rake", "~> 13.0"
42
47
  spec.add_development_dependency 'rake-compiler', '~> 1.2'
43
48
  spec.add_development_dependency 'rake-compiler-dock', '~> 1.3'
44
49
  spec.add_development_dependency "minitest", "~> 5.0"
45
50
  spec.add_development_dependency "minitest-focus", "~> 1.3"
51
+ spec.add_development_dependency "benchmark-ips", "~> 2.12"
52
+ spec.add_dependency "protocol-http", "~> 0.49"
53
+ spec.add_dependency "protocol-rack", "~> 0.22"
54
+ spec.add_dependency "console" # TODO: drop when protocol-rack declares this in its gemspec
55
+ spec.add_dependency "logger"
46
56
  spec.add_dependency "localhost", "~> 1.6"
47
57
  spec.add_dependency "rack", "~> 3.0"
48
58
  spec.add_dependency "rackup", "~> 2.0"
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.2.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Haroon Ahmed
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-12-17 00:00:00.000000000 Z
10
+ date: 2026-04-25 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: bundler
@@ -29,14 +29,14 @@ dependencies:
29
29
  requirements:
30
30
  - - "~>"
31
31
  - !ruby/object:Gem::Version
32
- version: '10.0'
32
+ version: '13.0'
33
33
  type: :development
34
34
  prerelease: false
35
35
  version_requirements: !ruby/object:Gem::Requirement
36
36
  requirements:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
- version: '10.0'
39
+ version: '13.0'
40
40
  - !ruby/object:Gem::Dependency
41
41
  name: rake-compiler
42
42
  requirement: !ruby/object:Gem::Requirement
@@ -93,6 +93,76 @@ dependencies:
93
93
  - - "~>"
94
94
  - !ruby/object:Gem::Version
95
95
  version: '1.3'
96
+ - !ruby/object:Gem::Dependency
97
+ name: benchmark-ips
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '2.12'
103
+ type: :development
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '2.12'
110
+ - !ruby/object:Gem::Dependency
111
+ name: protocol-http
112
+ requirement: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - "~>"
115
+ - !ruby/object:Gem::Version
116
+ version: '0.49'
117
+ type: :runtime
118
+ prerelease: false
119
+ version_requirements: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - "~>"
122
+ - !ruby/object:Gem::Version
123
+ version: '0.49'
124
+ - !ruby/object:Gem::Dependency
125
+ name: protocol-rack
126
+ requirement: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - "~>"
129
+ - !ruby/object:Gem::Version
130
+ version: '0.22'
131
+ type: :runtime
132
+ prerelease: false
133
+ version_requirements: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - "~>"
136
+ - !ruby/object:Gem::Version
137
+ version: '0.22'
138
+ - !ruby/object:Gem::Dependency
139
+ name: console
140
+ requirement: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - ">="
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
145
+ type: :runtime
146
+ prerelease: false
147
+ version_requirements: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ version: '0'
152
+ - !ruby/object:Gem::Dependency
153
+ name: logger
154
+ requirement: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - ">="
157
+ - !ruby/object:Gem::Version
158
+ version: '0'
159
+ type: :runtime
160
+ prerelease: false
161
+ version_requirements: !ruby/object:Gem::Requirement
162
+ requirements:
163
+ - - ">="
164
+ - !ruby/object:Gem::Version
165
+ version: '0'
96
166
  - !ruby/object:Gem::Dependency
97
167
  name: localhost
98
168
  requirement: !ruby/object:Gem::Requirement
@@ -144,6 +214,7 @@ extensions:
144
214
  extra_rdoc_files: []
145
215
  files:
146
216
  - ".github/workflows/ci.yml"
217
+ - ".github/workflows/cibuildgem.yaml"
147
218
  - ".gitignore"
148
219
  - ".gitmodules"
149
220
  - ".ruby-version"
@@ -153,36 +224,69 @@ files:
153
224
  - LICENSE
154
225
  - README.md
155
226
  - Rakefile
156
- - benchmarks/benchmark.rb
227
+ - benchmarks/components.rb
228
+ - benchmarks/concurrent.rb
229
+ - benchmarks/helpers.rb
157
230
  - benchmarks/quicsilver_server.rb
231
+ - benchmarks/rails.rb
232
+ - benchmarks/throughput.rb
158
233
  - bin/console
159
234
  - bin/setup
160
235
  - examples/README.md
236
+ - examples/benchmark.rb
237
+ - examples/connection_pool_demo.rb
238
+ - examples/example_helper.rb
239
+ - examples/falcon_middleware.rb
240
+ - examples/feature_demo.rb
241
+ - examples/grpc_style.rb
161
242
  - examples/minimal_http3_server.rb
243
+ - examples/priorities.rb
244
+ - examples/protocol_http_server.rb
162
245
  - examples/rack_http3_server.rb
163
- - examples/setup_certs.sh
246
+ - examples/rails_feature_test.rb
164
247
  - examples/simple_client_test.rb
248
+ - examples/streaming_sse.rb
249
+ - examples/trailers.rb
165
250
  - ext/quicsilver/extconf.rb
166
251
  - ext/quicsilver/quicsilver.c
167
252
  - lib/quicsilver.rb
168
- - lib/quicsilver/client.rb
169
- - lib/quicsilver/connection.rb
170
- - lib/quicsilver/event_loop.rb
171
- - lib/quicsilver/http3.rb
172
- - lib/quicsilver/http3/request_encoder.rb
173
- - lib/quicsilver/http3/request_parser.rb
174
- - lib/quicsilver/http3/response_encoder.rb
175
- - lib/quicsilver/http3/response_parser.rb
176
- - lib/quicsilver/listener_data.rb
177
- - lib/quicsilver/quic_stream.rb
178
- - lib/quicsilver/request_registry.rb
179
- - lib/quicsilver/server.rb
180
- - lib/quicsilver/server_configuration.rb
253
+ - lib/quicsilver/client/client.rb
254
+ - lib/quicsilver/client/connection_pool.rb
255
+ - lib/quicsilver/client/request.rb
256
+ - lib/quicsilver/libmsquic.2.dylib
257
+ - lib/quicsilver/protocol/adapter.rb
258
+ - lib/quicsilver/protocol/control_stream_parser.rb
259
+ - lib/quicsilver/protocol/frame_parser.rb
260
+ - lib/quicsilver/protocol/frame_reader.rb
261
+ - lib/quicsilver/protocol/frames.rb
262
+ - lib/quicsilver/protocol/priority.rb
263
+ - lib/quicsilver/protocol/qpack/decoder.rb
264
+ - lib/quicsilver/protocol/qpack/encoder.rb
265
+ - lib/quicsilver/protocol/qpack/header_block_decoder.rb
266
+ - lib/quicsilver/protocol/qpack/huffman.rb
267
+ - lib/quicsilver/protocol/request_encoder.rb
268
+ - lib/quicsilver/protocol/request_parser.rb
269
+ - lib/quicsilver/protocol/response_encoder.rb
270
+ - lib/quicsilver/protocol/response_parser.rb
271
+ - lib/quicsilver/protocol/stream_input.rb
272
+ - lib/quicsilver/protocol/stream_output.rb
273
+ - lib/quicsilver/quicsilver.bundle
274
+ - lib/quicsilver/server/listener_data.rb
275
+ - lib/quicsilver/server/request_handler.rb
276
+ - lib/quicsilver/server/request_registry.rb
277
+ - lib/quicsilver/server/server.rb
278
+ - lib/quicsilver/transport/configuration.rb
279
+ - lib/quicsilver/transport/connection.rb
280
+ - lib/quicsilver/transport/event_loop.rb
281
+ - lib/quicsilver/transport/inbound_stream.rb
282
+ - lib/quicsilver/transport/stream.rb
283
+ - lib/quicsilver/transport/stream_event.rb
181
284
  - lib/quicsilver/version.rb
182
285
  - lib/rackup/handler/quicsilver.rb
183
286
  - quicsilver.gemspec
184
287
  homepage: https://github.com/hahmed/quicsilver
185
- licenses: []
288
+ licenses:
289
+ - MIT
186
290
  metadata:
187
291
  allowed_push_host: https://rubygems.org
188
292
  homepage_uri: https://github.com/hahmed/quicsilver
@@ -195,7 +299,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
195
299
  requirements:
196
300
  - - ">="
197
301
  - !ruby/object:Gem::Version
198
- version: 3.2.0
302
+ version: 3.4.0
199
303
  required_rubygems_version: !ruby/object:Gem::Requirement
200
304
  requirements:
201
305
  - - ">="
@@ -1,68 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # Usage: REQUESTS=1000 CONNECTIONS=10 ruby benchmarks/benchmark.rb
3
-
4
- require 'bundler/setup'
5
- require 'quicsilver'
6
- require 'benchmark'
7
-
8
- REQUESTS = ENV.fetch('REQUESTS', 1000).to_i
9
- CONNECTIONS = ENV.fetch('CONNECTIONS', 1).to_i
10
- HOST = ENV.fetch('HOST', '127.0.0.1')
11
- PORT = ENV.fetch('PORT', 4433).to_i
12
-
13
- puts "Quicsilver HTTP/3 Benchmark"
14
- puts "=" * 50
15
- puts "Target: #{HOST}:#{PORT}"
16
- puts "Requests: #{REQUESTS} | Connections: #{CONNECTIONS}"
17
- puts "=" * 50
18
-
19
- results = []
20
- mutex = Mutex.new
21
-
22
- elapsed = Benchmark.realtime do
23
- threads = CONNECTIONS.times.map do |conn_id|
24
- Thread.new do
25
- client = Quicsilver::Client.new(HOST, PORT, unsecure: true)
26
- client.connect
27
-
28
- local_times = []
29
- per_conn = REQUESTS / CONNECTIONS
30
-
31
- per_conn.times do |i|
32
- start = Time.now
33
- begin
34
- client.get("/")
35
- local_times << (Time.now - start)
36
- rescue
37
- local_times << nil
38
- end
39
- print "." if conn_id == 0 && i % (per_conn / 10).clamp(1, 100) == 0
40
- end
41
-
42
- client.disconnect
43
- mutex.synchronize { results.concat(local_times) }
44
- end
45
- end
46
- threads.each(&:join)
47
- end
48
-
49
- times = results.compact
50
- failed = results.count(&:nil?)
51
-
52
- puts "\n" + "=" * 50
53
- puts "RESULTS"
54
- puts "=" * 50
55
- puts "Total: #{elapsed.round(3)}s"
56
- puts "Req/sec: #{(REQUESTS / elapsed).round(2)}"
57
- puts "Success: #{times.size} | Failed: #{failed}"
58
-
59
- if times.any?
60
- sorted = times.sort
61
- puts "Latency: avg=#{(times.sum / times.size * 1000).round(2)}ms " \
62
- "min=#{(sorted.first * 1000).round(2)}ms " \
63
- "max=#{(sorted.last * 1000).round(2)}ms"
64
- puts " p50=#{(sorted[sorted.size / 2] * 1000).round(2)}ms " \
65
- "p95=#{(sorted[(sorted.size * 0.95).to_i] * 1000).round(2)}ms " \
66
- "p99=#{(sorted[(sorted.size * 0.99).to_i] * 1000).round(2)}ms"
67
- end
68
- puts "=" * 50
@@ -1,57 +0,0 @@
1
- #!/bin/bash
2
-
3
- # Quicsilver Certificate Setup
4
- # This script generates self-signed certificates for QUIC testing
5
-
6
- echo "🔐 Generating certificates for Quicsilver QUIC testing..."
7
-
8
- # Create certs directory if it doesn't exist
9
- mkdir -p ../certs
10
- cd ../certs
11
-
12
- # Create OpenSSL config with proper TLS server extensions
13
- cat > openssl.conf << 'EOF'
14
- [req]
15
- distinguished_name = req_distinguished_name
16
- req_extensions = v3_req
17
- prompt = no
18
-
19
- [req_distinguished_name]
20
- CN = localhost
21
- O = QuicsilverTest
22
- C = US
23
-
24
- [v3_req]
25
- keyUsage = keyEncipherment, dataEncipherment
26
- extendedKeyUsage = serverAuth
27
- subjectAltName = @alt_names
28
-
29
- [alt_names]
30
- DNS.1 = localhost
31
- IP.1 = 127.0.0.1
32
- EOF
33
-
34
- # Generate private key and certificate
35
- echo "📝 Generating private key..."
36
- openssl genrsa -out server.key 2048
37
-
38
- echo "📜 Generating certificate..."
39
- openssl req -new -x509 -key server.key -out server.crt -days 365 \
40
- -config openssl.conf -extensions v3_req
41
-
42
- # Create PKCS#12 format (optional, for other tools)
43
- echo "📦 Creating PKCS#12 bundle..."
44
- openssl pkcs12 -export -out server.p12 -inkey server.key -in server.crt \
45
- -passout pass:password -name "localhost"
46
-
47
- # Clean up
48
- rm openssl.conf
49
-
50
- echo "✅ Certificate files created:"
51
- echo " 📄 server.crt - Certificate file"
52
- echo " 🔑 server.key - Private key file"
53
- echo " 📦 server.p12 - PKCS#12 bundle (password: 'password')"
54
- echo ""
55
- echo "🚀 You can now run:"
56
- echo " Terminal 1: ruby examples/test_server.rb"
57
- echo " Terminal 2: ruby examples/test_connection.rb"