tipi 0.40 → 0.45
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 +5 -1
- data/CHANGELOG.md +35 -0
- data/Gemfile +7 -1
- data/Gemfile.lock +55 -29
- data/README.md +184 -8
- data/Rakefile +1 -3
- 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 +16 -102
- data/df/server_utils.rb +175 -0
- data/examples/full_service.rb +13 -0
- data/examples/hello.rb +5 -0
- data/examples/http1_parser.rb +55 -0
- data/examples/http_server.js +1 -1
- data/examples/http_server.rb +15 -3
- data/examples/http_server_graceful.rb +1 -1
- data/examples/http_server_static.rb +6 -18
- 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 +315 -0
- data/lib/tipi/cli.rb +93 -0
- data/lib/tipi/config_dsl.rb +13 -13
- data/lib/tipi/configuration.rb +2 -2
- data/{e → lib/tipi/controller/bare_polyphony.rb} +0 -0
- data/lib/tipi/controller/bare_stock.rb +10 -0
- data/lib/tipi/controller/stock_http1_adapter.rb +15 -0
- data/lib/tipi/controller/web_polyphony.rb +351 -0
- data/lib/tipi/controller/web_stock.rb +631 -0
- data/lib/tipi/controller.rb +12 -0
- data/lib/tipi/digital_fabric/agent.rb +10 -8
- data/lib/tipi/digital_fabric/agent_proxy.rb +26 -12
- data/lib/tipi/digital_fabric/executive.rb +7 -3
- data/lib/tipi/digital_fabric/protocol.rb +19 -4
- data/lib/tipi/digital_fabric/request_adapter.rb +0 -4
- data/lib/tipi/digital_fabric/service.rb +84 -56
- data/lib/tipi/handler.rb +2 -2
- data/lib/tipi/http1_adapter.rb +86 -125
- data/lib/tipi/http2_adapter.rb +29 -16
- data/lib/tipi/http2_stream.rb +52 -56
- data/lib/tipi/rack_adapter.rb +2 -53
- data/lib/tipi/response_extensions.rb +2 -2
- 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 +8 -5
- 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 +12 -8
- metadata +88 -28
- data/examples/automatic_certificate.rb +0 -193
data/lib/tipi/rack_adapter.rb
CHANGED
@@ -4,69 +4,18 @@ require 'rack'
|
|
4
4
|
|
5
5
|
module Tipi
|
6
6
|
module RackAdapter
|
7
|
-
# Implements a rack input stream:
|
8
|
-
# https://www.rubydoc.info/github/rack/rack/master/file/SPEC#label-The+Input+Stream
|
9
|
-
class InputStream
|
10
|
-
def initialize(request)
|
11
|
-
@request = request
|
12
|
-
end
|
13
|
-
|
14
|
-
def gets; end
|
15
|
-
|
16
|
-
def read(length = nil, outbuf = nil); end
|
17
|
-
|
18
|
-
def each(&block)
|
19
|
-
@request.each_chunk(&block)
|
20
|
-
end
|
21
|
-
|
22
|
-
def rewind; end
|
23
|
-
end
|
24
|
-
|
25
7
|
class << self
|
26
8
|
def run(app)
|
27
9
|
->(req) { respond(req, app.(env(req))) }
|
28
10
|
end
|
29
|
-
|
11
|
+
|
30
12
|
def load(path)
|
31
13
|
src = IO.read(path)
|
32
14
|
instance_eval(src, path, 1)
|
33
15
|
end
|
34
16
|
|
35
|
-
RACK_ENV = {
|
36
|
-
'SCRIPT_NAME' => '',
|
37
|
-
'rack.version' => Rack::VERSION,
|
38
|
-
'SERVER_PORT' => '80', # ?
|
39
|
-
'rack.url_scheme' => 'http', # ?
|
40
|
-
'rack.errors' => STDERR, # ?
|
41
|
-
'rack.multithread' => false,
|
42
|
-
'rack.run_once' => false,
|
43
|
-
'rack.hijack?' => false,
|
44
|
-
'rack.hijack' => nil,
|
45
|
-
'rack.hijack_io' => nil,
|
46
|
-
'rack.session' => nil,
|
47
|
-
'rack.logger' => nil,
|
48
|
-
'rack.multipart.buffer_size' => nil,
|
49
|
-
'rack.multipar.tempfile_factory' => nil
|
50
|
-
}
|
51
|
-
|
52
17
|
def env(request)
|
53
|
-
|
54
|
-
h[k] = env_value_from_request(request, k)
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
HTTP_HEADER_RE = /^HTTP_(.+)$/.freeze
|
59
|
-
|
60
|
-
def env_value_from_request(request, key)
|
61
|
-
case key
|
62
|
-
when 'REQUEST_METHOD' then request.method
|
63
|
-
when 'PATH_INFO' then request.path
|
64
|
-
when 'QUERY_STRING' then request.query_string || ''
|
65
|
-
when 'SERVER_NAME' then request.headers['host']
|
66
|
-
when 'rack.input' then InputStream.new(request)
|
67
|
-
when HTTP_HEADER_RE then request.headers[$1.downcase]
|
68
|
-
else RACK_ENV[key]
|
69
|
-
end
|
18
|
+
Qeweney.rack_env_from_request(request)
|
70
19
|
end
|
71
20
|
|
72
21
|
def respond(request, (status_code, headers, body))
|
@@ -8,8 +8,8 @@ module Tipi
|
|
8
8
|
|
9
9
|
def serve_io(io, opts)
|
10
10
|
if !opts[:stat] || opts[:stat].size >= SPLICE_CHUNKS_SIZE_THRESHOLD
|
11
|
-
@adapter.respond_from_io(self, io, opts[:headers])
|
12
|
-
else
|
11
|
+
@adapter.respond_from_io(self, io, opts[:headers], opts[:chunk_size] || 2**14)
|
12
|
+
else
|
13
13
|
respond(io.read, opts[:headers] || {})
|
14
14
|
end
|
15
15
|
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'polyphony'
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
module Tipi
|
7
|
+
module Supervisor
|
8
|
+
class << self
|
9
|
+
def run(opts)
|
10
|
+
puts "Start supervisor pid: #{Process.pid}"
|
11
|
+
@opts = opts
|
12
|
+
@controller_watcher = start_controller_watcher
|
13
|
+
supervise_loop
|
14
|
+
end
|
15
|
+
|
16
|
+
def start_controller_watcher
|
17
|
+
spin do
|
18
|
+
cmd = controller_cmd
|
19
|
+
puts "Starting controller..."
|
20
|
+
pid = Kernel.spawn(*cmd)
|
21
|
+
@controller_pid = pid
|
22
|
+
puts "Controller pid: #{pid}"
|
23
|
+
_pid, status = Polyphony.backend_waitpid(pid)
|
24
|
+
puts "Controller has terminated with status: #{status.inspect}"
|
25
|
+
terminated = true
|
26
|
+
ensure
|
27
|
+
if pid && !terminated
|
28
|
+
puts "Terminate controller #{pid.inspect}"
|
29
|
+
Polyphony::Process.kill_process(pid)
|
30
|
+
end
|
31
|
+
Fiber.current.parent << pid
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def controller_cmd
|
36
|
+
[
|
37
|
+
'ruby',
|
38
|
+
File.join(__dir__, 'controller.rb'),
|
39
|
+
@opts.to_json
|
40
|
+
]
|
41
|
+
end
|
42
|
+
|
43
|
+
def supervise_loop
|
44
|
+
this_fiber = Fiber.current
|
45
|
+
trap('SIGUSR2') { this_fiber << :replace_controller }
|
46
|
+
loop do
|
47
|
+
case (msg = receive)
|
48
|
+
when :replace_controller
|
49
|
+
replace_controller
|
50
|
+
when Integer
|
51
|
+
pid = msg
|
52
|
+
if pid == @controller_pid
|
53
|
+
puts 'Detected dead controller. Restarting...'
|
54
|
+
exit!
|
55
|
+
@controller_watcher.restart
|
56
|
+
end
|
57
|
+
else
|
58
|
+
raise "Invalid message received: #{msg.inspect}"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def replace_controller
|
64
|
+
puts "Replacing controller"
|
65
|
+
old_watcher = @controller_watcher
|
66
|
+
@controller_watcher = start_controller_watcher
|
67
|
+
|
68
|
+
# TODO: we'll want to get some kind of signal from the new controller once it's ready
|
69
|
+
sleep 1
|
70
|
+
|
71
|
+
old_watcher.terminate(true)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
data/lib/tipi/version.rb
CHANGED
data/lib/tipi/websocket.rb
CHANGED
@@ -20,12 +20,12 @@ module Tipi
|
|
20
20
|
@version = headers['sec-websocket-version'].to_i
|
21
21
|
@reader = ::WebSocket::Frame::Incoming::Server.new(version: @version)
|
22
22
|
end
|
23
|
-
|
23
|
+
|
24
24
|
def recv
|
25
25
|
if (msg = @reader.next)
|
26
26
|
return msg.to_s
|
27
27
|
end
|
28
|
-
|
28
|
+
|
29
29
|
@conn.recv_loop do |data|
|
30
30
|
@reader << data
|
31
31
|
if (msg = @reader.next)
|
@@ -48,7 +48,7 @@ module Tipi
|
|
48
48
|
end
|
49
49
|
end
|
50
50
|
end
|
51
|
-
|
51
|
+
|
52
52
|
OutgoingFrame = ::WebSocket::Frame::Outgoing::Server
|
53
53
|
|
54
54
|
def send(data)
|
data/lib/tipi.rb
CHANGED
@@ -1,10 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'polyphony'
|
4
|
+
|
4
5
|
require_relative './tipi/http1_adapter'
|
5
6
|
require_relative './tipi/http2_adapter'
|
6
7
|
require_relative './tipi/configuration'
|
7
8
|
require_relative './tipi/response_extensions'
|
9
|
+
require_relative './tipi/acme'
|
10
|
+
|
8
11
|
require 'qeweney/request'
|
9
12
|
|
10
13
|
class Qeweney::Request
|
@@ -14,7 +17,7 @@ end
|
|
14
17
|
module Tipi
|
15
18
|
ALPN_PROTOCOLS = %w[h2 http/1.1].freeze
|
16
19
|
H2_PROTOCOL = 'h2'
|
17
|
-
|
20
|
+
|
18
21
|
class << self
|
19
22
|
def serve(host, port, opts = {}, &handler)
|
20
23
|
opts[:alpn_protocols] = ALPN_PROTOCOLS
|
@@ -23,7 +26,7 @@ module Tipi
|
|
23
26
|
ensure
|
24
27
|
server&.close
|
25
28
|
end
|
26
|
-
|
29
|
+
|
27
30
|
def listen(host, port, opts = {})
|
28
31
|
opts[:alpn_protocols] = ALPN_PROTOCOLS
|
29
32
|
Polyphony::Net.tcp_listen(host, port, opts).tap do |socket|
|
@@ -32,7 +35,7 @@ module Tipi
|
|
32
35
|
end
|
33
36
|
end
|
34
37
|
end
|
35
|
-
|
38
|
+
|
36
39
|
def accept_loop(server, opts, &handler)
|
37
40
|
server.accept_loop do |client|
|
38
41
|
spin { client_loop(client, opts, &handler) }
|
@@ -40,7 +43,7 @@ module Tipi
|
|
40
43
|
# disregard
|
41
44
|
end
|
42
45
|
end
|
43
|
-
|
46
|
+
|
44
47
|
def client_loop(client, opts, &handler)
|
45
48
|
client.no_delay if client.respond_to?(:no_delay)
|
46
49
|
adapter = protocol_adapter(client, opts)
|
@@ -48,7 +51,7 @@ module Tipi
|
|
48
51
|
ensure
|
49
52
|
client.close rescue nil
|
50
53
|
end
|
51
|
-
|
54
|
+
|
52
55
|
def protocol_adapter(socket, opts)
|
53
56
|
use_http2 = socket.respond_to?(:alpn_protocol) &&
|
54
57
|
socket.alpn_protocol == H2_PROTOCOL
|
data/test/coverage.rb
CHANGED
@@ -26,10 +26,10 @@ module Coverage
|
|
26
26
|
@result = {}
|
27
27
|
trace = TracePoint.new(:line) do |tp|
|
28
28
|
next if tp.path =~ /\(/
|
29
|
-
|
29
|
+
|
30
30
|
absolute = File.expand_path(tp.path)
|
31
31
|
next unless LIB_FILES.include?(absolute)# =~ /^#{LIB_DIR}/
|
32
|
-
|
32
|
+
|
33
33
|
@result[absolute] ||= relevant_lines_for_filename(absolute)
|
34
34
|
@result[absolute][tp.lineno - 1] = 1
|
35
35
|
end
|
data/test/helper.rb
CHANGED
@@ -8,32 +8,29 @@ require_relative './eg'
|
|
8
8
|
require_relative './coverage' if ENV['COVERAGE']
|
9
9
|
|
10
10
|
require 'minitest/autorun'
|
11
|
-
require 'minitest/reporters'
|
12
11
|
|
13
12
|
require 'polyphony'
|
14
13
|
|
15
14
|
::Exception.__disable_sanitized_backtrace__ = true
|
16
15
|
|
17
|
-
Minitest::Reporters.use! [
|
18
|
-
Minitest::Reporters::SpecReporter.new
|
19
|
-
]
|
20
|
-
|
21
16
|
class MiniTest::Test
|
22
17
|
def setup
|
23
|
-
#
|
24
|
-
if Fiber.current.children.size > 0
|
25
|
-
puts "Children left: #{Fiber.current.children.inspect}"
|
26
|
-
exit!
|
27
|
-
end
|
18
|
+
# trace "* setup #{self.name}"
|
28
19
|
Fiber.current.setup_main_fiber
|
29
20
|
Fiber.current.instance_variable_set(:@auto_watcher, nil)
|
21
|
+
Thread.current.backend.finalize
|
30
22
|
Thread.current.backend = Polyphony::Backend.new
|
31
23
|
sleep 0
|
32
24
|
end
|
33
25
|
|
34
26
|
def teardown
|
35
|
-
#
|
27
|
+
# trace "* teardown #{self.name}"
|
36
28
|
Fiber.current.shutdown_all_children
|
29
|
+
if Fiber.current.children.size > 0
|
30
|
+
puts "Children left after #{self.name}: #{Fiber.current.children.inspect}"
|
31
|
+
exit!
|
32
|
+
end
|
33
|
+
Fiber.current.instance_variable_set(:@auto_watcher, nil)
|
37
34
|
rescue => e
|
38
35
|
puts e
|
39
36
|
puts e.backtrace.join("\n")
|
@@ -47,4 +44,55 @@ module Kernel
|
|
47
44
|
rescue Exception => e
|
48
45
|
e
|
49
46
|
end
|
50
|
-
|
47
|
+
|
48
|
+
def trace(*args)
|
49
|
+
STDOUT.orig_write(format_trace(args))
|
50
|
+
end
|
51
|
+
|
52
|
+
def format_trace(args)
|
53
|
+
if args.first.is_a?(String)
|
54
|
+
if args.size > 1
|
55
|
+
format("%s: %p\n", args.shift, args)
|
56
|
+
else
|
57
|
+
format("%s\n", args.first)
|
58
|
+
end
|
59
|
+
else
|
60
|
+
format("%p\n", args.size == 1 ? args.first : args)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
class IO
|
66
|
+
# Creates two mockup sockets for simulating server-client communication
|
67
|
+
def self.server_client_mockup
|
68
|
+
server_in, client_out = IO.pipe
|
69
|
+
client_in, server_out = IO.pipe
|
70
|
+
|
71
|
+
server_connection = mockup_connection(server_in, server_out, client_out)
|
72
|
+
client_connection = mockup_connection(client_in, client_out, server_out)
|
73
|
+
|
74
|
+
[server_connection, client_connection]
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.mockup_connection(input, output, output2)
|
78
|
+
eg(
|
79
|
+
__parser_read_method__: ->() { :readpartial },
|
80
|
+
read: ->(*args) { input.read(*args) },
|
81
|
+
read_loop: ->(*args, &block) { input.read_loop(*args, &block) },
|
82
|
+
recv_loop: ->(*args, &block) { input.read_loop(*args, &block) },
|
83
|
+
readpartial: ->(*args) { input.readpartial(*args) },
|
84
|
+
recv: ->(*args) { input.readpartial(*args) },
|
85
|
+
'<<': ->(*args) { output.write(*args) },
|
86
|
+
write: ->(*args) { output.write(*args) },
|
87
|
+
close: -> { output.close },
|
88
|
+
eof?: -> { output2.closed? }
|
89
|
+
)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
module Minitest::Assertions
|
94
|
+
def assert_in_range exp_range, act
|
95
|
+
msg = message(msg) { "Expected #{mu_pp(act)} to be in range #{mu_pp(exp_range)}" }
|
96
|
+
assert exp_range.include?(act), msg
|
97
|
+
end
|
98
|
+
end
|
data/test/test_http_server.rb
CHANGED
@@ -4,38 +4,11 @@ require_relative 'helper'
|
|
4
4
|
require 'tipi'
|
5
5
|
|
6
6
|
class String
|
7
|
-
def
|
7
|
+
def crlf_lines
|
8
8
|
gsub "\n", "\r\n"
|
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?
|
@@ -60,7 +33,7 @@ class HTTP1ServerTest < MiniTest::Test
|
|
60
33
|
connection << "GET / HTTP/1.0\r\n\r\n"
|
61
34
|
|
62
35
|
response = connection.readpartial(8192)
|
63
|
-
expected = <<~HTTP.chomp.
|
36
|
+
expected = <<~HTTP.chomp.crlf_lines.chomp
|
64
37
|
HTTP/1.1 200
|
65
38
|
Content-Length: 13
|
66
39
|
|
@@ -78,7 +51,7 @@ class HTTP1ServerTest < MiniTest::Test
|
|
78
51
|
connection << "GET / HTTP/1.1\r\n\r\n"
|
79
52
|
|
80
53
|
response = connection.readpartial(8192)
|
81
|
-
expected = <<~HTTP.
|
54
|
+
expected = <<~HTTP.crlf_lines.chomp
|
82
55
|
HTTP/1.1 200
|
83
56
|
Content-Length: 13
|
84
57
|
|
@@ -100,7 +73,7 @@ class HTTP1ServerTest < MiniTest::Test
|
|
100
73
|
connection << "GET / HTTP/1.1\r\n\r\n"
|
101
74
|
response = connection.readpartial(8192)
|
102
75
|
assert !connection.eof?
|
103
|
-
expected = <<~HTTP.
|
76
|
+
expected = <<~HTTP.crlf_lines.chomp
|
104
77
|
HTTP/1.1 200
|
105
78
|
Content-Length: 2
|
106
79
|
|
@@ -127,7 +100,7 @@ class HTTP1ServerTest < MiniTest::Test
|
|
127
100
|
sleep 0.01
|
128
101
|
response = connection.readpartial(8192)
|
129
102
|
|
130
|
-
expected = <<~HTTP.
|
103
|
+
expected = <<~HTTP.crlf_lines.chomp
|
131
104
|
HTTP/1.1 200
|
132
105
|
Content-Length: 13
|
133
106
|
|
@@ -152,7 +125,7 @@ class HTTP1ServerTest < MiniTest::Test
|
|
152
125
|
req.finish
|
153
126
|
end
|
154
127
|
|
155
|
-
connection << <<~HTTP.
|
128
|
+
connection << <<~HTTP.crlf_lines
|
156
129
|
POST / HTTP/1.1
|
157
130
|
Transfer-Encoding: chunked
|
158
131
|
|
@@ -178,7 +151,7 @@ class HTTP1ServerTest < MiniTest::Test
|
|
178
151
|
|
179
152
|
response = connection.readpartial(8192)
|
180
153
|
|
181
|
-
expected = <<~HTTP.
|
154
|
+
expected = <<~HTTP.crlf_lines
|
182
155
|
HTTP/1.1 200
|
183
156
|
Transfer-Encoding: chunked
|
184
157
|
|
@@ -199,7 +172,7 @@ class HTTP1ServerTest < MiniTest::Test
|
|
199
172
|
upgrade: {
|
200
173
|
echo: lambda do |adapter, _headers|
|
201
174
|
conn = adapter.conn
|
202
|
-
conn << <<~HTTP.
|
175
|
+
conn << <<~HTTP.crlf_lines
|
203
176
|
HTTP/1.1 101 Switching Protocols
|
204
177
|
Upgrade: echo
|
205
178
|
Connection: Upgrade
|
@@ -219,7 +192,7 @@ class HTTP1ServerTest < MiniTest::Test
|
|
219
192
|
connection << "GET / HTTP/1.1\r\n\r\n"
|
220
193
|
response = connection.readpartial(8192)
|
221
194
|
assert !connection.eof?
|
222
|
-
expected = <<~HTTP.
|
195
|
+
expected = <<~HTTP.crlf_lines.chomp
|
223
196
|
HTTP/1.1 200
|
224
197
|
Content-Length: 2
|
225
198
|
|
@@ -227,7 +200,7 @@ class HTTP1ServerTest < MiniTest::Test
|
|
227
200
|
HTTP
|
228
201
|
assert_equal(expected, response)
|
229
202
|
|
230
|
-
connection << <<~HTTP.
|
203
|
+
connection << <<~HTTP.crlf_lines
|
231
204
|
GET / HTTP/1.1
|
232
205
|
Upgrade: echo
|
233
206
|
Connection: upgrade
|
@@ -237,7 +210,7 @@ class HTTP1ServerTest < MiniTest::Test
|
|
237
210
|
snooze
|
238
211
|
response = connection.readpartial(8192)
|
239
212
|
assert !connection.eof?
|
240
|
-
expected = <<~HTTP.
|
213
|
+
expected = <<~HTTP.crlf_lines
|
241
214
|
HTTP/1.1 101 Switching Protocols
|
242
215
|
Upgrade: echo
|
243
216
|
Connection: Upgrade
|
@@ -255,7 +228,7 @@ class HTTP1ServerTest < MiniTest::Test
|
|
255
228
|
|
256
229
|
connection.close
|
257
230
|
assert !done
|
258
|
-
|
231
|
+
|
259
232
|
sleep 0.01
|
260
233
|
assert done
|
261
234
|
end
|
@@ -278,7 +251,7 @@ class HTTP1ServerTest < MiniTest::Test
|
|
278
251
|
count = 0
|
279
252
|
|
280
253
|
connection << "GET / HTTP/1.1\r\n\r\n"
|
281
|
-
|
254
|
+
|
282
255
|
while (data = connection.read(chunk_size))
|
283
256
|
response << data
|
284
257
|
count += 1
|
@@ -286,7 +259,7 @@ class HTTP1ServerTest < MiniTest::Test
|
|
286
259
|
end
|
287
260
|
|
288
261
|
chunks = "#{chunk_size.to_s(16)}\n#{'*' * chunk_size}\n" * chunk_count
|
289
|
-
expected = <<~HTTP.
|
262
|
+
expected = <<~HTTP.crlf_lines
|
290
263
|
HTTP/1.1 200
|
291
264
|
Transfer-Encoding: chunked
|
292
265
|
|
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,8 +38,8 @@ 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', '
|
69
|
-
assert_equal '
|
41
|
+
assert_equal ['1', '2', '3'], req.headers['hi']
|
42
|
+
assert_equal 'GET', req.headers[':method']
|
70
43
|
assert_equal '/titi', req.headers[':path']
|
71
44
|
end
|
72
45
|
|
data/tipi.gemspec
CHANGED
@@ -19,21 +19,25 @@ Gem::Specification.new do |s|
|
|
19
19
|
|
20
20
|
s.executables = ['tipi']
|
21
21
|
|
22
|
-
s.add_runtime_dependency 'polyphony', '~>0.
|
23
|
-
s.add_runtime_dependency '
|
24
|
-
|
25
|
-
s.add_runtime_dependency '
|
26
|
-
s.add_runtime_dependency '
|
22
|
+
s.add_runtime_dependency 'polyphony', '~>0.71'
|
23
|
+
s.add_runtime_dependency 'ever', '~>0.1'
|
24
|
+
s.add_runtime_dependency 'qeweney', '~>0.14'
|
25
|
+
s.add_runtime_dependency 'extralite', '~>1.2'
|
26
|
+
s.add_runtime_dependency 'h1p', '~>0.2'
|
27
|
+
|
28
|
+
s.add_runtime_dependency 'http-2', '~>0.11'
|
27
29
|
s.add_runtime_dependency 'rack', '>=2.0.8', '<2.3.0'
|
28
30
|
s.add_runtime_dependency 'websocket', '~>1.2.8'
|
29
31
|
s.add_runtime_dependency 'acme-client', '~>2.0.8'
|
32
|
+
s.add_runtime_dependency 'localhost', '~>1.1.4'
|
30
33
|
|
31
34
|
# for digital fabric
|
32
35
|
s.add_runtime_dependency 'msgpack', '~>1.4.2'
|
33
36
|
|
34
|
-
s.add_development_dependency 'rake', '~>
|
35
|
-
s.add_development_dependency 'localhost', '~>1.1.4'
|
37
|
+
s.add_development_dependency 'rake', '~>13.0.6'
|
36
38
|
s.add_development_dependency 'minitest', '~>5.11.3'
|
37
|
-
s.add_development_dependency 'minitest-reporters', '~>1.4.2'
|
38
39
|
s.add_development_dependency 'simplecov', '~>0.17.1'
|
40
|
+
s.add_development_dependency 'memory_profiler', '~>1.0.0'
|
41
|
+
|
42
|
+
s.add_development_dependency 'cuba', '~>3.9.3'
|
39
43
|
end
|