yahns 0.0.1 → 0.0.2
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/.gitignore +1 -0
- data/Documentation/.gitignore +5 -0
- data/Documentation/GNUmakefile +50 -0
- data/Documentation/yahns-rackup.txt +152 -0
- data/Documentation/yahns.txt +68 -0
- data/Documentation/yahns_config.txt +563 -0
- data/GIT-VERSION-GEN +1 -1
- data/GNUmakefile +14 -7
- data/HACKING +56 -0
- data/INSTALL +8 -0
- data/README +15 -2
- data/Rakefile +2 -2
- data/bin/yahns +1 -2
- data/bin/yahns-rackup +9 -0
- data/examples/yahns_multi.conf.rb +14 -4
- data/examples/yahns_rack_basic.conf.rb +17 -1
- data/extras/README +16 -0
- data/extras/autoindex.rb +151 -0
- data/extras/exec_cgi.rb +108 -0
- data/extras/proxy_pass.rb +210 -0
- data/extras/try_gzip_static.rb +208 -0
- data/lib/yahns.rb +5 -2
- data/lib/yahns/acceptor.rb +64 -22
- data/lib/yahns/cap_input.rb +2 -2
- data/lib/yahns/{client_expire_portable.rb → client_expire_generic.rb} +12 -11
- data/lib/yahns/{client_expire.rb → client_expire_tcpi.rb} +7 -6
- data/lib/yahns/config.rb +107 -22
- data/lib/yahns/daemon.rb +2 -0
- data/lib/yahns/fdmap.rb +28 -9
- data/lib/yahns/http_client.rb +123 -37
- data/lib/yahns/http_context.rb +21 -3
- data/lib/yahns/http_response.rb +80 -19
- data/lib/yahns/log.rb +23 -4
- data/lib/yahns/queue_epoll.rb +20 -9
- data/lib/yahns/queue_quitter.rb +16 -0
- data/lib/yahns/queue_quitter_pipe.rb +24 -0
- data/lib/yahns/rack.rb +0 -1
- data/lib/yahns/rackup_handler.rb +57 -0
- data/lib/yahns/server.rb +189 -59
- data/lib/yahns/server_mp.rb +43 -35
- data/lib/yahns/sigevent_pipe.rb +1 -0
- data/lib/yahns/socket_helper.rb +37 -11
- data/lib/yahns/stream_file.rb +14 -4
- data/lib/yahns/stream_input.rb +13 -7
- data/lib/yahns/tcp_server.rb +7 -0
- data/lib/yahns/tmpio.rb +10 -3
- data/lib/yahns/unix_server.rb +7 -0
- data/lib/yahns/wbuf.rb +19 -2
- data/lib/yahns/wbuf_common.rb +10 -3
- data/lib/yahns/wbuf_str.rb +24 -0
- data/lib/yahns/worker.rb +5 -26
- data/test/helper.rb +15 -5
- data/test/server_helper.rb +37 -1
- data/test/test_bin.rb +17 -8
- data/test/test_buffer_tmpdir.rb +103 -0
- data/test/test_client_expire.rb +71 -35
- data/test/test_client_max_body_size.rb +5 -13
- data/test/test_config.rb +1 -1
- data/test/test_expect_100.rb +176 -0
- data/test/test_extras_autoindex.rb +53 -0
- data/test/test_extras_exec_cgi.rb +81 -0
- data/test/test_extras_exec_cgi.sh +35 -0
- data/test/test_extras_try_gzip_static.rb +177 -0
- data/test/test_input.rb +128 -0
- data/test/test_mt_accept.rb +48 -0
- data/test/test_output_buffering.rb +90 -63
- data/test/test_rack.rb +1 -1
- data/test/test_rack_hijack.rb +2 -6
- data/test/test_reopen_logs.rb +2 -8
- data/test/test_serve_static.rb +104 -8
- data/test/test_server.rb +448 -73
- data/test/test_stream_file.rb +1 -1
- data/test/test_unix_socket.rb +72 -0
- data/test/test_wbuf.rb +20 -17
- data/yahns.gemspec +3 -0
- metadata +57 -5
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
require_relative 'server_helper'
|
|
4
4
|
|
|
5
5
|
class TestClientMaxBodySize < Testcase
|
|
6
|
-
parallelize_me!
|
|
6
|
+
ENV["N"].to_i > 1 and parallelize_me!
|
|
7
7
|
include ServerHelper
|
|
8
8
|
alias setup server_helper_setup
|
|
9
9
|
alias teardown server_helper_teardown
|
|
@@ -27,14 +27,6 @@ class TestClientMaxBodySize < Testcase
|
|
|
27
27
|
"Content-Length: #{bytes}\r\n\r\n#{'*' * body_bytes}"
|
|
28
28
|
end
|
|
29
29
|
|
|
30
|
-
def mkserver(cfg)
|
|
31
|
-
fork do
|
|
32
|
-
srv = Yahns::Server.new(cfg)
|
|
33
|
-
ENV["YAHNS_FD"] = @srv.fileno.to_s
|
|
34
|
-
srv.start.join
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
|
|
38
30
|
def test_0_lazy; cmbs_test_0(:lazy); end
|
|
39
31
|
def test_0_true; cmbs_test_0(true); end
|
|
40
32
|
def test_0_false; cmbs_test_0(false); end
|
|
@@ -138,24 +130,24 @@ class TestClientMaxBodySize < Testcase
|
|
|
138
130
|
|
|
139
131
|
def default_identity_checks(host, port, defmax = DEFMBS)
|
|
140
132
|
if defmax >= 666
|
|
141
|
-
c =
|
|
133
|
+
c = get_tcp_client(host, port)
|
|
142
134
|
c.write(identity_req(666))
|
|
143
135
|
assert_equal "666", c.read.split(/\r\n\r\n/)[1]
|
|
144
136
|
c.close
|
|
145
137
|
end
|
|
146
138
|
|
|
147
|
-
c =
|
|
139
|
+
c = get_tcp_client(host, port)
|
|
148
140
|
c.write(identity_req(0))
|
|
149
141
|
assert_equal "0", c.read.split(/\r\n\r\n/)[1]
|
|
150
142
|
c.close
|
|
151
143
|
|
|
152
|
-
c =
|
|
144
|
+
c = get_tcp_client(host, port)
|
|
153
145
|
c.write(identity_req(defmax))
|
|
154
146
|
assert_equal "#{defmax}", c.read.split(/\r\n\r\n/)[1]
|
|
155
147
|
c.close
|
|
156
148
|
|
|
157
149
|
toobig = defmax + 1
|
|
158
|
-
c =
|
|
150
|
+
c = get_tcp_client(host, port)
|
|
159
151
|
c.write(identity_req(toobig, false))
|
|
160
152
|
assert_match(%r{\AHTTP/1\.[01] 413 }, Timeout.timeout(10) { c.read })
|
|
161
153
|
c.close
|
data/test/test_config.rb
CHANGED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# Copyright (C) 2013, Eric Wong <normalperson@yhbt.net> and all contributors
|
|
2
|
+
# License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
3
|
+
require_relative 'server_helper'
|
|
4
|
+
|
|
5
|
+
class TestExpect100 < Testcase
|
|
6
|
+
ENV["N"].to_i > 1 and parallelize_me!
|
|
7
|
+
include ServerHelper
|
|
8
|
+
alias setup server_helper_setup
|
|
9
|
+
alias teardown server_helper_teardown
|
|
10
|
+
|
|
11
|
+
APP = lambda do |env|
|
|
12
|
+
h = [ %w(Content-Length 0), %w(Content-Type text/plain) ]
|
|
13
|
+
if env["HTTP_EXPECT"] =~ /100-continue/
|
|
14
|
+
code = env["HTTP_X_FORCE_RCODE"] || 100
|
|
15
|
+
[ code, h, [] ]
|
|
16
|
+
else
|
|
17
|
+
env["rack.input"].read
|
|
18
|
+
[ 201, h, [] ]
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def test_buffer_noccc; _test_expect_100(true, false); end
|
|
23
|
+
def test_nobuffer_noccc; _test_expect_100(false, false); end
|
|
24
|
+
def test_lazybuffer_noccc; _test_expect_100(:lazy, false); end
|
|
25
|
+
def test_buffer_ccc; _test_expect_100(true, true); end
|
|
26
|
+
def test_nobuffer_ccc; _test_expect_100(false, true); end
|
|
27
|
+
def test_lazybuffer_ccc; _test_expect_100(:lazy, true); end
|
|
28
|
+
|
|
29
|
+
def _test_expect_100(btype, ccc)
|
|
30
|
+
err = @err
|
|
31
|
+
cfg = Yahns::Config.new
|
|
32
|
+
host, port = @srv.addr[3], @srv.addr[1]
|
|
33
|
+
cfg.instance_eval do
|
|
34
|
+
stderr_path err.path
|
|
35
|
+
GTL.synchronize {
|
|
36
|
+
app(:rack, APP) {
|
|
37
|
+
listen "#{host}:#{port}"
|
|
38
|
+
input_buffering btype
|
|
39
|
+
check_client_connection ccc
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
end
|
|
43
|
+
pid = mkserver(cfg)
|
|
44
|
+
c = get_tcp_client(host, port)
|
|
45
|
+
r = "PUT / HTTP/1.0\r\nExpect: 100-continue\r\n\r\n"
|
|
46
|
+
c.write(r)
|
|
47
|
+
assert c.wait(10), "timed out"
|
|
48
|
+
buf = c.read
|
|
49
|
+
assert_match(%r{\AHTTP/1\.1 100 Continue\r\n\r\nHTTP/1\.1 201}, buf)
|
|
50
|
+
|
|
51
|
+
rc = system("curl -sSf -T- http://#{host}:#{port}/", in: "/dev/null")
|
|
52
|
+
assert $?.success?, $?.inspect
|
|
53
|
+
assert rc
|
|
54
|
+
ensure
|
|
55
|
+
quit_wait(pid)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def _test_reject(btype, ccc)
|
|
59
|
+
err = @err
|
|
60
|
+
cfg = Yahns::Config.new
|
|
61
|
+
host, port = @srv.addr[3], @srv.addr[1]
|
|
62
|
+
cfg.instance_eval do
|
|
63
|
+
stderr_path err.path
|
|
64
|
+
GTL.synchronize {
|
|
65
|
+
app(:rack, APP) {
|
|
66
|
+
listen "#{host}:#{port}"
|
|
67
|
+
input_buffering btype
|
|
68
|
+
check_client_connection ccc
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
end
|
|
72
|
+
pid = mkserver(cfg)
|
|
73
|
+
c = get_tcp_client(host, port)
|
|
74
|
+
r = "PUT / HTTP/1.0\r\nExpect: 100-continue\r\nX-Force-RCODE: 666\r\n\r\n"
|
|
75
|
+
c.write(r)
|
|
76
|
+
assert c.wait(10), "timed out"
|
|
77
|
+
buf = c.read
|
|
78
|
+
assert_match(%r{\AHTTP/1\.1 666\r\n}, buf)
|
|
79
|
+
|
|
80
|
+
url = "http://#{host}:#{port}/"
|
|
81
|
+
rc = system("curl -sf -T- -HX-Force-Rcode:666 #{url}", in: "/dev/null")
|
|
82
|
+
refute $?.success?, $?.inspect
|
|
83
|
+
refute rc
|
|
84
|
+
ensure
|
|
85
|
+
quit_wait(pid)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def test_reject_lazy_noccc; _test_reject(:lazy, false); end
|
|
89
|
+
def test_reject_true_noccc; _test_reject(false, false); end
|
|
90
|
+
def test_reject_lazy_ccc; _test_reject(:lazy, true); end
|
|
91
|
+
def test_reject_true_ccc; _test_reject(false, true); end
|
|
92
|
+
|
|
93
|
+
def test_swait_t_t; _swait(true, true, [1]); end
|
|
94
|
+
def test_swait_f_f; _swait(false, false, [1]); end
|
|
95
|
+
def test_swait_t_f; _swait(true, false, [1]); end
|
|
96
|
+
def test_swait_f_t; _swait(false, true, [1]); end
|
|
97
|
+
def test_swait_l_t; _swait(:lazy, true, [1]); end
|
|
98
|
+
def test_swait_l_f; _swait(:lazy, false, [1]); end
|
|
99
|
+
|
|
100
|
+
def test_swait2_t_t; _swait(true, true, [1,2]); end
|
|
101
|
+
def test_swait2_f_f; _swait(false, false, [1,2]); end
|
|
102
|
+
def test_swait2_t_f; _swait(true, false, [1,2]); end
|
|
103
|
+
def test_swait2_f_t; _swait(false, true, [1,2]); end
|
|
104
|
+
def test_swait2_l_t; _swait(:lazy, true, [1,2]); end
|
|
105
|
+
def test_swait2_l_f; _swait(:lazy, false, [1,2]); end
|
|
106
|
+
|
|
107
|
+
def test_swait3_t_t; _swait(true, true, [1,3]); end
|
|
108
|
+
def test_swait3_f_f; _swait(false, false, [1,3]); end
|
|
109
|
+
def test_swait3_t_f; _swait(true, false, [1,3]); end
|
|
110
|
+
def test_swait3_f_t; _swait(false, true, [1,3]); end
|
|
111
|
+
def test_swait3_l_t; _swait(:lazy, true, [1,3]); end
|
|
112
|
+
def test_swait3_l_f; _swait(:lazy, false, [1,3]); end
|
|
113
|
+
|
|
114
|
+
def test_swait_t_t_ccc; _swait(true, true, [1], true); end
|
|
115
|
+
def test_swait_f_f_ccc; _swait(false, false, [1], true); end
|
|
116
|
+
def test_swait_t_f_ccc; _swait(true, false, [1], true); end
|
|
117
|
+
def test_swait_f_t_ccc; _swait(false, true, [1], true); end
|
|
118
|
+
def test_swait_l_t_ccc; _swait(:lazy, true, [1], true); end
|
|
119
|
+
def test_swait_l_f_ccc; _swait(:lazy, false, [1], true); end
|
|
120
|
+
|
|
121
|
+
def test_swait2_t_t_ccc; _swait(true, true, [1,2], true); end
|
|
122
|
+
def test_swait2_f_f_ccc; _swait(false, false, [1,2], true); end
|
|
123
|
+
def test_swait2_t_f_ccc; _swait(true, false, [1,2], true); end
|
|
124
|
+
def test_swait2_f_t_ccc; _swait(false, true, [1,2], true); end
|
|
125
|
+
def test_swait2_l_t_ccc; _swait(:lazy, true, [1,2], true); end
|
|
126
|
+
def test_swait2_l_f_ccc; _swait(:lazy, false, [1,2], true); end
|
|
127
|
+
|
|
128
|
+
def test_swait3_t_t_ccc; _swait(true, true, [1,3], true); end
|
|
129
|
+
def test_swait3_f_f_ccc; _swait(false, false, [1,3], true); end
|
|
130
|
+
def test_swait3_t_f_ccc; _swait(true, false, [1,3], true); end
|
|
131
|
+
def test_swait3_f_t_ccc; _swait(false, true, [1,3], true); end
|
|
132
|
+
def test_swait3_l_t_ccc; _swait(:lazy, true, [1,3], true); end
|
|
133
|
+
def test_swait3_l_f_ccc; _swait(:lazy, false, [1,3], true); end
|
|
134
|
+
|
|
135
|
+
def test_swait3_t_t_ccc_body; _swait(true, true, [1,3], true, "HI"); end
|
|
136
|
+
def test_swait3_f_f_ccc_body; _swait(false, false, [1,3], true, "HI"); end
|
|
137
|
+
def test_swait3_t_f_ccc_body; _swait(true, false, [1,3], true, "HI"); end
|
|
138
|
+
def test_swait3_f_t_ccc_body; _swait(false, true, [1,3], true, "HI"); end
|
|
139
|
+
def test_swait3_l_t_ccc_body; _swait(:lazy, true, [1,3], true, "HI"); end
|
|
140
|
+
def test_swait3_l_f_ccc_body; _swait(:lazy, false, [1,3], true, "HI"); end
|
|
141
|
+
|
|
142
|
+
def _swait(ibtype, obtype, block_on, ccc = false, body = "")
|
|
143
|
+
err = @err
|
|
144
|
+
cfg = Yahns::Config.new
|
|
145
|
+
host, port = @srv.addr[3], @srv.addr[1]
|
|
146
|
+
cfg.instance_eval do
|
|
147
|
+
stderr_path err.path
|
|
148
|
+
GTL.synchronize {
|
|
149
|
+
app(:rack, APP) {
|
|
150
|
+
listen "#{host}:#{port}"
|
|
151
|
+
output_buffering obtype
|
|
152
|
+
input_buffering ibtype
|
|
153
|
+
check_client_connection ccc
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
end
|
|
157
|
+
pid = mkserver(cfg) do
|
|
158
|
+
$_tw_blocked = 0
|
|
159
|
+
$_tw_block_on = block_on
|
|
160
|
+
Yahns::HttpClient.__send__(:include, TrywriteBlocked)
|
|
161
|
+
end
|
|
162
|
+
c = get_tcp_client(host, port)
|
|
163
|
+
if body.size > 0
|
|
164
|
+
r = "PUT / HTTP/1.0\r\nExpect: 100-continue\r\n"
|
|
165
|
+
r << "Content-Length: #{body.size}\r\n\r\n#{body}"
|
|
166
|
+
else
|
|
167
|
+
r = "PUT / HTTP/1.0\r\nExpect: 100-continue\r\n\r\n"
|
|
168
|
+
end
|
|
169
|
+
c.write(r)
|
|
170
|
+
assert c.wait(10), "timed out"
|
|
171
|
+
buf = c.read
|
|
172
|
+
assert_match(%r{\AHTTP/1\.1 100 Continue\r\n\r\nHTTP/1\.1 201}, buf)
|
|
173
|
+
ensure
|
|
174
|
+
quit_wait(pid)
|
|
175
|
+
end
|
|
176
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Copyright (C) 2013, Eric Wong <normalperson@yhbt.net> and all contributors
|
|
2
|
+
# License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
3
|
+
require_relative 'server_helper'
|
|
4
|
+
require 'zlib'
|
|
5
|
+
require 'time'
|
|
6
|
+
|
|
7
|
+
class TestExtrasAutoindex < Testcase
|
|
8
|
+
ENV["N"].to_i > 1 and parallelize_me!
|
|
9
|
+
include ServerHelper
|
|
10
|
+
|
|
11
|
+
def setup
|
|
12
|
+
@tmpdir = Dir.mktmpdir
|
|
13
|
+
server_helper_setup
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def teardown
|
|
17
|
+
server_helper_teardown
|
|
18
|
+
FileUtils.rm_rf @tmpdir
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def test_autoindex
|
|
22
|
+
err, cfg, host, port = @err, Yahns::Config.new, @srv.addr[3], @srv.addr[1]
|
|
23
|
+
tmpdir = @tmpdir
|
|
24
|
+
pid = mkserver(cfg) do
|
|
25
|
+
$LOAD_PATH.unshift "#{Dir.pwd}/extras"
|
|
26
|
+
require 'try_gzip_static'
|
|
27
|
+
require 'autoindex'
|
|
28
|
+
cfg.instance_eval do
|
|
29
|
+
app(:rack, Autoindex.new(TryGzipStatic.new(tmpdir))) do
|
|
30
|
+
listen "#{host}:#{port}"
|
|
31
|
+
end
|
|
32
|
+
stderr_path err.path
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
Net::HTTP.start(host, port) do |http|
|
|
37
|
+
res = http.request(Net::HTTP::Get.new("/"))
|
|
38
|
+
assert_equal 200, res.code.to_i
|
|
39
|
+
File.open("#@tmpdir/foo", "w").close
|
|
40
|
+
res = http.request(Net::HTTP::Get.new("/"))
|
|
41
|
+
assert_equal 200, res.code.to_i
|
|
42
|
+
assert_match %r{foo}, res.body
|
|
43
|
+
Dir.mkdir "#@tmpdir/bar"
|
|
44
|
+
|
|
45
|
+
res = http.request(Net::HTTP::Get.new("/"))
|
|
46
|
+
assert_equal 200, res.code.to_i
|
|
47
|
+
assert_match %r{foo}, res.body
|
|
48
|
+
assert_match %r{bar/}, res.body
|
|
49
|
+
end
|
|
50
|
+
ensure
|
|
51
|
+
quit_wait pid
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Copyright (C) 2013, Eric Wong <normalperson@yhbt.net> and all contributors
|
|
2
|
+
# License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
3
|
+
require_relative 'server_helper'
|
|
4
|
+
|
|
5
|
+
class TestExtrasExecCGI < Testcase
|
|
6
|
+
ENV["N"].to_i > 1 and parallelize_me!
|
|
7
|
+
include ServerHelper
|
|
8
|
+
alias setup server_helper_setup
|
|
9
|
+
alias teardown server_helper_teardown
|
|
10
|
+
|
|
11
|
+
def test_exec_cgi
|
|
12
|
+
err, cfg, host, port = @err, Yahns::Config.new, @srv.addr[3], @srv.addr[1]
|
|
13
|
+
runme = "#{Dir.pwd}/test/test_extras_exec_cgi.sh"
|
|
14
|
+
assert File.executable?(runme), "run test in project root"
|
|
15
|
+
pid = mkserver(cfg) do
|
|
16
|
+
require './extras/exec_cgi'
|
|
17
|
+
cfg.instance_eval do
|
|
18
|
+
app(:rack, ExecCgi.new(runme)) do
|
|
19
|
+
listen "#{host}:#{port}"
|
|
20
|
+
end
|
|
21
|
+
stderr_path err.path
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
Timeout.timeout(30) do # we can chunk
|
|
26
|
+
c = get_tcp_client(host, port)
|
|
27
|
+
c.write "GET / HTTP/1.1\r\nConnection: close\r\n" \
|
|
28
|
+
"Host: example.com\r\n\r\n"
|
|
29
|
+
head, body = c.read.split(/\r\n\r\n/, 2)
|
|
30
|
+
assert_match %r{^Transfer-Encoding: chunked\b}, head
|
|
31
|
+
assert_equal "5\r\nHIHI\n\r\n0\r\n\r\n", body
|
|
32
|
+
c.close
|
|
33
|
+
cerr = tmpfile(%w(curl .err))
|
|
34
|
+
assert_equal "HIHI\n", `curl -sSfv 2>#{cerr.path} http://#{host}:#{port}/`
|
|
35
|
+
assert_match %r{\bTransfer-Encoding: chunked\b}, cerr.read
|
|
36
|
+
cerr.close!
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
Timeout.timeout(30) do # do not chunk on clients who can't handle chunking
|
|
40
|
+
c = get_tcp_client(host, port)
|
|
41
|
+
c.write "GET / HTTP/1.0\r\nHost: example.com\r\n\r\n"
|
|
42
|
+
head, body = c.read.split(/\r\n\r\n/)
|
|
43
|
+
assert_equal "HIHI\n", body
|
|
44
|
+
refute_match %r{^Transfer-Encoding: chunked\b}, head
|
|
45
|
+
c.close
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
Timeout.timeout(30) do # sure env is sane
|
|
49
|
+
c = get_tcp_client(host, port)
|
|
50
|
+
c.write "GET /env\r\n\r\n"
|
|
51
|
+
head, body = c.read.split(/\r\n\r\n/)
|
|
52
|
+
assert_nil body
|
|
53
|
+
assert_match %r{^REQUEST_METHOD=GET$}, head
|
|
54
|
+
assert_match %r{^PATH_INFO=/env$}, head
|
|
55
|
+
assert_match %r{^QUERY_STRING=$}, head
|
|
56
|
+
c.close
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
Timeout.timeout(30) do # known length should not chunk
|
|
60
|
+
c = get_tcp_client(host, port)
|
|
61
|
+
c.write "GET /known-length HTTP/1.1\r\nConnection: close\r\n" \
|
|
62
|
+
"Host: example.com\r\n\r\n"
|
|
63
|
+
head, body = c.read.split(/\r\n\r\n/, 2)
|
|
64
|
+
refute_match %r{^Transfer-Encoding: chunked\b}, head
|
|
65
|
+
assert_match %r{^Content-Length: 5\b}, head
|
|
66
|
+
assert_equal "HIHI\n", body
|
|
67
|
+
c.close
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
Timeout.timeout(30) do # 404
|
|
71
|
+
c = get_tcp_client(host, port)
|
|
72
|
+
c.write "GET /not-found HTTP/1.0\r\n\r\n"
|
|
73
|
+
head, body = c.read.split(/\r\n\r\n/)
|
|
74
|
+
assert_match %r{\AHTTP/1\.1 404 Not Found}, head
|
|
75
|
+
assert_nil body
|
|
76
|
+
c.close
|
|
77
|
+
end
|
|
78
|
+
ensure
|
|
79
|
+
quit_wait(pid)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# Copyright (C) 2013, Eric Wong <normalperson@yhbt.net> and all contributors
|
|
3
|
+
# License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
4
|
+
|
|
5
|
+
# test CGI program, this remains portable POSIX shell (not bash)
|
|
6
|
+
set -e
|
|
7
|
+
|
|
8
|
+
stdhead () {
|
|
9
|
+
echo Content-Type: text/plain
|
|
10
|
+
echo Status: 200 OK
|
|
11
|
+
echo
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
case $PATH_INFO in
|
|
15
|
+
/)
|
|
16
|
+
stdhead
|
|
17
|
+
echo HIHI
|
|
18
|
+
;;
|
|
19
|
+
/env)
|
|
20
|
+
stdhead
|
|
21
|
+
env
|
|
22
|
+
;;
|
|
23
|
+
/known-length)
|
|
24
|
+
echo Content-Type: text/plain
|
|
25
|
+
echo Status: 200 OK
|
|
26
|
+
echo Content-Length: 5
|
|
27
|
+
echo
|
|
28
|
+
echo HIHI
|
|
29
|
+
;;
|
|
30
|
+
*)
|
|
31
|
+
echo Content-Type: text/plain
|
|
32
|
+
echo Status: 404 Not Found
|
|
33
|
+
echo
|
|
34
|
+
;;
|
|
35
|
+
esac
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# Copyright (C) 2013, Eric Wong <normalperson@yhbt.net> and all contributors
|
|
2
|
+
# License: GPLv3 or later (https://www.gnu.org/licenses/gpl-3.0.txt)
|
|
3
|
+
require_relative 'server_helper'
|
|
4
|
+
require 'zlib'
|
|
5
|
+
require 'time'
|
|
6
|
+
|
|
7
|
+
class TestExtrasTryGzipStatic < Testcase
|
|
8
|
+
ENV["N"].to_i > 1 and parallelize_me!
|
|
9
|
+
include ServerHelper
|
|
10
|
+
GPL_TEXT = IO.binread("COPYING").freeze
|
|
11
|
+
|
|
12
|
+
def setup
|
|
13
|
+
@tmpdir = Dir.mktmpdir
|
|
14
|
+
server_helper_setup
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def teardown
|
|
18
|
+
server_helper_teardown
|
|
19
|
+
FileUtils.rm_rf @tmpdir
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def test_gzip_static
|
|
23
|
+
err, cfg, host, port = @err, Yahns::Config.new, @srv.addr[3], @srv.addr[1]
|
|
24
|
+
tmpdir = @tmpdir
|
|
25
|
+
pid = mkserver(cfg) do
|
|
26
|
+
require './extras/try_gzip_static'
|
|
27
|
+
cfg.instance_eval do
|
|
28
|
+
app(:rack, TryGzipStatic.new(tmpdir)) do
|
|
29
|
+
listen "#{host}:#{port}"
|
|
30
|
+
end
|
|
31
|
+
stderr_path err.path
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
begin # setup
|
|
36
|
+
gpl = "#{tmpdir}/COPYING"
|
|
37
|
+
gplgz = "#{tmpdir}/COPYING.gz"
|
|
38
|
+
FileUtils.cp("COPYING", gpl)
|
|
39
|
+
_, status = Process.waitpid2(fork do
|
|
40
|
+
File.open(gplgz, "w") do |fp|
|
|
41
|
+
Zlib::GzipWriter.wrap(fp.dup) { |io| io.write(GPL_TEXT) }
|
|
42
|
+
end
|
|
43
|
+
exit!(0)
|
|
44
|
+
end)
|
|
45
|
+
assert status.success?, status.inspect
|
|
46
|
+
st = File.stat(gpl)
|
|
47
|
+
gz_st = File.stat(gplgz)
|
|
48
|
+
assert_equal GPL_TEXT, `zcat #{gplgz}`, "Eric screwed up using zlib"
|
|
49
|
+
File.utime(st.atime, st.mtime, gplgz)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
check = lambda do |req, &blk|
|
|
53
|
+
c = get_tcp_client(host, port)
|
|
54
|
+
begin
|
|
55
|
+
c.write "#{req}\r\n\r\n"
|
|
56
|
+
head, body = c.read.split(/\r\n\r\n/)
|
|
57
|
+
blk.call(head)
|
|
58
|
+
body
|
|
59
|
+
ensure
|
|
60
|
+
c.close
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
Timeout.timeout(30) do # basic tests
|
|
65
|
+
%w(GET HEAD).each do |m|
|
|
66
|
+
body = check.call("#{m} /COPYING HTTP/1.0") do |head|
|
|
67
|
+
refute_match %r{^Content-Encoding: gzip\b}, head
|
|
68
|
+
assert_match %r{^Content-Type: text/plain\b}, head
|
|
69
|
+
assert_match %r{^Content-Length: #{st.size}\b}, head
|
|
70
|
+
end
|
|
71
|
+
case m
|
|
72
|
+
when "GET" then assert_equal GPL_TEXT, body
|
|
73
|
+
when "HEAD" then assert_nil body
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
req = "#{m} /COPYING HTTP/1.0\r\nAccept-Encoding: gzip"
|
|
77
|
+
body = check.call(req) do |head|
|
|
78
|
+
assert_match %r{^Content-Encoding: gzip\b}, head
|
|
79
|
+
assert_match %r{^Content-Type: text/plain\b}, head
|
|
80
|
+
assert_match %r{^Content-Length: #{gz_st.size}\b}, head
|
|
81
|
+
end
|
|
82
|
+
case m
|
|
83
|
+
when "GET"
|
|
84
|
+
assert_equal GPL_TEXT, Zlib::GzipReader.new(StringIO.new(body)).read
|
|
85
|
+
when "HEAD" then assert_nil body
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
Timeout.timeout(30) do # range tests
|
|
91
|
+
%w(HEAD GET).each do |m|
|
|
92
|
+
req = "#{m} /COPYING HTTP/1.0\r\n" \
|
|
93
|
+
"Range: bytes=5-46\r\nAccept-Encoding: gzip"
|
|
94
|
+
body = check.call(req) do |head|
|
|
95
|
+
assert_match %r{\AHTTP/1\.1 206 Partial Content\r\n}, head
|
|
96
|
+
refute_match %r{^Content-Encoding: gzip\b}, head
|
|
97
|
+
assert_match %r{^Content-Type: text/plain\b}, head
|
|
98
|
+
assert_match %r{^Content-Length: 42\b}, head
|
|
99
|
+
assert_match %r{^Content-Range: bytes 5-46/#{st.size}\r\n}, head
|
|
100
|
+
end
|
|
101
|
+
case m
|
|
102
|
+
when "GET" then assert_equal GPL_TEXT[5..46], body
|
|
103
|
+
when "HEAD" then assert_nil body
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
req = "#{m} /COPYING HTTP/1.0\r\n" \
|
|
107
|
+
"Range: bytes=66666666-\r\nAccept-Encoding: gzip"
|
|
108
|
+
body = check.call(req) do |head|
|
|
109
|
+
assert_match %r{^Content-Range: bytes \*/#{st.size}\r\n}, head
|
|
110
|
+
assert_match %r{\AHTTP/1\.1 416 }, head
|
|
111
|
+
end
|
|
112
|
+
assert_nil body
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
Timeout.timeout(30) do # gzip counterpart is nonexistent
|
|
117
|
+
File.link(gpl, "#{gpl}.hardlink")
|
|
118
|
+
%w(GET HEAD).each do |m|
|
|
119
|
+
req = "#{m} /COPYING.hardlink HTTP/1.0\r\nAccept-Encoding: gzip"
|
|
120
|
+
body = check.call(req) do |head|
|
|
121
|
+
refute_match %r{^Content-Encoding: gzip\b}, head
|
|
122
|
+
assert_match %r{^Content-Type: text/plain\b}, head
|
|
123
|
+
assert_match %r{^Content-Length: #{st.size}\b}, head
|
|
124
|
+
end
|
|
125
|
+
case m
|
|
126
|
+
when "GET" then assert_equal GPL_TEXT, body
|
|
127
|
+
when "HEAD" then assert_nil body
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
Timeout.timeout(30) do # If-Modified-Since
|
|
133
|
+
%w(GET HEAD).each do |m|
|
|
134
|
+
req = "#{m} /COPYING HTTP/1.0\r\n" \
|
|
135
|
+
"If-Modified-Since: #{st.mtime.httpdate}"
|
|
136
|
+
body = check.call(req) do |head|
|
|
137
|
+
assert_match %r{\AHTTP/1\.1 304 Not Modified}, head
|
|
138
|
+
end
|
|
139
|
+
assert_nil body
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# skew the times of the gzip file, should now fail to use gzipped
|
|
144
|
+
Timeout.timeout(30) do
|
|
145
|
+
File.utime(Time.at(0), Time.at(0), gplgz)
|
|
146
|
+
|
|
147
|
+
%w(GET HEAD).each do |m|
|
|
148
|
+
req = "#{m} /COPYING HTTP/1.0\r\nAccept-Encoding: gzip"
|
|
149
|
+
body = check.call(req) do |head|
|
|
150
|
+
refute_match %r{^Content-Encoding: gzip\b}, head
|
|
151
|
+
assert_match %r{^Content-Type: text/plain\b}, head
|
|
152
|
+
assert_match %r{^Content-Length: #{st.size}\b}, head
|
|
153
|
+
end
|
|
154
|
+
case m
|
|
155
|
+
when "GET" then assert_equal GPL_TEXT, body
|
|
156
|
+
when "HEAD" then assert_nil body
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
Timeout.timeout(30) do # 404
|
|
162
|
+
%w(GET HEAD).each do |m|
|
|
163
|
+
req = "#{m} /cp-ing HTTP/1.0\r\nAccept-Encoding: gzip"
|
|
164
|
+
body = check.call(req) do |head|
|
|
165
|
+
assert_match %r{HTTP/1\.1 404 }, head
|
|
166
|
+
end
|
|
167
|
+
assert_nil body
|
|
168
|
+
end
|
|
169
|
+
body = check.call("FOO /COPYING HTTP/1.0") do |head|
|
|
170
|
+
assert_match %r{HTTP/1\.1 405 }, head
|
|
171
|
+
end
|
|
172
|
+
assert_nil body
|
|
173
|
+
end
|
|
174
|
+
ensure
|
|
175
|
+
quit_wait(pid)
|
|
176
|
+
end
|
|
177
|
+
end
|