module Network.HTTP.Lucu.Interaction
( Interaction(..)
, InteractionState(..)
, InteractionQueue
, newInteractionQueue
, newInteraction
, defaultPageContentType
, writeItr
, readItr
, readItrF
, updateItr
, updateItrF
)
where
import Control.Concurrent.STM
import qualified Data.ByteString as Strict (ByteString)
import qualified Data.ByteString.Lazy as Lazy (ByteString)
import Data.ByteString.Char8 as C8 hiding (ByteString)
import qualified Data.ByteString.Lazy.Char8 as L8 hiding (ByteString)
import qualified Data.Sequence as S
import Data.Sequence (Seq)
import Network.Socket
import Network.HTTP.Lucu.Config
import Network.HTTP.Lucu.Headers
import Network.HTTP.Lucu.HttpVersion
import Network.HTTP.Lucu.Request
import Network.HTTP.Lucu.Response
import OpenSSL.X509
data Interaction = Interaction {
itrConfig :: !Config
, itrLocalPort :: !PortNumber
, itrRemoteAddr :: !SockAddr
, itrRemoteCert :: !(Maybe X509)
, itrResourcePath :: !(Maybe [String])
, itrRequest :: !(TVar (Maybe Request))
, itrResponse :: !(TVar Response)
, itrRequestHasBody :: !(TVar Bool)
, itrRequestIsChunked :: !(TVar Bool)
, itrExpectedContinue :: !(TVar Bool)
, itrReqChunkLength :: !(TVar (Maybe Int))
, itrReqChunkRemaining :: !(TVar (Maybe Int))
, itrReqChunkIsOver :: !(TVar Bool)
, itrReqBodyWanted :: !(TVar (Maybe Int))
, itrReqBodyWasteAll :: !(TVar Bool)
, itrReceivedBody :: !(TVar Lazy.ByteString)
, itrWillReceiveBody :: !(TVar Bool)
, itrWillChunkBody :: !(TVar Bool)
, itrWillDiscardBody :: !(TVar Bool)
, itrWillClose :: !(TVar Bool)
, itrBodyToSend :: !(TVar Lazy.ByteString)
, itrBodyIsNull :: !(TVar Bool)
, itrState :: !(TVar InteractionState)
, itrWroteContinue :: !(TVar Bool)
, itrWroteHeader :: !(TVar Bool)
}
data InteractionState = ExaminingRequest
| GettingBody
| DecidingHeader
| DecidingBody
| Done
deriving (Show, Eq, Ord, Enum)
type InteractionQueue = TVar (Seq Interaction)
newInteractionQueue :: IO InteractionQueue
newInteractionQueue = newTVarIO S.empty
defaultPageContentType :: Strict.ByteString
defaultPageContentType = C8.pack "application/xhtml+xml"
newInteraction :: Config -> PortNumber -> SockAddr -> Maybe X509 -> Maybe Request -> IO Interaction
newInteraction !conf !port !addr !cert !req
= do request <- newTVarIO req
responce <- newTVarIO Response {
resVersion = HttpVersion 1 1
, resStatus = Ok
, resHeaders = toHeaders [(C8.pack "Content-Type", defaultPageContentType)]
}
requestHasBody <- newTVarIO False
requestIsChunked <- newTVarIO False
expectedContinue <- newTVarIO False
reqChunkLength <- newTVarIO Nothing
reqChunkRemaining <- newTVarIO Nothing
reqChunkIsOver <- newTVarIO False
reqBodyWanted <- newTVarIO Nothing
reqBodyWasteAll <- newTVarIO False
receivedBody <- newTVarIO L8.empty
willReceiveBody <- newTVarIO False
willChunkBody <- newTVarIO False
willDiscardBody <- newTVarIO False
willClose <- newTVarIO False
bodyToSend <- newTVarIO L8.empty
bodyIsNull <- newTVarIO True
state <- newTVarIO ExaminingRequest
wroteContinue <- newTVarIO False
wroteHeader <- newTVarIO False
return Interaction {
itrConfig = conf
, itrLocalPort = port
, itrRemoteAddr = addr
, itrRemoteCert = cert
, itrResourcePath = Nothing
, itrRequest = request
, itrResponse = responce
, itrRequestHasBody = requestHasBody
, itrRequestIsChunked = requestIsChunked
, itrExpectedContinue = expectedContinue
, itrReqChunkLength = reqChunkLength
, itrReqChunkRemaining = reqChunkRemaining
, itrReqChunkIsOver = reqChunkIsOver
, itrReqBodyWanted = reqBodyWanted
, itrReqBodyWasteAll = reqBodyWasteAll
, itrReceivedBody = receivedBody
, itrWillReceiveBody = willReceiveBody
, itrWillChunkBody = willChunkBody
, itrWillDiscardBody = willDiscardBody
, itrWillClose = willClose
, itrBodyToSend = bodyToSend
, itrBodyIsNull = bodyIsNull
, itrState = state
, itrWroteContinue = wroteContinue
, itrWroteHeader = wroteHeader
}
writeItr :: Interaction -> (Interaction -> TVar a) -> a -> STM ()
writeItr !itr !accessor !value
= writeTVar (accessor itr) value
readItr :: Interaction -> (Interaction -> TVar a) -> (a -> b) -> STM b
readItr !itr !accessor !reader
= fmap reader $ readTVar (accessor itr)
readItrF :: Functor f => Interaction -> (Interaction -> TVar (f a)) -> (a -> b) -> STM (f b)
readItrF !itr !accessor !reader
= readItr itr accessor (fmap reader)
updateItr :: Interaction -> (Interaction -> TVar a) -> (a -> a) -> STM ()
updateItr !itr !accessor !updator
= do old <- readItr itr accessor id
writeItr itr accessor (updator old)
updateItrF :: Functor f => Interaction -> (Interaction -> TVar (f a)) -> (a -> a) -> STM ()
updateItrF !itr !accessor !updator
= updateItr itr accessor (fmap updator)