From 83f55259fa45f2ecfc73e0c2833ec262347a0fdb Mon Sep 17 00:00:00 2001
From: "Julian K. Arni" <jkarni@gmail.com>
Date: Fri, 2 Jan 2015 19:34:15 +0100
Subject: [PATCH 1/4] Memoize requestBody IO action.

---
 src/Servant/Server/Internal.hs | 14 ++++++++++----
 1 file changed, 10 insertions(+), 4 deletions(-)

diff --git a/src/Servant/Server/Internal.hs b/src/Servant/Server/Internal.hs
index a706c211..8a8d423a 100644
--- a/src/Servant/Server/Internal.hs
+++ b/src/Servant/Server/Internal.hs
@@ -10,13 +10,14 @@ module Servant.Server.Internal where
 import Control.Applicative
 import Control.Monad.Trans.Either
 import Data.Aeson
-import Data.Maybe (catMaybes)
+import Data.IORef
+import Data.Maybe (catMaybes, fromMaybe)
 import Data.Monoid
 import Data.Proxy
 import Data.String
 import Data.String.Conversions
-import Data.Text (Text)
 import Data.Text.Encoding (decodeUtf8)
+import Data.Text (Text)
 import GHC.TypeLits
 import Network.HTTP.Types hiding (Header)
 import Network.Wai
@@ -25,7 +26,12 @@ import Servant.Common.Text
 
 toApplication :: RoutingApplication -> Application
 toApplication ra request respond = do
-  ra request (routingRespond . routeResult)
+  reqBodyRef <- newIORef Nothing
+  let memoReqBody = fromMaybe <$> (do
+        r <- requestBody request
+        writeIORef reqBodyRef $ Just r
+        return r ) <*> readIORef reqBodyRef
+  ra request{ requestBody = memoReqBody } (routingRespond . routeResult)
  where
   routingRespond :: Either RouteMismatch Response -> IO ResponseReceived
   routingRespond (Left NotFound) =
@@ -44,7 +50,7 @@ data RouteMismatch =
   | InvalidBody -- ^ an even more informative "your json request body wasn't valid" error
   deriving (Eq, Show)
 
--- | 
+-- |
 -- @
 -- > mempty = NotFound
 -- >

From ecb1da0e37a260b0ee9123a57ddbd10b4d39b70d Mon Sep 17 00:00:00 2001
From: "Julian K. Arni" <jkarni@gmail.com>
Date: Sat, 3 Jan 2015 18:07:39 +0100
Subject: [PATCH 2/4] Cycle through ByteString.

---
 src/Servant/Server/Internal.hs | 28 ++++++++++++++++++++++------
 1 file changed, 22 insertions(+), 6 deletions(-)

diff --git a/src/Servant/Server/Internal.hs b/src/Servant/Server/Internal.hs
index 8a8d423a..2b16fea0 100644
--- a/src/Servant/Server/Internal.hs
+++ b/src/Servant/Server/Internal.hs
@@ -10,8 +10,10 @@ module Servant.Server.Internal where
 import Control.Applicative
 import Control.Monad.Trans.Either
 import Data.Aeson
+import qualified Data.ByteString as B
+import qualified Data.ByteString.Lazy as BL
 import Data.IORef
-import Data.Maybe (catMaybes, fromMaybe)
+import Data.Maybe (catMaybes)
 import Data.Monoid
 import Data.Proxy
 import Data.String
@@ -24,13 +26,27 @@ import Network.Wai
 import Servant.API
 import Servant.Common.Text
 
+data ReqBodyState = Uncalled
+                  | Called B.ByteString
+                  | Done B.ByteString
+
 toApplication :: RoutingApplication -> Application
 toApplication ra request respond = do
-  reqBodyRef <- newIORef Nothing
-  let memoReqBody = fromMaybe <$> (do
-        r <- requestBody request
-        writeIORef reqBodyRef $ Just r
-        return r ) <*> readIORef reqBodyRef
+  reqBodyRef <- newIORef Uncalled
+  let memoReqBody = do
+          ior <- readIORef reqBodyRef
+          case ior of
+            Uncalled -> do
+                r <- BL.toStrict <$> strictRequestBody request
+                writeIORef reqBodyRef $ Done r
+                return r
+            Called bs -> do
+                writeIORef reqBodyRef $ Done bs
+                return bs
+            Done bs -> do
+                writeIORef reqBodyRef $ Called bs
+                return B.empty
+
   ra request{ requestBody = memoReqBody } (routingRespond . routeResult)
  where
   routingRespond :: Either RouteMismatch Response -> IO ResponseReceived

From 726848b8a3e58bc92172bc1bedd7e9d72d2b9b8c Mon Sep 17 00:00:00 2001
From: "Julian K. Arni" <jkarni@gmail.com>
Date: Sat, 3 Jan 2015 18:16:26 +0100
Subject: [PATCH 3/4] Add comment about what the bug was. Make ReqBodyState
 strict.

---
 src/Servant/Server/Internal.hs | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/src/Servant/Server/Internal.hs b/src/Servant/Server/Internal.hs
index 2b16fea0..5a9fbe5b 100644
--- a/src/Servant/Server/Internal.hs
+++ b/src/Servant/Server/Internal.hs
@@ -27,12 +27,15 @@ import Servant.API
 import Servant.Common.Text
 
 data ReqBodyState = Uncalled
-                  | Called B.ByteString
-                  | Done B.ByteString
+                  | Called !B.ByteString
+                  | Done !B.ByteString
 
 toApplication :: RoutingApplication -> Application
 toApplication ra request respond = do
   reqBodyRef <- newIORef Uncalled
+  -- We need to check the requestBody possibly more than once, so instead
+  -- of consuming it entirely once, we cycle through it.
+  -- See https://github.com/haskell-servant/servant/issues/3
   let memoReqBody = do
           ior <- readIORef reqBodyRef
           case ior of

From b43301967d20c160ef624129a0b20e596261af22 Mon Sep 17 00:00:00 2001
From: "Julian K. Arni" <jkarni@gmail.com>
Date: Sun, 4 Jan 2015 16:08:22 +0100
Subject: [PATCH 4/4] Update reqBodyRef comment.

---
 src/Servant/Server/Internal.hs | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/src/Servant/Server/Internal.hs b/src/Servant/Server/Internal.hs
index 5a9fbe5b..e87a4733 100644
--- a/src/Servant/Server/Internal.hs
+++ b/src/Servant/Server/Internal.hs
@@ -33,8 +33,11 @@ data ReqBodyState = Uncalled
 toApplication :: RoutingApplication -> Application
 toApplication ra request respond = do
   reqBodyRef <- newIORef Uncalled
-  -- We need to check the requestBody possibly more than once, so instead
-  -- of consuming it entirely once, we cycle through it.
+  -- We may need to consume the requestBody more than once.  In order to
+  -- maintain the illusion that 'requestBody' works as expected,
+  -- 'ReqBodyState' is introduced, and the complete body is memoized and
+  -- returned as many times as requested with empty "Done" marker chunks in
+  -- between.
   -- See https://github.com/haskell-servant/servant/issues/3
   let memoReqBody = do
           ior <- readIORef reqBodyRef