Skip to content

Instantly share code, notes, and snippets.

@sgarcez
Last active January 7, 2022 10:52
Show Gist options
  • Save sgarcez/1500f66c4ee39fb52c318f280bb919e3 to your computer and use it in GitHub Desktop.
Save sgarcez/1500f66c4ee39fb52c318f280bb919e3 to your computer and use it in GitHub Desktop.
Patron Compression Middlware Content-Length Issue

Patron Compression Middlware Content-Length Issue

Run test server

$ go run main.go

Before fix

$ curl -iH 'Accept-encoding: gzip' http://localhost:3001/cltest --compressed > cltest.txt
curl: (18) transfer closed with 2077 bytes remaining to read

# Server log
2022/01/07 10:39:49.514045 lvl=ERR error in deferred call to Close() method on gzip compression middleware : http: wrote more than the declared Content-Length

After fix

$ curl -iH 'Accept-encoding: gzip' http://localhost:3001/cltest --compressed > cltest.txt

$ cat cltest.txt
HTTP/1.1 200 OK
Accept-Ranges: bytes
Content-Encoding: gzip
Content-Type: image/png
Last-Modified: Fri, 07 Jan 2022 10:42:20 GMT
Date: Fri, 07 Jan 2022 10:42:20 GMT
Transfer-Encoding: chunked

...

Transfer-Encoding tests

$ curl -iH 'Accept-encoding: gzip' http://localhost:3001/chunktest1 --compressed > chunktest1.txt

$ cat chunktest1.txt
HTTP/1.1 200 OK
Content-Encoding: gzip
Content-Type: text/plain; charset=utf8
Date: Fri, 07 Jan 2022 10:35:15 GMT
Content-Length: 104

...

$ curl -iH 'Accept-encoding: gzip' http://localhost:3001/chunktest2 --compressed > chunktest2.txt

$ cat chunktest2.txt
HTTP/1.1 200 OK
Content-Encoding: gzip
Content-Type: text/plain; charset=utf8
Date: Fri, 07 Jan 2022 10:37:16 GMT
Transfer-Encoding: chunked

