Refactor request body handling to fix issue #147 #206
Closed
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Issue #147 happens because of the semantics of
Network.WAI
'srequestBody
function, which is an IO action that you must call repeatedly to get all the chunks of the body. Previously, Scotty's route handler code made the mistake of evaluating the whole body inRoute.hs:mkEnv
, which gets called on every matched route. As a result, if you match a route which then callsnext
, the body will be consumed already and will be unavailable to future routes.The solution in this PR is to recognize that we can't represent the
routes
inScottyState
simply as a[Middleware m]
, because evaluation of an earlierMiddleware
can screw up the state for future ones. Instead, we need an extra top-level piece of state, which I've called theBodyInfo
. This state will keep track of all the body chunks that we've read and provide them to the route-processing code, while preserving the semantics of things likebodyReader
.A
BodyInfo
is created inscottyAppT
at the beginning of request processing, and has 3 things:bodyReader
, andrequestBody
function which can be called to get more chunksThe key part of having two MVars is that we can "clone" the
BodyInfo
to create a copy where the index is reset to 0, but the chunk cache is the same. Passing a clonedBodyInfo
into each matched route allows them each to start from the first chunk if they callbodyReader
.Please let me know what you think! One TODO is to add some tests that
bodyReader
actually works properly, does anyone know how I can test a chunked request?