packages feed

wai-handler-hal 0.4.0.0 → 0.4.0.1

raw patch · 3 files changed

+92/−75 lines, 3 filesdep ~basedep ~networkPVP ok

version bump matches the API change (PVP)

Dependency ranges changed: base, network

API changes (from Hackage documentation)

Files

CHANGELOG.md view
@@ -1,5 +1,12 @@ # Revision history for wai-handler-hal +## 0.4.0.1 -- 2025-02-19++- Use a `NonEmpty` list when consuming the result of `getAddrInfo`.+- When resolving source IPs, do not require `AF_INET` (IPv4)+  addresses. This allows IPv6 source addresses to be passed through to+  the underlying `wai` `Application`.+ ## 0.4.0.0 -- 2024-01-17  - New function: `Wai.Handler.Hal.runWithOptions :: Options ->
src/Network/Wai/Handler/Hal.hs view
@@ -52,21 +52,23 @@ import Control.Exception (IOException, tryJust) import Control.Monad.IO.Class (MonadIO (..)) import Data.ByteString (ByteString)-import qualified Data.ByteString as B-import qualified Data.ByteString.Base64 as B64+import qualified Data.ByteString as ByteString+import qualified Data.ByteString.Base64 as Base64 import qualified Data.ByteString.Builder as Builder import qualified Data.ByteString.Builder.Extra as Builder-import qualified Data.ByteString.Lazy as BL+import qualified Data.ByteString.Lazy as LByteString import qualified Data.CaseInsensitive as CI import Data.Function ((&))+import Data.Functor ((<&>)) import Data.HashMap.Lazy (HashMap)-import qualified Data.HashMap.Lazy as H+import qualified Data.HashMap.Lazy as HashMap import qualified Data.IORef as IORef import Data.List (foldl', sort)+import Data.List.NonEmpty (NonEmpty (..)) import Data.Maybe (fromMaybe) import Data.Text (Text)-import qualified Data.Text as T-import qualified Data.Text.Encoding as T+import qualified Data.Text as Text+import qualified Data.Text.Encoding as Text import Data.Vault.Lazy (Key, Vault) import qualified Data.Vault.Lazy as Vault import Network.HTTP.Media (MediaType, matches, parseAccept, renderHeader)@@ -149,7 +151,7 @@  -- | A variant of 'run' with configurable 'Options'. Useful if you -- just want to override the 'binaryMediaTypes' setting but don't need--- the rest of 'runWithContext''s features.+-- the rest of 'runWithContext'\'s features. -- -- @since 0.4.0.0 runWithOptions ::@@ -160,12 +162,12 @@   HalRequest.ProxyRequest HalRequest.NoAuthorizer ->   m HalResponse.ProxyResponse runWithOptions opts app req = liftIO $ do-    waiReq <- toWaiRequest opts req-    responseRef <- IORef.newIORef Nothing-    Wai.ResponseReceived <- app waiReq $ \waiResp ->-      Wai.ResponseReceived <$ IORef.writeIORef responseRef (Just waiResp)-    Just waiResp <- IORef.readIORef responseRef-    fromWaiResponse opts waiResp+  waiReq <- toWaiRequest opts req+  responseRef <- IORef.newIORef Nothing+  Wai.ResponseReceived <- app waiReq $ \waiResp ->+    Wai.ResponseReceived <$ IORef.writeIORef responseRef (Just waiResp)+  Just waiResp <- IORef.readIORef responseRef+  fromWaiResponse opts waiResp  -- | Convert a WAI 'Wai.Application' into a function that can -- be run by hal's 'AWS.Lambda.Runtime.mRuntimeWithContext'. This@@ -222,16 +224,17 @@   IO Wai.Request toWaiRequest opts req = do   let port = portNumber opts-      pathSegments = T.splitOn "/" . T.dropWhile (== '/') $ HalRequest.path req-      query = sort . constructQuery $ HalRequest.multiValueQueryStringParameters req+      pathSegments =+        Text.splitOn "/" . Text.dropWhile (== '/') $ HalRequest.path req+      query =+        sort . constructQuery $ HalRequest.multiValueQueryStringParameters req       hints =         NS.defaultHints           { NS.addrFlags = [NS.AI_NUMERICHOST],-            NS.addrFamily = NS.AF_INET,             NS.addrSocketType = NS.Stream           }       sourceIp =-        T.unpack+        Text.unpack           . HalRequest.sourceIp           . HalRequest.identity           $ HalRequest.requestContext req@@ -243,7 +246,7 @@       (Just @IOException)       (NS.getAddrInfo (Just hints) (Just sourceIp) (Just $ show port))       >>= \case-        Right (s : _) -> pure $ NS.addrAddress s+        Right (s :| _) -> pure $ NS.addrAddress s         _ -> do           hPutStrLn stderr $             mconcat@@ -253,53 +256,57 @@               ]           pure . NS.SockAddrInet port $ NS.tupleToHostAddress (127, 0, 0, 1)   body <- returnChunks $ HalRequest.body req-  let waiReq =-        Wai.Request-          { Wai.requestMethod = T.encodeUtf8 $ HalRequest.httpMethod req,-            Wai.httpVersion = HttpVersion 1 1,-            Wai.rawPathInfo =-              BL.toStrict-                . Builder.toLazyByteString-                $ encodePath pathSegments [],-            Wai.rawQueryString = case query of-              [] -> ""-              _ -> renderQuery True query,-            Wai.requestHeaders =-              sort-                . foldMap-                  ( \(hName, hValues) ->-                      (CI.map T.encodeUtf8 hName,) . T.encodeUtf8 <$> hValues-                  )-                . H.toList-                $ HalRequest.multiValueHeaders req,-            Wai.isSecure = True,-            Wai.remoteHost = sourceHost,-            Wai.pathInfo = pathSegments,-            Wai.queryString = query,-            Wai.requestBody = body,-            Wai.vault = vault opts,-            Wai.requestBodyLength =-              Wai.KnownLength . fromIntegral . BL.length $ HalRequest.body req,-            Wai.requestHeaderHost = getHeader hHost req,-            Wai.requestHeaderRange = getHeader hRange req,-            Wai.requestHeaderReferer = getHeader hReferer req,-            Wai.requestHeaderUserAgent = getHeader hUserAgent req-          }-  pure waiReq+  pure+    Wai.Request+      { Wai.requestMethod = Text.encodeUtf8 $ HalRequest.httpMethod req,+        Wai.httpVersion = HttpVersion 1 1,+        Wai.rawPathInfo =+          LByteString.toStrict+            . Builder.toLazyByteString+            $ encodePath pathSegments [],+        Wai.rawQueryString = case query of+          [] -> ""+          _ -> renderQuery True query,+        Wai.requestHeaders =+          sort+            . foldMap+              ( \(hName, hValues) ->+                  hValues <&> \hValue ->+                    (CI.map Text.encodeUtf8 hName, Text.encodeUtf8 hValue)+              )+            . HashMap.toList+            $ HalRequest.multiValueHeaders req,+        Wai.isSecure = True,+        Wai.remoteHost = sourceHost,+        Wai.pathInfo = pathSegments,+        Wai.queryString = query,+        Wai.requestBody = body,+        Wai.vault = vault opts,+        Wai.requestBodyLength =+          Wai.KnownLength+            . fromIntegral+            . LByteString.length+            $ HalRequest.body req,+        Wai.requestHeaderHost = getHeader hHost req,+        Wai.requestHeaderRange = getHeader hRange req,+        Wai.requestHeaderReferer = getHeader hReferer req,+        Wai.requestHeaderUserAgent = getHeader hUserAgent req+      } --- | Unpack a lazy 'BL.ByteString' into its chunks, and return an IO--- action which returns each chunk in sequence, and returns 'B.empty'--- forever after the bytestring is exhausted.-returnChunks :: BL.ByteString -> IO (IO B.ByteString)+-- | Unpack a lazy 'LByteString.ByteString' into its chunks, and+-- return an IO action which returns each chunk in sequence, and+-- returns 'ByteString.empty' forever after the bytestring is+-- exhausted.+returnChunks :: LByteString.ByteString -> IO (IO ByteString.ByteString) returnChunks bs = do-  chunkRef <- IORef.newIORef $ BL.toChunks bs+  chunkRef <- IORef.newIORef $ LByteString.toChunks bs   pure . IORef.atomicModifyIORef' chunkRef $     \case       [] -> mempty       (ch : chs) -> (chs, ch)  constructQuery :: HashMap Text [Text] -> Query-constructQuery = foldMap expandParamList . H.toList+constructQuery = foldMap expandParamList . HashMap.toList   where     expandParamList :: (Text, [Text]) -> [QueryItem]     expandParamList (param, values) =@@ -309,7 +316,9 @@  getHeader :: HeaderName -> HalRequest.ProxyRequest a -> Maybe ByteString getHeader h =-  fmap T.encodeUtf8 . H.lookup (CI.map T.decodeUtf8 h) . HalRequest.headers+  fmap Text.encodeUtf8+    . HashMap.lookup (CI.map Text.decodeUtf8 h)+    . HalRequest.headers  -- | Convert a WAI 'Wai.Response' into a hal -- 'HalResponse.ProxyResponse'.@@ -326,7 +335,7 @@     . addHeaders headers     . HalResponse.response status     . createProxyBody opts (getContentType headers)-    . BL.toStrict+    . LByteString.toStrict     $ Builder.toLazyByteString builder fromWaiResponse opts (Wai.ResponseStream status headers stream) = do   builderRef <- IORef.newIORef mempty@@ -340,19 +349,19 @@ readFilePart :: FilePath -> Maybe Wai.FilePart -> IO ByteString readFilePart path mPart = withFile path ReadMode $ \h -> do   case mPart of-    Nothing -> B.hGetContents h+    Nothing -> ByteString.hGetContents h     Just (Wai.FilePart offset count _) -> do       hSeek h AbsoluteSeek offset-      B.hGet h $ fromIntegral count+      ByteString.hGet h $ fromIntegral count  createProxyBody :: Options -> MediaType -> ByteString -> HalResponse.ProxyBody createProxyBody opts contentType body =   HalResponse.ProxyBody-    { HalResponse.contentType = T.decodeUtf8 $ renderHeader contentType,+    { HalResponse.contentType = Text.decodeUtf8 $ renderHeader contentType,       HalResponse.serialized =         if isBase64Encoded-          then T.decodeUtf8 $ B64.encode body-          else T.decodeUtf8 body,+          then Text.decodeUtf8 $ Base64.encode body+          else Text.decodeUtf8 body,       HalResponse.isBase64Encoded     }   where@@ -364,8 +373,8 @@   where     addHeader r (hName, hValue) =       HalResponse.addHeader-        (T.decodeUtf8 $ CI.original hName)-        (T.decodeUtf8 hValue)+        (Text.decodeUtf8 $ CI.original hName)+        (Text.decodeUtf8 hValue)         r  -- | Try to find the content-type of a response, given the response
wai-handler-hal.cabal view
@@ -1,13 +1,13 @@ cabal-version:      2.2 name:               wai-handler-hal-version:            0.4.0.0+version:            0.4.0.1 synopsis:           Wrap WAI applications to run on AWS Lambda description:   This library provides a function 'Network.Wai.Handler.Hal.run' to   lift a @wai@ 'Network.Wai.Application' into a function that can be   passed to @hal@'s 'AWS.Lambda.Runtime.mRuntime'. This allows you to   run applications written in mature web frameworks (e.g., @servant@)-  on AWS Lambda, as proxy integrations of API Gateway Rest APIs.+  on AWS Lambda, as proxy integrations of API Gateway REST APIs.   .   More details, including deployment advice, are available in the   repository's @README.md@.@@ -21,8 +21,7 @@ copyright:          Copyright (C) 2021, 2024 Bellroy Pty Ltd category:           AWS, Cloud build-type:         Simple-extra-doc-files:-  CHANGELOG.md+extra-doc-files:    CHANGELOG.md extra-source-files:   README.md   test/data/ProxyRequest.json@@ -35,7 +34,9 @@    || ==9.0.2    || ==9.2.4    || ==9.4.5-   || ==9.6.2+   || ==9.6.6+   || ==9.8.2+   || ==9.10.1  common opts   default-language: Haskell2010@@ -48,15 +49,15 @@  common deps   build-depends:-    , base                  >=4.12      && <4.19+    , base                  >=4.12      && <4.21     , base64-bytestring     >=1.0.0.0   && <1.3-    , bytestring            >=0.10.8    && <0.12.1+    , bytestring            >=0.10.8    && <0.13     , case-insensitive      ^>=1.2.0.0     , hal                   >=0.4.7     && <0.4.11 || >=1.0.0 && <1.2     , http-media            ^>=0.8.1.1     , http-types            ^>=0.12.3-    , network               >=2.8.0.0   && <3.2-    , text                  ^>=1.2.3    || >=2.0   && <2.1.1+    , network               >=3.2.3.0   && <3.3+    , text                  ^>=1.2.3    || ^>=2.0  || ^>=2.1     , unordered-containers  ^>=0.2.10.0     , vault                 ^>=0.3.1.0     , wai                   ^>=3.2.2