Skip to content

Commit ca482af

Browse files
committed
Add servant-io-streams
1 parent 13cbac8 commit ca482af

File tree

13 files changed

+286
-2
lines changed

13 files changed

+286
-2
lines changed

.github/workflows/master.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ jobs:
7575
(cd servant-machines && eval $DOCTEST)
7676
(cd servant-conduit && eval $DOCTEST)
7777
(cd servant-pipes && eval $DOCTEST)
78+
(cd servant-io-streams && eval $DOCTEST)
7879
7980
# stack:
8081
# name: stack / ghc ${{ matrix.ghc }}

cabal.project

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ packages:
2020
servant-machines/
2121
servant-conduit/
2222
servant-pipes/
23+
servant-io-streams/
2324

2425
-- servant GHCJS
2526
-- packages:

default.nix

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ let
1616
servant-foreign = self.callCabal2nix "servant-foreign" ./servant-foreign {};
1717
servant-conduit = self.callCabal2nix "servant-conduit" ./servant-conduit {};
1818
servant-machines = self.callCabal2nix "servant-machines" ./servant-machines {};
19+
servant-io-streams = self.callCabal2nix "servant-io-streams" ./servant-io-streams {};
1920
servant-client-core = self.callCabal2nix "servant-client-core" ./servant-client-core {};
2021
servant-http-streams = self.callCabal2nix "servant-http-streams" ./servant-http-streams {};
2122
};
@@ -33,6 +34,7 @@ in
3334
servant-http-streams
3435
servant-machines
3536
servant-pipes
37+
servant-io-streams
3638
servant-server;
3739
}
3840

