@@ -38,10 +38,17 @@ import Data.Time.Clock.System (getSystemTime, systemSeconds)
3838import Network.HTTP.Client
3939import qualified Network.HTTP.Types as N
4040import qualified Simplex.Messaging.Crypto as C
41- import Simplex.Messaging.Notifications.Protocol (DeviceToken (.. ), NtfRegCode (.. ), WPAuth (.. ), WPKey (.. ), WPP256dh (.. ), WPTokenParams (.. ), encodePNMessages , wpAud , wpRequest )
41+ import Simplex.Messaging.Notifications.Protocol (DeviceToken (.. ), NtfRegCode (.. ), WPAuth (.. ), WPKey (.. ), WPP256dh (.. ), WPTokenParams (.. ), WPProvider ( .. ), encodePNMessages , wpAud , wpRequest )
4242import Simplex.Messaging.Notifications.Server.Push
4343import Simplex.Messaging.Notifications.Server.Store.Types
4444import Simplex.Messaging.Util (liftError' , safeDecodeUtf8 , tshow )
45+ import Simplex.Messaging.Transport.HTTP2.Client (HTTP2Client , getHTTP2Client , defaultHTTP2ClientConfig , HTTP2ClientError , sendRequest )
46+ import Network.Socket (ServiceName , HostName )
47+ import System.X509.Unix
48+ import qualified Network.HTTP2.Client as H
49+ import Data.ByteString.Builder (lazyByteString )
50+ import Simplex.Messaging.Encoding.String (StrEncoding (.. ))
51+ import Data.Bifunctor (first )
4552import UnliftIO.STM
4653
4754-- | Vapid
@@ -61,7 +68,6 @@ mkVapid key = VapidKey {key, fp}
6168data WebPushClient = WebPushClient
6269 { wpConfig :: WebPushConfig ,
6370 cache :: IORef (Maybe WPCache ),
64- manager :: Manager ,
6571 random :: TVar ChaChaDRG
6672 }
6773
@@ -132,26 +138,56 @@ mkVapidHeader VapidKey {key, fp} uriAuthority expire = do
132138 signedToken <- signedJWTTokenRaw key jwt
133139 pure $ " vapid t=" <> signedToken <> " ,k=" <> fp
134140
135- wpPushProviderClient :: WebPushClient -> PushProviderClient
136- wpPushProviderClient _ NtfTknRec {token = APNSDeviceToken _ _} _ = throwE PPInvalidPusher
137- wpPushProviderClient c@ WebPushClient {wpConfig, cache, manager} tkn@ NtfTknRec {token = token@ (WPDeviceToken pp params)} pn = do
141+ wpHTTP2Client :: HostName -> ServiceName -> IO (Either HTTP2ClientError HTTP2Client )
142+ wpHTTP2Client h p = do
143+ caStore <- Just <$> getSystemCertificateStore
144+ let config = defaultHTTP2ClientConfig
145+ getHTTP2Client h p caStore config nop
146+ where
147+ nop = pure ()
148+
149+ wpHeaders :: B. ByteString -> [N. Header ]
150+ wpHeaders vapidH = [
151+ -- Why http2-client doesn't accept TTL AND Urgency?
152+ -- Keeping Urgency for now, the TTL should be around 30 days by default on the push servers
153+ -- ("TTL", "2592000"), -- 30 days
154+ (" Urgency" , " high" ),
155+ (" Content-Encoding" , " aes128gcm" ),
156+ (" Authorization" , vapidH)
157+ -- TODO: topic for pings and interval
158+ ]
159+
160+ wpHTTP2Req :: B. ByteString -> [(N. HeaderName , B. ByteString )] -> LB. ByteString -> H. Request
161+ wpHTTP2Req path headers s = H. requestBuilder N. methodPost path headers (lazyByteString s)
162+
163+ wpPushProviderClientH2 :: WebPushClient -> HTTP2Client -> PushProviderClient
164+ wpPushProviderClientH2 _ _ NtfTknRec {token = APNSDeviceToken _ _} _ = throwE PPInvalidPusher
165+ wpPushProviderClientH2 c@ WebPushClient {wpConfig, cache} http2 tkn@ NtfTknRec {token = (WPDeviceToken pp@ (WPP p) params)} pn = do
166+ -- TODO [webpush] this function should accept type that is restricted to WP token (so, possibly WPProvider and WPTokenParams)
167+ -- parsing will happen in DeviceToken parser, so it won't fail here
168+ encBody <- body
169+ vapidH <- liftError' toPPWPError $ try $ getVapidHeader (vapidKey wpConfig) cache $ wpAud pp
170+ let req = wpHTTP2Req (wpPath params) (wpHeaders vapidH) $ LB. fromStrict encBody
171+ logDebug $ " HTTP/2 Request to " <> tshow (strEncode p)
172+ void $ liftHTTPS2 $ sendRequest http2 req Nothing
173+ where
174+ body :: ExceptT PushProviderError IO B. ByteString
175+ body = withExceptT PPCryptoError $ wpEncrypt c tkn params pn
176+ liftHTTPS2 a = ExceptT $ first PPConnection <$> a
177+
178+ wpPushProviderClientH1 :: WebPushClient -> Manager -> PushProviderClient
179+ wpPushProviderClientH1 _ _ NtfTknRec {token = APNSDeviceToken _ _} _ = throwE PPInvalidPusher
180+ wpPushProviderClientH1 c@ WebPushClient {wpConfig, cache} manager tkn@ NtfTknRec {token = token@ (WPDeviceToken pp params)} pn = do
138181 -- TODO [webpush] this function should accept type that is restricted to WP token (so, possibly WPProvider and WPTokenParams)
139182 -- parsing will happen in DeviceToken parser, so it won't fail here
140183 r <- wpRequest token
141184 vapidH <- liftError' toPPWPError $ try $ getVapidHeader (vapidKey wpConfig) cache $ wpAud pp
142185 logDebug $ " Web Push request to " <> tshow (host r)
143186 encBody <- withExceptT PPCryptoError $ wpEncrypt c tkn params pn
144- let requestHeaders =
145- [ (" TTL" , " 2592000" ), -- 30 days
146- (" Urgency" , " high" ),
147- (" Content-Encoding" , " aes128gcm" ),
148- (" Authorization" , vapidH)
149- -- TODO: topic for pings and interval
150- ]
151- req =
187+ let req =
152188 r
153189 { method = " POST" ,
154- requestHeaders,
190+ requestHeaders = wpHeaders vapidH ,
155191 requestBody = RequestBodyBS encBody,
156192 redirectCount = 0
157193 }
0 commit comments