theory_student
theory_student

Reputation: 77

How to run an action after response has been fully sent in Scotty / WAI

Upon a GET request, my Scotty webapp will run some computation and store its result in a temporary file, which it sends as the response using file.

Now I would like to run some cleanup (i.e. delete the temporary file) after the file has been sent. Scotty does not seem to include a way for doing so.

Is there is any functionality in WAI for achieving this?

Upvotes: 2

Views: 178

Answers (1)

danidiaz
danidiaz

Reputation: 27766

wai gives us a function responseStream

responseStream :: Status -> ResponseHeaders -> StreamingBody -> Response

that constructs a Response out of a StreamingBody, which is actually a function

type StreamingBody = (Builder -> IO ()) -> IO () -> IO ()

that, given a "write" action, and a "flush" action, finds some bytes somewhere and performs all the writing and flushing.

wai also provides us with a ready-made responseFile function:

responseFile :: Status -> ResponseHeaders -> FilePath -> Maybe FilePart -> Response

but it doesn't delete the file at the end. Could we modify it in some way? It seems that we can, with the help of the responseToStream auxiliary function

responseToStream :: Response -> (Status, ResponseHeaders, (StreamingBody -> IO a) -> IO a)

that "opens up" an already constructed Response, allowing us to tweak things.

Like this:

import Network.Wai
import Network.HTTP.Types.Status (status200)
import System.Directory (removeFile)

responseFileDeleting' :: FilePath -> Response
responseFileDeleting' filepath = 
    let (status,header,streamer) = 
            responseToStream $ responseFile status200 [] filepath Nothing
     in responseStream status header (\write flush ->
             -- this would be a good place to put a bracket, if needed
             do streamer (\body -> body write flush)
                removeFile filepath)

(Note: the type of streamer is a bit mind-twisting because of all the higher-orderness.)

This solution has the disadvantage that requests have to wait until the file is deleted in order to complete. Another option could be to send filepaths to some kind of concurrent queue that performed the deletions in another thread.

Upvotes: 1

Related Questions