...
package main
import (
"bytes"
"encoding/hex"
"fmt"
"io"
"log"
"net/http"
"os"
"strings"
"time"
patronhttp "github.com/beatlabs/patron/component/http"
patronlog "github.com/beatlabs/patron/log"
"github.com/beatlabs/patron/log/std"
)
const (
port = 3001
uncompressablePNG = `89504e470d0a1a0a0000000d49484452000000760000007608060000001da76711000007f3494441547801ed9d039423cd16806b6cdb48276bdbb66ddbfc6d1b6bdbd559dbb6bb27f36cdb46bdba39274fffb0533d9dae73f79cefae77efdc2f49978b54e75b3d0fcd776874bea2d3c3fcfb078a467fc0bfff3b8721a251ffc0ebfc35fefd35455737291e3ab0cdf768148754050895e2d4d5fe0e9ddec3625b2f9bb3bd4ee9c1020ea9880a7fd3e9a18d158d5ec58206168aaefe99f371c31f9f8de190b28050162075ac43a77fc24206b260fa04debd557ec73a4ad437b1707641fd89cb435b72c87f03e17f50743a078b653fb9d0b0e5101fdee0c35172b08ba2a97fc342d9f363f9bf9fb910bc74fed6e5482ef57b58241ba3d30f390480e0c559a23e8bc5b139d0d8d58fe4710881d0ec1b34c1a1a9bfc2e248d115dac22184078e7b3c164516b1f477cad74e45101e88a251158b220f2ecddd9774669743c13216442274759d6f609f2132a15e204ae9c1d681904cd1ad5d2c6bc37b2ce3e3d76a9ccccfdf64599b3e6039fb3e6785d7b6cbf09c2d252e9d0eb13a919c5d9fb0e0a4668c90da014170625316d5ba1f4b5eb480e5eeffdc8e727f4d144d1d6775226185ed058a110fe407920b6fecb4c73b56a37fb55c6ce1cd5d503c5b109cd094a5beb08c153fda8f622ba3e0f2560b65197f0767ae784b36b128d647dcc031acf8e13ed9c4a25820b2796ffeecdd219b5814ebfb68ce3fb54136b12816084969c10ace6f914d2c8a05221af62cf3998b6225207ed44419c5a25820edad1764148b6283631bfb5aca28563612a7cf94512c8a0d8a6ac40aaf6e47b1329230691a8a9591a0c806ace8ce6e146b1aa1f55978ad2e2caa6d7f16d5a63f8b68d09d054534a811b9a2270b506c705d16d36d28cbdaf85e99830630f50613fc315d879adbaf1d3e1ec58a2234bb35cbd9f9499573c8defc010b8e6b6cce5063464b142b82f03a5d61f2beda79e41d5e0313e9a6c8cd75af42b1fe101cdf94155cda6a3897cc956f992236f5c5e528d61f529f5fe6773e7143c789eff68c9f82628d0f083414d2b5c83fb39191d07a42c546771a84620d022d606139c5f61d29546c64e39e28d62849f3e609cb0966678436e85c9d51ac51d2de7c51584e79c7d7895d3a53dc11c51a25fddd97c4e5746eb3e095153d50ac5192172f149653fea98d621b4f1db0f16498d87ea384e594f1f1eb62bb3b13a7a258a3c090a0a08564b0105ca8d8f4b75f44b1568ff0145cd80293e442c5fa46c38c83438a7e2fdc8ee9395cf892549c041040686e5b78d719c905fac2c2871393172f40b1a208496dc1b2b77d54e51c8a1f1f604933670bcf03c83bba0ec58a26a6c730988eab74377d64d35e66fcffb02b1ed73c994964b33e2c79c922efb45cd6da7759da1bcfb38409535944fdee66febff0a241b1b211dd7e20ae2b9650acef5012142b13f1a327e3de1dd9c4862b9d58d1bdbd285626b1d0cdca3fbb09b751ca263667cf67964b45b1266c9d2c7e6aac0628163761a1580108987b45b15291f2dc52142ba3581252d7b77708c5ca066cdb2c7e7200c5ca082c8945b1121292d68215dddd8362652469ee5c146b0a61f5bceb8ee2474c6049b3e7784918370526d76123578d1c5e2df0183e141b92dc1c26d72bbcbc0176e7c14273b803c04cb970a1048a1534c95d70795b957380534bc39d9dcc9b74ef3204c5fa4b6cef1186c66ce1788330474753c4064537f4e7e318c586e5b763c50f8c1730efd06ad38e0982559328d620199fbde1773ec9f3e79b33ccf8ec52146bacb1d40cd6070bb9b12b38be8970b17143c6a15823c40e182d2c27e80e095f63dcaa2f8a3542cab2c5c272cafcfc0de16223ea7645b1568fcb169cdb245c6c58417b146bbdd8cdc2c5423f19c51a2069ce1c71878b1c5b2b5a2c9cb88a62ad3ee721edd567036583168a25a1f5855db00027a909efee0c1a8362ad5c48067b6e48501dc1628d3f2a502c105a0fae03379a0b0c479ab6b532fdbd5750acbfb767646ffdb0fa52f9faa498eee69d360ec727a05801ab0453962eaaf28c0a4cf145b51b60e66d95386d2792d09c36bc7ffb42b9333e45b777b394e58be126494beebb43b102e643e15287c4693359d2ac39dec3a6e17cc3a0c89ab9c923e3d3d751ac8c8bc70bafef40b1b201c7f7e12a45c980fe70ded1b5285636627a0ec39d0032924b57a258c9800b2364db6d8762614002fac82856aeab456119ab041b9f51acd84b2750ac78c4dffd83626d4d50787d96fec12bd64b45b16277f5e5ecfed43a9128563cd11d07957fc41e8add663ba191cd7ab3ec1d1f57f075a158d8f208c7d5d962dc37b2492f96b9e61d5fee812dd6a1d3511627023bcf035126ec7487112458afe4db196f17fe48145ded1c08c964ae780b26c061e0bcc689ed3392c50d1befbd99198e2e802bd072d595be739aecc8b748ed2f1d72c14f10795074f526a9e7a1b10e5dfd27164426b1743fe101b88505918812f70cc2037196a8cf62412441a7ffa8af1fcd203c903aa56ea72c1fc7887a8d43080440d1e81e2c8afd7179d46e1c422000b5bf74b850d1e95fb038b66e0d9fe51000c27ff31a16c89e281afdbde2a1753904f0061f8cb120874e0f61a16c86b77de41ecc213e20fc0f0d7f7c3686ffe1075830fba068ea731cf2df40f8025eb9ba7a108b16f0cfd43fbb4adc9338e4ff815026f0b1ecd4d457e02f631103512afdbaa2d1361c5216102a02861cf3b9dc1dd0f1c5825a0f77f25387ae2e68c61e8471487940a812b5be4c8b148d2ee55ce5ffc1dfb1c83589fa2b4557772b1e3a1c1e931c521910aa4de76f5d8e04d1b54a0eb673e97488a2a923c4823875da4ff1b89bc0f0e0eb8c05734875f817a86880808d97fb2f0000000049454e44ae426082`
)
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/chunktest1", func(w http.ResponseWriter, r *http.Request) {
writeString(w, fmt.Sprintf("5KB string compresses to under 2KB so should not be chunked: l%sng!", strings.Repeat("o", 5<<10)))
})
mux.HandleFunc("/chunktest2", func(w http.ResponseWriter, r *http.Request) {
writeString(w, fmt.Sprintf("3MB string compresses to over 2KB so should be chunked: l%sng!", strings.Repeat("o", 3000<<10)))
})
mux.HandleFunc("/cltest", func(w http.ResponseWriter, r *http.Request) {
data, err := hex.DecodeString(uncompressablePNG)
if err != nil {
panic(err)
}
content := bytes.NewReader(data)
http.ServeContent(w, r, "foo", time.Now(), content)
})
patronlog.Setup(std.New(os.Stdout, patronlog.DebugLevel, nil))
middleware := patronhttp.NewCompressionMiddleware(-1)
log.Printf("Listening on port %d...", port)
log.Println(http.ListenAndServe(fmt.Sprintf(":%d", port), middleware(mux)))
}
func writeString(w http.ResponseWriter, payload string) {
w.Header().Set("Content-Type", "text/plain; charset=utf8")
_, err := io.WriteString(w, payload+"\n")
if err != nil {
fmt.Printf("wrting body: %s\n", err)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment