hprox 0.5.2 → 0.5.3
raw patch · 5 files changed
+57/−28 lines, 5 filesPVP: major bump suggested
API removals or changes: PVP suggests a major version bump
API changes (from Hackage documentation)
+ Network.HProx: [_hide] :: Config -> Bool
- Network.HProx: Config :: Maybe String -> Int -> [(String, CertFile)] -> Maybe FilePath -> Maybe String -> Maybe String -> Maybe String -> Bool -> ByteString -> String -> LogLevel -> Config
+ Network.HProx: Config :: Maybe String -> Int -> [(String, CertFile)] -> Maybe FilePath -> Maybe String -> Maybe String -> Maybe String -> Bool -> Bool -> ByteString -> String -> LogLevel -> Config
Files
- Changelog.md +7/−0
- README.md +3/−1
- hprox.cabal +2/−2
- src/Network/HProx.hs +11/−7
- src/Network/HProx/Impl.hs +34/−18
Changelog.md view
@@ -1,3 +1,10 @@+## 0.5.3++- add macos-aarch64 build+- add `--hide` option for probe resistance+- gracefully close stream for HTTP CONNECT+- `gzip` encoding middleware removed+ ## 0.5.2 - add Windows build
README.md view
@@ -1,6 +1,7 @@ ## hprox [](https://circleci.com/gh/bjin/hprox)+[](https://cirrus-ci.com/github/bjin/hprox) [](https://packdeps.haskellers.com/feed?needle=hprox) [](https://github.com/bjin/hprox/releases) [](https://hackage.haskell.org/package/hprox)@@ -76,7 +77,8 @@ * Passwords are currently stored in plain text, please set permission accordingly and avoid using existing password.-* HTTP/3 currently only works on the first domain as specified by `-s/--tls`.+* HTTP/3 currently only works on the first domain as specified by `-s/--tls`, also SNI+ validation is unavailable as well. ### License
hprox.cabal view
@@ -1,11 +1,11 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.35.1.+-- This file has been generated from package.yaml by hpack version 0.35.2. -- -- see: https://github.com/sol/hpack name: hprox-version: 0.5.2+version: 0.5.3 synopsis: a lightweight HTTP proxy server, and more description: Please see the README on GitHub at <https://github.com/bjin/hprox#readme> category: Web
src/Network/HProx.hs view
@@ -68,6 +68,7 @@ , _ws :: Maybe String , _rev :: Maybe String , _doh :: Maybe String+ , _hide :: Bool , _naive :: Bool , _name :: BS8.ByteString , _log :: String@@ -79,7 +80,7 @@ -- | Default value of 'Config', same as running @hprox@ without arguments defaultConfig :: Config-defaultConfig = Config Nothing 3000 [] Nothing Nothing Nothing Nothing False "hprox" "stdout" INFO+defaultConfig = Config Nothing 3000 [] Nothing Nothing Nothing Nothing False False "hprox" "stdout" INFO #ifdef QUIC_ENABLED Nothing #endif@@ -116,6 +117,7 @@ <*> ws <*> rev <*> doh+ <*> hide <*> naive <*> name <*> logging@@ -165,6 +167,10 @@ <> metavar "dns-server:port" <> help "enable DNS-over-HTTPS(DoH) support (53 will be used if port is not specified)") + hide = switch+ ( long "hide"+ <> help "never send 'proxy authentication required' response (however this might break the use of HTTPS proxy in browser)")+ naive = switch ( long "naive" <> help "add naiveproxy compatible padding (requires TLS)")@@ -234,8 +240,7 @@ setLogger warpLogger $ setOnException exceptionHandler $ setNoParsePath True $- setServerName _name $- defaultSettings+ setServerName _name defaultSettings exceptionHandler req ex | _loglevel > DEBUG = return ()@@ -251,7 +256,7 @@ | Just ConnectionClosedByPeer <- fromException ex = return () | otherwise = logger DEBUG $ "exception: " <> toLogStr (displayException ex) <>- (if (isJust req) then " from: " <> logRequest (fromJust req) else "")+ (if isJust req then " from: " <> logRequest (fromJust req) else "") warpLogger req status _ | rawPathInfo req == "/.hprox/health" = return ()@@ -326,12 +331,11 @@ Just . flip elem . filter (isJust . BS8.elemIndex ':') . BS8.lines <$> BS8.readFile f manager <- newTlsManager - let pset = ProxySettings pauth (Just _name) (BS8.pack <$> _ws) (BS8.pack <$> _rev) (_naive && isSSL) logger+ let pset = ProxySettings pauth (Just _name) (BS8.pack <$> _ws) (BS8.pack <$> _rev) _hide (_naive && isSSL) logger proxy = healthCheckProvider $ (if isSSL then forceSSL pset else id) $ httpProxy pset manager $- reverseProxy pset manager $- fallback+ reverseProxy pset manager fallback when (isJust _ws) $ logger INFO $ "websocket redirect: " <> toLogStr (fromJust _ws) when (isJust _rev) $ logger INFO $ "reverse proxy: " <> toLogStr (fromJust _rev)
src/Network/HProx/Impl.hs view
@@ -15,7 +15,7 @@ ) where import Control.Applicative ((<|>))-import Control.Concurrent.Async (concurrently)+import Control.Concurrent.Async (cancel, wait, waitEither, withAsync) import Control.Exception (SomeException, try) import Control.Monad (unless, void, when) import Control.Monad.IO.Class (liftIO)@@ -35,23 +35,24 @@ wpsUpgradeToRaw) import Network.HTTP.Types qualified as HT import Network.HTTP.Types.Header qualified as HT+import System.Timeout (timeout) import Data.Conduit import Data.Maybe import Network.Wai-import Network.Wai.Middleware.Gzip import Network.Wai.Middleware.StripHeaders import Network.HProx.Log import Network.HProx.Util data ProxySettings = ProxySettings- { proxyAuth :: Maybe (BS.ByteString -> Bool)- , passPrompt :: Maybe BS.ByteString- , wsRemote :: Maybe BS.ByteString- , revRemote :: Maybe BS.ByteString- , naivePadding :: Bool- , logger :: Logger+ { proxyAuth :: Maybe (BS.ByteString -> Bool)+ , passPrompt :: Maybe BS.ByteString+ , wsRemote :: Maybe BS.ByteString+ , revRemote :: Maybe BS.ByteString+ , hideProxyAuth :: Bool+ , naivePadding :: Bool+ , logger :: Logger } logRequest :: Request -> LogStr@@ -102,12 +103,15 @@ checkAuth ProxySettings{..} req | isNothing proxyAuth = True | isNothing authRsp = False- | otherwise = fromJust proxyAuth decodedRsp+ | otherwise =+ pureLogger logger TRACE (authMsg <> " request (credential: " <> toLogStr decodedRsp <> ") from " <> toLogStr (show (remoteHost req))) authorized where authRsp = lookup HT.hProxyAuthorization (requestHeaders req)- decodedRsp = decodeLenient $ snd $ BS8.spanEnd (/=' ') $ fromJust authRsp + authorized = fromJust proxyAuth decodedRsp+ authMsg = if authorized then "authorized" else "unauthorized"+ redirectWebsocket :: ProxySettings -> Request -> Bool redirectWebsocket ProxySettings{..} req = wpsUpgradeToRaw defaultWaiProxySettings req && isJust wsRemote @@ -174,20 +178,17 @@ ] httpGetProxy :: ProxySettings -> HC.Manager -> Middleware-httpGetProxy pset@ProxySettings{..} mgr fallback = appWrapper $ waiProxyToSettings (return.proxyResponseFor) settings mgr+httpGetProxy pset@ProxySettings{..} mgr fallback = waiProxyToSettings (return.proxyResponseFor) settings mgr where settings = defaultWaiProxySettings { wpsSetIpHeader = SIHNone } - appWrapper = ifRequest isGetProxy (gzip def)-- isGetProxy req = case proxyResponseFor req of- WPRModifiedRequest _ _ -> True- _ -> False- proxyResponseFor req | redirectWebsocket pset req = wsWrapper (ProxyDest wsHost wsPort) | not isGETProxy = WPRApplication fallback | checkAuth pset req = WPRModifiedRequest nreq (ProxyDest host port)+ | hideProxyAuth =+ pureLogger logger WARN ("unauthorized request (hidden without response): " <> logRequest req) $+ WPRApplication fallback | otherwise = pureLogger logger WARN ("unauthorized request: " <> logRequest req) $ WPRResponse (proxyAuthRequiredResponse pset)@@ -224,6 +225,9 @@ httpConnectProxy pset@ProxySettings{..} fallback req respond | not isConnectProxy = fallback req respond | checkAuth pset req = respondResponse+ | hideProxyAuth = do+ logger WARN $ "unauthorized request (hidden without response): " <> logRequest req+ fallback req respond | otherwise = do logger WARN $ "unauthorized request: " <> logRequest req respond (proxyAuthRequiredResponse pset)@@ -240,6 +244,17 @@ tryAndCatchAll :: IO a -> IO (Either SomeException a) tryAndCatchAll = try + runStreams :: Int -> IO () -> IO () -> IO (Either SomeException ())+ runStreams secs left right = tryAndCatchAll $+ withAsync left $ \l -> do+ withAsync right $ \r -> do+ res1 <- waitEither l r+ let unfinished = case res1 of+ Left _ -> r+ _ -> l+ res2 <- timeout (secs * 1000000) (wait unfinished)+ when (isNothing res2) $ cancel unfinished+ respondResponse | HT.httpMajor (httpVersion req) < 2 = respond $ responseRaw (handleConnect True) backup | not naivePadding = respond $ responseStream HT.status200 [] streaming@@ -311,6 +326,7 @@ | otherwise = fromServer .| toClient in do when http1 $ runConduit $ yieldHttp1Response .| toClient- void $ tryAndCatchAll $ concurrently+ -- gracefully close the other stream after 5 seconds if one side of stream is closed.+ void $ runStreams 5 (runConduit clientToServer) (runConduit serverToClient)