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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5a83c181c05d8d8c8de50f236feab65b800b1bd266c2f0a4d4696d4853ac39fd
4
- data.tar.gz: d9a84fc9f72c3a5565f587e35fb4ed206c83c863fbb0b4b470449919a687463b
3
+ metadata.gz: 82155f4b86223d3fb32dc8c314abfeb75fda951747e63832ed5c15bc3b1f43cc
4
+ data.tar.gz: 721b98fb8d330d1bc38f8f236f82ddd909235d31f5875c1b4f89895e4e3926da
5
5
  SHA512:
6
- metadata.gz: e9c13f387c3431888d9d43c88d5bd00831c4e2e8976bf8ae4e1acc479b8d50fa888cd58af0fc433da2dab8e3ea09b99ca0adbdcc61abc71457a805c26aa03687
7
- data.tar.gz: df01a54c7c46928c67da321cfc830d8f33e8af7fa8197cd5930f3de73838efd4849d51b10af35b1a5297f7f484b296ff2ed6146dbb8d66476bf0b714f1dbfd68
6
+ metadata.gz: 10389779975234d90c968626b4f9d6b168491f9a32ecf1be71683c37cf11f58f1bac52a36478691e72328236a16d309a8462104bda556772e25cebe52b2b0d53
7
+ data.tar.gz: 99db2c15278cdfc286ffad05f266f5d62eec6a3bb1363f8dfa4c302549a308a691496ee60ec085ede59e3b594533e8e196bbca144878e5a6dccf2ecfc962151c
@@ -23,5 +23,9 @@ jobs:
23
23
  run: |
24
24
  gem install bundler
25
25
  POLYPHONY_USE_LIBEV=1 bundle install
26
+ - name: Show Linux kernel version
27
+ run: uname -r
28
+ - name: Compile C-extension
29
+ run: bundle exec rake compile
26
30
  - name: Run tests
27
31
  run: bundle exec rake test
data/.gitignore CHANGED
@@ -55,5 +55,7 @@ build-iPhoneSimulator/
55
55
  # Used by RuboCop. Remote config files pulled in from inherit_from directive.
56
56
  # .rubocop-https?--*
57
57
  log
58
+ log.*
58
59
 
59
- lib/tipi_ext*
60
+ lib/tipi_ext*
61
+ examples/certificate_store.db
data/CHANGELOG.md CHANGED
@@ -1,3 +1,14 @@
1
+ ## 0.42 2021-08-16
2
+
3
+ - HTTP/1 parser: disable UTF-8 parsing for all but header values
4
+ - Add support for parsing HTTP/1 from callable source
5
+ - Introduce full_service API for automatic HTTPS
6
+ - Introduce automatic SSL certificate provisioning
7
+ - Improve handling of exceptions
8
+ - Various fixes to DF service and agent pxoy
9
+ - Fix upgrading to HTTP2 with a request body
10
+ - Switch to new HTTP/1 parser
11
+
1
12
  ## 0.41 2021-07-26
2
13
 
