Closed exander77 closed 5 months ago
Hi Alexander !
Thanks for details !
This behavior seems to be valid as you send GET, which contains "content-length: 0".
According to RFC 7230 HTTP Message Syntax and Routing: "A user agent SHOULD NOT send a Content-Length header field when the request message does not contain a payload body and the method semantics do not anticipate such a body." https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2
So, since you normally doesn't send any additional data when you do a GET request, the header Content-Length should not be set at all.
If you include a message-body, when doing a GET request, it is considered as a bad practice. RFC 7231 HTTP Semantics and Content states on this: "A payload within a GET request message has no defined semantics; sending a payload body on a GET request might cause some existing implementations to reject the request."
It would be easier with the whole script and the configuration. But it seems the response is received from the server.. In case of a response to a HEAD request, the server set the content-length but does not send the payload. It is valid and there is no issue here. But by forcing the content-length to 0, this also works because there is no payload. It is also valid. However, for a response to a GET request, the payload is sent. When you set the content-length to 0, the payload is not removed. This triggers a processing error in HAProxy when it tries to send the response to client because there is a payload while it should not.
On recent versions (>= 2.8), in this case you should have an error is logs. The termination state shoud be ID--
. On older versions, the error is also reported but it is probably reported as CD--
. In all cases, you cannot change the content-length by hand expecting HAproxy will remove the payload automatically.
Hi Alexander !
Thanks for details !
This behavior seems to be valid as you send GET, which contains "content-length: 0".
According to RFC 7230 HTTP Message Syntax and Routing: "A user agent SHOULD NOT send a Content-Length header field when the request message does not contain a payload body and the method semantics do not anticipate such a body." https://datatracker.ietf.org/doc/html/rfc7230#section-3.3.2
So, since you normally doesn't send any additional data when you do a GET request, the header Content-Length should not be set at all.
I am not sure if it relates, as GET method semantics definitely anticipates body.
It would be easier with the whole script and the configuration. But it seems the response is received from the server.. In case of a response to a HEAD request, the server set the content-length but does not send the payload. It is valid and there is no issue here. But by forcing the content-length to 0, this also works because there is no payload. It is also valid. However, for a response to a GET request, the payload is sent. When you set the content-length to 0, the payload is not removed. This triggers a processing error in HAProxy when it tries to send the response to client because there is a payload while it should not.
On recent versions (>= 2.8), in this case you should have an error is logs. The termination state shoud be
ID--
. On older versions, the error is also reported but it is probably reported asCD--
. In all cases, you cannot change the content-length by hand expecting HAproxy will remove the payload automatically.
This sounds more like it. Maybe the issue is with not removing the payload?
What I am basically doing is that I am dropping body from GET response. And the common way to do it is to set Content-Length: 0
to do it. But maybe not in haproxy?
Then my question really is, how to properly remove content body from response in haproxy.
Whole code is pretty simple:
core.register_action("report_404", { "http-res" }, function(txn)
local status = txn.sf:status()
local method = txn.sf:method()
local path = txn.get_var(txn, "txn.path")
txn:Warning("404handler: " .. path)
if method == "HEAD" and path:match("%.webp$") then
txn:Warning("404handler body cleared: " .. path)
txn.http:res_set_header("Content-Length", "0")
end
if method == "GET" and path:match("%.webp$") then
txn:Warning("404handler body cleared: " .. path)
txn.http:res_set_header("Content-Length", "0")
end
end)
The issue here is indeed about the response with a payload and a content-length
header updated to be 0. It is detected as an internal error by HAProxy. From a server it is a invalid response. But, here it is because of an internal processing, so it is detected as an internal error.
To remove the payload from a lua script, you should use a filter. It is not so easy but it may work. Unfortunately, lua filters are not available in 2.4. You should at least upgrade to 2.6 to do so. Another solution would be to catch the 404 error from the server and replace it by your own one via an http-response return
action. It is probably easier this way. For instance:
http-response return status 404 if { status 404 }
The same may also be performed via a lua script. For instance:
function report_404(txn)
local reply = txn:reply()
reply:set_status(404, "Bad request")
reply:add_header("content-length", "0")
txn:done(reply)
end
core.register_action("report_404", { "http-res" }, report_404);
local reply = txn:reply() reply:set_status(404, "Bad request") reply:add_header("content-length", "0") txn:done(reply)
This actually works splendidely:
core.register_action("report_404", { "http-res" }, function(txn)
local status = txn.sf:status()
local method = txn.sf:method()
local path = txn.get_var(txn, "txn.path")
txn:Warning("404handler: " .. path)
if method == "HEAD" and path:match("%.webp$") then
txn:Warning("404handler body cleared: " .. path)
txn.http:res_set_header("Content-Length", "0")
end
if method == "GET" and path:match("%.webp$") then
txn:Warning("404handler body cleared: " .. path)
local reply = txn:reply()
reply:set_status(404, "Not Found")
txn:done(reply)
end
end)
Ok, fine. I'm closing now.
Detailed Description of the Problem
I have a handler for 404 errors:
When I do:
Everything works fine and I get:
When I do:
Then the server just closes the connection:
No error is issued in haproxy.log.
Expected Behavior
Haproxy should return a response the same way it does for HEAD request.
Steps to Reproduce the Behavior
Content-Length: 0
.Do you have any idea what may have caused this?
No response
Do you have an idea how to solve the issue?
No response
What is your configuration?
Output of
haproxy -vv
Last Outputs and Backtraces
No response
Additional Information
No response