{-# LANGUAGE DoAndIfThenElse #-}
{-# LANGUAGE OverloadedStrings #-}
{-# OPTIONS_HADDOCK hide, not-home #-}
module Network.Http.Connection (
Connection(..),
makeConnection,
withConnection,
openConnection,
openConnectionSSL,
openConnectionSSL',
openConnectionUnix,
closeConnection,
getHostname,
getRequestHeaders,
getHeadersFull,
sendRequest,
receiveResponse,
receiveResponseRaw,
unsafeReceiveResponse,
unsafeReceiveResponseRaw,
UnexpectedCompression,
receiveUpgradeResponse,
receiveConnectResponse,
unsafeWithRawStreams,
emptyBody,
fileBody,
bytestringBody,
lazyBytestringBody,
utf8TextBody,
utf8LazyTextBody,
inputStreamBody,
inputStreamBodyChunked,
debugHandler,
concatHandler
) where
import Blaze.ByteString.Builder (Builder)
import qualified Blaze.ByteString.Builder as Builder (flush, fromByteString, toByteString, fromLazyByteString)
import qualified Blaze.ByteString.Builder.Char.Utf8 as Builder (fromText, fromLazyText)
import qualified Blaze.ByteString.Builder.HTTP as Builder (chunkedTransferEncoding, chunkedTransferTerminator)
import Control.Exception (bracket)
import Data.ByteString (ByteString)
import qualified Data.ByteString.Lazy as BL
import qualified Data.ByteString.Char8 as S
import Data.Text (Text)
import qualified Data.Text.Lazy as TL (Text)
import Network.Socket
import OpenSSL (withOpenSSL)
import OpenSSL.Session (SSL, SSLContext)
import qualified OpenSSL.Session as SSL
import System.IO.Streams (InputStream, OutputStream, stdout)
import qualified System.IO.Streams as Streams
import qualified System.IO.Streams.SSL as Streams hiding (connect)
import qualified Data.Monoid as Mon
import Network.Http.Internal
import Network.Http.ResponseParser
data Connection
= Connection {
Connection -> ByteString
cHost :: !ByteString,
Connection -> IO ()
cClose :: IO (),
Connection -> OutputStream Builder
cOut :: OutputStream Builder,
Connection -> InputStream ByteString
cIn :: InputStream ByteString
}
instance Show Connection where
show :: Connection -> String
show Connection
c = {-# SCC "Connection.show" #-}
[String] -> String
forall (t :: * -> *) a. Foldable t => t [a] -> [a]
concat
[String
"Host: ",
ByteString -> String
S.unpack (ByteString -> String) -> ByteString -> String
forall a b. (a -> b) -> a -> b
$ Connection -> ByteString
cHost Connection
c,
String
"\n"]
makeConnection
:: ByteString
-> IO ()
-> OutputStream Builder
-> InputStream ByteString
-> Connection
makeConnection :: ByteString
-> IO ()
-> OutputStream Builder
-> InputStream ByteString
-> Connection
makeConnection = ByteString
-> IO ()
-> OutputStream Builder
-> InputStream ByteString
-> Connection
Connection
withConnection :: IO Connection -> (Connection -> IO γ) -> IO γ
withConnection :: forall γ. IO Connection -> (Connection -> IO γ) -> IO γ
withConnection IO Connection
mkC =
IO Connection
-> (Connection -> IO ()) -> (Connection -> IO γ) -> IO γ
forall a b c. IO a -> (a -> IO b) -> (a -> IO c) -> IO c
bracket IO Connection
mkC Connection -> IO ()
closeConnection
openConnection :: Hostname -> Port -> IO Connection
openConnection :: ByteString -> Port -> IO Connection
openConnection ByteString
h1' Port
p = do
is <- Maybe AddrInfo -> Maybe String -> Maybe String -> IO [AddrInfo]
forall (t :: * -> *).
GetAddrInfo t =>
Maybe AddrInfo -> Maybe String -> Maybe String -> IO (t AddrInfo)
getAddrInfo (AddrInfo -> Maybe AddrInfo
forall a. a -> Maybe a
Just AddrInfo
hints) (String -> Maybe String
forall a. a -> Maybe a
Just String
h1) (String -> Maybe String
forall a. a -> Maybe a
Just (String -> Maybe String) -> String -> Maybe String
forall a b. (a -> b) -> a -> b
$ Port -> String
forall a. Show a => a -> String
show Port
p)
let addr = [AddrInfo] -> AddrInfo
forall a. HasCallStack => [a] -> a
head [AddrInfo]
is
let a = AddrInfo -> SockAddr
addrAddress AddrInfo
addr
s <- socket (addrFamily addr) Stream defaultProtocol
connect s a
(i,o1) <- Streams.socketToStreams s
o2 <- Streams.builderStream o1
return Connection {
cHost = h2',
cClose = close s,
cOut = o2,
cIn = i
}
where
hints :: AddrInfo
hints = AddrInfo
defaultHints {
addrFlags = [AI_ADDRCONFIG, AI_NUMERICSERV],
addrSocketType = Stream
}
h2' :: ByteString
h2' = if Port
p Port -> Port -> Bool
forall a. Eq a => a -> a -> Bool
== Port
80
then ByteString
h1'
else [ByteString] -> ByteString
S.concat [ ByteString
h1', ByteString
":", String -> ByteString
S.pack (String -> ByteString) -> String -> ByteString
forall a b. (a -> b) -> a -> b
$ Port -> String
forall a. Show a => a -> String
show Port
p ]
h1 :: String
h1 = ByteString -> String
S.unpack ByteString
h1'
openConnectionSSL :: SSLContext -> Hostname -> Port -> IO Connection
openConnectionSSL :: SSLContext -> ByteString -> Port -> IO Connection
openConnectionSSL SSLContext
ctx ByteString
h1' = (SSL -> IO ()) -> SSLContext -> ByteString -> Port -> IO Connection
openConnectionSSL' SSL -> IO ()
modssl SSLContext
ctx ByteString
h1'
where
modssl :: SSL -> IO ()
modssl SSL
ssl = SSL -> String -> IO ()
SSL.setTlsextHostName SSL
ssl String
h1
h1 :: String
h1 = ByteString -> String
S.unpack ByteString
h1'
openConnectionSSL' :: (SSL -> IO ()) -> SSLContext -> Hostname -> Port -> IO Connection
openConnectionSSL' :: (SSL -> IO ()) -> SSLContext -> ByteString -> Port -> IO Connection
openConnectionSSL' SSL -> IO ()
modssl SSLContext
ctx ByteString
h1' Port
p = IO Connection -> IO Connection
forall a. IO a -> IO a
withOpenSSL (IO Connection -> IO Connection) -> IO Connection -> IO Connection
forall a b. (a -> b) -> a -> b
$ do
is <- Maybe AddrInfo -> Maybe String -> Maybe String -> IO [AddrInfo]
forall (t :: * -> *).
GetAddrInfo t =>
Maybe AddrInfo -> Maybe String -> Maybe String -> IO (t AddrInfo)
getAddrInfo Maybe AddrInfo
forall a. Maybe a
Nothing (String -> Maybe String
forall a. a -> Maybe a
Just String
h1) (String -> Maybe String
forall a. a -> Maybe a
Just (String -> Maybe String) -> String -> Maybe String
forall a b. (a -> b) -> a -> b
$ Port -> String
forall a. Show a => a -> String
show Port
p)
let a = AddrInfo -> SockAddr
addrAddress (AddrInfo -> SockAddr) -> AddrInfo -> SockAddr
forall a b. (a -> b) -> a -> b
$ [AddrInfo] -> AddrInfo
forall a. HasCallStack => [a] -> a
head [AddrInfo]
is
f = AddrInfo -> Family
addrFamily (AddrInfo -> Family) -> AddrInfo -> Family
forall a b. (a -> b) -> a -> b
$ [AddrInfo] -> AddrInfo
forall a. HasCallStack => [a] -> a
head [AddrInfo]
is
s <- socket f Stream defaultProtocol
connect s a
ssl <- SSL.connection ctx s
modssl ssl
SSL.connect ssl
(i,o1) <- Streams.sslToStreams ssl
o2 <- Streams.builderStream o1
return Connection {
cHost = h2',
cClose = closeSSL s ssl,
cOut = o2,
cIn = i
}
where
h2' :: ByteString
h2' :: ByteString
h2' = if Port
p Port -> Port -> Bool
forall a. Eq a => a -> a -> Bool
== Port
443
then ByteString
h1'
else [ByteString] -> ByteString
S.concat [ ByteString
h1', ByteString
":", String -> ByteString
S.pack (String -> ByteString) -> String -> ByteString
forall a b. (a -> b) -> a -> b
$ Port -> String
forall a. Show a => a -> String
show Port
p ]
h1 :: String
h1 = ByteString -> String
S.unpack ByteString
h1'
closeSSL :: Socket -> SSL -> IO ()
closeSSL :: Socket -> SSL -> IO ()
closeSSL Socket
s SSL
ssl = do
SSL -> ShutdownType -> IO ()
SSL.shutdown SSL
ssl ShutdownType
SSL.Unidirectional
Socket -> IO ()
close Socket
s
openConnectionUnix :: FilePath -> IO Connection
openConnectionUnix :: String -> IO Connection
openConnectionUnix String
path = do
let a :: SockAddr
a = String -> SockAddr
SockAddrUnix String
path
s <- Family -> SocketType -> ProtocolNumber -> IO Socket
socket Family
AF_UNIX SocketType
Stream ProtocolNumber
defaultProtocol
connect s a
(i,o1) <- Streams.socketToStreams s
o2 <- Streams.builderStream o1
return Connection {
cHost = S.pack path,
cClose = close s,
cOut = o2,
cIn = i
}
sendRequest :: Connection -> Request -> (OutputStream Builder -> IO α) -> IO α
sendRequest :: forall α.
Connection -> Request -> (OutputStream Builder -> IO α) -> IO α
sendRequest Connection
c Request
q OutputStream Builder -> IO α
handler = do
Maybe Builder -> OutputStream Builder -> IO ()
forall a. Maybe a -> OutputStream a -> IO ()
Streams.write (Builder -> Maybe Builder
forall a. a -> Maybe a
Just Builder
msg) OutputStream Builder
o2
e2 <- case ExpectMode
t of
ExpectMode
Normal -> do
EntityBody -> IO EntityBody
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return EntityBody
e
ExpectMode
Continue -> do
Maybe Builder -> OutputStream Builder -> IO ()
forall a. Maybe a -> OutputStream a -> IO ()
Streams.write (Builder -> Maybe Builder
forall a. a -> Maybe a
Just Builder
Builder.flush) OutputStream Builder
o2
p <- InputStream ByteString -> IO Response
readResponseHeader InputStream ByteString
i
case getStatusCode p of
Int
100 -> do
EntityBody -> IO EntityBody
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return EntityBody
e
Int
_ -> do
ByteString -> InputStream ByteString -> IO ()
forall a. a -> InputStream a -> IO ()
Streams.unRead (Response -> ByteString
rsp Response
p) InputStream ByteString
i
EntityBody -> IO EntityBody
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return EntityBody
Empty
x <- case e2 of
EntityBody
Empty -> do
o3 <- IO (OutputStream Builder)
forall a. IO (OutputStream a)
Streams.nullOutput
y <- handler o3
return y
EntityBody
Chunking -> do
o3 <- (Builder -> Builder)
-> OutputStream Builder -> IO (OutputStream Builder)
forall a b. (a -> b) -> OutputStream b -> IO (OutputStream a)
Streams.contramap Builder -> Builder
Builder.chunkedTransferEncoding OutputStream Builder
o2
y <- handler o3
Streams.write (Just Builder.chunkedTransferTerminator) o2
return y
(Static Int64
_) -> do
y <- OutputStream Builder -> IO α
handler OutputStream Builder
o2
return y
Streams.write (Just Builder.flush) o2
return x
where
o2 :: OutputStream Builder
o2 = Connection -> OutputStream Builder
cOut Connection
c
e :: EntityBody
e = Request -> EntityBody
qBody Request
q
t :: ExpectMode
t = Request -> ExpectMode
qExpect Request
q
msg :: Builder
msg = Request -> ByteString -> Builder
composeRequestBytes Request
q ByteString
h'
h' :: ByteString
h' = Connection -> ByteString
cHost Connection
c
i :: InputStream ByteString
i = Connection -> InputStream ByteString
cIn Connection
c
rsp :: Response -> ByteString
rsp Response
p = Builder -> ByteString
Builder.toByteString (Builder -> ByteString) -> Builder -> ByteString
forall a b. (a -> b) -> a -> b
$ Response -> Builder
composeResponseBytes Response
p
getHostname :: Connection -> Request -> ByteString
getHostname :: Connection -> Request -> ByteString
getHostname Connection
c Request
q =
case Request -> Maybe ByteString
qHost Request
q of
Just ByteString
h' -> ByteString
h'
Maybe ByteString
Nothing -> Connection -> ByteString
cHost Connection
c
{-# DEPRECATED getRequestHeaders "use retrieveHeaders . getHeadersFull instead" #-}
getRequestHeaders :: Connection -> Request -> [(ByteString, ByteString)]
Connection
c Request
q =
(ByteString
"Host", Connection -> Request -> ByteString
getHostname Connection
c Request
q) (ByteString, ByteString)
-> [(ByteString, ByteString)] -> [(ByteString, ByteString)]
forall a. a -> [a] -> [a]
: [(ByteString, ByteString)]
kvs
where
h :: Headers
h = Request -> Headers
qHeaders Request
q
kvs :: [(ByteString, ByteString)]
kvs = Headers -> [(ByteString, ByteString)]
retrieveHeaders Headers
h
getHeadersFull :: Connection -> Request -> Headers
Connection
c Request
q =
Headers
h'
where
h :: Headers
h = Request -> Headers
qHeaders Request
q
h' :: Headers
h' = Headers -> ByteString -> ByteString -> Headers
updateHeader Headers
h ByteString
"Host" (Connection -> Request -> ByteString
getHostname Connection
c Request
q)
receiveResponse :: Connection -> (Response -> InputStream ByteString -> IO β) -> IO β
receiveResponse :: forall β.
Connection -> (Response -> InputStream ByteString -> IO β) -> IO β
receiveResponse Connection
c Response -> InputStream ByteString -> IO β
handler = do
p <- InputStream ByteString -> IO Response
readResponseHeader InputStream ByteString
i
i' <- readResponseBody p i
x <- handler p i'
Streams.skipToEof i'
return x
where
i :: InputStream ByteString
i = Connection -> InputStream ByteString
cIn Connection
c
receiveResponseRaw :: Connection -> (Response -> InputStream ByteString -> IO β) -> IO β
receiveResponseRaw :: forall β.
Connection -> (Response -> InputStream ByteString -> IO β) -> IO β
receiveResponseRaw Connection
c Response -> InputStream ByteString -> IO β
handler = do
p <- InputStream ByteString -> IO Response
readResponseHeader InputStream ByteString
i
let p' = Response
p {
pContentEncoding = Identity
}
i' <- readResponseBody p' i
x <- handler p i'
Streams.skipToEof i'
return x
where
i :: InputStream ByteString
i = Connection -> InputStream ByteString
cIn Connection
c
unsafeReceiveResponse :: Connection -> (Response -> InputStream ByteString -> IO β) -> IO β
unsafeReceiveResponse :: forall β.
Connection -> (Response -> InputStream ByteString -> IO β) -> IO β
unsafeReceiveResponse Connection
c Response -> InputStream ByteString -> IO β
handler = do
p <- InputStream ByteString -> IO Response
readResponseHeader InputStream ByteString
i
i' <- readResponseBody p i
handler p i'
where
i :: InputStream ByteString
i = Connection -> InputStream ByteString
cIn Connection
c
unsafeReceiveResponseRaw :: Connection -> (Response -> InputStream ByteString -> IO β) -> IO β
unsafeReceiveResponseRaw :: forall β.
Connection -> (Response -> InputStream ByteString -> IO β) -> IO β
unsafeReceiveResponseRaw Connection
c Response -> InputStream ByteString -> IO β
handler = do
p <- InputStream ByteString -> IO Response
readResponseHeader InputStream ByteString
i
i' <- readResponseBody p { pContentEncoding = Identity } i
handler p i'
where
i :: InputStream ByteString
i = Connection -> InputStream ByteString
cIn Connection
c
unsafeWithRawStreams :: Connection -> (InputStream ByteString -> OutputStream Builder -> IO a) -> IO a
unsafeWithRawStreams :: forall a.
Connection
-> (InputStream ByteString -> OutputStream Builder -> IO a) -> IO a
unsafeWithRawStreams Connection
conn InputStream ByteString -> OutputStream Builder -> IO a
act = InputStream ByteString -> OutputStream Builder -> IO a
act (Connection -> InputStream ByteString
cIn Connection
conn) (Connection -> OutputStream Builder
cOut Connection
conn)
receiveUpgradeResponse :: Connection
-> (Response -> InputStream ByteString -> IO a)
-> (Response -> InputStream ByteString -> OutputStream Builder -> IO a)
-> IO a
receiveUpgradeResponse :: forall a.
Connection
-> (Response -> InputStream ByteString -> IO a)
-> (Response
-> InputStream ByteString -> OutputStream Builder -> IO a)
-> IO a
receiveUpgradeResponse Connection
c Response -> InputStream ByteString -> IO a
handler Response -> InputStream ByteString -> OutputStream Builder -> IO a
handler2 = do
p <- InputStream ByteString -> IO Response
readResponseHeader InputStream ByteString
i
case pStatusCode p of
Int
101 -> Response -> InputStream ByteString -> OutputStream Builder -> IO a
handler2 Response
p InputStream ByteString
i (Connection -> OutputStream Builder
cOut Connection
c)
Int
_ -> do
i' <- Response -> InputStream ByteString -> IO (InputStream ByteString)
readResponseBody Response
p InputStream ByteString
i
x <- handler p i'
Streams.skipToEof i'
return x
where
i :: InputStream ByteString
i = Connection -> InputStream ByteString
cIn Connection
c
receiveConnectResponse :: Connection
-> (Response -> InputStream ByteString -> IO a)
-> (Response -> InputStream ByteString -> OutputStream Builder -> IO a)
-> IO a
receiveConnectResponse :: forall a.
Connection
-> (Response -> InputStream ByteString -> IO a)
-> (Response
-> InputStream ByteString -> OutputStream Builder -> IO a)
-> IO a
receiveConnectResponse Connection
c Response -> InputStream ByteString -> IO a
handler Response -> InputStream ByteString -> OutputStream Builder -> IO a
handler2 = do
p <- InputStream ByteString -> IO Response
readResponseHeader InputStream ByteString
i
case pStatusCode p of
Int
code | Int
code Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
>= Int
200, Int
code Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
300 -> Response -> InputStream ByteString -> OutputStream Builder -> IO a
handler2 Response
p InputStream ByteString
i (Connection -> OutputStream Builder
cOut Connection
c)
Int
_ -> do
i' <- Response -> InputStream ByteString -> IO (InputStream ByteString)
readResponseBody Response
p InputStream ByteString
i
x <- handler p i'
Streams.skipToEof i'
return x
where
i :: InputStream ByteString
i = Connection -> InputStream ByteString
cIn Connection
c
emptyBody :: OutputStream Builder -> IO ()
emptyBody :: OutputStream Builder -> IO ()
emptyBody OutputStream Builder
_ = () -> IO ()
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ()
bytestringBody :: ByteString -> OutputStream Builder -> IO ()
bytestringBody :: ByteString -> OutputStream Builder -> IO ()
bytestringBody ByteString
bs = Maybe Builder -> OutputStream Builder -> IO ()
forall a. Maybe a -> OutputStream a -> IO ()
Streams.write (Builder -> Maybe Builder
forall a. a -> Maybe a
Just (Builder -> Maybe Builder) -> Builder -> Maybe Builder
forall a b. (a -> b) -> a -> b
$! ByteString -> Builder
Builder.fromByteString ByteString
bs)
lazyBytestringBody :: BL.ByteString -> OutputStream Builder -> IO ()
lazyBytestringBody :: ByteString -> OutputStream Builder -> IO ()
lazyBytestringBody ByteString
bs = Maybe Builder -> OutputStream Builder -> IO ()
forall a. Maybe a -> OutputStream a -> IO ()
Streams.write (Builder -> Maybe Builder
forall a. a -> Maybe a
Just (Builder -> Maybe Builder) -> Builder -> Maybe Builder
forall a b. (a -> b) -> a -> b
$ ByteString -> Builder
Builder.fromLazyByteString ByteString
bs)
utf8TextBody :: Text -> OutputStream Builder -> IO ()
utf8TextBody :: Text -> OutputStream Builder -> IO ()
utf8TextBody Text
t = Maybe Builder -> OutputStream Builder -> IO ()
forall a. Maybe a -> OutputStream a -> IO ()
Streams.write (Builder -> Maybe Builder
forall a. a -> Maybe a
Just (Builder -> Maybe Builder) -> Builder -> Maybe Builder
forall a b. (a -> b) -> a -> b
$! Text -> Builder
Builder.fromText Text
t)
utf8LazyTextBody :: TL.Text -> OutputStream Builder -> IO ()
utf8LazyTextBody :: Text -> OutputStream Builder -> IO ()
utf8LazyTextBody Text
t = Maybe Builder -> OutputStream Builder -> IO ()
forall a. Maybe a -> OutputStream a -> IO ()
Streams.write (Builder -> Maybe Builder
forall a. a -> Maybe a
Just (Builder -> Maybe Builder) -> Builder -> Maybe Builder
forall a b. (a -> b) -> a -> b
$ Text -> Builder
Builder.fromLazyText Text
t)
fileBody :: FilePath -> OutputStream Builder -> IO ()
fileBody :: String -> OutputStream Builder -> IO ()
fileBody String
p OutputStream Builder
o = do
String -> (InputStream ByteString -> IO ()) -> IO ()
forall a. String -> (InputStream ByteString -> IO a) -> IO a
Streams.withFileAsInput String
p (\InputStream ByteString
i -> InputStream ByteString -> OutputStream Builder -> IO ()
inputStreamBody InputStream ByteString
i OutputStream Builder
o)
inputStreamBody :: InputStream ByteString -> OutputStream Builder -> IO ()
inputStreamBody :: InputStream ByteString -> OutputStream Builder -> IO ()
inputStreamBody InputStream ByteString
i1 OutputStream Builder
o = do
i2 <- (ByteString -> Builder)
-> InputStream ByteString -> IO (InputStream Builder)
forall a b. (a -> b) -> InputStream a -> IO (InputStream b)
Streams.map ByteString -> Builder
Builder.fromByteString InputStream ByteString
i1
Streams.supply i2 o
inputStreamBodyChunked :: Int -> InputStream ByteString -> OutputStream Builder -> IO ()
inputStreamBodyChunked :: Int -> InputStream ByteString -> OutputStream Builder -> IO ()
inputStreamBodyChunked Int
maxChunkSize InputStream ByteString
i OutputStream Builder
o
| Int
maxChunkSize Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
> Int
0 = IO ()
go
| Bool
otherwise = InputStream ByteString -> OutputStream Builder -> IO ()
inputStreamBody InputStream ByteString
i OutputStream Builder
o
where
go :: IO ()
go = do
mchunk <- InputStream ByteString -> IO (Maybe ByteString)
forall a. InputStream a -> IO (Maybe a)
Streams.read InputStream ByteString
i
case mchunk of
Maybe ByteString
Nothing -> () -> IO ()
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ()
Just ByteString
chunk
| Int
chunkLen Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
<= Int
maxChunkSize -> do
Maybe Builder -> OutputStream Builder -> IO ()
forall a. Maybe a -> OutputStream a -> IO ()
Streams.write (Builder -> Maybe Builder
forall a. a -> Maybe a
Just (Builder -> Maybe Builder) -> Builder -> Maybe Builder
forall a b. (a -> b) -> a -> b
$! ByteString -> Builder
Builder.fromByteString ByteString
chunk) OutputStream Builder
o
IO ()
go
| Bool
otherwise -> do
let (ByteString
chunk1,ByteString
rest) | Int
chunkLen Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
2Int -> Int -> Int
forall a. Num a => a -> a -> a
*Int
maxChunkSize = Int -> ByteString -> (ByteString, ByteString)
S.splitAt (Int
chunkLen Int -> Int -> Int
forall a. Integral a => a -> a -> a
`quot` Int
2) ByteString
chunk
| Bool
otherwise = Int -> ByteString -> (ByteString, ByteString)
S.splitAt Int
maxChunkSize ByteString
chunk
ByteString -> InputStream ByteString -> IO ()
forall a. a -> InputStream a -> IO ()
Streams.unRead ByteString
rest InputStream ByteString
i
Maybe Builder -> OutputStream Builder -> IO ()
forall a. Maybe a -> OutputStream a -> IO ()
Streams.write (Builder -> Maybe Builder
forall a. a -> Maybe a
Just (Builder -> Maybe Builder) -> Builder -> Maybe Builder
forall a b. (a -> b) -> a -> b
$! ByteString -> Builder
Builder.fromByteString ByteString
chunk1) OutputStream Builder
o
IO ()
go
where
chunkLen :: Int
chunkLen = ByteString -> Int
S.length ByteString
chunk
debugHandler :: Response -> InputStream ByteString -> IO ()
debugHandler :: Response -> InputStream ByteString -> IO ()
debugHandler Response
p InputStream ByteString
i = do
ByteString -> IO ()
S.putStr (ByteString -> IO ()) -> ByteString -> IO ()
forall a b. (a -> b) -> a -> b
$ (Char -> Bool) -> ByteString -> ByteString
S.filter (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
/= Char
'\r') (ByteString -> ByteString) -> ByteString -> ByteString
forall a b. (a -> b) -> a -> b
$ Builder -> ByteString
Builder.toByteString (Builder -> ByteString) -> Builder -> ByteString
forall a b. (a -> b) -> a -> b
$ Response -> Builder
composeResponseBytes Response
p
InputStream ByteString -> OutputStream ByteString -> IO ()
forall a. InputStream a -> OutputStream a -> IO ()
Streams.connect InputStream ByteString
i OutputStream ByteString
stdout
concatHandler :: Response -> InputStream ByteString -> IO ByteString
concatHandler :: Response -> InputStream ByteString -> IO ByteString
concatHandler Response
_ InputStream ByteString
i1 = do
i2 <- (ByteString -> Builder)
-> InputStream ByteString -> IO (InputStream Builder)
forall a b. (a -> b) -> InputStream a -> IO (InputStream b)
Streams.map ByteString -> Builder
Builder.fromByteString InputStream ByteString
i1
x <- Streams.fold Mon.mappend Mon.mempty i2
return $ Builder.toByteString x
closeConnection :: Connection -> IO ()
closeConnection :: Connection -> IO ()
closeConnection Connection
c = Connection -> IO ()
cClose Connection
c