swift-server / async-http-client

HTTP client library built on SwiftNIO

Home Page:https://swiftpackageindex.com/swift-server/async-http-client/main/documentation/asynchttpclient

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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?