module Serv.Internal.Server.Type where
import Data.String
import Data.Maybe (fromMaybe)
import qualified Data.ByteString.Char8 as S8
import qualified Data.ByteString.Lazy as Sl
import Data.Function ((&))
import Data.Proxy
import Network.HTTP.Media (MediaType, Quality, renderHeader)
import qualified Network.HTTP.Types as HTTP
import qualified Network.Wai as Wai
import Serv.Internal.Api
import qualified Serv.Internal.Header as Header
import qualified Serv.Internal.MediaType as MediaType
import Serv.Internal.Pair
import Serv.Internal.Rec
import qualified Serv.Internal.Verb as Verb
import Serv.Internal.Server.Context (Context)
import qualified Serv.Internal.Server.Context as Context
import Serv.Internal.Server.Error (RoutingError)
import qualified Serv.Internal.Server.Error as Error
data NotHere = NotHere
noOp :: Applicative m => m NotHere
noOp = pure NotHere
data a :<|> b = a :<|> b
infixr 5 :<|>
data ServerValue
= RoutingError RoutingError
| WaiResponse Wai.Response
| Application Wai.Application
runServerWai
:: Context
-> (Wai.Response -> IO Wai.ResponseReceived)
-> (Server IO -> IO Wai.ResponseReceived)
runServerWai context respond server = do
val <- runServer server context
case val of
RoutingError err -> respond $ case err of
Error.NotFound ->
Wai.responseLBS HTTP.notFound404 [] ""
Error.BadRequest e -> do
let errString = fromString (fromMaybe "" e)
Wai.responseLBS HTTP.badRequest400 [] (fromString errString)
Error.UnsupportedMediaType ->
Wai.responseLBS HTTP.unsupportedMediaType415 [] ""
Error.MethodNotAllowed verbs -> do
let verbNames = map Verb.standardName verbs
allowHeader = S8.intercalate "," verbNames
Wai.responseLBS HTTP.methodNotAllowed405 [("Allow", allowHeader)] ""
WaiResponse resp -> respond resp
Application app -> app (Context.request context) respond
newtype Server m = Server { runServer :: Context -> m ServerValue }
transformServer :: (forall x . m x -> n x) -> Server m -> Server n
transformServer phi (Server act) = Server (phi . act)
orElse :: Monad m => Server m -> Server m -> Server m
orElse sa sb = Server $ \ctx -> do
a <- runServer sa ctx
case a of
RoutingError e
| Error.ignorable e -> runServer sb ctx
| otherwise -> return a
_ -> return a
routingError :: Monad m => RoutingError -> m ServerValue
routingError err = return (RoutingError err)
data Response (headers :: [Pair Header.HeaderName *]) body where
Response
:: HTTP.Status
-> [HTTP.Header]
-> Rec headers
-> a
-> Response headers ('Body ctypes a)
EmptyResponse
:: HTTP.Status
-> [HTTP.Header]
-> Rec headers
-> Response headers 'Empty
emptyResponse :: HTTP.Status -> Response '[] 'Empty
emptyResponse status = EmptyResponse status [] Nil
withBody
:: a -> Response headers 'Empty -> Response headers ('Body ctypes a)
withBody a (EmptyResponse status secretHeaders headers) =
Response status secretHeaders headers a
withHeader
:: Proxy name -> value
-> Response headers body -> Response (name '::: value ': headers) body
withHeader proxy val r = case r of
Response status secretHeaders headers body ->
Response status secretHeaders (headers & proxy -: val) body
EmptyResponse status secretHeaders headers ->
EmptyResponse status secretHeaders (headers & proxy -: val)
withQuietHeader
:: Header.HeaderEncode name value
=> Proxy name -> value
-> Response headers body -> Response headers body
withQuietHeader proxy value r =
case Header.headerPair proxy value of
Nothing -> r
Just newHeader ->
case r of
Response status secretHeaders headers body ->
Response status (newHeader : secretHeaders) headers body
EmptyResponse status secretHeaders headers ->
EmptyResponse status (newHeader : secretHeaders) headers
resortHeaders :: RecordIso headers headers' => Response headers body -> Response headers' body
resortHeaders r =
case r of
Response status secretHeaders headers body ->
Response status secretHeaders (reorder headers) body
EmptyResponse status secretHeaders headers ->
EmptyResponse status secretHeaders (reorder headers)
deleteBody :: Response headers body -> Response headers 'Empty
deleteBody r =
case r of
Response status secretHeaders headers _ ->
EmptyResponse status secretHeaders headers
EmptyResponse{} -> r
class Header.ReflectHeaders headers => WaiResponse headers body where
waiResponse :: [Quality MediaType] -> Response headers body -> Wai.Response
instance Header.ReflectHeaders headers => WaiResponse headers 'Empty where
waiResponse _ (EmptyResponse status secretHeaders headers) =
Wai.responseLBS status (secretHeaders ++ Header.reflectHeaders headers) ""
instance
(Header.ReflectHeaders headers, MediaType.ReflectEncoders ctypes a) =>
WaiResponse headers ('Body ctypes a)
where
waiResponse accepts (Response status secretHeaders headers value) =
case MediaType.negotiateContentAlways (Proxy :: Proxy ctypes) accepts value of
Nothing -> Wai.responseLBS HTTP.notAcceptable406 [] ""
Just (mtChosen, result) ->
let headers0 = Header.reflectHeaders headers
headers1 = ("Content-Type", renderHeader mtChosen) : headers0
headers2 = secretHeaders ++ headers1
in Wai.responseLBS status headers2 $ Sl.fromStrict result