1

In a Go http server, I can get POST request body. Go net/http package seems to remove GET request body. I know it is better not to use http GET with request body,but I need to handle http GET with request body. Is it possible without changing the standard lib? Please help since I don't want to switch back to C with libevent!

When a client sends a POST with request body, below code will show the body content. But when a client sends a GET with request body, there is nothing in the body.

func handler(w http.ResponseWriter, r *http.Request) {
    defer r.Body.Close()
    body, _ := ioutil.ReadAll(r.Body)

    log.Printf("body: %v", string(body))
}

func main() {
    http.HandleFunc("/", handler)
    http.ListenAndServe(":8080", nil)
}
Community
  • 1
  • 1
gosharplite
  • 101
  • 2
  • 7
  • You "**need** to handle http GET with request body" ? Really ? There's probably a problem in your design. – Denys Séguret Jul 16 '13 at 12:21
  • I can't change the protocol. I wish I could. – gosharplite Jul 16 '13 at 12:22
  • Can you post an example of the code you *want* to implement? It's hard to understand what you're asking. – elithrar Jul 16 '13 at 13:19
  • I took an admittedly quick look at Go's net/http source code, and I didn't find something like "drop the body if this is a GET". There are the expected conditions for server responses, like if method is a HEAD or if the status code is < 200, 204 or 304, I haven't seen nothing for client requests. Are you absolutely sure the client sends the body? Like checking the Request with Fiddler or something? – mna Jul 16 '13 at 14:31
  • Somebody should edit the library so it's compliant, even if it's an uncommon use case. – joshlf Jul 16 '13 at 15:38
  • I had a look at http://golang.org/src/pkg/net/http/transfer.go?s=11448:11710#L450 and it states "RFC 2616 doesn't explicitly permit nor forbid an entity-body on a GET request so we permit one if declared, but we default to 0 here (not -1 below) if there's no mention of a body." So it should work in theory. – Intermernet Jul 16 '13 at 15:49
  • Note that in your example code there you don't need to close r.Body (it's closed after the response handler completes), but you do need to check for the error on that ReadAll -- which is making two copies of arbitrary requests in RAM on your server. I'm certain you don't want that. – Dustin Jul 20 '13 at 04:10

1 Answers1

3

Most of the magic happens in transfer.go. Here's what I found that looks relevant in the fixLength func:

if !isResponse && requestMethod == "GET" {
    // RFC 2616 doesn't explicitly permit nor forbid an
    // entity-body on a GET request so we permit one if
    // declared, but we default to 0 here (not -1 below)
    // if there's no mention of a body.
    return 0, nil
}

Looks like, as long as your client is ending a Content-Length header, you're all good. If not, the library will assume there's no body on a GET request.

You're kind of off the edge of the map as your client is doing some pretty unusual/broken stuff. If you can fix the client, that's your best bet.

That said, if you have a client you need to support that's doing this wrong, you're going to have to roll some things yourself. You don't need to go all the way down to C and libevent. Simply copy the net/http package from the standard library into your project and modify it. Then change your import statements to point at your version of the library.

Alternatively, if you know that the client is not using keep-alive, you can Hijack the connection and just read whatever's left on the socket.

  • You're correct. When GET does not specify Content-Length, r.Body is an eofReader (eofReader is a non-nil io.ReadCloser that always returns EOF). – gosharplite Jul 17 '13 at 01:34