diff options
| author | Caleb Maclennan <caleb@alerque.com> | 2026-08-31 11:04:37 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-08-31 11:04:37 +0300 |
| commit | 01162f05408ac4206be15842ef91048c63c23869 (patch) | |
| tree | 2b7c8a2916cf8083b557a897c7d258e00a9c4fa3 /test | |
| parent | 5afe174eeffc7f3e3b9ef80b5871237620c68415 (diff) | |
| parent | c84e79f6d1ae735bfa191694ec6a62eb8231166d (diff) | |
| download | luasocket-01162f05408ac4206be15842ef91048c63c23869.tar.gz luasocket-01162f05408ac4206be15842ef91048c63c23869.tar.bz2 luasocket-01162f05408ac4206be15842ef91048c63c23869.zip | |
Merge pull request #467 from lunarmodules/fix/http-size-protect
Fix/http size protect
Diffstat (limited to 'test')
| -rw-r--r-- | test/maxsize_http.lua | 110 | ||||
| -rw-r--r-- | test/maxsize_tp.lua | 90 |
2 files changed, 200 insertions, 0 deletions
diff --git a/test/maxsize_http.lua b/test/maxsize_http.lua new file mode 100644 index 0000000..3c0dc74 --- /dev/null +++ b/test/maxsize_http.lua | |||
| @@ -0,0 +1,110 @@ | |||
| 1 | -- Exercises the maxsize caps added to socket.http's line-based receive() | ||
| 2 | -- calls (see PLAN-RECEIVE-MAXSIZE.md). Self-contained: uses a single | ||
| 3 | -- process with a real TCP loopback connection, so it needs no paired | ||
| 4 | -- server script. | ||
| 5 | local socket = require "socket" | ||
| 6 | local http = require "socket.http" | ||
| 7 | local ltn12 = require "ltn12" | ||
| 8 | |||
| 9 | local host = "127.0.0.1" | ||
| 10 | |||
| 11 | -- connects `open_fn(host, port)` to a freshly bound loopback listener and | ||
| 12 | -- returns the client-side object it produced plus the server-side raw | ||
| 13 | -- socket accepted for that connection. | ||
| 14 | local function new_pair(open_fn) | ||
| 15 | local server = assert(socket.bind(host, 0)) | ||
| 16 | local ip, port = server:getsockname() | ||
| 17 | local client = assert(open_fn(ip, port)) | ||
| 18 | local srv = assert(server:accept()) | ||
| 19 | server:close() | ||
| 20 | return client, srv | ||
| 21 | end | ||
| 22 | |||
| 23 | local failures = 0 | ||
| 24 | |||
| 25 | local function check(ok, msg) | ||
| 26 | if ok then | ||
| 27 | print("PASS: " .. msg) | ||
| 28 | else | ||
| 29 | failures = failures + 1 | ||
| 30 | print("FAIL: " .. msg) | ||
| 31 | end | ||
| 32 | end | ||
| 33 | |||
| 34 | local function http_open(ip, port) | ||
| 35 | return http.open(ip, port, socket.tcp) | ||
| 36 | end | ||
| 37 | |||
| 38 | do -- sanity: normal status line + headers still parse | ||
| 39 | http.MAXHEADERLINE, http.MAXHEADERSIZE = 8192, 65536 | ||
| 40 | local h, srv = new_pair(http_open) | ||
| 41 | srv:send("HTTP/1.1 200 OK\r\nContent-Length: 0\r\n\r\n") | ||
| 42 | local code = socket.protect(function() return h:receivestatusline() end)() | ||
| 43 | local headers = socket.protect(function() return h:receiveheaders() end)() | ||
| 44 | check(code == 200 and headers and headers["content-length"] == "0", | ||
| 45 | "http: normal status line + headers parse") | ||
| 46 | h:close(); srv:close() | ||
| 47 | end | ||
| 48 | |||
| 49 | do -- status line over MAXHEADERLINE is rejected | ||
| 50 | http.MAXHEADERLINE, http.MAXHEADERSIZE = 16, 1024 | ||
| 51 | local h, srv = new_pair(http_open) | ||
| 52 | srv:send("HTTP/1.1 200 " .. string.rep("x", 40) .. "\r\n") | ||
| 53 | local code, err = socket.protect(function() return h:receivestatusline() end)() | ||
| 54 | check(code == nil and err == "oversized", | ||
| 55 | "http: status line over MAXHEADERLINE -> oversized") | ||
| 56 | h:close(); srv:close() | ||
| 57 | end | ||
| 58 | |||
| 59 | do -- a single header line over MAXHEADERLINE is rejected | ||
| 60 | http.MAXHEADERLINE, http.MAXHEADERSIZE = 32, 1024 | ||
| 61 | local h, srv = new_pair(http_open) | ||
| 62 | srv:send("HTTP/1.1 200 OK\r\n") | ||
| 63 | assert(socket.protect(function() return h:receivestatusline() end)() == 200) | ||
| 64 | srv:send("X-Foo: " .. string.rep("y", 60) .. "\r\n\r\n") | ||
| 65 | local headers, err = socket.protect(function() return h:receiveheaders() end)() | ||
| 66 | check(headers == nil and err == "oversized", | ||
| 67 | "http: single header line over MAXHEADERLINE -> oversized") | ||
| 68 | h:close(); srv:close() | ||
| 69 | end | ||
| 70 | |||
| 71 | do -- each header line individually fits MAXHEADERLINE, but the total exceeds MAXHEADERSIZE | ||
| 72 | http.MAXHEADERLINE, http.MAXHEADERSIZE = 32, 40 | ||
| 73 | local h, srv = new_pair(http_open) | ||
| 74 | srv:send("HTTP/1.1 200 OK\r\n") | ||
| 75 | assert(socket.protect(function() return h:receivestatusline() end)() == 200) | ||
| 76 | -- each header line is ~23 bytes, individually under MAXHEADERLINE(32) | ||
| 77 | srv:send("A: 111111111111111111\r\n") | ||
| 78 | srv:send("B: 222222222222222222\r\n") | ||
| 79 | local headers, err = socket.protect(function() return h:receiveheaders() end)() | ||
| 80 | check(headers == nil and err == "oversized", | ||
| 81 | "http: total headers over MAXHEADERSIZE -> oversized (no single line over MAXHEADERLINE)") | ||
| 82 | h:close(); srv:close() | ||
| 83 | end | ||
| 84 | |||
| 85 | do -- chunk-size line over MAXHEADERLINE is rejected | ||
| 86 | http.MAXHEADERLINE, http.MAXHEADERSIZE = 32, 1024 | ||
| 87 | local h, srv = new_pair(http_open) | ||
| 88 | srv:send("HTTP/1.1 200 OK\r\n") | ||
| 89 | assert(socket.protect(function() return h:receivestatusline() end)() == 200) | ||
| 90 | srv:send("Transfer-Encoding: chunked\r\n\r\n") | ||
| 91 | local headers = assert(socket.protect(function() return h:receiveheaders() end)()) | ||
| 92 | srv:send(string.rep("f", 40) .. "\r\n") -- oversized chunk-size line | ||
| 93 | local t = {} | ||
| 94 | local ok, err = socket.protect(function() | ||
| 95 | return h:receivebody(headers, (ltn12.sink.table(t))) | ||
| 96 | end)() | ||
| 97 | check(ok == nil and err == "oversized", | ||
| 98 | "http: chunk-size line over MAXHEADERLINE -> oversized") | ||
| 99 | h:close(); srv:close() | ||
| 100 | end | ||
| 101 | |||
| 102 | http.MAXHEADERLINE, http.MAXHEADERSIZE = 8192, 65536 | ||
| 103 | |||
| 104 | if failures == 0 then | ||
| 105 | print("All http maxsize tests passed") | ||
| 106 | os.exit(0) | ||
| 107 | else | ||
| 108 | print(failures .. " http maxsize test(s) failed") | ||
| 109 | os.exit(1) | ||
| 110 | end | ||
diff --git a/test/maxsize_tp.lua b/test/maxsize_tp.lua new file mode 100644 index 0000000..57ce9bc --- /dev/null +++ b/test/maxsize_tp.lua | |||
| @@ -0,0 +1,90 @@ | |||
| 1 | -- Exercises the maxsize caps added to socket.tp's line-based receive() | ||
| 2 | -- calls (see PLAN-RECEIVE-MAXSIZE.md). socket.tp is the shared control | ||
| 3 | -- channel underneath both socket.ftp and socket.smtp, so this covers both. | ||
| 4 | -- Self-contained: uses a single process with a real TCP loopback | ||
| 5 | -- connection, so it needs no paired server script. | ||
| 6 | local socket = require "socket" | ||
| 7 | local tp = require "socket.tp" | ||
| 8 | |||
| 9 | local host = "127.0.0.1" | ||
| 10 | |||
| 11 | -- connects `open_fn(host, port)` to a freshly bound loopback listener and | ||
| 12 | -- returns the client-side object it produced plus the server-side raw | ||
| 13 | -- socket accepted for that connection. | ||
| 14 | local function new_pair(open_fn) | ||
| 15 | local server = assert(socket.bind(host, 0)) | ||
| 16 | local ip, port = server:getsockname() | ||
| 17 | local client = assert(open_fn(ip, port)) | ||
| 18 | local srv = assert(server:accept()) | ||
| 19 | server:close() | ||
| 20 | return client, srv | ||
| 21 | end | ||
| 22 | |||
| 23 | local failures = 0 | ||
| 24 | |||
| 25 | local function check(ok, msg) | ||
| 26 | if ok then | ||
| 27 | print("PASS: " .. msg) | ||
| 28 | else | ||
| 29 | failures = failures + 1 | ||
| 30 | print("FAIL: " .. msg) | ||
| 31 | end | ||
| 32 | end | ||
| 33 | |||
| 34 | local function tp_open(ip, port) | ||
| 35 | return tp.connect(ip, port, 5) | ||
| 36 | end | ||
| 37 | |||
| 38 | do -- sanity: normal single-line reply still parses | ||
| 39 | tp.MAXLINE, tp.MAXREPLY = 8192, 65536 | ||
| 40 | local c, srv = new_pair(tp_open) | ||
| 41 | srv:send("230 logged in\r\n") | ||
| 42 | local code, reply = c:check("2..") | ||
| 43 | check(code == 230 and reply == "230 logged in", | ||
| 44 | "tp: normal single-line reply parses") | ||
| 45 | c:close(); srv:close() | ||
| 46 | end | ||
| 47 | |||
| 48 | do -- sanity: normal multiline reply still parses | ||
| 49 | tp.MAXLINE, tp.MAXREPLY = 8192, 65536 | ||
| 50 | local c, srv = new_pair(tp_open) | ||
| 51 | srv:send("214-first line\r\n214-second line\r\n214 done\r\n") | ||
| 52 | local code, reply = c:check("2..") | ||
| 53 | check(code == 214 and reply == "214-first line\n214-second line\n214 done", | ||
| 54 | "tp: normal multiline reply parses") | ||
| 55 | c:close(); srv:close() | ||
| 56 | end | ||
| 57 | |||
| 58 | do -- a single reply line over MAXLINE is rejected | ||
| 59 | tp.MAXLINE, tp.MAXREPLY = 8, 65536 | ||
| 60 | local c, srv = new_pair(tp_open) | ||
| 61 | srv:send("230 this line is way over the line cap\r\n") | ||
| 62 | local code, err = c:check("2..") | ||
| 63 | check(code == nil and err == "oversized", | ||
| 64 | "tp: single line over MAXLINE -> oversized") | ||
| 65 | c:close(); srv:close() | ||
| 66 | end | ||
| 67 | |||
| 68 | do -- each line individually fits MAXLINE, but the reply total exceeds MAXREPLY | ||
| 69 | tp.MAXLINE, tp.MAXREPLY = 16, 20 | ||
| 70 | local c, srv = new_pair(tp_open) | ||
| 71 | -- first line: 14 payload bytes, under both MAXLINE(16) and MAXREPLY(20) | ||
| 72 | srv:send("123-aaaaaaaaaa\r\n") | ||
| 73 | -- second line: another 14 payload bytes, individually under MAXLINE(16), | ||
| 74 | -- but only 6 bytes remain in the MAXREPLY(20) budget | ||
| 75 | srv:send("123-bbbbbbbbbb\r\n") | ||
| 76 | local code, err = c:check("2..") | ||
| 77 | check(code == nil and err == "oversized", | ||
| 78 | "tp: multiline reply over MAXREPLY -> oversized (no single line over MAXLINE)") | ||
| 79 | c:close(); srv:close() | ||
| 80 | end | ||
| 81 | |||
| 82 | tp.MAXLINE, tp.MAXREPLY = 8192, 65536 | ||
| 83 | |||
| 84 | if failures == 0 then | ||
| 85 | print("All tp maxsize tests passed") | ||
| 86 | os.exit(0) | ||
| 87 | else | ||
| 88 | print(failures .. " tp maxsize test(s) failed") | ||
| 89 | os.exit(1) | ||
| 90 | end | ||
