yahns 1.12.5 → 1.13.0
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 +0 -10
- data/Documentation/yahns_config.pod +3 -0
- data/GIT-VERSION-FILE +1 -1
- data/GIT-VERSION-GEN +1 -1
- data/NEWS +80 -0
- data/examples/init.sh +34 -9
- data/examples/logrotate.conf +5 -0
- data/examples/yahns.socket +17 -0
- data/examples/yahns@.service +50 -0
- data/examples/yahns_rack_basic.conf.rb +0 -6
- data/extras/autoindex.rb +3 -2
- data/extras/exec_cgi.rb +1 -0
- data/extras/try_gzip_static.rb +19 -5
- data/lib/yahns/chunk_body.rb +27 -0
- data/lib/yahns/fdmap.rb +7 -4
- data/lib/yahns/http_client.rb +39 -10
- data/lib/yahns/http_response.rb +41 -22
- data/lib/yahns/openssl_client.rb +7 -3
- data/lib/yahns/proxy_http_response.rb +132 -159
- data/lib/yahns/proxy_pass.rb +6 -170
- data/lib/yahns/queue_epoll.rb +1 -0
- data/lib/yahns/queue_kqueue.rb +1 -0
- data/lib/yahns/req_res.rb +164 -0
- data/lib/yahns/server.rb +2 -1
- data/lib/yahns/server_mp.rb +1 -1
- data/lib/yahns/version.rb +1 -1
- data/lib/yahns/wbuf.rb +5 -6
- data/lib/yahns/wbuf_common.rb +5 -10
- data/lib/yahns/wbuf_lite.rb +111 -0
- data/man/yahns-rackup.1 +29 -29
- data/man/yahns_config.5 +47 -35
- data/test/helper.rb +12 -0
- data/test/test_auto_chunk.rb +56 -0
- data/test/test_extras_exec_cgi.rb +1 -3
- data/test/test_extras_try_gzip_static.rb +30 -16
- data/test/test_output_buffering.rb +5 -1
- data/test/test_proxy_pass.rb +2 -2
- data/test/test_proxy_pass_no_buffering.rb +170 -0
- data/test/test_reopen_logs.rb +5 -1
- data/test/test_response.rb +42 -0
- data/test/test_server.rb +35 -0
- data/test/test_ssl.rb +0 -6
- data/test/test_tmpio.rb +4 -0
- data/test/test_wbuf.rb +11 -4
- metadata +10 -4
- data/lib/yahns/sendfile_compat.rb +0 -24
data/man/yahns_config.5
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
.\" Automatically generated by Pod::Man 2.
|
1
|
+
.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.28)
|
2
2
|
.\"
|
3
3
|
.\" Standard preamble:
|
4
4
|
.\" ========================================================================
|
@@ -38,6 +38,8 @@
|
|
38
38
|
. ds PI \(*p
|
39
39
|
. ds L" ``
|
40
40
|
. ds R" ''
|
41
|
+
. ds C`
|
42
|
+
. ds C'
|
41
43
|
'br\}
|
42
44
|
.\"
|
43
45
|
.\" Escape single quotes in literal strings from groff's Unicode transform.
|
@@ -48,17 +50,24 @@
|
|
48
50
|
.\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index
|
49
51
|
.\" entries marked with X<> in POD. Of course, you'll have to process the
|
50
52
|
.\" output yourself in some meaningful fashion.
|
51
|
-
|
52
|
-
.
|
53
|
-
.
|
53
|
+
.\"
|
54
|
+
.\" Avoid warning from groff about undefined register 'F'.
|
55
|
+
.de IX
|
54
56
|
..
|
55
|
-
.
|
56
|
-
.
|
57
|
-
|
58
|
-
.
|
59
|
-
.
|
57
|
+
.nr rF 0
|
58
|
+
.if \n(.g .if rF .nr rF 1
|
59
|
+
.if (\n(rF:(\n(.g==0)) \{
|
60
|
+
. if \nF \{
|
61
|
+
. de IX
|
62
|
+
. tm Index:\\$1\t\\n%\t"\\$2"
|
60
63
|
..
|
64
|
+
. if !\nF==2 \{
|
65
|
+
. nr % 0
|
66
|
+
. nr F 2
|
67
|
+
. \}
|
68
|
+
. \}
|
61
69
|
.\}
|
70
|
+
.rr rF
|
62
71
|
.\"
|
63
72
|
.\" Accent mark definitions (@(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2).
|
64
73
|
.\" Fear. Run. Save yourself. No user-serviceable parts.
|
@@ -124,7 +133,7 @@
|
|
124
133
|
.\" ========================================================================
|
125
134
|
.\"
|
126
135
|
.IX Title "YAHNS_CONFIG 5"
|
127
|
-
.TH YAHNS_CONFIG 5 "1994-10-02" "yahns 1.12.
|
136
|
+
.TH YAHNS_CONFIG 5 "1994-10-02" "yahns 1.12.5.48.g013d" "yahns user manual"
|
128
137
|
.\" For nroff, turn off justification. Always turn off hyphenation; it makes
|
129
138
|
.\" way too many mistakes in technical documents.
|
130
139
|
.if n .ad l
|
@@ -152,7 +161,7 @@ An app with the same :TYPE and \s-1APP_ARGUMENTS\s0 may be defined multiple
|
|
152
161
|
times with different &BLOCK contents (with different listeners and
|
153
162
|
buffering settings).
|
154
163
|
.Sp
|
155
|
-
See the \*(L"APP-LEVEL \s-1DIRECTIVES
|
164
|
+
See the \*(L"APP-LEVEL \s-1DIRECTIVES\*(R"\s0 section for details on what goes into
|
156
165
|
the &BLOCK.
|
157
166
|
.Sp
|
158
167
|
This directive may be specified multiple times for different or
|
@@ -161,7 +170,7 @@ identical applications.
|
|
161
170
|
If the \*(L"working_directory\*(R" directive is used, all app directives may
|
162
171
|
only be specified after setting \*(L"working_directory\*(R".
|
163
172
|
.Sp
|
164
|
-
For Rack \s-1HTTP\s0 applications, see \*(L"\s-1RACK
|
173
|
+
For Rack \s-1HTTP\s0 applications, see \*(L"\s-1RACK APP ARGUMENTS\*(R"\s0 for more
|
165
174
|
information.
|
166
175
|
.IP "before_exec &BLOCK" 4
|
167
176
|
.IX Item "before_exec &BLOCK"
|
@@ -227,7 +236,7 @@ Default: none
|
|
227
236
|
.IX Item "queue [NAME] &BLOCK"
|
228
237
|
As a top-level directive, this configures or defines a queue.
|
229
238
|
If no \s-1NAME\s0 is specified, a default queue (named :default) is
|
230
|
-
assumed. See the \*(L"QUEUE-LEVEL \s-1DIRECTIVES
|
239
|
+
assumed. See the \*(L"QUEUE-LEVEL \s-1DIRECTIVES\*(R"\s0 section for details.
|
231
240
|
.Sp
|
232
241
|
A &BLOCK must be given if used as a top-level directive. This
|
233
242
|
behaves slightly differently inside an app &BLOCK. This may also
|
@@ -297,8 +306,8 @@ Default: 7
|
|
297
306
|
.IX Subsection "APP-LEVEL DIRECTIVES"
|
298
307
|
.IP "atfork_prepare, atfork_parent, atfork_child" 4
|
299
308
|
.IX Item "atfork_prepare, atfork_parent, atfork_child"
|
300
|
-
These are identical to the methods defined in \s-1WORKER_PROCESSES\-LEVEL
|
301
|
-
|
309
|
+
These are identical to the methods defined in \s-1WORKER_PROCESSES\-LEVEL
|
310
|
+
DIRECTIVES,\s0 however they are available inside the app blocks for
|
302
311
|
convenience in case it is easier to organize per-app hooks.
|
303
312
|
.Sp
|
304
313
|
Default: (none)
|
@@ -315,7 +324,7 @@ This only affects clients connecting over Unix domain sockets and
|
|
315
324
|
\&\s-1TCP\s0 via loopback (127.*.*.*). It is unlikely to detect disconnects
|
316
325
|
if the client is on a remote host (even on a fast \s-1LAN\s0).
|
317
326
|
.Sp
|
318
|
-
This has no effect for (rare) \s-1HTTP/0\s0
|
327
|
+
This has no effect for (rare) \s-1HTTP/0.9\s0 clients.
|
319
328
|
.Sp
|
320
329
|
Default: false
|
321
330
|
.IP "client_body_buffer_size \s-1INTEGER\s0" 4
|
@@ -340,7 +349,7 @@ Default: 4000 bytes
|
|
340
349
|
.IP "client_max_body_size {INTEGER|nil}" 4
|
341
350
|
.IX Item "client_max_body_size {INTEGER|nil}"
|
342
351
|
This controls the maximum request body size before a client is
|
343
|
-
rejected with an \s-1HTTP\s0
|
352
|
+
rejected with an \s-1HTTP 413\s0 error.
|
344
353
|
.Sp
|
345
354
|
Setting this to nil will allow unlimited-sized inputs.
|
346
355
|
.Sp
|
@@ -367,7 +376,7 @@ Default: 15 (seconds)
|
|
367
376
|
.IP "errors {IO|PATHNAME}" 4
|
368
377
|
.IX Item "errors {IO|PATHNAME}"
|
369
378
|
For Rack applications, this controls the env[\*(L"rack.errors\*(R"]
|
370
|
-
destination. If given a \s-1PATHNAME
|
379
|
+
destination. If given a \s-1PATHNAME,\s0 it will be a writable file
|
371
380
|
opened with O_APPEND without userspace buffering, making it suitable
|
372
381
|
for concurrent appends by multiple processes and threads, as well as
|
373
382
|
making it eligible for reopening via \s-1SIGUSR1\s0 after log rotation.
|
@@ -421,7 +430,7 @@ Default: Dir.tmpdir (usually from \s-1TMPDIR\s0 env or /tmp)
|
|
421
430
|
.IX Item "listen ADDRESS [, OPTIONS]"
|
422
431
|
Adds an \s-1ADDRESS\s0 to the existing listener set. May be specified more
|
423
432
|
than once. \s-1ADDRESS\s0 may be an Integer port number for a \s-1TCP\s0 port, an
|
424
|
-
\&\*(L"\s-1IP_ADDRESS:PORT
|
433
|
+
\&\*(L"\s-1IP_ADDRESS:PORT\*(R"\s0 for \s-1TCP\s0 listeners or a pathname for \s-1UNIX\s0 domain sockets.
|
425
434
|
.Sp
|
426
435
|
.Vb 2
|
427
436
|
\& # listen to port 3000 on all TCP interfaces
|
@@ -476,7 +485,7 @@ listener is required if this is true.
|
|
476
485
|
.Sp
|
477
486
|
Enabling this option for the IPv6\-only listener and having a
|
478
487
|
separate IPv4 listener is recommended if you wish to support IPv6
|
479
|
-
on the same \s-1TCP\s0 port. Otherwise, the value of env[\*(L"\s-1REMOTE_ADDR
|
488
|
+
on the same \s-1TCP\s0 port. Otherwise, the value of env[\*(L"\s-1REMOTE_ADDR\*(R"\s0]
|
480
489
|
will appear as an ugly IPv4\-mapped\-IPv6 address for IPv4 clients
|
481
490
|
(e.g \*(L":ffff:10.0.0.1\*(R" instead of just \*(L"10.0.0.1\*(R").
|
482
491
|
.Sp
|
@@ -502,9 +511,9 @@ This enables multiple, independently-started yahns instances to
|
|
502
511
|
bind to the same port (as long as all the processes enable this).
|
503
512
|
.Sp
|
504
513
|
This option must be used when yahns first binds the listen socket.
|
505
|
-
It cannot be enabled when a socket is inherited via \s-1SIGUSR2
|
506
|
-
(but it will remain on if inherited), and it cannot be enabled
|
507
|
-
directly via \s-1SIGHUP
|
514
|
+
It cannot be enabled when a socket is inherited via \s-1SIGUSR2
|
515
|
+
\&\s0(but it will remain on if inherited), and it cannot be enabled
|
516
|
+
directly via \s-1SIGHUP.\s0
|
508
517
|
.Sp
|
509
518
|
Note: there is a chance of connections being dropped if
|
510
519
|
one of the yahns instances is stopped while using this.
|
@@ -520,7 +529,7 @@ To enable \s-1TLS\s0 connections, you must configure this yourself.
|
|
520
529
|
See documentation for OpenSSL::SSL::SSLContext
|
521
530
|
for more information:
|
522
531
|
.Sp
|
523
|
-
http://docs.ruby\-lang.org/en/trunk/OpenSSL/SSL/SSLContext.html
|
532
|
+
<http://docs.ruby\-lang.org/en/trunk/OpenSSL/SSL/SSLContext.html>
|
524
533
|
.Sp
|
525
534
|
Default: none
|
526
535
|
.Sp
|
@@ -545,6 +554,9 @@ An example which seems to work is:
|
|
545
554
|
\& # but disable client certificate verification as it is rare:
|
546
555
|
\& ssl_ctx.set_params(verify_mode: OpenSSL::SSL::VERIFY_NONE)
|
547
556
|
\&
|
557
|
+
\& # Built\-in session cache (only works if worker_processes is nil or 1)
|
558
|
+
\& ssl_ctx.session_cache_mode = OpenSSL::SSL::SSLContext::SESSION_CACHE_SERVER
|
559
|
+
\&
|
548
560
|
\& app(:rack, "/path/to/my/app/config.ru") do
|
549
561
|
\& listen 443, ssl_ctx: ssl_ctx
|
550
562
|
\& end
|
@@ -624,7 +636,7 @@ client_expire_threshold is hit and a client hits its client_timeout.
|
|
624
636
|
Default: true
|
625
637
|
.IP "user \s-1USER\s0 [,GROUP]" 4
|
626
638
|
.IX Item "user USER [,GROUP]"
|
627
|
-
Runs application process(es) as the specified \s-1USER\s0 and \s-1GROUP
|
639
|
+
Runs application process(es) as the specified \s-1USER\s0 and \s-1GROUP.\s0
|
628
640
|
.Sp
|
629
641
|
If using worker_processes, this only affects the workers and the
|
630
642
|
master stays running as the user who started it. This switch will
|
@@ -641,7 +653,7 @@ existing queue.
|
|
641
653
|
If given a &BLOCK\-only, this will create an anonymous queue for this
|
642
654
|
application context only. If given a &BLOCK, this takes the same
|
643
655
|
parameters (worker_threads and max_events) as described in
|
644
|
-
\&\*(L"QUEUE-LEVEL \s-1DIRECTIVES
|
656
|
+
\&\*(L"QUEUE-LEVEL \s-1DIRECTIVES\*(R"\s0.
|
645
657
|
.Sp
|
646
658
|
\&\s-1NAME\s0 and &BLOCK may not be combined inside an app &BLOCK.
|
647
659
|
.Sp
|
@@ -665,16 +677,16 @@ model instead of a single process.
|
|
665
677
|
.Sp
|
666
678
|
If an optional &BLOCK is given, it may be used to configure
|
667
679
|
\&\fIpthread_atfork\fR\|(3)\-style hooks.
|
668
|
-
See \*(L"\s-1WORKER_PROCESSES\-LEVEL
|
680
|
+
See \*(L"\s-1WORKER_PROCESSES\-LEVEL DIRECTIVES\*(R"\s0 for details.
|
669
681
|
.Sp
|
670
682
|
Using worker_processes is strongly recommended if your application
|
671
683
|
relies on using a \s-1SIGCHLD\s0 handler for reaping forked processes.
|
672
684
|
Without worker_processes, yahns must reserve \s-1SIGCHLD\s0 for rolling
|
673
685
|
back \s-1SIGUSR2\s0 upgrades, leading to conflicts if the appplication
|
674
|
-
expects to handle \s-1SIGCHLD
|
686
|
+
expects to handle \s-1SIGCHLD.\s0
|
675
687
|
.Sp
|
676
688
|
Default: nil (single process, no master/worker separation)
|
677
|
-
.SS "\s-1WORKER_PROCESSES\-LEVEL
|
689
|
+
.SS "\s-1WORKER_PROCESSES\-LEVEL DIRECTIVES\s0"
|
678
690
|
.IX Subsection "WORKER_PROCESSES-LEVEL DIRECTIVES"
|
679
691
|
Note: all of the atfork_* hooks described here are available inside the
|
680
692
|
\&\*(L"app\*(R" blocks, too.
|
@@ -709,11 +721,11 @@ The only supported keyword argument is:
|
|
709
721
|
.IP "preload: \s-1BOOLEAN\s0" 4
|
710
722
|
.IX Item "preload: BOOLEAN"
|
711
723
|
preload: only makes sense if worker_processes are configured.
|
712
|
-
Preloading an app allows memory to be saved under a copy-on-write \s-1GC
|
724
|
+
Preloading an app allows memory to be saved under a copy-on-write \s-1GC,\s0
|
713
725
|
but will often require atfork_* hooks to be registered when configuring
|
714
726
|
worker_processes. preload: defaults to false for maximum out-of-the-box
|
715
727
|
compatibility.
|
716
|
-
.SS "\s-1RACK
|
728
|
+
.SS "\s-1RACK APP EXAMPLE\s0"
|
717
729
|
.IX Subsection "RACK APP EXAMPLE"
|
718
730
|
.Vb 3
|
719
731
|
\& app(:rack, "/path/to/config.ru", preload: false) do
|
@@ -729,13 +741,13 @@ See the examples/ directory in the git source tree.
|
|
729
741
|
.Ve
|
730
742
|
.SH "CONTACT"
|
731
743
|
.IX Header "CONTACT"
|
732
|
-
All feedback welcome via plain-text mail to mailto:yahns\-public@yhbt.net
|
744
|
+
All feedback welcome via plain-text mail to <mailto:yahns\-public@yhbt.net>
|
733
745
|
No subscription is necessary to post to the mailing list.
|
734
|
-
List archives are available at http://yhbt.net/yahns\-public
|
746
|
+
List archives are available at <http://yhbt.net/yahns\-public/>
|
735
747
|
.SH "COPYRIGHT"
|
736
748
|
.IX Header "COPYRIGHT"
|
737
|
-
Copyright (C) 2013\-2016 all contributors mailto:yahns\-public@yhbt.net
|
738
|
-
License: \s-1GPL\-3
|
749
|
+
Copyright (C) 2013\-2016 all contributors <mailto:yahns\-public@yhbt.net>
|
750
|
+
License: \s-1GPL\-3.0+ \s0<http://www.gnu.org/licenses/gpl\-3.0.txt>
|
739
751
|
.SH "SEE ALSO"
|
740
752
|
.IX Header "SEE ALSO"
|
741
753
|
\&\fIyahns\fR\|(1)
|
data/test/helper.rb
CHANGED
@@ -147,6 +147,18 @@ class DieIfUsed
|
|
147
147
|
end
|
148
148
|
end
|
149
149
|
|
150
|
+
# tricky to test output buffering behavior across different OSes
|
151
|
+
def skip_skb_mem
|
152
|
+
return if ENV['YAHNS_TEST_FORCE']
|
153
|
+
skip "linux-only test" unless RUBY_PLATFORM =~ /linux/
|
154
|
+
[ [ '/proc/sys/net/ipv4/tcp_rmem', "4096 87380 6291456\n" ],
|
155
|
+
[ '/proc/sys/net/ipv4/tcp_wmem', "4096 16384 4194304\n" ]
|
156
|
+
].each do |file, expect|
|
157
|
+
val = IO.read(file)
|
158
|
+
val == expect or skip "#{file} had: #{val}expected: #{expect}"
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
150
162
|
require 'yahns'
|
151
163
|
|
152
164
|
# needed for parallel (MT) tests)
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# Copyright (C) 2013-2016 all contributors <yahns-public@yhbt.net>
|
2
|
+
# License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
|
3
|
+
# frozen_string_literal: true
|
4
|
+
require_relative 'server_helper'
|
5
|
+
|
6
|
+
class TestAutoChunk < Testcase
|
7
|
+
ENV["N"].to_i > 1 and parallelize_me!
|
8
|
+
include ServerHelper
|
9
|
+
alias setup server_helper_setup
|
10
|
+
alias teardown server_helper_teardown
|
11
|
+
|
12
|
+
def test_auto_head
|
13
|
+
err = @err
|
14
|
+
cfg = Yahns::Config.new
|
15
|
+
host, port = @srv.addr[3], @srv.addr[1]
|
16
|
+
cfg.instance_eval do
|
17
|
+
GTL.synchronize do
|
18
|
+
app = Rack::Builder.new do
|
19
|
+
use Rack::ContentType, "text/plain"
|
20
|
+
run(lambda do |env|
|
21
|
+
[ 200, {}, %w(a b c) ]
|
22
|
+
end)
|
23
|
+
end
|
24
|
+
app(:rack, app) { listen "#{host}:#{port}" }
|
25
|
+
end
|
26
|
+
logger(Logger.new(err.path))
|
27
|
+
end
|
28
|
+
pid = mkserver(cfg)
|
29
|
+
s = TCPSocket.new(host, port)
|
30
|
+
s.write("GET / HTTP/1.0\r\n\r\n")
|
31
|
+
assert s.wait(30), "IO wait failed"
|
32
|
+
buf = s.read
|
33
|
+
assert_match %r{\r\n\r\nabc\z}, buf
|
34
|
+
s.close
|
35
|
+
|
36
|
+
s = TCPSocket.new(host, port)
|
37
|
+
s.write("GET / HTTP/1.1\r\nHost: example.com\r\n\r\n")
|
38
|
+
buf = ''.dup
|
39
|
+
Timeout.timeout(30) do
|
40
|
+
until buf =~ /\r\n\r\n1\r\na\r\n1\r\nb\r\n1\r\nc\r\n0\r\n\r\n\z/
|
41
|
+
buf << s.readpartial(16384)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
assert_match(%r{^Transfer-Encoding: chunked\r\n}, buf)
|
45
|
+
s.close
|
46
|
+
|
47
|
+
Net::HTTP.start(host, port) do |http|
|
48
|
+
req = Net::HTTP::Get.new("/")
|
49
|
+
res = http.request(req)
|
50
|
+
assert_equal 200, res.code.to_i
|
51
|
+
assert_equal 'abc', res.body
|
52
|
+
end
|
53
|
+
ensure
|
54
|
+
quit_wait(pid)
|
55
|
+
end
|
56
|
+
end
|
@@ -169,10 +169,8 @@ class TestExtrasExecCGI < Testcase
|
|
169
169
|
assert_match %r{200 OK}, head
|
170
170
|
assert_match %r{\A\d+\n\z}, body
|
171
171
|
exec_pid = body.to_i
|
172
|
-
assert_raises(Errno::EAGAIN, IO::WaitReadable) { c.read_nonblock(666) }
|
173
172
|
poke_until_dead exec_pid
|
174
|
-
|
175
|
-
assert_raises(Errno::EAGAIN, IO::WaitReadable) { c.read_nonblock(666) }
|
173
|
+
assert_raises(EOFError) { c.read_nonblock(666) }
|
176
174
|
else
|
177
175
|
raise "BUG in test, bad rtype"
|
178
176
|
end
|
@@ -34,8 +34,22 @@ class TestExtrasTryGzipStatic < Testcase
|
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
37
|
+
Net::HTTP.start(host, port) do |http|
|
38
|
+
uri = "/COPYING/foo" + ('-' * 4096)
|
39
|
+
begin
|
40
|
+
res = http.request(Net::HTTP::Get.new(uri))
|
41
|
+
end while res.code.to_i == 414 && uri.chop!
|
42
|
+
res = http.request(Net::HTTP::Get.new("/COPYING/foo"))
|
43
|
+
assert_equal 404, res.code.to_i
|
44
|
+
lines = File.readlines(err.path)
|
45
|
+
File.truncate(err.path, 0)
|
46
|
+
assert_operator lines.size, :<, 3, lines.map! { |s| s[0,64] }.inspect
|
47
|
+
end
|
48
|
+
|
37
49
|
begin # setup
|
38
50
|
gpl = "#{tmpdir}/COPYING"
|
51
|
+
File.symlink gpl, "#{tmpdir}/COPYING.abssymlink"
|
52
|
+
File.symlink "COPYING", "#{tmpdir}/COPYING.relsymlink"
|
39
53
|
gplgz = "#{tmpdir}/COPYING.gz"
|
40
54
|
FileUtils.cp("COPYING", gpl)
|
41
55
|
_, status = Process.waitpid2(fork do
|
@@ -76,16 +90,19 @@ class TestExtrasTryGzipStatic < Testcase
|
|
76
90
|
when "HEAD" then assert_nil body
|
77
91
|
end
|
78
92
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
93
|
+
%w(COPYING COPYING.abssymlink COPYING.relsymlink).each do |path|
|
94
|
+
req = "#{m} /#{path} HTTP/1.0\r\nAccept-Encoding: gzip"
|
95
|
+
body = check.call(req) do |head|
|
96
|
+
assert_match %r{^Content-Encoding: gzip\b}, head
|
97
|
+
assert_match %r{^Content-Type: text/plain\b}, head
|
98
|
+
assert_match %r{^Content-Length: #{gz_st.size}\b}, head
|
99
|
+
end
|
100
|
+
case m
|
101
|
+
when "GET"
|
102
|
+
body =StringIO.new(body)
|
103
|
+
assert_equal GPL_TEXT, Zlib::GzipReader.new(body).read
|
104
|
+
when "HEAD" then assert_nil body
|
105
|
+
end
|
89
106
|
end
|
90
107
|
end
|
91
108
|
end
|
@@ -108,11 +125,10 @@ class TestExtrasTryGzipStatic < Testcase
|
|
108
125
|
|
109
126
|
req = "#{m} /COPYING HTTP/1.0\r\n" \
|
110
127
|
"Range: bytes=66666666-\r\nAccept-Encoding: gzip"
|
111
|
-
|
128
|
+
check.call(req) do |head|
|
112
129
|
assert_match %r{^Content-Range: bytes \*/#{st.size}\r\n}, head
|
113
130
|
assert_match %r{\AHTTP/1\.1 416 }, head
|
114
131
|
end
|
115
|
-
assert_nil body
|
116
132
|
end
|
117
133
|
end
|
118
134
|
|
@@ -164,15 +180,13 @@ class TestExtrasTryGzipStatic < Testcase
|
|
164
180
|
Timeout.timeout(30) do # 404
|
165
181
|
%w(GET HEAD).each do |m|
|
166
182
|
req = "#{m} /cp-ing HTTP/1.0\r\nAccept-Encoding: gzip"
|
167
|
-
|
183
|
+
check.call(req) do |head|
|
168
184
|
assert_match %r{HTTP/1\.1 404 }, head
|
169
185
|
end
|
170
|
-
assert_nil body
|
171
186
|
end
|
172
|
-
|
187
|
+
check.call("FOO /COPYING HTTP/1.0") do |head|
|
173
188
|
assert_match %r{HTTP/1\.1 405 }, head
|
174
189
|
end
|
175
|
-
assert_nil body
|
176
190
|
end
|
177
191
|
|
178
192
|
Net::HTTP.start(host, port) do |http|
|
@@ -126,7 +126,10 @@ class TestOutputBuffering < Testcase
|
|
126
126
|
end
|
127
127
|
size = gplv3.size
|
128
128
|
len = size.to_s
|
129
|
-
|
129
|
+
|
130
|
+
ranges = Rack::Utils.respond_to?(:get_byte_ranges) ?
|
131
|
+
Rack::Utils.get_byte_ranges(e['HTTP_RANGE'], size) :
|
132
|
+
Rack::Utils.byte_ranges(e, size)
|
130
133
|
status = 200
|
131
134
|
h = { "Content-Type" => "text/plain", "Content-Length" => len }
|
132
135
|
if ranges && ranges.size == 1
|
@@ -216,6 +219,7 @@ class TestOutputBuffering < Testcase
|
|
216
219
|
|
217
220
|
def test_client_timeout
|
218
221
|
err = @err
|
222
|
+
skip_skb_mem
|
219
223
|
apperr = tmpfile(%w(app .err))
|
220
224
|
cfg = Yahns::Config.new
|
221
225
|
size = RAND.size * NR
|
data/test/test_proxy_pass.rb
CHANGED
@@ -485,7 +485,7 @@ class TestProxyPass < Testcase
|
|
485
485
|
assert_equal exp, res
|
486
486
|
errs = File.readlines(@err.path).grep(/\bERROR\b/)
|
487
487
|
assert_equal 1, errs.size
|
488
|
-
assert_match(/
|
488
|
+
assert_match(/upstream EOF/, errs[0])
|
489
489
|
@err.truncate(0)
|
490
490
|
|
491
491
|
# truncated headers or no response at all...
|
@@ -501,7 +501,7 @@ class TestProxyPass < Testcase
|
|
501
501
|
s.close
|
502
502
|
errs = File.readlines(@err.path).grep(/\bERROR\b/)
|
503
503
|
assert_equal 1, errs.size
|
504
|
-
assert_match(/
|
504
|
+
assert_match(/upstream EOF/, errs[0])
|
505
505
|
@err.truncate(0)
|
506
506
|
end
|
507
507
|
end
|
@@ -0,0 +1,170 @@
|
|
1
|
+
# Copyright (C) 2015-2016 all contributors <yahns-public@yhbt.net>
|
2
|
+
# License: GPL-3.0+ (https://www.gnu.org/licenses/gpl-3.0.txt)
|
3
|
+
# frozen_string_literal: true
|
4
|
+
require_relative 'server_helper'
|
5
|
+
begin
|
6
|
+
require 'kcar'
|
7
|
+
rescue LoadError
|
8
|
+
end
|
9
|
+
require 'digest/md5'
|
10
|
+
class TestProxyPassNoBuffering < Testcase
|
11
|
+
ENV["N"].to_i > 1 and parallelize_me!
|
12
|
+
include ServerHelper
|
13
|
+
STR4 = 'abcd' * (256 * 1024)
|
14
|
+
NCHUNK = 50
|
15
|
+
class ProxiedApp
|
16
|
+
def call(env)
|
17
|
+
case env['REQUEST_METHOD']
|
18
|
+
when 'GET'
|
19
|
+
case env['PATH_INFO']
|
20
|
+
when '/giant-body'
|
21
|
+
h = [ %W(content-type text/pain) ]
|
22
|
+
|
23
|
+
# HTTP/1.0 is not Rack-compliant, so no Rack::Lint for us :)
|
24
|
+
if env['HTTP_VERSION'] == 'HTTP/1.1'
|
25
|
+
h << %W(content-length #{NCHUNK * STR4.size})
|
26
|
+
end
|
27
|
+
|
28
|
+
body = Object.new
|
29
|
+
def body.each
|
30
|
+
NCHUNK.times { yield STR4 }
|
31
|
+
end
|
32
|
+
[ 200, h, body ]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def setup
|
39
|
+
@srv2 = TCPServer.new(ENV["TEST_HOST"] || "127.0.0.1", 0)
|
40
|
+
server_helper_setup
|
41
|
+
skip "kcar missing yahns/proxy_pass" unless defined?(Kcar)
|
42
|
+
require 'yahns/proxy_pass'
|
43
|
+
@tmpdir = yahns_mktmpdir
|
44
|
+
end
|
45
|
+
|
46
|
+
def teardown
|
47
|
+
@srv2.close if defined?(@srv2) && !@srv2.closed?
|
48
|
+
server_helper_teardown
|
49
|
+
FileUtils.rm_rf(@tmpdir) if defined?(@tmpdir)
|
50
|
+
end
|
51
|
+
|
52
|
+
def check_headers(io)
|
53
|
+
l = io.gets
|
54
|
+
assert_match %r{\AHTTP/1\.[01] 200\b}, l
|
55
|
+
begin
|
56
|
+
l = io.gets
|
57
|
+
end until l == "\r\n"
|
58
|
+
end
|
59
|
+
|
60
|
+
def test_proxy_pass_no_buffering
|
61
|
+
to_close = []
|
62
|
+
err, cfg, host, port = @err, Yahns::Config.new, @srv.addr[3], @srv.addr[1]
|
63
|
+
host2, port2 = @srv2.addr[3], @srv2.addr[1]
|
64
|
+
pxp = Yahns::ProxyPass.new("http://#{host2}:#{port2}",
|
65
|
+
proxy_buffering: false)
|
66
|
+
tmpdir = @tmpdir
|
67
|
+
pid = mkserver(cfg) do
|
68
|
+
ObjectSpace.each_object(Yahns::TmpIO) { |io| io.close unless io.closed? }
|
69
|
+
@srv2.close
|
70
|
+
cfg.instance_eval do
|
71
|
+
app(:rack, pxp) do
|
72
|
+
listen "#{host}:#{port}"
|
73
|
+
output_buffering true, tmpdir: tmpdir
|
74
|
+
end
|
75
|
+
stderr_path err.path
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
pid2 = mkserver(cfg, @srv2) do
|
80
|
+
ObjectSpace.each_object(Yahns::TmpIO) { |io| io.close unless io.closed? }
|
81
|
+
@srv.close
|
82
|
+
cfg.instance_eval do
|
83
|
+
app(:rack, ProxiedApp.new) do
|
84
|
+
output_buffering false
|
85
|
+
listen "#{host2}:#{port2}"
|
86
|
+
end
|
87
|
+
stderr_path err.path
|
88
|
+
end
|
89
|
+
end
|
90
|
+
%w(1.0 1.1).each do |ver|
|
91
|
+
s = TCPSocket.new(host, port)
|
92
|
+
to_close << s
|
93
|
+
req = "GET /giant-body HTTP/#{ver}\r\nHost: example.com\r\n".dup
|
94
|
+
req << "Connection: close\r\n" if ver == '1.1'
|
95
|
+
req << "\r\n"
|
96
|
+
s.write(req)
|
97
|
+
bufs = []
|
98
|
+
sleep 1
|
99
|
+
10.times do
|
100
|
+
sleep 0.1
|
101
|
+
# ensure no files get created
|
102
|
+
if RUBY_PLATFORM =~ /\blinux\b/ && `which lsof 2>/dev/null`.size >= 4
|
103
|
+
qtmpdir = Regexp.quote("#@tmpdir/")
|
104
|
+
deleted1 = `lsof -p #{pid}`.split("\n")
|
105
|
+
deleted1 = deleted1.grep(/\bREG\b.*#{qtmpdir}.* \(deleted\)/)
|
106
|
+
deleted2 = `lsof -p #{pid2}`.split("\n")
|
107
|
+
deleted2 = deleted2.grep(/\bREG\b.*#{qtmpdir}.* \(deleted\)/)
|
108
|
+
[ deleted1, deleted2 ].each do |ary|
|
109
|
+
ary.delete_if { |x| x =~ /\.(?:err|out|rb|ru) \(deleted\)/ }
|
110
|
+
end
|
111
|
+
assert_equal 0, deleted1.size, "pid1=#{deleted1.inspect}"
|
112
|
+
assert_equal 0, deleted2.size, "pid2=#{deleted2.inspect}"
|
113
|
+
bufs.push(deleted1[0])
|
114
|
+
end
|
115
|
+
end
|
116
|
+
before = bufs.size
|
117
|
+
bufs.uniq!
|
118
|
+
assert bufs.size < before, 'unlinked buffer should not grow'
|
119
|
+
buf = ''.dup
|
120
|
+
slow = Digest::MD5.new
|
121
|
+
ft = Thread.new do
|
122
|
+
fast = Digest::MD5.new
|
123
|
+
f = TCPSocket.new(host2, port2)
|
124
|
+
f.write(req)
|
125
|
+
b2 = ''.dup
|
126
|
+
check_headers(f)
|
127
|
+
nf = 0
|
128
|
+
begin
|
129
|
+
f.readpartial(1024 * 1024, b2)
|
130
|
+
nf += b2.bytesize
|
131
|
+
fast.update(b2)
|
132
|
+
rescue EOFError
|
133
|
+
f = f.close
|
134
|
+
end while f
|
135
|
+
b2.clear
|
136
|
+
[ nf, fast.hexdigest ]
|
137
|
+
end
|
138
|
+
Thread.abort_on_exception = true
|
139
|
+
check_headers(s)
|
140
|
+
n = 0
|
141
|
+
begin
|
142
|
+
s.readpartial(1024 * 1024, buf)
|
143
|
+
slow.update(buf)
|
144
|
+
n += buf.bytesize
|
145
|
+
sleep 0.01
|
146
|
+
rescue EOFError
|
147
|
+
s = s.close
|
148
|
+
end while s
|
149
|
+
ft.join(5)
|
150
|
+
assert_equal [n, slow.hexdigest ], ft.value
|
151
|
+
|
152
|
+
fast = Digest::MD5.new
|
153
|
+
f = TCPSocket.new(host, port)
|
154
|
+
f.write(req)
|
155
|
+
check_headers(f)
|
156
|
+
begin
|
157
|
+
f.readpartial(1024 * 1024, buf)
|
158
|
+
fast.update(buf)
|
159
|
+
rescue EOFError
|
160
|
+
f = f.close
|
161
|
+
end while f
|
162
|
+
buf.clear
|
163
|
+
assert_equal slow.hexdigest, fast.hexdigest
|
164
|
+
end
|
165
|
+
ensure
|
166
|
+
to_close.each { |io| io.close unless io.closed? }
|
167
|
+
quit_wait(pid)
|
168
|
+
quit_wait(pid2)
|
169
|
+
end
|
170
|
+
end
|
data/test/test_reopen_logs.rb
CHANGED
@@ -2,7 +2,11 @@
|
|
2
2
|
# License: GPL-3.0+ (https://www.gnu.org/licenses/gpl-3.0.txt)
|
3
3
|
# frozen_string_literal: true
|
4
4
|
require_relative 'server_helper'
|
5
|
-
require 'rack
|
5
|
+
require 'rack'
|
6
|
+
# trigger autoload, since rack 1.x => 2.x renames:
|
7
|
+
# 'rack/commonlogger' => 'rack/common_logger'
|
8
|
+
# so we can't require directly
|
9
|
+
Rack::CommonLogger.class
|
6
10
|
|
7
11
|
class TestReopenLogs < Testcase
|
8
12
|
ENV["N"].to_i > 1 and parallelize_me!
|
data/test/test_response.rb
CHANGED
@@ -9,6 +9,48 @@ class TestResponse < Testcase
|
|
9
9
|
alias setup server_helper_setup
|
10
10
|
alias teardown server_helper_teardown
|
11
11
|
|
12
|
+
def test_auto_head
|
13
|
+
err = @err
|
14
|
+
cfg = Yahns::Config.new
|
15
|
+
host, port = @srv.addr[3], @srv.addr[1]
|
16
|
+
str = "HELLO WORLD\n"
|
17
|
+
cfg.instance_eval do
|
18
|
+
GTL.synchronize do
|
19
|
+
app = Rack::Builder.new do
|
20
|
+
use Rack::ContentLength
|
21
|
+
use Rack::ContentType, "text/plain"
|
22
|
+
run(lambda do |env|
|
23
|
+
case env['PATH_INFO']
|
24
|
+
when '/'; return [ 200, {}, [ str ] ]
|
25
|
+
when '/304'; return [ 304, {}, [ str ] ]
|
26
|
+
else
|
27
|
+
abort 'unsupported'
|
28
|
+
end
|
29
|
+
end)
|
30
|
+
end
|
31
|
+
app(:rack, app) { listen "#{host}:#{port}" }
|
32
|
+
end
|
33
|
+
logger(Logger.new(err.path))
|
34
|
+
end
|
35
|
+
pid = mkserver(cfg)
|
36
|
+
s = TCPSocket.new(host, port)
|
37
|
+
s.write("HEAD / HTTP/1.0\r\n\r\n")
|
38
|
+
assert s.wait(30), "IO wait failed"
|
39
|
+
buf = s.read
|
40
|
+
assert_match %r{\r\n\r\n\z}, buf
|
41
|
+
s.close
|
42
|
+
|
43
|
+
s = TCPSocket.new(host, port)
|
44
|
+
s.write("GET /304 HTTP/1.0\r\n\r\n")
|
45
|
+
assert s.wait(30), "IO wait failed"
|
46
|
+
buf = s.read
|
47
|
+
assert_match %r{\r\n\r\n\z}, buf
|
48
|
+
assert_match %r{\b304\b}, buf
|
49
|
+
s.close
|
50
|
+
ensure
|
51
|
+
quit_wait(pid)
|
52
|
+
end
|
53
|
+
|
12
54
|
def test_response_time_empty_body
|
13
55
|
err = @err
|
14
56
|
cfg = Yahns::Config.new
|