Skip to content

Commit d7f636c

Browse files
authored
fix/drop content length when chunked (#234)
* Make flakey test a little less flakey * Ensure Content-Length is dropped if Transfer-Encoding is specified https://tools.ietf.org/html/rfc7230#section-3.3.3
1 parent 1120af7 commit d7f636c

File tree

3 files changed

+78
-16
lines changed

3 files changed

+78
-16
lines changed

lib/resty/http.lua

+25-16
Original file line numberDiff line numberDiff line change
@@ -659,28 +659,37 @@ function _M.send_request(self, params)
659659
headers["Proxy-Authorization"] = self.http_proxy_auth
660660
end
661661

662-
-- Ensure minimal headers are set
662+
-- Ensure we have appropriate message length or encoding.
663+
do
664+
local is_chunked = transfer_encoding_is_chunked(headers)
665+
666+
if is_chunked then
667+
-- If we have both Transfer-Encoding and Content-Length we MUST
668+
-- drop the Content-Length, to help prevent request smuggling.
669+
-- https://tools.ietf.org/html/rfc7230#section-3.3.3
670+
headers["Content-Length"] = nil
663671

664-
if not headers["Content-Length"] then
665-
local body_type = type(body)
672+
elseif not headers["Content-Length"] then
673+
-- A length was not given, try to calculate one.
666674

667-
if body_type == "function" then
668-
if not transfer_encoding_is_chunked(headers) then
675+
local body_type = type(body)
676+
677+
if body_type == "function" then
669678
return nil, "Request body is a function but a length or chunked encoding is not specified"
670-
end
671679

672-
elseif body_type == "table" then
673-
local length = 0
674-
for _, v in ipairs(body) do
675-
length = length + #tostring(v)
676-
end
677-
headers["Content-Length"] = length
680+
elseif body_type == "table" then
681+
local length = 0
682+
for _, v in ipairs(body) do
683+
length = length + #tostring(v)
684+
end
685+
headers["Content-Length"] = length
678686

679-
elseif body == nil and EXPECTING_BODY[str_upper(params.method)] then
680-
headers["Content-Length"] = 0
687+
elseif body == nil and EXPECTING_BODY[str_upper(params.method)] then
688+
headers["Content-Length"] = 0
681689

682-
elseif body ~= nil then
683-
headers["Content-Length"] = #tostring(body)
690+
elseif body ~= nil then
691+
headers["Content-Length"] = #tostring(body)
692+
end
684693
end
685694
end
686695

t/02-chunked.t

+52
Original file line numberDiff line numberDiff line change
@@ -263,10 +263,62 @@ table
263263
headers["Transfer-Encoding"] = { "chunked", " ChuNkEd " }
264264
assert(http.transfer_encoding_is_chunked(headers) == true,
265265
"te set to table values containing `chunked` should return true`")
266+
267+
headers["Transfer-Encoding"] = "chunked"
268+
headers["Content-Length"] = 10
269+
assert(http.transfer_encoding_is_chunked(headers) == true,
270+
"transfer encoding should override content-length`")
271+
}
272+
}
273+
--- request
274+
GET /a
275+
--- no_error_log
276+
[error]
277+
[warn]
278+
279+
280+
=== TEST 6: Don't send Content-Length if Transfer-Encoding is specified
281+
--- http_config eval: $::HttpConfig
282+
--- config
283+
location = /a {
284+
content_by_lua_block {
285+
local httpc = require("resty.http").new()
286+
local yield = coroutine.yield
287+
288+
local uri = "http://127.0.0.1:" .. ngx.var.server_port .. "/b"
289+
290+
local res, err = assert(httpc:request_uri(uri, {
291+
body = coroutine.wrap(function()
292+
yield("3\r\n")
293+
yield("foo\r\n")
294+
295+
yield("3\r\n")
296+
yield("bar\r\n")
297+
298+
yield("0\r\n")
299+
yield("\r\n")
300+
end),
301+
headers = {
302+
["Transfer-Encoding"] = "chunked",
303+
["Content-Length"] = 42,
304+
},
305+
}))
306+
307+
ngx.say(res.body)
308+
}
309+
}
310+
location = /b {
311+
content_by_lua_block {
312+
ngx.req.read_body()
313+
ngx.say(ngx.req.get_headers()["Content-Length"])
314+
ngx.print(ngx.req.get_body_data())
266315
}
267316
}
268317
--- request
269318
GET /a
319+
--- response_body
320+
nil
321+
foobar
270322
--- no_error_log
271323
[error]
272324
[warn]

t/14-host-header.t

+1
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ Host: www.google.com
6767
local http = require "resty.http"
6868
local httpc = http.new()
6969
70+
httpc:set_timeouts(300, 1000, 1000)
7071
local res, err = httpc:request_uri("https://www.google.com:443", { ssl_verify = false })
7172
';
7273
}

0 commit comments

Comments
 (0)