hGetPeerCert ∷ h → IO (Maybe X509)
hGetPeerCert = const $ return Nothing
#endif
+ hIsSSL ∷ h → Bool
+ hIsSSL _ = False
hFlush ∷ h → IO ()
hClose ∷ h → IO ()
SSL.getPeerCertificate s
else
return Nothing
+ hIsSSL _ = True
hFlush _ = return () -- No need to do anything.
hClose s = SSL.shutdown s SSL.Bidirectional
, OverloadedStrings
, RecordWildCards
, UnicodeSyntax
+ , ViewPatterns
#-}
module Network.HTTP.Lucu.Preprocess
( AugmentedRequest(..)
)
where
import Control.Applicative
+import Control.Applicative.Unicode
import Control.Monad
import Control.Monad.State.Strict
import Data.Ascii (Ascii)
| Chunked
deriving (Eq, Show)
-preprocess ∷ CI Text → PortNumber → Request → AugmentedRequest
-preprocess localHost localPort req@(Request {..})
+preprocess ∷ CI Text → PortNumber → Bool → Request → AugmentedRequest
+preprocess localHost localPort isSSL req@(Request {..})
= execState go initialAR
where
initialAR ∷ AugmentedRequest
go ∷ State AugmentedRequest ()
go = do examineHttpVersion
examineMethod
+ examineScheme isSSL
examineAuthority localHost localPort
examineHeaders
examineBodyLength
DELETE → return ()
_ → setStatus NotImplemented
+examineScheme ∷ Bool → State AugmentedRequest ()
+examineScheme isSSL
+ = do req ← gets arRequest
+ when (null ∘ uriScheme $ reqURI req) $
+ let uri' = (reqURI req) {
+ uriScheme = if isSSL then
+ "https:"
+ else
+ "http:"
+ }
+ req' = req { reqURI = uri' }
+ in
+ setRequest req'
+
examineAuthority ∷ CI Text → PortNumber → State AugmentedRequest ()
examineAuthority localHost localPort
= do req ← gets arRequest
HttpVersion 1 0
→ let host = localHost
port = case localPort of
- 80 → ""
- n → A.unsafeFromString $ ':':show n
+ n | Just n ≡ defaultPort (reqURI req)
+ → ""
+ n → A.unsafeFromString $ ':':show n
req' = updateAuthority host port req
in
setRequest req'
-- Should never reach here...
ver → fail ("internal error: unknown version: " ⧺ show ver)
+defaultPort ∷ Alternative f ⇒ URI → f PortNumber
+{-# INLINEABLE defaultPort #-}
+defaultPort (uriScheme → s)
+ | s ≡ "http:" = pure 80
+ | s ≡ "https:" = pure 443
+ | otherwise = (∅)
+
parseHost ∷ Ascii → (CI Text, Ascii)
parseHost hp
= let (h, p) = C8.break (≡ ':') $ cs hp
→ Lazy.ByteString
→ IO ()
acceptParsableRequest ctx@(Context {..}) req input
- = do let ar = preprocess (cnfServerHost cConfig) cPort req
+ = do let ar = preprocess (cnfServerHost cConfig) cPort (hIsSSL cHandle) req
if isError $ arInitialStatus ar then
acceptSemanticallyInvalidRequest ctx ar input
else
{-# LANGUAGE
- GeneralizedNewtypeDeriving
+ Arrows
+ , GeneralizedNewtypeDeriving
+ , OverloadedStrings
+ , TypeOperators
, UnicodeSyntax
#-}
-- |FIXME: doc
module Network.HTTP.Lucu.Router
- ( Router
+ ( -- * The 'Router' arrow
+ Router
, runRouter
+
+ -- * Testing for URI scheme
+ , schemeWith
+ , scheme
+ , http
+ , http'
+ , https
)
where
import Control.Applicative
import Control.Arrow
import Control.Arrow.ArrowKleisli
import Control.Arrow.List
-import Data.Maybe
import Control.Monad.IO.Class
+import Data.Ascii (CIAscii)
+import Data.Maybe
+import Network.URI hiding (scheme)
+import Network.HTTP.Lucu.Utils
import Prelude.Unicode
-- |FIXME: doc
runRouter ∷ (Applicative m, MonadIO m) ⇒ Router m α β → α → m (Maybe β)
{-# INLINE runRouter #-}
runRouter = ((listToMaybe <$>) ∘) ∘ runListTArrow ∘ unRouter
+
+-- |FIXME: doc
+schemeWith ∷ (ArrowChoice (⇝), ArrowZero (⇝))
+ ⇒ (CIAscii → Bool)
+ → URI ⇝ (Host, Path)
+{-# INLINEABLE schemeWith #-}
+schemeWith f
+ = proc uri →
+ if f (uriCIScheme uri) then
+ arr uriHost &&& arr uriPathSegments ⤙ uri
+ else
+ zeroArrow ⤙ (⊥)
+
+-- |@'scheme' s@ = @'schemeWith' ('==' s)@
+scheme ∷ (ArrowChoice (⇝), ArrowZero (⇝)) ⇒ CIAscii → URI ⇝ (Host, Path)
+{-# INLINE scheme #-}
+scheme = schemeWith ∘ (≡)
+
+-- |@'http' = 'scheme' \"http\" '<+>' 'scheme' \"https\"@
+http ∷ (ArrowChoice (⇝), ArrowPlus (⇝), ArrowZero (⇝)) ⇒ URI ⇝ (Host, Path)
+{-# INLINE http #-}
+http = scheme "http" <+> scheme "https"
+
+-- |@'http'' = 'scheme' \"http\"
+http' ∷ (ArrowChoice (⇝), ArrowZero (⇝)) ⇒ URI ⇝ (Host, Path)
+http' = scheme "http"
+
+-- |@'https' = 'scheme' \"https\"
+https ∷ (ArrowChoice (⇝), ArrowZero (⇝)) ⇒ URI ⇝ (Host, Path)
+https = scheme "https"
#-}
-- |Utility functions used internally in this package.
module Network.HTTP.Lucu.Utils
- ( Host
+ ( Scheme
+ , Host
, PathSegment
, Path
, splitBy
, quoteStr
, parseWWWFormURLEncoded
+ , uriCIScheme
, uriHost
, uriPathSegments
, trim
where
import Control.Applicative hiding (empty)
import Control.Monad hiding (mapM)
-import Data.Ascii (Ascii, AsciiBuilder)
+import Data.Ascii (Ascii, AsciiBuilder, CIAscii)
import qualified Data.Ascii as A
import Data.ByteString (ByteString)
import qualified Data.ByteString.Char8 as BS
import Prelude.Unicode
import System.Directory
+-- |'Scheme' represents an URI scheme.
+type Scheme = CIAscii
+
-- |'Host' represents an IP address or a host name in an URI
-- authority.
type Host = CI Text
plusToSpace '+' = ' '
plusToSpace c = c
+-- |>>> uriCIScheme "http://example.com/foo/bar"
+-- "http"
+uriCIScheme ∷ URI → CIAscii
+{-# INLINE uriCIScheme #-}
+uriCIScheme = convertUnsafe ∘ fst ∘ fromJust ∘ back ∘ uriScheme
+
-- |>>> uriHost "http://example.com/foo/bar"
-- "example.com"
uriHost ∷ URI → Host