packages feed

ficketed-0.1.0.0: Network/Socketed/Application/Ficketed.hs

{-# LANGUAGE OverloadedStrings #-}

module Network.Socketed.Application.Ficketed (
      FicketedOptions(..), runFicketedServer, mimeLookup
   ) where

import Data.ByteString (ByteString, isPrefixOf)
import Data.ByteString.Char8 (pack, unpack)
import Data.ByteString.Lazy (toStrict)
import Data.Binary.Builder (
      fromByteString, toLazyByteString, putStringUtf8
   )
import qualified Data.Text as T
import Data.Text.Encoding (decodeUtf8)
import Data.Maybe (fromMaybe)
import Data.Map.Strict (insert)
import Data.String.Utils (split)

import Network.Mime (
      FileName, MimeType,
      defaultMimeLookup, defaultMimeMap, mimeByExt
   )
import Network.HTTP.Types (Header)

import Network.Wai (
      Middleware, Response, StreamingBody,
      mapResponseHeaders, responseToStream, responseStream, rawPathInfo
   )
import Network.Wai.Application.Static (
      defaultFileServerSettings, staticApp, ssIndices
   )
import Network.Wai.Handler.Warp (run)

import System.Directory (getCurrentDirectory)

import Text.Blaze.Html5 (text)
import Text.Blaze.Html.Renderer.String (renderHtml)

import Network.Socketed.Application.Ficketed.Internal (FicketedOptions(..))
import Network.Socketed.Application.Ficketed.Template (refreshHtml)

coerceHeaders :: Response -> Response
coerceHeaders = mapResponseHeaders (map f)
   where
      f :: Header -> Header
      f ("Content-Type", _) = ("Content-Type", "text/html")
      f h = h

rewriteHTML :: FicketedOptions -> ByteString -> StreamingBody -> StreamingBody
rewriteHTML opts path sbody send flush
   = sbody send flush
   >> send (fromByteString . pack . refreshHtml (unpack path) $ opts)
   >> flush

rewriteText :: FicketedOptions -> ByteString -> StreamingBody -> StreamingBody
rewriteText opts path sbody send flush
   = send (fromByteString "<pre>")
   >> sbody
      (send
         . putStringUtf8
         . renderHtml
         . text
         . decodeUtf8
         . toStrict
         . toLazyByteString)
      flush
   >> send (fromByteString "</pre>")
   >> send (fromByteString . pack .refreshHtml (unpack path) $ opts)
   >> flush

mimeLookup :: FicketedOptions -> FileName -> MimeType
mimeLookup opts fn = mimeByExt mimeMap (defaultMimeLookup fn) fn
   where
      f m e = insert (T.pack e) "text/plain" m
      mimeMap = foldl f defaultMimeMap $ split "," $ textExts opts

shouldRewrite :: FicketedOptions -> MimeType -> Bool
shouldRewrite (FicketedOptions _ _ _ _ _ x e _ _) m =
   ("text/" `isPrefixOf` m || m `elem` extra)
   && unpack m `notElem` skipped
      where
         skipped = split "," x
         extra = map pack $ split "," e

wrap :: FicketedOptions -> Middleware
wrap opts app req respond = app req f where
   m = mimeLookup opts . decodeUtf8 . rawPathInfo $ req
   f res =
      case () of
      _
         | shouldRewrite opts m ->
            let
               (s, hs, wb) = responseToStream . coerceHeaders $ res
               hMimes = map pack $ split "," (htmlMimes opts)
               rewrite = if m `elem` hMimes then rewriteHTML else rewriteText
            in
               wb
                  $ respond
                  . responseStream s hs
                  . rewrite opts (rawPathInfo req)
         | otherwise -> respond res

runFicketedServer :: FicketedOptions -> IO ()
runFicketedServer opts@(FicketedOptions d p _ _ _ _ _ _ _) = do
   pwd <- getCurrentDirectory
   run p
      $ wrap opts
      $ staticApp (defaultFileServerSettings $ fromMaybe pwd d) {
            ssIndices = [] -- disable auto index
         }