Request suspension in Scotty with MVar

March 04, 2016

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".