3
14
  - Fix Rack adapter (#11)
data/Gemfile CHANGED
@@ -1,3 +1,7 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- gemspec
3
+ gemspec
4
+ %w{polyphony qeweney}.each do |dep|
5
+ dir = "../#{dep}"
6
+ gem(dep, path: dir) if File.directory?(dir)
7
+ end
data/Gemfile.lock CHANGED
@@ -1,13 +1,25 @@
1
+ PATH
2
+ remote: ../polyphony
3
+ specs:
4
+ polyphony (0.69)
5
+
6
+ PATH
7
+ remote: ../qeweney
8
+ specs:
9
+ qeweney (0.14)
10
+ escape_utils (~> 1.2.1)
11
+
1
12
  PATH
2
13
  remote: .
3
14
  specs:
4
- tipi (0.41)
15
+ tipi (0.42)
5
16
  acme-client (~> 2.0.8)
6
- http-2 (~> 0.10.0)
7
- http_parser.rb (~> 0.6.0)
17
+ extralite (~> 1.2)
18
+ http-2 (~> 0.11)
19
+ localhost (~> 1.1.4)
8
20
  msgpack (~> 1.4.2)
9
- polyphony (~> 0.64)
10
- qeweney (~> 0.11)
21
+ polyphony (~> 0.69)
22
+ qeweney (~> 0.14)
11
23
  rack (>= 2.0.8, < 2.3.0)
12
24
  websocket (~> 1.2.8)
13
25
 
@@ -22,7 +34,8 @@ GEM
22
34
  rack (>= 1.6.0)
23
35
  docile (1.4.0)
24
36
  escape_utils (1.2.1)
25
- faraday (1.5.1)
37
+ extralite (1.2)
38
+ faraday (1.7.0)
26
39
  faraday-em_http (~> 1.0)
27
40
  faraday-em_synchrony (~> 1.0)
28
41
  faraday-excon (~> 1.1)
@@ -30,6 +43,7 @@ GEM
30
43
  faraday-net_http (~> 1.0)
31
44
  faraday-net_http_persistent (~> 1.1)
32
45
  faraday-patron (~> 1.0)
46
+ faraday-rack (~> 1.0)
33
47
  multipart-post (>= 1.2, < 3)
34
48
  ruby2_keywords (>= 0.0.4)
35
49
  faraday-em_http (1.0.0)
@@ -39,8 +53,9 @@ GEM
39
53
  faraday-net_http (1.0.1)
40
54
  faraday-net_http_persistent (1.2.0)
41
55
  faraday-patron (1.0.0)
42
- http-2 (0.10.2)
43
- http_parser.rb (0.6.0)
56
+ faraday-rack (1.0.0)
57
+ http-2 (0.11.0)
58
+ http_parser.rb (0.7.0)
44
59
  json (2.5.1)
45
60
  localhost (1.1.8)
46
61
  minitest (5.11.3)
@@ -51,9 +66,6 @@ GEM
51
66
  ruby-progressbar
52
67
  msgpack (1.4.2)
53
68
  multipart-post (2.1.1)
54
- polyphony (0.64)
55
- qeweney (0.11)
56
- escape_utils (~> 1.2.1)
57
69
  rack (2.2.3)
58
70
  rake (12.3.3)
59
71
  rake-compiler (1.1.1)
@@ -72,9 +84,11 @@ PLATFORMS
72
84
 
73
85
  DEPENDENCIES
74
86
  cuba (~> 3.9.3)
75
- localhost (~> 1.1.4)
87
+ http_parser.rb (= 0.7.0)
76
88
  minitest (~> 5.11.3)
77
89
  minitest-reporters (~> 1.4.2)
90
+ polyphony!
91
+ qeweney!
78
92
  rake (~> 12.3.3)
79
93
  rake-compiler (= 1.1.1)
80
94
  simplecov (~> 0.17.1)
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+
5
+ HTTP_REQUEST = "GET /foo HTTP/1.1\r\nHost: example.com\r\nAccept: */*\r\n\r\n"
6
+
7
+ def benchmark_other_http1_parser(iterations)
8
+ STDOUT << "http_parser.rb: "
9
+ require 'http_parser.rb'
10
+
11
+ i, o = IO.pipe
12
+ parser = Http::Parser.new
13
+ done = false
14
+ headers = nil
15
+ parser.on_headers_complete = proc do |h|
16
+ headers = h
17
+ headers[':method'] = parser.http_method
18
+ headers[':path'] = parser.request_url
19
+ headers[':protocol'] = parser.http_version
20
+ end
21
+ parser.on_message_complete = proc { done = true }
22
+
23
+ t0 = Time.now
24
+ iterations.times do
25
+ o << HTTP_REQUEST
26
+ done = false
27
+ while !done
28
+ msg = i.readpartial(4096)
29
+ parser << msg
30
+ end
31
+ end
32
+ t1 = Time.now
33
+ puts "#{iterations / (t1 - t0)} ips"
34
+ end
35
+
36
+ def benchmark_tipi_http1_parser(iterations)
37
+ STDOUT << "tipi parser: "
38
+ require_relative '../lib/tipi_ext'
39
+ i, o = IO.pipe
40
+ reader = proc { |len| i.readpartial(len) }
41
+ parser = Tipi::HTTP1Parser.new(reader)
42
+
43
+ t0 = Time.now
44
+ iterations.times do
45
+ o << HTTP_REQUEST
46
+ headers = parser.parse_headers
47
+ end
48
+ t1 = Time.now
49
+ puts "#{iterations / (t1 - t0)} ips"
50
+ end
51
+
52
+ def fork_benchmark(method, iterations)
53
+ pid = fork { send(method, iterations) }
54
+ Process.wait(pid)
55
+ end
56
+
57
+ x = 500000
58
+ fork_benchmark(:benchmark_other_http1_parser, x)
59
+ fork_benchmark(:benchmark_tipi_http1_parser, x)
60
+
61
+ # benchmark_tipi_http1_parser(x)
data/bin/benchmark ADDED
@@ -0,0 +1,37 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+
6
+ def parse_latency(latency)
7
+ m = latency.match(/^([\d\.]+)(us|ms|s)$/)
8
+ return nil unless m
9
+
10
+ value = m[1].to_f
11
+ case m[2]
12
+ when 's' then value
13
+ when 'ms' then value / 1000
14
+ when 'us' then value / 1000000
15
+ end
16
+ end
17
+
18
+ def parse_wrk_results(results)
19
+ lines = results.lines
20
+ latencies = lines[3].strip.split(/\s+/)
21
+ throughput = lines[6].strip.split(/\s+/)
22
+
23
+ {
24
+ latency_avg: parse_latency(latencies[1]),
25
+ latency_max: parse_latency(latencies[3]),
26
+ rate: throughput[1].to_f
27
+ }
28
+ end
29
+
30
+ def run_wrk(duration: 10, threads: 2, connections: 10, url: )
31
+ `wrk -d#{duration} -t#{threads} -c#{connections} #{url}`
32
+ end
33
+
34
+ [8, 64, 256, 512].each do |c|
35
+ puts "connections: #{c}"
36
+ p parse_wrk_results(run_wrk(duration: 10, threads: 4, connections: c, url: "http://localhost:10080/"))
37
+ end
data/bin/h1pd ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env bash
2
+
3
+ set -e
4
+ rake compile
5
+ ruby test/test_http1_parser.rb
6
+ ruby benchmarks/bm_http1_parser.rb
data/bin/tipi CHANGED
@@ -1,26 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require 'bundler/setup'
4
- require 'polyphony'
5
- require File.expand_path('../lib/tipi/configuration', __dir__)
4
+ require 'tipi/cli'
6
5
 
7
- config = {}
8
- #config[:forked] = 4
6
+ trap('SIGINT') { exit }
9
7
 
10
- puts DATA.read
11
- puts
12
-
13
- configuration_manager = spin { Tipi::Configuration.supervise_config }
14
-
15
- configuration_manager << config
16
- configuration_manager.await
17
-
18
- __END__
19
-
20
- ooo
21
- oo
22
- o
23
- \|/
24
- / \ Tipi - A better web server for a better world
25
- /___\
26
-
8
+ Tipi::CLI.start
data/df/server.rb CHANGED
@@ -17,7 +17,7 @@ rescue Interrupt
17
17
  rescue SystemExit
18
18
  # ignore
19
19
  rescue Exception => e
20
- log("Uncaught exception", error: e, backtrace: e.backtrace)
20
+ log("Uncaught exception", error: e, source: e.source_fiber, raising: e.raising_fiber, backtrace: e.backtrace)
21
21
  ensure
22
22
  log('DF server stopped')
23
23
  end
data/df/server_utils.rb CHANGED
@@ -7,24 +7,7 @@ require 'tipi/digital_fabric/executive'
7
7
  require 'json'
8
8
  require 'fileutils'
9
9
  require 'time'
10
-
11
- module ::Kernel
12
- def trace(*args)
13
- STDOUT.orig_write(format_trace(args))
14
- end
15
-
16
- def format_trace(args)
17
- if args.first.is_a?(String)
18
- if args.size > 1
19
- format("%s: %p\n", args.shift, args)
20
- else
21
- format("%s\n", args.first)
22
- end
23
- else
24
- format("%p\n", args.size == 1 ? args.first : args)
25
- end
26
- end
27
- end
10
+ require 'polyphony/extensions/debug'
28
11
 
29
12
  FileUtils.cd(__dir__)
30
13
 
@@ -65,8 +48,10 @@ def listen_http
65
48
  # log("Done with HTTP connection", client: client)
66
49
  @service.decr_connection_count
67
50
  end
68
- rescue => e
69
- log("HTTP accept loop error", error: e, backtrace: e.backtrace)
51
+ rescue Polyphony::BaseException
52
+ raise
53
+ rescue Exception => e
54
+ log 'HTTP accept (unknown) error', error: e, backtrace: e.backtrace
70
55
  end
71
56
  end
72
57
  end
@@ -74,7 +59,7 @@ end
74
59
  CERTIFICATE_REGEXP = /(-----BEGIN CERTIFICATE-----\n[^-]+-----END CERTIFICATE-----\n)/.freeze
75
60
 
76
61
  def listen_https
77
- spin('https_listener') do
62
+ spin(:https_listener) do
78
63
  private_key = OpenSSL::PKey::RSA.new IO.read('../../reality/ssl/privkey.pem')
79
64
  c = IO.read('../../reality/ssl/cacert.pem')
80
65
  certificates = c.scan(CERTIFICATE_REGEXP).map { |p| OpenSSL::X509::Certificate.new(p.first) }
@@ -83,6 +68,13 @@ def listen_https
83
68
  log "SSL Certificate expires: #{cert.not_after.inspect}"
84
69
  ctx.add_certificate(cert, private_key, certificates)
85
70
  ctx.ciphers = 'ECDH+aRSA'
71
+ ctx.send(
72
+ :set_minmax_proto_version,
73
+ OpenSSL::SSL::SSL3_VERSION,
74
+ OpenSSL::SSL::TLS1_3_VERSION
75
+ )
76
+ # ctx.min_version = OpenSSL::SSL::SSL3_VERSION #OpenSSL::SSL::TLS1_VERSION
77
+ # ctx.max_version = OpenSSL::SSL::TLS1_3_VERSION
86
78
 
87
79
  # TODO: further limit ciphers
88
80
  # ref: https://github.com/socketry/falcon/blob/3ec805b3ceda0a764a2c5eb68cde33897b6a35ff/lib/falcon/environments/tls.rb
@@ -99,8 +91,10 @@ def listen_https
99
91
  server = Polyphony::Net.tcp_listen('0.0.0.0', 10443, opts)
100
92
  id = 0
101
93
  loop do
94
+ log('Before HTTPS server.accept')
102
95
  client = server.accept
103
- # log('Accept HTTPS client connection', client: client)
96
+ log('After HTTPS server.accept')
97
+ log('Accept HTTPS client connection', client: client)
104
98
  spin("https#{id += 1}") do
105
99
  @service.incr_connection_count
106
100
  Tipi.client_loop(client, opts) { |req| @service.http_request(req) }
@@ -111,9 +105,11 @@ def listen_https
111
105
  @service.decr_connection_count
112
106
  end
113
107
  rescue OpenSSL::SSL::SSLError, SystemCallError, TypeError => e
114
- # log('HTTPS accept error', error: e, backtrace: e.backtrace)
115
- rescue => e
116
- log('HTTPS accept (unknown) error', error: e, backtrace: e.backtrace)
108
+ log('HTTPS accept error', error: e)
109
+ rescue Polyphony::BaseException
110
+ raise
111
+ rescue Exception => e
112
+ log 'HTTPS accept (unknown) error', error: e, backtrace: e.backtrace
117
113
  end
118
114
  end
119
115
  end
@@ -126,13 +122,16 @@ def listen_unix
126
122
  socket = UNIXServer.new(UNIX_SOCKET_PATH)
127
123
 
128
124
  id = 0
129
- socket.accept_loop do |client|
125
+ loop do
126
+ client = socket.accept
130
127
  # log('Accept Unix connection', client: client)
131
128
  spin("unix#{id += 1}") do
132
129
  Tipi.client_loop(client, {}) { |req| @service.http_request(req, true) }
133
130
  end
134
- rescue OpenSSL::SSL::SSLError
135
- # disregard
131
+ rescue Polyphony::BaseException
132
+ raise
133
+ rescue Exception => e
134
+ log 'Unix accept error', error: e, backtrace: e.backtrace
136
135
  end
137
136
  end
138
137
  end
@@ -141,33 +140,39 @@ def listen_df
141
140
  spin(:df_listener) do
142
141
  opts = {
143
142
  reuse_addr: true,
143
+ reuse_port: true,
144
144
  dont_linger: true,
145
145
  }
146
146
  log('Listening for DF connections on localhost:4321')
147
147
  server = Polyphony::Net.tcp_listen('0.0.0.0', 4321, opts)
148
148
 
149
149
  id = 0
150
- server.accept_loop do |client|
150
+ loop do
151
+ client = server.accept
151
152
  # log('Accept DF connection', client: client)
152
153
  spin("df#{id += 1}") do
153
154
  Tipi.client_loop(client, {}) { |req| @service.http_request(req, true) }
154
155
  end
155
- rescue OpenSSL::SSL::SSLError
156
- # disregard
156
+ rescue Polyphony::BaseException
157
+ raise
158
+ rescue Exception => e
159
+ log 'DF accept (unknown) error', error: e, backtrace: e.backtrace
157
160
  end
158
161
  end
159
162
  end
160
163
 
161
- # Thread.backend.trace_proc = proc do |event, fiber, value, pri|
162
- # fiber_id = fiber.tag || fiber.inspect
163
- # case event
164
- # when :fiber_schedule
165
- # log format("=> %s %s %s %s", event, fiber_id, value.inspect, pri ? '' : '(priority)')
166
- # when :fiber_run
167
- # log format("=> %s %s %s", event, fiber_id, value.inspect)
168
- # when :fiber_create, :fiber_terminate
169
- # log format("=> %s %s", event, fiber_id)
170
- # else
171
- # log format("=> %s", event)
172
- # end
173
- # end
164
+ if ENV['TRACE'] == '1'
165
+ Thread.backend.trace_proc = proc do |event, fiber, value, pri|
166
+ fiber_id = fiber.tag || fiber.inspect
167
+ case event
168
+ when :fiber_schedule
169
+ log format("=> %s %s %s %s", event, fiber_id, value.inspect, pri ? '(priority)' : '')
170
+ when :fiber_run
171
+ log format("=> %s %s %s", event, fiber_id, value.inspect)
172
+ when :fiber_create, :fiber_terminate
173
+ log format("=> %s %s", event, fiber_id)
174
+ else
175
+ log format("=> %s", event)
176
+ end
177
+ end
178
+ end