packages feed

warp-s2n-tls 0.1.0.0 → 0.1.1.0

raw patch · 4 files changed

+93/−3 lines, 4 files

Files

CHANGELOG.md view
@@ -7,6 +7,24 @@  ## [Unreleased] +## [0.1.1.0] - 2026-09-08++### Added+- ALPN protocol advertisement. The server now offers `h2` and `http/1.1`, so+  Warp can select HTTP/2 for clients that request it.++### Changed+- **HTTP/2 is now reachable, where previously it was not.** Warp chooses+  between HTTP/1.1 and HTTP/2 from the ALPN protocol negotiated on the+  `Transport`. No protocol preferences were ever set on the s2n config, so no+  application protocol was negotiated, `tlsNegotiatedProtocol` was always+  `Nothing`, and every connection fell through to HTTP/1.1 regardless of the+  caller's Warp settings.++  The advertised list is derived from Warp's own `settingsHTTP2Enabled`, so+  `setHTTP2Disabled` withdraws `h2` from the offer and restores the previous+  behaviour exactly. Callers that require HTTP/1.1 should set it.+ ## [0.1.0.0] - 2026-04-28  ### Added
src/Network/Wai/Handler/WarpS2N.hs view
@@ -413,6 +413,16 @@   -- Initialize s2n config   config <- initS2nConfig tls tlsSet +  -- Advertise ALPN protocols.  Warp selects HTTP/2 from the negotiated+  -- protocol on the Transport, so without this it never sees "h2" and every+  -- connection stays HTTP/1.1.  Deriving the list from the caller's Warp+  -- settings keeps 'setHTTP2Disabled' working as the single kill switch+  -- rather than adding a second, separately-configured one here.+  tls.setProtocolPreferences config+    $ if WarpI.settingsHTTP2Enabled settings+      then ["h2", "http/1.1"]+      else ["http/1.1"]+   -- Set up ticket key manager if configured, then run the server   rotateAction <- case tlsTicketKeyManager of     Nothing -> pure (pure ()) -- dummy rotation action
test/ProtocolTests.hs view
@@ -4,6 +4,7 @@  import Control.Applicative import Control.Exception (SomeException, bracket, try)+import Data.ByteString (ByteString) import Data.Default.Class (def) import Network.HTTP.Types (status200) import Network.Socket qualified as Socket@@ -11,7 +12,12 @@ import Network.TLS qualified as TLS import Network.TLS.Extra.Cipher qualified as TLS import Network.Wai (responseLBS)-import Network.Wai.Handler.Warp (defaultSettings, setBeforeMainLoop)+import Network.Wai.Handler.Warp (+    Settings,+    defaultSettings,+    setBeforeMainLoop,+    setHTTP2Disabled,+ ) import Network.Wai.Handler.WarpS2N (     TLSSettings (..),     runTLSSocketLib,@@ -28,7 +34,34 @@ protocolSpec = do     describe "TLS version negotiation" versionNegotiationSpec     describe "Cipher preferences" cipherPreferencesSpec+    describe "ALPN" alpnSpec +alpnSpec :: SpecWith S2nTls+alpnSpec = do+    it "negotiates h2 when the client offers it" $ \tls -> do+        let serverSettings = tlsSettings testCertPath testKeyPath+        proto <- withTestServerGetALPN tls serverSettings defaultSettings ["h2", "http/1.1"]+        proto `shouldBe` Just "h2"++    it "selects http/1.1 when the client does not offer h2" $ \tls -> do+        let serverSettings = tlsSettings testCertPath testKeyPath+        proto <- withTestServerGetALPN tls serverSettings defaultSettings ["http/1.1"]+        proto `shouldBe` Just "http/1.1"++    -- The advertised list is derived from Warp's own settingsHTTP2Enabled, so+    -- setHTTP2Disabled has to withdraw h2 from the offer entirely.  Leaving it+    -- advertised would let a client select a protocol Warp then refuses to+    -- speak, which is worse than never offering it.+    it "withdraws h2 when the Warp settings disable HTTP/2" $ \tls -> do+        let serverSettings = tlsSettings testCertPath testKeyPath+        proto <-+            withTestServerGetALPN+                tls+                serverSettings+                (setHTTP2Disabled defaultSettings)+                ["h2", "http/1.1"]+        proto `shouldBe` Just "http/1.1"+ versionNegotiationSpec :: SpecWith S2nTls versionNegotiationSpec = do     it "negotiates TLS 1.3 with default settings" $ \tls -> do@@ -99,6 +132,35 @@             let version = TLS.infoVersion <$> info             TLS.bye ctx             pure version++{- | Helper: Start server and report the ALPN protocol the client settles on.++Takes the Warp 'Settings' explicitly, because the advertised protocol list is+derived from them.+-}+withTestServerGetALPN :: S2nTls -> TLSSettings -> Settings -> [ByteString] -> IO (Maybe ByteString)+withTestServerGetALPN tls tlsSet baseWarpSet clientProtos =+    bracket bindFreePort (Socket.close . fst) $ \(sock, port) -> do+        serverReady <- newEmptyTMVarIO+        let app _ respond = respond $ responseLBS status200 [] "blarg!"+            warpSet = setBeforeMainLoop (atomically $ putTMVar serverReady ()) baseWarpSet+        withAsync (runTLSSocketLib tls tlsSet warpSet sock app) $ \as -> do+            atomically $ waitSTM as <|> takeTMVar serverReady+            threadDelay 10_000+            backend <- makeClientSocket port+            params <- makeClientParams [TLS.TLS13, TLS.TLS12]+            let alpnParams =+                    params+                        { TLS.clientHooks =+                            (TLS.clientHooks params)+                                { TLS.onSuggestALPN = pure (Just clientProtos)+                                }+                        }+            ctx <- TLS.contextNew backend alpnParams+            TLS.handshake ctx+            proto <- TLS.getNegotiatedProtocol ctx+            TLS.bye ctx+            pure proto  -- | Helper: Start server and test if connection succeeds withTestServerConnect :: S2nTls -> TLSSettings -> IO Bool
warp-s2n-tls.cabal view
@@ -1,6 +1,6 @@ cabal-version:      3.0 name:               warp-s2n-tls-version:            0.1.0.0+version:            0.1.1.0 synopsis:           TLS support for Warp via s2n-tls description:     This package provides TLS support for the Warp web server using@@ -28,7 +28,7 @@ source-repository this     type:     git     location: https://github.com/goertzenator/warp-s2n-tls.git-    tag:      v0.1.0.0+    tag:      v0.1.1.0  library     exposed-modules:    Network.Wai.Handler.WarpS2N