tipi 0.38 → 0.39
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/workflows/test.yml +1 -1
- data/.gitignore +1 -0
- data/CHANGELOG.md +9 -0
- data/Gemfile.lock +5 -5
- data/TODO.md +77 -1
- data/df/sample_agent.rb +1 -1
- data/df/server.rb +46 -4
- data/examples/http_server.rb +1 -1
- data/examples/http_server_forked.rb +2 -0
- data/examples/http_server_throttled.rb +3 -2
- data/examples/https_server.rb +3 -4
- data/examples/https_wss_server.rb +2 -1
- data/examples/rack_server.rb +5 -0
- data/examples/rack_server_https.rb +1 -1
- data/examples/rack_server_https_forked.rb +4 -3
- data/examples/routing_server.rb +5 -4
- data/lib/tipi/digital_fabric/agent.rb +16 -13
- data/lib/tipi/digital_fabric/agent_proxy.rb +79 -32
- data/lib/tipi/digital_fabric/protocol.rb +71 -14
- data/lib/tipi/digital_fabric/request_adapter.rb +7 -7
- data/lib/tipi/digital_fabric/service.rb +9 -7
- data/lib/tipi/http1_adapter.rb +35 -26
- data/lib/tipi/http2_adapter.rb +35 -4
- data/lib/tipi/http2_stream.rb +63 -20
- data/lib/tipi/version.rb +1 -1
- data/tipi.gemspec +2 -2
- metadata +7 -7
data/lib/tipi/http2_adapter.rb
CHANGED
@@ -16,7 +16,9 @@ module Tipi
|
|
16
16
|
@opts = opts
|
17
17
|
@upgrade_headers = upgrade_headers
|
18
18
|
@first = true
|
19
|
-
|
19
|
+
@rx = (upgrade_headers && upgrade_headers[':rx']) || 0
|
20
|
+
@tx = (upgrade_headers && upgrade_headers[':tx']) || 0
|
21
|
+
|
20
22
|
@interface = ::HTTP2::Server.new
|
21
23
|
@connection_fiber = Fiber.current
|
22
24
|
@interface.on(:frame, &method(:send_frame))
|
@@ -24,6 +26,9 @@ module Tipi
|
|
24
26
|
end
|
25
27
|
|
26
28
|
def send_frame(data)
|
29
|
+
if @transfer_count_request
|
30
|
+
@transfer_count_request.tx_incr(data.bytesize)
|
31
|
+
end
|
27
32
|
@conn << data
|
28
33
|
rescue Exception => e
|
29
34
|
@connection_fiber.transfer e
|
@@ -38,6 +43,7 @@ module Tipi
|
|
38
43
|
|
39
44
|
def upgrade
|
40
45
|
@conn << UPGRADE_MESSAGE
|
46
|
+
@tx += UPGRADE_MESSAGE.bytesize
|
41
47
|
settings = @upgrade_headers['http2-settings']
|
42
48
|
Fiber.current.schedule(nil)
|
43
49
|
@interface.upgrade(settings, @upgrade_headers, '')
|
@@ -49,16 +55,31 @@ module Tipi
|
|
49
55
|
def each(&block)
|
50
56
|
@interface.on(:stream) { |stream| start_stream(stream, &block) }
|
51
57
|
upgrade if @upgrade_headers
|
52
|
-
|
53
|
-
@conn.recv_loop
|
58
|
+
|
59
|
+
@conn.recv_loop do |data|
|
60
|
+
@rx += data.bytesize
|
61
|
+
@interface << data
|
62
|
+
end
|
54
63
|
rescue SystemCallError, IOError
|
55
64
|
# ignore
|
56
65
|
ensure
|
57
66
|
finalize_client_loop
|
58
67
|
end
|
68
|
+
|
69
|
+
def get_rx_count
|
70
|
+
count = @rx
|
71
|
+
@rx = 0
|
72
|
+
count
|
73
|
+
end
|
74
|
+
|
75
|
+
def get_tx_count
|
76
|
+
count = @tx
|
77
|
+
@tx = 0
|
78
|
+
count
|
79
|
+
end
|
59
80
|
|
60
81
|
def start_stream(stream, &block)
|
61
|
-
stream = HTTP2StreamHandler.new(stream, @conn, @first, &block)
|
82
|
+
stream = HTTP2StreamHandler.new(self, stream, @conn, @first, &block)
|
62
83
|
@first = nil if @first
|
63
84
|
@streams[stream] = true
|
64
85
|
end
|
@@ -72,5 +93,15 @@ module Tipi
|
|
72
93
|
def close
|
73
94
|
@conn.close
|
74
95
|
end
|
96
|
+
|
97
|
+
def set_request_for_transfer_count(request)
|
98
|
+
@transfer_count_request = request
|
99
|
+
end
|
100
|
+
|
101
|
+
def unset_request_for_transfer_count(request)
|
102
|
+
return unless @transfer_count_request == request
|
103
|
+
|
104
|
+
@transfer_count_request = nil
|
105
|
+
end
|
75
106
|
end
|
76
107
|
end
|
data/lib/tipi/http2_stream.rb
CHANGED
@@ -9,13 +9,15 @@ module Tipi
|
|
9
9
|
attr_accessor :__next__
|
10
10
|
attr_reader :conn
|
11
11
|
|
12
|
-
def initialize(stream, conn, first, &block)
|
12
|
+
def initialize(adapter, stream, conn, first, &block)
|
13
|
+
@adapter = adapter
|
13
14
|
@stream = stream
|
14
15
|
@conn = conn
|
15
16
|
@first = first
|
16
17
|
@connection_fiber = Fiber.current
|
17
18
|
@stream_fiber = spin { |req| handle_request(req, &block) }
|
18
|
-
|
19
|
+
Thread.current.fiber_unschedule(@stream_fiber)
|
20
|
+
|
19
21
|
# Stream callbacks occur on the connection fiber (see HTTP2Adapter#each).
|
20
22
|
# The request handler is run on a separate fiber for each stream, allowing
|
21
23
|
# concurrent handling of incoming requests on the same HTTP/2 connection.
|
@@ -47,14 +49,17 @@ module Tipi
|
|
47
49
|
|
48
50
|
def on_headers(headers)
|
49
51
|
@request = Qeweney::Request.new(headers.to_h, self)
|
52
|
+
@request.rx_incr(@adapter.get_rx_count)
|
53
|
+
@request.tx_incr(@adapter.get_tx_count)
|
50
54
|
if @first
|
51
55
|
@request.headers[':first'] = true
|
52
56
|
@first = false
|
53
57
|
end
|
54
58
|
@stream_fiber.schedule @request
|
55
59
|
end
|
56
|
-
|
60
|
+
|
57
61
|
def on_data(data)
|
62
|
+
data = data.to_s # chunks might be wrapped in a HTTP2::Buffer
|
58
63
|
if @waiting_for_body_chunk
|
59
64
|
@waiting_for_body_chunk = nil
|
60
65
|
@stream_fiber.schedule data
|
@@ -78,62 +83,100 @@ module Tipi
|
|
78
83
|
def protocol
|
79
84
|
'h2'
|
80
85
|
end
|
86
|
+
|
87
|
+
def with_transfer_count(request)
|
88
|
+
@adapter.set_request_for_transfer_count(request)
|
89
|
+
yield
|
90
|
+
ensure
|
91
|
+
@adapter.unset_request_for_transfer_count(request)
|
92
|
+
end
|
81
93
|
|
82
|
-
def get_body_chunk
|
94
|
+
def get_body_chunk(request)
|
83
95
|
# called in the context of the stream fiber
|
84
96
|
return nil if @request.complete?
|
85
97
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
98
|
+
with_transfer_count(request) do
|
99
|
+
@waiting_for_body_chunk = true
|
100
|
+
# the chunk (or an exception) will be returned once the stream fiber is
|
101
|
+
# resumed
|
102
|
+
suspend
|
103
|
+
end
|
90
104
|
ensure
|
91
105
|
@waiting_for_body_chunk = nil
|
92
106
|
end
|
93
107
|
|
94
108
|
# Wait for request to finish
|
95
|
-
def consume_request
|
109
|
+
def consume_request(request)
|
96
110
|
return if @request.complete?
|
97
111
|
|
98
|
-
|
99
|
-
|
112
|
+
with_transfer_count(request) do
|
113
|
+
@waiting_for_half_close = true
|
114
|
+
suspend
|
115
|
+
end
|
100
116
|
ensure
|
101
117
|
@waiting_for_half_close = nil
|
102
118
|
end
|
103
119
|
|
104
120
|
# response API
|
105
|
-
def respond(chunk, headers)
|
121
|
+
def respond(request, chunk, headers)
|
106
122
|
headers[':status'] ||= Qeweney::Status::OK
|
107
|
-
|
108
|
-
|
123
|
+
headers[':status'] = headers[':status'].to_s
|
124
|
+
with_transfer_count(request) do
|
125
|
+
@stream.headers(transform_headers(headers))
|
126
|
+
@stream.data(chunk || '')
|
127
|
+
end
|
109
128
|
@headers_sent = true
|
129
|
+
rescue HTTP2::Error::StreamClosed
|
130
|
+
# ignore
|
131
|
+
end
|
132
|
+
|
133
|
+
def transform_headers(headers)
|
134
|
+
headers.each_with_object([]) do |(k, v), a|
|
135
|
+
if v.is_a?(Array)
|
136
|
+
v.each { |vv| a << [k, vv.to_s] }
|
137
|
+
else
|
138
|
+
a << [k, v.to_s]
|
139
|
+
end
|
140
|
+
end
|
110
141
|
end
|
111
142
|
|
112
|
-
def send_headers(headers, empty_response
|
143
|
+
def send_headers(request, headers, empty_response: false)
|
113
144
|
return if @headers_sent
|
114
145
|
|
115
146
|
headers[':status'] ||= (empty_response ? Qeweney::Status::NO_CONTENT : Qeweney::Status::OK).to_s
|
116
|
-
|
147
|
+
with_transfer_count(request) do
|
148
|
+
@stream.headers(transform_headers(headers), end_stream: false)
|
149
|
+
end
|
117
150
|
@headers_sent = true
|
151
|
+
rescue HTTP2::Error::StreamClosed
|
152
|
+
# ignore
|
118
153
|
end
|
119
154
|
|
120
|
-
def send_chunk(chunk, done: false)
|
155
|
+
def send_chunk(request, chunk, done: false)
|
121
156
|
send_headers({}, false) unless @headers_sent
|
122
157
|
|
123
158
|
if chunk
|
124
|
-
|
159
|
+
with_transfer_count(request) do
|
160
|
+
@stream.data(chunk, end_stream: done)
|
161
|
+
end
|
125
162
|
elsif done
|
126
163
|
@stream.close
|
127
164
|
end
|
165
|
+
rescue HTTP2::Error::StreamClosed
|
166
|
+
# ignore
|
128
167
|
end
|
129
168
|
|
130
|
-
def finish
|
169
|
+
def finish(request)
|
131
170
|
if @headers_sent
|
132
171
|
@stream.close
|
133
172
|
else
|
134
173
|
headers[':status'] ||= Qeweney::Status::NO_CONTENT
|
135
|
-
|
174
|
+
with_transfer_count(request) do
|
175
|
+
@stream.headers(transform_headers(headers), end_stream: true)
|
176
|
+
end
|
136
177
|
end
|
178
|
+
rescue HTTP2::Error::StreamClosed
|
179
|
+
# ignore
|
137
180
|
end
|
138
181
|
|
139
182
|
def stop
|
data/lib/tipi/version.rb
CHANGED
data/tipi.gemspec
CHANGED
@@ -19,8 +19,8 @@ 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 'qeweney', '~>0.
|
22
|
+
s.add_runtime_dependency 'polyphony', '~>0.55.0'
|
23
|
+
s.add_runtime_dependency 'qeweney', '~>0.9.1'
|
24
24
|
|
25
25
|
s.add_runtime_dependency 'http_parser.rb', '~>0.6.0'
|
26
26
|
s.add_runtime_dependency 'http-2', '~>0.10.0'
|
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.
|
4
|
+
version: '0.39'
|
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-
|
11
|
+
date: 2021-06-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: polyphony
|
@@ -16,28 +16,28 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: 0.
|
19
|
+
version: 0.55.0
|
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.
|
26
|
+
version: 0.55.0
|
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:
|
33
|
+
version: 0.9.1
|
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:
|
40
|
+
version: 0.9.1
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: http_parser.rb
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -292,7 +292,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
292
292
|
- !ruby/object:Gem::Version
|
293
293
|
version: '0'
|
294
294
|
requirements: []
|
295
|
-
rubygems_version: 3.
|
295
|
+
rubygems_version: 3.1.4
|
296
296
|
signing_key:
|
297
297
|
specification_version: 4
|
298
298
|
summary: Tipi - the All-in-one Web Server for Ruby Apps
|