diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,31 @@
+Copyright Julian K. Arni (c) 2015
+
+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 Julian K. Arni 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/servant-streaming-server.cabal b/servant-streaming-server.cabal
new file mode 100644
--- /dev/null
+++ b/servant-streaming-server.cabal
@@ -0,0 +1,78 @@
+-- This file has been generated from package.yaml by hpack version 0.20.0.
+--
+-- see: https://github.com/sol/hpack
+--
+-- hash: 2c782b81db834803cac4a8b8aeffb9f7e765d9e4097f1acee1d86460c5a266fd
+
+name:           servant-streaming-server
+version:        0.2.0.0
+synopsis:       Server instances for the 'servant-streaming' package.
+description:    This package defines instances that allow using the 'StreamBody' and 'StreamResponse' combinators in 'servant' servers.
+homepage:       http://github.com/plow-technologies/servant-streaming-server#readme
+bug-reports:    https://github.com/plow-technologies/servant-streaming-server/issues
+author:         Julian K. Arni
+maintainer:     jkarni@gmail.com
+copyright:      (c) Julian K. Arni
+license:        BSD3
+license-file:   LICENSE
+tested-with:    GHC == 8.2.2
+build-type:     Simple
+cabal-version:  >= 1.10
+
+source-repository head
+  type: git
+  location: https://github.com/plow-technologies/servant-streaming-server
+
+library
+  hs-source-dirs:
+      src
+  default-extensions: AutoDeriveTypeable ConstraintKinds DataKinds DefaultSignatures DeriveFoldable DeriveFunctor DeriveGeneric DeriveTraversable FlexibleContexts FlexibleInstances FunctionalDependencies GADTs KindSignatures MultiParamTypeClasses OverloadedStrings RankNTypes ScopedTypeVariables TypeFamilies TypeOperators
+  ghc-options: -Wall
+  build-depends:
+      base >=4.7 && <4.11
+    , bytestring
+    , http-media >=0.6 && <0.8
+    , http-types >=0.9 && <0.13
+    , resourcet >=1.1 && <1.3
+    , servant
+    , servant-server >=0.8 && <0.14
+    , servant-streaming >=0.2 && <0.3
+    , streaming >=0.1 && <0.3
+    , streaming-wai >=0.1 && <0.2
+    , wai >=3.0 && <3.3
+  exposed-modules:
+      Servant.Streaming.Server
+      Servant.Streaming.Server.Internal
+  default-language: Haskell2010
+
+test-suite spec
+  type: exitcode-stdio-1.0
+  main-is: Spec.hs
+  hs-source-dirs:
+      test
+  default-extensions: AutoDeriveTypeable ConstraintKinds DataKinds DefaultSignatures DeriveFoldable DeriveFunctor DeriveGeneric DeriveTraversable FlexibleContexts FlexibleInstances FunctionalDependencies GADTs KindSignatures MultiParamTypeClasses OverloadedStrings RankNTypes ScopedTypeVariables TypeFamilies TypeOperators
+  ghc-options: -Wall -Wall -with-rtsopts=-T -threaded
+  build-depends:
+      QuickCheck >=2.8 && <2.11
+    , base >=4.7 && <4.11
+    , bytestring
+    , directory
+    , hspec >2 && <3
+    , http-media >=0.6 && <0.8
+    , http-types >=0.9 && <0.13
+    , pipes >=4 && <5
+    , pipes-http >=1 && <2
+    , resourcet >=1.1 && <1.3
+    , servant
+    , servant-server >=0.8 && <0.14
+    , servant-streaming >=0.2 && <0.3
+    , servant-streaming-server
+    , streaming >=0.1 && <0.3
+    , streaming-bytestring >=0.1 && <0.2
+    , streaming-wai >=0.1 && <0.2
+    , wai >=3.0 && <3.3
+    , warp >=3.2.4 && <3.3
+  other-modules:
+      Servant.Streaming.ServerSpec
+      Paths_servant_streaming_server
+  default-language: Haskell2010
diff --git a/src/Servant/Streaming/Server.hs b/src/Servant/Streaming/Server.hs
new file mode 100644
--- /dev/null
+++ b/src/Servant/Streaming/Server.hs
@@ -0,0 +1,16 @@
+-- | This module provides an instance for HasServant for 'servant-streaming'
+-- combinators, so that servers with streaming (requests or responses) may be
+-- used.
+--
+-- As as convenience, it also re-exports the combinators themselves.
+module Servant.Streaming.Server
+  ( StreamResponse
+  , StreamBody
+  , StreamResponseGet
+  , StreamResponsePost
+  , StreamResponsePut
+  , StreamResponsePatch
+  ) where
+
+import Servant.Streaming
+import Servant.Streaming.Server.Internal ()
diff --git a/src/Servant/Streaming/Server/Internal.hs b/src/Servant/Streaming/Server/Internal.hs
new file mode 100644
--- /dev/null
+++ b/src/Servant/Streaming/Server/Internal.hs
@@ -0,0 +1,93 @@
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+module Servant.Streaming.Server.Internal where
+
+import           Control.Exception                          (bracket)
+import           Control.Monad.IO.Class
+import           Control.Monad.Trans.Resource               (ResourceT,
+                                                             InternalState,
+                                                             createInternalState,
+                                                             closeInternalState,
+                                                             runInternalState)
+import qualified Data.ByteString                            as BS
+import           Data.Maybe                                 (fromMaybe)
+import           GHC.TypeLits                               (KnownNat, natVal)
+import qualified Network.HTTP.Media                         as M
+import           Network.HTTP.Types                         (Method, Status,
+                                                             hAccept,
+                                                             hContentType)
+import           Network.Wai                                (Response,
+                                                             requestHeaders)
+import           Network.Wai.Streaming                      (streamingRequest,
+                                                             streamingResponse)
+import           Servant                                    hiding (Stream)
+import           Servant.API.ContentTypes                   (AllMime (allMime))
+import           Servant.Server.Internal                    (ct_wildcard,
+                                                             methodCheck,
+                                                             acceptCheck)
+import           Servant.Server.Internal.Router             (leafRouter)
+import           Servant.Server.Internal.RoutingApplication (DelayedIO,
+                                                             RouteResult (Route),
+                                                             addBodyCheck,
+                                                             addMethodCheck,
+                                                             addAcceptCheck,
+                                                             delayedFailFatal,
+                                                             runAction,
+                                                             withRequest)
+import           Servant.Streaming
+import           Streaming
+
+instance ( AllMime contentTypes, HasServer subapi ctx, MonadIO n
+         ) => HasServer (StreamBodyMonad contentTypes n :> subapi) ctx where
+  type ServerT (StreamBodyMonad contentTypes n :> subapi) m
+    = (M.MediaType, Stream (Of BS.ByteString) (ResourceT n) ())
+      -> ServerT subapi m
+
+  route _ ctxt subapi =
+    route (Proxy :: Proxy subapi) ctxt
+      $ addBodyCheck subapi getContentType makeBody
+      where
+        getContentType :: DelayedIO M.MediaType
+        getContentType = withRequest $ \request -> do
+          let contentTypeHdr
+               = fromMaybe ("application" M.// "octet-stream")
+               $ lookup hContentType (requestHeaders request) >>= M.parseAccept
+          if contentTypeHdr `elem` contentTypeList
+            then return contentTypeHdr else delayedFailFatal err415
+
+        contentTypeList :: [M.MediaType]
+        contentTypeList = allMime (Proxy :: Proxy contentTypes)
+
+        makeBody :: MonadIO m => a -> DelayedIO (a, Stream (Of BS.ByteString) m ())
+        makeBody a = withRequest $ \req -> return (a, streamingRequest req)
+
+  hoistServerWithContext _ a b c
+    = hoistServerWithContext (Proxy :: Proxy subapi) a b . c
+
+
+instance ( KnownNat status, AllMime contentTypes, ReflectMethod method
+         ) => HasServer (StreamResponse method status contentTypes) ctx where
+  type ServerT (StreamResponse method status contentTypes) m
+    = m (Stream (Of BS.ByteString) (ResourceT IO) ())
+
+  route _ _ctxt subapi = leafRouter $ \env request respond ->
+    let action = subapi `addMethodCheck` methodCheck method request
+                        `addAcceptCheck` acceptCheck contentTypeProxy accept
+        accept = fromMaybe ct_wildcard $ lookup hAccept $ requestHeaders request
+    in bracket createInternalState
+               closeInternalState
+               (runAction action env request respond . streamResponse)
+    where
+      method :: Method
+      method = reflectMethod (Proxy :: Proxy method)
+
+      contentTypeProxy :: Proxy contentTypes
+      contentTypeProxy = Proxy
+
+      streamResponse :: InternalState -> Stream (Of BS.ByteString) (ResourceT IO) () -> RouteResult Response
+      streamResponse st stream = Route $ streamingResponse (hoist (`runInternalState` st) stream) status []
+
+      status :: Status
+      status = toEnum $ fromInteger $ natVal (Proxy :: Proxy status)
+
+  hoistServerWithContext _ _ b c
+    = b c
diff --git a/test/Servant/Streaming/ServerSpec.hs b/test/Servant/Streaming/ServerSpec.hs
new file mode 100644
--- /dev/null
+++ b/test/Servant/Streaming/ServerSpec.hs
@@ -0,0 +1,198 @@
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+{-# OPTIONS_GHC -fno-warn-type-defaults #-}
+module Servant.Streaming.ServerSpec (spec) where
+
+import           Control.Concurrent
+import           Control.Exception            (bracket)
+import           Control.Monad.Trans.Resource (ResourceT, getInternalState,
+                                               runInternalState, runResourceT)
+import           Data.ByteString              (ByteString)
+import qualified Data.ByteString.Streaming    as BSS
+import qualified Data.ByteString              as BS
+import qualified Data.ByteString.Lazy         as BSL
+
+import           Data.IORef
+import           Data.String                  (fromString, IsString)
+import           GHC.Stats
+import qualified Network.HTTP.Media           as M
+import           Network.HTTP.Types           (status200, status405, status406,
+                                               status415)
+import           Network.Wai.Handler.Warp
+import           Pipes.HTTP                   (Request, RequestBody (..),
+                                               Response, brRead,
+                                               defaultManagerSettings,
+                                               defaultRequest, httpLbs, method,
+                                               newManager, path, port,
+                                               requestBody, requestHeaders,
+                                               responseBody, responseStatus,
+                                               responseTimeout,
+                                               responseTimeoutNone,
+                                               withResponse)
+import           Servant                      ((:<|>) ((:<|>)), (:>), JSON,
+                                               MimeRender (..), PlainText, Post,
+                                               Proxy (..), Server, serve)
+import           Servant.Streaming.Server
+import           Streaming
+import qualified Streaming.Prelude            as S
+import           System.Directory             (removeFile)
+import           Test.Hspec
+
+
+spec :: Spec
+spec = do
+  streamBodySpec
+  streamResponseSpec
+
+streamBodySpec :: Spec
+streamBodySpec = describe "StreamBody instance" $ around withServer $ do
+
+  it "streams the request body" $ \port' -> do
+    let req = streamReq port' "length" (S.each ["h","i"])
+    responseBody <$> makeRequest req `shouldReturn` "2"
+
+  it "does not keep the request in memory" $ \port' -> do
+    let req = streamReq port' "length"
+            $ S.replicate megabyte
+            $ BS.replicate 1000 97 -- 1000 MB total
+    responseBody <$> makeRequest req
+      `shouldReturn` fromString (show (1000 * megabyte :: Int))
+    bytes <- max_live_bytes <$> getRTSStats
+    bytes < 200 * megabyte `shouldBe` True
+
+  it "passes as argument the content-type" $ \port' -> do
+    let req = streamReq port' "contentType" (S.each ["h","i"])
+    responseBody <$> makeRequest req `shouldReturn` "application/json"
+    let req' = req { requestHeaders = [("Content-Type", "text/plain;charset=utf-8")] }
+    responseBody <$> makeRequest req' `shouldReturn` "text/plain;charset=utf-8"
+
+  it "responds with '415 - Unsupported Media Type' on wrong content type" $ \port' -> do
+    let req' = streamReq port' "length" (S.each ["h","i"])
+        req = req' { requestHeaders = [("Content-Type", "bla/bla")] }
+    responseStatus <$> makeRequest req `shouldReturn` status415
+
+streamResponseSpec :: Spec
+streamResponseSpec = describe "StreamResponse instance" $ around withServer $ do
+
+  it "streams the response body" $ \port' -> do
+    let req = streamReq port' "echo" (S.each ["h","i"])
+    responseBody <$> makeRequest req `shouldReturn` "hi"
+
+  it "does not keep the response in memory" $ \port' -> do
+    let req = streamReq port' "echo"
+            $ S.replicate 100
+            $ BS.replicate megabyte 97 -- 100 MB total
+    _ <- makeRequestStreamResponse req (runResourceT . S.length)
+    bytes <- max_live_bytes <$> getRTSStats
+    bytes < 200 * megabyte `shouldBe` True
+
+  it "sets the specified status code" $ \port' -> do
+    let req = streamReq port' "length" (S.each ["h","i"])
+    responseStatus <$> makeRequest req `shouldReturn` status200
+
+  it "responds with '405 - Method Not Allowed' on wrong method" $ \port' -> do
+    let req' = streamReq port' "echo" (S.each ["h","i"])
+        req = req' { method = "GET" }
+    responseStatus <$> makeRequest req `shouldReturn` status405
+
+  it "responds with '406 - Not Acceptable' on wrong content type" $ \port' -> do
+    let req' = streamReq port' "echo" (S.each ["h","i"])
+        req = req' { requestHeaders = ("Accept", "bla/bla"):requestHeaders req' }
+    responseStatus <$> makeRequest req `shouldReturn` status406
+
+  it "handles resource deallocation correctly" $ \port' -> do
+    let req' = streamReq port' "getfile" (S.each []) -- TODO: simplify
+        contents :: IsString a => a
+        contents = "foobar"
+    x <- bracket
+      (BS.writeFile "hello.txt" contents)
+      (\_ -> removeFile "hello.txt")
+      (\_ -> responseBody <$> makeRequest req')
+    x `shouldBe` fromString contents
+
+
+------------------------------------------------------------------------------
+-- API
+
+type API
+  =    "length" :> StreamBody '[JSON] :> Post '[PlainText] Int
+  :<|> "contentType" :> StreamBody '[JSON, PlainText] :> Post '[PlainText] M.MediaType
+  :<|> "echo" :> StreamBody '[JSON] :> StreamResponsePost '[JSON]
+  :<|> "getfile" :> StreamResponsePost '[PlainText]
+
+api :: Proxy API
+api = Proxy
+
+server :: Server API
+server = lengthH :<|> contentTypeH :<|> echoH :<|> getfileH
+  where
+    lengthH      (_contentType, stream')
+      = liftIO . runResourceT $ S.sum_ $ S.subst (\x -> BS.length x :> ()) stream'
+    contentTypeH (contentType, _stream')
+      = return contentType
+    echoH        (_contentType, stream')
+      = return stream'
+    getfileH = return $ BSS.toChunks (BSS.readFile "hello.txt")
+
+withServer :: (Port -> IO ()) -> IO ()
+withServer = withApplicationSettings settings (return $ serve api server)
+  where
+    settings = setTimeout 60 defaultSettings
+
+------------------------------------------------------------------------------
+-- Utils
+
+makeRequest
+  :: Request -> IO (Response BSL.ByteString)
+makeRequest req = do
+  manager <- newManager defaultManagerSettings
+  httpLbs req manager
+
+makeRequestStreamResponse
+  :: Request -> (Stream (Of ByteString) (ResourceT IO) () -> IO r) -> IO r
+makeRequestStreamResponse req responseAction = do
+  manager <- newManager defaultManagerSettings
+  withResponse req manager (responseAction . go . responseBody)
+  where
+    go :: MonadIO m => IO BS.ByteString -> Stream (Of ByteString) m ()
+    go respReader = do
+      n <- liftIO $ brRead respReader
+      if BS.null n
+      then return ()
+      else do
+        S.yield n
+        go respReader
+
+streamReq
+  :: Port -> ByteString -> Stream (Of ByteString) (ResourceT IO) () -> Request
+streamReq appPort urlPath requestStream = defaultRequest
+  { port = appPort
+  , requestHeaders = [("Content-Type", "application/json")]
+  , method = "POST"
+  , path = urlPath
+  , requestBody = streamReqBody requestStream
+  , responseTimeout = responseTimeoutNone
+  }
+  where
+    streamReqBody body
+      = RequestBodyStreamChunked body'
+      where
+        body' :: (IO BS.ByteString -> IO ()) -> IO ()
+        body' write = void . forkIO . runResourceT $ do
+          ref <- liftIO $ newIORef body
+          is <- getInternalState
+          let popper :: IO BS.ByteString
+              popper = do
+                rsrc <- readIORef ref
+                mres <- runInternalState (S.uncons rsrc) is
+                case mres of
+                  Nothing -> return BS.empty
+                  Just (bs, str)
+                    | BS.null bs -> writeIORef ref str >> popper
+                    | otherwise -> writeIORef ref str >> return bs
+          liftIO $ write popper
+
+instance Show a => MimeRender PlainText a where
+  mimeRender _ = fromString . show
+
+megabyte :: Num a => a
+megabyte = 1000 ^ 2
diff --git a/test/Spec.hs b/test/Spec.hs
new file mode 100644
--- /dev/null
+++ b/test/Spec.hs
@@ -0,0 +1,1 @@
+{-# OPTIONS_GHC -F -pgmF hspec-discover #-}
