4 , ExistentialQuantification
9 module Network.HTTP.Lucu.Interaction
12 , EndOfInteraction(..)
14 , SyntacticallyInvalidInteraction(..)
15 , mkSyntacticallyInvalidInteraction
17 , SemanticallyInvalidInteraction(..)
18 , mkSemanticallyInvalidInteraction
20 , NormalInteraction(..)
21 , InteractionState(..)
22 , ReceiveBodyRequest(..)
31 import Blaze.ByteString.Builder (Builder)
32 import Control.Applicative
33 import Control.Concurrent.STM
34 import Data.Ascii (Ascii)
35 import Data.ByteString (ByteString)
36 import Data.Monoid.Unicode
37 import Data.Sequence (Seq)
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
48 import Network.HTTP.Lucu.Utils
53 class Typeable i ⇒ Interaction i where
54 toInteraction ∷ i → SomeInteraction
55 toInteraction = SomeInteraction
57 fromInteraction ∷ SomeInteraction → Maybe i
58 fromInteraction (SomeInteraction i) = cast i
61 = ∀i. Interaction i ⇒ SomeInteraction !i
64 instance Interaction SomeInteraction where
66 fromInteraction = Just
68 -- |'EndOfInteraction' is an 'Interaction' indicating the end of
69 -- (possibly pipelined) requests. The connection has already been
70 -- closed so no need to reply anything.
71 data EndOfInteraction = EndOfInteraction
73 instance Interaction EndOfInteraction
75 -- |'SyntacticallyInvalidInteraction' is an 'Interaction' without even
76 -- a syntactically valid 'Request'. The response code will always be
78 data SyntacticallyInvalidInteraction
80 syiResponse ∷ !Response
81 , syiBodyToSend ∷ !Builder
84 instance Interaction SyntacticallyInvalidInteraction
86 mkSyntacticallyInvalidInteraction ∷ Config
87 → IO SyntacticallyInvalidInteraction
88 mkSyntacticallyInvalidInteraction config@(Config {..})
89 = do date ← getCurrentDate
90 let res = setHeader "Server" cnfServerSoftware $
91 setHeader "Date" date $
92 setHeader "Content-Type" defaultPageContentType $
93 emptyResponse BadRequest
94 body = getDefaultPage config Nothing res
97 , syiBodyToSend = body
100 -- |'SemanticallyInvalidInteraction' is an 'Interaction' without a
101 -- semantically valid 'Request'. The response code will always satisfy
103 data SemanticallyInvalidInteraction
105 seiRequest ∷ !Request
106 , seiExpectedContinue ∷ !Bool
107 , seiReqBodyLength ∷ !(Maybe RequestBodyLength)
109 , seiResponse ∷ !Response
110 , seiWillChunkBody ∷ !Bool
111 , seiWillDiscardBody ∷ !Bool
112 , seiWillClose ∷ !Bool
113 , seiBodyToSend ∷ !Builder
116 instance Interaction SemanticallyInvalidInteraction
118 mkSemanticallyInvalidInteraction ∷ Config
120 → IO SemanticallyInvalidInteraction
121 mkSemanticallyInvalidInteraction config@(Config {..}) (AugmentedRequest {..})
122 = do date ← getCurrentDate
123 let res = setHeader "Server" cnfServerSoftware $
124 setHeader "Date" date $
125 setHeader "Content-Type" defaultPageContentType $
126 emptyResponse arInitialStatus
127 body = getDefaultPage config (Just arRequest) res
129 seiRequest = arRequest
130 , seiExpectedContinue = arExpectedContinue
131 , seiReqBodyLength = arReqBodyLength
134 , seiWillChunkBody = arWillChunkBody
135 , seiWillDiscardBody = arWillDiscardBody
136 , seiWillClose = arWillClose
137 , seiBodyToSend = body
140 -- |'NormalInteraction' is an 'Interaction' with a semantically
141 -- correct 'Request'.
142 data NormalInteraction
145 , niRemoteAddr ∷ !SockAddr
146 #if defined(HAVE_SSL)
147 , niRemoteCert ∷ !(Maybe X509)
149 , niRequest ∷ !Request
150 , niResourcePath ∷ !Path
151 , niExpectedContinue ∷ !Bool
152 , niReqBodyLength ∷ !(Maybe RequestBodyLength)
154 , niReceiveBodyReq ∷ !(TMVar ReceiveBodyRequest)
155 , niReceivedBody ∷ !(TMVar ByteString)
157 , niResponse ∷ !(TVar Response)
158 , niSendContinue ∷ !(TMVar Bool)
159 , niWillChunkBody ∷ !Bool
160 , niWillDiscardBody ∷ !(TVar Bool)
161 , niWillClose ∷ !(TVar Bool)
162 , niResponseHasCType ∷ !(TVar Bool)
163 -- FIXME: use TBChan Builder (in stm-chans package)
164 , niBodyToSend ∷ !(TMVar Builder)
166 , niState ∷ !(TVar InteractionState)
169 instance Interaction NormalInteraction
171 data ReceiveBodyRequest
172 = ReceiveBody !Int -- ^ Maximum number of octets to receive.
176 -- |The interaction state of Resource monad. 'ExaminingRequest' is the
178 data InteractionState
184 deriving (Show, Eq, Ord, Enum)
186 mkNormalInteraction ∷ Config
188 #if defined(HAVE_SSL)
193 → IO NormalInteraction
194 #if defined(HAVE_SSL)
195 mkNormalInteraction config remoteAddr remoteCert (AugmentedRequest {..}) rsrcPath
197 mkNormalInteraction config remoteAddr (AugmentedRequest {..}) rsrcPath
199 = do receiveBodyReq ← newEmptyTMVarIO
200 receivedBody ← newEmptyTMVarIO
202 response ← newTVarIO $ emptyResponse arInitialStatus
203 sendContinue ← newEmptyTMVarIO
204 willDiscardBody ← newTVarIO arWillDiscardBody
205 willClose ← newTVarIO arWillClose
206 responseHasCType ← newTVarIO False
207 bodyToSend ← newEmptyTMVarIO
209 state ← newTVarIO ExaminingRequest
213 , niRemoteAddr = remoteAddr
214 #if defined(HAVE_SSL)
215 , niRemoteCert = remoteCert
217 , niRequest = arRequest
218 , niResourcePath = rsrcPath
219 , niExpectedContinue = arExpectedContinue
220 , niReqBodyLength = arReqBodyLength
222 , niReceiveBodyReq = receiveBodyReq
223 , niReceivedBody = receivedBody
225 , niResponse = response
226 , niSendContinue = sendContinue
227 , niWillChunkBody = arWillChunkBody
228 , niWillDiscardBody = willDiscardBody
229 , niWillClose = willClose
230 , niResponseHasCType = responseHasCType
231 , niBodyToSend = bodyToSend
236 type InteractionQueue = TVar (Seq SomeInteraction)
238 mkInteractionQueue ∷ IO InteractionQueue
239 mkInteractionQueue = newTVarIO (∅)
241 getCurrentDate ∷ IO Ascii
242 getCurrentDate = HTTP.toAscii <$> getCurrentTime