tipi 0.41 → 0.42

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.
@@ -9,33 +9,6 @@ class String
9
9
  end
10
10
  end
11
11
 
12
- class IO
13
- # Creates two mockup sockets for simulating server-client communication
14
- def self.server_client_mockup
15
- server_in, client_out = IO.pipe
16
- client_in, server_out = IO.pipe
17
-
18
- server_connection = mockup_connection(server_in, server_out, client_out)
19
- client_connection = mockup_connection(client_in, client_out, server_out)
20
-
21
- [server_connection, client_connection]
22
- end
23
-
24
- def self.mockup_connection(input, output, output2)
25
- eg(
26
- :read => ->(*args) { input.read(*args) },
27
- :read_loop => ->(*args, &block) { input.read_loop(*args, &block) },
28
- :recv_loop => ->(*args, &block) { input.read_loop(*args, &block) },
29
- :readpartial => ->(*args) { input.readpartial(*args) },
30
- :recv => ->(*args) { input.readpartial(*args) },
31
- :<< => ->(*args) { output.write(*args) },
32
- :write => ->(*args) { output.write(*args) },
33
- :close => -> { output.close },
34
- :eof? => -> { output2.closed? }
35
- )
36
- end
37
- end
38
-
39
12
  class HTTP1ServerTest < MiniTest::Test
40
13
  def teardown
41
14
  @server&.interrupt if @server&.alive?
data/test/test_request.rb CHANGED
@@ -9,33 +9,6 @@ class String
9
9
  end
10
10
  end
11
11
 
12
- class IO
13
- # Creates two mockup sockets for simulating server-client communication
14
- def self.server_client_mockup
15
- server_in, client_out = IO.pipe
16
- client_in, server_out = IO.pipe
17
-
18
- server_connection = mockup_connection(server_in, server_out, client_out)
19
- client_connection = mockup_connection(client_in, client_out, server_out)
20
-
21
- [server_connection, client_connection]
22
- end
23
-
24
- def self.mockup_connection(input, output, output2)
25
- eg(
26
- :read => ->(*args) { input.read(*args) },
27
- :read_loop => ->(*args, &block) { input.read_loop(*args, &block) },
28
- :recv_loop => ->(*args, &block) { input.read_loop(*args, &block) },
29
- :readpartial => ->(*args) { input.readpartial(*args) },
30
- :recv => ->(*args) { input.readpartial(*args) },
31
- :<< => ->(*args) { output.write(*args) },
32
- :write => ->(*args) { output.write(*args) },
33
- :close => -> { output.close },
34
- :eof? => -> { output2.closed? }
35
- )
36
- end
37
- end
38
-
39
12
  class RequestHeadersTest < MiniTest::Test
40
13
  def teardown
41
14
  @server&.interrupt if @server&.alive?
@@ -65,7 +38,7 @@ class RequestHeadersTest < MiniTest::Test
65
38
  assert_kind_of Qeweney::Request, req
66
39
  assert_equal 'blah.com', req.headers['host']
67
40
  assert_equal 'bar', req.headers['foo']
68
- assert_equal ['1', '3', '2'], req.headers['hi']
41
+ assert_equal ['1', '2', '3'], req.headers['hi']
69
42
  assert_equal 'get', req.headers[':method']
70
43
  assert_equal '/titi', req.headers[':path']
71
44
  end
data/tipi.gemspec CHANGED
@@ -20,24 +20,25 @@ Gem::Specification.new do |s|
20
20
 
21
21
  s.executables = ['tipi']
22
22
 
23
- s.add_runtime_dependency 'polyphony', '~>0.64'
24
- s.add_runtime_dependency 'qeweney', '~>0.11'
23
+ s.add_runtime_dependency 'polyphony', '~>0.69'
24
+ s.add_runtime_dependency 'qeweney', '~>0.14'
25
+ s.add_runtime_dependency 'extralite', '~>1.2'
25
26
 
26
- s.add_runtime_dependency 'http_parser.rb', '~>0.6.0'
27
- s.add_runtime_dependency 'http-2', '~>0.10.0'
27
+ s.add_runtime_dependency 'http-2', '~>0.11'
28
28
  s.add_runtime_dependency 'rack', '>=2.0.8', '<2.3.0'
29
29
  s.add_runtime_dependency 'websocket', '~>1.2.8'
30
30
  s.add_runtime_dependency 'acme-client', '~>2.0.8'
31
+ s.add_runtime_dependency 'localhost', '~>1.1.4'
31
32
 
32
33
  # for digital fabric
33
34
  s.add_runtime_dependency 'msgpack', '~>1.4.2'
34
35
 
35
36
  s.add_development_dependency 'rake-compiler', '1.1.1'
36
37
  s.add_development_dependency 'rake', '~>12.3.3'
37
- s.add_development_dependency 'localhost', '~>1.1.4'
38
38
  s.add_development_dependency 'minitest', '~>5.11.3'
39
39
  s.add_development_dependency 'minitest-reporters', '~>1.4.2'
40
40
  s.add_development_dependency 'simplecov', '~>0.17.1'
41
41
 
42
42
  s.add_development_dependency 'cuba', '~>3.9.3'
