http-reverse-proxy 0.6.1.0 → 0.6.2.0
raw patch · 4 files changed
+103/−58 lines, 4 filesdep ~blaze-builderdep ~bytestringdep ~case-insensitivenew-uploaderPVP ok
version bump matches the API change (PVP)
Dependency ranges changed: blaze-builder, bytestring, case-insensitive, conduit, conduit-extra, containers, http-client, http-types, network, resourcet, streaming-commons, text, transformers, unliftio, wai, wai-logger, word8
API changes (from Hackage documentation)
Files
- ChangeLog.md +3/−0
- Network/HTTP/ReverseProxy.hs +19/−2
- http-reverse-proxy.cabal +66/−56
- test/main.hs +15/−0
ChangeLog.md view
@@ -1,3 +1,6 @@+## 0.6.2.0++# Changes internal handling of the `X-Forwarded-For` header (avoids `Text` round-trips, tolerates invalid UTF-8) [#49](https://github.com/fpco/http-reverse-proxy/pull/49) ## 0.6.1.0
Network/HTTP/ReverseProxy.hs view
@@ -6,6 +6,7 @@ {-# LANGUAGE LambdaCase #-} {-# LANGUAGE TupleSections #-} {-# LANGUAGE CPP #-}+ module Network.HTTP.ReverseProxy ( -- * Types ProxyDest (..)@@ -63,7 +64,6 @@ import qualified Data.Text.Lazy as TL import qualified Data.Text.Lazy.Encoding as TLE import qualified Data.Text as T-import qualified Data.Text.Encoding as TE import Data.Word8 (isSpace, _colon, _cr) import GHC.Generics (Generic) import Network.HTTP.Client (BodyReader, brRead)@@ -79,6 +79,23 @@ , pdPort :: !Int } deriving (Read, Show, Eq, Ord, Generic) +-- | Trim optional whitespace (OWS) from both ends of a strict 'ByteString'.++-- Per RFC 7230 §3.2.3, OWS = SP / HTAB. This function removes only leading+-- and trailing space (0x20) and horizontal tab (0x09) bytes. It does not+-- modify internal whitespace.++-- On newer @bytestring@ versions (those that provide+-- 'Data.ByteString.Char8.strip') this is a thin wrapper around that+-- function; on older versions we provide a compatible fallback.+trimOWS :: S8.ByteString -> S8.ByteString+#if MIN_VERSION_bytestring(0,12,0)+trimOWS = S8.strip+#else+trimOWS = S8.reverse . S8.dropWhile isOWS . S8.reverse . S8.dropWhile isOWS+ where isOWS c = c == ' ' || c == '\t'+#endif+ -- | Set up a reverse proxy server, which will have a minimal overhead. -- -- This function uses raw sockets, parsing as little of the request as@@ -362,7 +379,7 @@ fromSocket = (("X-Real-IP", S8.pack $ showSockAddr $ WAI.remoteHost req):) fromForwardedFor = do h <- lookup "x-forwarded-for" (WAI.requestHeaders req)- listToMaybe $ map (TE.encodeUtf8 . T.strip) $ T.splitOn "," $ TE.decodeUtf8 h+ listToMaybe $ map trimOWS $ S8.split ',' h addXRealIP = case wpsSetIpHeader wps of SIHFromSocket -> fromSocket
http-reverse-proxy.cabal view
@@ -1,64 +1,74 @@-name: http-reverse-proxy-version: 0.6.1.0-synopsis: Reverse proxy HTTP requests, either over raw sockets or with WAI-description: Provides a simple means of reverse-proxying HTTP requests. The raw approach uses the same technique as leveraged by keter, whereas the WAI approach performs full request/response parsing via WAI and http-conduit.-homepage: https://github.com/fpco/http-reverse-proxy-license: BSD3-license-file: LICENSE-author: Michael Snoyman-maintainer: michael@fpcomplete.com-category: Web-build-type: Simple-cabal-version: >=1.10-extra-source-files: README.md ChangeLog.md+name: http-reverse-proxy+version: 0.6.2.0+synopsis:+ Reverse proxy HTTP requests, either over raw sockets or with WAI +description:+ Provides a simple means of reverse-proxying HTTP requests. The raw approach uses the same technique as leveraged by keter, whereas the WAI approach performs full request/response parsing via WAI and http-conduit.++homepage: https://github.com/fpco/http-reverse-proxy+license: BSD3+license-file: LICENSE+author: Michael Snoyman+maintainer: michael@fpcomplete.com+category: Web+build-type: Simple+cabal-version: >=1.10+extra-source-files:+ ChangeLog.md+ README.md+ library- default-language: Haskell2010- exposed-modules: Network.HTTP.ReverseProxy- other-modules: Paths_http_reverse_proxy- if impl(ghc < 8)+ default-language: Haskell2010+ exposed-modules: Network.HTTP.ReverseProxy+ other-modules: Paths_http_reverse_proxy++ if impl(ghc <8) buildable: False- build-depends: base >= 4.11 && < 5- , text >= 0.11- , bytestring >= 0.9- , case-insensitive >= 0.4- , http-types >= 0.6- , word8 >= 0.0- , blaze-builder >= 0.3- , http-client >= 0.3- , wai >= 3.0- , network- , conduit >= 1.3- , conduit-extra- , wai-logger- , resourcet- , containers- , unliftio >= 0.2- , transformers- , streaming-commons + build-depends:+ base >=4.11 && <5+ , blaze-builder >=0.3 && <0.5+ , bytestring >=0.9 && <0.13+ , case-insensitive >=0.4 && <1.3+ , conduit >=1.3 && <1.4+ , conduit-extra <1.4+ , containers <0.9+ , http-client >=0.3 && <0.8+ , http-types >=0.6 && <0.13+ , network <3.3+ , resourcet <1.4+ , streaming-commons <0.3+ , text >=0.11 && <2.2+ , transformers <0.7+ , unliftio >=0.2 && <0.3+ , wai >=3.0 && <3.3+ , wai-logger >=2.0 && <2.6+ , word8 >=0.0 && <0.2+ test-suite test- default-language: Haskell2010- type: exitcode-stdio-1.0- main-is: main.hs- hs-source-dirs: test- build-depends: base < 10- , http-reverse-proxy- , wai- , http-types- , hspec >= 1.3- , warp >= 2.1- , bytestring- , conduit >= 1.1- , conduit-extra- , blaze-builder- , transformers- , unliftio- , network- , http-conduit >= 2.3- , resourcet- , streaming-commons+ default-language: Haskell2010+ type: exitcode-stdio-1.0+ main-is: main.hs+ hs-source-dirs: test+ build-depends:+ base <10+ , blaze-builder+ , bytestring+ , conduit >=1.1+ , conduit-extra+ , hspec >=1.3+ , http-conduit >=2.3+ , http-reverse-proxy+ , http-types+ , network+ , resourcet+ , streaming-commons+ , transformers+ , unliftio+ , wai+ , warp >=2.1 source-repository head type: git- location: git://github.com/fpco/http-reverse-proxy.git+ location: https://github.com/fpco/http-reverse-proxy.git
test/main.hs view
@@ -211,6 +211,21 @@ withCApp (rawTcpProxyTo (ProxyDest "127.0.0.1" port3)) $ \port4 -> do lbs <- httpWithForwardedFor $ "http://127.0.0.1:" ++ show port4 lbs `shouldBe` "127.0.0.1"+ it "doesn't fail on invalid utf8 in x-forwarded-for header" $+ let getRealIp req = L8.fromStrict $ fromMaybe "" $ lookup "x-real-ip" (requestHeaders req)+ httpWithForwardedFor url = liftIO $ do+ man <- HC.newManager HC.tlsManagerSettings+ oreq <- liftIO $ HC.parseUrlThrow url+ let req = oreq { HC.requestHeaders = [("X-Forwarded-For", "\xbf\xf0\x9f\x92\xa1"), ("Connection", "close")] }+ HC.responseBody <$> HC.httpLbs req man+ waiProxyTo' getDest onError = waiProxyToSettings getDest defaultWaiProxySettings { wpsOnExc = onError, wpsSetIpHeader = SIHFromHeader }+ in withMan $ \manager ->+ withWApp (\r f -> f $ responseLBS status200 [] $ getRealIp r ) $ \port1 ->+ withWApp (waiProxyTo' (const $ return $ WPRProxyDest $ ProxyDest "127.0.0.1" port1) defaultOnExc manager) $ \port2 ->+ withCApp (rawProxyTo (const $ return $ Right $ ProxyDest "127.0.0.1" port2)) $ \port3 ->+ withCApp (rawTcpProxyTo (ProxyDest "127.0.0.1" port3)) $ \port4 -> do+ lbs <- httpWithForwardedFor $ "http://127.0.0.1:" ++ show port4+ lbs `shouldBe` "\xbf\xf0\x9f\x92\xa1" it "performs log action" $ let ioref :: IO (IORef Int) ioref = newIORef 1