-- ------------------------------------------------------ -- -- Copyright © 2014 AlephCloud Systems, Inc. -- ------------------------------------------------------ -- {-# LANGUAGE CPP #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE OverloadedLists #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE UnicodeSyntax #-} {-# OPTIONS_HADDOCK show-extensions #-} module Configuration.Utils.Http ( -- * HTTP Service TLS Configuration HttpServiceTLSConfiguration , hstcCertFile , hstcKeyFile , defaultHttpServiceTLSConfiguration , pHttpServiceTLSConfiguration , validateHttpServiceTLSConfiguration -- * HTTP Service Configuration , HttpServiceConfiguration , hscHost , hscPort , hscUseTLS , defaultHttpServiceConfiguration , pHttpServiceConfiguration , validateHttpServiceConfiguration -- * Http Client Configuration , HttpClientConfiguration , hccHost , hccPort , hccUseTLS , defaultHttpClientConfiguration , pHttpClientConfiguration , validateHttpClientConfiguration , httpService2clientConfiguration ) where import Configuration.Utils import Configuration.Utils.Internal import Configuration.Utils.Validation import Control.Monad (when) import Control.Monad.Writer.Class (tell) import qualified Data.ByteString.Char8 as B8 import qualified Data.DList as DL import Data.Maybe (isJust) import Data.Monoid.Unicode #if MIN_VERSION_base(4,13,0) import Prelude.Unicode hiding ((×)) #else import Prelude.Unicode #endif -- -------------------------------------------------------------------------- -- -- Http Service TLS Configuration -- | In order to make TLS optional this type should be used -- wrapped into a Maybe. -- data HttpServiceTLSConfiguration = HttpServiceTLSConfiguration { _hstcCertFile ∷ !FilePath , _hstcKeyFile ∷ !FilePath } deriving (Show, Read, Eq, Ord) hstcCertFile ∷ Lens' HttpServiceTLSConfiguration FilePath hstcCertFile = lens _hstcCertFile $ \s a → s { _hstcCertFile = a} hstcKeyFile ∷ Lens' HttpServiceTLSConfiguration FilePath hstcKeyFile = lens _hstcKeyFile $ \s a → s { _hstcKeyFile = a} defaultHttpServiceTLSConfiguration ∷ HttpServiceTLSConfiguration defaultHttpServiceTLSConfiguration = HttpServiceTLSConfiguration { _hstcCertFile = "cert.pem" , _hstcKeyFile = "key.pem" } validateHttpServiceTLSConfiguration ∷ ConfigValidation HttpServiceTLSConfiguration f validateHttpServiceTLSConfiguration conf = do validateFileReadable "cert-file" $ _hstcCertFile conf validateFileReadable "key-file" $ _hstcKeyFile conf instance FromJSON (HttpServiceTLSConfiguration → HttpServiceTLSConfiguration) where parseJSON = withObject "HttpServiceTLSConfiguration" $ \o → id <$< hstcCertFile ..: "cert-file" % o <*< hstcKeyFile ..: "pem-file" % o -- | This is used as default when wrapped into Maybe and -- -- 1. the parsed value is not 'Null' and -- 2. the given default is not 'Nothing'. -- instance FromJSON HttpServiceTLSConfiguration where parseJSON v = parseJSON v <*> pure defaultHttpServiceTLSConfiguration instance ToJSON HttpServiceTLSConfiguration where toJSON HttpServiceTLSConfiguration{..} = object [ "cert-file" .= _hstcCertFile , "key-file" .= _hstcKeyFile ] -- | This option parser does not allow to enable or disable -- usage of TLS. The option will have effect only when TLS -- usage is configured in the configuration file or the default -- configuration. -- -- FIXME: print a warning and exit when one of these options is -- provided even though TLS is turned off. -- pHttpServiceTLSConfiguration ∷ String → MParser HttpServiceTLSConfiguration pHttpServiceTLSConfiguration prefix = id <$< hstcCertFile .:: strOption % long (prefix ⊕ "cert-file") ⊕ help "File with PEM encoded TLS Certificate" <*< hstcKeyFile .:: strOption % long (prefix ⊕ "key-file") ⊕ help "File with PEM encoded TLS key" -- -------------------------------------------------------------------------- -- -- Http Service Configuration -- | We restrict services to use either HTTP or HTTPS but not both. -- -- TLS can be turned off explicitely in the configuration file by -- setting the respective section to @null@. It can not be -- turned on or off via command line options. But once it is turned -- on the values for the certificate and key file can be changed -- by command line options. -- data HttpServiceConfiguration = HttpServiceConfiguration { _hscHost ∷ !B8.ByteString , _hscPort ∷ !Int , _hscInterface ∷ !B8.ByteString , _hscUseTLS ∷ !(Maybe HttpServiceTLSConfiguration) } deriving (Show, Read, Eq, Ord) hscHost ∷ Lens' HttpServiceConfiguration B8.ByteString hscHost = lens _hscHost $ \s a → s { _hscHost = a} hscPort ∷ Lens' HttpServiceConfiguration Int hscPort = lens _hscPort $ \s a → s { _hscPort = a} hscInterface ∷ Lens' HttpServiceConfiguration B8.ByteString hscInterface = lens _hscInterface $ \s a → s { _hscInterface = a} hscUseTLS ∷ Lens' HttpServiceConfiguration (Maybe HttpServiceTLSConfiguration) hscUseTLS = lens _hscUseTLS $ \s a → s { _hscUseTLS = a} defaultHttpServiceConfiguration ∷ HttpServiceConfiguration defaultHttpServiceConfiguration = HttpServiceConfiguration { _hscHost = "localhost" , _hscPort = 80 , _hscInterface = "0.0.0.0" , _hscUseTLS = Nothing } validateHttpServiceConfiguration ∷ ConfigValidation HttpServiceConfiguration DL.DList validateHttpServiceConfiguration conf = do maybe (return ()) validateHttpServiceTLSConfiguration $ _hscUseTLS conf validatePort "port" $ _hscPort conf when (_hscPort conf < 1024) $ tell ["listening on a priviledged port requires super user rights"] validateNonEmpty "host" $ _hscHost conf validateIPv4 "interface" . B8.unpack $ _hscInterface conf instance FromJSON (HttpServiceConfiguration → HttpServiceConfiguration) where parseJSON = withObject "HttpServiceConfiguration" $ \o → id <$< hscHost ∘ bs ..: "host" % o <*< hscPort ..: "port" % o <*< hscInterface ∘ bs ..: "interface" % o <*< hscUseTLS %.: "use-tls" % o where bs ∷ Iso' B8.ByteString String bs = iso B8.unpack B8.pack instance ToJSON HttpServiceConfiguration where toJSON HttpServiceConfiguration{..} = object [ "host" .= B8.unpack _hscHost , "port" .= _hscPort , "interface" .= B8.unpack _hscInterface , "use-tls" .= _hscUseTLS ] pHttpServiceConfiguration ∷ String → MParser HttpServiceConfiguration pHttpServiceConfiguration prefix = id <$< hscHost ∘ bs .:: strOption % long (prefix ⊕ "host") ⊕ help "Hostname of the service" <*< hscPort .:: option auto % long (prefix ⊕ "port") ⊕ help "Port of the service" <*< hscInterface ∘ bs .:: option auto % long (prefix ⊕ "interface") ⊕ help "Interface of the service" <*< (hscUseTLS %:: (fmap <$> pHttpServiceTLSConfiguration prefix)) where bs ∷ Iso' B8.ByteString String bs = iso B8.unpack B8.pack -- -------------------------------------------------------------------------- -- -- Http Client Configuration data HttpClientConfiguration = HttpClientConfiguration { _hccHost ∷ !B8.ByteString , _hccPort ∷ !Int , _hccUseTLS ∷ !Bool } deriving (Show, Read, Eq, Ord) hccHost ∷ Lens' HttpClientConfiguration B8.ByteString hccHost = lens _hccHost $ \s a → s { _hccHost = a} hccPort ∷ Lens' HttpClientConfiguration Int hccPort = lens _hccPort $ \s a → s { _hccPort = a} hccUseTLS ∷ Lens' HttpClientConfiguration Bool hccUseTLS = lens _hccUseTLS $ \s a → s { _hccUseTLS = a} defaultHttpClientConfiguration ∷ HttpClientConfiguration defaultHttpClientConfiguration = HttpClientConfiguration { _hccHost = "localhost" , _hccPort = 80 , _hccUseTLS = False } validateHttpClientConfiguration ∷ ConfigValidation HttpClientConfiguration f validateHttpClientConfiguration conf = do validatePort "port" $ _hccPort conf validateNonEmpty "host" $ _hccHost conf instance FromJSON (HttpClientConfiguration → HttpClientConfiguration) where parseJSON = withObject "HttpClientConfiguration" $ \o → id <$< hccHost ∘ bs ..: "host" % o <*< hccPort ..: "port" % o <*< hccUseTLS ..: "use-tls" % o where bs ∷ Iso' B8.ByteString String bs = iso B8.unpack B8.pack instance ToJSON HttpClientConfiguration where toJSON HttpClientConfiguration{..} = object [ "host" .= B8.unpack _hccHost , "port" .= _hccPort , "use-tls" .= _hccUseTLS ] pHttpClientConfiguration ∷ String → MParser HttpClientConfiguration pHttpClientConfiguration serviceName = id <$< hccHost ∘ bs .:: strOption % long (serviceName ⊕ "-host") ⊕ help ("Hostname of " ⊕ serviceName) <*< hccPort .:: option auto % long (serviceName ⊕ "-port") ⊕ help ("Port of " ⊕ serviceName) <*< hccUseTLS .:: switch % long (serviceName ⊕ "-use-tls") ⊕ help ("Connect to " ⊕ serviceName ⊕ " via TLS") where bs ∷ Iso' B8.ByteString String bs = iso B8.unpack B8.pack httpService2clientConfiguration ∷ HttpServiceConfiguration → HttpClientConfiguration httpService2clientConfiguration HttpServiceConfiguration{..} = HttpClientConfiguration { _hccHost = _hscHost , _hccPort = _hscPort , _hccUseTLS = isJust _hscUseTLS }