From 245d09b80b2726f7ad8a0ab649a804cf7bf1501d Mon Sep 17 00:00:00 2001 From: s0kil Date: Tue, 26 Apr 2022 09:52:59 -0400 Subject: [PATCH 1/3] Pin required packages for Brotli compression. --- NixSupport/haskell-packages/hs-brotli.nix | 34 +++++++++++++++++++ .../wai-middleware-brotli.nix | 34 +++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 NixSupport/haskell-packages/hs-brotli.nix create mode 100644 NixSupport/haskell-packages/wai-middleware-brotli.nix diff --git a/NixSupport/haskell-packages/hs-brotli.nix b/NixSupport/haskell-packages/hs-brotli.nix new file mode 100644 index 000000000..3990e278d --- /dev/null +++ b/NixSupport/haskell-packages/hs-brotli.nix @@ -0,0 +1,34 @@ +{ mkDerivation +, base +, bytestring +, fetchgit +, ghc-prim +, lib +, QuickCheck +, quickcheck-instances +, tasty +, tasty-hunit +, tasty-quickcheck +}: +let + nixpkgs = import { }; +in +mkDerivation { + pname = "hs-brotli"; + version = "0.1.0.0"; + src = fetchgit { + url = "https://github.com/iand675/hs-brotli"; + sha256 = "1i4qaqn79m4jcg16pdm222xy6x97r5fv8ikyykf1nl79hs0dajrh"; + rev = "d7bce54b265883fb30a14d39d00cbf1c1308b2b1"; + fetchSubmodules = true; + }; + postUnpack = "sourceRoot+=/brotli; echo source root reset to $sourceRoot"; + libraryHaskellDepends = [ base bytestring ghc-prim ]; + librarySystemDepends = [ nixpkgs.brotli ]; + libraryToolDepends = [ nixpkgs.pkg-config ]; + libraryPkgconfigDepends = [ nixpkgs.brotli ]; + testHaskellDepends = [ base bytestring QuickCheck quickcheck-instances tasty tasty-hunit tasty-quickcheck ]; + homepage = "https://github.com/iand675/hs-brotli#readme"; + description = "Compression and decompression in the brotli format"; + license = lib.licenses.bsd3; +} diff --git a/NixSupport/haskell-packages/wai-middleware-brotli.nix b/NixSupport/haskell-packages/wai-middleware-brotli.nix new file mode 100644 index 000000000..93bce62e7 --- /dev/null +++ b/NixSupport/haskell-packages/wai-middleware-brotli.nix @@ -0,0 +1,34 @@ +{ mkDerivation +, base +, binary +, bytestring +, directory +, fetchgit +, filepath +, hs-brotli +, http-types +, lib +, mtl +, tasty +, tasty-hspec +, tasty-hunit +, unix +, wai +, wai-extra +}: +mkDerivation { + pname = "wai-middleware-brotli"; + version = "0.1.0.0"; + src = fetchgit { + url = "https://github.com/iand675/hs-brotli"; + sha256 = "1i4qaqn79m4jcg16pdm222xy6x97r5fv8ikyykf1nl79hs0dajrh"; + rev = "d7bce54b265883fb30a14d39d00cbf1c1308b2b1"; + fetchSubmodules = true; + }; + postUnpack = "sourceRoot+=/wai-middleware-brotli; echo source root reset to $sourceRoot"; + libraryHaskellDepends = [ base binary bytestring directory filepath hs-brotli http-types unix wai ]; + testHaskellDepends = [ base bytestring hs-brotli http-types mtl tasty tasty-hspec tasty-hunit wai wai-extra ]; + homepage = "https://github.com/iand675/hs-brotli#readme"; + description = "WAI middleware for brotli compression"; + license = lib.licenses.bsd3; +} From 6af262fb9115c825f46d39157ec2578d1104cb3e Mon Sep 17 00:00:00 2001 From: s0kil Date: Tue, 26 Apr 2022 09:53:07 -0400 Subject: [PATCH 2/3] Add Guide On Using Compression Middleware. --- Guide/config.markdown | 54 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/Guide/config.markdown b/Guide/config.markdown index 4d7f62104..184a6f468 100644 --- a/Guide/config.markdown +++ b/Guide/config.markdown @@ -63,7 +63,7 @@ action MyAction = do ### Reading Environment Variables -Inside `Config/Config.hs` you can use `env` to read environment variables. +Inside `Config/Config.hs` you can use `env` to read environment variables. ```haskell module Config where @@ -152,3 +152,55 @@ config = do option $ CustomMiddleware $ addHeaders [("X-My-Header", "Custom WAI Middleware!")] . realIp ``` +### Compression Middleware +We can compress assets using gzip or brotli. +First, let's add the required Haskell dependencies: +In your `default.nix` file, add: +```nix + haskellDeps = p: with p; [ + ... + + # Wai Middleware + wai-middleware-brotli # <-- Add This Dependency + wai-extra # <-- And This One + ]; +``` +Run, `nix-shell --run 'make -B .envrc'` to update the environment. +Once that succeeds, we can use it in your `Config/Config.hs`: + +Add two imports, one for Gzip compression, another for Brotli compression: +```haskell +module Config where +... +import Network.Wai.Middleware.Brotli -- <-- Add This Import +import Network.Wai.Middleware.Gzip -- <-- And This One +``` + +And then create a function `compressionMiddleware` that combines (composes) Gzip and Brotli compression middleware's into one middleware: +```haskell +-- | Gzip And Brotli Compression Middleware +compressionMiddleware :: CustomMiddleware +compressionMiddleware = + let + -- With `GzipCompress` and `BrotliCompress` options, it will compress per request. + gzipSettings = def { gzipFiles = GzipCompress } + brotliSettings = defaultSettings { brotliFilesBehavior = BrotliCompress } + in + CustomMiddleware (gzip gzipSettings . brotli brotliSettings) + +``` +Lastly, we can use it as: +```haskell +config :: ConfigBuilder +config = do + ... + option compressionMiddleware -- <-- Here we add our middleware +``` + +The default behavior for `GzipCompress` and `BrotliCompress` is to compress files on the fly. +You can customize this behavior, take a look at the [brotli config](https://github.com/iand675/hs-brotli/blob/master/wai-middleware-brotli/src/Network/Wai/Middleware/Brotli.hs#L53-L66) and [gzip config](https://github.com/yesodweb/wai/blob/master/wai-extra/Network/Wai/Middleware/Gzip.hs#L62-L73). + +Also notice `CustomMiddleware (gzip gzipSettings . brotli brotliSettings)`, It's [important that brotli middleware wraps the gzip middleware](https://github.com/iand675/hs-brotli/blob/master/wai-middleware-brotli/src/Network/Wai/Middleware/Brotli.hs#L15-L17), so the responses are not compressed by both, if the client supports brotli, compress with brotli, otherwise gzip, fallback to no compression. + +By default all `text/*` content types will be compressed, including `application/json`, `application/javascript`, `application/ecmascript` and `image/x-icon`. +Simply put, html, text, css, javascript, json and icons. From ddc190c60ad653f2368e807d687674e321577e27 Mon Sep 17 00:00:00 2001 From: s0kil Date: Wed, 4 May 2022 18:06:02 -0400 Subject: [PATCH 3/3] Add Compression Libraries To Cachix --- .github/workflows/build.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 38118dae1..0c89c783d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -34,7 +34,7 @@ jobs: echo "\$file_contents = file_get_contents('./default.nix');" >> replace.php echo '$new_content = preg_replace("/\".{40}\"/", "\"$argv[1]\"", $file_contents);' >> replace.php echo '$new_content_with_ref = preg_replace("/refs\/tags\/v\d+.\d+.\d+/", "${{ github.ref }}", $new_content);' >> replace.php - echo '$new_content_with_ref = str_replace("p.ihp", "p.ihp wreq mmark mmark-ext strip-ansi-escape stripe-signature stripe-concepts http-conduit units units-defs haskell-to-elm aeson-casing ihp-zip ihp-sentry minio-hs", $new_content_with_ref);' >> replace.php + echo '$new_content_with_ref = str_replace("p.ihp", "p.ihp wreq mmark mmark-ext strip-ansi-escape stripe-signature stripe-concepts http-conduit units units-defs haskell-to-elm aeson-casing ihp-zip ihp-sentry minio-hs hs-brotli wai-middleware-brotli", $new_content_with_ref);' >> replace.php echo "file_put_contents('./default.nix', \$new_content_with_ref);" >> replace.php echo "?>" >> replace.php php replace.php $GITHUB_SHA @@ -42,4 +42,3 @@ jobs: mv Makefile Makefile.old echo 'GHC_OPTIONS+= -rtsopts=all\n.SHELLFLAGS := -eu -o pipefail -c\n\n'|cat - Makefile.old > Makefile nix-shell --run "new-application Web && make build/bin/RunUnoptimizedProdServer" -