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
114 let res = setHeader "Server" cnfServerSoftware $
115 setHeader "Date" date $
116 setHeader "Content-Type" defaultPageContentType $
117 emptyResponse arInitialStatus
118 body = getDefaultPage config (Just arRequest) res
120 seiRequest = arRequest
121 , seiExpectedContinue = arExpectedContinue
122 , seiReqBodyLength = arReqBodyLength
125 , seiWillChunkBody = arWillChunkBody
126 , seiWillDiscardBody = arWillDiscardBody
127 , seiWillClose = arWillClose
128 , seiBodyToSend = body
131 -- |'NormalInteraction' is an 'Interaction' with a semantically
132 -- correct 'Request'.
133 data NormalInteraction
136 , niRemoteAddr ∷ !SockAddr
137 , niRemoteCert ∷ !(Maybe X509)
138 , niRequest ∷ !Request
139 , niResourcePath ∷ ![Strict.ByteString]
140 , niExpectedContinue ∷ !Bool
141 , niReqBodyLength ∷ !(S.Maybe RequestBodyLength)
143 , niReceiveBodyReq ∷ !(TMVar ReceiveBodyRequest)
144 , niReceivedBody ∷ !(TMVar Strict.ByteString)
146 , niResponse ∷ !(TVar Response)
147 , niSendContinue ∷ !(TMVar Bool)
148 , niWillChunkBody ∷ !Bool
149 , niWillDiscardBody ∷ !(TVar Bool)
150 , niWillClose ∷ !(TVar Bool)
151 , niResponseHasCType ∷ !(TVar Bool)
152 , niBodyToSend ∷ !(TMVar Builder)
154 , niState ∷ !(TVar InteractionState)
157 instance Interaction NormalInteraction
159 data ReceiveBodyRequest
160 = ReceiveBody !Int -- ^ Maximum number of octets to receive.
164 -- |The interaction state of Resource monad. 'ExaminingRequest' is the
166 data InteractionState
172 deriving (Show, Eq, Ord, Enum)
174 mkNormalInteraction ∷ Config
178 → [Strict.ByteString]
179 → IO NormalInteraction
180 mkNormalInteraction config remoteAddr remoteCert (AugmentedRequest {..}) rsrcPath
181 = do receiveBodyReq ← newEmptyTMVarIO
182 receivedBody ← newEmptyTMVarIO
184 response ← newTVarIO $ emptyResponse arInitialStatus
185 sendContinue ← newEmptyTMVarIO
186 willDiscardBody ← newTVarIO arWillDiscardBody
187 willClose ← newTVarIO arWillClose
188 responseHasCType ← newTVarIO False
189 bodyToSend ← newEmptyTMVarIO
191 state ← newTVarIO ExaminingRequest
195 , niRemoteAddr = remoteAddr
196 , niRemoteCert = remoteCert
197 , niRequest = arRequest
198 , niResourcePath = rsrcPath
199 , niExpectedContinue = arExpectedContinue
200 , niReqBodyLength = arReqBodyLength
202 , niReceiveBodyReq = receiveBodyReq
203 , niReceivedBody = receivedBody
205 , niResponse = response
206 , niSendContinue = sendContinue
207 , niWillChunkBody = arWillChunkBody
208 , niWillDiscardBody = willDiscardBody
209 , niWillClose = willClose
210 , niResponseHasCType = responseHasCType
211 , niBodyToSend = bodyToSend
216 type InteractionQueue = TVar (Seq SomeInteraction)
218 mkInteractionQueue ∷ IO InteractionQueue
219 mkInteractionQueue = newTVarIO (∅)
221 setResponseStatus ∷ NormalInteraction → StatusCode → STM ()
222 setResponseStatus (NI {..}) sc
223 = do res ← readTVar niResponse
227 writeTVar niResponse res'
229 getCurrentDate ∷ IO Ascii
230 getCurrentDate = HTTP.toAscii <$> getCurrentTime