               → Route Response
serveDirectory l d p = do
    fullP ← pathString
    partialPath p
    (method GET <|> method HEAD)
    urlP ← pathString
    let p' = dropWhileEnd (≡ '/') d ++ B.unpack urlP
    fe ← liftIO $ doesFileExist p'
    de ← liftIO $ doesDirectoryExist p'
    guard (fe ∨ de ∧ l)
    if fe
        then detectContentType p' >> file p'
        else do
            cs ← filter (\x → x ≠ "." ∧ x ≠ "..") <$> liftIO (getDirectoryContents p') >>= liftIO ∘ mapM (\x → let y = B.pack x in doesDirectoryExist (dropWhileEnd (≡ '/') d ++ B.unpack urlP ++ '/' : x) >>= \b → return $ if b then B.concat [y, "/"] else y)
            contentType "text/html"
            okay $ B.concat ["<html><head><title>Index of ", fullP, "</title></head><body><h2>Index of "
                            , fullP, "</h2><hr>", B.concat $ (\x → ["<a href=\"", fullP, (if fullP ≠ "/" then "/" else "" ∷ B.ByteString), x, "\">", x, "</a><br>"]) =<< if fullP ≠ "/" then ".." : cs else cs
                            , "</body></html>"]

runRoutes ∷ [Route Response] → (Request, [Header]) → IO (Maybe (Response, [Header]))
runRoutes rs s = do
    v ← mapM (`runMaybeState` s) rs >>= return ∘ msum
    case v of
        Just (r, (_, hs)) → return $ Just (r, hs)
        Nothing
serveDirectory ∷ Bool -- ^ Allow file index for directories
               → FilePath -- ^ Path to the directory
               → B.ByteString -- ^ Routing path
               → Route Response
serveDirectory l d p = do
    fullP ← pathString
    partialPath p
    (method GET <|> method HEAD)
    urlP ← pathString
    let p' = dropWhileEnd (≡ '/') d ++ B.unpack urlP
    fe ← liftIO $ doesFileExist p'
    de ← liftIO $ doesDirectoryExist p'
    guard (fe ∨ de ∧ l)
    if fe
        then detectContentType p' >> file p'
        else do
            cs ← filter (\x → x ≠ "." ∧ x ≠ "..") <$> liftIO (getDirectoryContents p') >>= liftIO ∘ mapM (\x → let y = B.pack x in doesDirectoryExist (dropWhileEnd (≡ '/') d ++ B.unpack urlP ++ '/' : x) >>= \b → return $ if b then B.concat [y, "/"] else y)
            contentType "text/html"
            okay $ B.concat ["<html><head><title>Index of ", fullP, "</title></head><body><h2>Index of "
                            , fullP, "</h2><hr>", B.concat $ (\x → ["<a href=\"", fullP, (if fullP ≠ "/" then "/" else "" ∷ B.ByteString), x, "\">", x, "</a><br>"]) =<< if fullP ≠ "/" then ".." : cs else cs
                            , "</body></html>"]
serveDirectory l d p = do
    fullP ← pathString
    partialPath p
    (method GET <|> method HEAD)
    urlP ← pathString
    let p' = dropWhileEnd (≡ '/') d ++ B.unpack urlP
    fe ← liftIO $ doesFileExist p'
    de ← liftIO $ doesDirectoryExist p'
    guard (fe ∨ de ∧ l)
    if fe
        then detectContentType p' >> file p'
        else do
            cs ← filter (\x → x ≠ "." ∧ x ≠ "..") <$> liftIO (getDirectoryContents p') >>= liftIO ∘ mapM (\x → let y = B.pack x in doesDirectoryExist (dropWhileEnd (≡ '/') d ++ B.unpack urlP ++ '/' : x) >>= \b → return $ if b then B.concat [y, "/"] else y)
            contentType "text/html"
            okay $ B.concat ["<html><head><title>Index of ", fullP, "</title></head><body><h2>Index of "
                            , fullP, "</h2><hr>", B.concat $ (\x → ["<a href=\"", fullP, (if fullP ≠ "/" then "/" else "" ∷ B.ByteString), x, "\">", x, "</a><br>"]) =<< if fullP ≠ "/" then ".." : cs else cs
                            , "</body></html>"]