{-# LANGUAGE NoImplicitPrelude #-}
module Wuss
( runSecureClient,
newSecureClientConnection,
runSecureClientWith,
newSecureClientConnectionWith,
Config (..),
defaultConfig,
runSecureClientWithConfig,
newSecureClientConnectionWithConfig,
)
where
import qualified Control.Applicative as Applicative
import qualified Control.Exception as Exception
import qualified Control.Monad.Catch as Catch
import qualified Control.Monad.IO.Class as MonadIO
import qualified Data.ByteString as StrictBytes
import qualified Data.ByteString.Lazy as LazyBytes
import qualified Data.Default as Default
import qualified Data.Maybe as Maybe
import qualified Data.String as String
import qualified Network.Connection as Connection
import qualified Network.Socket as Socket
import qualified Network.WebSockets as WebSockets
import qualified Network.WebSockets.Stream as Stream
import qualified System.IO as IO
import qualified System.IO.Error as IO.Error
import Prelude (($), (.))
runSecureClient ::
(MonadIO.MonadIO m) =>
(Catch.MonadMask m) =>
Socket.HostName ->
Socket.PortNumber ->
String.String ->
WebSockets.ClientApp a ->
m a
runSecureClient :: forall (m :: * -> *) a.
(MonadIO m, MonadMask m) =>
HostName -> PortNumber -> HostName -> ClientApp a -> m a
runSecureClient HostName
host PortNumber
port HostName
path ClientApp a
app = do
let options :: ConnectionOptions
options = ConnectionOptions
WebSockets.defaultConnectionOptions
HostName
-> PortNumber
-> HostName
-> ConnectionOptions
-> Headers
-> ClientApp a
-> m a
forall (m :: * -> *) a.
(MonadIO m, MonadMask m) =>
HostName
-> PortNumber
-> HostName
-> ConnectionOptions
-> Headers
-> ClientApp a
-> m a
runSecureClientWith HostName
host PortNumber
port HostName
path ConnectionOptions
options [] ClientApp a
app
newSecureClientConnection ::
(MonadIO.MonadIO m) =>
(Catch.MonadMask m) =>
Socket.HostName ->
Socket.PortNumber ->
String.String ->
m (WebSockets.Connection, IO.IO ())
newSecureClientConnection :: forall (m :: * -> *).
(MonadIO m, MonadMask m) =>
HostName -> PortNumber -> HostName -> m (Connection, IO ())
newSecureClientConnection HostName
host PortNumber
port HostName
path = do
let options :: ConnectionOptions
options = ConnectionOptions
WebSockets.defaultConnectionOptions
HostName
-> PortNumber
-> HostName
-> ConnectionOptions
-> Headers
-> m (Connection, IO ())
forall (m :: * -> *).
(MonadIO m, MonadMask m) =>
HostName
-> PortNumber
-> HostName
-> ConnectionOptions
-> Headers
-> m (Connection, IO ())
newSecureClientConnectionWith HostName
host PortNumber
port HostName
path ConnectionOptions
options []
runSecureClientWith ::
(MonadIO.MonadIO m) =>
(Catch.MonadMask m) =>
Socket.HostName ->
Socket.PortNumber ->
String.String ->
WebSockets.ConnectionOptions ->
WebSockets.Headers ->
WebSockets.ClientApp a ->
m a
runSecureClientWith :: forall (m :: * -> *) a.
(MonadIO m, MonadMask m) =>
HostName
-> PortNumber
-> HostName
-> ConnectionOptions
-> Headers
-> ClientApp a
-> m a
runSecureClientWith HostName
host PortNumber
port HostName
path ConnectionOptions
options Headers
headers ClientApp a
app = do
let config :: Config
config = Config
defaultConfig
HostName
-> PortNumber
-> HostName
-> Config
-> ConnectionOptions
-> Headers
-> ClientApp a
-> m a
forall (m :: * -> *) a.
(MonadIO m, MonadMask m) =>
HostName
-> PortNumber
-> HostName
-> Config
-> ConnectionOptions
-> Headers
-> ClientApp a
-> m a
runSecureClientWithConfig HostName
host PortNumber
port HostName
path Config
config ConnectionOptions
options Headers
headers ClientApp a
app
newSecureClientConnectionWith ::
(MonadIO.MonadIO m) =>
(Catch.MonadMask m) =>
Socket.HostName ->
Socket.PortNumber ->
String.String ->
WebSockets.ConnectionOptions ->
WebSockets.Headers ->
m (WebSockets.Connection, IO.IO ())
newSecureClientConnectionWith :: forall (m :: * -> *).
(MonadIO m, MonadMask m) =>
HostName
-> PortNumber
-> HostName
-> ConnectionOptions
-> Headers
-> m (Connection, IO ())
newSecureClientConnectionWith HostName
host PortNumber
port HostName
path ConnectionOptions
options Headers
headers = do
let config :: Config
config = Config
defaultConfig
HostName
-> PortNumber
-> HostName
-> Config
-> ConnectionOptions
-> Headers
-> m (Connection, IO ())
forall (m :: * -> *).
(MonadIO m, MonadMask m) =>
HostName
-> PortNumber
-> HostName
-> Config
-> ConnectionOptions
-> Headers
-> m (Connection, IO ())
newSecureClientConnectionWithConfig HostName
host PortNumber
port HostName
path Config
config ConnectionOptions
options Headers
headers
newtype Config = Config
{
Config -> Connection -> IO ByteString
connectionGet :: Connection.Connection -> IO.IO StrictBytes.ByteString
}
defaultConfig :: Config
defaultConfig :: Config
defaultConfig = do
Config {connectionGet :: Connection -> IO ByteString
connectionGet = Connection -> IO ByteString
Connection.connectionGetChunk}
runSecureClientWithConfig ::
(MonadIO.MonadIO m) =>
(Catch.MonadMask m) =>
Socket.HostName ->
Socket.PortNumber ->
String.String ->
Config ->
WebSockets.ConnectionOptions ->
WebSockets.Headers ->
WebSockets.ClientApp a ->
m a
runSecureClientWithConfig :: forall (m :: * -> *) a.
(MonadIO m, MonadMask m) =>
HostName
-> PortNumber
-> HostName
-> Config
-> ConnectionOptions
-> Headers
-> ClientApp a
-> m a
runSecureClientWithConfig HostName
host PortNumber
port HostName
path Config
config ConnectionOptions
options Headers
headers ClientApp a
app =
m (Connection, IO ())
-> ((Connection, IO ()) -> m ())
-> ((Connection, IO ()) -> m a)
-> m a
forall (m :: * -> *) a c b.
(HasCallStack, MonadMask m) =>
m a -> (a -> m c) -> (a -> m b) -> m b
Catch.bracket
(HostName
-> PortNumber
-> HostName
-> Config
-> ConnectionOptions
-> Headers
-> m (Connection, IO ())
forall (m :: * -> *).
(MonadIO m, MonadMask m) =>
HostName
-> PortNumber
-> HostName
-> Config
-> ConnectionOptions
-> Headers
-> m (Connection, IO ())
newSecureClientConnectionWithConfig HostName
host PortNumber
port HostName
path Config
config ConnectionOptions
options Headers
headers)
(\(Connection
_, IO ()
close) -> IO () -> m ()
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
MonadIO.liftIO IO ()
close)
(\(Connection
conn, IO ()
_) -> IO a -> m a
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
MonadIO.liftIO (ClientApp a
app Connection
conn))
newSecureClientConnectionWithConfig ::
(MonadIO.MonadIO m) =>
(Catch.MonadMask m) =>
Socket.HostName ->
Socket.PortNumber ->
String.String ->
Config ->
WebSockets.ConnectionOptions ->
WebSockets.Headers ->
m (WebSockets.Connection, IO.IO ())
newSecureClientConnectionWithConfig :: forall (m :: * -> *).
(MonadIO m, MonadMask m) =>
HostName
-> PortNumber
-> HostName
-> Config
-> ConnectionOptions
-> Headers
-> m (Connection, IO ())
newSecureClientConnectionWithConfig HostName
host PortNumber
port HostName
path Config
config ConnectionOptions
options Headers
headers = do
ConnectionContext
context <- IO ConnectionContext -> m ConnectionContext
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
MonadIO.liftIO IO ConnectionContext
Connection.initConnectionContext
m Connection
-> (Connection -> m ())
-> (Connection -> m (Connection, IO ()))
-> m (Connection, IO ())
forall (m :: * -> *) a c b.
(HasCallStack, MonadMask m) =>
m a -> (a -> m c) -> (a -> m b) -> m b
Catch.bracketOnError
(IO Connection -> m Connection
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
MonadIO.liftIO (IO Connection -> m Connection) -> IO Connection -> m Connection
forall a b. (a -> b) -> a -> b
$ ConnectionContext -> ConnectionParams -> IO Connection
Connection.connectTo ConnectionContext
context (HostName -> PortNumber -> ConnectionParams
connectionParams HostName
host PortNumber
port))
(IO () -> m ()
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
MonadIO.liftIO (IO () -> m ()) -> (Connection -> IO ()) -> Connection -> m ()
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Connection -> IO ()
Connection.connectionClose)
( \Connection
connection -> IO (Connection, IO ()) -> m (Connection, IO ())
forall a. IO a -> m a
forall (m :: * -> *) a. MonadIO m => IO a -> m a
MonadIO.liftIO (IO (Connection, IO ()) -> m (Connection, IO ()))
-> IO (Connection, IO ()) -> m (Connection, IO ())
forall a b. (a -> b) -> a -> b
$ do
Stream
stream <-
IO (Maybe ByteString) -> (Maybe ByteString -> IO ()) -> IO Stream
Stream.makeStream
(Config -> Connection -> IO (Maybe ByteString)
reader Config
config Connection
connection)
(Connection -> Maybe ByteString -> IO ()
writer Connection
connection)
Connection
conn <- Stream
-> HostName
-> HostName
-> ConnectionOptions
-> Headers
-> IO Connection
WebSockets.newClientConnection Stream
stream HostName
host HostName
path ConnectionOptions
options Headers
headers
(Connection, IO ()) -> IO (Connection, IO ())
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
Applicative.pure (Connection
conn, Connection -> IO ()
Connection.connectionClose Connection
connection)
)
connectionParams ::
Socket.HostName -> Socket.PortNumber -> Connection.ConnectionParams
connectionParams :: HostName -> PortNumber -> ConnectionParams
connectionParams HostName
host PortNumber
port = do
Connection.ConnectionParams
{ connectionHostname :: HostName
Connection.connectionHostname = HostName
host,
connectionPort :: PortNumber
Connection.connectionPort = PortNumber
port,
connectionUseSecure :: Maybe TLSSettings
Connection.connectionUseSecure = TLSSettings -> Maybe TLSSettings
forall a. a -> Maybe a
Maybe.Just TLSSettings
tlsSettings,
connectionUseSocks :: Maybe ProxySettings
Connection.connectionUseSocks = Maybe ProxySettings
forall a. Maybe a
Maybe.Nothing
}
tlsSettings :: Connection.TLSSettings
tlsSettings :: TLSSettings
tlsSettings = TLSSettings
forall a. Default a => a
Default.def
reader ::
Config ->
Connection.Connection ->
IO.IO (Maybe.Maybe StrictBytes.ByteString)
reader :: Config -> Connection -> IO (Maybe ByteString)
reader Config
config Connection
connection =
IO (Maybe ByteString)
-> (IOError -> IO (Maybe ByteString)) -> IO (Maybe ByteString)
forall a. IO a -> (IOError -> IO a) -> IO a
IO.Error.catchIOError
( do
ByteString
chunk <- Config -> Connection -> IO ByteString
connectionGet Config
config Connection
connection
Maybe ByteString -> IO (Maybe ByteString)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
Applicative.pure (ByteString -> Maybe ByteString
forall a. a -> Maybe a
Maybe.Just ByteString
chunk)
)
( \IOError
e ->
if IOError -> Bool
IO.Error.isEOFError IOError
e
then Maybe ByteString -> IO (Maybe ByteString)
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
Applicative.pure Maybe ByteString
forall a. Maybe a
Maybe.Nothing
else IOError -> IO (Maybe ByteString)
forall e a. Exception e => e -> IO a
Exception.throwIO IOError
e
)
writer ::
Connection.Connection -> Maybe.Maybe LazyBytes.ByteString -> IO.IO ()
writer :: Connection -> Maybe ByteString -> IO ()
writer Connection
connection Maybe ByteString
maybeBytes = do
case Maybe ByteString
maybeBytes of
Maybe ByteString
Maybe.Nothing -> do
() -> IO ()
forall a. a -> IO a
forall (f :: * -> *) a. Applicative f => a -> f a
Applicative.pure ()
Maybe.Just ByteString
bytes -> do
Connection -> ByteString -> IO ()
Connection.connectionPut Connection
connection (ByteString -> ByteString
LazyBytes.toStrict ByteString
bytes)