7 module Network.HTTP.Lucu.Preprocess
9 , RequestBodyLength(..)
13 import Control.Applicative
15 import Control.Monad.State
16 import Data.Ascii (Ascii)
17 import qualified Data.Ascii as A
18 import qualified Data.ByteString.Char8 as C8
20 import Data.Text (Text)
21 import qualified Data.Text as T
22 import qualified Data.Text.Encoding as T
23 import Network.HTTP.Lucu.Headers
24 import Network.HTTP.Lucu.HttpVersion
25 import Network.HTTP.Lucu.Request
26 import Network.HTTP.Lucu.Response
29 import Prelude.Unicode
33 arRequest ∷ !(Maybe Request)
34 , arInitialStatus ∷ !StatusCode
36 , arWillDiscardBody ∷ !Bool
37 , arExpectedContinue ∷ !(Maybe Bool)
38 , arReqBodyLength ∷ !(Maybe RequestBodyLength)
41 data RequestBodyLength
48 → Either StatusCode Request
50 preprocess localHost localPort request
53 → preprocess' localHost localPort req
57 unparsable ∷ StatusCode → AugmentedRequest
61 , arInitialStatus = sc
63 , arWillDiscardBody = False
64 , arExpectedContinue = Nothing
65 , arReqBodyLength = Nothing
68 preprocess' ∷ Text → PortNumber → Request → AugmentedRequest
69 preprocess' localHost localPort req@(Request {..})
70 = execState go initialAR
72 initialAR ∷ AugmentedRequest
73 initialAR = AugmentedRequest {
75 , arInitialStatus = Ok
77 , arWillDiscardBody = False
78 , arExpectedContinue = Just False
79 , arReqBodyLength = Nothing
82 go ∷ State AugmentedRequest ()
83 go = do examineHttpVersion
85 examineAuthority localHost localPort
89 setRequest ∷ Request → State AugmentedRequest ()
91 = modify $ \ar → ar { arRequest = Just req }
93 setStatus ∷ StatusCode → State AugmentedRequest ()
95 = modify $ \ar → ar { arInitialStatus = sc }
97 setWillClose ∷ Bool → State AugmentedRequest ()
99 = modify $ \ar → ar { arWillClose = b }
101 setBodyLength ∷ Maybe RequestBodyLength → State AugmentedRequest ()
103 = modify $ \ar → ar { arReqBodyLength = len }
105 examineHttpVersion ∷ State AugmentedRequest ()
107 = do req ← gets (fromJust ∘ arRequest)
108 case reqVersion req of
109 -- HTTP/1.0 requests can't Keep-Alive.
114 _ → do setStatus HttpVersionNotSupported
117 examineMethod ∷ State AugmentedRequest ()
119 = do req ← gets (fromJust ∘ arRequest)
120 case reqMethod req of
122 HEAD → modify $ \ar → ar { arWillDiscardBody = True }
126 _ → setStatus NotImplemented
128 examineAuthority ∷ Text → PortNumber → State AugmentedRequest ()
129 examineAuthority localHost localPort
130 = do req ← gets (fromJust ∘ arRequest)
131 when (isNothing $ uriAuthority $ reqURI req) $
132 case reqVersion req of
133 -- HTTP/1.0 requests have no Host header so complete it
134 -- with the configuration value.
136 → let host = localHost
137 port = case localPort of
139 n → A.unsafeFromString $ ':':show n
140 req' = updateAuthority host port req
143 -- HTTP/1.1 requests MUST have a Host header.
145 → case getHeader "Host" req of
149 req' = updateAuthority host port req
153 → setStatus BadRequest
154 -- Should never reach here...
155 ver → fail ("internal error: unknown version: " ⧺ show ver)
157 parseHost ∷ Ascii → (Text, Ascii)
159 = let (h, p) = C8.break (≡ ':') $ A.toByteString hp
160 -- FIXME: should decode punycode here.
161 hText = T.decodeUtf8 h
162 pAscii = A.unsafeFromByteString p
166 updateAuthority ∷ Text → Ascii → Request → Request
167 updateAuthority host port req
168 = let uri = reqURI req
170 uriAuthority = Just URIAuth {
172 , uriRegName = T.unpack host
173 , uriPort = A.toString port
177 req { reqURI = uri' }
179 examineHeaders ∷ State AugmentedRequest ()
181 = do req ← gets (fromJust ∘ arRequest)
183 case getCIHeader "Expect" req of
187 → modify $ \ar → ar { arExpectedContinue = Just True }
189 → setStatus ExpectationFailed
191 case getCIHeader "Transfer-Encoding" req of
197 → setBodyLength $ Just Chunked
199 → setStatus NotImplemented
201 case A.toByteString <$> getHeader "Content-Length" req of
203 Just value → case C8.readInt value of
205 | C8.null garbage ∧ len ≥ 0
206 → setBodyLength $ Just $ Fixed len
207 _ → setStatus BadRequest
209 case getCIHeader "Connection" req of
215 examineBodyLength ∷ State AugmentedRequest ()
217 = do req ← gets (fromJust ∘ arRequest)
218 len ← gets arReqBodyLength
219 if reqMustHaveBody req then
220 -- POST and PUT requests must have an entity body.
222 $ setStatus LengthRequired
224 -- Other requests must NOT have an entity body.
226 $ setStatus BadRequest