doc/cookbook/basic-streaming/Streaming.lhs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,9 @@ In other words, without streaming libraries.
1010
We have bindings for them though.
1111
- Similar example is bundled with each of our streaming library interop packages (see
1212
[servant-pipes](https://github.com/haskell-servant/servant/blob/master/servant-pipes/example/Main.hs),
13-
[servant-conduit](https://github.com/haskell-servant/servant/blob/master/servant-conduit/example/Main.hs) and
14-
[servant-machines](https://github.com/haskell-servant/servant/blob/master/servant-machines/example/Main.hs))
13+
[servant-conduit](https://github.com/haskell-servant/servant/blob/master/servant-conduit/example/Main.hs),
14+
[servant-machines](https://github.com/haskell-servant/servant/blob/master/servant-machines/example/Main.hs) and
15+
[servant-io-streams](https://github.com/haskell-servant/servant/blob/master/servant-io-streams/example/Main.hs))
1516
- `SourceT` doesn't have *Prelude* with handy combinators, so we have to write
1617
things ourselves. (Note to self: `mapM` and `foldM` would be handy to have).
1718

servant-io-streams/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
0.1
2+
----
3+
4+
- First release

servant-io-streams/LICENSE

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
Copyright (c) 2023, Servant Contributors
2+
3+
All rights reserved.
4+
5+
Redistribution and use in source and binary forms, with or without
6+
modification, are permitted provided that the following conditions are met:
7+
8+
* Redistributions of source code must retain the above copyright
9+
notice, this list of conditions and the following disclaimer.
10+
11+
* Redistributions in binary form must reproduce the above
12+
copyright notice, this list of conditions and the following
13+
disclaimer in the documentation and/or other materials provided
14+
with the distribution.
15+
16+
* Neither the name of Servant Contributors nor the names of other
17+
contributors may be used to endorse or promote products derived
18+
from this software without specific prior written permission.
19+
20+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24+
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26+
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28+
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

servant-io-streams/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# servant-io-streams - Servant Stream support for io-streams
2+
3+
![servant](https://raw.githubusercontent.com/haskell-servant/servant/master/servant.png)

servant-io-streams/Setup.hs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
import Distribution.Simple
2+
main = defaultMain

servant-io-streams/example/Main.hs

Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
{-# LANGUAGE DataKinds #-}
2+
{-# LANGUAGE DeriveGeneric #-}
3+
{-# LANGUAGE TypeOperators #-}
4+
module Main (main) where
5+
6+
import Prelude ()
7+
import Prelude.Compat
8+
9+
import Control.Concurrent
10+
(threadDelay)
11+
import Control.Monad.IO.Class
12+
(MonadIO (..))
13+
import qualified Data.ByteString as BS
14+
import Data.Maybe
15+
(fromMaybe)
16+
import Network.HTTP.Client
17+
(defaultManagerSettings, newManager)
18+
import System.Environment
19+
(getArgs, lookupEnv)
20+
import System.IO
21+
(IOMode (..), openFile, hClose)
22+
import Text.Read
23+
(readMaybe)
24+
25+
import qualified System.IO.Streams as IOS
26+
import System.IO.Streams.Combinators
27+
(atEndOfInput)
28+
import System.IO.Streams.Handle
29+
(handleToInputStream)
30+
import Servant
31+
import Servant.Client.Streaming
32+
import Servant.IO.Streams ()
33+
34+
import qualified Network.Wai.Handler.Warp as Warp
35+
36+
type FastAPI = "get" :> Capture "num" Int :> StreamGet NewlineFraming JSON (IOS.InputStream Int)
37+
38+
type API = FastAPI
39+
:<|> "slow" :> Capture "num" Int :> StreamGet NewlineFraming JSON (IOS.InputStream Int)
40+
:<|> "readme" :> StreamGet NoFraming OctetStream (IOS.InputStream BS.ByteString)
41+
-- we can have streaming request body
42+
:<|> "proxy"
43+
:> StreamBody NoFraming OctetStream (IOS.InputStream BS.ByteString)
44+
:> StreamPost NoFraming OctetStream (IOS.InputStream BS.ByteString)
45+
46+
api :: Proxy API
47+
api = Proxy
48+
49+
server :: Server API
50+
server = fast :<|> slow :<|> readme :<|> proxy
51+
where
52+
fast n = liftIO $ do
53+
putStrLn ("/get/" ++ show n)
54+
IOS.fromGenerator $ fastGenerator n
55+
56+
slow n = liftIO $ do
57+
putStrLn ("/slow/" ++ show n)
58+
IOS.fromGenerator $ slowGenerator n
59+
60+
readme = liftIO $ do
61+
putStrLn "/readme"
62+
h <- openFile "README.md" ReadMode
63+
is <- handleToInputStream h
64+
atEndOfInput (hClose h) is
65+
66+
proxy c = liftIO $ do
67+
putStrLn "/proxy"
68+
return c
69+
70+
fastGenerator n
71+
| n < 0 = return ()
72+
| otherwise = IOS.yield n >> fastGenerator (n - 1)
73+
74+
slowGenerator n
75+
| n < 0 = return ()
76+
| otherwise = IOS.yield n >> liftIO (threadDelay 1000000) >> slowGenerator (n - 1)
77+
78+
app :: Application
79+
app = serve api server
80+
81+
cli :: Client ClientM FastAPI
82+
cli :<|> _ :<|> _ :<|> _ = client api
83+
84+
main :: IO ()
85+
main = do
86+
args <- getArgs
87+
case args of
88+
("server":_) -> do
89+
putStrLn "Starting servant-io-streams:example at http://localhost:8000"
90+
port <- fromMaybe 8000 . (>>= readMaybe) <$> lookupEnv "PORT"
91+
Warp.run port app
92+
("client":ns:_) -> do
93+
n <- maybe (fail $ "not a number: " ++ ns) pure $ readMaybe ns
94+
mgr <- newManager defaultManagerSettings
95+
burl <- parseBaseUrl "http://localhost:8000/"
96+
withClientM (cli n) (mkClientEnv mgr burl) $ \me -> case me of
97+
Left err -> print err
98+
Right s -> do
99+
x <- IOS.fold (\c _ -> c + 1) (0 :: Int) s
100+
print x
101+
_ -> do
102+
putStrLn "Try:"
103+
putStrLn "cabal new-run servant-io-streams:example server"
104+
putStrLn "cabal new-run servant-io-streams:example client 10"
105+
putStrLn "time curl -H 'Accept: application/json' localhost:8000/slow/5"
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
cabal-version: 2.2
2+
name: servant-io-streams
3+
version: 0.1
4+
5+
synopsis: Servant Stream support for io-streams
6+
category: Servant, Web, io-streams
7+
description: Servant Stream support for io-streams.
8+
.
9+
Provides 'ToSourceIO' and 'FromSourceIO' instances for 'InputStream'.
10+
11+
homepage: http://docs.servant.dev/
12+
bug-reports: http://github.com/haskell-servant/servant/issues
13+
license: BSD-3-Clause
14+
license-file: LICENSE
15+
author: Servant Contributors
16+
maintainer: [email protected]
17+
copyright: 2023 Servant Contributors
18+
build-type: Simple
19+
tested-with: GHC ==8.6.5 || ==8.8.4 || ==8.10.2
20+
21+
extra-source-files:
22+
CHANGELOG.md
23+
24+
source-repository head
25+
type: git
26+
location: http://github.com/haskell-servant/servant.git
27+
28+
library
29+
exposed-modules: Servant.IO.Streams
30+
build-depends:
31+
base >=4.9 && <5
32+
, io-streams ^>=1.5
33+
, servant >=0.15 && <0.20
34+
hs-source-dirs: src
35+
default-language: Haskell2010
36+
ghc-options: -Wall
37+
38+
test-suite example
39+
type: exitcode-stdio-1.0
40+
main-is: Main.hs
41+
hs-source-dirs:
42+
example
43+
ghc-options: -Wall -rtsopts -threaded
44+
build-depends:
45+
base
46+
, base-compat
47+
, bytestring
48+
, http-media
49+
, servant
50+
, servant-io-streams
51+
, io-streams ^>= 1.5
52+
, servant-server >=0.15 && <0.20
53+
, servant-client >=0.15 && <0.20
54+
, wai >=3.2.1.2 && <3.3
55+
, warp >=3.2.25 && <3.4
56+
, http-client
57+
default-language: Haskell2010

0 commit comments

Comments
 (0)