@@ -132,14 +132,14 @@ toApplication ra request respond = ra request routingRespond
132
132
-- 405 (bad method)
133
133
-- 401 (unauthorized)
134
134
-- 415 (unsupported media type)
135
- -- 400 (bad request)
136
135
-- 406 (not acceptable)
136
+ -- 400 (bad request)
137
137
-- @
138
138
--
139
139
-- Therefore, while routing, we delay most checks so that they
140
140
-- will ultimately occur in the right order.
141
141
--
142
- -- A 'Delayed' contains three delayed blocks of tests, and
142
+ -- A 'Delayed' contains many delayed blocks of tests, and
143
143
-- the actual handler:
144
144
--
145
145
-- 1. Delayed captures. These can actually cause 404, and
@@ -152,23 +152,36 @@ toApplication ra request respond = ra request routingRespond
152
152
-- it does not provide an input for the handler. Method checks
153
153
-- are comparatively cheap.
154
154
--
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.
159
164
--
160
165
data Delayed env c where
161
166
Delayed :: { capturesD :: env -> DelayedIO captures
162
167
, methodD :: DelayedIO ()
163
168
, 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
166
179
} -> Delayed env c
167
180
168
181
instance Functor (Delayed env ) where
169
182
fmap f Delayed {.. } =
170
183
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
172
185
, ..
173
186
} -- Note [Existential Record Update]
174
187
@@ -200,7 +213,7 @@ runDelayedIO m req = transResourceT runRouteResultT $ runReaderT (runDelayedIO'
200
213
-- | A 'Delayed' without any stored checks.
201
214
emptyDelayed :: RouteResult a -> Delayed env a
202
215
emptyDelayed result =
203
- Delayed (const r) r r r ( \ _ _ _ _ -> result)
216
+ Delayed (const r) r r r r r ( const r) ( \ _ _ _ _ _ -> result)
204
217
where
205
218
r = return ()
206
219
@@ -225,10 +238,21 @@ addCapture :: Delayed env (a -> b)
225
238
addCapture Delayed {.. } new =
226
239
Delayed
227
240
{ 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
229
242
, ..
230
243
} -- Note [Existential Record Update]
231
244
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
+
232
256
-- | Add a method check to the end of the method block.
233
257
addMethodCheck :: Delayed env a
234
258
-> DelayedIO ()
@@ -246,24 +270,29 @@ addAuthCheck :: Delayed env (a -> b)
246
270
addAuthCheck Delayed {.. } new =
247
271
Delayed
248
272
{ 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
250
274
, ..
251
275
} -- Note [Existential Record Update]
252
276
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.
254
281
addBodyCheck :: Delayed env (a -> b )
255
- -> DelayedIO a
282
+ -> DelayedIO c -- ^ content type check
283
+ -> (c -> DelayedIO a ) -- ^ body check
256
284
-> Delayed env b
257
- addBodyCheck Delayed {.. } new =
285
+ addBodyCheck Delayed {.. } newContentD newBodyD =
258
286
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
261
290
, ..
262
291
} -- Note [Existential Record Update]
263
292
264
293
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
267
296
-- to take a bad body (400) response take precedence over a
268
297
-- failed accept check (406). BUT to allow streaming the body,
269
298
-- we cannot run the body check and then still backtrack.
@@ -277,7 +306,7 @@ addAcceptCheck :: Delayed env a
277
306
-> Delayed env a
278
307
addAcceptCheck Delayed {.. } new =
279
308
Delayed
280
- { bodyD = new *> bodyD
309
+ { acceptD = acceptD *> new
281
310
, ..
282
311
} -- Note [Existential Record Update]
283
312
@@ -287,7 +316,7 @@ addAcceptCheck Delayed{..} new =
287
316
passToServer :: Delayed env (a -> b ) -> (Request -> a ) -> Delayed env b
288
317
passToServer Delayed {.. } x =
289
318
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
291
320
, ..
292
321
} -- Note [Existential Record Update]
293
322
@@ -301,16 +330,16 @@ runDelayed :: Delayed env a
301
330
-> env
302
331
-> Request
303
332
-> 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)
314
343
315
344
-- | Runs a delayed server and the resulting action.
316
345
-- Takes a continuation that lets us send a response.
0 commit comments