News for hackers. Stuff that matters.
Re:The problem with Go’s default HTTP handlers by mrweasel on Tuesday August 09, 2022 @11:55AM (#32399623)
It's not something I even thought about, but I guess I see the point, I just don't agree. In the HTTP handlers it makes sense that you don't have return values, because: What would you do with that value exactly?

The HTTP handler should always ensure that "something" is returned, at the very least a return code. Once something has been returned to the client, can you really argue that there's an error?

There's a point to the risk of missing return value, you just run the risk of complicating the request flow by adding it. I'd argue that most people would view the code path as having ended, once the client receives a response. The request shouldn't really continue on the server end after that. There can be background processing, go routines or something like that, but that's a bit different, as it doesn't involve the original requests-response.

Re:The problem with Go’s default HTTP handlers by jerf on Tuesday August 09, 2022 @12:03PM (#32399748)
"In the HTTP handlers it makes sense that you don't have return values, because: What would you do with that value exactly?"

I agree that the baseline net/http implementation is correct. There is no appropriate default error (return) handler that the framework could implement, at the level it lives at, that would be correct and wouldn't be limiting.

However I very often in my own Go code immediately create an abstraction that does exactly this, because it is a good default for everything I'm doing. It just isn't suitable for everything (e.g., what do you do with this in the case of a protocol upgrade to websockets? the HTTP handlers need to be able to just terminate gracefully without being forced to "return" something).

What I would say is, net/http is a little too low level to directly use. You don't need a full framework necessarily, though if you want to use one that does this more power to you. But if you understand HTTP, it doesn't take much wrapping to make net/http useful for "normal" APIs or websites. It takes a little, though.

Re:The problem with Go’s default HTTP handlers by morelisp on Tuesday August 09, 2022 @03:18PM (#32402712)
I agree with you that an error return would be superfluous and probably misused, but middleware with the default handler could be a lot more usable if you could just query a ResponseWriter's written status code.

Instead everyone tries to do it by putting in their own implementation of the ResponseWriter interface proxying back to the 'real' one, which works perfectly but with a lot of extra code about 80% of the time, sort of works but often takes some performance hit 19% of the time, and blows up utterly when it composes poorly with someone else's 1% of the time.

Re:The problem with Go’s default HTTP handlers by jerf on Tuesday August 09, 2022 @10:27PM (#32407255)
I agree totally, and it's worth highlighting part of the problem for those who don't write Go. http.ResponseWriter superficially looks relatively simple, but it has a number of optional interfaces attached to it that allow for things like "hijacking" the connection (for things like websockets, that initiate an HTTP connection but use it to transform into something else). Writing a wrapper around the easy and declared use case is not that hard, but blows up in weird ways if you don't also handle all the optional interfaces, and that gets even more fun if you start having frameworks trying to layer multiple of these together.

Go interfaces are fun and all but optional interfaces can get a bit spiky when you start trying to use the decorator pattern on them. And in Go, decorator is a fundamental pattern in the toolkit used quite frequently, not an obscure novelty to pull out once every few years.

Re:The problem with Go’s default HTTP handlers by aniforprez on Tuesday August 09, 2022 @12:35PM (#32400208)
"net/http" doesn't seem to come with a lot of great defaults that almost every web app would use such as named path parameters which you'd need to write a bunch of code and regex to extract, middleware, routing based on HTTP method etc. This is all stuff you'd have to implement anyway

I'd much prefer using something that's an abstraction of it like Gin, Chi, Echo and the like. They're fairly performative and add very little overhead on top of the standard package

Re:The problem with Go’s default HTTP handlers by biomcgary on Tuesday August 09, 2022 @01:22PM (#32400899)
I use Gin and enjoy the conveniences. Part of the reason is that it is opinionated. However, for the standard library, I think a strong, but minimal base is more important. One could argue about how extensible net/http is, but it seems reasonable as a foundation and it has been extended in a variety of opinionated (to varying degrees) ways!
Re:The problem with Go’s default HTTP handlers by gorjusborg on Tuesday August 09, 2022 @01:45PM (#32401215)
> In the HTTP handlers it makes sense that you don't have return values, because: What would you do with that value exactly?

I think that approach used by clojure's ring shows an elegant way to represent http responses https://github.com/ring-clojure/ring/wiki/Concepts#responses. They are essentially structs with the following fields:

status := number

headers := map of string->string

body := stream | string | seq | inputstream

Request handlers are handed a request struct that is similar. The handler is a function that maps a request to a response (it doesn't actually write to streams itself).

I like this style for an http library for a couple of reasons:

1. HTTP resources can be viewed as functions whose domain is the request, and range is the response. Having the abstraction match that makes for really nice code. 2. If you model the request/response structs symmetrically and expose your http client as a handler itself, you can write proxies very easily. For an example of this, see http4k (https://www.http4k.org/documentation/) (a kotlin library).

I won't argue that the net/http abstraction should change, but I do agree with the author's take on the desired shape of an http library. I'd probably just write my own abstraction on top to fill the gap.

Re:The problem with Go’s default HTTP handlers by morelisp on Tuesday August 09, 2022 @03:13PM (#32402626)
There are major performance advantages, especially once you're past HTTP/1.1, to passing a mutable response (i.e. in Go a ResponseWriter) to the handler rather than expecting the handler to return something created whole-cloth.
Re:The problem with Go’s default HTTP handlers by dap on Tuesday August 09, 2022 @04:54PM (#32404152)
What advantages are you thinking of? The obvious one is that you might want to stream the response rather than construct it in-memory at once. But as I read the parent (and as I've used similar interfaces in Rust), you don't have to have constructed the whole response with this interface. You can return something whose body is a stream.
Re:The problem with Go’s default HTTP handlers by morelisp on Tuesday August 09, 2022 @05:11PM (#32404377)
If you return something whose body is a stream you still have to construct that thing, including the stream. And if you return something whose body is a stream you didn't fill in yet, you need to create entire async thunks or threads to fill that data in. You have also gained ~nothing.
Re:The problem with Go’s default HTTP handlers by TheDong on Tuesday August 09, 2022 @06:31PM (#32405395)
> There are major performance advantages

You didn't outline the performance advantage. You've got "you need to create entire async thunks or threads to fill that data in", but that absolutely isn't true.

Let's look at how we might stream a file in response in go with the current model vs this new model, and you can point out where the performance difference is:

    // current
    func responseHandler(rw http.ResponseWriter, req *http.Request) {
        fi, err := os.OpenFile("/tmp/file")
        if err != nil {
            rw.WriteHeader(500)
            rw.Write([]byte("error opening file"))
        }
        io.Copy(rw, fi)
        fi.Close()
    }

    // returning a streaming Response object
    func responseHandler(req *http.Request) *http.HandlerResponse {
        fi, err := os.OpenFile("/tmp/file")
        if err != nil {
            return &http.HandlerResponse{Status: 500, Body: ioutil.NopCloser(bytes.NewReader([]byte("error opening file"))}
        }
        return &http.HandlerResponse{Status: 200, Body: fi}
    }

In the happy path, there's no difference in number of goroutines or anything, right? In both cases, this all runs in the goroutine or thread or whatever that the caller _already_ created for the request handler. What difference does it make if the request handler calls `io.Copy(response, body)` or if your function does, in both case it's the same goroutine/thread.

> You have also gained ~nothing.

You were the one that claimed you lose massive performance, and that's what we're asking about. The ergonomics are a separate thing from performance

Re:The problem with Go’s default HTTP handlers by morelisp on Tuesday August 09, 2022 @07:03PM (#32405752)
Now do a body that requires literally any transformation or generation at all. Say, a series of DB query results as a CSV. (Oh, and make sure to cancel work when the request disconnects!)

Or heck, just try adding some error handling from the `io.Copy` result to what you have.

Re:The problem with Go’s default HTTP handlers by suremarc on Tuesday August 09, 2022 @08:28PM (#32406478)
Just return an io.Reader that wraps the DB cursor and outputs CSV data... I fail to see what makes this so difficult. If the "mutable" approach involves passing down a mutable *http.ResponseWriter, then the dual approach of passing an io.Reader upstream is equivalent.
Re:The problem with Go’s default HTTP handlers by int_19h on Wednesday August 10, 2022 @02:03AM (#32408257)
This all is much more straightforward in languages with syntactic sugar for iterators, such as "yield" in C# and Python:

https://flask.palletsprojects.com/en/2.2.x/patterns/streamin...

Re:The problem with Go’s default HTTP handlers by morelisp on Wednesday August 10, 2022 @05:16AM (#32409482)
Love too make those async thunks!
Re:The problem with Go’s default HTTP handlers by morelisp on Wednesday August 10, 2022 @05:15AM (#32409476)
Try to write this, really. It’s such an unergonomic mess compared to for loop in the handler. It will also allocate a very complex reader that the actual version doesn’t need to.