@@ -132,14 +132,14 @@ toApplication ra request respond = ra request routingRespond
132132-- 405 (bad method)
133133-- 401 (unauthorized)
134134-- 415 (unsupported media type)
135−-- 400 (bad request)
136135-- 406 (not acceptable)
136+-- 400 (bad request)
137137-- @
138138--
139139-- Therefore, while routing, we delay most checks so that they
140140-- will ultimately occur in the right order.
141141--
142−-- A 'Delayed' contains three delayed blocks of tests, and
142+-- A 'Delayed' contains many delayed blocks of tests, and
143143-- the actual handler:
144144--
145145-- 1. Delayed captures. These can actually cause 404, and
@@ -152,23 +152,36 @@ toApplication ra request respond = ra request routingRespond
152152-- it does not provide an input for the handler. Method checks
153153-- are comparatively cheap.
154154--
155−-- 3. Body and accept header checks. The request body check can
156−-- cause both 400 and 415. This provides an input to the handler.
157−-- The accept header check can be performed as the final
158−-- computation in this block. It can cause a 406.
155+-- 3. Authentication checks. This can cause 401.
156+--
157+-- 4. Accept and content type header checks. These checks
158+-- can cause 415 and 406 errors.
159+--
160+-- 5. Query parameter checks. They require parsing and can cause 400 if the
161+-- parsing fails. Query parameter checks provide inputs to the handler
162+--
163+-- 6. Body check. The request body check can cause 400.
159164--
160165data Delayed env c where
161166 Delayed :: { capturesD :: env -> DelayedIO captures
162167 , methodD :: DelayedIO ()
163168 , authD :: DelayedIO auth
164− , bodyD :: DelayedIO body
165− , serverD :: captures -> auth -> body -> Request -> RouteResult c
169+ , acceptD :: DelayedIO ()
170+ , contentD :: DelayedIO contentType
171+ , paramsD :: DelayedIO params
172+ , bodyD :: contentType -> DelayedIO body
173+ , serverD :: captures
174+ -> params
175+ -> auth
176+ -> body
177+ -> Request
178+ -> RouteResult c
166179 } -> Delayed env c
167180
168181instance Functor (Delayed env) where
169182 fmap f Delayed{..} =
170183 Delayed
171− { serverD = \ c a b req -> f <$> serverD c a b req
184+ { serverD = \ c p a b req -> f <$> serverD c p a b req
172185 , ..
173186 } -- Note [Existential Record Update]
174187
@@ -200,7 +213,7 @@ runDelayedIO m req = transResourceT runRouteResultT $ runReaderT (runDelayedIO'
200213-- | A 'Delayed' without any stored checks.
201214emptyDelayed :: RouteResult a -> Delayed env a
202215emptyDelayed result =
203− Delayed (const r) r r r (\ _ _ _ _ -> result)
216+ Delayed (const r) r r r r r (const r) (\ _ _ _ _ _ -> result)
204217 where
205218 r = return ()
206219
@@ -225,10 +238,21 @@ addCapture :: Delayed env (a -> b)
225238addCapture Delayed{..} new =
226239 Delayed
227240 { capturesD = \ (txt, env) -> (,) <$> capturesD env <*> new txt
228− , serverD = \ (x, v) a b req -> ($ v) <$> serverD x a b req
241+ , serverD = \ (x, v) p a b req -> ($ v) <$> serverD x p a b req
229242 , ..
230243 } -- Note [Existential Record Update]
231244
245+-- | Add a parameter check to the end of the params block
246+addParameterCheck :: Delayed env (a -> b)
247+ -> DelayedIO a
248+ -> Delayed env b
249+addParameterCheck Delayed {..} new =
250+ Delayed
251+ { paramsD = (,) <$> paramsD <*> new
252+ , serverD = \c (p, pNew) a b req -> ($ pNew) <$> serverD c p a b req
253+ , ..
254+ }
255+
232256-- | Add a method check to the end of the method block.
233257addMethodCheck :: Delayed env a
234258 -> DelayedIO ()
@@ -246,24 +270,29 @@ addAuthCheck :: Delayed env (a -> b)
246270addAuthCheck Delayed{..} new =
247271 Delayed
248272 { authD = (,) <$> authD <*> new
249− , serverD = \ c (y, v) b req -> ($ v) <$> serverD c y b req
273+ , serverD = \ c p (y, v) b req -> ($ v) <$> serverD c p y b req
250274 , ..
251275 } -- Note [Existential Record Update]
252276
253−-- | Add a body check to the end of the body block.
277+-- | Add a content type and body checks around parameter checks.
278+--
279+-- We'll report failed content type check (415), before trying to parse
280+-- query parameters (400). Which, in turn, happens before request body parsing.
254281addBodyCheck :: Delayed env (a -> b)
255− -> DelayedIO a
282+ -> DelayedIO c -- ^ content type check
283+ -> (c -> DelayedIO a) -- ^ body check
256284 -> Delayed env b
257−addBodyCheck Delayed{..} new =
285+addBodyCheck Delayed{..} newContentD newBodyD =
258286 Delayed
259− { bodyD = (,) <$> bodyD <*> new
260− , serverD = \ c a (z, v) req -> ($ v) <$> serverD c a z req
287+ { contentD = (,) <$> contentD <*> newContentD
288+ , bodyD = \(content, c) -> (,) <$> bodyD content <*> newBodyD c
289+ , serverD = \ c p a (z, v) req -> ($ v) <$> serverD c p a z req
261290 , ..
262291 } -- Note [Existential Record Update]
263292
264293
265−-- | Add an accept header check to the beginning of the body
266−-- block. There is a tradeoff here. In principle, we'd like
294+-- | Add an accept header check before handling parameters.
295+-- In principle, we'd like
267296-- to take a bad body (400) response take precedence over a
268297-- failed accept check (406). BUT to allow streaming the body,
269298-- we cannot run the body check and then still backtrack.
@@ -277,7 +306,7 @@ addAcceptCheck :: Delayed env a
277306 -> Delayed env a
278307addAcceptCheck Delayed{..} new =
279308 Delayed
280− { bodyD = new *> bodyD
309+ { acceptD = acceptD *> new
281310 , ..
282311 } -- Note [Existential Record Update]
283312
@@ -287,7 +316,7 @@ addAcceptCheck Delayed{..} new =
287316passToServer :: Delayed env (a -> b) -> (Request -> a) -> Delayed env b
288317passToServer Delayed{..} x =
289318 Delayed
290− { serverD = \ c a b req -> ($ x req) <$> serverD c a b req
319+ { serverD = \ c p a b req -> ($ x req) <$> serverD c p a b req
291320 , ..
292321 } -- Note [Existential Record Update]
293322
@@ -301,16 +330,16 @@ runDelayed :: Delayed env a
301330 -> env
302331 -> Request
303332 -> ResourceT IO (RouteResult a)
304−runDelayed Delayed{..} env req =
305− runDelayedIO
306− (do c <- capturesD env
307− methodD
308− a <- authD
309− b <- bodyD
310− r <- ask
311− liftRouteResult (serverD c a b r)
312− )
313− req
333+runDelayed Delayed{..} env = runDelayedIO $ do
334+ r <- ask
335+ c <- capturesD env
336+ methodD
337+ a <- authD
338+ acceptD
339+ content <- contentD
340+ p <- paramsD -- Has to be before body parsing, but after content-type checks
341+ b <- bodyD content
342+ liftRouteResult (serverD c p a b r)
314343
315344-- | Runs a delayed server and the resulting action.
316345-- Takes a continuation that lets us send a response.