packages feed

schedule-planner-1.0.1.1: src/SchedulePlanner/Server.hs

{-# LANGUAGE CPP               #-}
{-# LANGUAGE OverloadedStrings #-}
{-|
Module      : $Header$
Description : functions necessary for deploying the application as a webservice
Copyright   : (c) Justus Adam, 2015
License     : LGPL-3
Maintainer  : development@justusadam.com
Stability   : experimental
Portability : POSIX

Uses wai and warp to create a deployable web service instance of this software.
-}
module SchedulePlanner.Server (server, app, ServerOptions(..)) where


import           Data.ByteString.Lazy     (ByteString)
import           Network.HTTP.Types       (Header, imATeaPot418, methodOptions,
                                           methodPost, ok200)
import           Network.Wai              (Application, lazyRequestBody,
                                           remoteHost, requestMethod,
                                           responseLBS)
import           Network.Wai.Handler.Warp (run)
#ifndef NOSCRAPER
import           SchedulePlanner.Scraper
#endif
import           System.IO                (IOMode (AppendMode), hPutStrLn,
                                           withFile)


defaultHeaders :: [Header]
defaultHeaders =
  [ ("Access-Control-Allow-Origin" , "http://justus.science")
  , ("Access-Control-Allow-Methods", "POST")
  , ("Access-Control-Allow-Headers", "Content-Type")
  ]


{-|
  Options used for the "serve" subcommand.
-}
data ServerOptions = ServerOptions
  { port    :: Int -- ^ default 'defaultServerPort'
  , logFile :: Maybe FilePath
  }


writeToLog :: FilePath -> String -> IO ()
writeToLog logfile = withFile logfile AppendMode . flip hPutStrLn


{-|
  The 'Application' used for the server instance.
-}
app :: ServerOptions -> (ByteString -> ByteString) -> Application
app opts app' request respond
  | rMethod == methodPost =
    logPureReq ("New POST request from " ++ (show $ remoteHost request)) >>
    lazyRequestBody request >>=
      respond . responseLBS ok200 headers . app'
  | rMethod == methodOptions = respond $ responseLBS ok200 headers "Bring it!"
  | otherwise =
    logPureReq ("Unhandleable request: " ++ show request) >>
    respond (responseLBS imATeaPot418 [] "What are you doing to an innocent teapot?")
  where
    logAction logfile = withFile logfile AppendMode . flip hPutStrLn
    logPureReq message =
      maybe
        (return ())
        (flip logAction message)
        (logFile opts)
    logIOReq messageGetter =
      maybe
        (return ())
        ((>>=) messageGetter . logAction)
        (logFile opts)
    rMethod = requestMethod request
    headers = defaultHeaders


{-|
  Run the server.
-}
server :: ServerOptions -> (ByteString -> ByteString) -> IO ()
server opts@(ServerOptions { port = port, logFile = logfile }) =
  (>>) (maybe (return ()) logInit logfile) . run port . app opts
  where
    logInit = flip writeToLog $ "Server starting on port " ++ show port