diff --git a/changelog.d/5-internal/WPB-1220-servantify-proxy-internal b/changelog.d/5-internal/WPB-1220-servantify-proxy-internal
new file mode 100644
index 00000000000..f161136a346
--- /dev/null
+++ b/changelog.d/5-internal/WPB-1220-servantify-proxy-internal
@@ -0,0 +1 @@
+Servantify internal routing table for proxy.
diff --git a/libs/metrics-wai/default.nix b/libs/metrics-wai/default.nix
index eb3a260e929..8bb74088e5e 100644
--- a/libs/metrics-wai/default.nix
+++ b/libs/metrics-wai/default.nix
@@ -14,6 +14,7 @@
, servant
, servant-multipart
, text
+, types-common
, utf8-string
, wai
, wai-middleware-prometheus
@@ -32,6 +33,7 @@ mkDerivation {
servant
servant-multipart
text
+ types-common
utf8-string
wai
wai-middleware-prometheus
diff --git a/libs/metrics-wai/metrics-wai.cabal b/libs/metrics-wai/metrics-wai.cabal
index ed848c893cb..1b6e5cfa03b 100644
--- a/libs/metrics-wai/metrics-wai.cabal
+++ b/libs/metrics-wai/metrics-wai.cabal
@@ -76,6 +76,7 @@ library
, servant
, servant-multipart
, text >=0.11
+ , types-common
, utf8-string
, wai >=3
, wai-middleware-prometheus
diff --git a/libs/metrics-wai/src/Data/Metrics/Middleware/Prometheus.hs b/libs/metrics-wai/src/Data/Metrics/Middleware/Prometheus.hs
index f1f7c1ca562..39b73e351e9 100644
--- a/libs/metrics-wai/src/Data/Metrics/Middleware/Prometheus.hs
+++ b/libs/metrics-wai/src/Data/Metrics/Middleware/Prometheus.hs
@@ -17,10 +17,12 @@
module Data.Metrics.Middleware.Prometheus
( waiPrometheusMiddleware,
+ waiPrometheusMiddlewarePaths,
normalizeWaiRequestRoute,
)
where
+import Data.Id
import Data.Metrics.Types (Paths, treeLookup)
import Data.Metrics.WaiRoute (treeToPaths)
import Data.Text.Encoding qualified as T
@@ -33,12 +35,17 @@ import Network.Wai.Routing.Route (Routes, prepare)
-- This middleware requires your servers 'Routes' because it does some normalization
-- (e.g. removing params from calls)
waiPrometheusMiddleware :: (Monad m) => Routes a m b -> Wai.Middleware
-waiPrometheusMiddleware routes =
+waiPrometheusMiddleware routes = waiPrometheusMiddlewarePaths $ treeToPaths $ prepare routes
+
+-- | Helper function that should only be needed as long as we have wai-routing code left in
+-- proxy: run 'treeToPaths' on old routing tables and 'routeToPaths' on the servant ones, and
+-- feed both to this function.
+waiPrometheusMiddlewarePaths :: Paths -> Wai.Middleware
+waiPrometheusMiddlewarePaths paths =
Promth.prometheus conf . instrument (normalizeWaiRequestRoute paths)
where
-- See Note [Raw Response]
instrument = Promth.instrumentHandlerValueWithFilter Promth.ignoreRawResponses
- paths = treeToPaths $ prepare routes
conf =
Promth.def
{ Promth.prometheusEndPoint = ["i", "metrics"],
@@ -57,4 +64,4 @@ normalizeWaiRequestRoute paths req = pathInfo
-- Use the normalized path info if available; otherwise dump the raw path info for
-- debugging purposes
pathInfo :: Text
- pathInfo = T.decodeUtf8 $ fromMaybe "N/A" mPathInfo
+ pathInfo = T.decodeUtf8 $ fromMaybe defRequestId mPathInfo
diff --git a/libs/metrics-wai/src/Data/Metrics/Servant.hs b/libs/metrics-wai/src/Data/Metrics/Servant.hs
index a66da6837a2..490ed13ded2 100644
--- a/libs/metrics-wai/src/Data/Metrics/Servant.hs
+++ b/libs/metrics-wai/src/Data/Metrics/Servant.hs
@@ -27,6 +27,7 @@
module Data.Metrics.Servant where
import Data.ByteString.UTF8 qualified as UTF8
+import Data.Id
import Data.Metrics.Types
import Data.Metrics.Types qualified as Metrics
import Data.Proxy
@@ -49,7 +50,7 @@ servantPrometheusMiddleware _ = Promth.prometheus conf . instrument promthNormal
promthNormalize req = pathInfo
where
mPathInfo = Metrics.treeLookup (routesToPaths @api) $ encodeUtf8 <$> Wai.pathInfo req
- pathInfo = decodeUtf8With lenientDecode $ fromMaybe "N/A" mPathInfo
+ pathInfo = decodeUtf8With lenientDecode $ fromMaybe defRequestId mPathInfo
-- See Note [Raw Response]
instrument = Promth.instrumentHandlerValueWithFilter Promth.ignoreRawResponses
diff --git a/libs/metrics-wai/src/Data/Metrics/Types.hs b/libs/metrics-wai/src/Data/Metrics/Types.hs
index 0d1a70903d0..4d83874789b 100644
--- a/libs/metrics-wai/src/Data/Metrics/Types.hs
+++ b/libs/metrics-wai/src/Data/Metrics/Types.hs
@@ -41,6 +41,7 @@ newtype PathTemplate = PathTemplate Text
-- (e.g. user id).
newtype Paths = Paths (Forest PathSegment)
deriving (Eq, Show)
+ deriving newtype (Semigroup)
type PathSegment = Either ByteString ByteString
diff --git a/libs/types-common/src/Data/Id.hs b/libs/types-common/src/Data/Id.hs
index 3ef7152c913..0a1dbe22ad3 100644
--- a/libs/types-common/src/Data/Id.hs
+++ b/libs/types-common/src/Data/Id.hs
@@ -48,6 +48,7 @@ module Data.Id
-- * Other IDs
ConnId (..),
RequestId (..),
+ defRequestId,
BotId (..),
NoId,
OAuthClientId,
@@ -418,6 +419,9 @@ newtype RequestId = RequestId
ToBytes
)
+defRequestId :: (IsString s) => s
+defRequestId = "N/A"
+
instance ToSchema RequestId where
schema =
RequestId . encodeUtf8
diff --git a/libs/wai-utilities/src/Network/Wai/Utilities/Request.hs b/libs/wai-utilities/src/Network/Wai/Utilities/Request.hs
index 2450bfd7b47..484c1b34643 100644
--- a/libs/wai-utilities/src/Network/Wai/Utilities/Request.hs
+++ b/libs/wai-utilities/src/Network/Wai/Utilities/Request.hs
@@ -1,4 +1,3 @@
-{-# LANGUAGE OverloadedStrings #-}
{-# OPTIONS_GHC -Wno-orphans #-}
-- This file is part of the Wire Server implementation.
@@ -56,7 +55,7 @@ lookupRequestId reqIdHeaderName =
getRequestId :: HeaderName -> Request -> RequestId
getRequestId reqIdHeaderName req =
- RequestId $ fromMaybe "N/A" $ lookupRequestId reqIdHeaderName req
+ RequestId $ fromMaybe defRequestId $ lookupRequestId reqIdHeaderName req
----------------------------------------------------------------------------
-- Typed JSON 'Request'
diff --git a/libs/wai-utilities/src/Network/Wai/Utilities/Server.hs b/libs/wai-utilities/src/Network/Wai/Utilities/Server.hs
index dd3306f4a65..20f8fc9b934 100644
--- a/libs/wai-utilities/src/Network/Wai/Utilities/Server.hs
+++ b/libs/wai-utilities/src/Network/Wai/Utilities/Server.hs
@@ -60,6 +60,7 @@ import Data.ByteString.Builder
import Data.ByteString.Char8 qualified as C
import Data.ByteString.Lazy qualified as LBS
import Data.Domain (domainText)
+import Data.Id
import Data.Metrics.GC (spawnGCMetricsCollector)
import Data.Streaming.Zlib (ZlibException (..))
import Data.Text.Encoding qualified as Text
@@ -168,7 +169,7 @@ compile routes = Route.prepare (Route.renderer predicateError >> routes)
r = reasonStr <$> reason e
t = message e
in case catMaybes [l, s, r] of
- [] -> maybe "N/A" (LT.decodeUtf8With lenientDecode . LBS.fromStrict) t
+ [] -> maybe defRequestId (LT.decodeUtf8With lenientDecode . LBS.fromStrict) t
bs -> LT.decodeUtf8With lenientDecode . toLazyByteString $ mconcat bs <> messageStr t
labelStr [] = Nothing
labelStr ls =
@@ -311,7 +312,7 @@ heavyDebugLogging sanitizeReq lvl lgr reqIdHeaderName app = \req cont -> do
logMostlyEverything req bdy resp = Log.debug lgr logMsg
where
logMsg =
- field "request" (fromMaybe "N/A" $ lookupRequestId reqIdHeaderName req)
+ field "request" (fromMaybe defRequestId $ lookupRequestId reqIdHeaderName req)
. field "request_details" (show req)
. field "request_body" bdy
. field "response_status" (show $ responseStatus resp)
@@ -350,7 +351,7 @@ rethrow5xx getRequestId logger app req k = app req k'
let logMsg =
field "canoncalpath" (show $ pathInfo req)
. field "rawpath" (rawPathInfo req)
- . field "request" (fromMaybe "N/A" $ getRequestId req)
+ . field "request" (fromMaybe defRequestId $ getRequestId req)
. msg (val "ResponseRaw - cannot collect metrics or log info on errors")
Log.log logger Log.Debug logMsg
k resp
@@ -436,7 +437,7 @@ logError' g mr e = liftIO $ doLog g (logErrorMsgWithRequest mr e)
logJSONResponse :: (MonadIO m) => Logger -> Maybe ByteString -> JSONResponse -> m ()
logJSONResponse g mReqId e = do
- let r = fromMaybe "N/A" mReqId
+ let r = fromMaybe defRequestId mReqId
liftIO $
doLog g $
field "request" r
@@ -462,7 +463,7 @@ logErrorMsg (Wai.Error c l m md inner) =
logErrorMsgWithRequest :: Maybe ByteString -> Wai.Error -> Msg -> Msg
logErrorMsgWithRequest mr e =
- field "request" (fromMaybe "N/A" mr) . logErrorMsg e
+ field "request" (fromMaybe defRequestId mr) . logErrorMsg e
runHandlers :: SomeException -> [Handler IO a] -> IO a
runHandlers e [] = throwIO e
diff --git a/libs/wire-subsystems/src/Wire/EmailSubsystem/Interpreter.hs b/libs/wire-subsystems/src/Wire/EmailSubsystem/Interpreter.hs
index a78e26f3754..f3451750e47 100644
--- a/libs/wire-subsystems/src/Wire/EmailSubsystem/Interpreter.hs
+++ b/libs/wire-subsystems/src/Wire/EmailSubsystem/Interpreter.hs
@@ -345,8 +345,8 @@ renderNewClientEmail email name locale Client {..} NewClientEmailTemplate {..} b
html = renderHtmlWithBranding newClientEmailBodyHtml replace branding
subj = renderTextWithBranding newClientEmailSubject replace branding
replace "name" = fromName name
- replace "label" = fromMaybe "N/A" clientLabel
- replace "model" = fromMaybe "N/A" clientModel
+ replace "label" = fromMaybe defRequestId clientLabel
+ replace "model" = fromMaybe defRequestId clientModel
replace "date" =
formatDateTime
"%A %e %B %Y, %H:%M - %Z"
diff --git a/libs/wire-subsystems/test/unit/Wire/NotificationSubsystem/InterpreterSpec.hs b/libs/wire-subsystems/test/unit/Wire/NotificationSubsystem/InterpreterSpec.hs
index 880a213d25b..9fbd3babe89 100644
--- a/libs/wire-subsystems/test/unit/Wire/NotificationSubsystem/InterpreterSpec.hs
+++ b/libs/wire-subsystems/test/unit/Wire/NotificationSubsystem/InterpreterSpec.hs
@@ -1,9 +1,9 @@
module Wire.NotificationSubsystem.InterpreterSpec (spec) where
-import Bilge (RequestId (..))
import Control.Concurrent.Async (async, wait)
import Control.Exception (throwIO)
import Data.Data (Proxy (Proxy))
+import Data.Id
import Data.List.NonEmpty (NonEmpty ((:|)), fromList)
import Data.List1 qualified as List1
import Data.Range (fromRange, toRange)
@@ -37,7 +37,7 @@ spec = describe "NotificationSubsystem.Interpreter" do
{ fanoutLimit = toRange $ Proxy @30,
chunkSize = 12,
slowPushDelay = 0,
- requestId = RequestId "N/A"
+ requestId = RequestId defRequestId
}
connId2 <- generate arbitrary
@@ -98,7 +98,7 @@ spec = describe "NotificationSubsystem.Interpreter" do
{ fanoutLimit = toRange $ Proxy @30,
chunkSize = 12,
slowPushDelay = 0,
- requestId = RequestId "N/A"
+ requestId = RequestId defRequestId
}
connId2 <- generate arbitrary
@@ -153,7 +153,7 @@ spec = describe "NotificationSubsystem.Interpreter" do
{ fanoutLimit = toRange $ Proxy @30,
chunkSize = 12,
slowPushDelay = 1,
- requestId = RequestId "N/A"
+ requestId = RequestId defRequestId
}
connId2 <- generate arbitrary
@@ -211,7 +211,7 @@ spec = describe "NotificationSubsystem.Interpreter" do
{ fanoutLimit = toRange $ Proxy @30,
chunkSize = 12,
slowPushDelay = 1,
- requestId = RequestId "N/A"
+ requestId = RequestId defRequestId
}
user1 <- generate arbitrary
diff --git a/services/background-worker/src/Wire/BackendNotificationPusher.hs b/services/background-worker/src/Wire/BackendNotificationPusher.hs
index 464c93e0cf0..6a6cf2f7f62 100644
--- a/services/background-worker/src/Wire/BackendNotificationPusher.hs
+++ b/services/background-worker/src/Wire/BackendNotificationPusher.hs
@@ -116,7 +116,7 @@ pushNotification runningFlag targetDomain (msg, envelope) = do
ceHttp2Manager <- asks http2Manager
let ceOriginDomain = notif.ownDomain
ceTargetDomain = targetDomain
- ceOriginRequestId = fromMaybe (RequestId "N/A") notif.requestId
+ ceOriginRequestId = fromMaybe (RequestId defRequestId) notif.requestId
cveEnv = FederatorClientEnv {..}
cveVersion = Just V0 -- V0 is assumed for non-versioned queue messages
fcEnv = FederatorClientVersionedEnv {..}
@@ -135,7 +135,7 @@ pushNotification runningFlag targetDomain (msg, envelope) = do
ceFederator = federator,
ceHttp2Manager = manager,
ceOriginRequestId =
- fromMaybe (RequestId "N/A") . (.requestId) . NE.head $ bundle.notifications
+ fromMaybe (RequestId defRequestId) . (.requestId) . NE.head $ bundle.notifications
}
remoteVersions :: Set Int <-
liftIO
@@ -166,7 +166,7 @@ pushNotification runningFlag targetDomain (msg, envelope) = do
ceHttp2Manager <- asks http2Manager
let ceOriginDomain = notif.ownDomain
ceTargetDomain = targetDomain
- ceOriginRequestId = fromMaybe (RequestId "N/A") notif.requestId
+ ceOriginRequestId = fromMaybe (RequestId defRequestId) notif.requestId
cveEnv = FederatorClientEnv {..}
fcEnv = FederatorClientVersionedEnv {..}
sendNotificationIgnoringVersionMismatch fcEnv notif.targetComponent notif.path notif.body
diff --git a/services/background-worker/test/Test/Wire/BackendNotificationPusherSpec.hs b/services/background-worker/test/Test/Wire/BackendNotificationPusherSpec.hs
index 29906684cae..416a2653f82 100644
--- a/services/background-worker/test/Test/Wire/BackendNotificationPusherSpec.hs
+++ b/services/background-worker/test/Test/Wire/BackendNotificationPusherSpec.hs
@@ -67,7 +67,7 @@ spec = do
path = "/on-user-deleted-connections",
body = RawJson $ Aeson.encode notifContent,
bodyVersions = Nothing,
- requestId = Just $ RequestId "N/A"
+ requestId = Just $ RequestId defRequestId
}
envelope <- newMockEnvelope
let msg =
@@ -104,7 +104,7 @@ spec = do
notifContent <-
generate $
ClientRemovedRequest <$> arbitrary <*> arbitrary <*> arbitrary
- let bundle = toBundle @'OnClientRemovedTag (RequestId "N/A") origDomain notifContent
+ let bundle = toBundle @'OnClientRemovedTag (RequestId defRequestId) origDomain notifContent
envelope <- newMockEnvelope
let msg =
Q.newMsg
@@ -148,8 +148,8 @@ spec = do
}
let update0 = conversationUpdateToV0 update
let bundle =
- toBundle (RequestId "N/A") origDomain update
- <> toBundle (RequestId "N/A") origDomain update0
+ toBundle (RequestId defRequestId) origDomain update
+ <> toBundle (RequestId defRequestId) origDomain update0
envelope <- newMockEnvelope
let msg =
Q.newMsg
@@ -215,7 +215,7 @@ spec = do
path = "/on-user-deleted-connections",
body = RawJson $ Aeson.encode notifContent,
bodyVersions = Nothing,
- requestId = Just $ RequestId "N/A"
+ requestId = Just $ RequestId defRequestId
}
envelope <- newMockEnvelope
let msg =
diff --git a/services/brig/src/Brig/App.hs b/services/brig/src/Brig/App.hs
index 28735c7817b..eb7b06457e2 100644
--- a/services/brig/src/Brig/App.hs
+++ b/services/brig/src/Brig/App.hs
@@ -92,7 +92,6 @@ module Brig.App
)
where
-import Bilge (RequestId (..))
import Bilge qualified as RPC
import Bilge.IO
import Bilge.RPC (HasRequestId (..))
@@ -122,6 +121,7 @@ import Control.Monad.Trans.Resource
import Data.ByteString.Conversion
import Data.Credentials (Credentials (..))
import Data.Domain
+import Data.Id
import Data.Misc
import Data.Qualified
import Data.Text qualified as Text
@@ -278,7 +278,7 @@ newEnv opts = do
awsEnv = aws, -- used by `journalEvent` directly
appLogger = lgr,
internalEvents = (eventsQueue :: QueueEnv),
- requestId = RequestId "N/A",
+ requestId = RequestId defRequestId,
userTemplates = utp,
providerTemplates = ptp,
teamTemplates = ttp,
diff --git a/services/cannon/src/Cannon/App.hs b/services/cannon/src/Cannon/App.hs
index 842d38135a3..770bf0ff499 100644
--- a/services/cannon/src/Cannon/App.hs
+++ b/services/cannon/src/Cannon/App.hs
@@ -29,7 +29,7 @@ import Control.Monad.Catch
import Data.Aeson hiding (Error, Key, (.=))
import Data.ByteString.Conversion
import Data.ByteString.Lazy (toStrict)
-import Data.Id (ClientId)
+import Data.Id
import Data.Text.Lazy qualified as Text
import Data.Timeout
import Imports hiding (threadDelay)
@@ -155,7 +155,7 @@ rejectOnError :: PendingConnection -> HandshakeException -> IO a
rejectOnError p x = do
let f lb mg = toStrict . encode $ mkError status400 lb mg
case x of
- NotSupported -> rejectRequest p (f "protocol not supported" "N/A")
+ NotSupported -> rejectRequest p (f "protocol not supported" defRequestId)
MalformedRequest _ m -> rejectRequest p (f "malformed-request" (Text.pack m))
OtherHandshakeException m -> rejectRequest p (f "other-error" (Text.pack m))
_ -> pure ()
diff --git a/services/cannon/src/Cannon/Types.hs b/services/cannon/src/Cannon/Types.hs
index eec8d20ac4b..6fa37b78a65 100644
--- a/services/cannon/src/Cannon/Types.hs
+++ b/services/cannon/src/Cannon/Types.hs
@@ -33,7 +33,7 @@ module Cannon.Types
)
where
-import Bilge (Manager, RequestId (..))
+import Bilge (Manager)
import Bilge.RPC (HasRequestId (..))
import Cannon.Dict (Dict)
import Cannon.Options
@@ -42,6 +42,7 @@ import Cannon.WS qualified as WS
import Control.Concurrent.Async (mapConcurrently)
import Control.Lens ((^.))
import Control.Monad.Catch
+import Data.Id
import Data.Text.Encoding
import Imports
import Prometheus
@@ -100,7 +101,7 @@ mkEnv ::
Clock ->
Env
mkEnv external o l d p g t =
- Env o l d (RequestId "N/A") $
+ Env o l d (RequestId defRequestId) $
WS.env external (o ^. cannon . port) (encodeUtf8 $ o ^. gundeck . host) (o ^. gundeck . port) l p d g t (o ^. drainOpts)
runCannon :: Env -> Cannon a -> IO a
diff --git a/services/cannon/src/Cannon/WS.hs b/services/cannon/src/Cannon/WS.hs
index 0ad9820df96..ea106f4cf03 100644
--- a/services/cannon/src/Cannon/WS.hs
+++ b/services/cannon/src/Cannon/WS.hs
@@ -62,7 +62,7 @@ import Data.ByteString.Char8 (pack)
import Data.ByteString.Conversion
import Data.ByteString.Lazy qualified as L
import Data.Hashable
-import Data.Id (ClientId, ConnId (..), UserId)
+import Data.Id (ClientId, ConnId (..), UserId, defRequestId)
import Data.List.Extra (chunksOf)
import Data.Text.Encoding (decodeUtf8)
import Data.Timeout (TimeoutUnit (..), (#))
@@ -192,7 +192,7 @@ env ::
Clock ->
DrainOpts ->
Env
-env leh lp gh gp = Env leh lp (host gh . port gp $ empty) (RequestId "N/A")
+env leh lp gh gp = Env leh lp (host gh . port gp $ empty) (RequestId defRequestId)
runWS :: (MonadIO m) => Env -> WS a -> m a
runWS e m = liftIO $ runReaderT (_conn m) e
diff --git a/services/cargohold/src/CargoHold/App.hs b/services/cargohold/src/CargoHold/App.hs
index 1495bda34fe..5acb66a57ed 100644
--- a/services/cargohold/src/CargoHold/App.hs
+++ b/services/cargohold/src/CargoHold/App.hs
@@ -47,7 +47,7 @@ module CargoHold.App
where
import Amazonka (S3AddressingStyle (S3AddressingStylePath))
-import Bilge (Manager, MonadHttp, RequestId (..), newManager, withResponse)
+import Bilge (Manager, MonadHttp, newManager, withResponse)
import qualified Bilge
import Bilge.RPC (HasRequestId (..))
import qualified CargoHold.AWS as AWS
@@ -57,6 +57,7 @@ import Control.Error (ExceptT, exceptT)
import Control.Exception (throw)
import Control.Lens (lensField, lensRules, makeLensesWith, non, (.~), (?~), (^.))
import Control.Monad.Catch (MonadCatch, MonadMask, MonadThrow)
+import Data.Id
import qualified Data.Map as Map
import Data.Qualified
import HTTP2.Client.Manager (Http2Manager, http2ManagerWithSSLCtx)
@@ -100,7 +101,7 @@ newEnv opts = do
awsEnv <- initAws opts.aws logger httpMgr
multiIngressAWS <- initMultiIngressAWS logger httpMgr
let localDomain = toLocalUnsafe opts.settings.federationDomain ()
- pure $ Env awsEnv logger httpMgr http2Mgr (RequestId "N/A") opts localDomain multiIngressAWS
+ pure $ Env awsEnv logger httpMgr http2Mgr (RequestId defRequestId) opts localDomain multiIngressAWS
where
initMultiIngressAWS :: Logger -> Manager -> IO (Map String AWS.Env)
initMultiIngressAWS logger httpMgr =
diff --git a/services/federator/src/Federator/Run.hs b/services/federator/src/Federator/Run.hs
index d7ddfb27d4f..83b9883b414 100644
--- a/services/federator/src/Federator/Run.hs
+++ b/services/federator/src/Federator/Run.hs
@@ -92,7 +92,7 @@ run opts = do
newEnv :: Opts -> DNS.Resolver -> Log.Logger -> IO Env
newEnv o _dnsResolver _applog = do
- let _requestId = RequestId "N/A"
+ let _requestId = RequestId defRequestId
_runSettings = o.optSettings
_service Brig = o.brig
_service Galley = o.galley
diff --git a/services/federator/test/integration/Test/Federator/IngressSpec.hs b/services/federator/test/integration/Test/Federator/IngressSpec.hs
index babbaca3a43..41b9f42af3c 100644
--- a/services/federator/test/integration/Test/Federator/IngressSpec.hs
+++ b/services/federator/test/integration/Test/Federator/IngressSpec.hs
@@ -150,7 +150,7 @@ inwardBrigCallViaIngressWithSettings sslCtx requestPath payload =
mgr <- liftToCodensity . liftIO $ http2ManagerWithSSLCtx sslCtx
liftToCodensity
. runInputConst mgr
- . runInputConst (RequestId "N/A")
+ . runInputConst (RequestId defRequestId)
. assertNoError @DiscoveryFailure
. discoverConst target
. interpretRemote
diff --git a/services/federator/test/unit/Test/Federator/Client.hs b/services/federator/test/unit/Test/Federator/Client.hs
index a816f7710c9..36c2717a6b4 100644
--- a/services/federator/test/unit/Test/Federator/Client.hs
+++ b/services/federator/test/unit/Test/Federator/Client.hs
@@ -97,7 +97,7 @@ withMockFederatorClient mock action = withTempMockFederator mock $ \port -> do
ceTargetDomain = targetDomain,
ceFederator = Endpoint "127.0.0.1" (fromIntegral port),
ceHttp2Manager = mgr,
- ceOriginRequestId = RequestId "N/A"
+ ceOriginRequestId = RequestId defRequestId
}
a <- runFederatorClient env action
case a of
@@ -137,7 +137,7 @@ testClientStreaming = withInfiniteMockServer $ \port -> do
ceTargetDomain = targetDomain,
ceFederator = Endpoint "127.0.0.1" (fromIntegral port),
ceHttp2Manager = mgr,
- ceOriginRequestId = RequestId "N/A"
+ ceOriginRequestId = RequestId defRequestId
}
venv = FederatorClientVersionedEnv env Nothing
let c = clientIn (Proxy @StreamingAPI) (Proxy @(FederatorClient 'Brig))
@@ -202,7 +202,7 @@ testClientConnectionError = do
ceTargetDomain = targetDomain,
ceFederator = Endpoint "127.0.0.1" 1,
ceHttp2Manager = mgr,
- ceOriginRequestId = RequestId "N/A"
+ ceOriginRequestId = RequestId defRequestId
}
result <- runFederatorClient env (fedClient @'Brig @"get-user-by-handle" handle)
case result of
diff --git a/services/galley/src/Galley/App.hs b/services/galley/src/Galley/App.hs
index baa3284e861..a9a02e660ea 100644
--- a/services/galley/src/Galley/App.hs
+++ b/services/galley/src/Galley/App.hs
@@ -167,7 +167,7 @@ createEnv o l = do
mgr <- initHttpManager o
h2mgr <- initHttp2Manager
codeURIcfg <- validateOptions o
- Env (RequestId "N/A") o l mgr h2mgr (o ^. O.federator) (o ^. O.brig) cass
+ Env (RequestId defRequestId) o l mgr h2mgr (o ^. O.federator) (o ^. O.brig) cass
<$> Q.new 16000
<*> initExtEnv
<*> maybe (pure Nothing) (fmap Just . Aws.mkEnv l mgr) (o ^. journal)
diff --git a/services/gundeck/src/Gundeck/Env.hs b/services/gundeck/src/Gundeck/Env.hs
index e3d1fcbe148..2397005c68a 100644
--- a/services/gundeck/src/Gundeck/Env.hs
+++ b/services/gundeck/src/Gundeck/Env.hs
@@ -27,6 +27,7 @@ import Control.Concurrent.Async (Async)
import Control.Lens (makeLenses, (^.))
import Control.Retry (capDelay, exponentialBackoff)
import Data.ByteString.Char8 qualified as BSChar8
+import Data.Id
import Data.Misc (Milliseconds (..))
import Data.Text qualified as Text
import Data.Time.Clock
@@ -100,7 +101,7 @@ createEnv o = do
{ updateAction = Ms . round . (* 1000) <$> getPOSIXTime
}
mtbs <- mkThreadBudgetState `mapM` (o ^. settings . maxConcurrentNativePushes)
- pure $! (rThread : rAdditionalThreads,) $! Env (RequestId "N/A") o l n p r rAdditional a io mtbs
+ pure $! (rThread : rAdditionalThreads,) $! Env (RequestId defRequestId) o l n p r rAdditional a io mtbs
reqIdMsg :: RequestId -> Logger.Msg -> Logger.Msg
reqIdMsg = ("request" Logger..=) . unRequestId
diff --git a/services/proxy/default.nix b/services/proxy/default.nix
index b6205a6acee..8b689661b9c 100644
--- a/services/proxy/default.nix
+++ b/services/proxy/default.nix
@@ -9,6 +9,7 @@
, bytestring
, case-insensitive
, configurator
+, errors
, exceptions
, extended
, gitignoreSource
@@ -21,6 +22,7 @@
, lib
, metrics-wai
, retry
+, servant-server
, text
, tinylog
, types-common
@@ -46,6 +48,7 @@ mkDerivation {
bytestring
case-insensitive
configurator
+ errors
exceptions
extended
http-client
@@ -56,6 +59,7 @@ mkDerivation {
lens
metrics-wai
retry
+ servant-server
text
tinylog
types-common
diff --git a/services/proxy/proxy.cabal b/services/proxy/proxy.cabal
index e92831949f6..5da48e93a8b 100644
--- a/services/proxy/proxy.cabal
+++ b/services/proxy/proxy.cabal
@@ -17,7 +17,7 @@ flag static
library
exposed-modules:
- Proxy.API
+ Proxy.API.Internal
Proxy.API.Public
Proxy.Env
Proxy.Options
@@ -80,6 +80,7 @@ library
, bytestring >=0.10
, case-insensitive >=1.2
, configurator >=0.3
+ , errors
, exceptions >=0.8
, extended
, http-client >=0.7
@@ -90,6 +91,7 @@ library
, lens >=4.11
, metrics-wai >=0.5
, retry >=0.7
+ , servant-server
, text >=1.2
, tinylog >=0.12
, types-common >=0.8
diff --git a/services/proxy/src/Proxy/API.hs b/services/proxy/src/Proxy/API/Internal.hs
similarity index 55%
rename from services/proxy/src/Proxy/API.hs
rename to services/proxy/src/Proxy/API/Internal.hs
index d3ba31ca4f3..c7128b0bfdb 100644
--- a/services/proxy/src/Proxy/API.hs
+++ b/services/proxy/src/Proxy/API/Internal.hs
@@ -15,29 +15,19 @@
-- You should have received a copy of the GNU Affero General Public License along
-- with this program. If not, see .
-module Proxy.API
- ( sitemap,
+module Proxy.API.Internal
+ ( InternalAPI,
+ servantSitemap,
)
where
import Imports hiding (head)
-import Network.Wai.Predicate (true)
-import Network.Wai.Routing (Routes, continue, get, head)
-import Network.Wai.Utilities (empty)
-import Proxy.API.Public qualified as Public
-import Proxy.Env (Env)
-import Proxy.Proxy (Proxy)
+import Proxy.Proxy qualified
+import Servant
+import Wire.API.Routes.MultiVerb
+import Wire.API.Routes.Named (Named (Named))
-sitemap :: Env -> Routes a Proxy ()
-sitemap e = do
- Public.sitemap e
- routesInternal
+type InternalAPI = Named "status" ("i" :> "status" :> MultiVerb 'GET '[Servant.JSON] '[RespondEmpty 200 "OK"] ())
--- | IF YOU MODIFY THIS, BE AWARE OF:
---
--- >>> /libs/wire-api/src/Wire/API/Routes/Public/Proxy.hs
--- >>> https://wearezeta.atlassian.net/browse/SQSERVICES-1647
-routesInternal :: Routes a Proxy ()
-routesInternal = do
- head "/i/status" (continue $ const (pure empty)) true
- get "/i/status" (continue $ const (pure empty)) true
+servantSitemap :: ServerT InternalAPI Proxy.Proxy.Proxy
+servantSitemap = Named @"status" (pure ())
diff --git a/services/proxy/src/Proxy/API/Public.hs b/services/proxy/src/Proxy/API/Public.hs
index a33bafb1d9c..24989369d48 100644
--- a/services/proxy/src/Proxy/API/Public.hs
+++ b/services/proxy/src/Proxy/API/Public.hs
@@ -16,7 +16,9 @@
-- with this program. If not, see .
module Proxy.API.Public
- ( sitemap,
+ ( PublicAPI,
+ servantSitemap,
+ waiRoutingSitemap,
)
where
@@ -41,18 +43,35 @@ import Network.Wai.Internal qualified as I
import Network.Wai.Predicate hiding (Error, err, setStatus)
import Network.Wai.Predicate.Request (getRequest)
import Network.Wai.Routing hiding (path, route)
+import Network.Wai.Routing qualified as Routing
import Network.Wai.Utilities
+import Network.Wai.Utilities.Server (compile)
import Proxy.Env
import Proxy.Proxy
+import Servant qualified
import System.Logger.Class hiding (Error, info, render)
import System.Logger.Class qualified as Logger
+type PublicAPI = Servant.Raw -- see https://wearezeta.atlassian.net/browse/WPB-1216
+
+servantSitemap :: Env -> Servant.ServerT PublicAPI Proxy.Proxy.Proxy
+servantSitemap e = Servant.Tagged app
+ where
+ app :: Application
+ app r k = appInProxy e r (Routing.route tree r k')
+ where
+ tree :: Tree (App Proxy)
+ tree = compile (waiRoutingSitemap e)
+
+ k' :: Response -> Proxy.Proxy.Proxy ResponseReceived
+ k' = liftIO . k
+
-- | IF YOU MODIFY THIS, BE AWARE OF:
--
-- >>> /libs/wire-api/src/Wire/API/Routes/Public/Proxy.hs
-- >>> https://wearezeta.atlassian.net/browse/SQSERVICES-1647
-sitemap :: Env -> Routes a Proxy ()
-sitemap e = do
+waiRoutingSitemap :: Env -> Routes a Proxy ()
+waiRoutingSitemap e = do
get
"/proxy/youtube/v3/:path"
(proxy e "key" "secrets.youtube" Prefix "/youtube/v3" youtube)
@@ -107,7 +126,7 @@ proxy e qparam keyname reroute path phost rq k = do
then do
threadDelay 5000
loop runInIO (n - 1) waiReq req
- else runProxy e waiReq (k res)
+ else appInProxy e waiReq (k res)
onUpstreamError runInIO x _ next = do
void . runInIO $ Logger.warn (msg (val "gateway error") ~~ field "error" (show x))
next (errorRs error502)
diff --git a/services/proxy/src/Proxy/Env.hs b/services/proxy/src/Proxy/Env.hs
index d429787d1be..7b50325ed80 100644
--- a/services/proxy/src/Proxy/Env.hs
+++ b/services/proxy/src/Proxy/Env.hs
@@ -32,7 +32,7 @@ where
import Control.Lens (makeLenses, (^.))
import Data.Configurator
import Data.Configurator.Types
-import Data.Id (RequestId (..))
+import Data.Id (RequestId (..), defRequestId)
import Imports
import Network.HTTP.Client
import Network.HTTP.Client.TLS (tlsManagerSettings)
@@ -62,7 +62,7 @@ createEnv o = do
}
let ac = AutoConfig 60 (reloadError g)
(c, t) <- autoReload ac [Required $ o ^. secretsConfig]
- let rid = RequestId "N/A"
+ let rid = RequestId defRequestId
pure $! Env rid o g n c t
where
reloadError g x =
diff --git a/services/proxy/src/Proxy/Proxy.hs b/services/proxy/src/Proxy/Proxy.hs
index fe65dc4b920..348c96eb39b 100644
--- a/services/proxy/src/Proxy/Proxy.hs
+++ b/services/proxy/src/Proxy/Proxy.hs
@@ -17,7 +17,7 @@
-- You should have received a copy of the GNU Affero General Public License along
-- with this program. If not, see .
-module Proxy.Proxy (Proxy, runProxy) where
+module Proxy.Proxy (Proxy, appInProxy, runProxy) where
import Bilge.Request (requestIdName)
import Control.Lens hiding ((.=))
@@ -51,11 +51,14 @@ newtype Proxy a = Proxy
instance MonadLogger Proxy where
log l m = ask >>= \e -> Logger.log (e ^. applog) l (reqIdMsg (e ^. reqId) . m)
-runProxy :: Env -> Request -> Proxy ResponseReceived -> IO ResponseReceived
-runProxy e r m = do
+appInProxy :: Env -> Request -> Proxy ResponseReceived -> IO ResponseReceived
+appInProxy e r m = do
rid <- lookupReqId (e ^. applog) r
runReaderT (unProxy m) (reqId .~ rid $ e)
+runProxy :: Env -> Proxy a -> IO a
+runProxy e m = runReaderT (unProxy m) e
+
reqIdMsg :: RequestId -> Msg -> Msg
reqIdMsg = ("request" .=) . unRequestId
{-# INLINE reqIdMsg #-}
diff --git a/services/proxy/src/Proxy/Run.hs b/services/proxy/src/Proxy/Run.hs
index 16d43994006..14ebb11f691 100644
--- a/services/proxy/src/Proxy/Run.hs
+++ b/services/proxy/src/Proxy/Run.hs
@@ -20,29 +20,69 @@ module Proxy.Run
)
where
+import Bilge.Request (requestIdName)
+import Control.Error
import Control.Lens hiding ((.=))
import Control.Monad.Catch
-import Data.Metrics.Middleware.Prometheus (waiPrometheusMiddleware)
+import Data.Id (RequestId (RequestId), defRequestId)
+import Data.Metrics.Middleware.Prometheus (waiPrometheusMiddlewarePaths)
+import Data.Metrics.Servant
+import Data.Metrics.Types
+import Data.Metrics.WaiRoute
import Imports hiding (head)
+import Network.Wai (Middleware, Request, requestHeaders)
import Network.Wai.Middleware.Gunzip qualified as GZip
+import Network.Wai.Routing.Route
import Network.Wai.Utilities.Server hiding (serverPort)
-import Proxy.API (sitemap)
+import Proxy.API.Internal as I
+import Proxy.API.Public as P
import Proxy.Env
import Proxy.Options
import Proxy.Proxy
+import Servant qualified
import Wire.API.Routes.Version
import Wire.API.Routes.Version.Wai
+type CombinedAPI = PublicAPI Servant.:<|> InternalAPI
+
+combinedSitemap :: Env -> Servant.ServerT CombinedAPI Proxy
+combinedSitemap env = P.servantSitemap env Servant.:<|> I.servantSitemap
+
run :: Opts -> IO ()
run o = do
e <- createEnv o
s <- newSettings $ defaultServer (o ^. host) (o ^. port) (e ^. applog)
- let rtree = compile (sitemap e)
- let app r k = runProxy e r (route rtree r k)
- let middleware =
+
+ let metricsMW :: Middleware
+ metricsMW =
+ -- FUTUREWORK: once wai-routing has been removed from proxy: use `servantPrometheusMiddleware
+ -- (Servant.Proxy @CombinedAPI)` here (and probably inline the whole thing).
+ waiPrometheusMiddlewarePaths (pub <> int)
+ where
+ pub, int :: Paths
+ pub = treeToPaths $ prepare (P.waiRoutingSitemap e)
+ int = routesToPaths @InternalAPI
+
+ middleware :: Middleware
+ middleware =
versionMiddleware (foldMap expandVersionExp (o ^. disabledAPIVersions))
. requestIdMiddleware (e ^. applog) defaultRequestIdHeaderName
- . waiPrometheusMiddleware (sitemap e)
+ . metricsMW
. GZip.gunzip
. catchErrors (e ^. applog) defaultRequestIdHeaderName
- runSettingsWithShutdown s (middleware app) Nothing `finally` destroyEnv e
+
+ runSettingsWithShutdown s (middleware (mkApp e)) Nothing `finally` destroyEnv e
+
+mkApp :: Env -> Servant.Application
+mkApp env req = Servant.serve (Servant.Proxy @CombinedAPI) toServantSitemap req
+ where
+ toServantSitemap :: Servant.Server CombinedAPI
+ toServantSitemap = Servant.hoistServer (Servant.Proxy @CombinedAPI) toServantHandler (combinedSitemap env)
+
+ toServantHandler :: Proxy a -> Servant.Handler a
+ toServantHandler p = Servant.Handler . ExceptT $ Right <$> runProxy (injectReqId req env) p
+
+ injectReqId :: Request -> Env -> Env
+ injectReqId r = reqId .~ lookupReqId r
+ where
+ lookupReqId = RequestId . fromMaybe defRequestId . lookup requestIdName . requestHeaders
diff --git a/services/proxy/test/scripts/proxy-test.sh b/services/proxy/test/scripts/proxy-test.sh
index 3f8ee9ed3ba..ea7b89fe403 100755
--- a/services/proxy/test/scripts/proxy-test.sh
+++ b/services/proxy/test/scripts/proxy-test.sh
@@ -11,7 +11,8 @@ instance. this replaces more thorough integration tests, since
integration tests for just proxy without the proxied services
installed is hard and inadequate.
-WIRE_BACKEND: $WIRE_BACKEND
+WIRE_BACKEND: $WIRE_BACKEND (do not append a / to host:port!)
+
WIRE_ADMIN: $WIRE_ADMIN
WIRE_PASSWD:
"
diff --git a/services/spar/src/Spar/Run.hs b/services/spar/src/Spar/Run.hs
index e577e9ed5b6..170721df48b 100644
--- a/services/spar/src/Spar/Run.hs
+++ b/services/spar/src/Spar/Run.hs
@@ -99,7 +99,7 @@ mkApp sparCtxOpts = do
Bilge.host (sparCtxOpts ^. to galley . to host . to encodeUtf8)
. Bilge.port (sparCtxOpts ^. to galley . to port)
$ Bilge.empty
- let sparCtxRequestId = RequestId "N/A"
+ let sparCtxRequestId = RequestId defRequestId
let ctx0 = Env {..}
let heavyLogOnly :: (Wai.Request, LByteString) -> Maybe (Wai.Request, LByteString)
heavyLogOnly out@(req, _) =
diff --git a/tools/stern/src/Stern/App.hs b/tools/stern/src/Stern/App.hs
index 1056cf37182..e0f021a0932 100644
--- a/tools/stern/src/Stern/App.hs
+++ b/tools/stern/src/Stern/App.hs
@@ -69,7 +69,7 @@ newEnv opts = do
(mkRequest opts.ibis)
(mkRequest opts.galeb)
l
- (RequestId "N/A")
+ (RequestId defRequestId)
<$> newManager
where
mkRequest s = Bilge.host (encodeUtf8 s.host) . Bilge.port s.port $ Bilge.empty