{-# LANGUAGE OverloadedStrings #-}
module Network.WebSockets.Server
    ( ServerApp
    , runServer
    , ServerOptions (..)
    , defaultServerOptions
    , runServerWithOptions
    , runServerWith
    , makeListenSocket
    , makePendingConnection
    , makePendingConnectionFromStream
    , PongTimeout
    ) where
import qualified Control.Concurrent.Async      as Async
import           Control.Exception             (bracket,
                                                bracketOnError, finally, mask_,
                                                throwIO)
import           Network.Socket                (Socket)
import qualified Network.Socket                as S
import           Network.WebSockets.Connection
import           Network.WebSockets.Connection.PingPong (PongTimeout(..))
import           Network.WebSockets.Http
import qualified Network.WebSockets.Stream     as Stream
import           Network.WebSockets.Types
type ServerApp = PendingConnection -> IO ()
runServer :: String     
          -> Int        
          -> ServerApp  
          -> IO ()      
runServer :: String -> Int -> ServerApp -> IO ()
runServer String
host Int
port ServerApp
app = String -> Int -> ConnectionOptions -> ServerApp -> IO ()
runServerWith String
host Int
port ConnectionOptions
defaultConnectionOptions ServerApp
app
runServerWith :: String -> Int -> ConnectionOptions -> ServerApp -> IO ()
runServerWith :: String -> Int -> ConnectionOptions -> ServerApp -> IO ()
runServerWith String
host Int
port ConnectionOptions
opts = forall a. ServerOptions -> ServerApp -> IO a
runServerWithOptions ServerOptions
defaultServerOptions
    { serverHost :: String
serverHost              = String
host
    , serverPort :: Int
serverPort              = Int
port
    , serverConnectionOptions :: ConnectionOptions
serverConnectionOptions = ConnectionOptions
opts
    }
{-# DEPRECATED runServerWith "Use 'runServerWithOptions' instead" #-}
data ServerOptions = ServerOptions
    { ServerOptions -> String
serverHost              :: String
    , ServerOptions -> Int
serverPort              :: Int
    , ServerOptions -> ConnectionOptions
serverConnectionOptions :: ConnectionOptions
    }
defaultServerOptions :: ServerOptions
defaultServerOptions :: ServerOptions
defaultServerOptions = ServerOptions
    { serverHost :: String
serverHost              = String
"127.0.0.1"
    , serverPort :: Int
serverPort              = Int
8080
    , serverConnectionOptions :: ConnectionOptions
serverConnectionOptions = ConnectionOptions
defaultConnectionOptions
    }
runServerWithOptions :: ServerOptions -> ServerApp -> IO a
runServerWithOptions :: forall a. ServerOptions -> ServerApp -> IO a
runServerWithOptions ServerOptions
opts ServerApp
app = forall a. IO a -> IO a
S.withSocketsDo forall a b. (a -> b) -> a -> b
$
    forall a b c. IO a -> (a -> IO b) -> (a -> IO c) -> IO c
bracket
    (String -> Int -> IO Socket
makeListenSocket (ServerOptions -> String
serverHost ServerOptions
opts) (ServerOptions -> Int
serverPort ServerOptions
opts))
    Socket -> IO ()
S.close
    (\Socket
sock ->
        let
            mainThread :: IO b
mainThread = do
                (Socket
conn, SockAddr
_) <- Socket -> IO (Socket, SockAddr)
S.accept Socket
sock
                forall a b.
((forall a. IO a -> IO a) -> IO a) -> (Async a -> IO b) -> IO b
Async.withAsyncWithUnmask
                    (\forall a. IO a -> IO a
unmask -> forall a. IO a -> IO a
unmask (Socket -> ConnectionOptions -> ServerApp -> IO ()
runApp Socket
conn (ServerOptions -> ConnectionOptions
serverConnectionOptions ServerOptions
opts) ServerApp
app) forall a b. IO a -> IO b -> IO a
`finally` Socket -> IO ()
S.close Socket
conn)
                    (\Async ()
_ -> IO b
mainThread)
        in forall a. IO a -> IO a
mask_ forall {b}. IO b
mainThread
    )
makeListenSocket :: String -> Int -> IO Socket
makeListenSocket :: String -> Int -> IO Socket
makeListenSocket String
host Int
port = do
  AddrInfo
addr:[AddrInfo]
_ <- Maybe AddrInfo -> Maybe String -> Maybe String -> IO [AddrInfo]
S.getAddrInfo (forall a. a -> Maybe a
Just AddrInfo
hints) (forall a. a -> Maybe a
Just String
host) (forall a. a -> Maybe a
Just (forall a. Show a => a -> String
show Int
port))
  forall a b c. IO a -> (a -> IO b) -> (a -> IO c) -> IO c
bracketOnError
    (Family -> SocketType -> ProtocolNumber -> IO Socket
S.socket (AddrInfo -> Family
S.addrFamily AddrInfo
addr) SocketType
S.Stream ProtocolNumber
S.defaultProtocol)
    Socket -> IO ()
S.close
    (\Socket
sock -> do
        ()
_     <- Socket -> SocketOption -> Int -> IO ()
S.setSocketOption Socket
sock SocketOption
S.ReuseAddr Int
1
        ()
_     <- Socket -> SocketOption -> Int -> IO ()
S.setSocketOption Socket
sock SocketOption
S.NoDelay   Int
1
        Socket -> SockAddr -> IO ()
S.bind Socket
sock (AddrInfo -> SockAddr
S.addrAddress AddrInfo
addr)
        Socket -> Int -> IO ()
S.listen Socket
sock Int
5
        forall (m :: * -> *) a. Monad m => a -> m a
return Socket
sock
        )
  where
    hints :: AddrInfo
hints = AddrInfo
S.defaultHints { addrSocketType :: SocketType
S.addrSocketType = SocketType
S.Stream }
runApp :: Socket
       -> ConnectionOptions
       -> ServerApp
       -> IO ()
runApp :: Socket -> ConnectionOptions -> ServerApp -> IO ()
runApp Socket
socket ConnectionOptions
opts ServerApp
app =
    forall a b c. IO a -> (a -> IO b) -> (a -> IO c) -> IO c
bracket
        (Socket -> ConnectionOptions -> IO PendingConnection
makePendingConnection Socket
socket ConnectionOptions
opts)
        (Stream -> IO ()
Stream.close forall b c a. (b -> c) -> (a -> b) -> a -> c
. PendingConnection -> Stream
pendingStream)
        ServerApp
app
makePendingConnection
    :: Socket -> ConnectionOptions -> IO PendingConnection
makePendingConnection :: Socket -> ConnectionOptions -> IO PendingConnection
makePendingConnection Socket
socket ConnectionOptions
opts = do
    Stream
stream <- Socket -> IO Stream
Stream.makeSocketStream Socket
socket
    Stream -> ConnectionOptions -> IO PendingConnection
makePendingConnectionFromStream Stream
stream ConnectionOptions
opts
makePendingConnectionFromStream
    :: Stream.Stream -> ConnectionOptions -> IO PendingConnection
makePendingConnectionFromStream :: Stream -> ConnectionOptions -> IO PendingConnection
makePendingConnectionFromStream Stream
stream ConnectionOptions
opts = do
    
    Maybe RequestHead
mbRequest <- forall a. Stream -> Parser a -> IO (Maybe a)
Stream.parse Stream
stream (Bool -> Parser RequestHead
decodeRequestHead Bool
False)
    case Maybe RequestHead
mbRequest of
        Maybe RequestHead
Nothing      -> forall e a. Exception e => e -> IO a
throwIO ConnectionException
ConnectionClosed
        Just RequestHead
request -> forall (m :: * -> *) a. Monad m => a -> m a
return PendingConnection
            { pendingOptions :: ConnectionOptions
pendingOptions  = ConnectionOptions
opts
            , pendingRequest :: RequestHead
pendingRequest  = RequestHead
request
            , pendingOnAccept :: Connection -> IO ()
pendingOnAccept = \Connection
_ -> forall (m :: * -> *) a. Monad m => a -> m a
return ()
            , pendingStream :: Stream
pendingStream   = Stream
stream
            }