yahns 1.12.3 → 1.12.4
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/Documentation/yahns-rackup.pod +10 -0
- data/GIT-VERSION-GEN +1 -1
- data/examples/yahns_rack_basic.conf.rb +6 -0
- data/extras/exec_cgi.rb +8 -0
- data/lib/yahns/proxy_http_response.rb +23 -17
- data/lib/yahns/proxy_pass.rb +3 -2
- data/lib/yahns/wbuf_common.rb +1 -0
- data/test/test_proxy_pass.rb +15 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9dc252e71729c27f38d5ef8bb6f82274d80408e5
|
4
|
+
data.tar.gz: ba8aa4cbf09b75919f065ba64924f1f6e76603d8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d768bd39c63353df5d086f6a5c1dfcb7c5e4725b4198ac2bf273a2c43865b3f804a1c4cd0d029bc8b95dc0eb4ecde6803e49a56eeba936df50da11a5458896ed
|
7
|
+
data.tar.gz: e0a962d0514d0c0fd19ab83b3109c0b75ed07e551900c87307c0fba8300b346b1bedd9bb84036ede20ce0bc55fff509c1306e53f5c0074e0aa2e0e58ca8da10b
|
@@ -159,6 +159,16 @@ The RACK_ENV variable is set by the aforementioned -E switch.
|
|
159
159
|
If RACK_ENV is already set, it will be used unless -E is used.
|
160
160
|
See rackup documentation for more details.
|
161
161
|
|
162
|
+
=head1 CAVEATS
|
163
|
+
|
164
|
+
yahns is strict about buggy, non-compliant Rack applications.
|
165
|
+
Some existing servers work fine without "Content-Length" or
|
166
|
+
"Transfer-Encoding: chunked" response headers enforced by Rack::Lint.
|
167
|
+
Forgetting these headers with yahns causes clients to stall as they
|
168
|
+
assume more data is coming. Loading the Rack::ContentLength and/or
|
169
|
+
Rack::Chunked middlewares will set the necessary response headers
|
170
|
+
and fix your app.
|
171
|
+
|
162
172
|
=head1 CONTACT
|
163
173
|
|
164
174
|
All feedback welcome via plain-text mail to L<mailto:yahns-public@yhbt.net>
|
data/GIT-VERSION-GEN
CHANGED
@@ -30,6 +30,12 @@
|
|
30
30
|
worker_threads 50
|
31
31
|
end
|
32
32
|
|
33
|
+
# note: Rack requires responses set "Content-Length" or use
|
34
|
+
# "Transfer-Encoding: chunked". Some Rack servers tolerate
|
35
|
+
# the lack of these, yahns does not. Thus you should load
|
36
|
+
# Rack::Chunked and/or Rack::ContentLength middleware in your
|
37
|
+
# config.ru to ensure clients know when your application
|
38
|
+
# responses terminate.
|
33
39
|
app(:rack, "config.ru", preload: false) do
|
34
40
|
listen 80
|
35
41
|
|
data/extras/exec_cgi.rb
CHANGED
@@ -12,6 +12,14 @@
|
|
12
12
|
# (delfater: ensure that parent body is always closed)
|
13
13
|
# Otherwise you will get zombies from HEAD requests which accept compressed
|
14
14
|
# responses.
|
15
|
+
#
|
16
|
+
# Usage in config.ru using cgit as an example:
|
17
|
+
#
|
18
|
+
# use Rack::Chunked
|
19
|
+
# # other Rack middlewares can go here...
|
20
|
+
#
|
21
|
+
# run ExecCgi.new('/path/to/cgit.cgi') # cgit: https://git.zx2c4.com/cgit/
|
22
|
+
#
|
15
23
|
class ExecCgi
|
16
24
|
class MyIO < Kgio::Pipe
|
17
25
|
attr_writer :my_pid
|
@@ -43,7 +43,12 @@ def proxy_err_response(code, req_res, exc, wbuf)
|
|
43
43
|
Rack::Utils::HTTP_STATUS_CODES[code]}\r\n\r\n") rescue nil
|
44
44
|
|
45
45
|
shutdown rescue nil
|
46
|
-
|
46
|
+
@input = @input.close if @input
|
47
|
+
|
48
|
+
# this is safe ONLY because we are in an :ignore state after
|
49
|
+
# Fdmap#forget when we got hijacked:
|
50
|
+
close
|
51
|
+
|
47
52
|
nil # signal close of req_res from yahns_step in yahns/proxy_pass.rb
|
48
53
|
ensure
|
49
54
|
wbuf.wbuf_abort if wbuf
|
@@ -56,6 +61,7 @@ def wait_on_upstream(req_res, alive, wbuf)
|
|
56
61
|
:wait_readable # self remains in :ignore, wait on upstream
|
57
62
|
end
|
58
63
|
|
64
|
+
# start streaming the response once upstream is done sending headers to us.
|
59
65
|
# returns :wait_readable if we need to read more from req_res
|
60
66
|
# returns :ignore if we yield control to the client(self)
|
61
67
|
# returns nil if completely done
|
@@ -101,7 +107,6 @@ def proxy_response_start(res, tip, kcar, req_res)
|
|
101
107
|
# but the backend does not terminate properly
|
102
108
|
if alive && ! term && (env['HTTP_VERSION'] == 'HTTP/1.1'.freeze)
|
103
109
|
res << "Transfer-Encoding: chunked\r\n".freeze
|
104
|
-
alive = true
|
105
110
|
end
|
106
111
|
res << (alive ? "Connection: keep-alive\r\n\r\n".freeze
|
107
112
|
: "Connection: close\r\n\r\n".freeze)
|
@@ -168,6 +173,7 @@ def proxy_response_start(res, tip, kcar, req_res)
|
|
168
173
|
tmp = chunk_out(tmp) if alive
|
169
174
|
wbuf = proxy_write(wbuf, tmp, alive)
|
170
175
|
when nil
|
176
|
+
wbuf = proxy_write(wbuf, "0\r\n\r\n".freeze, true) if alive
|
171
177
|
req_res.shutdown
|
172
178
|
break
|
173
179
|
when :wait_readable
|
@@ -177,16 +183,15 @@ def proxy_response_start(res, tip, kcar, req_res)
|
|
177
183
|
end
|
178
184
|
end
|
179
185
|
|
180
|
-
|
181
|
-
|
182
|
-
proxy_busy_mod_blocked(wbuf, wbuf.busy)
|
186
|
+
# all done reading response from upstream, req_res will be discarded
|
187
|
+
# when we return nil:
|
188
|
+
wbuf ? proxy_busy_mod_blocked(wbuf, wbuf.busy) : proxy_busy_mod_done(alive)
|
183
189
|
rescue => e
|
184
190
|
proxy_err_response(502, req_res, e, wbuf)
|
185
191
|
end
|
186
192
|
|
187
193
|
def proxy_response_finish(kcar, wbuf, req_res)
|
188
194
|
rbuf = Thread.current[:yahns_rbuf]
|
189
|
-
alive = wbuf.wbuf_persist
|
190
195
|
if len = kcar.body_bytes_left # known Content-Length
|
191
196
|
|
192
197
|
case tmp = req_res.kgio_tryread(0x2000, rbuf)
|
@@ -232,6 +237,7 @@ def proxy_response_finish(kcar, wbuf, req_res)
|
|
232
237
|
|
233
238
|
else # no Content-Length or Transfer-Encoding: chunked, wait on EOF!
|
234
239
|
|
240
|
+
alive = wbuf.wbuf_persist
|
235
241
|
case tmp = req_res.kgio_tryread(0x2000, rbuf)
|
236
242
|
when String
|
237
243
|
tmp = chunk_out(tmp) if alive
|
@@ -247,7 +253,7 @@ def proxy_response_finish(kcar, wbuf, req_res)
|
|
247
253
|
end
|
248
254
|
|
249
255
|
busy = wbuf.busy and return proxy_busy_mod_blocked(wbuf, busy)
|
250
|
-
proxy_busy_mod_done(
|
256
|
+
proxy_busy_mod_done(wbuf.wbuf_persist) # returns nil to close req_res
|
251
257
|
end
|
252
258
|
|
253
259
|
def proxy_wait_next(qflags)
|
@@ -288,23 +294,23 @@ def proxy_busy_mod_done(alive)
|
|
288
294
|
when :close then close
|
289
295
|
end
|
290
296
|
|
291
|
-
nil # close
|
297
|
+
nil # signal close for ReqRes#yahns_step
|
292
298
|
end
|
293
299
|
|
294
300
|
def proxy_busy_mod_blocked(wbuf, busy)
|
295
|
-
q = Thread.current[:yahns_queue]
|
296
301
|
# we are completely done reading and buffering the upstream response,
|
297
302
|
# but have not completely written the response to the client,
|
298
303
|
# yield control to the client socket:
|
299
304
|
@state = wbuf
|
300
|
-
case busy
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
# no touching self after
|
307
|
-
|
305
|
+
proxy_wait_next(case busy
|
306
|
+
when :wait_readable then Yahns::Queue::QEV_RD
|
307
|
+
when :wait_writable then Yahns::Queue::QEV_WR
|
308
|
+
else
|
309
|
+
abort "BUG: invalid wbuf.busy: #{busy.inspect}"
|
310
|
+
end)
|
311
|
+
# no touching self after proxy_wait_next, we may be running
|
312
|
+
# HttpClient#yahns_step in a different thread at this point
|
313
|
+
nil # signal close for ReqRes#yahns_step
|
308
314
|
end
|
309
315
|
|
310
316
|
# n.b.: we can use String#size for optimized dispatch under YARV instead
|
data/lib/yahns/proxy_pass.rb
CHANGED
@@ -63,7 +63,8 @@ def yahns_step # yahns event loop entry point
|
|
63
63
|
|
64
64
|
when Yahns::WbufCommon # streaming/buffering the response body
|
65
65
|
|
66
|
-
|
66
|
+
# we assign wbuf for rescue below:
|
67
|
+
return c.proxy_response_finish(req, wbuf = resbuf, self)
|
67
68
|
|
68
69
|
end while true # case @resbuf
|
69
70
|
|
@@ -79,7 +80,7 @@ def yahns_step # yahns event loop entry point
|
|
79
80
|
when Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::EPIPE
|
80
81
|
e.set_backtrace([])
|
81
82
|
end
|
82
|
-
c.proxy_err_response(502, self, e,
|
83
|
+
c.proxy_err_response(502, self, e, wbuf)
|
83
84
|
end
|
84
85
|
|
85
86
|
# returns :wait_readable if complete, :wait_writable if not
|
data/lib/yahns/wbuf_common.rb
CHANGED
data/test/test_proxy_pass.rb
CHANGED
@@ -586,6 +586,21 @@ def check_eof_body(host, port)
|
|
586
586
|
assert_match %r{\AHTTP/1\.1 200 OK\r\n}, res
|
587
587
|
assert_match %r{\r\n\r\neof-body-slow\z}, res
|
588
588
|
s.close
|
589
|
+
|
590
|
+
# we auto-chunk on 1.1 requests and 1.0 backends
|
591
|
+
%w(eof-body-slow eof-body-fast).each do |x|
|
592
|
+
s = TCPSocket.new(host, port)
|
593
|
+
s.write("GET /#{x} HTTP/1.1\r\nHost: example.com\r\n\r\n")
|
594
|
+
res = ''.dup
|
595
|
+
res << s.readpartial(512) until res =~ /0\r\n\r\n\z/
|
596
|
+
s.close
|
597
|
+
head, body = res.split("\r\n\r\n", 2)
|
598
|
+
head = head.split("\r\n")
|
599
|
+
assert_equal 'HTTP/1.1 200 OK', head[0]
|
600
|
+
assert head.include?('Connection: keep-alive')
|
601
|
+
assert head.include?('Transfer-Encoding: chunked')
|
602
|
+
assert_match %r{\Ad\r\n#{x}\r\n0\r\n\r\n\z}, body
|
603
|
+
end
|
589
604
|
end
|
590
605
|
end
|
591
606
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: yahns
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.12.
|
4
|
+
version: 1.12.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- yahns hackers
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-05-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: kgio
|