tipi 0.41 → 0.46
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.
- checksums.yaml +4 -4
- data/.github/FUNDING.yml +1 -0
- data/.github/workflows/test.yml +3 -1
- data/.gitignore +3 -1
- data/CHANGELOG.md +34 -0
- data/Gemfile +7 -1
- data/Gemfile.lock +53 -33
- data/README.md +184 -8
- data/Rakefile +1 -7
- data/benchmarks/bm_http1_parser.rb +85 -0
- data/bin/benchmark +37 -0
- data/bin/h1pd +6 -0
- data/bin/tipi +3 -21
- data/bm.png +0 -0
- data/df/agent.rb +1 -1
- data/df/sample_agent.rb +2 -2
- data/df/server.rb +3 -1
- data/df/server_utils.rb +48 -46
- data/examples/full_service.rb +13 -0
- data/examples/hello.rb +5 -0
- data/examples/hello.ru +3 -3
- data/examples/http1_parser.rb +10 -8
- data/examples/http_server.js +1 -1
- data/examples/http_server.rb +4 -1
- data/examples/http_server_graceful.rb +1 -1
- data/examples/https_server.rb +41 -15
- data/examples/rack_server_forked.rb +26 -0
- data/examples/rack_server_https_forked.rb +1 -1
- data/examples/servername_cb.rb +37 -0
- data/examples/websocket_demo.rb +1 -1
- data/lib/tipi/acme.rb +320 -0
- data/lib/tipi/cli.rb +93 -0
- data/lib/tipi/config_dsl.rb +13 -13
- data/lib/tipi/configuration.rb +2 -2
- data/lib/tipi/controller/bare_polyphony.rb +0 -0
- data/lib/tipi/controller/bare_stock.rb +10 -0
- data/lib/tipi/controller/extensions.rb +37 -0
- data/lib/tipi/controller/stock_http1_adapter.rb +15 -0
- data/lib/tipi/controller/web_polyphony.rb +353 -0
- data/lib/tipi/controller/web_stock.rb +635 -0
- data/lib/tipi/controller.rb +12 -0
- data/lib/tipi/digital_fabric/agent.rb +5 -5
- data/lib/tipi/digital_fabric/agent_proxy.rb +15 -8
- data/lib/tipi/digital_fabric/executive.rb +7 -3
- data/lib/tipi/digital_fabric/protocol.rb +3 -3
- data/lib/tipi/digital_fabric/request_adapter.rb +0 -4
- data/lib/tipi/digital_fabric/service.rb +17 -18
- data/lib/tipi/handler.rb +2 -2
- data/lib/tipi/http1_adapter.rb +85 -124
- data/lib/tipi/http2_adapter.rb +29 -16
- data/lib/tipi/http2_stream.rb +52 -57
- data/lib/tipi/rack_adapter.rb +2 -2
- data/lib/tipi/response_extensions.rb +1 -1
- data/lib/tipi/supervisor.rb +75 -0
- data/lib/tipi/version.rb +1 -1
- data/lib/tipi/websocket.rb +3 -3
- data/lib/tipi.rb +9 -7
- data/test/coverage.rb +2 -2
- data/test/helper.rb +60 -12
- data/test/test_http_server.rb +14 -41
- data/test/test_request.rb +2 -29
- data/tipi.gemspec +10 -10
- metadata +80 -54
- data/examples/automatic_certificate.rb +0 -193
- data/ext/tipi/extconf.rb +0 -12
- data/ext/tipi/http1_parser.c +0 -534
- data/ext/tipi/http1_parser.h +0 -18
- data/ext/tipi/tipi_ext.c +0 -5
- data/lib/tipi/http1_adapter_new.rb +0 -293
@@ -1,193 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'bundler/setup'
|
4
|
-
require 'tipi'
|
5
|
-
require 'openssl'
|
6
|
-
require 'acme-client'
|
7
|
-
|
8
|
-
# ::Exception.__disable_sanitized_backtrace__ = true
|
9
|
-
|
10
|
-
class CertificateManager
|
11
|
-
def initialize(store:, challenge_handler:)
|
12
|
-
@store = store
|
13
|
-
@challenge_handler = challenge_handler
|
14
|
-
@workers = {}
|
15
|
-
@contexts = {}
|
16
|
-
end
|
17
|
-
|
18
|
-
def [](name)
|
19
|
-
worker = worker_for_name(name)
|
20
|
-
p worker: worker
|
21
|
-
|
22
|
-
worker << Fiber.current
|
23
|
-
# cancel_after(30) { receive }
|
24
|
-
receive.tap { |ctx| p got_ctx: ctx }
|
25
|
-
rescue Exception => e
|
26
|
-
p e
|
27
|
-
puts e.backtrace.join("\n")
|
28
|
-
nil
|
29
|
-
end
|
30
|
-
|
31
|
-
def worker_for_name(name)
|
32
|
-
@workers[name] ||= spin { worker_loop(name) }
|
33
|
-
end
|
34
|
-
|
35
|
-
def worker_loop(name)
|
36
|
-
while (client = receive)
|
37
|
-
puts "get request for #{name} from #{client.inspect}"
|
38
|
-
ctx = get_context(name)
|
39
|
-
client << ctx rescue nil
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
def get_context(name)
|
44
|
-
@contexts[name] ||= setup_context(name)
|
45
|
-
end
|
46
|
-
|
47
|
-
CERTIFICATE_REGEXP = /(-----BEGIN CERTIFICATE-----\n[^-]+-----END CERTIFICATE-----\n)/.freeze
|
48
|
-
|
49
|
-
def setup_context(name)
|
50
|
-
certificate = get_certificate(name)
|
51
|
-
ctx = OpenSSL::SSL::SSLContext.new
|
52
|
-
chain = certificate.scan(CERTIFICATE_REGEXP).map { |p| OpenSSL::X509::Certificate.new(p.first) }
|
53
|
-
cert = chain.shift
|
54
|
-
puts "Certificate expires: #{cert.not_after.inspect}"
|
55
|
-
ctx.add_certificate(cert, private_key, chain)
|
56
|
-
Polyphony::Net.setup_alpn(ctx, Tipi::ALPN_PROTOCOLS)
|
57
|
-
ctx
|
58
|
-
end
|
59
|
-
|
60
|
-
def get_certificate(name)
|
61
|
-
@store[name] ||= provision_certificate(name)
|
62
|
-
end
|
63
|
-
|
64
|
-
def private_key
|
65
|
-
@private_key ||= OpenSSL::PKey::RSA.new(4096)
|
66
|
-
end
|
67
|
-
|
68
|
-
ACME_DIRECTORY = 'https://acme-staging-v02.api.letsencrypt.org/directory'
|
69
|
-
|
70
|
-
def acme_client
|
71
|
-
@acme_client ||= setup_acme_client
|
72
|
-
end
|
73
|
-
|
74
|
-
def setup_acme_client
|
75
|
-
client = Acme::Client.new(
|
76
|
-
private_key: private_key,
|
77
|
-
directory: ACME_DIRECTORY
|
78
|
-
)
|
79
|
-
p client: client
|
80
|
-
account = client.new_account(
|
81
|
-
contact: 'mailto:info@noteflakes.com',
|
82
|
-
terms_of_service_agreed: true
|
83
|
-
)
|
84
|
-
p account: account.kid
|
85
|
-
client
|
86
|
-
end
|
87
|
-
|
88
|
-
def provision_certificate(name)
|
89
|
-
order = acme_client.new_order(identifiers: [name])
|
90
|
-
p order: true
|
91
|
-
authorization = order.authorizations.first
|
92
|
-
p authorization: authorization
|
93
|
-
challenge = authorization.http
|
94
|
-
p challenge: challenge
|
95
|
-
|
96
|
-
@challenge_handler.add(challenge)
|
97
|
-
challenge.request_validation
|
98
|
-
p challenge_status: challenge.status
|
99
|
-
while challenge.status == 'pending'
|
100
|
-
sleep(1)
|
101
|
-
challenge.reload
|
102
|
-
p challenge_status: challenge.status
|
103
|
-
end
|
104
|
-
|
105
|
-
csr = Acme::Client::CertificateRequest.new(private_key: @private_key, subject: { common_name: name })
|
106
|
-
p csr: csr
|
107
|
-
order.finalize(csr: csr)
|
108
|
-
p order_status: order.status
|
109
|
-
while order.status == 'processing'
|
110
|
-
sleep(1)
|
111
|
-
order.reload
|
112
|
-
p order_status: order.status
|
113
|
-
end
|
114
|
-
order.certificate.tap { |c| p certificate: c } # => PEM-formatted certificate
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
class AcmeHTTPChallengeHandler
|
119
|
-
def initialize
|
120
|
-
@challenges = {}
|
121
|
-
end
|
122
|
-
|
123
|
-
def add(challenge)
|
124
|
-
path = "/.well-known/acme-challenge/#{challenge.token}"
|
125
|
-
@challenges[path] = challenge
|
126
|
-
end
|
127
|
-
|
128
|
-
def call(req)
|
129
|
-
# handle incoming request
|
130
|
-
challenge = @challenges[req.path]
|
131
|
-
return req.respond(nil, ':status' => 400) unless challenge
|
132
|
-
|
133
|
-
req.respond(challenge.file_content, 'content-type' => challenge.content_type)
|
134
|
-
@challenges.delete(req.path)
|
135
|
-
end
|
136
|
-
end
|
137
|
-
|
138
|
-
challenge_handler = AcmeHTTPChallengeHandler.new
|
139
|
-
certificate_manager = CertificateManager.new(
|
140
|
-
store: {},
|
141
|
-
challenge_handler: challenge_handler
|
142
|
-
)
|
143
|
-
|
144
|
-
http_handler = Tipi.route do |r|
|
145
|
-
r.on('/.well-known/acme-challenge') { challenge_handler.call(r) }
|
146
|
-
r.default { r.redirect "https://#{r.host}#{r.path}" }
|
147
|
-
end
|
148
|
-
|
149
|
-
https_handler = ->(r) { r.respond('Hello, world!') }
|
150
|
-
|
151
|
-
http_listener = spin do
|
152
|
-
opts = {
|
153
|
-
reuse_addr: true,
|
154
|
-
dont_linger: true,
|
155
|
-
}
|
156
|
-
puts 'Listening for HTTP on localhost:10080'
|
157
|
-
Tipi.serve('0.0.0.0', 10080, opts, &http_handler)
|
158
|
-
end
|
159
|
-
|
160
|
-
https_listener = spin do
|
161
|
-
ctx = OpenSSL::SSL::SSLContext.new
|
162
|
-
ctx.servername_cb = proc { |_, name| p name: name; certificate_manager[name] }
|
163
|
-
opts = {
|
164
|
-
reuse_addr: true,
|
165
|
-
dont_linger: true,
|
166
|
-
secure_context: ctx,
|
167
|
-
alpn_protocols: Tipi::ALPN_PROTOCOLS
|
168
|
-
}
|
169
|
-
|
170
|
-
puts 'Listening for HTTPS on localhost:10443'
|
171
|
-
server = Polyphony::Net.tcp_listen('0.0.0.0', 10443, opts)
|
172
|
-
server.accept_loop do |client|
|
173
|
-
spin do
|
174
|
-
service.incr_connection_count
|
175
|
-
Tipi.client_loop(client, opts) { |req| service.http_request(req) }
|
176
|
-
ensure
|
177
|
-
service.decr_connection_count
|
178
|
-
end
|
179
|
-
rescue Exception => e
|
180
|
-
puts "HTTPS accept_loop error: #{e.inspect}"
|
181
|
-
puts e.backtrace.join("\n")
|
182
|
-
end
|
183
|
-
end
|
184
|
-
|
185
|
-
begin
|
186
|
-
Fiber.await(http_listener, https_listener)
|
187
|
-
rescue Interrupt
|
188
|
-
puts "Got SIGINT, terminating"
|
189
|
-
rescue Exception => e
|
190
|
-
puts '*' * 40
|
191
|
-
p e
|
192
|
-
puts e.backtrace.join("\n")
|
193
|
-
end
|
data/ext/tipi/extconf.rb
DELETED