aboutsummaryrefslogtreecommitdiff
path: root/src/http.lua
diff options
context:
space:
mode:
Diffstat (limited to 'src/http.lua')
-rw-r--r--src/http.lua27
1 files changed, 21 insertions, 6 deletions
diff --git a/src/http.lua b/src/http.lua
index 259eb2b..f031c1d 100644
--- a/src/http.lua
+++ b/src/http.lua
@@ -25,6 +25,11 @@ local _M = socket.http
25_M.TIMEOUT = 60 25_M.TIMEOUT = 60
26-- user agent field sent in request 26-- user agent field sent in request
27_M.USERAGENT = socket._VERSION 27_M.USERAGENT = socket._VERSION
28-- maximum size of a single header line (also used for the status line and
29-- the chunk-size line, which carry the same shape of risk)
30_M.MAXHEADERLINE = 8192
31-- maximum total size of all header lines in a single header block
32_M.MAXHEADERSIZE = 65536
28 33
29-- supported schemes and their particulars 34-- supported schemes and their particulars
30local SCHEMES = { 35local SCHEMES = {
@@ -47,8 +52,18 @@ local SCHEMES = {
47local function receiveheaders(sock, headers) 52local function receiveheaders(sock, headers)
48 local line, name, value, err 53 local line, name, value, err
49 headers = headers or {} 54 headers = headers or {}
55 -- bounds total bytes read across all header lines, on top of the
56 -- per-line MAXHEADERLINE cap, so a peer can't exhaust memory by sending
57 -- many lines that each individually fit under MAXHEADERLINE
58 local budget = _M.MAXHEADERSIZE
59 local function recvline()
60 if budget <= 0 then return nil, "oversized" end
61 local line, err = sock:receive("*l", nil, math.min(budget, _M.MAXHEADERLINE))
62 if line then budget = budget - #line end
63 return line, err
64 end
50 -- get first line 65 -- get first line
51 line, err = sock:receive() 66 line, err = recvline()
52 if err then return nil, err end 67 if err then return nil, err end
53 -- headers go until a blank line is found 68 -- headers go until a blank line is found
54 while line ~= "" do 69 while line ~= "" do
@@ -57,12 +72,12 @@ local function receiveheaders(sock, headers)
57 if not (name and value) then return nil, "malformed response headers" end 72 if not (name and value) then return nil, "malformed response headers" end
58 name = string.lower(name) 73 name = string.lower(name)
59 -- get next line (value might be folded) 74 -- get next line (value might be folded)
60 line, err = sock:receive() 75 line, err = recvline()
61 if err then return nil, err end 76 if err then return nil, err end
62 -- unfold any folded values 77 -- unfold any folded values
63 while string.find(line, "^%s") do 78 while string.find(line, "^%s") do
64 value = value .. line 79 value = value .. line
65 line, err = sock:receive() 80 line, err = recvline()
66 if err then return nil, err end 81 if err then return nil, err end
67 end 82 end
68 -- save pair in table 83 -- save pair in table
@@ -82,7 +97,7 @@ socket.sourcet["http-chunked"] = function(sock, headers)
82 }, { 97 }, {
83 __call = function() 98 __call = function()
84 -- get chunk size, skip extension 99 -- get chunk size, skip extension
85 local line, err = sock:receive() 100 local line, err = sock:receive("*l", nil, _M.MAXHEADERLINE)
86 if err then return nil, err end 101 if err then return nil, err end
87 local size = base.tonumber(string.gsub(line, ";.*", ""), 16) 102 local size = base.tonumber(string.gsub(line, ";.*", ""), 16)
88 if not size then return nil, "invalid chunk size" end 103 if not size then return nil, "invalid chunk size" end
@@ -90,7 +105,7 @@ socket.sourcet["http-chunked"] = function(sock, headers)
90 if size > 0 then 105 if size > 0 then
91 -- if not, get chunk and skip terminating CRLF 106 -- if not, get chunk and skip terminating CRLF
92 local chunk, err, _ = sock:receive(size) 107 local chunk, err, _ = sock:receive(size)
93 if chunk then sock:receive() end 108 if chunk then sock:receive("*l", nil, _M.MAXHEADERLINE) end
94 return chunk, err 109 return chunk, err
95 else 110 else
96 -- if it was, read trailers into headers table 111 -- if it was, read trailers into headers table
@@ -167,7 +182,7 @@ function metat.__index:receivestatusline()
167 return nil, status 182 return nil, status
168 end 183 end
169 -- otherwise proceed reading a status line 184 -- otherwise proceed reading a status line
170 status = self.try(self.c:receive("*l", status)) 185 status = self.try(self.c:receive("*l", status, _M.MAXHEADERLINE))
171 local code = socket.skip(2, string.find(status, "HTTP/%d*%.%d* (%d%d%d)")) 186 local code = socket.skip(2, string.find(status, "HTTP/%d*%.%d* (%d%d%d)"))
172 return self.try(base.tonumber(code), status) 187 return self.try(base.tonumber(code), status)
173end 188end