content: {-# LANGUAGE RankNTypes #-}
  ------------------------------------------------------------------------------
  -- | Types internal to the implementation of the Snap HTTP server.
  module Snap.Internal.Http.Server.Types
    ( ServerConfig(..)
    , PerSessionData(..)
    , DataFinishedHook
    , EscapeSnapHook
    , ExceptionHook
    , ParseHook
    , NewRequestHook
    , UserHandlerFinishedHook
  
    -- * Handlers
    , SendFileHandler
    , ServerHandler
    , AcceptFunc(..)
  
    -- * Socket types
    , SocketConfig(..)
    ) where
  
  ------------------------------------------------------------------------------
  import           Control.Exception                (SomeException)
  import           Data.ByteString                  (ByteString)
  import           Data.IORef                       (IORef)
  import           Data.Word                        (Word64)
  import           Network.Socket                   (Socket)
  ------------------------------------------------------------------------------
  import           Data.ByteString.Builder          (Builder)
  import           Data.ByteString.Builder.Internal (Buffer)
  import           System.IO.Streams                (InputStream, OutputStream)
  ------------------------------------------------------------------------------
  import           Snap.Core                        (Request, Response)
  
  
  ------------------------------------------------------------------------------
  -- | The 'NewRequestHook' is called once processing for an HTTP request begins,
  -- i.e. after the connection has been accepted and we know that there's data
  -- available to read from the socket. The IORef passed to the hook initially
  -- contains a bottom value that will throw an exception if evaluated.
  type NewRequestHook hookState = PerSessionData -> IO hookState
  
  -- | The 'ParseHook' is called after the HTTP Request has been parsed by the
  -- server, but before the user handler starts running.
  type ParseHook hookState = IORef hookState -> Request -> IO ()
  
  -- | The 'UserHandlerFinishedHook' is called once the user handler has finished
  -- running, but before the data for the HTTP response starts being sent to the
  -- client.
  type UserHandlerFinishedHook hookState =
      IORef hookState -> Request -> Response -> IO ()
  
  -- | The 'DataFinishedHook' is called once the server has finished sending the
  -- HTTP response to the client.
  type DataFinishedHook hookState =
      IORef hookState -> Request -> Response -> IO ()
  
  -- | The 'ExceptionHook' is called if an exception reaches the toplevel of the
  -- server, i.e. if an exception leaks out of the user handler or if an
  -- exception is raised during the sending of the HTTP response data.
  type ExceptionHook hookState = IORef hookState -> SomeException -> IO ()
  
  -- | The 'EscapeSnapHook' is called if the user handler escapes the HTTP
  -- session, e.g. for websockets.
  type EscapeSnapHook hookState = IORef hookState -> IO ()
  
  
                               ---------------------
                               -- data structures --
                               ---------------------
  ------------------------------------------------------------------------------
  -- | Data and services that all HTTP response handlers share.
  --
  data ServerConfig hookState = ServerConfig
      { _logAccess             :: !(Request -> Response -> Word64 -> IO ())
      , _logError              :: !(Builder -> IO ())
      , _onNewRequest          :: !(NewRequestHook hookState)
      , _onParse               :: !(ParseHook hookState)
      , _onUserHandlerFinished :: !(UserHandlerFinishedHook hookState)
      , _onDataFinished        :: !(DataFinishedHook hookState)
      , _onException           :: !(ExceptionHook hookState)
      , _onEscape              :: !(EscapeSnapHook hookState)
  
        -- | will be overridden by a @Host@ header if it appears.
      , _localHostname         :: !ByteString
      , _defaultTimeout        :: {-# UNPACK #-} !Int
      , _isSecure              :: !Bool
  
        -- | Number of accept loops to spawn.
      , _numAcceptLoops        :: {-# UNPACK #-} !Int
      }
  
  
  ------------------------------------------------------------------------------
  -- | All of the things a session needs to service a single HTTP request.
  data PerSessionData = PerSessionData
      { -- | If the bool stored in this IORef becomes true, the server will close
        -- the connection after the current request is processed.
        _forceConnectionClose :: {-# UNPACK #-} !(IORef Bool)
  
        -- | An IO action to modify the current request timeout.
      , _twiddleTimeout       :: !((Int -> Int) -> IO ())
  
        -- | The value stored in this IORef is True if this request is the first
        -- on a new connection, and False if it is a subsequent keep-alive
        -- request.
      , _isNewConnection      :: !(IORef Bool)
  
        -- | The function called when we want to use @sendfile().@
      , _sendfileHandler      :: !SendFileHandler
  
        -- | The server's idea of its local address.
      , _localAddress         :: !ByteString
  
        -- | The listening port number.
      , _localPort            :: {-# UNPACK #-} !Int
  
        -- | The address of the remote user.
      , _remoteAddress        :: !ByteString
  
        -- | The remote user's port.
      , _remotePort           :: {-# UNPACK #-} !Int
  
        -- | The read end of the socket connection.
      , _readEnd              :: !(InputStream ByteString)
  
        -- | The write end of the socket connection.
      , _writeEnd             :: !(OutputStream ByteString)
      }
  
  
  ------------------------------------------------------------------------------
  newtype AcceptFunc = AcceptFunc {
    runAcceptFunc :: (forall a . IO a -> IO a)         -- exception restore function
                      -> IO ( SendFileHandler          -- what to do on sendfile
                            , ByteString               -- local address
                            , Int                      -- local port
                            , ByteString               -- remote address
                            , Int                      -- remote port
                            , InputStream ByteString   -- socket read end
                            , OutputStream ByteString  -- socket write end
                            , IO ()                    -- cleanup action
                            )
    }
  
                               --------------------
                               -- function types --
                               --------------------
  ------------------------------------------------------------------------------
  -- | This function, provided to the web server internals from the outside, is
  -- responsible for producing a 'Response' once the server has parsed the
  -- 'Request'.
  --
  type ServerHandler hookState =
          ServerConfig hookState     -- ^ global server config
       -> PerSessionData             -- ^ per-connection data
       -> Request                    -- ^ HTTP request object
       -> IO (Request, Response)
  
  
  ------------------------------------------------------------------------------
  -- | A 'SendFileHandler' is called if the user handler requests that a file be
  -- sent using @sendfile()@ on systems that support it (Linux, Mac OSX, and
  -- FreeBSD).
  type SendFileHandler =
         Buffer                   -- ^ builder buffer
      -> Builder                  -- ^ status line and headers
      -> FilePath                 -- ^ file to send
      -> Word64                   -- ^ start offset
      -> Word64                   -- ^ number of bytes
      -> IO ()
  
  
  
                          -------------------------------
                          -- types for server backends --
                          -------------------------------
  
  ------------------------------------------------------------------------------
  -- | Either the server should start listening on the given interface \/ port
  -- combination, or the server should start up with a 'Socket' that has already
  -- had @bind()@ and @listen()@ called on it.
  data SocketConfig = StartListening ByteString Int
                    | PreBound Socket
  