Skip to content

Commit

Permalink
Merge pull request #1447 from digitallyinduced/s0kil/compression-midd…
Browse files Browse the repository at this point in the history
…leware

Compression Middleware Guide
  • Loading branch information
mpscholten committed May 20, 2022
2 parents d7f60df + ddc190c commit a00a380
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 3 deletions.
3 changes: 1 addition & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,11 @@ 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
cat default.nix
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"
54 changes: 53 additions & 1 deletion Guide/config.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.
34 changes: 34 additions & 0 deletions NixSupport/haskell-packages/hs-brotli.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{ mkDerivation
, base
, bytestring
, fetchgit
, ghc-prim
, lib
, QuickCheck
, quickcheck-instances
, tasty
, tasty-hunit
, tasty-quickcheck
}:
let
nixpkgs = import <nixpkgs> { };
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;
}
34 changes: 34 additions & 0 deletions NixSupport/haskell-packages/wai-middleware-brotli.nix
Original file line number Diff line number Diff line change
@@ -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;
}

0 comments on commit a00a380

Please sign in to comment.