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.
Files changed (77) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/Documentation/.gitignore +5 -0
  4. data/Documentation/GNUmakefile +50 -0
  5. data/Documentation/yahns-rackup.txt +152 -0
  6. data/Documentation/yahns.txt +68 -0
  7. data/Documentation/yahns_config.txt +563 -0
  8. data/GIT-VERSION-GEN +1 -1
  9. data/GNUmakefile +14 -7
  10. data/HACKING +56 -0
  11. data/INSTALL +8 -0
  12. data/README +15 -2
  13. data/Rakefile +2 -2
  14. data/bin/yahns +1 -2
  15. data/bin/yahns-rackup +9 -0
  16. data/examples/yahns_multi.conf.rb +14 -4
  17. data/examples/yahns_rack_basic.conf.rb +17 -1
  18. data/extras/README +16 -0
  19. data/extras/autoindex.rb +151 -0
  20. data/extras/exec_cgi.rb +108 -0
  21. data/extras/proxy_pass.rb +210 -0
  22. data/extras/try_gzip_static.rb +208 -0
  23. data/lib/yahns.rb +5 -2
  24. data/lib/yahns/acceptor.rb +64 -22
  25. data/lib/yahns/cap_input.rb +2 -2
  26. data/lib/yahns/{client_expire_portable.rb → client_expire_generic.rb} +12 -11
  27. data/lib/yahns/{client_expire.rb → client_expire_tcpi.rb} +7 -6
  28. data/lib/yahns/config.rb +107 -22
  29. data/lib/yahns/daemon.rb +2 -0
  30. data/lib/yahns/fdmap.rb +28 -9
  31. data/lib/yahns/http_client.rb +123 -37
  32. data/lib/yahns/http_context.rb +21 -3
  33. data/lib/yahns/http_response.rb +80 -19
  34. data/lib/yahns/log.rb +23 -4
  35. data/lib/yahns/queue_epoll.rb +20 -9
  36. data/lib/yahns/queue_quitter.rb +16 -0
  37. data/lib/yahns/queue_quitter_pipe.rb +24 -0
  38. data/lib/yahns/rack.rb +0 -1
  39. data/lib/yahns/rackup_handler.rb +57 -0
  40. data/lib/yahns/server.rb +189 -59
  41. data/lib/yahns/server_mp.rb +43 -35
  42. data/lib/yahns/sigevent_pipe.rb +1 -0
  43. data/lib/yahns/socket_helper.rb +37 -11
  44. data/lib/yahns/stream_file.rb +14 -4
  45. data/lib/yahns/stream_input.rb +13 -7
  46. data/lib/yahns/tcp_server.rb +7 -0
  47. data/lib/yahns/tmpio.rb +10 -3
  48. data/lib/yahns/unix_server.rb +7 -0
  49. data/lib/yahns/wbuf.rb +19 -2
  50. data/lib/yahns/wbuf_common.rb +10 -3
  51. data/lib/yahns/wbuf_str.rb +24 -0
  52. data/lib/yahns/worker.rb +5 -26
  53. data/test/helper.rb +15 -5
  54. data/test/server_helper.rb +37 -1
  55. data/test/test_bin.rb +17 -8
  56. data/test/test_buffer_tmpdir.rb +103 -0
  57. data/test/test_client_expire.rb +71 -35
  58. data/test/test_client_max_body_size.rb +5 -13
  59. data/test/test_config.rb +1 -1
  60. data/test/test_expect_100.rb +176 -0
  61. data/test/test_extras_autoindex.rb +53 -0
  62. data/test/test_extras_exec_cgi.rb +81 -0
  63. data/test/test_extras_exec_cgi.sh +35 -0
  64. data/test/test_extras_try_gzip_static.rb +177 -0
  65. data/test/test_input.rb +128 -0
  66. data/test/test_mt_accept.rb +48 -0
  67. data/test/test_output_buffering.rb +90 -63
  68. data/test/test_rack.rb +1 -1
  69. data/test/test_rack_hijack.rb +2 -6
  70. data/test/test_reopen_logs.rb +2 -8
  71. data/test/test_serve_static.rb +104 -8
  72. data/test/test_server.rb +448 -73
  73. data/test/test_stream_file.rb +1 -1
  74. data/test/test_unix_socket.rb +72 -0
  75. data/test/test_wbuf.rb +20 -17
  76. data/yahns.gemspec +3 -0
  77. 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 = TCPSocket.new(host, port)
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 = TCPSocket.new(host, port)
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 = TCPSocket.new(host, port)
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 = TCPSocket.new(host, port)
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
@@ -4,7 +4,7 @@ require_relative 'helper'
4
4
  require 'rack/lobster'
5
5
 
6
6
  class TestConfig < Testcase
7
- parallelize_me!
7
+ ENV["N"].to_i > 1 and parallelize_me!
8
8
 
9
9
  def test_initialize
10
10
  cfg = Yahns::Config.new
@@ -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