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 +4 -4
- data/.github/workflows/test.yml +4 -0
- data/.gitignore +3 -1
- data/CHANGELOG.md +11 -0
- data/Gemfile +5 -1
- data/Gemfile.lock +26 -12
- data/benchmarks/bm_http1_parser.rb +61 -0
- data/bin/benchmark +37 -0
- data/bin/h1pd +6 -0
- data/bin/tipi +3 -21
- data/df/server.rb +1 -1
- data/df/server_utils.rb +49 -44
- data/examples/full_service.rb +13 -0
- data/examples/http1_parser.rb +10 -8
- data/examples/http_server.rb +4 -1
- data/examples/https_server.rb +3 -0
- data/examples/servername_cb.rb +37 -0
- data/ext/tipi/extconf.rb +3 -2
- data/ext/tipi/http1_parser.c +478 -189
- data/lib/tipi.rb +84 -3
- data/lib/tipi/acme.rb +308 -0
- data/lib/tipi/cli.rb +30 -0
- data/lib/tipi/digital_fabric/agent.rb +2 -2
- data/lib/tipi/digital_fabric/agent_proxy.rb +4 -3
- data/lib/tipi/digital_fabric/executive.rb +6 -2
- data/lib/tipi/digital_fabric/protocol.rb +2 -2
- data/lib/tipi/digital_fabric/request_adapter.rb +0 -4
- data/lib/tipi/digital_fabric/service.rb +5 -10
- data/lib/tipi/http1_adapter.rb +55 -100
- data/lib/tipi/http2_adapter.rb +19 -6
- data/lib/tipi/http2_stream.rb +39 -43
- data/lib/tipi/version.rb +1 -1
- data/security/http1.rb +12 -0
- data/test/helper.rb +60 -11
- data/test/test_http1_parser.rb +586 -0
- data/test/test_http_server.rb +0 -27
- data/test/test_request.rb +1 -28
- data/tipi.gemspec +6 -5
- metadata +48 -27
- data/examples/automatic_certificate.rb +0 -193
- data/lib/tipi/http1_adapter_new.rb +0 -293
@@ -172,8 +172,9 @@ module DigitalFabric
|
|
172
172
|
http_request(req)
|
173
173
|
rescue IOError, Errno::ECONNREFUSED, Errno::EPIPE
|
174
174
|
# ignore
|
175
|
-
rescue Polyphony::Terminate
|
175
|
+
rescue Polyphony::Terminate => e
|
176
176
|
req.respond(nil, { ':status' => Qeweney::Status::SERVICE_UNAVAILABLE }) if Fiber.current.graceful_shutdown?
|
177
|
+
raise e
|
177
178
|
ensure
|
178
179
|
@requests.delete(id)
|
179
180
|
@long_running_requests.delete(id)
|
@@ -187,7 +188,6 @@ module DigitalFabric
|
|
187
188
|
complete = msg[Protocol::Attribute::HttpRequest::COMPLETE]
|
188
189
|
req = Qeweney::Request.new(headers, RequestAdapter.new(self, msg))
|
189
190
|
req.buffer_body_chunk(body_chunk) if body_chunk
|
190
|
-
req.complete! if complete
|
191
191
|
req
|
192
192
|
end
|
193
193
|
|
@@ -36,7 +36,7 @@ module DigitalFabric
|
|
36
36
|
process_incoming_messages(false)
|
37
37
|
rescue GracefulShutdown
|
38
38
|
puts "Proxy got graceful shutdown, left: #{@requests.size} requests" if @requests.size > 0
|
39
|
-
process_incoming_messages(true)
|
39
|
+
move_on_after(15) { process_incoming_messages(true) }
|
40
40
|
ensure
|
41
41
|
# keep_alive_timer&.stop
|
42
42
|
unmount
|
@@ -60,7 +60,7 @@ module DigitalFabric
|
|
60
60
|
@mounted = nil
|
61
61
|
end
|
62
62
|
|
63
|
-
def
|
63
|
+
def send_shutdown
|
64
64
|
send_df_message(Protocol.shutdown)
|
65
65
|
@fiber.raise GracefulShutdown.new
|
66
66
|
end
|
@@ -144,7 +144,8 @@ module DigitalFabric
|
|
144
144
|
t0 = Time.now
|
145
145
|
t1 = nil
|
146
146
|
with_request do |id|
|
147
|
-
|
147
|
+
msg = Protocol.http_request(id, req.headers, req.next_chunk(true), req.complete?)
|
148
|
+
send_df_message(msg)
|
148
149
|
while (message = receive)
|
149
150
|
unless t1
|
150
151
|
t1 = Time.now
|
@@ -16,7 +16,7 @@ module DigitalFabric
|
|
16
16
|
@service.mount(route, self)
|
17
17
|
@current_request_count = 0
|
18
18
|
# @updater = spin_loop(:executive_updater, interval: 10) { update_service_stats }
|
19
|
-
|
19
|
+
update_service_stats
|
20
20
|
end
|
21
21
|
|
22
22
|
def current_request_count
|
@@ -33,9 +33,13 @@ module DigitalFabric
|
|
33
33
|
req.respond(message.to_json, { 'Content-Type' => 'text.json' })
|
34
34
|
when '/stream/stats'
|
35
35
|
stream_stats(req)
|
36
|
+
when '/upload'
|
37
|
+
req.respond("body: #{req.read.inspect}")
|
36
38
|
else
|
37
39
|
req.respond('Invalid path', { ':status' => Qeweney::Status::NOT_FOUND })
|
38
40
|
end
|
41
|
+
rescue => e
|
42
|
+
puts "Error: #{e.inspect}"
|
39
43
|
ensure
|
40
44
|
@current_request_count -= 1
|
41
45
|
end
|
@@ -43,7 +47,7 @@ module DigitalFabric
|
|
43
47
|
def stream_stats(req)
|
44
48
|
req.send_headers({ 'Content-Type' => 'text/event-stream' })
|
45
49
|
|
46
|
-
|
50
|
+
every(10) do
|
47
51
|
message = last_service_stats
|
48
52
|
req.send_chunk(format_sse_event(message.to_json))
|
49
53
|
end
|
@@ -102,8 +102,8 @@ module DigitalFabric
|
|
102
102
|
DF_UPGRADE_RESPONSE
|
103
103
|
end
|
104
104
|
|
105
|
-
def http_request(id,
|
106
|
-
[ HTTP_REQUEST, id,
|
105
|
+
def http_request(id, headers, buffered_chunk, complete)
|
106
|
+
[ HTTP_REQUEST, id, headers, buffered_chunk, complete ]
|
107
107
|
end
|
108
108
|
|
109
109
|
def http_response(id, body, headers, complete, transfer_count_key = nil)
|
@@ -17,10 +17,6 @@ module DigitalFabric
|
|
17
17
|
@agent.get_http_request_body(@id, 1)
|
18
18
|
end
|
19
19
|
|
20
|
-
def consume_request(request)
|
21
|
-
@agent.get_http_request_body(@id, nil)
|
22
|
-
end
|
23
|
-
|
24
20
|
def respond(request, body, headers)
|
25
21
|
@agent.send_df_message(
|
26
22
|
Protocol.http_response(@id, body, headers, true)
|
@@ -25,7 +25,7 @@ module DigitalFabric
|
|
25
25
|
@http_latency_max = 0
|
26
26
|
@last_counters = @counters.merge(stamp: Time.now.to_f - 1)
|
27
27
|
@fiber = Fiber.current
|
28
|
-
@timer = Polyphony::Timer.new('service_timer', resolution: 5)
|
28
|
+
# @timer = Polyphony::Timer.new('service_timer', resolution: 5)
|
29
29
|
end
|
30
30
|
|
31
31
|
def calculate_stats
|
@@ -81,6 +81,8 @@ module DigitalFabric
|
|
81
81
|
s = `ps -p #{pid} -o %cpu,rss`
|
82
82
|
cpu, rss = s.lines[1].chomp.strip.split(' ')
|
83
83
|
[cpu.to_f, rss.to_i]
|
84
|
+
rescue Polyphony::BaseException
|
85
|
+
raise
|
84
86
|
rescue Exception
|
85
87
|
[nil, nil]
|
86
88
|
end
|
@@ -231,13 +233,6 @@ module DigitalFabric
|
|
231
233
|
@route_keys = @routes.keys
|
232
234
|
end
|
233
235
|
|
234
|
-
def wait_for_agent(wait_list)
|
235
|
-
wait_list << Fiber.current
|
236
|
-
@timer.move_on_after(10) { suspend }
|
237
|
-
ensure
|
238
|
-
wait_list.delete(self)
|
239
|
-
end
|
240
|
-
|
241
236
|
def path_regexp(path)
|
242
237
|
/^#{path}/
|
243
238
|
end
|
@@ -245,8 +240,8 @@ module DigitalFabric
|
|
245
240
|
def graceful_shutdown
|
246
241
|
@shutdown = true
|
247
242
|
@agents.keys.each do |agent|
|
248
|
-
if agent.respond_to?(:
|
249
|
-
agent.
|
243
|
+
if agent.respond_to?(:send_shutdown)
|
244
|
+
agent.send_shutdown
|
250
245
|
else
|
251
246
|
@agents.delete(agent)
|
252
247
|
end
|
data/lib/tipi/http1_adapter.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require '
|
3
|
+
require 'tipi_ext'
|
4
4
|
require_relative './http2_adapter'
|
5
5
|
require 'qeweney/request'
|
6
6
|
|
@@ -14,41 +14,56 @@ module Tipi
|
|
14
14
|
@conn = conn
|
15
15
|
@opts = opts
|
16
16
|
@first = true
|
17
|
-
@parser = ::
|
17
|
+
@parser = Tipi::HTTP1Parser.new(@conn)
|
18
18
|
end
|
19
19
|
|
20
20
|
def each(&block)
|
21
|
-
|
22
|
-
|
21
|
+
while true
|
22
|
+
headers = @parser.parse_headers
|
23
|
+
break unless headers
|
24
|
+
|
25
|
+
# handle_request returns true if connection is not persistent or was
|
26
|
+
# upgraded
|
27
|
+
break if handle_request(headers, &block)
|
23
28
|
end
|
29
|
+
rescue Tipi::HTTP1Parser::Error
|
30
|
+
# ignore
|
24
31
|
rescue SystemCallError, IOError
|
25
32
|
# ignore
|
26
33
|
ensure
|
27
34
|
finalize_client_loop
|
28
35
|
end
|
29
36
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
37
|
+
def handle_request(headers, &block)
|
38
|
+
scheme = (proto = headers['x-forwarded-proto']) ?
|
39
|
+
proto.downcase : scheme_from_connection
|
40
|
+
headers[':scheme'] = scheme
|
41
|
+
@protocol = headers[':protocol']
|
42
|
+
if @first
|
43
|
+
headers[':first'] = true
|
44
|
+
@first = nil
|
45
|
+
end
|
46
|
+
|
47
|
+
return true if upgrade_connection(headers, &block)
|
48
|
+
|
49
|
+
request = Qeweney::Request.new(headers, self)
|
50
|
+
if !@parser.complete?
|
51
|
+
request.buffer_body_chunk(@parser.read_body_chunk(true))
|
52
|
+
end
|
53
|
+
block.call(request)
|
54
|
+
return !persistent_connection?(headers)
|
55
|
+
end
|
56
|
+
|
57
|
+
def persistent_connection?(headers)
|
58
|
+
if headers[':protocol'] == 'http/1.1'
|
59
|
+
return headers['connection'] != 'close'
|
60
|
+
else
|
61
|
+
connection = headers['connection']
|
62
|
+
return connection && connection != 'close'
|
45
63
|
end
|
46
|
-
nil
|
47
64
|
end
|
48
65
|
|
49
66
|
def finalize_client_loop
|
50
|
-
# release references to various objects
|
51
|
-
@requests_head = @requests_tail = nil
|
52
67
|
@parser = nil
|
53
68
|
@splicing_pipe = nil
|
54
69
|
@conn.shutdown if @conn.respond_to?(:shutdown) rescue nil
|
@@ -57,82 +72,20 @@ module Tipi
|
|
57
72
|
|
58
73
|
# Reads a body chunk for the current request. Transfers control to the parse
|
59
74
|
# loop, and resumes once the parse_loop has fired the on_body callback
|
60
|
-
def get_body_chunk(request)
|
61
|
-
@
|
62
|
-
@next_chunk = nil
|
63
|
-
while !@requests_tail.complete? && (data = @conn.readpartial(8192))
|
64
|
-
request.rx_incr(data.bytesize)
|
65
|
-
@parser << data
|
66
|
-
return @next_chunk if @next_chunk
|
67
|
-
|
68
|
-
snooze
|
69
|
-
end
|
70
|
-
nil
|
71
|
-
ensure
|
72
|
-
@waiting_for_body_chunk = nil
|
73
|
-
end
|
74
|
-
|
75
|
-
# Waits for the current request to complete. Transfers control to the parse
|
76
|
-
# loop, and resumes once the parse_loop has fired the on_message_complete
|
77
|
-
# callback
|
78
|
-
def consume_request(request)
|
79
|
-
request = @requests_head
|
80
|
-
@conn.recv_loop do |data|
|
81
|
-
request.rx_incr(data.bytesize)
|
82
|
-
@parser << data
|
83
|
-
return if request.complete?
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
def protocol
|
88
|
-
version = @parser.http_version
|
89
|
-
"HTTP #{version.join('.')}"
|
90
|
-
end
|
91
|
-
|
92
|
-
def on_headers_complete(headers)
|
93
|
-
headers = normalize_headers(headers)
|
94
|
-
headers[':path'] = @parser.request_url
|
95
|
-
headers[':method'] = @parser.http_method.downcase
|
96
|
-
scheme = (proto = headers['x-forwarded-proto']) ?
|
97
|
-
proto.downcase : scheme_from_connection
|
98
|
-
headers[':scheme'] = scheme
|
99
|
-
queue_request(Qeweney::Request.new(headers, self))
|
75
|
+
def get_body_chunk(request, buffered_only = false)
|
76
|
+
@parser.read_body_chunk(buffered_only)
|
100
77
|
end
|
101
78
|
|
102
|
-
def
|
103
|
-
|
104
|
-
k = k.downcase
|
105
|
-
hk = h[k]
|
106
|
-
if hk
|
107
|
-
hk = h[k] = [hk] unless hk.is_a?(Array)
|
108
|
-
v.is_a?(Array) ? hk.concat(v) : hk << v
|
109
|
-
else
|
110
|
-
h[k] = v
|
111
|
-
end
|
112
|
-
end
|
79
|
+
def get_body(request)
|
80
|
+
@parser.read_body
|
113
81
|
end
|
114
|
-
|
115
|
-
def
|
116
|
-
|
117
|
-
@requests_tail.__next__ = request
|
118
|
-
@requests_tail = request
|
119
|
-
else
|
120
|
-
@requests_head = @requests_tail = request
|
121
|
-
end
|
122
|
-
end
|
123
|
-
|
124
|
-
def on_body(chunk)
|
125
|
-
if @waiting_for_body_chunk
|
126
|
-
@next_chunk = chunk
|
127
|
-
@waiting_for_body_chunk = nil
|
128
|
-
else
|
129
|
-
@requests_tail.buffer_body_chunk(chunk)
|
130
|
-
end
|
82
|
+
|
83
|
+
def complete?(request)
|
84
|
+
@parser.complete?
|
131
85
|
end
|
132
86
|
|
133
|
-
def
|
134
|
-
@
|
135
|
-
@requests_tail.complete!(@parser.keep_alive?)
|
87
|
+
def protocol
|
88
|
+
@protocol
|
136
89
|
end
|
137
90
|
|
138
91
|
# Upgrades the connection to a different protocol, if the 'Upgrade' header is
|
@@ -170,14 +123,15 @@ module Tipi
|
|
170
123
|
end
|
171
124
|
|
172
125
|
def upgrade_with_handler(handler, headers)
|
173
|
-
@parser =
|
126
|
+
@parser = nil
|
174
127
|
handler.(self, headers)
|
175
128
|
true
|
176
129
|
end
|
177
130
|
|
178
131
|
def upgrade_to_http2(headers, &block)
|
179
|
-
|
180
|
-
|
132
|
+
headers = http2_upgraded_headers(headers)
|
133
|
+
body = @parser.read_body
|
134
|
+
HTTP2Adapter.upgrade_each(@conn, @opts, headers, body, &block)
|
181
135
|
true
|
182
136
|
end
|
183
137
|
|
@@ -210,7 +164,6 @@ module Tipi
|
|
210
164
|
# @param body [String] response body
|
211
165
|
# @param headers
|
212
166
|
def respond(request, body, headers)
|
213
|
-
consume_request(request) if @parsing
|
214
167
|
formatted_headers = format_headers(headers, body, false)
|
215
168
|
request.tx_incr(formatted_headers.bytesize + (body ? body.bytesize : 0))
|
216
169
|
if body
|
@@ -221,8 +174,6 @@ module Tipi
|
|
221
174
|
end
|
222
175
|
|
223
176
|
def respond_from_io(request, io, headers, chunk_size = 2**14)
|
224
|
-
consume_request(request) if @parsing
|
225
|
-
|
226
177
|
formatted_headers = format_headers(headers, true, true)
|
227
178
|
request.tx_incr(formatted_headers.bytesize)
|
228
179
|
|
@@ -246,10 +197,14 @@ module Tipi
|
|
246
197
|
# @param chunked [boolean] whether to use chunked transfer encoding
|
247
198
|
# @return [void]
|
248
199
|
def send_headers(request, headers, empty_response: false, chunked: true)
|
249
|
-
formatted_headers = format_headers(headers, !empty_response,
|
200
|
+
formatted_headers = format_headers(headers, !empty_response, http1_1?(request) && chunked)
|
250
201
|
request.tx_incr(formatted_headers.bytesize)
|
251
202
|
@conn.write(formatted_headers)
|
252
203
|
end
|
204
|
+
|
205
|
+
def http1_1?(request)
|
206
|
+
request.headers[':protocol'] == 'http/1.1'
|
207
|
+
end
|
253
208
|
|
254
209
|
# Sends a response body chunk. If no headers were sent, default headers are
|
255
210
|
# sent using #send_headers. if the done option is true(thy), an empty chunk
|
data/lib/tipi/http2_adapter.rb
CHANGED
@@ -3,18 +3,30 @@
|
|
3
3
|
require 'http/2'
|
4
4
|
require_relative './http2_stream'
|
5
5
|
|
6
|
+
# patch to fix bug in HTTP2::Stream
|
7
|
+
class HTTP2::Stream
|
8
|
+
def end_stream?(frame)
|
9
|
+
case frame[:type]
|
10
|
+
when :data, :headers, :continuation
|
11
|
+
frame[:flags]&.include?(:end_stream)
|
12
|
+
else false
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
6
17
|
module Tipi
|
7
18
|
# HTTP2 server adapter
|
8
19
|
class HTTP2Adapter
|
9
|
-
def self.upgrade_each(socket, opts, headers, &block)
|
10
|
-
adapter = new(socket, opts, headers)
|
20
|
+
def self.upgrade_each(socket, opts, headers, body, &block)
|
21
|
+
adapter = new(socket, opts, headers, body)
|
11
22
|
adapter.each(&block)
|
12
23
|
end
|
13
24
|
|
14
|
-
def initialize(conn, opts, upgrade_headers = nil)
|
25
|
+
def initialize(conn, opts, upgrade_headers = nil, upgrade_body = nil)
|
15
26
|
@conn = conn
|
16
27
|
@opts = opts
|
17
28
|
@upgrade_headers = upgrade_headers
|
29
|
+
@upgrade_body = upgrade_body
|
18
30
|
@first = true
|
19
31
|
@rx = (upgrade_headers && upgrade_headers[':rx']) || 0
|
20
32
|
@tx = (upgrade_headers && upgrade_headers[':tx']) || 0
|
@@ -30,6 +42,8 @@ module Tipi
|
|
30
42
|
@transfer_count_request.tx_incr(data.bytesize)
|
31
43
|
end
|
32
44
|
@conn << data
|
45
|
+
rescue Polyphony::BaseException
|
46
|
+
raise
|
33
47
|
rescue Exception => e
|
34
48
|
@connection_fiber.transfer e
|
35
49
|
end
|
@@ -45,8 +59,7 @@ module Tipi
|
|
45
59
|
@conn << UPGRADE_MESSAGE
|
46
60
|
@tx += UPGRADE_MESSAGE.bytesize
|
47
61
|
settings = @upgrade_headers['http2-settings']
|
48
|
-
|
49
|
-
@interface.upgrade(settings, @upgrade_headers, '')
|
62
|
+
@interface.upgrade(settings, @upgrade_headers, @upgrade_body || '')
|
50
63
|
ensure
|
51
64
|
@upgrade_headers = nil
|
52
65
|
end
|
@@ -60,7 +73,7 @@ module Tipi
|
|
60
73
|
@rx += data.bytesize
|
61
74
|
@interface << data
|
62
75
|
end
|
63
|
-
rescue SystemCallError, IOError
|
76
|
+
rescue SystemCallError, IOError, HTTP2::Error::Error
|
64
77
|
# ignore
|
65
78
|
ensure
|
66
79
|
finalize_client_loop
|
data/lib/tipi/http2_stream.rb
CHANGED
@@ -15,8 +15,7 @@ module Tipi
|
|
15
15
|
@conn = conn
|
16
16
|
@first = first
|
17
17
|
@connection_fiber = Fiber.current
|
18
|
-
@stream_fiber = spin {
|
19
|
-
Thread.current.fiber_unschedule(@stream_fiber)
|
18
|
+
@stream_fiber = spin { run(&block) }
|
20
19
|
|
21
20
|
# Stream callbacks occur on the connection fiber (see HTTP2Adapter#each).
|
22
21
|
# The request handler is run on a separate fiber for each stream, allowing
|
@@ -34,16 +33,16 @@ module Tipi
|
|
34
33
|
stream.on(:half_close, &method(:on_half_close))
|
35
34
|
end
|
36
35
|
|
37
|
-
def
|
36
|
+
def run(&block)
|
37
|
+
request = receive
|
38
38
|
error = nil
|
39
39
|
block.(request)
|
40
40
|
@connection_fiber.schedule
|
41
|
-
rescue Polyphony::
|
42
|
-
|
41
|
+
rescue Polyphony::BaseException
|
42
|
+
raise
|
43
43
|
rescue Exception => e
|
44
44
|
error = e
|
45
45
|
ensure
|
46
|
-
@done = true
|
47
46
|
@connection_fiber.schedule error
|
48
47
|
end
|
49
48
|
|
@@ -55,29 +54,19 @@ module Tipi
|
|
55
54
|
@request.headers[':first'] = true
|
56
55
|
@first = false
|
57
56
|
end
|
58
|
-
@stream_fiber
|
57
|
+
@stream_fiber << @request
|
59
58
|
end
|
60
59
|
|
61
60
|
def on_data(data)
|
62
61
|
data = data.to_s # chunks might be wrapped in a HTTP2::Buffer
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
else
|
67
|
-
@request.buffer_body_chunk(data)
|
68
|
-
end
|
62
|
+
|
63
|
+
(@buffered_chunks ||= []) << data
|
64
|
+
@get_body_chunk_fiber&.schedule
|
69
65
|
end
|
70
66
|
|
71
67
|
def on_half_close
|
72
|
-
|
73
|
-
|
74
|
-
@stream_fiber.schedule
|
75
|
-
elsif @waiting_for_half_close
|
76
|
-
@waiting_for_half_close = nil
|
77
|
-
@stream_fiber.schedule
|
78
|
-
else
|
79
|
-
@request.complete!
|
80
|
-
end
|
68
|
+
@get_body_chunk_fiber&.schedule
|
69
|
+
@complete = true
|
81
70
|
end
|
82
71
|
|
83
72
|
def protocol
|
@@ -90,31 +79,38 @@ module Tipi
|
|
90
79
|
ensure
|
91
80
|
@adapter.unset_request_for_transfer_count(request)
|
92
81
|
end
|
93
|
-
|
94
|
-
def get_body_chunk(request)
|
95
|
-
|
96
|
-
return
|
82
|
+
|
83
|
+
def get_body_chunk(request, buffered_only = false)
|
84
|
+
@buffered_chunks ||= []
|
85
|
+
return @buffered_chunks.shift unless @buffered_chunks.empty?
|
86
|
+
return nil if @complete
|
97
87
|
|
98
|
-
|
99
|
-
@
|
100
|
-
# the chunk (or an exception) will be returned once the stream fiber is
|
101
|
-
# resumed
|
88
|
+
begin
|
89
|
+
@get_body_chunk_fiber = Fiber.current
|
102
90
|
suspend
|
91
|
+
ensure
|
92
|
+
@get_body_chunk_fiber = nil
|
103
93
|
end
|
104
|
-
|
105
|
-
@waiting_for_body_chunk = nil
|
94
|
+
@buffered_chunks.shift
|
106
95
|
end
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
return if @
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
96
|
+
|
97
|
+
def get_body(request)
|
98
|
+
@buffered_chunks ||= []
|
99
|
+
return @buffered_chunks.join if @complete
|
100
|
+
|
101
|
+
while !@complete
|
102
|
+
begin
|
103
|
+
@get_body_chunk_fiber = Fiber.current
|
104
|
+
suspend
|
105
|
+
ensure
|
106
|
+
@get_body_chunk_fiber = nil
|
107
|
+
end
|
115
108
|
end
|
116
|
-
|
117
|
-
|
109
|
+
@buffered_chunks.join
|
110
|
+
end
|
111
|
+
|
112
|
+
def complete?(request)
|
113
|
+
@complete
|
118
114
|
end
|
119
115
|
|
120
116
|
# response API
|
@@ -194,7 +190,7 @@ module Tipi
|
|
194
190
|
end
|
195
191
|
|
196
192
|
def stop
|
197
|
-
return if @
|
193
|
+
return if @complete
|
198
194
|
|
199
195
|
@stream.close
|
200
196
|
@stream_fiber.schedule(Polyphony::MoveOn.new)
|