HttpClient removes Content-Length and causes 411
sherwinzadeh opened this issue · comments
When attempting to make GET calls with Django based server, I'm getting a 411 Length Required. Looking at the code in RequestValidation.swift, it is removing Content-Length
from headers for GET
calls. This may be according to specs but it appears that some servers still require this (probably set to 0). There should be some way to override this behavior or to cause it to force Content-Length to appear in the headers.
I have worked around this by implementing my own networking code using URLSession
and the calls do work since I can control the headers exactly.
For reference, the code that is causing the problem is here:
private mutating func setTransportFraming(
method: HTTPMethod,
bodyLength: RequestBodyLength
) {
self.remove(name: "Content-Length")
self.remove(name: "Transfer-Encoding")
switch bodyLength {
case .known(0):
// if we don't have a body we might not need to send the Content-Length field
// https://tools.ietf.org/html/rfc7230#section-3.3.2
switch method {
case .GET, .HEAD, .DELETE, .CONNECT, .TRACE:
// 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.
break
default:
// A user agent SHOULD send a Content-Length in a request message when
// no Transfer-Encoding is sent and the request method defines a meaning
// for an enclosed payload body.
self.add(name: "Content-Length", value: "0")
}
case .known(let length):
self.add(name: "Content-Length", value: String(length))
case .unknown:
self.add(name: "Transfer-Encoding", value: "chunked")
}
}
Wow, that's pretty wild. This is a Django-based server? It's news to me that Django misbehaves in this way.
We'd certainly be happy to accept a patch to add a configuration option to disable the transport framing validation.
Just a little more insight, I tried adding a body using code like this (this is a Vapor project):
let headers = HTTPHeaders([
("Authorization", authorization),
("User-Agent", "Zohar/1.0"),
("Content-Type", "application/json"),
("Accept", "application/json")
])
let clientResponse = try await req.client.get(uri, headers: headers, beforeSend: { clientRequest in
clientRequest.body = ByteBuffer(string: "{}")
})
And it went from returning a 411 to returning a 400 and complaining about the body.
The odd thing is that in URLSession, it works if I don't pass Content-Length either. I wonder if URLSession is adding it? What could be the difference between how HttpClient does it and URLSession does it?