1 {-# LANGUAGE GADTs #-} -- for 'Router'
2 {-# LANGUAGE ConstraintKinds #-}
3 {-# LANGUAGE DataKinds #-} -- for 'Tree'
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)
33 import Prelude (undefined) -- for factorizing captures
35 import Text.Show (Show(..))
36 import qualified Control.Monad.Classes as MC
37 import qualified Control.Monad.Trans.Cont as C
38 import qualified Control.Monad.Trans.Reader as R
39 import qualified Control.Monad.Trans.State.Strict as S
40 import qualified Control.Monad.Trans.Writer.Strict as W
41 import qualified Data.ByteString as BS
42 import qualified Data.ByteString.Base64 as BS64
43 import qualified Data.ByteString.Builder as BSB
44 import qualified Data.ByteString.Lazy as BSL
45 import qualified Data.List as List
46 import qualified Data.List.NonEmpty as NonEmpty
47 import qualified Data.Map.Merge.Strict as Map
48 import qualified Data.Map.Strict as Map
49 import qualified Data.Text as Text
50 import qualified Data.Text.Encoding as Text
51 import qualified Data.Word8 as Word8
52 import qualified Network.HTTP.Media as Media
53 import qualified Network.HTTP.Types as HTTP
54 import qualified Network.HTTP.Types.Header as HTTP
55 import qualified Network.Wai as Wai
56 import qualified Web.HttpApiData as Web
61 -- | (@'Server' handlers k@) is a recipe to produce an 'Wai.Application'
62 -- from given ('handlers') (one per number of alternative routes),
63 -- separated by (':!:').
65 -- 'Server' is analogous to a scanf using the API as a format customized for HTTP routing.
67 -- The multiple 'ServerCheckT' monad transformers are there
68 -- to prioritize the errors according to the type of check raising them,
69 -- instead of the order of the combinators within an actual API specification.
70 newtype Server handlers k = Server { unServer ::
72 (ServerCheckT [ServerErrorBody] -- 8th check, 400 error
73 (ServerCheckT [ServerErrorHeader] -- 7th check, 400 error
74 (ServerCheckT [ServerErrorQuery] -- 6th check, 400 error
75 (ServerCheckT [ServerErrorContentType] -- 5th check, 415 error
76 (ServerCheckT [ServerErrorAccept] -- 4th check, 406 error
77 (ServerCheckT [ServerErrorBasicAuth] -- 3rd check, 401 or 403 error
78 (ServerCheckT [ServerErrorMethod] -- 2nd check, 405 error
79 (ServerCheckT [ServerErrorPath] -- 1st check, 404 error
84 -- | (@'server' api handlers@) returns an 'Wai.Application'
85 -- ready to be given to @Warp.run 80@.
87 Router Server handlers (Response Server) ->
90 server api handlers rq re = do
91 lrPath <- runServerChecks (unServer $ unTrans $ router api) $ ServerState rq
93 Left err -> respondError HTTP.status404 [] err
96 Left err -> respondError HTTP.status405 [] err
100 case failError err of
101 [] -> respondError HTTP.status500 [] err
102 ServerErrorBasicAuth realm ba:_ ->
104 BasicAuth_Unauthorized ->
105 respondError HTTP.status403 [] err
107 respondError HTTP.status401
108 [ ( HTTP.hWWWAuthenticate
109 , "Basic realm=\""<>Web.toHeader realm<>"\""
113 Left err -> respondError HTTP.status406 [] err
114 Right lrContentType ->
115 case lrContentType of
116 Left err -> respondError HTTP.status415 [] err
119 Left err -> respondError HTTP.status400 [] err
122 Left err -> respondError HTTP.status400 [] err
125 Left err -> respondError HTTP.status400 [] err
132 [(HTTP.HeaderName, HeaderValue)] ->
133 err -> IO Wai.ResponseReceived
134 respondError st hs err =
135 -- Trace.trace (show err) $
136 re $ Wai.responseLBS st
137 ( (HTTP.hContentType, Media.renderHeader $ mediaType @PlainText)
139 ) (fromString $ show err) -- TODO: see what to return in the body
141 -- | Unwrap the 'ExceptT' constructors to reach the 'Either' constructors.
143 S.StateT ServerState (ExceptT e1 (ExceptT e2 (ExceptT e3 (ExceptT e4 (ExceptT e5 (ExceptT e6 (ExceptT e7 (ExceptT e8 IO)))))))) a ->
144 ServerState -> IO (Either e8 (Either e7 (Either e6 (Either e5 (Either e4 (Either e3 (Either e2 (Either e1 (a, ServerState)))))))))
145 runServerChecks s st =
156 -- ** Type 'ServerCheckT'
157 type ServerCheckT e = ExceptT (Fail e)
159 -- *** Type 'RouteResult'
160 type RouteResult e = Either (Fail e)
164 = Fail ServerState e -- ^ Keep trying other paths. 404, 405 or 406.
165 | FailFatal !ServerState !e -- ^ Don't try other paths.
167 failState :: Fail e -> ServerState
168 failState (Fail st _) = st
169 failState (FailFatal st _) = st
170 failError :: Fail e -> e
171 failError (Fail _st e) = e
172 failError (FailFatal _st e) = e
173 instance Semigroup e => Semigroup (Fail e) where
174 Fail _ x <> Fail st y = Fail st (x<>y)
175 FailFatal _ x <> Fail st y = FailFatal st (x<>y)
176 Fail _ x <> FailFatal st y = FailFatal st (x<>y)
177 FailFatal _ x <> FailFatal st y = FailFatal st (x<>y)
179 -- ** Type 'ServerState'
180 newtype ServerState = ServerState
181 { serverState_request :: Wai.Request
183 instance Show ServerState where
184 show _ = "ServerState"
186 instance Cat Server where
190 repr a b -> repr b c -> repr a c
191 -- NOTE: if x fails, run y to see if it fails on a more prioritized check.
192 -- And if so, fail with y instead of x.
194 -- This long spaghetti code may probably be avoided
195 -- with a more sophisticated 'Server' using a binary tree
196 -- instead of nested 'Either's, so that its 'Monad' instance
197 -- would do the right thing. But to my mind,
198 -- with the very few priorities of checks currently needed,
199 -- this is not worth the cognitive pain to design it.
200 -- Some copying/pasting/adapting will do for now.
201 Server x <.> Server y = Server $
203 xPath <- MC.exec @IO $ runServerChecks x st
205 Left xe -> MC.throw xe
209 yPath <- MC.exec @IO $ runServerChecks y (failState xe)
211 Left ye -> MC.throw ye
212 Right _yMethod -> MC.throw xe
216 yPath <- MC.exec @IO $ runServerChecks y (failState xe)
218 Left ye -> MC.throw ye
221 Left ye -> MC.throw ye
222 Right _yBasicAuth -> MC.throw xe
226 yPath <- MC.exec @IO $ runServerChecks y (failState xe)
228 Left ye -> MC.throw ye
231 Left ye -> MC.throw ye
234 Left ye -> MC.throw ye
235 Right _yAccept -> MC.throw xe
236 Right xContentType ->
239 yPath <- MC.exec @IO $ runServerChecks y (failState xe)
241 Left ye -> MC.throw ye
244 Left ye -> MC.throw ye
247 Left ye -> MC.throw ye
250 Left ye -> MC.throw ye
251 Right _yQuery -> MC.throw xe
255 yPath <- MC.exec @IO $ runServerChecks y (failState xe)
257 Left ye -> MC.throw ye
260 Left ye -> MC.throw ye
263 Left ye -> MC.throw ye
266 Left ye -> MC.throw ye
269 Left ye -> MC.throw ye
270 Right _yHeader -> MC.throw xe
274 yPath <- MC.exec @IO $ runServerChecks y (failState xe)
276 Left ye -> MC.throw ye
279 Left ye -> MC.throw ye
282 Left ye -> MC.throw ye
285 Left ye -> MC.throw ye
288 Left ye -> MC.throw ye
291 Left ye -> MC.throw ye
292 Right _yBody -> MC.throw xe
296 yPath <- MC.exec @IO $ runServerChecks y (failState xe)
298 Left ye -> MC.throw ye
301 Left ye -> MC.throw ye
304 Left ye -> MC.throw ye
307 Left ye -> MC.throw ye
310 Left ye -> MC.throw ye
313 Left ye -> MC.throw ye
314 Right _yBody -> MC.throw xe
316 first (. a2b) <$> S.runStateT y st'
317 instance Alt Server where
318 -- (<!>) :: repr a k -> repr b k -> repr (a:!:b) k
319 Server x <!> Server y = Server $
321 xPath <- MC.exec @IO $ runServerChecks x st
322 let fy = (first (\b2k (_a:!:b) -> b2k b) <$>)
324 Left xe | FailFatal{} <- xe -> MC.throw xe
326 yPath <- MC.exec @IO $ runServerChecks y st
328 Left ye -> MC.throw (xe<>ye)
330 fy $ ExceptT $ ExceptT $ ExceptT $ ExceptT $ ExceptT $ ExceptT $ ExceptT $ ExceptT $
331 return $ Right yMethod
334 Left xe | FailFatal{} <- xe -> MC.throw xe
336 yPath <- MC.exec @IO $ runServerChecks y st
338 Left _ye -> MC.throw xe
341 Left ye -> MC.throw (xe<>ye)
343 fy $ ExceptT $ ExceptT $ ExceptT $ ExceptT $ ExceptT $ ExceptT $ ExceptT $
344 return $ Right $ yBasicAuth
347 Left xe | FailFatal{} <- xe -> MC.throw xe
349 yPath <- MC.exec @IO $ runServerChecks y st
351 Left _ye -> MC.throw xe
354 Left _ye -> MC.throw xe
357 Left ye -> MC.throw (xe<>ye)
359 fy $ ExceptT $ ExceptT $ ExceptT $ ExceptT $ ExceptT $ ExceptT $
360 return $ Right yAccept
363 Left xe | FailFatal{} <- xe -> MC.throw xe
365 yPath <- MC.exec @IO $ runServerChecks y st
367 Left _ye -> MC.throw xe
370 Left _ye -> MC.throw xe
373 Left _ye -> MC.throw xe
376 Left ye -> MC.throw (xe<>ye)
377 Right yContentType ->
378 fy $ ExceptT $ ExceptT $ ExceptT $ ExceptT $ ExceptT $
379 return $ Right yContentType
380 Right xContentType ->
382 Left xe | FailFatal{} <- xe -> MC.throw xe
384 yPath <- MC.exec @IO $ runServerChecks y st
386 Left _ye -> MC.throw xe
389 Left _ye -> MC.throw xe
392 Left _ye -> MC.throw xe
395 Left _ye -> MC.throw xe
396 Right yContentType ->
398 Left ye -> MC.throw (xe<>ye)
400 fy $ ExceptT $ ExceptT $ ExceptT $ ExceptT $
401 return $ Right yQuery
404 Left xe | FailFatal{} <- xe -> MC.throw xe
406 yPath <- MC.exec @IO $ runServerChecks y st
408 Left _ye -> MC.throw xe
411 Left _ye -> MC.throw xe
414 Left _ye -> MC.throw xe
417 Left _ye -> MC.throw xe
418 Right yContentType ->
420 Left _ye -> MC.throw xe
423 Left ye -> MC.throw (xe<>ye)
425 fy $ ExceptT $ ExceptT $ ExceptT $
426 return $ Right yHeader
429 Left xe | FailFatal{} <- xe -> MC.throw xe
431 yPath <- MC.exec @IO $ runServerChecks y st
433 Left _ye -> MC.throw xe
436 Left _ye -> MC.throw xe
439 Left _ye -> MC.throw xe
442 Left _ye -> MC.throw xe
443 Right yContentType ->
445 Left _ye -> MC.throw xe
448 Left _ye -> MC.throw xe
451 Left ye -> MC.throw (xe<>ye)
453 fy $ ExceptT $ ExceptT $
457 Left xe | FailFatal{} <- xe -> MC.throw xe
459 yPath <- MC.exec @IO $ runServerChecks y st
461 Left _ye -> MC.throw xe
464 Left _ye -> MC.throw xe
467 Left _ye -> MC.throw xe
470 Left _ye -> MC.throw xe
471 Right yContentType ->
473 Left _ye -> MC.throw xe
476 Left _ye -> MC.throw xe
479 Left _ye -> MC.throw xe
482 Left ye -> MC.throw (xe<>ye)
487 return $ first (\a2k (a:!:_b) -> a2k a) xr
488 instance Pro Server where
489 dimap a2b _b2a (Server r) = Server $ (\k b2k -> k (b2k . a2b)) <$> r
491 -- ** Type 'ServerErrorPath'
492 newtype ServerErrorPath = ServerErrorPath Text
495 instance HTTP_Path Server where
496 type PathConstraint Server a = Web.FromHttpApiData a
497 segment expSegment = Server $ do
499 { serverState_request = req
501 case Wai.pathInfo req of
502 [] -> MC.throw $ Fail st [ServerErrorPath "empty path segment"]
503 [""] -> MC.throw $ Fail st [ServerErrorPath "trailing slash"]
505 | curr /= expSegment ->
506 MC.throw $ Fail st [ServerErrorPath $ "expected: "<>expSegment<>" but got: "<>curr]
509 { serverState_request = req{ Wai.pathInfo = next }
512 capture' :: forall a k. PathConstraint Server a => Name -> Server (a -> k) k
513 capture' name = Server $ do
515 { serverState_request = req
517 case Wai.pathInfo req of
518 [] -> MC.throw $ Fail st [ServerErrorPath "empty"]
519 [""] -> MC.throw $ Fail st [ServerErrorPath "trailing slash"]
521 case Web.parseUrlPiece curr of
522 Left err -> MC.throw $ Fail st [ServerErrorPath $ "capture: "<>fromString name<>": "<>err]
525 { serverState_request = req{ Wai.pathInfo = next }
528 captureAll = Server $ do
529 req <- S.gets serverState_request
530 return ($ Wai.pathInfo req)
532 -- ** Type 'ServerErrorMethod'
533 data ServerErrorMethod = ServerErrorMethod
536 -- | TODO: add its own error?
537 instance HTTP_Version Server where
538 version exp = Server $ do
540 let got = Wai.httpVersion $ serverState_request st
543 else MC.throw $ Fail st [ServerErrorMethod] -- FIXME: ServerErrorVersion
545 -- ** Type 'ServerErrorAccept'
546 data ServerErrorAccept =
549 (Maybe (Either BS.ByteString MediaType))
552 -- ** Type 'ServerErrorContentType'
553 data ServerErrorContentType = ServerErrorContentType
556 -- ** Type 'ServerErrorQuery'
557 newtype ServerErrorQuery = ServerErrorQuery Text
559 instance HTTP_Query Server where
560 type QueryConstraint Server a = Web.FromHttpApiData a
561 queryParams' name = Server $ do
563 lift $ ExceptT $ ExceptT $ ExceptT $ return $
564 let qs = Wai.queryString $ serverState_request st in
565 let vals = catMaybes $ (<$> qs) $ \(n,v) ->
567 then Web.parseQueryParam . Text.decodeUtf8 <$> v
569 case sequence vals of
570 Left err -> Left $ Fail st [ServerErrorQuery err]
571 Right vs -> Right $ Right $ Right ($ vs)
573 -- ** Type 'ServerErrorHeader'
574 data ServerErrorHeader = ServerErrorHeader
576 instance HTTP_Header Server where
577 header n = Server $ do
579 lift $ ExceptT $ ExceptT $ return $
580 let hs = Wai.requestHeaders $ serverState_request st in
581 case List.lookup n hs of
582 Nothing -> Left $ Fail st [ServerErrorHeader]
583 Just v -> Right $ Right ($ v)
585 -- ** Type 'ServerErrorBasicAuth'
586 data ServerErrorBasicAuth =
587 ServerErrorBasicAuth BasicAuthRealm (BasicAuth ())
590 -- ** Class 'ServerBasicAuth'
591 -- | Custom 'BasicAuth' check.
592 class ServerBasicAuth a where
598 -- | WARNING: current implementation of Basic Access Authentication
599 -- is not immune to certain kinds of timing attacks.
600 -- Decoding payloads does not take a fixed amount of time.
601 instance HTTP_BasicAuth Server where
602 type BasicAuthConstraint Server a = ServerBasicAuth a
603 type BasicAuthArgs Server a k = a -> k
604 basicAuth' realm = Server $ do
606 let err e = MC.throw $ Fail st [ServerErrorBasicAuth realm e]
607 case decodeAuthorization $ serverState_request st of
608 Nothing -> err BasicAuth_BadPassword
609 Just (user, pass) -> do
610 MC.exec @IO (serverBasicAuth user pass) >>= \case
611 BasicAuth_BadPassword -> err BasicAuth_BadPassword
612 BasicAuth_NoSuchUser -> err BasicAuth_NoSuchUser
613 BasicAuth_Unauthorized -> err BasicAuth_Unauthorized
614 BasicAuth_Authorized u -> return ($ u)
616 -- | Find and decode an 'Authorization' header from the request as a Basic Auth
617 decodeAuthorization :: Wai.Request -> Maybe (BasicAuthUser, BasicAuthPass)
618 decodeAuthorization req = do
619 hAuthorization <- List.lookup "Authorization" $ Wai.requestHeaders req
620 let (basic, rest) = BS.break Word8.isSpace hAuthorization
621 guard (BS.map Word8.toLower basic == "basic")
622 let decoded = BS64.decodeLenient (BS.dropWhile Word8.isSpace rest)
623 let (user, colon_pass) = BS.break (== Word8._colon) decoded
624 (_, pass) <- BS.uncons colon_pass
625 return (Text.decodeUtf8 user, Text.decodeUtf8 pass)
627 -- ** Type 'ServerErrorBody'
628 newtype ServerErrorBody = ServerErrorBody String
631 -- *** Type 'ServerBodyArg'
632 newtype ServerBodyArg (ts::[Type]) a = ServerBodyArg a
634 instance HTTP_Body Server where
635 type BodyConstraint Server a ts = MimeTypes ts (MimeDecodable a)
636 type BodyArg Server a ts = ServerBodyArg ts a
639 BodyConstraint repr a ts =>
641 repr (BodyArg repr a ts -> k) k
644 lift $ ExceptT $ ExceptT $ ExceptT $ ExceptT $ do
645 let hs = Wai.requestHeaders $ serverState_request st
647 -- DOC: http://www.w3.org/Protocols/rfc2616/rfc2616-sec7.html#sec7.2.1
648 -- DOC: http://www.w3.org/2001/tag/2002/0129-mime
649 fromMaybe "application/octet-stream" $
650 List.lookup HTTP.hContentType hs
651 case matchContent @ts @(MimeDecodable a) reqContentType of
652 Nothing -> return $ Left $ Fail st [ServerErrorContentType]
653 Just (MimeType mt) -> do
654 bodyBS <- MC.exec @IO $ Wai.requestBody $ serverState_request st
655 return $ Right $ Right $ Right $
656 -- NOTE: delay 'mimeDecode' after all checks
657 case mimeDecode mt $ BSL.fromStrict bodyBS of
658 Left err -> Left $ Fail st [ServerErrorBody err]
659 Right a -> Right ($ ServerBodyArg a)
661 -- *** Type 'ServerBodyStreamArg'
662 newtype ServerBodyStreamArg as (ts::[Type]) framing
663 = ServerBodyStreamArg as
664 instance HTTP_BodyStream Server where
665 type BodyStreamConstraint Server as ts framing =
666 ( FramingDecode framing as
667 , MC.MonadExec IO (FramingMonad as)
668 , MimeTypes ts (MimeDecodable (FramingYield as))
670 type BodyStreamArg Server as ts framing =
671 ServerBodyStreamArg as ts framing
673 forall as ts framing k repr.
674 BodyStreamConstraint repr as ts framing =>
676 repr (BodyStreamArg repr as ts framing -> k) k
677 bodyStream'= Server $ do
679 lift $ ExceptT $ ExceptT $ ExceptT $ ExceptT $ do
680 let hs = Wai.requestHeaders $ serverState_request st
682 -- DOC: http://www.w3.org/Protocols/rfc2616/rfc2616-sec7.html#sec7.2.1
683 -- DOC: http://www.w3.org/2001/tag/2002/0129-mime
684 fromMaybe "application/octet-stream" $
685 List.lookup HTTP.hContentType hs
686 case matchContent @ts @(MimeDecodable (FramingYield as)) reqContentType of
687 Nothing -> return $ Left $ Fail st [ServerErrorContentType]
688 Just (MimeType mt) -> do
689 let bodyBS = Wai.requestBody $ serverState_request st
690 return $ Right $ Right $ Right $
691 Right ($ ServerBodyStreamArg $
692 framingDecode (Proxy @framing) (mimeDecode mt) $
696 -- * Type 'ServerResponse'
697 -- | A continuation for 'server''s users to respond.
699 -- This newtype has two uses :
701 -- * Carrying the 'ts' type variable to 'server'.
702 -- * Providing a 'return' for the simple response case
703 -- of 'HTTP.status200' and no extra headers.
704 newtype ServerRes (ts::[Type]) m a
706 { unServerResponse :: m a
707 } deriving (Functor, Applicative, Monad)
708 type ServerResponse ts m = ServerRes ts
709 (R.ReaderT Wai.Request
710 (W.WriterT HTTP.ResponseHeaders
711 (W.WriterT HTTP.Status
712 (C.ContT Wai.Response m))))
713 instance MonadTrans (ServerRes ts) where
714 lift = ServerResponse
715 -- | All supported effects are handled by nested 'Monad's.
716 type instance MC.CanDo (ServerResponse ts m) eff = 'False
717 type instance MC.CanDo (C.ContT ts m) (MC.EffExec eff) = 'False
719 instance HTTP_Response Server where
720 type ResponseConstraint Server a ts = MimeTypes ts (MimeEncodable a)
721 type ResponseArgs Server a ts = ServerResponse ts IO a
722 type Response Server =
724 (Wai.Response -> IO Wai.ResponseReceived) ->
725 IO Wai.ResponseReceived
728 ResponseConstraint repr a ts =>
731 repr (ResponseArgs repr a ts)
733 response expMethod = Server $ do
735 { serverState_request = req
738 -- Check the path has been fully consumed
739 unless (List.null $ Wai.pathInfo req) $
740 MC.throw $ Fail st [ServerErrorPath "path is longer"]
743 let reqMethod = Wai.requestMethod $ serverState_request st
744 unless (reqMethod == expMethod
745 || reqMethod == HTTP.methodHead
746 && expMethod == HTTP.methodGet) $
747 MC.throw $ Fail st [ServerErrorMethod]
749 -- Check the Accept header
750 let reqHeaders = Wai.requestHeaders $ serverState_request st
751 MimeType reqAccept <- do
752 case List.lookup HTTP.hAccept reqHeaders of
754 return $ NonEmpty.head $ mimeTypes @ts @(MimeEncodable a)
756 case matchAccept @ts @(MimeEncodable a) h of
757 Nothing -> MC.throw $ Fail st [ServerErrorAccept (mediaTypes @ts @(MimeEncodable a)) (Just (Left h))]
760 return $ \(ServerResponse k) rq re -> re =<< do
761 C.runContT (W.runWriterT $ W.runWriterT $ R.runReaderT k rq) $ \((a,hs),sta) ->
764 ((HTTP.hContentType, Media.renderHeader $ mediaTypeFor reqAccept):hs)
765 (if reqMethod == HTTP.methodHead
767 else mimeEncode reqAccept a)
769 -- * Type 'ServerResponseStream'
771 -- This newtype has three uses :
773 -- * Carrying the 'framing' type variable to 'server'.
774 -- * Carrying the 'ts' type variable to 'server'.
775 -- * Providing a 'return' for the simple response case
776 -- of 'HTTP.status200' and no extra headers.
777 newtype ServerResStream framing (ts::[Type]) m as
778 = ServerResponseStream
779 { unServerResponseStream :: m as
780 } deriving (Functor, Applicative, Monad)
781 instance MonadTrans (ServerResStream framing ts) where
782 lift = ServerResponseStream
783 type ServerResponseStream framing ts m = ServerResStream framing ts
784 (R.ReaderT Wai.Request
785 (W.WriterT HTTP.ResponseHeaders
786 (W.WriterT HTTP.Status
787 (C.ContT Wai.Response m))))
788 -- | All supported effects are handled by nested 'Monad's.
789 type instance MC.CanDo (ServerResponseStream framing ts m) eff = 'False
791 instance HTTP_ResponseStream Server where
792 type ResponseStreamConstraint Server as ts framing =
793 ( FramingEncode framing as
794 , MimeTypes ts (MimeEncodable (FramingYield as))
796 type ResponseStreamArgs Server as ts framing =
797 ServerResponseStream framing ts IO as
798 type ResponseStream Server =
800 (Wai.Response -> IO Wai.ResponseReceived) ->
801 IO Wai.ResponseReceived
803 forall as ts framing repr.
804 ResponseStreamConstraint repr as ts framing =>
807 repr (ResponseStreamArgs repr as ts framing)
808 (ResponseStream repr)
809 responseStream expMethod = Server $ do
811 { serverState_request = req
814 -- Check the path has been fully consumed
815 unless (List.null $ Wai.pathInfo req) $
816 MC.throw $ Fail st [ServerErrorPath "path is longer"]
819 let reqMethod = Wai.requestMethod $ serverState_request st
820 unless (reqMethod == expMethod
821 || reqMethod == HTTP.methodHead
822 && expMethod == HTTP.methodGet) $
823 MC.throw $ Fail st [ServerErrorMethod]
825 -- Check the Accept header
826 let reqHeaders = Wai.requestHeaders $ serverState_request st
827 MimeType reqAccept <- do
828 case List.lookup HTTP.hAccept reqHeaders of
830 return $ NonEmpty.head $ mimeTypes @ts @(MimeEncodable (FramingYield as))
832 case matchAccept @ts @(MimeEncodable (FramingYield as)) h of
833 Nothing -> MC.throw $ Fail st [ServerErrorAccept (mediaTypes @ts @(MimeEncodable (FramingYield as))) (Just (Left h))]
836 return $ \(ServerResponseStream k) rq re -> re =<< do
837 C.runContT (W.runWriterT $ W.runWriterT $ R.runReaderT k rq) $ \((as,hs),sta) ->
839 Wai.responseStream sta
840 ( (HTTP.hContentType, Media.renderHeader $ mediaTypeFor reqAccept)
843 if reqMethod == HTTP.methodHead
846 let enc = framingEncode (Proxy @framing) (mimeEncode reqAccept) in
850 Right (bsl, next) -> do
851 unless (BSL.null bsl) $ do
852 write (BSB.lazyByteString bsl)
857 -- | Return worse 'HTTP.Status'.
858 instance Semigroup HTTP.Status where
860 if rank (HTTP.statusCode x) < rank (HTTP.statusCode y)
865 rank 404 = 0 -- Not Found
866 rank 405 = 1 -- Method Not Allowed
867 rank 401 = 2 -- Unauthorized
868 rank 415 = 3 -- Unsupported Media Type
869 rank 406 = 4 -- Not Acceptable
870 rank 400 = 5 -- Bad Request
872 -- | Useful when 'HTTP.Status' is within a 'W.WriterT'.
873 instance Monoid HTTP.Status where
874 mempty = HTTP.status200
879 -- | 'Trans'form a 'Server' to merge 'Alt'ernative 'segment's into a 'routing'.
880 data Router repr a b where
881 -- | Lift any @(repr)@ into 'Router', those not useful to segregate
882 -- wrt. the 'Trans'formation performed, aka. 'noTrans'.
883 Router_Any :: repr a b -> Router repr a b
884 -- | Represent 'segment'.
885 Router_Seg :: PathSegment -> Router repr k k
886 -- | Represent ('<.>').
887 Router_Cat :: Router repr a b -> Router repr b c -> Router repr a c
888 -- | Represent 'routing'.
889 Router_Map :: Map.Map PathSegment (Router repr a k) -> Router repr a k
890 Router_Map2 :: Map.Map PathSegment (Segments (Router repr) ms k) ->
891 Router repr (Caps2Alts ms) k
892 -- | Represent ('<!>').
893 Router_Alt :: Router repr a k -> Router repr b k -> Router repr (a:!:b) k
894 -- | Used to transform 'Router_Alt' into 'Router_Map',
895 -- while following the way ('<!>') combinators are associated in the API.
896 -- Use 'router_AltL' to insert it correctly.
897 Router_AltL :: Router repr a k -> Router repr (a:!:b) k
898 -- | Used to transform 'Router_Alt' into 'Router_Map'
899 -- while following the way ('<!>') combinators are associated in the API.
900 -- Use 'router_AltR' to insert it correctly.
901 Router_AltR :: Router repr b k -> Router repr (a:!:b) k
902 -- Router_AltB :: Router repr (a:!:a) k -> Router repr a k
903 -- Router_AltB :: Router repr (a:!:a) k -> Router repr a k
904 Router_Cap :: PathConstraint Server a => Name -> Router repr (a->k) k
906 Router_Caps :: Captures xs ->
907 Router repr (Caps2Alts xs) (Caps2AltsCoDomain xs)
910 -- | Use @DataKinds@ to define a 'Tree' of 'Type's.
911 -- Useful for factorizing 'capture's of different 'Type's.
914 | Tree2 (Tree a) (Tree a)
916 -- ** Type 'Segments'
917 data Segments repr (cs::Tree Type) k where
918 Segments0 :: repr a k -> Segments repr ('Tree0 a) k
919 SegmentsL :: Segments repr a k -> Segments repr ('Tree2 a b) k
920 SegmentsR :: Segments repr b k -> Segments repr ('Tree2 a b) k
922 SegmentsB :: repr (a:!:b) k -> Segments repr ('Tree2 (Tree0 a) (Tree0 b)) k
923 Segments2 :: Segments repr x k ->
925 Segments repr ('Tree2 x y) k
927 SegmentsLL :: Segments (Router repr) ('Tree2 a1 b) k ->
928 Segments (Router repr) ('Tree2 ('Tree2 a1 b1) b) k
931 data Segs repr (cs::Type) k where
932 Segs0 :: repr a k -> Segs repr (a) k
933 SegsL :: Segs repr a k -> Segs repr (a:!:b) k
934 SegsR :: Segs repr b k -> Segs repr (a:!:b) k
936 SegsB :: repr (a:!:b) k -> Segs repr (a:!:b) k
937 Segs2 :: Segs repr x k -> Segs repr y k -> Segs repr (x:!:y) k
941 Segs (Router repr) a k ->
942 Segs (Router repr) b k ->
943 Segs (Router repr) (a:!:b) k
944 mergeSegs (Segs0 x) (Segs0 y) = Segs0 (x`router_Alt`y)
945 -- mergeSegs (SegsL x) y = _e $ mergeSegs x y
946 -- mergeSegs (SegsL x) y = SegsLL $ mergeSegs x y
949 -- ** Type 'Captures'
950 data Captures (cs::Tree Type) where
951 Captures0 :: PathConstraint Server a =>
952 Proxy (a->k) -> Name ->
954 Captures ('Tree0 (a->k))
955 Captures2 :: Captures x ->
957 Captures ('Tree2 x y)
959 -- ** Type 'Captured'
960 data Captured (cs::Tree Type) where
961 Captured0 :: a -> Captured ('Tree0 a)
962 Captured2 :: Captured x -> Captured y -> Captured ('Tree2 x y)
964 -- ** Type family 'Caps2Alts'
965 type family Caps2Alts (cs::Tree Type) :: Type where
966 Caps2Alts ('Tree0 x) = x
967 Caps2Alts ('Tree2 x y) = Caps2Alts x :!: Caps2Alts y
969 -- ** Type family 'Caps2AltsCoDomain'
970 type family Caps2AltsCoDomain (cs::Tree Type) :: Type where
971 Caps2AltsCoDomain ('Tree0 a2k) = CoDomain a2k
972 Caps2AltsCoDomain ('Tree2 x y) = Caps2AltsCoDomain x :!: Caps2AltsCoDomain y
974 -- ** Type family 'Domain'
975 type family Domain x :: * where
978 -- ** Type family 'CoDomain'
979 type family CoDomain x :: * where
982 -- ** Class 'Alts2Caps'
983 class Alts2Caps cs where
984 alts2caps :: Caps2Alts cs -> Captured cs
985 instance Alts2Caps ('Tree0 a) where
986 alts2caps a = Captured0 a
987 instance (Alts2Caps x, Alts2Caps y) => Alts2Caps ('Tree2 x y) where
988 alts2caps (a:!:b) = Captured2 (alts2caps a) (alts2caps b)
990 instance Trans (Router Server) where
991 type UnTrans (Router Server) = Server
993 unTrans (Router_Any x) = x
994 unTrans (Router_Seg s) = segment s
995 unTrans (Router_Cat x y) = unTrans x <.> unTrans y
996 unTrans (Router_Alt x y) = unTrans x <!> unTrans y
997 unTrans (Router_AltL x) = Server $ (\a2k (a:!:_b) -> a2k a) <$> unServer (unTrans x)
998 unTrans (Router_AltR x) = Server $ (\b2k (_a:!:b) -> b2k b) <$> unServer (unTrans x)
999 unTrans (Router_Map ms) = routing (unTrans <$> ms)
1000 unTrans (Router_Cap n) = capture' n
1001 unTrans (Router_Caps xs) = captures xs
1002 -- Server $ _e <$> unServer (captures xs)
1004 unTrans (Router_Caps xs) =
1005 Server $ (\c2k -> c2k . alts2caps) <$> unServer
1006 (captures (unTransCaptures xs))
1008 unTransCaptures :: Captures (Router Server) cs k -> Captures Server cs k
1009 unTransCaptures (Captures0 a n r) = Captures0 a n (unTrans r)
1010 unTransCaptures (Captures2 x y) = unTransCaptures x `Captures2` unTransCaptures y
1013 instance Cat (Router Server) where
1015 instance Alt (Router Server) where
1017 instance repr ~ Server => HTTP_Path (Router repr) where
1018 type PathConstraint (Router repr) a = PathConstraint repr a
1019 segment = Router_Seg
1020 capture' = Router_Cap
1021 instance HTTP_Routing (Router Server) where
1022 routing = Router_Map
1023 captures = Router_Caps -- . unTransCaptures
1026 unTransCaptures :: Captures (Router Server) cs k -> Captures Server cs k
1027 unTransCaptures (Captures0 a n r) = Captures0 a n (unTrans r)
1028 unTransCaptures (Captures2 x y) = unTransCaptures x `Captures2` unTransCaptures y
1030 instance Pro (Router Server)
1031 instance HTTP_Query (Router Server)
1032 instance HTTP_Header (Router Server)
1033 instance HTTP_Body (Router Server)
1034 instance HTTP_BodyStream (Router Server)
1035 instance HTTP_BasicAuth (Router Server)
1036 instance HTTP_Response (Router Server)
1037 instance HTTP_ResponseStream (Router Server)
1039 -- ** Class 'HTTP_Routing'
1040 class HTTP_Routing repr where
1041 routing :: Map.Map PathSegment (repr a k) -> repr a k
1042 captures :: Captures cs -> repr (Caps2Alts cs) (Caps2AltsCoDomain cs)
1046 HTTP_Routing (UnTrans repr) =>
1047 Map.Map PathSegment (repr a k) -> repr a k
1048 routing = noTrans . routing . (unTrans <$>)
1049 {- NOTE: cannot define this default simply,
1050 - due to the need for: forall a. PathConstraint (Router repr) a ~ PathConstraint repr a
1051 - so let's just define it in (HTTP_Routing (Router Server))
1054 HTTP_Routing (UnTrans repr) =>
1055 Captures cs -> repr (Captured cs) k
1056 captures = noTrans . captures . unTransCaptures
1058 unTransCaptures :: Captures cs -> Captures cs
1059 unTransCaptures (Captures0 a n r) = Captures0 a n (unTrans r)
1060 unTransCaptures (Captures2 x y) = Captures2 (unTransCaptures x) (unTransCaptures y)
1063 instance HTTP_Routing Server where
1064 routing ms = Server $ do
1066 { serverState_request = req
1068 case Wai.pathInfo req of
1069 [] -> MC.throw $ Fail st [ServerErrorPath "empty path segment"]
1070 [""] -> MC.throw $ Fail st [ServerErrorPath "trailing slash"]
1072 case Map.lookup curr ms of
1073 Nothing -> MC.throw $ Fail st [ServerErrorPath $ "expected: "<>Text.pack (show (Map.keys ms))<>" but got: "<>curr]
1076 { serverState_request = req{ Wai.pathInfo = next }
1080 captures :: Captures cs -> Server (Caps2Alts cs) (Caps2AltsCoDomain cs)
1081 captures cs = Server $ do
1083 { serverState_request = req
1085 case Wai.pathInfo req of
1086 [] -> MC.throw $ Fail st [ServerErrorPath "empty"]
1087 [""] -> MC.throw $ Fail st [ServerErrorPath "trailing slash"]
1090 Left errs -> MC.throw $ Fail st
1091 [ServerErrorPath $ "captures: "<>fromString (List.intercalate "|" ((\(name,err) -> name) <$> errs))]
1092 Right a -> unServer a
1096 Either [(Name,Text)]
1097 (Server (Caps2Alts cs) (Caps2AltsCoDomain cs))
1098 go (Captures0 (Proxy::Proxy (a->x)) name) =
1099 case Web.parseUrlPiece currSeg of
1100 Left err -> Left [(name,err)]
1101 Right (a::a) -> Right $ Server $ do
1102 S.put st { serverState_request = req{ Wai.pathInfo = nextSeg } }
1104 go (Captures2 x y) =
1108 Left ye -> Left (xe<>ye)
1109 Right a -> Right $ Server $ (\y2ky (_x:!:y') -> undefined:!:y2ky y') <$> unServer a
1110 Right a -> Right $ Server $ (\x2kx (x':!:_y) -> x2kx x':!:undefined) <$> unServer a
1112 -- | Traverse a 'Router' to transform 'Router_Alt'
1113 -- into 'Router_Map' when possible.
1114 -- Used in 'server' on the 'Router' inferred from the given API.
1115 -- router :: Router repr a b -> Router repr a b
1116 router :: repr ~ Server => Router repr a b -> Router repr a b
1117 router i = case {-Dbg.trace ("router: " <> show i)-} i of
1121 Router_Cat (Router_Seg s) (l`Router_Alt`r) ->
1122 (Router_Cat (Router_Seg s) l) `Router_Alt`
1123 (Router_Cat (Router_Seg s) r)
1125 Router_Cat x y -> router x `Router_Cat` router y
1126 Router_Alt x y -> router_Alt x y
1127 Router_AltL x -> Router_AltL (router x)
1128 Router_AltR x -> Router_AltR (router x)
1129 Router_Map xs -> Router_Map (router <$> xs)
1130 Router_Cap n -> Router_Cap n
1131 Router_Caps cs -> Router_Caps ({-go-} cs)
1134 go :: Captures (Router repr) cs k -> Captures (Router repr) cs k
1135 go (Captures0 a n r) = Captures0 a n (router r)
1136 go (Captures2 x y) = Captures2 (go x) (go y)
1139 router_Cat :: repr ~ Server => Router repr a b -> Router repr b c -> Router repr a c
1142 ({-Dbg.trace ("cat: x: " <> show x0)-} x0)
1143 ({-Dbg.trace ("cat: y: " <> show y0)-} y0) in
1144 {-Dbg.trace ("cat: r: " <> show r)-} r
1146 go x y = Router_Cat x y
1148 go (Router_Seg x `Router_Cat` xt)
1149 (Router_Seg y `Router_Cat` yt) =
1152 -- | Insert a 'Router_Alt' or a 'Router_Map' if possible.
1155 PathConstraint (Router repr) a ~ PathConstraint repr a =>
1156 PathConstraint (Router repr) b ~ PathConstraint repr b =>
1159 Router repr a k -> Router repr b k -> Router repr (a:!:b) k
1162 ({-Dbg.trace ("alt: x: " <> show x0)-} x0)
1163 ({-Dbg.trace ("alt: y: " <> show y0)-} y0) in
1164 {-Dbg.trace ("alt: r: " <> show r)-} r
1166 go :: forall a b k repr.
1168 Router repr a k -> Router repr b k -> Router repr (a:!:b) k
1170 go (Router_Seg x `Router_Cat` xt)
1171 (Router_Seg y `Router_Cat` yt)
1172 | x == y = Router_Seg y `Router_Cat` (xt `router_Alt` yt)
1174 Router_Map2 $ Map.fromListWith
1175 (\_xt _yt -> SegmentsB $ xt `router_Alt` yt)
1176 [ (x, SegmentsL (Segments0 xt))
1177 , (y, SegmentsR (Segments0 yt))
1179 go (Router_Seg x `Router_Cat` xt) (Router_Map2 ys) =
1181 let l = Map.singleton x (SegmentsL (Segments0 xt)) in
1182 let r = SegmentsR <$> ys in
1183 Router_Map2 $ l <> r
1187 (Map.traverseMissing $ const $ return . SegmentsL . Segments0)
1188 (Map.traverseMissing $ const $ return . SegmentsR)
1189 (Map.zipWithAMatched $ const $ \a b -> return $ Segments2 (Segments0 a) b)
1190 (Map.singleton x xt)
1194 go (Router_Map2 xs) (Router_Seg y `Router_Cat` yt) =
1197 (Map.traverseMissing $ const $ return . SegmentsL)
1198 (Map.traverseMissing $ const $ return . SegmentsR)
1199 (Map.zipWithAMatched $ const $ \a b -> return $ SegmentsB $ a`router_Alt`b)
1200 xs (Map.singleton y yt)
1201 go (Router_Map2 xs) (Router_Map2 ys) =
1204 (Map.traverseMissing $ const $ return . SegmentsL)
1205 (Map.traverseMissing $ const $ return . SegmentsR)
1206 (Map.zipWithAMatched $ const $ \a b -> return $ SegmentsB $ a`router_Alt`b)
1210 go (Router_Seg x `Router_Cat` xt)
1211 (Router_Seg y `Router_Cat` yt)
1212 | x == y = Router_Seg y `Router_Cat` (xt `router_Alt` yt)
1214 Router_Map $ Map.fromListWith
1215 (\_xt _yt -> xt `router_Alt` yt)
1216 [ (x, router_AltL xt)
1217 , (y, router_AltR yt)
1219 go (Router_Seg x `Router_Cat` xt) (Router_Map ys) =
1222 (Map.traverseMissing $ const $ return . router_AltL . router)
1223 (Map.traverseMissing $ const $ return . router_AltR . router)
1224 (Map.zipWithAMatched $ const $ \a b -> return $ a`router_Alt`b)
1225 (Map.singleton x xt) ys
1226 go (Router_Map xs) (Router_Seg y `Router_Cat` yt) =
1229 (Map.traverseMissing $ const $ return . router_AltL . router)
1230 (Map.traverseMissing $ const $ return . router_AltR . router)
1231 (Map.zipWithAMatched $ const $ \a b -> return $ a`router_Alt`b)
1232 xs (Map.singleton y yt)
1233 go (Router_Map xs) (Router_Map ys) =
1236 (Map.traverseMissing $ const $ return . router_AltL . router)
1237 (Map.traverseMissing $ const $ return . router_AltR . router)
1238 (Map.zipWithAMatched $ const $ \a b -> return $ a`router_Alt`b)
1241 go (Router_Cat (Router_Cat x y) z) w =
1242 router_Alt (Router_Cat x (Router_Cat y z)) w
1243 go w (Router_Cat (Router_Cat x y) z) =
1244 router_Alt w (Router_Cat x (Router_Cat y z))
1246 go (Router_Cap xn `Router_Cat` x)
1247 (Router_Cap yn `Router_Cat` y) =
1249 (Captures0 Proxy xn `Captures2` Captures0 Proxy yn)
1250 `Router_Cat` (x`router_Alt`y)
1251 go (Router_Caps xs `Router_Cat` x)
1252 (Router_Caps ys `Router_Cat` y) =
1253 Router_Caps (xs`Captures2`ys)
1254 `Router_Cat` router_Alt x y
1255 go (Router_Cap xn `Router_Cat` x)
1256 (Router_Caps ys `Router_Cat` y) =
1258 (Captures0 Proxy xn `Captures2` ys)
1259 `Router_Cat` router_Alt x y
1260 go (Router_Caps xs `Router_Cat` x)
1261 (Router_Cap yn `Router_Cat` y) =
1263 (xs `Captures2` Captures0 Proxy yn)
1264 `Router_Cat` router_Alt x y
1267 go x (Router_AltL y) =
1268 case router_Alt x y of
1269 Router_Alt x' y' -> Router_Alt x' (router_AltL y')
1270 _ -> Router_Alt x (Router_AltL y)
1271 go x (Router_AltR y) =
1272 case router_Alt x y of
1273 Router_Alt x' y' -> Router_Alt x' (router_AltR y')
1274 _ -> Router_Alt x (Router_AltR y)
1275 go (Router_AltL x) y =
1276 case router_Alt x y of
1277 Router_Alt x' y' -> Router_Alt (router_AltL x') y'
1278 _ -> Router_Alt (Router_AltL x) y
1279 go (Router_AltR x) y =
1280 case router_Alt x y of
1281 Router_Alt x' y' -> Router_Alt (router_AltR x') y'
1282 _ -> Router_Alt (Router_AltR x) y
1285 go x (Router_Alt y z) =
1286 case router_Alt y z of
1287 yz@Router_Alt{} -> Router_Alt x yz
1288 yz -> router_Alt x yz
1289 go (Router_Alt x y) z =
1290 case router_Alt x y of
1291 xy@Router_Alt{} -> Router_Alt xy z
1292 xy -> router_Alt xy z
1293 go x y = Router_Alt (router x) (router y)
1295 -- | Insert a 'Router_AltL' as deep as possible
1296 -- in order to not prevent the transformation
1297 -- of 'Router_Alt' into 'Router_Map' in 'router_Alt'.
1298 router_AltL :: repr ~ Server => Router repr a k -> Router repr (a:!:b) k
1300 Router_Cat (Router_Seg x) y -> Router_Cat (Router_Seg x) (router_AltL y)
1301 Router_Cat x y -> Router_Cat (router_AltL x) y
1302 Router_Map xs -> Router_Map (router_AltL <$> xs)
1303 -- Router_Alt x y -> Router_Alt x y
1305 Router_Caps (Captures0 a n r) ->
1306 let () = Router_Caps (Captures0 a n (router_AltL r)) in
1308 Router_Caps xs -> Router_Caps (mapCaptures xs)
1310 -- mapCaptures :: Captures (Router repr) a k -> Captures (Router repr) (a:!:b) k
1311 mapCaptures (Captures0 a n r) = Captures0 a n (router_AltL r)
1312 -- mapCaptures (Captures2 x y) = Captures2 (mapCaptures x) (mapCaptures y)
1315 -- Router_Any x -> Router_Any $ Server $ (\a2k (a:!:_b) -> a2k a) <$> unServer x
1318 -- | Like 'router_AltL' but for 'Router_AltR'.
1319 router_AltR :: repr ~ Server => Router repr b k -> Router repr (a:!:b) k
1321 Router_Cat (Router_Seg x) y -> Router_Cat (Router_Seg x) (router_AltR y)
1322 Router_Cat x y -> Router_Cat (router_AltR x) y
1323 -- Router_Alt x y -> router_AltR (Router_Alt x y)
1324 Router_Map xs -> Router_Map (router_AltR <$> xs)