hprox 0.1.0.2 → 0.1.1
raw patch · 4 files changed
+29/−63 lines, 4 filesdep +http-client-tls
Dependencies added: http-client-tls
Files
- README.md +3/−2
- hprox.cabal +5/−3
- src/HProx.hs +6/−48
- src/Main.hs +15/−10
README.md view
@@ -12,7 +12,7 @@ * Basic HTTP proxy functionality. * Simple password authentication.-* TLS encryption (requires a valid certificate). Supports TLS 1.3 and HTTP 2, also known as SPDY Proxy.+* TLS encryption (requires a valid certificate). Supports TLS 1.3 and HTTP/2, also known as SPDY Proxy. * TLS SNI validation (blocks all clients with invalid domain name). * Provide PAC file for easy client side configuration (supports Chrome and Firefox). * Websocket redirection (compatible with [v2ray-plugin](https://github.com/shadowsocks/v2ray-plugin)).@@ -32,6 +32,8 @@ stack install ``` +Alternatively, you also can use the statically linked binary for the [latest release](https://github.com/bjin/hprox/releases).+ ### Usage Use `hprox --help` to list options with detailed explanation.@@ -62,7 +64,6 @@ ### Known Issue -* Only HTTP servers are supported as websocket and reverse proxy redirection destination. * Passwords are currently stored in plain text, please set permission accordingly and avoid using existing password.
hprox.cabal view
@@ -1,13 +1,13 @@ cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.31.1.+-- This file has been generated from package.yaml by hpack version 0.31.2. -- -- see: https://github.com/sol/hpack ----- hash: 7456eded738e088a6f5195326db8a89c8149a54e92f46ed9962c9be01f73d762+-- hash: 7b3965e17f09091c9d4a3bdedd075b5d746f3d7c24fc907dde01a2de3168dc85 name: hprox-version: 0.1.0.2+version: 0.1.1 synopsis: a lightweight HTTP proxy server, and more description: Please see the README on GitHub at <https://github.com/bjin/hprox#readme> category: Web@@ -35,6 +35,7 @@ main-is: Main.hs other-modules: HProx+ Paths_hprox hs-source-dirs: src ghc-options: -Wall -O2 -threaded -rtsopts -with-rtsopts=-N@@ -48,6 +49,7 @@ , conduit >=1.3 , conduit-extra >=1.3 , http-client >=0.5+ , http-client-tls >=0.3.4 , http-reverse-proxy >=0.4.0 , http-types >=0.12 , optparse-applicative >=0.14
src/HProx.hs view
@@ -93,54 +93,10 @@ fromMaybe (hostPort, defaultPort) $ parseHostPort hostPort isProxyHeader :: HT.HeaderName -> Bool-isProxyHeader k- | BS.length bs <= 4 = False- | c0 /= 112 && c0 /= 80 = False -- 'p'- | c1 /= 114 && c1 /= 82 = False -- 'r'- | c2 /= 111 && c2 /= 79 = False -- 'o'- | c3 /= 120 && c3 /= 88 = False -- 'x'- | c4 /= 121 && c4 /= 89 = False -- 'y'- | otherwise = True- where- bs = CI.original k- idx = BS.index bs-- c0 = idx 0- c1 = idx 1- c2 = idx 2- c3 = idx 3- c4 = idx 4+isProxyHeader k = "proxy" `BS.isPrefixOf` CI.foldedCase k isForwardedHeader :: HT.HeaderName -> Bool-isForwardedHeader k- | BS.length bs <= 10 = False- | c0 /= 120 && c0 /= 88 = False -- 'x'- | c1 /= 45 = False -- '-'- | c2 /= 102 && c2 /= 70 = False -- 'f'- | c3 /= 111 && c3 /= 79 = False -- 'o'- | c4 /= 114 && c4 /= 82 = False -- 'r'- | c5 /= 119 && c5 /= 87 = False -- 'w'- | c6 /= 97 && c6 /= 65 = False -- 'a'- | c7 /= 114 && c7 /= 82 = False -- 'r'- | c8 /= 100 && c8 /= 68 = False -- 'd'- | c9 /= 101 && c9 /= 69 = False -- 'e'- | ca /= 100 && ca /= 68 = False -- 'd'- | otherwise = True- where- bs = CI.original k- idx = BS.index bs-- c0 = idx 0- c1 = idx 1- c2 = idx 2- c3 = idx 3- c4 = idx 4- c5 = idx 5- c6 = idx 6- c7 = idx 7- c8 = idx 8- c9 = idx 9- ca = idx 10+isForwardedHeader k = "x-forwarded" `BS.isPrefixOf` CI.foldedCase k isToStripHeader :: HT.HeaderName -> Bool isToStripHeader h = isProxyHeader h || isForwardedHeader h || h == "X-Real-IP" || h == "X-Scheme"@@ -193,8 +149,9 @@ isReverseProxy = isJust (revRemote pset) (revHost, revPort) = parseHostPortWithDefault 80 (fromJust (revRemote pset))+ revWrapper = if revPort == 443 then WPRModifiedRequestSecure else WPRModifiedRequest - proxyResponseFor req = WPRModifiedRequest nreq (ProxyDest revHost revPort)+ proxyResponseFor req = revWrapper nreq (ProxyDest revHost revPort) where nreq = req { requestHeaders = hdrs@@ -212,7 +169,7 @@ settings = defaultWaiProxySettings { wpsSetIpHeader = SIHNone } proxyResponseFor req- | redirectWebsocket = WPRProxyDest (ProxyDest wsHost wsPort)+ | redirectWebsocket = wsWrapper (ProxyDest wsHost wsPort) | not isGetProxy = WPRApplication fallback | checkAuth pset req = WPRModifiedRequest nreq (ProxyDest host port) | otherwise = WPRResponse (proxyAuthRequiredResponse pset)@@ -220,6 +177,7 @@ isWebsocket = wpsUpgradeToRaw defaultWaiProxySettings req redirectWebsocket = isWebsocket && isJust (wsRemote pset) (wsHost, wsPort) = parseHostPortWithDefault 80 (fromJust (wsRemote pset))+ wsWrapper = if wsPort == 443 then WPRProxyDestSecure else WPRProxyDest notCONNECT = requestMethod req /= "CONNECT" rawPath = rawPathInfo req
src/Main.hs view
@@ -7,7 +7,7 @@ import qualified Data.ByteString.Char8 as BS8 import Data.String (fromString)-import qualified Network.HTTP.Client as HC+import Network.HTTP.Client.TLS (newTlsManager) import Network.TLS as TLS import Network.Wai.Handler.Warp (HostPreference, defaultSettings, runSettings, setBeforeMainLoop,@@ -22,10 +22,12 @@ import Data.Maybe import Data.Monoid ((<>))+import Data.Version (showVersion) import Options.Applicative import HProx (ProxySettings (..), dumbApp, forceSSL, httpProxy, reverseProxy)+import Paths_hprox (version) data Opts = Opts { _bind :: Maybe HostPreference@@ -52,12 +54,15 @@ | otherwise = let y:ys = splitBy c xs in (x:y):ys parser :: ParserInfo Opts-parser = info (helper <*> opts) fullDesc+parser = info (helper <*> ver <*> opts) (fullDesc <> progDesc desc) where parseSSL s = case splitBy ':' s of [host, cert, key] -> Right (host, CertFile cert key) _ -> Left "invalid format for ssl certificates" + desc = "a lightweight HTTP proxy server, and more"+ ver = infoOption (showVersion version) (long "version" <> help "show version")+ opts = Opts <$> bind <*> (fromMaybe 3000 <$> port) <*> ssl@@ -70,7 +75,7 @@ ( long "bind" <> short 'b' <> metavar "bind_ip"- <> help "the ip address to bind on (default: all interfaces)")+ <> help "ip address to bind on (default: all interfaces)") port = optional $ option auto ( long "port"@@ -82,7 +87,7 @@ ( long "tls" <> short 's' <> metavar "hostname:cerfile:keyfile"- <> help "enable TLS and specify a domain and associated TLS certificate (can be used multiple times for multiple domains)")+ <> help "enable TLS and specify a domain and associated TLS certificate (can be specified multiple times for multiple domains)") user = optional $ strOption ( long "user"@@ -94,17 +99,17 @@ ( long "auth" <> short 'a' <> metavar "userpass.txt"- <> help "password file for proxy authentication (plain text file with lines each containaing a colon separated user/password pair)")+ <> help "password file for proxy authentication (plain text file with lines each containing a colon separated user/password pair)") ws = optional $ strOption ( long "ws"- <> metavar "remote-host:80"- <> help "remote host to handle websocket requests (http server only)")+ <> metavar "remote-host:port"+ <> help "remote host to handle websocket requests (port 443 indicates HTTPS remote server)") rev = optional $ strOption ( long "rev"- <> metavar "remote-host:80"- <> help "remote host for revere proxy (http server only)")+ <> metavar "remote-host:port"+ <> help "remote host for reverse proxy (port 443 indicates HTTPS remote server)") setuid :: String -> IO ()@@ -144,7 +149,7 @@ pauth <- case _auth opts of Nothing -> return Nothing Just f -> Just . flip elem . filter (isJust . BS8.elemIndex ':') . BS8.lines <$> BS8.readFile f- manager <- HC.newManager HC.defaultManagerSettings+ manager <- newTlsManager let pset = ProxySettings pauth Nothing (BS8.pack <$> _ws opts) (BS8.pack <$> _rev opts) proxy = (if isSSL then forceSSL else id) $ gzip def $ httpProxy pset manager $ reverseProxy pset manager dumbApp