mirror of
https://github.com/simonmichael/hledger.git
synced 2024-12-26 20:02:27 +03:00
web: Add option --socket to use UNIX socket file
This commit adds the --socket option to use hledger-web over an AF_UNIX socket file. It allows running multiple instances of hledger-web on the same system without having to manually choose a port for each instance, which is helpful for running individual instances for multiple users. In this scenario, the socket path is predictable, as it can be derived from the username. It also introduces the following dependencies: - network - Used to create the unix domain socket - unix-compat - Used to identify if the socket file is still a socket, to reduce the risk of deleting a file when cleaning up the socket
This commit is contained in:
parent
c7a88b50fb
commit
72acd7c22a
@ -9,15 +9,19 @@ Released under GPL version 3 or later.
|
||||
|
||||
module Hledger.Web.Main where
|
||||
|
||||
import Control.Exception (bracket)
|
||||
import Control.Monad (when)
|
||||
import Data.String (fromString)
|
||||
import qualified Data.Text as T
|
||||
import Network.Socket
|
||||
import Network.Wai (Application)
|
||||
import Network.Wai.Handler.Warp (runSettings, defaultSettings, setHost, setPort)
|
||||
import Network.Wai.Handler.Warp (runSettings, runSettingsSocket, defaultSettings, setHost, setPort)
|
||||
import Network.Wai.Handler.Launch (runHostPortUrl)
|
||||
import Prelude hiding (putStrLn)
|
||||
import System.Exit (exitSuccess)
|
||||
import System.Directory (removeFile)
|
||||
import System.Exit (exitSuccess, exitFailure)
|
||||
import System.IO (hFlush, stdout)
|
||||
import System.PosixCompat.Files (getFileStatus, isSocket)
|
||||
import Text.Printf (printf)
|
||||
import Yesod.Default.Config
|
||||
import Yesod.Default.Main (defaultDevelApp)
|
||||
@ -76,7 +80,27 @@ web opts j = do
|
||||
putStrLn "Press ctrl-c to quit"
|
||||
hFlush stdout
|
||||
let warpsettings = setHost (fromString h) (setPort p defaultSettings)
|
||||
Network.Wai.Handler.Warp.runSettings warpsettings app
|
||||
case socket_ opts of
|
||||
Just s -> do
|
||||
if isUnixDomainSocketAvailable then
|
||||
bracket
|
||||
(do
|
||||
sock <- socket AF_UNIX Stream 0
|
||||
setSocketOption sock ReuseAddr 1
|
||||
bind sock $ SockAddrUnix s
|
||||
listen sock maxListenQueue
|
||||
return sock
|
||||
)
|
||||
(\_ -> do
|
||||
sockstat <- getFileStatus s
|
||||
when (isSocket sockstat) $ removeFile s
|
||||
)
|
||||
(\sock -> Network.Wai.Handler.Warp.runSettingsSocket warpsettings sock app)
|
||||
else do
|
||||
putStrLn "Unix domain sockets are not available on your operating system"
|
||||
putStrLn "Please try again without --socket"
|
||||
exitFailure
|
||||
Nothing -> Network.Wai.Handler.Warp.runSettings warpsettings app
|
||||
else do
|
||||
putStrLn "This server will exit after 2m with no browser windows open (or press ctrl-c)"
|
||||
putStrLn "Opening web browser..."
|
||||
|
@ -43,6 +43,11 @@ webflags =
|
||||
(\s opts -> Right $ setopt "cors" s opts)
|
||||
"ORIGIN"
|
||||
("allow cross-origin requests from the specified origin; setting ORIGIN to \"*\" allows requests from any origin")
|
||||
, flagReq
|
||||
["socket"]
|
||||
(\s opts -> Right $ setopt "socket" s opts)
|
||||
"SOCKET"
|
||||
"use the given socket instead of the given IP and port (implies --serve)"
|
||||
, flagReq
|
||||
["host"]
|
||||
(\s opts -> Right $ setopt "host" s opts)
|
||||
@ -110,10 +115,11 @@ data WebOpts = WebOpts
|
||||
, capabilities_ :: [Capability]
|
||||
, capabilitiesHeader_ :: Maybe (CI ByteString)
|
||||
, cliopts_ :: CliOpts
|
||||
, socket_ :: Maybe String
|
||||
} deriving (Show)
|
||||
|
||||
defwebopts :: WebOpts
|
||||
defwebopts = WebOpts def def Nothing def def def def [CapView, CapAdd] Nothing def
|
||||
defwebopts = WebOpts def def Nothing def def def def [CapView, CapAdd] Nothing def Nothing
|
||||
|
||||
instance Default WebOpts where def = defwebopts
|
||||
|
||||
@ -131,9 +137,12 @@ rawOptsToWebOpts rawopts =
|
||||
Left e -> error' ("Unknown capability: " ++ T.unpack e)
|
||||
Right [] -> [CapView, CapAdd]
|
||||
Right xs -> xs
|
||||
sock = stripTrailingSlash <$> maybestringopt "socket" rawopts
|
||||
return
|
||||
defwebopts
|
||||
{ serve_ = boolopt "serve" rawopts
|
||||
{ serve_ = case sock of
|
||||
Just _ -> True
|
||||
Nothing -> boolopt "serve" rawopts
|
||||
, serve_api_ = boolopt "serve-api" rawopts
|
||||
, cors_ = maybestringopt "cors" rawopts
|
||||
, host_ = h
|
||||
@ -143,6 +152,7 @@ rawOptsToWebOpts rawopts =
|
||||
, capabilities_ = caps
|
||||
, capabilitiesHeader_ = mk . BC.pack <$> maybestringopt "capabilities-header" rawopts
|
||||
, cliopts_ = cliopts
|
||||
, socket_ = sock
|
||||
}
|
||||
where
|
||||
stripTrailingSlash = reverse . dropWhile (== '/') . reverse -- yesod don't like it
|
||||
|
@ -1,10 +1,10 @@
|
||||
cabal-version: 1.12
|
||||
|
||||
-- This file has been generated from package.yaml by hpack version 0.31.2.
|
||||
-- This file has been generated from package.yaml by hpack version 0.32.0.
|
||||
--
|
||||
-- see: https://github.com/sol/hpack
|
||||
--
|
||||
-- hash: 194a16a30440803cd2bef5d5df0b6115ad66076c44407ed31efea9c4602e5e95
|
||||
-- hash: a9a6dea39ea5c963970cda9f595e7c4251332954aacd137fb6638a1e7640ee59
|
||||
|
||||
name: hledger-web
|
||||
version: 1.16.99
|
||||
@ -176,12 +176,14 @@ library
|
||||
, http-types
|
||||
, megaparsec >=7.0.0 && <8
|
||||
, mtl >=2.2.1
|
||||
, network
|
||||
, semigroups
|
||||
, shakespeare >=2.0.2.2
|
||||
, template-haskell
|
||||
, text >=1.2
|
||||
, time >=1.5
|
||||
, transformers
|
||||
, unix-compat
|
||||
, utf8-string
|
||||
, wai
|
||||
, wai-cors
|
||||
|
@ -72,6 +72,10 @@ as shown in the synopsis above.
|
||||
`--port=PORT`
|
||||
: listen on this TCP port (default: 5000)
|
||||
|
||||
`--socket=SOCKETFILE`
|
||||
: use a unix domain socket file to listen for requests instead of a TCP socket. Implies
|
||||
`--serve`. It can only be used if the operating system can provide this type of socket.
|
||||
|
||||
`--base-url=URL`
|
||||
: set the base url (default: http://IPADDR:PORT).
|
||||
You would change this when sharing over the network, or integrating within a larger website.
|
||||
@ -119,6 +123,20 @@ You can use `--host` to change this, eg `--host 0.0.0.0` to listen on all config
|
||||
Similarly, use `--port` to set a TCP port other than 5000, eg if you are
|
||||
running multiple hledger-web instances.
|
||||
|
||||
Both of these options are ignored when `--socket` is used. In this case, it
|
||||
creates an `AF_UNIX` socket file at the supplied path and uses that for communication.
|
||||
This is an alternative way of running multiple hledger-web instances behind
|
||||
a reverse proxy that handles authentication for different users.
|
||||
The path can be derived in a predictable way, eg by using the username within the path.
|
||||
As an example, `nginx` as reverse proxy can use the variabel `$remote_user` to
|
||||
derive a path from the username used in a [HTTP basic authentication](https://docs.nginx.com/nginx/admin-guide/security-controls/configuring-http-basic-authentication/).
|
||||
The following `proxy_pass` directive allows access to all `hledger-web`
|
||||
instances that created a socket in `/tmp/hledger/`:
|
||||
|
||||
```
|
||||
proxy_pass http://unix:/tmp/hledger/${remote_user}.socket;
|
||||
```
|
||||
|
||||
You can use `--base-url` to change the protocol, hostname, port and path that appear in hyperlinks,
|
||||
useful eg for integrating hledger-web within a larger website.
|
||||
The default is `http://HOST:PORT/` using the server's configured host address and TCP port
|
||||
|
@ -120,12 +120,14 @@ library:
|
||||
- http-types
|
||||
- megaparsec >=7.0.0 && <8
|
||||
- mtl >=2.2.1
|
||||
- network
|
||||
- semigroups
|
||||
- shakespeare >=2.0.2.2
|
||||
- template-haskell
|
||||
- text >=1.2
|
||||
- time >=1.5
|
||||
- transformers
|
||||
- unix-compat
|
||||
- utf8-string
|
||||
- wai
|
||||
- wai-extra
|
||||
|
Loading…
Reference in New Issue
Block a user