diff --git a/nselib/http.lua b/nselib/http.lua
index 8ebfeb423..71e2def34 100644
--- a/nselib/http.lua
+++ b/nselib/http.lua
@@ -270,7 +270,27 @@ local function recv_header(s, partial)
return table.concat(lines), partial
end
--- Receive exactly length bytes.
+-- Receive until the connection is closed.
+local function recv_all(s, partial)
+ local parts
+
+ partial = partial or ""
+
+ parts = {partial}
+ while true do
+ local status, part = s:receive()
+ if not status then
+ break
+ else
+ parts[#parts + 1] = part
+ end
+ end
+
+ return table.concat(parts), ""
+end
+
+-- Receive exactly length bytes. Returns nil if that
+-- many aren't available.
local function recv_length(s, length, partial)
local parts, last
@@ -284,6 +304,9 @@ local function recv_length(s, length, partial)
parts[#parts + 1] = last
status, last = s:receive()
+ if not status then
+ return nil
+ end
length = length - #last
end
@@ -358,21 +381,55 @@ end
-- recv_header. The handling is sensitive to the request method
-- and the status code of the response.
local function recv_body(s, response, method, partial)
+ local connection_close, connection_keepalive
+ local version_major, version_minor
local transfer_encoding
local content_length
local err
partial = partial or ""
+ -- First check for Connection: close and Connection: keep-alive. This is
+ -- necessary to handle some servers that don't follow the protocol.
+ connection_close = false
+ connection_keepalive = false
+ if response.header.connection then
+ local offset, token
+ offset = 0
+ while true do
+ offset, token = get_token(response.header.connection, offset + 1)
+ if not offset then
+ break
+ end
+ if string.lower(token) == "close" then
+ connection_close = true
+ elseif string.lower(token) == "keep-alive" then
+ connection_keepalive = true
+ end
+ end
+ end
+
+ -- The HTTP version may also affect our decisions.
+ version_major, version_minor = string.match(response["status-line"], "^HTTP/(%d+)%.(%d+)")
+
-- See RFC 2616, section 4.4 "Message Length".
-- 1. Any response message which "MUST NOT" include a message-body (such as
-- the 1xx, 204, and 304 responses and any response to a HEAD request) is
-- always terminated by the first empty line after the header fields...
+ --
+ -- Despite the above, some servers return a body with response to a HEAD
+ -- request. So if an HTTP/1.0 server returns a response without Connection:
+ -- keep-alive, or any server returns a response with Connection: close, read
+ -- whatever's left on the socket (should be zero bytes).
if string.upper(method) == "HEAD"
or (response.status >= 100 and response.status <= 199)
or response.status == 204 or response.status == 304 then
- return "", partial
+ if connection_close or (version_major == "1" and version_minor == "0" and not connection_keepalive) then
+ return recv_all(s, partial)
+ else
+ return "", partial
+ end
end
-- 2. If a Transfer-Encoding header field (section 14.41) is present and has
@@ -412,19 +469,7 @@ local function recv_body(s, response, method, partial)
-- Case 4 is unhandled.
-- 5. By the server closing the connection.
- do
- local parts = {partial}
- while true do
- local status, part = s:receive()
- if not status then
- break
- else
- parts[#parts + 1] = part
- end
- end
-
- return table.concat(parts), ""
- end
+ return recv_all(s, partial)
end
-- Sets response["status-line"] and response.status.