-
Notifications
You must be signed in to change notification settings - Fork 17.7k
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
proposal: compress/gzip: add compressing reader and decompressing writer #51092
Comments
Sorry, I don't know if I understand what you are asking for. Are you looking for code like // CompressingReader returns a reader that reads the data in r after it has been compressed.
func CompressingReader(r io.Reader) io.Reader {
pr, pw := io.Pipe()
go func() { io.Copy(gzip.NewWriter(pw), r) }()
return pr
} |
In terms of functionality, yes. In terms of implementation, no. I was suggesting a fork of Why? First, I claim that the approach you suggest is error prone. For example, I am pretty sure that the code you wrote is incorrect as it does not close the gzip writer which may leave buffered data and will skip the GZIP footer. Second, I claim that it is inefficient. In addition to overhead of the goroutine and the |
When does this case come up? Is it worth the cost of adding to, and maintaining in, the standard library? (I agree that my sample code was not production quality, but the general idea might be sound.) |
The case that motivated me filing this issue is wanting to compress the contents of a file and send it as an HTTP request body. I did some research and it looks like at least one other person has run into something similar and thought the solution was worth sharing: Finally, I believe gzip performance is more likely to be a concern than other stream operations. |
You're probably right about inefficiency, but is the gains worth the complexity? It will probably lead to significant duplication of code in the When compressing, the Either we add this extra state tracking to the current implementation and slow down the implementation for |
FWIW, I wrote a helper function that can do this. I think it is correct, but I would love a bug report if it is not. |
This proposal has been added to the active column of the proposals project |
It sounds like the use case here is that http.Client uses an http.Request, and to send a body you fill in http.Request.Body, which is an io.Reader. And so if you want to send a compressed request body, compressing on the fly, it would be nice to be able to do
The http.Client's Transport guarantees to call req.Body.Close, so it seems like an io.Pipe-based solution (needing Close to stop a goroutine) should be OK. @dsnet, what do you think of adding some API backed by an io.Pipe and a goroutine to satisfy this use case? (The name CompressingReader is a placeholder above.) |
Is there a use case for the decompressing writer? |
I'm not quite convinced this needs to be in the If it were in the req.Body = io.NewReaderFromWriter(uncompressedDataReader, gzip.NewWriter) where you pass in the constructor for the writer. However, there is no standard interface for constructors, so actual usage may be awkward. The API in func NewReaderFromWriter(r io.Reader, new func(io.Writer) io.WriteCloser) io.WriteCloser Actual callsite would look like: req.Body = io.NewReaderFromWriter(uncompressedDataReader, func(w io.Writer) io.WriteCloser {
return gzip.NewWriter(w)
}) You need to wrap With generics, we could probably avoid this wrapping by changing the signature to: func NewReaderFromWriter[AnyWriteCloser io.WriteCloser](r io.Reader, new func(io.Writer) AnyWriteCloser) io.WriteCloser |
This general form seems pretty awkward. Maybe there is a clearer presentation, but it took me quite a while to wrap my head around why there is a func in the API.
seems much clearer, and zstd and brotli could add their own too. Even if we did have a primitive in io, it might probably be worth having a function in package gzip, so others don't have to puzzle through how to use it. |
I'm not yet convinced there is anything to do here. I think that @iangudger is asking for a duplication of the gzip compression code to work on an If we avoid the duplication, then all we need is code that pipes from an So I think are choices are to extend gzip in the standard library, which I don't think we should do, or use |
We implemented this ourselves and found the error handling to be very error-prone. We ended up adding a lot of synchronization because calling |
Change https://go.dev/cl/389514 mentions this issue: |
In https://go.dev/cl/389514 I wrote an example of how to write a compressing reader to use with an HTTP PUT request. This will appear in compress/gzip/example_test.go. Comments welcome. Thanks. |
@dsnet, what do you think? Is this a common enough need to add code like Ian's example as a function like CompressingReader to gzip? |
I like Ian's approach of adding an example. While the example is in the |
For #51092 Change-Id: If0a233651ac75f113569ddfffd056084f6092564 Reviewed-on: https://go-review.googlesource.com/c/go/+/389514 Trust: Ian Lance Taylor <iant@golang.org> Run-TryBot: Ian Lance Taylor <iant@golang.org> Reviewed-by: Joseph Tsai <joetsai@digital-static.net> TryBot-Result: Gopher Robot <gobot@golang.org>
OK, it sounds like we're happy with the example for now. |
Based on the discussion above, this proposal seems like a likely decline. |
No change in consensus, so declined. |
Building a reader out of a writer and visa versa can be error prone and inefficient.
The text was updated successfully, but these errors were encountered: