Request suspension in Scotty with MVar

MVar makes it easy to share mutable state among different IO actions. This is handy in Scotty, which uses ActionT Text IO for request/response handling.

Let's use MVar to make a couple of endpoints: one that suspends, waiting for an MVar to be set, and another to set it.

{-# LANGUAGE OverloadedStrings #-}

import qualified Control.Concurrent.MVar as MVar
import qualified Control.Monad.Trans.Class as Class
import qualified Data.Text.Lazy as TL
import qualified Web.Scotty as Scotty

main :: IO ()
main = do
  sem <- MVar.newEmptyMVar
  Scotty.scotty 3000 $ do
    Scotty.get "/suspend" $ do
      key <- Class.lift $ MVar.takeMVar sem
      Scotty.text $ TL.pack $
        concat [
            "Resumed with \""
          , (TL.unpack key)
          , "\"."
        ]
    Scotty.get "/resume/:key" $ do
      key <- Scotty.param "key"
      Class.lift $ MVar.putMVar sem key
      Scotty.text $ TL.pack $
        concat [
            "Resuming with \""
          , (TL.unpack key)
          , "\"."
        ]

This requires a few dependencies from Hackage:

build-depends: base, scotty, transformers, text

We can run the server with cabal:

$ cabal run
Preprocessing executable 'scotty-suspend' for scotty-suspend-0.1.0.0...
Running scotty-suspend...
Setting phasers to stun... (port 3000) (ctrl-c to quit)

Now we can make a couple of requests that get suspended until later:

$ curl localhost:3000/suspend
(this hangs until the first /resume/:key is requested)
Resumed with "foo".
$ curl localhost:3000/suspend
(this hangs until the next /resume/:key is requested)
Resumed with "bar".

Finally we can resume the suspended requests with a couple more:

$ curl localhost:3000/resume/foo
Resuming with "foo".
$ curl localhost:3000/resume/bar
Resuming with "bar".