yahns 1.12.5 → 1.13.0
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 +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
|