1 {-# LANGUAGE GADTs #-} -- for 'Router' and 'Router_Union'
 
   2 {-# LANGUAGE ConstraintKinds #-}
 
   3 {-# LANGUAGE DataKinds #-} -- for 'BinTree'
 
   4 {-# LANGUAGE DefaultSignatures #-}
 
   5 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
 
   6 {-# LANGUAGE OverloadedStrings #-}
 
   7 {-# LANGUAGE UndecidableInstances #-} -- for nested type family application,
 
   8                                       -- eg. in 'BodyStreamConstraint'
 
   9 {-# OPTIONS_GHC -fno-warn-orphans #-}
 
  10 -- | See <https://hackage.haskell.org/package/symantic-http-demo symantic-http-demo>
 
  11 -- for an example of how to use this module.
 
  12 module Symantic.HTTP.Server where
 
  14 import Control.Applicative (Applicative(..))
 
  15 import Control.Arrow (first)
 
  16 import Control.Monad (Monad(..), unless, sequence, guard, (=<<))
 
  17 import Control.Monad.Trans.Class (MonadTrans(..))
 
  18 import Control.Monad.Trans.Except (ExceptT(..), runExceptT)
 
  20 import Data.Either (Either(..))
 
  21 import Data.Eq (Eq(..))
 
  22 import Data.Function (($), (.), id, const)
 
  23 import Data.Functor (Functor(..), (<$>))
 
  25 import Data.Kind (Type)
 
  26 import Data.Maybe (Maybe(..), fromMaybe, catMaybes)
 
  27 import Data.Monoid (Monoid(..))
 
  28 import Data.Ord (Ord(..))
 
  29 import Data.Proxy (Proxy(..))
 
  30 import Data.Semigroup (Semigroup(..))
 
  31 import Data.String (String, IsString(..))
 
  32 import Data.Text (Text)
 
  34 import Text.Show (Show(..))
 
  35 import qualified Control.Monad.Classes as MC
 
  36 import qualified Control.Monad.Trans.Cont as C
 
  37 import qualified Control.Monad.Trans.Reader as R
 
  38 import qualified Control.Monad.Trans.State.Strict as S
 
  39 import qualified Control.Monad.Trans.Writer.Strict as W
 
  40 import qualified Data.ByteString as BS
 
  41 import qualified Data.ByteString.Base64 as BS64
 
  42 import qualified Data.ByteString.Builder as BSB
 
  43 import qualified Data.ByteString.Lazy as BSL
 
  44 import qualified Data.List as List
 
  45 import qualified Data.List.NonEmpty as NonEmpty
 
  46 import qualified Data.Map.Merge.Strict as Map
 
  47 import qualified Data.Map.Strict as Map
 
  48 import qualified Data.Text as Text
 
  49 import qualified Data.Text.Encoding as Text
 
  50 import qualified Data.Word8 as Word8
 
  51 import qualified Network.HTTP.Media as Media
 
  52 import qualified Network.HTTP.Types as HTTP
 
  53 import qualified Network.HTTP.Types.Header as HTTP
 
  54 import qualified Network.Wai as Wai
 
  55 import qualified Web.HttpApiData as Web
 
  60 -- | (@'Server' handlers k@) is a recipe to produce an 'Wai.Application'
 
  61 -- from given ('handlers') (one per number of alternative routes),
 
  62 -- separated by (':!:').
 
  64 -- 'Server' is analogous to a scanf using the API as a format customized for HTTP routing.
 
  66 -- The multiple 'ServerCheckT' monad transformers are there
 
  67 -- to prioritize the errors according to the type of check raising them,
 
  68 -- instead of the order of the combinators within an actual API specification.
 
  69 newtype Server handlers k = Server { unServer ::
 
  71          (ServerCheckT [ServerErrorBody]        -- 8th check, 400 error
 
  72          (ServerCheckT [ServerErrorHeader]      -- 7th check, 400 error
 
  73          (ServerCheckT [ServerErrorQuery]       -- 6th check, 400 error
 
  74          (ServerCheckT [ServerErrorContentType] -- 5th check, 415 error
 
  75          (ServerCheckT [ServerErrorAccept]      -- 4th check, 406 error
 
  76          (ServerCheckT [ServerErrorBasicAuth]   -- 3rd check, 401 or 403 error
 
  77          (ServerCheckT [ServerErrorMethod]      -- 2nd check, 405 error
 
  78          (ServerCheckT [ServerErrorPath]        -- 1st check, 404 error
 
  83 -- | (@'server' api handlers@) returns an 'Wai.Application'
 
  84 -- ready to be given to @Warp.run 80@.
 
  86  Router Server handlers (Response Server) ->
 
  89 server api handlers rq re = do
 
  90         lrPath <- runServerChecks (unServer $ unTrans $ router api) $ ServerState rq
 
  92          Left err -> respondError HTTP.status404 [] err
 
  95                  Left err -> respondError HTTP.status405 [] err
 
 100                                  [] -> respondError HTTP.status500 [] err
 
 101                                  ServerErrorBasicAuth realm ba:_ ->
 
 103                                          BasicAuth_Unauthorized ->
 
 104                                                 respondError HTTP.status403 [] err
 
 106                                                 respondError HTTP.status401
 
 107                                                  [ ( HTTP.hWWWAuthenticate
 
 108                                                    , "Basic realm=\""<>Web.toHeader realm<>"\""
 
 112                                  Left err -> respondError HTTP.status406 [] err
 
 113                                  Right lrContentType ->
 
 114                                         case lrContentType of
 
 115                                          Left err -> respondError HTTP.status415 [] err
 
 118                                                  Left err -> respondError HTTP.status400 [] err
 
 121                                                          Left err -> respondError HTTP.status400 [] err
 
 124                                                                  Left err -> respondError HTTP.status400 [] err
 
 126                                                                         app handlers (serverState_request st) re
 
 131          [(HTTP.HeaderName, HeaderValue)] ->
 
 132          err -> IO Wai.ResponseReceived
 
 133         respondError st hs err =
 
 134                 -- Trace.trace (show err) $
 
 135                 re $ Wai.responseLBS st
 
 136                  ( (HTTP.hContentType, Media.renderHeader $ mediaType @PlainText)
 
 138                  ) (fromString $ show err) -- TODO: see what to return in the body
 
 140 -- | Unwrap the 'ExceptT' constructors to reach the 'Either' constructors.
 
 142  S.StateT ServerState (ExceptT e1 (ExceptT e2 (ExceptT e3 (ExceptT e4 (ExceptT e5 (ExceptT e6 (ExceptT e7 (ExceptT e8 IO)))))))) a ->
 
 143  ServerState -> IO (Either e8 (Either e7 (Either e6 (Either e5 (Either e4 (Either e3 (Either e2 (Either e1 (a, ServerState)))))))))
 
 144 runServerChecks s st =
 
 155 -- ** Type 'ServerCheckT'
 
 156 type ServerCheckT e = ExceptT (Fail e)
 
 158 -- *** Type 'RouteResult'
 
 159 type RouteResult e = Either (Fail e)
 
 163  =   Fail ServerState e -- ^ Keep trying other paths. 404, 405 or 406.
 
 164  |   FailFatal !ServerState !e -- ^ Don't try other paths.
 
 166 failState :: Fail e -> ServerState
 
 167 failState (Fail st _)      = st
 
 168 failState (FailFatal st _) = st
 
 169 failError :: Fail e -> e
 
 170 failError (Fail _st e)      = e
 
 171 failError (FailFatal _st e) = e
 
 172 instance Semigroup e => Semigroup (Fail e) where
 
 173         Fail _ x      <> Fail st y      = Fail      st (x<>y)
 
 174         FailFatal _ x <> Fail st y      = FailFatal st (x{-<>y-})
 
 175         Fail _ x      <> FailFatal st y = FailFatal st ({-x<>-}y)
 
 176         FailFatal _ x <> FailFatal st y = FailFatal st (x<>y)
 
 178 -- ** Type 'ServerState'
 
 179 newtype ServerState = ServerState
 
 180  { serverState_request :: Wai.Request
 
 182 instance Show ServerState where
 
 183         show _ = "ServerState"
 
 185 instance Cat Server where
 
 189          repr a b -> repr b c -> repr a c
 
 190         -- NOTE: if x fails, run y to see if it fails on a more prioritized check.
 
 191         -- And if so, fail with y instead of x.
 
 193         -- This long spaghetti code may probably be avoided
 
 194         -- with a more sophisticated 'Server' using a binary tree
 
 195         -- instead of nested 'Either's, so that its 'Monad' instance
 
 196         -- would do the right thing. But to my mind,
 
 197         -- with the very few priorities of checks currently needed,
 
 198         -- this is not worth the cognitive pain to design it.
 
 199         -- Some copying/pasting/adapting will do for now.
 
 200         Server x <.> Server y = Server $
 
 202                         xPath <- MC.exec @IO $ runServerChecks x st
 
 204                          Left xe -> MC.throw xe
 
 208                                         yPath <- MC.exec @IO $ runServerChecks y (failState xe)
 
 210                                          Left ye -> MC.throw ye
 
 211                                          Right _yMethod -> MC.throw xe
 
 215                                                 yPath <- MC.exec @IO $ runServerChecks y (failState xe)
 
 217                                                  Left ye -> MC.throw ye
 
 220                                                          Left ye -> MC.throw ye
 
 221                                                          Right _yBasicAuth -> MC.throw xe
 
 225                                                         yPath <- MC.exec @IO $ runServerChecks y (failState xe)
 
 227                                                          Left ye -> MC.throw ye
 
 230                                                                  Left ye -> MC.throw ye
 
 233                                                                          Left ye -> MC.throw ye
 
 234                                                                          Right _yAccept -> MC.throw xe
 
 235                                                  Right xContentType ->
 
 238                                                                 yPath <- MC.exec @IO $ runServerChecks y (failState xe)
 
 240                                                                  Left ye -> MC.throw ye
 
 243                                                                          Left ye -> MC.throw ye
 
 246                                                                                  Left ye -> MC.throw ye
 
 249                                                                                          Left ye -> MC.throw ye
 
 250                                                                                          Right _yQuery -> MC.throw xe
 
 254                                                                         yPath <- MC.exec @IO $ runServerChecks y (failState xe)
 
 256                                                                          Left ye -> MC.throw ye
 
 259                                                                                  Left ye -> MC.throw ye
 
 262                                                                                          Left ye -> MC.throw ye
 
 265                                                                                                  Left ye -> MC.throw ye
 
 268                                                                                                          Left ye -> MC.throw ye
 
 269                                                                                                          Right _yHeader -> MC.throw xe
 
 273                                                                                 yPath <- MC.exec @IO $ runServerChecks y (failState xe)
 
 275                                                                                  Left ye -> MC.throw ye
 
 278                                                                                          Left ye -> MC.throw ye
 
 281                                                                                                  Left ye -> MC.throw ye
 
 284                                                                                                          Left ye -> MC.throw ye
 
 287                                                                                                                  Left ye -> MC.throw ye
 
 290                                                                                                                          Left ye -> MC.throw ye
 
 291                                                                                                                          Right _yBody -> MC.throw xe
 
 295                                                                                         yPath <- MC.exec @IO $ runServerChecks y (failState xe)
 
 297                                                                                          Left ye -> MC.throw ye
 
 300                                                                                                  Left ye -> MC.throw ye
 
 303                                                                                                          Left ye -> MC.throw ye
 
 306                                                                                                                  Left ye -> MC.throw ye
 
 309                                                                                                                          Left ye -> MC.throw ye
 
 312                                                                                                                                  Left ye -> MC.throw ye
 
 313                                                                                                                                  Right _yBody -> MC.throw xe
 
 315                                                                                         first (. a2b) <$> S.runStateT y st'
 
 316 instance Alt Server where
 
 317         -- (<!>) :: repr a k -> repr b k -> repr (a:!:b) k
 
 318         Server x <!> Server y = Server $
 
 320                         xPath <- MC.exec @IO $ runServerChecks x st
 
 321                         let fy = (first (\b2k (_a:!:b) -> b2k b) <$>)
 
 323                          Left xe | FailFatal{} <- xe -> MC.throw xe
 
 325                                 yPath <- MC.exec @IO $ runServerChecks y st
 
 327                                  Left ye -> MC.throw (xe<>ye)
 
 329                                         fy $ ExceptT $ ExceptT $ ExceptT $ ExceptT $ ExceptT $ ExceptT $ ExceptT $ ExceptT $
 
 330                                                 return $ Right yMethod
 
 333                                  Left xe | FailFatal{} <- xe -> MC.throw xe
 
 335                                         yPath <- MC.exec @IO $ runServerChecks y st
 
 337                                          Left _ye -> MC.throw xe
 
 340                                                  Left ye -> MC.throw (xe<>ye)
 
 342                                                         fy $ ExceptT $ ExceptT $ ExceptT $ ExceptT $ ExceptT $ ExceptT $ ExceptT $
 
 343                                                                 return $ Right $ yBasicAuth
 
 346                                          Left xe | FailFatal{} <- xe -> MC.throw xe
 
 348                                                 yPath <- MC.exec @IO $ runServerChecks y st
 
 350                                                  Left _ye -> MC.throw xe
 
 353                                                          Left _ye -> MC.throw xe
 
 356                                                                  Left ye -> MC.throw (xe<>ye)
 
 358                                                                         fy $ ExceptT $ ExceptT $ ExceptT $ ExceptT $ ExceptT $ ExceptT $
 
 359                                                                                 return $ Right yAccept
 
 362                                                  Left xe | FailFatal{} <- xe -> MC.throw xe
 
 364                                                         yPath <- MC.exec @IO $ runServerChecks y st
 
 366                                                          Left _ye -> MC.throw xe
 
 369                                                                  Left _ye -> MC.throw xe
 
 372                                                                          Left _ye -> MC.throw xe
 
 375                                                                                  Left ye -> MC.throw (xe<>ye)
 
 376                                                                                  Right yContentType ->
 
 377                                                                                         fy $ ExceptT $ ExceptT $ ExceptT $ ExceptT $ ExceptT $
 
 378                                                                                                 return $ Right yContentType
 
 379                                                  Right xContentType ->
 
 381                                                          Left xe | FailFatal{} <- xe -> MC.throw xe
 
 383                                                                 yPath <- MC.exec @IO $ runServerChecks y st
 
 385                                                                  Left _ye -> MC.throw xe
 
 388                                                                          Left _ye -> MC.throw xe
 
 391                                                                                  Left _ye -> MC.throw xe
 
 394                                                                                          Left _ye -> MC.throw xe
 
 395                                                                                          Right yContentType ->
 
 397                                                                                                  Left ye -> MC.throw (xe<>ye)
 
 399                                                                                                         fy $ ExceptT $ ExceptT $ ExceptT $ ExceptT $
 
 400                                                                                                                 return $ Right yQuery
 
 403                                                                  Left xe | FailFatal{} <- xe -> MC.throw xe
 
 405                                                                         yPath <- MC.exec @IO $ runServerChecks y st
 
 407                                                                          Left _ye -> MC.throw xe
 
 410                                                                                  Left _ye -> MC.throw xe
 
 413                                                                                          Left _ye -> MC.throw xe
 
 416                                                                                                  Left _ye -> MC.throw xe
 
 417                                                                                                  Right yContentType ->
 
 419                                                                                                          Left _ye -> MC.throw xe
 
 422                                                                                                                  Left ye -> MC.throw (xe<>ye)
 
 424                                                                                                                         fy $ ExceptT $ ExceptT $ ExceptT $
 
 425                                                                                                                                 return $ Right yHeader
 
 428                                                                          Left xe | FailFatal{} <- xe -> MC.throw xe
 
 430                                                                                 yPath <- MC.exec @IO $ runServerChecks y st
 
 432                                                                                  Left _ye -> MC.throw xe
 
 435                                                                                          Left _ye -> MC.throw xe
 
 438                                                                                                  Left _ye -> MC.throw xe
 
 441                                                                                                          Left _ye -> MC.throw xe
 
 442                                                                                                          Right yContentType ->
 
 444                                                                                                                  Left _ye -> MC.throw xe
 
 447                                                                                                                          Left _ye -> MC.throw xe
 
 450                                                                                                                                  Left ye -> MC.throw (xe<>ye)
 
 452                                                                                                                                         fy $ ExceptT $ ExceptT $
 
 456                                                                                  Left xe | FailFatal{} <- xe -> MC.throw xe
 
 458                                                                                         yPath <- MC.exec @IO $ runServerChecks y st
 
 460                                                                                          Left _ye -> MC.throw xe
 
 463                                                                                                  Left _ye -> MC.throw xe
 
 466                                                                                                          Left _ye -> MC.throw xe
 
 469                                                                                                                  Left _ye -> MC.throw xe
 
 470                                                                                                                  Right yContentType ->
 
 472                                                                                                                          Left _ye -> MC.throw xe
 
 475                                                                                                                                  Left _ye -> MC.throw xe
 
 478                                                                                                                                          Left _ye -> MC.throw xe
 
 481                                                                                                                                                  Left ye -> MC.throw (xe<>ye)
 
 486                                                                                         return $ first (\a2k (a:!:_b) -> a2k a) xr
 
 487 instance Pro Server where
 
 488         dimap a2b _b2a (Server r) = Server $ (\k b2k -> k (b2k . a2b)) <$> r
 
 490 -- ** Type 'ServerErrorPath'
 
 491 newtype ServerErrorPath = ServerErrorPath Text
 
 494 instance HTTP_Path Server where
 
 495         type PathConstraint Server a = Web.FromHttpApiData a
 
 496         segment expSegment = Server $ do
 
 498                  { serverState_request = req
 
 500                 case Wai.pathInfo req of
 
 501                  []   -> MC.throw $ Fail st [ServerErrorPath "empty path segment"]
 
 502                  [""] -> MC.throw $ Fail st [ServerErrorPath "trailing slash"]
 
 504                   | curr /= expSegment ->
 
 505                         MC.throw $ Fail st [ServerErrorPath $ "expected: "<>expSegment<>" but got: "<>curr]
 
 508                          { serverState_request = req{ Wai.pathInfo = next }
 
 511         capture' :: forall a k. PathConstraint Server a => Name -> Server (a -> k) k
 
 512         capture' name = Server $ do
 
 514                  { serverState_request = req
 
 516                 case Wai.pathInfo req of
 
 517                  []   -> MC.throw $ Fail st [ServerErrorPath "empty"]
 
 518                  [""] -> MC.throw $ Fail st [ServerErrorPath "trailing slash"]
 
 520                         case Web.parseUrlPiece curr of
 
 521                          Left err -> MC.throw $ Fail st [ServerErrorPath $ "capture: "<>fromString name<>": "<>err]
 
 524                                  { serverState_request = req{ Wai.pathInfo = next }
 
 527         captureAll = Server $ do
 
 528                 req <- S.gets serverState_request
 
 529                 return ($ Wai.pathInfo req)
 
 531 -- ** Type 'ServerErrorMethod'
 
 532 data ServerErrorMethod = ServerErrorMethod
 
 535 -- | TODO: add its own error?
 
 536 instance HTTP_Version Server where
 
 537         version exp = Server $ do
 
 539                 let got = Wai.httpVersion $ serverState_request st
 
 542                  else MC.throw $ Fail st [ServerErrorMethod] -- FIXME: ServerErrorVersion
 
 544 -- ** Type 'ServerErrorAccept'
 
 545 data ServerErrorAccept =
 
 548   (Maybe (Either BS.ByteString MediaType))
 
 551 -- ** Type 'ServerErrorContentType'
 
 552 data ServerErrorContentType = ServerErrorContentType
 
 555 -- ** Type 'ServerErrorQuery'
 
 556 newtype ServerErrorQuery = ServerErrorQuery Text
 
 558 instance HTTP_Query Server where
 
 559         type QueryConstraint Server a = Web.FromHttpApiData a
 
 560         queryParams' name = Server $ do
 
 562                 lift $ ExceptT $ ExceptT $ ExceptT $ return $
 
 563                         let qs = Wai.queryString $ serverState_request st in
 
 564                         let vals = catMaybes $ (<$> qs) $ \(n,v) ->
 
 566                                  then Web.parseQueryParam . Text.decodeUtf8 <$> v
 
 568                         case sequence vals of
 
 569                          Left err -> Left  $ Fail st [ServerErrorQuery err]
 
 570                          Right vs -> Right $ Right $ Right ($ vs)
 
 572 -- ** Type 'ServerErrorHeader'
 
 573 data ServerErrorHeader = ServerErrorHeader
 
 575 instance HTTP_Header Server where
 
 576         header n = Server $ do
 
 578                 lift $ ExceptT $ ExceptT $ return $
 
 579                         let hs = Wai.requestHeaders $ serverState_request st in
 
 580                         case List.lookup n hs of
 
 581                          Nothing -> Left $ Fail st [ServerErrorHeader]
 
 582                          Just v -> Right $ Right ($ v)
 
 584 instance HTTP_Raw Server where
 
 585         type RawConstraint Server = ()
 
 586         type RawArgs Server = Wai.Application
 
 587         type Raw Server = Wai.Application
 
 588         raw = Server $ return id
 
 590 -- ** Type 'ServerErrorBasicAuth'
 
 591 data ServerErrorBasicAuth =
 
 592      ServerErrorBasicAuth BasicAuthRealm (BasicAuth ())
 
 595 -- ** Class 'ServerBasicAuth'
 
 596 -- | Custom 'BasicAuth' check.
 
 597 class ServerBasicAuth a where
 
 603 -- | WARNING: current implementation of Basic Access Authentication
 
 604 -- is not immune to certain kinds of timing attacks.
 
 605 -- Decoding payloads does not take a fixed amount of time.
 
 606 instance HTTP_BasicAuth Server where
 
 607         type BasicAuthConstraint Server a = ServerBasicAuth a
 
 608         type BasicAuthArgs Server a k = a -> k
 
 609         basicAuth' realm = Server $ do
 
 611                 let err e = MC.throw $ Fail st [ServerErrorBasicAuth realm e]
 
 612                 case decodeAuthorization $ serverState_request st of
 
 613                  Nothing -> err BasicAuth_BadPassword
 
 614                  Just (user, pass) -> do
 
 615                         MC.exec @IO (serverBasicAuth user pass) >>= \case
 
 616                          BasicAuth_BadPassword  -> err BasicAuth_BadPassword
 
 617                          BasicAuth_NoSuchUser   -> err BasicAuth_NoSuchUser
 
 618                          BasicAuth_Unauthorized -> err BasicAuth_Unauthorized
 
 619                          BasicAuth_Authorized u -> return ($ u)
 
 621                 -- | Find and decode an 'Authorization' header from the request as a Basic Auth
 
 622                 decodeAuthorization :: Wai.Request -> Maybe (BasicAuthUser, BasicAuthPass)
 
 623                 decodeAuthorization req = do
 
 624                         hAuthorization <- List.lookup "Authorization" $ Wai.requestHeaders req
 
 625                         let (basic, rest) = BS.break Word8.isSpace hAuthorization
 
 626                         guard (BS.map Word8.toLower basic == "basic")
 
 627                         let decoded = BS64.decodeLenient (BS.dropWhile Word8.isSpace rest)
 
 628                         let (user, colon_pass) = BS.break (== Word8._colon) decoded
 
 629                         (_, pass) <- BS.uncons colon_pass
 
 630                         return (Text.decodeUtf8 user, Text.decodeUtf8 pass)
 
 632 -- ** Type 'ServerErrorBody'
 
 633 newtype ServerErrorBody = ServerErrorBody String
 
 636 -- *** Type 'ServerBodyArg'
 
 637 newtype ServerBodyArg (ts::[Type]) a = ServerBodyArg a
 
 639 instance HTTP_Body Server where
 
 640         type BodyConstraint Server a ts = MimeTypes ts (MimeDecodable a)
 
 641         type BodyArg Server a ts = ServerBodyArg ts a
 
 644          BodyConstraint repr a ts =>
 
 646          repr (BodyArg repr a ts -> k) k
 
 649                 lift $ ExceptT $ ExceptT $ ExceptT $ ExceptT $ do
 
 650                         let hs = Wai.requestHeaders $ serverState_request st
 
 652                                 -- DOC: http://www.w3.org/Protocols/rfc2616/rfc2616-sec7.html#sec7.2.1
 
 653                                 -- DOC: http://www.w3.org/2001/tag/2002/0129-mime
 
 654                                 fromMaybe "application/octet-stream" $
 
 655                                 List.lookup HTTP.hContentType hs
 
 656                         case matchContent @ts @(MimeDecodable a) reqContentType of
 
 657                          Nothing -> return $ Left $ Fail st [ServerErrorContentType]
 
 658                          Just (MimeType mt) -> do
 
 659                                 bodyBS <- MC.exec @IO $ Wai.requestBody $ serverState_request st
 
 660                                 return $ Right $ Right $ Right $
 
 661                                         -- NOTE: delay 'mimeDecode' after all checks.
 
 662                                         case mimeDecode mt $ BSL.fromStrict bodyBS of
 
 663                                          Left err -> Left $ Fail st [ServerErrorBody err]
 
 664                                          Right a -> Right ($ ServerBodyArg a)
 
 666 -- *** Type 'ServerBodyStreamArg'
 
 667 newtype ServerBodyStreamArg as (ts::[Type]) framing
 
 668  =      ServerBodyStreamArg as
 
 669 instance HTTP_BodyStream Server where
 
 670         type BodyStreamConstraint Server as ts framing =
 
 671          ( FramingDecode framing as
 
 672          , MC.MonadExec IO (FramingMonad as)
 
 673          , MimeTypes ts (MimeDecodable (FramingYield as))
 
 675         type BodyStreamArg Server as ts framing =
 
 676          ServerBodyStreamArg as ts framing
 
 678          forall as ts framing k repr.
 
 679          BodyStreamConstraint repr as ts framing =>
 
 681          repr (BodyStreamArg repr as ts framing -> k) k
 
 682         bodyStream'= Server $ do
 
 684                 lift $ ExceptT $ ExceptT $ ExceptT $ ExceptT $ do
 
 685                         let hs = Wai.requestHeaders $ serverState_request st
 
 687                                 -- DOC: http://www.w3.org/Protocols/rfc2616/rfc2616-sec7.html#sec7.2.1
 
 688                                 -- DOC: http://www.w3.org/2001/tag/2002/0129-mime
 
 689                                 fromMaybe "application/octet-stream" $
 
 690                                 List.lookup HTTP.hContentType hs
 
 691                         case matchContent @ts @(MimeDecodable (FramingYield as)) reqContentType of
 
 692                          Nothing -> return $ Left $ Fail st [ServerErrorContentType]
 
 693                          Just (MimeType mt) -> do
 
 694                                 let bodyBS = Wai.requestBody $ serverState_request st
 
 695                                 return $ Right $ Right $ Right $
 
 696                                         Right ($ ServerBodyStreamArg $
 
 697                                                 framingDecode (Proxy @framing) (mimeDecode mt) $
 
 701 -- * Type 'ServerResponse'
 
 702 -- | A continuation for 'server''s users to respond.
 
 704 -- This newtype has two uses :
 
 706 --   * Carrying the 'ts' type variable to 'server'.
 
 707 --   * Providing a 'return' for the simple response case
 
 708 --     of 'HTTP.status200' and no extra headers.
 
 709 newtype ServerRes (ts::[Type]) m a
 
 711  {    unServerResponse :: m a
 
 712  } deriving (Functor, Applicative, Monad)
 
 713 type ServerResponse ts m = ServerRes ts
 
 714  (R.ReaderT Wai.Request
 
 715  (W.WriterT HTTP.ResponseHeaders
 
 716  (W.WriterT HTTP.Status
 
 717  (C.ContT Wai.Response m))))
 
 718 instance MonadTrans (ServerRes ts) where
 
 719         lift = ServerResponse
 
 720 -- | All supported effects are handled by nested 'Monad's.
 
 721 type instance MC.CanDo (ServerResponse ts m) eff = 'False
 
 722 type instance MC.CanDo (C.ContT ts m) (MC.EffExec eff) = 'False
 
 724 instance HTTP_Response Server where
 
 725         type ResponseConstraint Server a ts = MimeTypes ts (MimeEncodable a)
 
 726         type ResponseArgs Server a ts = ServerResponse ts IO a
 
 727         type Response Server =
 
 729          (Wai.Response -> IO Wai.ResponseReceived) ->
 
 730          IO Wai.ResponseReceived
 
 733          ResponseConstraint repr a ts =>
 
 736          repr (ResponseArgs repr a ts)
 
 738         response expMethod = Server $ do
 
 740                  { serverState_request = req
 
 743                 -- Check the path has been fully consumed
 
 744                 unless (List.null $ Wai.pathInfo req) $
 
 745                         MC.throw $ Fail st [ServerErrorPath "path is longer"]
 
 748                 let reqMethod = Wai.requestMethod $ serverState_request st
 
 749                 unless (reqMethod == expMethod
 
 750                  || reqMethod == HTTP.methodHead
 
 751                  && expMethod == HTTP.methodGet) $
 
 752                         MC.throw $ Fail st [ServerErrorMethod]
 
 754                 -- Check the Accept header
 
 755                 let reqHeaders = Wai.requestHeaders $ serverState_request st
 
 756                 MimeType reqAccept <- do
 
 757                         case List.lookup HTTP.hAccept reqHeaders of
 
 759                                 return $ NonEmpty.head $ mimeTypes @ts @(MimeEncodable a)
 
 761                                 case matchAccept @ts @(MimeEncodable a) h of
 
 762                                  Nothing -> MC.throw $ Fail st [ServerErrorAccept (mediaTypes @ts @(MimeEncodable a)) (Just (Left h))]
 
 765                 return $ \(ServerResponse k) rq re -> re =<< do
 
 766                         C.runContT (W.runWriterT $ W.runWriterT $ R.runReaderT k rq) $ \((a,hs),sta) ->
 
 769                                          ((HTTP.hContentType, Media.renderHeader $ mediaTypeFor reqAccept):hs)
 
 770                                          (if reqMethod == HTTP.methodHead
 
 772                                                 else mimeEncode reqAccept a)
 
 774 -- * Type 'ServerResponseStream'
 
 776 -- This newtype has three uses :
 
 778 --   * Carrying the 'framing' type variable to 'server'.
 
 779 --   * Carrying the 'ts' type variable to 'server'.
 
 780 --   * Providing a 'return' for the simple response case
 
 781 --     of 'HTTP.status200' and no extra headers.
 
 782 newtype ServerResStream framing (ts::[Type]) m as
 
 783  =      ServerResponseStream
 
 784  {    unServerResponseStream :: m as
 
 785  } deriving (Functor, Applicative, Monad)
 
 786 instance MonadTrans (ServerResStream framing ts) where
 
 787         lift = ServerResponseStream
 
 788 type ServerResponseStream framing ts m = ServerResStream framing ts
 
 789  (R.ReaderT Wai.Request
 
 790  (W.WriterT HTTP.ResponseHeaders
 
 791  (W.WriterT HTTP.Status
 
 792  (C.ContT Wai.Response m))))
 
 793 -- | All supported effects are handled by nested 'Monad's.
 
 794 type instance MC.CanDo (ServerResponseStream framing ts m) eff = 'False
 
 796 instance HTTP_ResponseStream Server where
 
 797         type ResponseStreamConstraint Server as ts framing =
 
 798          ( FramingEncode framing as
 
 799          , MimeTypes ts (MimeEncodable (FramingYield as))
 
 801         type ResponseStreamArgs Server as ts framing =
 
 802          ServerResponseStream framing ts IO as
 
 803         type ResponseStream Server =
 
 807          (Wai.Response -> IO Wai.ResponseReceived) ->
 
 808          IO Wai.ResponseReceived
 
 811          forall as ts framing repr.
 
 812          ResponseStreamConstraint repr as ts framing =>
 
 815          repr (ResponseStreamArgs repr as ts framing)
 
 816               (ResponseStream repr)
 
 817         responseStream expMethod = Server $ do
 
 819                  { serverState_request = req
 
 822                 -- Check the path has been fully consumed
 
 823                 unless (List.null $ Wai.pathInfo req) $
 
 824                         MC.throw $ Fail st [ServerErrorPath "path is longer"]
 
 827                 let reqMethod = Wai.requestMethod $ serverState_request st
 
 828                 unless (reqMethod == expMethod
 
 829                  || reqMethod == HTTP.methodHead
 
 830                  && expMethod == HTTP.methodGet) $
 
 831                         MC.throw $ Fail st [ServerErrorMethod]
 
 833                 -- Check the Accept header
 
 834                 let reqHeaders = Wai.requestHeaders $ serverState_request st
 
 835                 MimeType reqAccept <- do
 
 836                         case List.lookup HTTP.hAccept reqHeaders of
 
 838                                 return $ NonEmpty.head $ mimeTypes @ts @(MimeEncodable (FramingYield as))
 
 840                                 case matchAccept @ts @(MimeEncodable (FramingYield as)) h of
 
 841                                  Nothing -> MC.throw $ Fail st [ServerErrorAccept (mediaTypes @ts @(MimeEncodable (FramingYield as))) (Just (Left h))]
 
 844                 return $ \(ServerResponseStream k) rq re -> re =<< do
 
 845                         C.runContT (W.runWriterT $ W.runWriterT $ R.runReaderT k rq) $ \((as,hs),sta) ->
 
 847                                         Wai.responseStream sta
 
 848                                          ( (HTTP.hContentType, Media.renderHeader $ mediaTypeFor reqAccept)
 
 851                                                 if reqMethod == HTTP.methodHead
 
 854                                                         let enc = framingEncode (Proxy @framing) (mimeEncode reqAccept) in
 
 858                                                                  Right (bsl, next) -> do
 
 859                                                                         unless (BSL.null bsl) $ do
 
 860                                                                                 write (BSB.lazyByteString bsl)
 
 865 -- | Return worse 'HTTP.Status'.
 
 866 instance Semigroup HTTP.Status where
 
 868                 if rank (HTTP.statusCode x) < rank (HTTP.statusCode y)
 
 873                 rank 404 = 0 -- Not Found
 
 874                 rank 405 = 1 -- Method Not Allowed
 
 875                 rank 401 = 2 -- Unauthorized
 
 876                 rank 415 = 3 -- Unsupported Media Type
 
 877                 rank 406 = 4 -- Not Acceptable
 
 878                 rank 400 = 5 -- Bad Request
 
 880 -- | Useful when 'HTTP.Status' is within a 'W.WriterT'.
 
 881 instance Monoid HTTP.Status where
 
 882         mempty  = HTTP.status200
 
 886 -- | 'Trans'form a 'Server' to merge 'Alt'ernative 'segment's into a 'routing'.
 
 887 data Router repr a b where
 
 888  -- | Lift any @(repr)@ into 'Router', those not useful to segregate
 
 889  -- wrt. the 'Trans'formation performed, aka. 'noTrans'.
 
 890  Router_Any :: repr a b -> Router repr a b
 
 891  -- | Represent 'segment'.
 
 892  Router_Seg :: PathSegment -> Router repr k k
 
 893  -- | Represent ('<.>').
 
 894  Router_Cat :: Router repr a b -> Router repr b c -> Router repr a c
 
 895  -- | Represent 'routing'.
 
 896  Router_Map :: Map.Map PathSegment (Router repr a k) -> Router repr a k
 
 897  -- | Represent ('<!>').
 
 898  Router_Alt :: Router repr a k -> Router repr b k -> Router repr (a:!:b) k
 
 899  -- | Represent 'capture''.
 
 900  Router_Cap :: PathConstraint Server a => Name -> Router repr (a->k) k
 
 901  -- | Represent 'captures'.
 
 902  Router_Caps :: Captures (Router repr) cs k -> Router repr (AltFromBinTree cs) k
 
 903  -- | Unify 'Router's which have different 'handlers'.
 
 904  -- Useful to put alternative 'Router's in a 'Map.Map' as in 'Router_Map'.
 
 905  Router_Union :: (b->a) -> Router repr a k -> Router repr b k
 
 907 -- ** Type 'Captures'
 
 908 data Captures repr (cs::BinTree Type) k where
 
 909  Captures0 :: PathConstraint Server a =>
 
 910               Proxy a -> Name -> repr x k ->
 
 911               Captures repr ('BinTree0 (a->x)) k
 
 912  Captures2 :: Captures repr x k ->
 
 914               Captures repr ('BinTree2 x y) k
 
 916 -- *** Type 'BinTree'
 
 917 -- | Use @DataKinds@ to define a 'BinTree' of 'Type's.
 
 918 -- Useful for gathering together 'capture's of different 'Type's.
 
 921  |   BinTree2 (BinTree a) (BinTree a)
 
 923 -- *** Type family 'AltFromBinTree'
 
 924 type family AltFromBinTree (cs::BinTree Type) :: Type where
 
 925  AltFromBinTree ('BinTree0 x)   = x
 
 926  AltFromBinTree ('BinTree2 x y) = AltFromBinTree x :!: AltFromBinTree y
 
 928 instance Trans (Router Server) where
 
 929         type UnTrans (Router Server) = Server
 
 931         unTrans (Router_Any x)   = x
 
 932         unTrans (Router_Seg s)   = segment s
 
 933         unTrans (Router_Cat x y) = unTrans x <.> unTrans y
 
 934         unTrans (Router_Alt x y) = unTrans x <!> unTrans y
 
 935         unTrans (Router_Map ms)  = routing (unTrans <$> ms)
 
 936         unTrans (Router_Cap n)   = capture' n
 
 937         unTrans (Router_Caps xs) = captures $ unTransCaptures xs
 
 939                 unTransCaptures :: Captures (Router Server) cs k -> Captures Server cs k
 
 940                 unTransCaptures (Captures0 a n r) = Captures0 a n (unTrans r)
 
 941                 unTransCaptures (Captures2 x y)   = unTransCaptures x `Captures2` unTransCaptures y
 
 942         unTrans (Router_Union u x) = Server $ (. u) <$> unServer (unTrans x)
 
 944 instance Cat (Router Server) where
 
 946 instance Alt (Router Server) where
 
 948 instance repr ~ Server => HTTP_Path (Router repr) where
 
 949         type PathConstraint (Router repr) a = PathConstraint repr a
 
 951         capture' = Router_Cap
 
 952 instance HTTP_Routing (Router Server) where
 
 954         captures = Router_Caps
 
 955 instance HTTP_Raw (Router Server)
 
 956 instance Pro (Router Server)
 
 957 instance HTTP_Query (Router Server)
 
 958 instance HTTP_Header (Router Server)
 
 959 instance HTTP_Body (Router Server)
 
 960 instance HTTP_BodyStream (Router Server)
 
 961 instance HTTP_BasicAuth (Router Server)
 
 962 instance HTTP_Response (Router Server)
 
 963 instance HTTP_ResponseStream (Router Server)
 
 965 -- ** Class 'HTTP_Routing'
 
 966 class HTTP_Routing repr where
 
 967         routing  :: Map.Map PathSegment (repr a k) -> repr a k
 
 968         captures :: Captures repr cs k -> repr (AltFromBinTree cs) k
 
 972          HTTP_Routing (UnTrans repr) =>
 
 973          Map.Map PathSegment (repr a k) -> repr a k
 
 974         routing = noTrans . routing . (unTrans <$>)
 
 977          HTTP_Routing (UnTrans repr) =>
 
 978          Captures repr cs k -> repr (AltFromBinTree cs) k
 
 979         captures = noTrans . captures . unTransCaptures
 
 981                 unTransCaptures :: Captures repr cs k -> Captures (UnTrans repr) cs k
 
 982                 unTransCaptures (Captures0 a n r) = Captures0 a n (unTrans r)
 
 983                 unTransCaptures (Captures2 x y)   = Captures2 (unTransCaptures x) (unTransCaptures y)
 
 985 instance HTTP_Routing Server where
 
 986         routing ms = Server $ do
 
 988                  { serverState_request = req
 
 990                 case Wai.pathInfo req of
 
 991                  []   -> MC.throw $ Fail st [ServerErrorPath "empty path segment"]
 
 992                  [""] -> MC.throw $ Fail st [ServerErrorPath "trailing slash"]
 
 994                         case Map.lookup curr ms of
 
 995                          Nothing -> MC.throw $ Fail st [ServerErrorPath $ "expected: "<>Text.pack (show (Map.keys ms))<>" but got: "<>curr]
 
 998                                  { serverState_request = req{ Wai.pathInfo = next }
 
1002         captures :: Captures Server cs k -> Server (AltFromBinTree cs) k
 
1003         captures cs = Server $ do
 
1005                  { serverState_request = req
 
1007                 case Wai.pathInfo req of
 
1008                  []   -> MC.throw $ Fail st [ServerErrorPath "empty"]
 
1009                  [""] -> MC.throw $ Fail st [ServerErrorPath "trailing slash"]
 
1012                          Left errs -> MC.throw $ Fail st
 
1013                                  [ServerErrorPath $ "captures: "<>
 
1014                                         fromString (List.intercalate "|" ((\(name,err) -> name) <$> errs))]
 
1015                          Right a -> unServer a
 
1017                         go :: forall cs k. Captures Server cs k -> Either [(Name,Text)] (Server (AltFromBinTree cs) k)
 
1018                         go (Captures0 (Proxy::Proxy a) name currRepr) =
 
1019                                 case Web.parseUrlPiece currSeg of
 
1020                                  Left err -> Left [(name,err)]
 
1023                                                 S.put st { serverState_request = req{ Wai.pathInfo = nextSeg } }
 
1024                                                 (\x2k a2x -> x2k (a2x a)) <$> unServer currRepr
 
1025                         go (Captures2 x y) =
 
1029                                          Left ye -> Left (xe<>ye)
 
1030                                          Right a -> Right $ Server $ (\r2k (_l:!:r) -> r2k r) <$> unServer a
 
1031                                  Right a   -> Right $ Server $ (\l2k (l:!:_r) -> l2k l) <$> unServer a
 
1033 -- | Traverse a 'Router' to transform it:
 
1035 --   * Associate 'Router_Cat' to the right.
 
1036 --   * Replace 'Router_Seg' with 'Router_Map'.
 
1037 --   * Replace 'Router_Cap' with 'Router_Caps'.
 
1039 -- Used in 'server' on the 'Router' inferred from the given API.
 
1040 router :: Router repr a b -> Router repr a b
 
1041 router = {-debug1 "router" $-} \case
 
1044  Router_Seg x `Router_Cat` y -> Router_Map $ Map.singleton x $ router y
 
1045  Router_Alt x y -> router x`router_Alt` router y
 
1046  Router_Map xs -> Router_Map $ router <$> xs
 
1047  Router_Cap xn `Router_Cat` x -> Router_Caps $ Captures0 Proxy xn x
 
1048  Router_Cap n -> Router_Cap n
 
1049  Router_Caps cs -> Router_Caps (go cs)
 
1051         go :: Captures (Router repr) cs k -> Captures (Router repr) cs k
 
1052         go (Captures0 a n r) = Captures0 a n (router r)
 
1053         go (Captures2 x y)   = Captures2 (go x) (go y)
 
1057                 -- Associate to the right
 
1058                 Router_Cat (router x) $
 
1059                 Router_Cat (router y) (router z)
 
1060          _ -> router xy `Router_Cat` router z
 
1061  Router_Union u x -> Router_Union u (router x)
 
1063 -- | Merge/reorder alternatives if possible or default to a 'Router_Alt'.
 
1067  Router repr (a:!:b) k
 
1068 router_Alt = {-debug2 "router_Alt"-} go
 
1070         -- Merge alternative segments together.
 
1071         go (Router_Seg x `Router_Cat` xt) (Router_Seg y `Router_Cat` yt) =
 
1072                 Map.singleton x (router xt)
 
1074                 Map.singleton y (router yt)
 
1075         go (Router_Seg x `Router_Cat` xt) (Router_Map ys) =
 
1076                 Map.singleton x (router xt)
 
1078         go (Router_Map xs) (Router_Seg y `Router_Cat` yt) =
 
1080                 Map.singleton y (router yt)
 
1081         go (Router_Map xs) (Router_Map ys) =
 
1084         -- Merge alternative 'capture''s together.
 
1085         go (Router_Cap xn `Router_Cat` x) (Router_Cap yn `Router_Cat` y) =
 
1087                         Captures0 Proxy xn x
 
1089                         Captures0 Proxy yn y
 
1090         go (Router_Caps xs) (Router_Caps ys) =
 
1091                 Router_Caps $ xs`Captures2`ys
 
1092         go (Router_Cap xn `Router_Cat` x) (Router_Caps ys) =
 
1093                 Router_Caps $ Captures0 Proxy xn x `Captures2` ys
 
1094         go (Router_Caps xs) (Router_Cap yn `Router_Cat` y) =
 
1095                 Router_Caps $ xs `Captures2` Captures0 Proxy yn y
 
1097         -- Merge left first or right first, depending on which removes 'Router_Alt'.
 
1098         go x (y`Router_Alt`z) =
 
1099                 case x`router_Alt`y of
 
1101                         case y'`router_Alt`z of
 
1102                          yz@(Router_Alt _y z') ->
 
1103                                 case x'`router_Alt`z' of
 
1104                                  Router_Alt{} -> router x'`Router_Alt`yz
 
1105                                  xz -> Router_Union (\(a:!:(b:!:c)) -> (a:!:c):!:b) $ xz`router_Alt`y
 
1106                                         -- NOTE: prioritize the merged router 'xz' over over the non-mergeable 'y'.
 
1107                          yz -> x'`router_Alt`yz
 
1108                  xy -> Router_Union (\(a:!:(b:!:c)) -> (a:!:b):!:c) $ xy`router_Alt`z
 
1109         go (x`Router_Alt`y) z =
 
1110                 case y`router_Alt`z of
 
1112                         case x`router_Alt`y' of
 
1113                          xy@(Router_Alt x' _y) ->
 
1114                                 case x'`router_Alt`z' of
 
1115                                  Router_Alt{} -> xy`Router_Alt`router z'
 
1116                                  xz -> Router_Union (\((a:!:b):!:c) -> (a:!:c):!:b) $ xz`router_Alt`y
 
1117                                         -- NOTE: prioritize the merged router 'xz' over the non-mergeable 'y'.
 
1118                          xy -> xy`router_Alt`z'
 
1119                  yz -> Router_Union (\((a:!:b):!:c) -> a:!:(b:!:c)) $ x`router_Alt`yz
 
1121         -- Merge through 'Router_Union'.
 
1122         go (Router_Union u x) y = Router_Union (\(a:!:b) -> u a:!:b) (x`router_Alt`y)
 
1123         go x (Router_Union u y) = Router_Union (\(a:!:b) -> a:!:u b) (x`router_Alt`y)
 
1126         go x y = x`Router_Alt`y
 
1129  Map.Map PathSegment (Router repr a k) ->
 
1130  Map.Map PathSegment (Router repr b k) ->
 
1131  Router repr (a:!:b) k
 
1133         -- NOTE: a little bit more complex than required
 
1134         -- in order to merge 'Router_Union's instead of nesting them,
 
1135         -- such that 'unTrans' 'Router_Union' applies them all at once.
 
1138          (Map.mapMissing $ const $ \case
 
1139                  Router_Union u r -> Router_Union (\(x:!:_y) -> u x) r
 
1140                  r -> Router_Union (\(x:!:_y) -> x) r)
 
1141          (Map.mapMissing $ const $ \case
 
1142                  Router_Union u r -> Router_Union (\(_x:!:y) -> u y) r
 
1143                  r -> Router_Union (\(_x:!:y) -> y) r)
 
1144          (Map.zipWithMatched $ const $ \case
 
1145                  Router_Union xu xr -> \case
 
1146                          Router_Union yu yr -> Router_Union (\(x:!:y) -> xu x:!:yu y) $ xr`router_Alt`yr
 
1147                          yr -> Router_Union (\(a:!:b) -> xu a:!:b) $ xr`router_Alt`yr
 
1149                          Router_Union yu yr -> Router_Union (\(a:!:b) -> a:!:yu b) $ xr`router_Alt`yr
 
1150                          yr -> xr`router_Alt`yr)
 
1154 debug0 :: Show a => String -> a -> a
 
1155 debug0 n a = Debug.trace (" {"<>n<>": "<>show a) a
 
1156 debug1 :: Show a => Show b => String -> (a->b) -> (a->b)
 
1157 debug1 n a2b a = Debug.trace ("} "<>n<>": r: "<>show b) b
 
1158         where b = a2b $ Debug.trace ("{ "<>n<>": a: "<>show a) a
 
1159 debug2 :: Show a => Show b => Show c => String -> (a->b->c) -> (a->b->c)
 
1160 debug2 n a2b2c a b = Debug.trace ("} "<>n<>": r: "<>show c) c
 
1162         b2c = a2b2c $ Debug.trace ("{ "<>n<>": a: "<>show a) a
 
1163         c   = b2c   $ Debug.trace (n<>": b: "<>show b) b