Skip to content

Commit bb5c636

Browse files
committed
Import some HTTP tests
1 parent 8d24eb4 commit bb5c636

2 files changed

Lines changed: 383 additions & 0 deletions

File tree

Lines changed: 296 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,296 @@
1+
--- @type GLuaTest_TestGroup
2+
return {
3+
groupName = "Global:HTTP",
4+
cases = {
5+
{
6+
name = "Null bytes in response body are kept",
7+
async = true,
8+
timeout = 1,
9+
func = function()
10+
HTTP({
11+
url = "http://127.0.0.1:5000/response_null_byte_in_body",
12+
success = function(code, body, headers)
13+
expect(body).to.equal("Hello World\0!")
14+
done()
15+
end,
16+
failed = function(err)
17+
fail("HTTP request failed: " .. err)
18+
end
19+
})
20+
end
21+
},
22+
{
23+
name = "Set-Cookie headers with same name are concatenated",
24+
async = true,
25+
timeout = 1,
26+
func = function()
27+
HTTP({
28+
url = "http://127.0.0.1:5000/response_multiple_cookies",
29+
success = function(code, body, headers)
30+
-- Note: RFC 6265 states that origin servers should not fold Set-Cookie
31+
-- headers due to semantics changes. This does not just apply to
32+
-- Cookies with Expires fields (as tested below).
33+
34+
-- Header order is nondeterministic, so we need to test both combinations.
35+
expect((headers["Set-Cookie"] == "CookieA=1,CookieB=2") or (headers["Set-Cookie"] == "CookieB=2,CookieA=1")).to.beTrue()
36+
done()
37+
end,
38+
failed = function(err)
39+
fail("HTTP request failed: " .. err)
40+
end
41+
})
42+
end
43+
},
44+
{
45+
name = "Set-Cookie headers with same name are concatenated even if invalid",
46+
async = true,
47+
timeout = 1,
48+
func = function()
49+
HTTP({
50+
url = "http://127.0.0.1:5000/response_multiple_cookies_with_expires",
51+
success = function(code, body, headers)
52+
53+
-- Header order is nondeterministic, so we need to test both combinations.
54+
local cookie_a = "CookieA=1; Expires=Sat, 02 Feb 2002 12:17:00 GMT"
55+
local cookie_b = "CookieB=2; Expires=Fri, 21 Jul 2023 13:36:35 GMT"
56+
expect((headers["Set-Cookie"] == (cookie_a .. "," .. cookie_b)) or (headers["Set-Cookie"] == (cookie_b .. "," .. cookie_a))).to.beTrue()
57+
done()
58+
end,
59+
failed = function(err)
60+
fail("HTTP request failed: " .. err)
61+
end
62+
})
63+
end
64+
},
65+
{
66+
name = "Headers with same name are concatenated",
67+
async = true,
68+
timeout = 1,
69+
func = function()
70+
HTTP({
71+
url = "http://127.0.0.1:5000/response_multiple_warning",
72+
success = function(code, body, headers)
73+
-- Header order is nondeterministic, so we need to test both combinations.
74+
local warning_a = "199 - Warning1"
75+
local warning_b = "199 - Warning2"
76+
expect((headers["Warning"] == (warning_a .. "," .. warning_b)) or (headers["Warning"] == (warning_b .. "," .. warning_a))).to.beTrue()
77+
done()
78+
end,
79+
failed = function(err)
80+
fail("HTTP request failed: " .. err)
81+
end
82+
})
83+
end
84+
},
85+
{
86+
name = "Redirects are followed",
87+
async = true,
88+
timeout = 1,
89+
func = function()
90+
HTTP({
91+
url = "http://127.0.0.1:5000/response_redirect",
92+
success = function(code, body, headers)
93+
expect(code).to.equal(200)
94+
expect(body).to.equal("Redirected!")
95+
96+
-- Let's make sure that headers from the redirection request are discarded.
97+
expect(headers["Location"]).to.beNil()
98+
done()
99+
end,
100+
failed = function(err)
101+
fail("HTTP request failed: " .. err)
102+
end
103+
})
104+
end
105+
},
106+
{
107+
name = "Content-Type for GET request is unset by default",
108+
async = true,
109+
timeout = 1,
110+
func = function()
111+
HTTP({
112+
method = "GET",
113+
url = "http://127.0.0.1:5000/echo_content_type",
114+
success = function(code, body, headers)
115+
expect(code).to.equal(404)
116+
done()
117+
end,
118+
failed = function(err)
119+
fail("HTTP request failed: " .. err)
120+
end
121+
})
122+
end
123+
},
124+
{
125+
name = "Content-Type for POST request defaults to urlencoded form",
126+
async = true,
127+
timeout = 1,
128+
func = function()
129+
HTTP({
130+
method = "POST",
131+
url = "http://127.0.0.1:5000/echo_content_type",
132+
success = function(code, body, headers)
133+
expect(body).to.equal("application/x-www-form-urlencoded")
134+
done()
135+
end,
136+
failed = function(err)
137+
fail("HTTP request failed: " .. err)
138+
end
139+
})
140+
end
141+
},
142+
{
143+
name = "Content-Type for POST request with body defaults to plaintext",
144+
async = true,
145+
timeout = 1,
146+
func = function()
147+
HTTP({
148+
method = "POST",
149+
url = "http://127.0.0.1:5000/echo_content_type",
150+
body = "Hello world!",
151+
success = function(code, body, headers)
152+
expect(body).to.equal("text/plain; charset=utf-8")
153+
done()
154+
end,
155+
failed = function(err)
156+
fail("HTTP request failed: " .. err)
157+
end
158+
})
159+
end
160+
},
161+
{
162+
name = "Content-Type for POST request with header override sticks",
163+
async = true,
164+
timeout = 1,
165+
func = function()
166+
HTTP({
167+
method = "POST",
168+
url = "http://127.0.0.1:5000/echo_content_type",
169+
headers = {
170+
["Content-Type"] = "application/octet-stream",
171+
},
172+
success = function(code, body, headers)
173+
expect(body).to.equal("application/octet-stream")
174+
done()
175+
end,
176+
failed = function(err)
177+
fail("HTTP request failed: " .. err)
178+
end
179+
})
180+
end
181+
},
182+
{
183+
name = "Content-Type for POST request with body and header override sticks",
184+
async = true,
185+
timeout = 1,
186+
func = function()
187+
HTTP({
188+
method = "POST",
189+
url = "http://127.0.0.1:5000/echo_content_type",
190+
headers = {
191+
["Content-Type"] = "application/octet-stream",
192+
},
193+
body = "Hello world!",
194+
success = function(code, body, headers)
195+
expect(body).to.equal("application/octet-stream")
196+
done()
197+
end,
198+
failed = function(err)
199+
fail("HTTP request failed: " .. err)
200+
end
201+
})
202+
end
203+
},
204+
{
205+
name = "Request methods are checked and translated",
206+
async = true,
207+
timeout = 1,
208+
func = function()
209+
-- We have to check multiple async requests, so we effectively implement something like a semaphore here.
210+
-- We initialize to 1 and decrement once after all requests have been queued so we can reliably check at all exits.
211+
local pending_requests = 1
212+
213+
local function on_request_done()
214+
pending_requests = pending_requests - 1
215+
if pending_requests == 0 then
216+
done()
217+
end
218+
end
219+
220+
local function test_method(method_in, method_out)
221+
pending_requests = pending_requests + 1
222+
223+
local queued = HTTP({
224+
method = method_in,
225+
url = "http://127.0.0.1:5000/echo_method",
226+
success = function(code, body, headers)
227+
expect(body).to.equal(method_out)
228+
on_request_done()
229+
end,
230+
failed = function(err)
231+
expect(err).to.equal("invalid method")
232+
on_request_done()
233+
end,
234+
})
235+
expect(queued).to.beTrue()
236+
end
237+
238+
-- An unset method should default to GET
239+
test_method(nil, "GET")
240+
241+
-- Request methods noted as supported in Structures/HTTPRequest
242+
test_method("get", "GET")
243+
test_method("GET", "GET")
244+
test_method("post", "POST")
245+
test_method("POST", "POST")
246+
test_method("put", "PUT")
247+
test_method("PUT", "PUT")
248+
test_method("delete", "DELETE")
249+
test_method("DELETE", "DELETE")
250+
test_method("patch", "PATCH")
251+
test_method("PATCH", "PATCH")
252+
test_method("options", "OPTIONS")
253+
test_method("OPTIONS", "OPTIONS")
254+
255+
-- HEAD is also one of those, but we don't get back the response body.
256+
test_method("head", "")
257+
test_method("HEAD", "")
258+
259+
-- Other request methods defined by RFC 9110, those are unsupported.
260+
test_method("trace", nil)
261+
test_method("TRACE", nil)
262+
test_method("connect", nil)
263+
test_method("CONNECT", nil)
264+
265+
-- All requests scheduled, check just in case the scheduler beat us every time.
266+
-- That said, the way GLuaTest runs tests should make this impossible.
267+
on_request_done()
268+
end
269+
},
270+
{
271+
name = "system.HTTPDisabled trick works",
272+
func = function()
273+
expect(HTTP({})).to.beTrue()
274+
end
275+
},
276+
{
277+
name = "Missing HTTPRequest throws error",
278+
func = function()
279+
expect(HTTP).to.err()
280+
end
281+
},
282+
{
283+
name = "Unset URL calls failure handler",
284+
async = true,
285+
timeout = 1,
286+
func = function()
287+
HTTP({
288+
failed = function(err)
289+
expect(err).to.equal("invalid url or GET parameters")
290+
done()
291+
end,
292+
})
293+
end
294+
},
295+
}
296+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
#!/usr/bin/env python3
2+
3+
import flask
4+
5+
app = flask.Flask(__name__)
6+
7+
8+
@app.route("/", methods=["GET"])
9+
def response_normal():
10+
return b"Hello World!", 200
11+
12+
13+
@app.route("/echo_content_type", methods=["GET", "POST"])
14+
def echo_content_type():
15+
if "Content-Type" not in flask.request.headers:
16+
return b"", 404
17+
18+
return flask.request.headers["Content-Type"], 200
19+
20+
21+
@app.route("/echo_method", methods=["GET", "HEAD", "OPTIONS", "TRACE", "PUT", "DELETE", "POST", "PATCH", "CONNECT"])
22+
def echo_method():
23+
return flask.request.method, 200
24+
25+
26+
@app.route("/response_null_byte_in_body", methods=["GET"])
27+
def response_null_byte_in_body():
28+
return b"Hello World\0!", 200
29+
30+
31+
@app.route("/response_multiple_cookies", methods=["GET"])
32+
def response_multiple_cookies():
33+
return (
34+
b"Hello World!",
35+
200,
36+
{
37+
"Set-Cookie": [
38+
"CookieA=1",
39+
"CookieB=2",
40+
],
41+
},
42+
)
43+
44+
45+
@app.route("/response_multiple_cookies_with_expires", methods=["GET"])
46+
def response_multiple_cookies_with_expires():
47+
return (
48+
b"Hello World!",
49+
200,
50+
{
51+
"Set-Cookie": [
52+
"CookieA=1; Expires=Sat, 02 Feb 2002 12:17:00 GMT",
53+
"CookieB=2; Expires=Fri, 21 Jul 2023 13:36:35 GMT",
54+
],
55+
},
56+
)
57+
58+
59+
@app.route("/response_multiple_warning", methods=["GET"])
60+
def response_multiple_warning():
61+
return (
62+
b"Hello World!",
63+
200,
64+
{
65+
"Warning": [
66+
"199 - Warning1",
67+
"199 - Warning2",
68+
],
69+
},
70+
)
71+
72+
@app.route("/response_redirect", methods=["GET"])
73+
def response_redirect():
74+
return (
75+
b"Redirecting...",
76+
307,
77+
{
78+
"Location": "/response_redirect_landing",
79+
},
80+
)
81+
82+
@app.route("/response_redirect_landing", methods=["GET"])
83+
def response_redirect_landing():
84+
return b"Redirected!", 200
85+
86+
if __name__ == "__main__":
87+
app.run(debug=False, host="0.0.0.0")

0 commit comments

Comments
 (0)