diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,31 @@
+Copyright (c) 2016, Ben Gamari
+              2019, Herbert Valerio Riedel
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+    * Redistributions of source code must retain the above copyright
+      notice, this list of conditions and the following disclaimer.
+
+    * Redistributions in binary form must reproduce the above
+      copyright notice, this list of conditions and the following
+      disclaimer in the documentation and/or other materials provided
+      with the distribution.
+
+    * Neither the name of Ben Gamari nor the names of other
+      contributors may be used to endorse or promote products derived
+      from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Setup.hs b/Setup.hs
new file mode 100644
--- /dev/null
+++ b/Setup.hs
@@ -0,0 +1,2 @@
+import Distribution.Simple
+main = defaultMain
diff --git a/Test.hs b/Test.hs
new file mode 100644
--- /dev/null
+++ b/Test.hs
@@ -0,0 +1,22 @@
+import Control.Monad (void)
+import Control.Applicative ((<$>))
+
+import Pipes
+import Pipes.Brotli
+import qualified Pipes.Prelude as PP
+import qualified Data.ByteString as BS
+
+import Test.QuickCheck
+import Test.QuickCheck.Monadic
+
+roundTrip :: [BS.ByteString] -> Property
+roundTrip bss = monadicIO $ do
+    bss' <- run $ PP.toListM $ void $ decompress $ compress $ each bss
+    monitor $ counterexample $ show bss'
+    assert (BS.concat bss' == BS.concat bss)
+
+main :: IO ()
+main = quickCheck roundTrip
+
+instance Arbitrary BS.ByteString where
+    arbitrary = BS.pack <$> arbitrary
diff --git a/pipes-brotli.cabal b/pipes-brotli.cabal
new file mode 100644
--- /dev/null
+++ b/pipes-brotli.cabal
@@ -0,0 +1,46 @@
+cabal-version:       1.12
+build-type:          Simple
+name:                pipes-brotli
+version:             0.0.0.0
+
+synopsis:            Brotli (RFC7932) compressors and decompressors for the Pipes package
+description:
+    <http://brotli.org Brotli> (<https://tools.ietf.org/html/rfc7932 RFC7932>) is a generic-purpose lossless compression algorithm suitable for <https://en.wikipedia.org/wiki/HTTP_compression HTTP compression> that compresses data using a combination of a modern variant of the LZ77 algorithm, Huffman coding and 2nd order context modeling.
+    .
+    This package provides a @pipes@ interface to the <http://brotli.org Brotli compression algorithm>.
+
+homepage:            http://github.com/hvr/pipes-brotli
+bug-reports:         http://github.com/hvr/pipes-brotli/issues
+license:             BSD3
+license-file:        LICENSE
+author:              Ben Gamari, Herbert Valerio Riedel
+maintainer:          hvr@gnu.org
+copyright:           (c) 2016 Ben Gamari, 2019 Herbert Valerio Riedel
+category:            Codec, Compression, Pipes
+
+source-repository head
+  type:                git
+  location:            https://github.com/hvr/pipes-brotli.git
+
+library
+  exposed-modules:     Pipes.Brotli
+  build-depends:       base >=4.5 && <4.13,
+                       bytestring == 0.10.*,
+                       pipes >=4.0 && <4.4,
+                       brotli == 0.0.*
+  hs-source-dirs:      src
+  default-language:    Haskell2010
+  other-extensions:    ScopedTypeVariables
+                       Trustworthy
+
+test-suite pipes-brotli-tests
+  type:                exitcode-stdio-1.0
+  main-is:             Test.hs
+  build-depends:       base,
+                       bytestring,
+                       pipes,
+                       pipes-brotli,
+
+                       pipes-bytestring == 2.1.*,
+                       QuickCheck == 2.13.*
+  default-language:    Haskell2010
diff --git a/src/Pipes/Brotli.hs b/src/Pipes/Brotli.hs
new file mode 100644
--- /dev/null
+++ b/src/Pipes/Brotli.hs
@@ -0,0 +1,103 @@
+{-# LANGUAGE ScopedTypeVariables #-}
+{-# LANGUAGE Trustworthy #-}
+
+-- |
+-- Module      : Pipes.Brotli
+-- Copyright   : © 2016 Herbert Valerio Riedel
+--
+-- Maintainer  : hvr@gnu.org
+--
+-- Compression and decompression of data streams in the \"Brotli\" format (<https://tools.ietf.org/html/rfc7932 RFC7932>)
+--
+module Pipes.Brotli
+    ( -- * Simple interface
+      compress
+    , decompress
+
+      -- * Extended interface
+      -- ** Compression
+    , compressWith
+
+    , Brotli.defaultCompressParams
+    , Brotli.CompressParams
+    , Brotli.compressLevel
+    , Brotli.compressWindowSize
+    , Brotli.compressMode
+    , Brotli.compressSizeHint
+    , Brotli.CompressionLevel(..)
+    , Brotli.CompressionWindowSize(..)
+    , Brotli.CompressionMode(..)
+
+      -- ** Decompression
+    , decompressWith
+
+    , Brotli.defaultDecompressParams
+    , Brotli.DecompressParams
+    , Brotli.decompressDisableRingBufferReallocation
+
+    ) where
+
+import Pipes
+import Data.ByteString (ByteString)
+import qualified Data.ByteString as BS
+import qualified Codec.Compression.Brotli as Brotli
+
+-- | Decompress a 'ByteString'.
+decompress :: forall m r. MonadIO m
+           => Producer ByteString m r
+           -> Producer ByteString m (Producer ByteString m r)
+decompress = decompressWith Brotli.defaultDecompressParams
+
+-- | Decompress a 'ByteString' with a given set of 'Brotli.DecompressParams'.
+decompressWith :: forall m r. MonadIO m
+               => Brotli.DecompressParams
+               -> Producer ByteString m r
+               -> Producer ByteString m (Producer ByteString m r)
+decompressWith params prod0 = liftIO (Brotli.decompressIO params) >>= go prod0
+  where
+    go :: Producer ByteString m r
+       -> Brotli.DecompressStream IO
+       -> Producer ByteString m (Producer ByteString m r)
+    go prod s@(Brotli.DecompressInputRequired more) = do
+        mx <- lift $ next prod
+        case mx of
+          Right (x, prod')
+            | BS.null x    -> go prod' s
+            | otherwise    -> liftIO (more x) >>= go prod'
+          Left r           -> liftIO (more BS.empty) >>= go (return r)
+    go prod (Brotli.DecompressOutputAvailable output cont) = do
+        yield output
+        liftIO cont >>= go prod
+    go prod (Brotli.DecompressStreamEnd leftover) =
+        return (yield leftover >> prod)
+    go _prod (Brotli.DecompressStreamError ecode) =
+        fail $ "Pipes.Brotli.decompress: error (" ++ Brotli.showBrotliDecoderErrorCode ecode ++ ")"
+
+-- | Compress a 'ByteString'.
+compress :: forall m r. MonadIO m
+         => Producer ByteString m r
+         -> Producer ByteString m r
+compress = compressWith Brotli.defaultCompressParams
+
+-- | Compress a 'ByteString' with a given set of 'Brotli.CompressParams'.
+compressWith :: forall m r. MonadIO m
+             => Brotli.CompressParams
+             -> Producer ByteString m r
+             -> Producer ByteString m r
+compressWith params prod0 = liftIO (Brotli.compressIO params) >>= go prod0
+  where
+    go :: Producer ByteString m r
+       -> Brotli.CompressStream IO
+       -> Producer ByteString m r
+    go prod s@(Brotli.CompressInputRequired _flush more) = do
+        mx <- lift $ next prod
+        case mx of
+          Right (x, prod')
+            | BS.null x    -> go prod' s
+            | otherwise    -> liftIO (more x) >>= go prod'
+          Left r           -> liftIO (more BS.empty) >>= go (return r)
+    go prod (Brotli.CompressOutputAvailable output cont) = do
+        yield output
+        liftIO cont >>= go prod
+    go prod Brotli.CompressStreamEnd =
+        prod
