/
test_response_header.rb
144 lines (109 loc) · 4.16 KB
/
test_response_header.rb
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
require_relative "helper"
require "puma/events"
require "net/http"
class TestResponseHeader < Minitest::Test
parallelize_me!
def setup
@host = "127.0.0.1"
@ios = []
@app = ->(env) { [200, {}, [env['rack.url_scheme']]] }
@events = Puma::Events.strings
@server = Puma::Server.new @app, @events
end
def teardown
@server.stop(true)
@ios.each { |io| io.close if io && !io.closed? }
end
def server_run(app: @app, early_hints: false)
@server.app = app
@port = (@server.add_tcp_listener @host, 0).addr[1]
@server.early_hints = true if early_hints
@server.run
end
def send_http_and_read(req)
send_http(req).read
end
def send_http(req)
new_connection << req
end
def new_connection
TCPSocket.new(@host, @port).tap {|sock| @ios << sock}
end
# The header keys must be Strings
def test_integer_key
server_run app: ->(env) { [200, { 1 => 'Boo'}, []] }
data = send_http_and_read "GET / HTTP/1.0\r\n\r\n"
assert_match(/HTTP\/1.1 500 Internal Server Error/, data)
end
# The header must respond to each
def test_nil_header
server_run app: ->(env) { [200, nil, []] }
data = send_http_and_read "GET / HTTP/1.0\r\n\r\n"
assert_match(/HTTP\/1.1 500 Internal Server Error/, data)
end
# The values of the header must be Strings
def test_integer_value
server_run app: ->(env) { [200, {'Content-Length' => 500}, []] }
data = send_http_and_read "GET / HTTP/1.0\r\n\r\n"
assert_match(/HTTP\/1.0 200 OK\r\nContent-Length: 500\r\n\r\n/, data)
end
def assert_ignore_header(name, value, opts={})
header = { name => value }
if opts[:early_hints]
app = ->(env) do
env['rack.early_hints'].call(header)
[200, {}, ['Hello']]
end
else
app = -> (env) { [200, header, ['hello']]}
end
server_run(app: app, early_hints: opts[:early_hints])
data = send_http_and_read "GET / HTTP/1.0\r\n\r\n"
refute_match("#{name}: #{value}", data)
end
# The header must not contain a Status key.
def test_status_key
assert_ignore_header("Status", "500")
end
# The header key can contain the word status.
def test_key_containing_status
server_run app: ->(env) { [200, {'Teapot-Status' => 'Boiling'}, []] }
data = send_http_and_read "GET / HTTP/1.0\r\n\r\n"
assert_match(/HTTP\/1.0 200 OK\r\nTeapot-Status: Boiling\r\n\r\n/, data)
end
# Special headers starting “rack.” are for communicating with the server, and must not be sent back to the client.
def test_rack_key
assert_ignore_header("rack.command_to_server_only", "work")
end
# The header key can still start with the word rack
def test_racket_key
server_run app: ->(env) { [200, {'Racket' => 'Bouncy'}, []] }
data = send_http_and_read "GET / HTTP/1.0\r\n\r\n"
assert_match(/HTTP\/1.0 200 OK\r\nRacket: Bouncy\r\n\r\n/, data)
end
# testing header key must conform rfc token specification
# i.e. cannot contain non-printable ASCII, DQUOTE or “(),/:;<=>?@[]{}”.
# Header keys will be set through two ways: Regular and early hints.
def test_illegal_character_in_key
assert_ignore_header("\"F\u0000o\u0025(@o}", "Boo")
end
def test_illegal_character_in_key_when_early_hints
assert_ignore_header("\"F\u0000o\u0025(@o}", "Boo", early_hints: true)
end
# testing header value can be separated by \n into line, and each line must not contain characters below 037
# Header values can be set through three ways: Regular, early hints and a special case for overriding content-length
def test_illegal_character_in_value
assert_ignore_header("X-header", "First \000Lin\037e")
end
def test_illegal_character_in_value_when_early_hints
assert_ignore_header("X-header", "First \000Lin\037e", early_hints: true)
end
def test_illegal_character_in_value_when_override_content_length
assert_ignore_header("Content-Length", "\037")
end
def test_illegal_character_in_value_when_newline
server_run app: ->(env) { [200, {'X-header' => "First\000 line\nSecond Lin\037e"}, ["Hello"]] }
data = send_http_and_read "GET / HTTP/1.0\r\n\r\n"
refute_match("X-header: First\000 line\r\nX-header: Second Lin\037e\r\n", data)
end
end