implement BaseUrl to allow https for Clients
This commit is contained in:
parent
11f82633da
commit
f0bca3852c
9 changed files with 46 additions and 41 deletions
|
@ -15,7 +15,6 @@ import Data.Monoid
|
||||||
import Data.Proxy
|
import Data.Proxy
|
||||||
import Data.Text
|
import Data.Text
|
||||||
import GHC.Generics
|
import GHC.Generics
|
||||||
import Network.URI
|
|
||||||
import Network.Wai
|
import Network.Wai
|
||||||
import Network.Wai.Handler.Warp
|
import Network.Wai.Handler.Warp
|
||||||
|
|
||||||
|
@ -75,9 +74,9 @@ server = hello :<|> greet :<|> delete
|
||||||
clientApi :: Client TestApi
|
clientApi :: Client TestApi
|
||||||
clientApi = client testApi
|
clientApi = client testApi
|
||||||
|
|
||||||
getGreet :: Text -> Maybe Bool -> URIAuth -> EitherT String IO Greet
|
getGreet :: Text -> Maybe Bool -> BaseUrl -> EitherT String IO Greet
|
||||||
postGreet :: Greet -> URIAuth -> EitherT String IO Greet
|
postGreet :: Greet -> BaseUrl -> EitherT String IO Greet
|
||||||
deleteGreet :: Text -> URIAuth -> EitherT String IO ()
|
deleteGreet :: Text -> BaseUrl -> EitherT String IO ()
|
||||||
getGreet :<|> postGreet :<|> deleteGreet = clientApi
|
getGreet :<|> postGreet :<|> deleteGreet = clientApi
|
||||||
|
|
||||||
-- Turn the server into a WAI app
|
-- Turn the server into a WAI app
|
||||||
|
@ -96,7 +95,7 @@ runTestServer port = run port test
|
||||||
main :: IO ()
|
main :: IO ()
|
||||||
main = do
|
main = do
|
||||||
tid <- forkIO $ runTestServer 8001
|
tid <- forkIO $ runTestServer 8001
|
||||||
let uri = mkHost "localhost" 8001
|
let uri = BaseUrl Http "localhost" 8001
|
||||||
print =<< runEitherT (getGreet "alp" (Just True) uri)
|
print =<< runEitherT (getGreet "alp" (Just True) uri)
|
||||||
print =<< runEitherT (getGreet "alp" (Just False) uri)
|
print =<< runEitherT (getGreet "alp" (Just False) uri)
|
||||||
let g = Greet "yo"
|
let g = Greet "yo"
|
||||||
|
|
|
@ -43,7 +43,7 @@ library
|
||||||
, split
|
, split
|
||||||
, http-client
|
, http-client
|
||||||
, http-types
|
, http-types
|
||||||
, network-uri
|
, network-uri >= 2.6
|
||||||
, wai
|
, wai
|
||||||
, warp
|
, warp
|
||||||
, safe
|
, safe
|
||||||
|
@ -70,7 +70,6 @@ executable greet
|
||||||
, wai
|
, wai
|
||||||
, either
|
, either
|
||||||
, text
|
, text
|
||||||
, network-uri
|
|
||||||
|
|
||||||
test-suite spec
|
test-suite spec
|
||||||
type: exitcode-stdio-1.0
|
type: exitcode-stdio-1.0
|
||||||
|
@ -90,7 +89,6 @@ test-suite spec
|
||||||
, http-client
|
, http-client
|
||||||
, http-types
|
, http-types
|
||||||
, network >= 2.6
|
, network >= 2.6
|
||||||
, network-uri >= 2.6
|
|
||||||
, QuickCheck
|
, QuickCheck
|
||||||
, servant
|
, servant
|
||||||
, string-conversions
|
, string-conversions
|
||||||
|
|
|
@ -9,7 +9,6 @@ import Data.Proxy
|
||||||
import Data.String.Conversions
|
import Data.String.Conversions
|
||||||
import Data.Typeable
|
import Data.Typeable
|
||||||
import Network.HTTP.Types
|
import Network.HTTP.Types
|
||||||
import Network.URI
|
|
||||||
import Network.Wai
|
import Network.Wai
|
||||||
import Servant.Client
|
import Servant.Client
|
||||||
import Servant.Docs
|
import Servant.Docs
|
||||||
|
@ -36,7 +35,7 @@ instance HasServer Delete where
|
||||||
| otherwise = respond $ failWith NotFound
|
| otherwise = respond $ failWith NotFound
|
||||||
|
|
||||||
instance HasClient Delete where
|
instance HasClient Delete where
|
||||||
type Client Delete = URIAuth -> EitherT String IO ()
|
type Client Delete = BaseUrl -> EitherT String IO ()
|
||||||
|
|
||||||
clientWithRoute Proxy req host =
|
clientWithRoute Proxy req host =
|
||||||
performRequest methodDelete req 204 host
|
performRequest methodDelete req 204 host
|
||||||
|
|
|
@ -10,7 +10,6 @@ import Data.Proxy
|
||||||
import Data.String.Conversions
|
import Data.String.Conversions
|
||||||
import Data.Typeable
|
import Data.Typeable
|
||||||
import Network.HTTP.Types
|
import Network.HTTP.Types
|
||||||
import Network.URI
|
|
||||||
import Network.Wai
|
import Network.Wai
|
||||||
import Servant.Client
|
import Servant.Client
|
||||||
import Servant.Docs
|
import Servant.Docs
|
||||||
|
@ -37,7 +36,7 @@ instance ToJSON result => HasServer (Get result) where
|
||||||
| otherwise = respond $ failWith NotFound
|
| otherwise = respond $ failWith NotFound
|
||||||
|
|
||||||
instance FromJSON result => HasClient (Get result) where
|
instance FromJSON result => HasClient (Get result) where
|
||||||
type Client (Get result) = URIAuth -> EitherT String IO result
|
type Client (Get result) = BaseUrl -> EitherT String IO result
|
||||||
clientWithRoute Proxy req host =
|
clientWithRoute Proxy req host =
|
||||||
performRequest methodGet req 200 host
|
performRequest methodGet req 200 host
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,6 @@ import Data.Proxy
|
||||||
import Data.String.Conversions
|
import Data.String.Conversions
|
||||||
import Data.Typeable
|
import Data.Typeable
|
||||||
import Network.HTTP.Types
|
import Network.HTTP.Types
|
||||||
import Network.URI
|
|
||||||
import Network.Wai
|
import Network.Wai
|
||||||
import Servant.Client
|
import Servant.Client
|
||||||
import Servant.Docs
|
import Servant.Docs
|
||||||
|
@ -39,7 +38,7 @@ instance ToJSON a => HasServer (Post a) where
|
||||||
| otherwise = respond $ failWith NotFound
|
| otherwise = respond $ failWith NotFound
|
||||||
|
|
||||||
instance FromJSON a => HasClient (Post a) where
|
instance FromJSON a => HasClient (Post a) where
|
||||||
type Client (Post a) = URIAuth -> EitherT String IO a
|
type Client (Post a) = BaseUrl -> EitherT String IO a
|
||||||
|
|
||||||
clientWithRoute Proxy req uri =
|
clientWithRoute Proxy req uri =
|
||||||
performRequest methodPost req 201 uri
|
performRequest methodPost req 201 uri
|
||||||
|
|
|
@ -10,7 +10,6 @@ import Data.Proxy
|
||||||
import Data.String.Conversions
|
import Data.String.Conversions
|
||||||
import Data.Typeable
|
import Data.Typeable
|
||||||
import Network.HTTP.Types
|
import Network.HTTP.Types
|
||||||
import Network.URI
|
|
||||||
import Network.Wai
|
import Network.Wai
|
||||||
import Servant.Client
|
import Servant.Client
|
||||||
import Servant.Docs
|
import Servant.Docs
|
||||||
|
@ -38,7 +37,7 @@ instance ToJSON a => HasServer (Put a) where
|
||||||
| otherwise = respond $ failWith NotFound
|
| otherwise = respond $ failWith NotFound
|
||||||
|
|
||||||
instance FromJSON a => HasClient (Put a) where
|
instance FromJSON a => HasClient (Put a) where
|
||||||
type Client (Put a) = URIAuth -> EitherT String IO a
|
type Client (Put a) = BaseUrl -> EitherT String IO a
|
||||||
|
|
||||||
clientWithRoute Proxy req host =
|
clientWithRoute Proxy req host =
|
||||||
performRequest methodPut req 200 host
|
performRequest methodPut req 200 host
|
||||||
|
|
|
@ -14,9 +14,16 @@ import System.IO.Unsafe
|
||||||
|
|
||||||
-- * Accessing APIs as a Client
|
-- * Accessing APIs as a Client
|
||||||
|
|
||||||
-- | Convenience function for creating 'URIAuth's.
|
data Scheme = Http | Https
|
||||||
mkHost :: String -> Int -> URIAuth
|
|
||||||
mkHost hostName port = URIAuth "" hostName (":" ++ show port)
|
data BaseUrl = BaseUrl {
|
||||||
|
baseUrlScheme :: Scheme,
|
||||||
|
baseUrlHost :: String,
|
||||||
|
baseUrlPort :: Int
|
||||||
|
}
|
||||||
|
|
||||||
|
httpBaseUrl :: String -> BaseUrl
|
||||||
|
httpBaseUrl host = BaseUrl Http host 80
|
||||||
|
|
||||||
-- | 'client' allows you to produce operations to query an API from a client.
|
-- | 'client' allows you to produce operations to query an API from a client.
|
||||||
client :: HasClient layout => Proxy layout -> Client layout
|
client :: HasClient layout => Proxy layout -> Client layout
|
||||||
|
@ -50,11 +57,17 @@ appendToQueryString pname pvalue req =
|
||||||
setRQBody :: ByteString -> Req -> Req
|
setRQBody :: ByteString -> Req -> Req
|
||||||
setRQBody b req = req { reqBody = b }
|
setRQBody b req = req { reqBody = b }
|
||||||
|
|
||||||
reqToRequest :: (Functor m, MonadThrow m) => Req -> URIAuth -> m Request
|
reqToRequest :: (Functor m, MonadThrow m) => Req -> BaseUrl -> m Request
|
||||||
reqToRequest req host = fmap (setrqb . setQS ) $ parseUrl url
|
reqToRequest req (BaseUrl scheme host port) = fmap (setrqb . setQS ) $ parseUrl url
|
||||||
|
|
||||||
where url = show $ nullURI { uriScheme = "http:"
|
where url = show $ nullURI { uriScheme = case scheme of
|
||||||
, uriAuthority = Just host
|
Http -> "http:"
|
||||||
|
Https -> "https:"
|
||||||
|
, uriAuthority = Just $
|
||||||
|
URIAuth { uriUserInfo = ""
|
||||||
|
, uriRegName = host
|
||||||
|
, uriPort = ":" ++ show port
|
||||||
|
}
|
||||||
, uriPath = reqPath req
|
, uriPath = reqPath req
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,13 +13,12 @@ import Data.Attoparsec.ByteString
|
||||||
import Data.ByteString.Lazy
|
import Data.ByteString.Lazy
|
||||||
import Data.String.Conversions
|
import Data.String.Conversions
|
||||||
import Network.HTTP.Types
|
import Network.HTTP.Types
|
||||||
import Network.URI
|
|
||||||
import Servant.Client
|
import Servant.Client
|
||||||
|
|
||||||
import qualified Network.HTTP.Client as Client
|
import qualified Network.HTTP.Client as Client
|
||||||
|
|
||||||
performRequest :: FromJSON result =>
|
performRequest :: FromJSON result =>
|
||||||
Method -> Req -> Int -> URIAuth -> EitherT String IO result
|
Method -> Req -> Int -> BaseUrl -> EitherT String IO result
|
||||||
performRequest method req wantedStatus host = do
|
performRequest method req wantedStatus host = do
|
||||||
partialRequest <- liftIO $ reqToRequest req host
|
partialRequest <- liftIO $ reqToRequest req host
|
||||||
|
|
||||||
|
|
|
@ -13,7 +13,6 @@ import Data.Foldable (forM_)
|
||||||
import Data.Proxy
|
import Data.Proxy
|
||||||
import Data.Typeable
|
import Data.Typeable
|
||||||
import Network.Socket
|
import Network.Socket
|
||||||
import Network.URI
|
|
||||||
import Network.Wai
|
import Network.Wai
|
||||||
import Network.Wai.Handler.Warp
|
import Network.Wai.Handler.Warp
|
||||||
import Test.Hspec
|
import Test.Hspec
|
||||||
|
@ -56,17 +55,17 @@ server = serve api (
|
||||||
:<|> \ a b c d -> return (a, b, c, d)
|
:<|> \ a b c d -> return (a, b, c, d)
|
||||||
)
|
)
|
||||||
|
|
||||||
withServer :: (URIAuth -> IO a) -> IO a
|
withServer :: (BaseUrl -> IO a) -> IO a
|
||||||
withServer action = withWaiDaemon (return server) (action . mkHost "localhost")
|
withServer action = withWaiDaemon (return server) action
|
||||||
|
|
||||||
getGet :: URIAuth -> EitherT String IO Person
|
getGet :: BaseUrl -> EitherT String IO Person
|
||||||
getCapture :: String -> URIAuth -> EitherT String IO Person
|
getCapture :: String -> BaseUrl -> EitherT String IO Person
|
||||||
getBody :: Person -> URIAuth -> EitherT String IO Person
|
getBody :: Person -> BaseUrl -> EitherT String IO Person
|
||||||
getQueryParam :: Maybe String -> URIAuth -> EitherT String IO Person
|
getQueryParam :: Maybe String -> BaseUrl -> EitherT String IO Person
|
||||||
getQueryParams :: [String] -> URIAuth -> EitherT String IO [Person]
|
getQueryParams :: [String] -> BaseUrl -> EitherT String IO [Person]
|
||||||
getQueryFlag :: Bool -> URIAuth -> EitherT String IO Bool
|
getQueryFlag :: Bool -> BaseUrl -> EitherT String IO Bool
|
||||||
getMultiple :: String -> Maybe Int -> Bool -> [(String, [Rational])]
|
getMultiple :: String -> Maybe Int -> Bool -> [(String, [Rational])]
|
||||||
-> URIAuth
|
-> BaseUrl
|
||||||
-> EitherT String IO (String, Maybe Int, Bool, [(String, [Rational])])
|
-> EitherT String IO (String, Maybe Int, Bool, [(String, [Rational])])
|
||||||
( getGet
|
( getGet
|
||||||
:<|> getCapture
|
:<|> getCapture
|
||||||
|
@ -119,8 +118,8 @@ spec = do
|
||||||
test (WrappedApi api) =
|
test (WrappedApi api) =
|
||||||
it (show (typeOf api)) $
|
it (show (typeOf api)) $
|
||||||
withWaiDaemon (return (serve api (left (500, "error message")))) $
|
withWaiDaemon (return (serve api (left (500, "error message")))) $
|
||||||
\ (mkHost "localhost" -> host) -> do
|
\ host -> do
|
||||||
let getResponse :: URIAuth -> EitherT String IO ()
|
let getResponse :: BaseUrl -> EitherT String IO ()
|
||||||
getResponse = client api
|
getResponse = client api
|
||||||
Left result <- runEitherT (getResponse host)
|
Left result <- runEitherT (getResponse host)
|
||||||
result `shouldContain` "error message"
|
result `shouldContain` "error message"
|
||||||
|
@ -133,17 +132,17 @@ spec = do
|
||||||
|
|
||||||
data WrappedApi where
|
data WrappedApi where
|
||||||
WrappedApi :: (HasServer api, Server api ~ EitherT (Int, String) IO a,
|
WrappedApi :: (HasServer api, Server api ~ EitherT (Int, String) IO a,
|
||||||
HasClient api, Client api ~ (URIAuth -> EitherT String IO ()),
|
HasClient api, Client api ~ (BaseUrl -> EitherT String IO ()),
|
||||||
Typeable api) =>
|
Typeable api) =>
|
||||||
Proxy api -> WrappedApi
|
Proxy api -> WrappedApi
|
||||||
|
|
||||||
|
|
||||||
-- * utils
|
-- * utils
|
||||||
|
|
||||||
withWaiDaemon :: IO Application -> (Port -> IO a) -> IO a
|
withWaiDaemon :: IO Application -> (BaseUrl -> IO a) -> IO a
|
||||||
withWaiDaemon mkApplication action = do
|
withWaiDaemon mkApplication action = do
|
||||||
application <- mkApplication
|
application <- mkApplication
|
||||||
bracket (acquire application) free (\ (_, _, port) -> action port)
|
bracket (acquire application) free (\ (_, _, baseUrl) -> action baseUrl)
|
||||||
where
|
where
|
||||||
acquire application = do
|
acquire application = do
|
||||||
(notifyStart, waitForStart) <- lvar
|
(notifyStart, waitForStart) <- lvar
|
||||||
|
@ -158,7 +157,8 @@ withWaiDaemon mkApplication action = do
|
||||||
runSettingsSocket settings socket application)
|
runSettingsSocket settings socket application)
|
||||||
`finally` notifyKilled ()
|
`finally` notifyKilled ()
|
||||||
krakenPort <- waitForStart
|
krakenPort <- waitForStart
|
||||||
return (thread, waitForKilled, krakenPort)
|
let baseUrl = (httpBaseUrl "localhost"){baseUrlPort = krakenPort}
|
||||||
|
return (thread, waitForKilled, baseUrl)
|
||||||
free (thread, waitForKilled, _) = do
|
free (thread, waitForKilled, _) = do
|
||||||
killThread thread
|
killThread thread
|
||||||
waitForKilled
|
waitForKilled
|
||||||
|
|
Loading…
Reference in a new issue