43
+ s.add_development_dependency 'http_parser.rb', '0.7.0'
43
44
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tipi
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.41'
4
+ version: '0.42'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sharon Rosner
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-07-26 00:00:00.000000000 Z
11
+ date: 2021-08-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: polyphony
@@ -16,56 +16,56 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0.64'
19
+ version: '0.69'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '0.64'
26
+ version: '0.69'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: qeweney
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '0.11'
33
+ version: '0.14'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '0.11'
40
+ version: '0.14'
41
41
  - !ruby/object:Gem::Dependency
42
- name: http_parser.rb
42
+ name: extralite
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: 0.6.0
47
+ version: '1.2'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: 0.6.0
54
+ version: '1.2'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: http-2
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: 0.10.0
61
+ version: '0.11'
62
62
  type: :runtime
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: 0.10.0
68
+ version: '0.11'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: rack
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -114,6 +114,20 @@ dependencies:
114
114
  - - "~>"
115
115
  - !ruby/object:Gem::Version
116
116
  version: 2.0.8
117
+ - !ruby/object:Gem::Dependency
118
+ name: localhost
119
+ requirement: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - "~>"
122
+ - !ruby/object:Gem::Version
123
+ version: 1.1.4
124
+ type: :runtime
125
+ prerelease: false
126
+ version_requirements: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - "~>"
129
+ - !ruby/object:Gem::Version
130
+ version: 1.1.4
117
131
  - !ruby/object:Gem::Dependency
118
132
  name: msgpack
119
133
  requirement: !ruby/object:Gem::Requirement
@@ -156,20 +170,6 @@ dependencies:
156
170
  - - "~>"
157
171
  - !ruby/object:Gem::Version
158
172
  version: 12.3.3
159
- - !ruby/object:Gem::Dependency
160
- name: localhost
161
- requirement: !ruby/object:Gem::Requirement
162
- requirements:
163
- - - "~>"
164
- - !ruby/object:Gem::Version
165
- version: 1.1.4
166
- type: :development
167
- prerelease: false
168
- version_requirements: !ruby/object:Gem::Requirement
169
- requirements:
170
- - - "~>"
171
- - !ruby/object:Gem::Version
172
- version: 1.1.4
173
173
  - !ruby/object:Gem::Dependency
174
174
  name: minitest
175
175
  requirement: !ruby/object:Gem::Requirement
@@ -226,6 +226,20 @@ dependencies:
226
226
  - - "~>"
227
227
  - !ruby/object:Gem::Version
228
228
  version: 3.9.3
229
+ - !ruby/object:Gem::Dependency
230
+ name: http_parser.rb
231
+ requirement: !ruby/object:Gem::Requirement
232
+ requirements:
233
+ - - '='
234
+ - !ruby/object:Gem::Version
235
+ version: 0.7.0
236
+ type: :development
237
+ prerelease: false
238
+ version_requirements: !ruby/object:Gem::Requirement
239
+ requirements:
240
+ - - '='
241
+ - !ruby/object:Gem::Version
242
+ version: 0.7.0
229
243
  description:
230
244
  email: sharon@noteflakes.com
231
245
  executables:
@@ -244,6 +258,9 @@ files:
244
258
  - README.md
245
259
  - Rakefile
246
260
  - TODO.md
261
+ - benchmarks/bm_http1_parser.rb
262
+ - bin/benchmark
263
+ - bin/h1pd
247
264
  - bin/tipi
248
265
  - df/agent.rb
249
266
  - df/etc_benchmark.rb
@@ -258,8 +275,8 @@ files:
258
275
  - df/ws_page.html
259
276
  - docs/README.md
260
277
  - docs/tipi-logo.png
261
- - examples/automatic_certificate.rb
262
278
  - examples/cuba.ru
279
+ - examples/full_service.rb
263
280
  - examples/hanami-api.ru
264
281
  - examples/hello.ru
265
282
  - examples/http1_parser.rb
@@ -284,6 +301,7 @@ files:
284
301
  - examples/rack_server_https.rb
285
302
  - examples/rack_server_https_forked.rb
286
303
  - examples/routing_server.rb
304
+ - examples/servername_cb.rb
287
305
  - examples/websocket_client.rb
288
306
  - examples/websocket_demo.rb
289
307
  - examples/websocket_secure_server.rb
@@ -295,6 +313,8 @@ files:
295
313
  - ext/tipi/http1_parser.h
296
314
  - ext/tipi/tipi_ext.c
297
315
  - lib/tipi.rb
316
+ - lib/tipi/acme.rb
317
+ - lib/tipi/cli.rb
298
318
  - lib/tipi/config_dsl.rb
299
319
  - lib/tipi/configuration.rb
300
320
  - lib/tipi/digital_fabric.rb
@@ -307,17 +327,18 @@ files:
307
327
  - lib/tipi/digital_fabric/service.rb
308
328
  - lib/tipi/handler.rb
309
329
  - lib/tipi/http1_adapter.rb
310
- - lib/tipi/http1_adapter_new.rb
311
330
  - lib/tipi/http2_adapter.rb
312
331
  - lib/tipi/http2_stream.rb
313
332
  - lib/tipi/rack_adapter.rb
314
333
  - lib/tipi/response_extensions.rb
315
334
  - lib/tipi/version.rb
316
335
  - lib/tipi/websocket.rb
336
+ - security/http1.rb
317
337
  - test/coverage.rb
318
338
  - test/eg.rb
319
339
  - test/helper.rb
320
340
  - test/run.rb
341
+ - test/test_http1_parser.rb
321
342
  - test/test_http_server.rb
322
343
  - test/test_request.rb
323
344
  - tipi.gemspec
@@ -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