3 , ExistentialQuantification
8 module Network.HTTP.Lucu.Interaction
12 , SyntacticallyInvalidInteraction(..)
13 , mkSyntacticallyInvalidInteraction
15 , SemanticallyInvalidInteraction(..)
16 , mkSemanticallyInvalidInteraction
18 , NormalInteraction(..)
19 , InteractionState(..)
20 , ReceiveBodyRequest(..)
30 import Blaze.ByteString.Builder (Builder)
31 import Control.Applicative
32 import Control.Concurrent.STM
33 import Data.Ascii (Ascii)
34 import qualified Data.ByteString as Strict
35 import Data.Monoid.Unicode
36 import Data.Sequence (Seq)
37 import qualified Data.Strict.Maybe as S
39 import qualified Data.Time.HTTP as HTTP
42 import Network.HTTP.Lucu.Config
43 import Network.HTTP.Lucu.DefaultPage
44 import Network.HTTP.Lucu.Headers
45 import Network.HTTP.Lucu.Preprocess
46 import Network.HTTP.Lucu.Request
47 import Network.HTTP.Lucu.Response
50 class Typeable i ⇒ Interaction i where
51 toInteraction ∷ i → SomeInteraction
52 toInteraction = SomeInteraction
54 fromInteraction ∷ SomeInteraction → Maybe i
55 fromInteraction (SomeInteraction i) = cast i
58 = ∀i. Interaction i ⇒ SomeInteraction !i
61 instance Interaction SomeInteraction where
63 fromInteraction = Just
65 -- |'SyntacticallyInvalidInteraction' is an 'Interaction' without even
66 -- a syntactically valid 'Request'. The response code will always be
68 data SyntacticallyInvalidInteraction
70 syiResponse ∷ !Response
71 , syiBodyToSend ∷ !Builder
74 instance Interaction SyntacticallyInvalidInteraction
76 mkSyntacticallyInvalidInteraction ∷ Config
77 → IO SyntacticallyInvalidInteraction
78 mkSyntacticallyInvalidInteraction config@(Config {..})
79 = do date ← getCurrentDate
80 let res = setHeader "Server" cnfServerSoftware $
81 setHeader "Date" date $
82 setHeader "Content-Type" defaultPageContentType $
83 emptyResponse BadRequest
84 body = getDefaultPage config Nothing res
87 , syiBodyToSend = body
90 -- |'SemanticallyInvalidInteraction' is an 'Interaction' without a
91 -- semantically valid 'Request'. The response code will always satisfy
93 data SemanticallyInvalidInteraction
96 , seiExpectedContinue ∷ !Bool
97 , seiReqBodyLength ∷ !(S.Maybe RequestBodyLength)
99 , seiResponse ∷ !Response
100 , seiWillChunkBody ∷ !Bool
101 , seiWillDiscardBody ∷ !Bool
102 , seiWillClose ∷ !Bool
103 , seiBodyToSend ∷ !Builder
106 instance Interaction SemanticallyInvalidInteraction
108 mkSemanticallyInvalidInteraction ∷ Config
110 → IO SemanticallyInvalidInteraction
111 mkSemanticallyInvalidInteraction config@(Config {..}) (AugmentedRequest {..})
112 = do date ← getCurrentDate
113 let res = setHeader "Server" cnfServerSoftware $
114 setHeader "Date" date $
115 setHeader "Content-Type" defaultPageContentType $
116 emptyResponse arInitialStatus
117 body = getDefaultPage config (Just arRequest) res
119 seiRequest = arRequest
120 , seiExpectedContinue = arExpectedContinue
121 , seiReqBodyLength = arReqBodyLength
124 , seiWillChunkBody = arWillChunkBody
125 , seiWillDiscardBody = arWillDiscardBody
126 , seiWillClose = arWillClose
127 , seiBodyToSend = body
130 -- |'NormalInteraction' is an 'Interaction' with a semantically
131 -- correct 'Request'.
132 data NormalInteraction
135 , niRemoteAddr ∷ !SockAddr
136 , niRemoteCert ∷ !(Maybe X509)
137 , niRequest ∷ !Request
138 , niResourcePath ∷ ![Strict.ByteString]
139 , niExpectedContinue ∷ !Bool
140 , niReqBodyLength ∷ !(S.Maybe RequestBodyLength)
142 , niReceiveBodyReq ∷ !(TMVar ReceiveBodyRequest)
143 , niReceivedBody ∷ !(TMVar Strict.ByteString)
145 , niResponse ∷ !(TVar Response)
146 , niSendContinue ∷ !(TMVar Bool)
147 , niWillChunkBody ∷ !Bool
148 , niWillDiscardBody ∷ !(TVar Bool)
149 , niWillClose ∷ !(TVar Bool)
150 , niResponseHasCType ∷ !(TVar Bool)
151 , niBodyToSend ∷ !(TMVar Builder)
153 , niState ∷ !(TVar InteractionState)
156 instance Interaction NormalInteraction
158 data ReceiveBodyRequest
159 = ReceiveBody !Int -- ^ Maximum number of octets to receive.
163 -- |The interaction state of Resource monad. 'ExaminingRequest' is the
165 data InteractionState
171 deriving (Show, Eq, Ord, Enum)
173 mkNormalInteraction ∷ Config
177 → [Strict.ByteString]
178 → IO NormalInteraction
179 mkNormalInteraction config remoteAddr remoteCert (AugmentedRequest {..}) rsrcPath
180 = do receiveBodyReq ← newEmptyTMVarIO
181 receivedBody ← newEmptyTMVarIO
183 response ← newTVarIO $ emptyResponse arInitialStatus
184 sendContinue ← newEmptyTMVarIO
185 willDiscardBody ← newTVarIO arWillDiscardBody
186 willClose ← newTVarIO arWillClose
187 responseHasCType ← newTVarIO False
188 bodyToSend ← newEmptyTMVarIO
190 state ← newTVarIO ExaminingRequest
194 , niRemoteAddr = remoteAddr
195 , niRemoteCert = remoteCert
196 , niRequest = arRequest
197 , niResourcePath = rsrcPath
198 , niExpectedContinue = arExpectedContinue
199 , niReqBodyLength = arReqBodyLength
201 , niReceiveBodyReq = receiveBodyReq
202 , niReceivedBody = receivedBody
204 , niResponse = response
205 , niSendContinue = sendContinue
206 , niWillChunkBody = arWillChunkBody
207 , niWillDiscardBody = willDiscardBody
208 , niWillClose = willClose
209 , niResponseHasCType = responseHasCType
210 , niBodyToSend = bodyToSend
215 type InteractionQueue = TVar (Seq SomeInteraction)
217 mkInteractionQueue ∷ IO InteractionQueue
218 mkInteractionQueue = newTVarIO (∅)
220 setResponseStatus ∷ NormalInteraction → StatusCode → STM ()
221 setResponseStatus (NI {..}) sc
222 = do res ← readTVar niResponse
226 writeTVar niResponse res'
228 getCurrentDate ∷ IO Ascii
229 getCurrentDate = HTTP.toAscii <$> getCurrentTime