diff --git a/CHANGELOG b/CHANGELOG index 09d75769af..76ce9410b1 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ git-annex (10.20250631) UNRELEASED; urgency=medium * p2phttp: Scan multilevel directories with --directory. + * p2phttp: Added --socket option. -- Joey Hess Mon, 07 Jul 2025 15:59:42 -0400 diff --git a/Command/P2PHttp.hs b/Command/P2PHttp.hs index 3d61c213ef..1463ec5dda 100644 --- a/Command/P2PHttp.hs +++ b/Command/P2PHttp.hs @@ -22,11 +22,13 @@ import qualified Git.Construct import qualified Annex import Types.Concurrency import qualified Utility.RawFilePath as R +import Utility.FileMode import Servant import qualified Network.Wai.Handler.Warp as Warp import qualified Network.Wai.Handler.WarpTLS as Warp import Network.Socket (PortNumber) +import qualified Network.Socket as Socket import System.PosixCompat.Files (isSymbolicLink) import qualified Data.Map as M import Data.String @@ -42,6 +44,7 @@ cmd = noMessages $ dontCheck repoExists $ data Options = Options { portOption :: Maybe PortNumber , bindOption :: Maybe String + , socketOption :: Maybe FilePath , certFileOption :: Maybe FilePath , privateKeyFileOption :: Maybe FilePath , chainFileOption :: [FilePath] @@ -67,6 +70,10 @@ optParser _ = Options ( long "bind" <> metavar paramAddress <> help "specify address to bind to" )) + <*> optional (strOption + ( long "socket" <> metavar paramPath + <> help "bind to unix domain socket" + )) <*> optional (strOption ( long "certfile" <> metavar paramFile <> help "TLS certificate file for HTTPS" @@ -174,12 +181,20 @@ runServer o mst = go `finally` serverShutdownCleanup mst let settings = Warp.setPort port $ Warp.setHost host $ Warp.defaultSettings mstv <- newTMVarIO mst + let app = p2pHttpApp mstv case (certFileOption o, privateKeyFileOption o) of - (Nothing, Nothing) -> Warp.runSettings settings (p2pHttpApp mstv) - (Just certfile, Just privatekeyfile) -> do - let tlssettings = Warp.tlsSettingsChain - certfile (chainFileOption o) privatekeyfile - Warp.runTLS tlssettings settings (p2pHttpApp mstv) + (Nothing, Nothing) -> case socketOption o of + Nothing -> Warp.runSettings settings app + Just socketpath -> + withsocket socketpath $ \sock -> + Warp.runSettingsSocket settings sock app + (Just certfile, Just privatekeyfile) -> + case socketOption o of + Nothing -> do + let tlssettings = Warp.tlsSettingsChain + certfile (chainFileOption o) privatekeyfile + Warp.runTLS tlssettings settings app + Just _socketpath -> giveup "HTTPS is not supported with --socket" _ -> giveup "You must use both --certfile and --privatekeyfile options to enable HTTPS." port = maybe (fromIntegral defaultP2PHttpProtocolPort) @@ -189,6 +204,13 @@ runServer o mst = go `finally` serverShutdownCleanup mst (fromString "*") -- both ipv4 and ipv6 fromString (bindOption o) + withsocket socketpath = + bracket (opensocket socketpath) Socket.close + opensocket socketpath = protectedOutput $ do + sock <- Socket.socket Socket.AF_UNIX Socket.Stream 0 + Socket.bind sock $ Socket.SockAddrUnix socketpath + Socket.listen sock Socket.maxListenQueue + return sock mkServerState :: Options -> M.Map Auth P2P.ServerMode -> Annex P2PHttpServerState mkServerState o authenv = diff --git a/doc/git-annex-p2phttp.mdwn b/doc/git-annex-p2phttp.mdwn index c712721fb1..a7ecd581d8 100644 --- a/doc/git-annex-p2phttp.mdwn +++ b/doc/git-annex-p2phttp.mdwn @@ -103,6 +103,12 @@ convenient way to download the content of any key, by using the path What address to bind to. The default is to bind to all addresses. +* `--socket=path` + + Rather than binding to an address, create and listen to a unix domain + socket at the specified location. This can be useful when proxying + to `git-annex p2phttp`. + * `--certfile=filename` TLS certificate file to use. Combining this with `--privatekeyfile` diff --git a/doc/todo/p2phttp__58___listen_on_unix_domain_sockets.mdwn b/doc/todo/p2phttp__58___listen_on_unix_domain_sockets.mdwn index d61a8a5402..58a79fe2f4 100644 --- a/doc/todo/p2phttp__58___listen_on_unix_domain_sockets.mdwn +++ b/doc/todo/p2phttp__58___listen_on_unix_domain_sockets.mdwn @@ -1,3 +1,5 @@ For p2phttp support in forgejo-aneksajo I decided to just spawn a `git annex p2phttp --wideopen` server, do authentication on the Forgejo side, and then proxy requests to p2phttp. Since p2phttp only supports serving one repository at the moment this means that I have to allocate one free port per repository. Actually finding a free port adds complexity and a race condition, as there also seems to be no way to set `--port 0` for p2phttp and then figure out which port it bound to. This would be simplified if p2phttp could listen on unix domain sockets instead. + +> [[done]] --[[Joey]] diff --git a/doc/todo/p2phttp__58___listen_on_unix_domain_sockets/comment_3_eadeac1803ffc89e7684df508765e561._comment b/doc/todo/p2phttp__58___listen_on_unix_domain_sockets/comment_3_eadeac1803ffc89e7684df508765e561._comment new file mode 100644 index 0000000000..b596538988 --- /dev/null +++ b/doc/todo/p2phttp__58___listen_on_unix_domain_sockets/comment_3_eadeac1803ffc89e7684df508765e561._comment @@ -0,0 +1,9 @@ +[[!comment format=mdwn + username="joey" + subject="""comment 3""" + date="2025-07-07T19:26:56Z" + content=""" +I've made it support nested directories, which was easy. + +Should be possible to make it use runSettingsSocket indeed though. +"""]] diff --git a/doc/todo/p2phttp__58___listen_on_unix_domain_sockets/comment_4_3d8584bfb819f3c6ece5ecdec3d9c020._comment b/doc/todo/p2phttp__58___listen_on_unix_domain_sockets/comment_4_3d8584bfb819f3c6ece5ecdec3d9c020._comment new file mode 100644 index 0000000000..43e4f2ef3a --- /dev/null +++ b/doc/todo/p2phttp__58___listen_on_unix_domain_sockets/comment_4_3d8584bfb819f3c6ece5ecdec3d9c020._comment @@ -0,0 +1,12 @@ +[[!comment format=mdwn + username="joey" + subject="""comment 4""" + date="2025-07-07T20:28:38Z" + content=""" +Implemented a --socket option. I have not tried connecting to it as a +client, but it seems to be listening to it, so I assume all is good. + +Note that it still checks for authentication when using the socket, +so you will probably want to combine it with --wideopen. The socket mode +allows only the current user to access it. +"""]]