Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automatically drain the body on close? #448

Open
rw-access opened this issue Mar 14, 2022 · 1 comment
Open

Automatically drain the body on close? #448

rw-access opened this issue Mar 14, 2022 · 1 comment

Comments

@rw-access
Copy link

rw-access commented Mar 14, 2022

I just started using the module, and was a little surprised by the drain + close behavior.

I completely understand the need to always require res.Body.Close(), and that's pretty standard practice for Go. However, what seems a little unusual to me is that Close doesn't automatically do this for you.

I understand that there are some restrictions and that it's important to always read it, but it still looks a bit like a footgun. I want to remove this risk entirely from my codebase, since it's unlikely this be remembered at every call site (and frankly, it's too cumbersome). However, I do think it's more reasonable to expect defer resp.Body.Close() since that's consistent with every IO based library out there.

Two ways that first come to mind, would just need to make an io.Reader wrapper that can automatically drain itself on close:

type autodrainingReader struct {
	io.ReadCloser
}

func (a *autodrainingReader) Close() error {
	_ = io.Copy(ioutil.Discard, a.ReadCloser)
	return a.ReadCloser.Close()
}

// might not even be needed, looks like this is just for websockets
type autodrainingReadWriter struct {
	io.ReadWriteCloser
}

func (a *autodrainingReadWriter) Close() error {
	_ = io.Copy(ioutil.Discard, a.ReadWriteCloser)
	return a.ReadWriteCloser.Close()
}

func makeAutoDraining(r io.ReadCloser) io.ReadCloser {
	if rw, ok := r.(io.ReadWriteCloser); ok {
		return &autodrainingReadWriter{rw}
	}

	return &autodrainingReader{r}
}

I think the question then comes down to where this goes. Two options come to mind

  1. Wrap the transport http.RoundTripper so that it returns requests with auto-draining bodies. I'll test this out client-side, at least that gives me a workaround
  2. Wrap the requests themselves as they are performed (more error prone)

What seems strange to me is that I see nothing in net/http that suggests the same issue where bodies must be read before they are closed (wrong: corrected below). But yet looking in the go-elasticsearch code base, I don't see anything that would challenge that either, so I'm not sure what the disclaimer is referring to.

Update: discovered this golang/go@ce83415 via #123 (comment).

@Anaethelion
Copy link
Contributor

That's an interesting idea, thank you for raising this.
We could implement it as an option as this hugely depends on how you built your consumption of the body, if any.

Let me know how your experiments are going, there are several options and one of them being not in the client but as you pointed in the transport.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants