tipi 0.41 → 0.42

Sign up to get free protection for your applications and to get access to all the features.
@@ -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