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