lrleon
lrleon

Reputation: 2648

How to keep the gzip representation of HTTP body

In the context of a go web app, I am using a cache of responses and compressing them, so I save bandwidth (responses are larger). I am using gin-gonic framework and the package gzip for compressing. This package works pretty well and makes everything that I need for receiving and replying gzip-compressed bodies. It is enough this couple of lines:

router := gin.Default()
router.Use(gzip.Gzip(gzip.DefaultCompression))

And everything is magic. Now, I am also using a cache which stores the responses. To save space and CPU, I would like to store in the cache the already compressed responses, so I do not repeat the compression either. As a middleware that wraps the HTTP body processing, gzip transparently uncompresses requests and compresses the responses. However, at my handler level, I would like to get the compressed representation of my response so I can store thus into my cache. Also, I would require to tell gzip that my body is already compressed.

I have made some searches for an automatized way that uses gzip package or any other else and that prevents me to intervene some library, execution path in the HTTP stack, or worst, to program myself things that are already made by another library such gzip.

Does somebody know how gzip package could manage this problem?

Upvotes: 2

Views: 909

Answers (1)

lrleon
lrleon

Reputation: 2648

First of all, I continued using the gzip and gin packages.

Now, I compress my responses with a function similar to this:

func compress(response interface{}) ([]byte, error) {
    body, err := json.Marshal(response)
    if err != nil {
        return nil, err
    }
    var buffer bytes.Buffer
    zw := gzip.NewWriter(&buffer)
    _, err = zw.Write(body)
    closeErr := zw.Close()
    if err != nil {
        return nil, err
    }
    if closeErr != nil {
        return nil, err
    }
    return buffer.Bytes(), nil
}

When I need to respond to a request, I execute something like this:

    gzipOutput, err := compressResponse(output)
    if err != nil {
        c.JSON(http.StatusInternalServerError, err.Error())
        return
    }

    c.Writer.Header().Set("Accept-Encoding", "gzip")
    c.Writer.Header().Set("Content-Encoding", "gzip")
    c.Writer.Header().Set("Content-Type", "application/json")
    c.Data(http.StatusOK, "gzip", gzipOutput)

As we can see, the idea is to tell gin that the response is compressed by setting the HTML header.

It has been tested on five different APIs since eight months.

I hope it will be useful to another just like it was for me.

Upvotes: 1

Related Questions