1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
| | # Copyright (C) 2013-2016 all contributors <yahns-public@yhbt.net>
# License: GPL-3.0+ (https://www.gnu.org/licenses/gpl-3.0.txt)
# frozen_string_literal: true
require_relative 'server_helper'
require 'digest/md5'
class TestInput < Testcase
ENV["N"].to_i > 1 and parallelize_me!
include ServerHelper
alias setup server_helper_setup
alias teardown server_helper_teardown
MD5 = lambda do |e|
input = e["rack.input"]
tmp = e["rack.tempfiles"]
case input
when StringIO, Yahns::StreamInput
abort "unexpected tempfiles" if tmp && tmp.include?(input)
when Yahns::TmpIO
abort "rack.tempfiles missing" unless tmp
abort "rack.tempfiles missing rack.input" unless tmp.include?(input)
else
abort "unrecognized input type: #{input.class}"
end
buf = ''.dup
md5 = Digest::MD5.new
while input.read(16384, buf)
md5 << buf
end
body = md5.hexdigest
h = {
"Content-Length" => body.size.to_s,
"Content-Type" => 'text/plain',
"X-Input-Class" => input.class.to_s,
}
[ 200, h, [body] ]
end
def test_input_timeout_lazybuffer
stream_input_timeout(:lazy)
end
def test_input_timeout_nobuffer
stream_input_timeout(false)
end
def stream_input_timeout(ibtype)
err, cfg, host, port = @err, Yahns::Config.new, @srv.addr[3], @srv.addr[1]
cfg.instance_eval do
GTL.synchronize do
app(:rack, MD5) do
listen "#{host}:#{port}"
input_buffering ibtype
client_timeout 1
end
end
stderr_path err.path
end
pid = mkserver(cfg)
c = get_tcp_client(host, port)
c.write "PUT / HTTP/1.1\r\nContent-Length: 666\r\n\r\n"
assert_equal c, c.wait(6)
Timeout.timeout(30) { assert_match %r{HTTP/1\.1 408 }, c.read }
c.close
ensure
quit_wait(pid)
end
def input_server(ru, ibtype)
err, cfg, host, port = @err, Yahns::Config.new, @srv.addr[3], @srv.addr[1]
cfg.instance_eval do
GTL.synchronize do
app(:rack, ru) { listen "#{host}:#{port}"; input_buffering ibtype }
end
stderr_path err.path
end
pid = mkserver(cfg)
[ host, port, pid ]
end
def test_big_buffer_true
host, port, pid = input_server(MD5, true)
c = get_tcp_client(host, port)
buf = 'hello'
c.write "PUT / HTTP/1.0\r\nContent-Length: 5\r\n\r\n#{buf}"
head, body = c.read.split(/\r\n\r\n/)
assert_match %r{^X-Input-Class: StringIO\r\n}, head
assert_equal Digest::MD5.hexdigest(buf), body
c.close
c = get_tcp_client(host, port)
buf = 'hello' * 10000
c.write "PUT / HTTP/1.0\r\nContent-Length: 50000\r\n\r\n#{buf}"
head, body = c.read.split(/\r\n\r\n/)
# TODO: shouldn't need CapInput with known Content-Length...
assert_match %r{^X-Input-Class: Yahns::(CapInput|TmpIO)\r\n}, head
assert_equal Digest::MD5.hexdigest(buf), body
c.close
c = get_tcp_client(host, port)
c.write "PUT / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n"
c.write "Transfer-Encoding: chunked\r\n\r\n"
c.write "#{50000.to_s(16)}\r\n#{buf}\r\n0\r\n\r\n"
head, body = c.read.split(/\r\n\r\n/)
assert_match %r{^X-Input-Class: Yahns::CapInput\r\n}, head
assert_equal Digest::MD5.hexdigest(buf), body
c.close
ensure
quit_wait(pid)
end
def test_read_negative_lazy; _read_neg(:lazy); end
def test_read_negative_nobuffer; _read_neg(false); end
def _read_neg(ibtype)
ru = lambda do |env|
rv = []
input = env["rack.input"]
begin
input.read(-1)
rescue => e
rv << e.class.to_s
end
rv << input.read
rv << input.read(1).nil?
rv = rv.join(",")
h = { "Content-Length" => rv.size.to_s }
[ 200, h, [ rv ] ]
end
host, port, pid = input_server(ru, ibtype)
c = get_tcp_client(host, port)
c.write "PUT / HTTP/1.0\r\nContent-Length: 5\r\n\r\nhello"
assert_equal c, c.wait(30)
head, body = c.read.split(/\r\n\r\n/)
assert_match %r{ 200 OK}, head
exc, full, final = body.split(/,/)
assert_equal "hello", full
assert_equal "ArgumentError", exc
assert_equal true.to_s, final
c.close
ensure
quit_wait(pid)
end
def test_gets_lazy; _gets(:lazy); end
def test_gets_nobuffer; _gets(false); end
def _gets(ibtype)
in_join = lambda do |input|
rv = []
while line = input.gets
rv << line
end
rv.join(",")
end
ru = lambda do |env|
rv = in_join.call(env["rack.input"])
h = { "Content-Length" => rv.size.to_s }
[ 200, h, [ rv ] ]
end
host, port, pid = input_server(ru, ibtype)
c = get_tcp_client(host, port)
buf = "a\nb\n\n"
c.write "PUT / HTTP/1.0\r\nContent-Length: 5\r\n\r\n#{buf}"
assert_equal c, c.wait(30)
head, body = c.read.split(/\r\n\r\n/)
assert_match %r{ 200 OK}, head
expect = in_join.call(StringIO.new(buf))
assert_equal expect, body
c.close
ensure
quit_wait(pid)
end
end
|