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
| | #!ruby
# Copyright (C) unicorn hackers <unicorn-public@80x24.org>
# License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
# this goes for t/integration.t We'll try to put as many tests
# in here as possible to avoid startup overhead of Ruby.
def early_hints(env, val)
env['rack.early_hints'].call('link' => val) # val may be ary or string
[ 200, {}, [ val.class.to_s ] ]
end
$orig_rack_200 = nil
def tweak_status_code
$orig_rack_200 = Rack::Utils::HTTP_STATUS_CODES[200]
Rack::Utils::HTTP_STATUS_CODES[200] = "HI"
[ 200, {}, [] ]
end
def restore_status_code
$orig_rack_200 or return [ 500, {}, [] ]
Rack::Utils::HTTP_STATUS_CODES[200] = $orig_rack_200
[ 200, {}, [] ]
end
class WriteOnClose
def each(&block)
@callback = block
end
def close
@callback.call "7\r\nGoodbye\r\n0\r\n\r\n"
end
end
def write_on_close
[ 200, { 'transfer-encoding' => 'chunked' }, WriteOnClose.new ]
end
def env_dump(env)
require 'json'
h = {}
env.each do |k,v|
case v
when String, Integer, true, false; h[k] = v
else
case k
when 'rack.version', 'rack.after_reply'; h[k] = v
end
end
end
h.to_json
end
def rack_input_tests(env)
return [ 100, {}, [] ] if /\A100-continue\z/i =~ env['HTTP_EXPECT']
cap = 16384
require 'digest/md5'
dig = Digest::MD5.new
input = env['rack.input']
case env['PATH_INFO']
when '/rack_input/size_first'; input.size
when '/rack_input/rewind_first'; input.rewind
when '/rack_input'; # OK
else
abort "bad path: #{env['PATH_INFO']}"
end
if buf = input.read(rand(cap))
begin
raise "#{buf.size} > #{cap}" if buf.size > cap
dig.update(buf)
end while input.read(rand(cap), buf)
buf.clear # remove this call if Ruby ever gets escape analysis
end
h = { 'content-type' => 'text/plain' }
if env['HTTP_TRAILER'] =~ /\bContent-MD5\b/i
cmd5_b64 = env['HTTP_CONTENT_MD5'] or return [500, {}, ['No Content-MD5']]
cmd5_bin = cmd5_b64.unpack('m')[0]
if cmd5_bin != dig.digest
h['content-length'] = cmd5_b64.size.to_s
return [ 500, h, [ cmd5_b64 ] ]
end
end
h['content-length'] = '32'
[ 200, h, [ dig.hexdigest ] ]
end
class NoArray
def each
yield "HI\n"
end
end
run(lambda do |env|
case env['REQUEST_METHOD']
when 'GET'
case env['PATH_INFO']
when '/rack-2-newline-headers'; [ 200, { 'X-R2' => "a\nb\nc" }, [] ]
when '/rack-3-array-headers'; [ 200, { 'x-r3' => %w(a b c) }, [] ]
when '/nil-header-value'; [ 200, { 'X-Nil' => nil }, [] ]
when '/unknown-status-pass-through'; [ '666 I AM THE BEAST', {}, [] ]
when '/env_dump'; [ 200, {}, [ env_dump(env) ] ]
when '/write_on_close'; write_on_close
when '/pid'; [ 200, {}, [ "#$$\n" ] ]
when '/early_hints_rack2'; early_hints(env, "r\n2")
when '/early_hints_rack3'; early_hints(env, %w(r 3))
when '/no-ary'; [ 200, {}, NoArray.new ]
else '/'; [ 200, {}, [ env_dump(env) ] ]
end # case PATH_INFO (GET)
when 'POST'
case env['PATH_INFO']
when '/tweak-status-code'; tweak_status_code
when '/restore-status-code'; restore_status_code
end # case PATH_INFO (POST)
# ...
when 'PUT'
case env['PATH_INFO']
when %r{\A/rack_input}; rack_input_tests(env)
end
end # case REQUEST_METHOD
end) # run
|