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