--- /dev/null
+{- P2P protocol over HTTP, running client actions
+ -
+ - https://git-annex.branchable.com/design/p2p_protocol_over_http/
+ -
+ - Copyright 2024 Joey Hess <id@joeyh.name>
+ -
+ - Licensed under the GNU AGPL version 3 or higher.
+ -}
+
+{-# LANGUAGE CPP #-}
+
+module P2P.Http.Client where
+
+import Types
+import Annex.Url
+
+#ifdef WITH_SERVANT
+import Annex.UUID
+import Types.Remote
+import P2P.Protocol (ProtocolVersion(..))
+import P2P.Http.Types
+import P2P.Http.Url
+import Servant.Client
+#endif
+
+p2pHttpClient
+ :: Remote
+ -> (String -> Annex a)
+#ifdef WITH_SERVANT
+ -> (ClientEnv -> ProtocolVersion -> B64UUID ServerSide -> B64UUID ClientSide -> [B64UUID Bypass] -> Maybe Auth -> Annex a)
+#endif
+ -> Annex a
+#ifdef WITH_SERVANT
+p2pHttpClient rmt fallback httpaction =
+ case p2pHttpBaseUrl <$> remoteAnnexP2PHttpUrl (gitconfig rmt) of
+ Nothing -> error "internal"
+ Just baseurl -> do
+ myuuid <- getUUID
+ mgr <- httpManager <$> getUrlOptions
+ let clientenv = mkClientEnv mgr baseurl
+ -- TODO: try other protocol versions
+ -- TODO: authentication
+ -- TODO: catch 404 etc
+ httpaction clientenv
+ (ProtocolVersion 3)
+ (B64UUID (uuid rmt))
+ (B64UUID myuuid)
+ []
+ Nothing
+#else
+runP2PHttpClient rmt fallback = fallback "This remote uses an annex+http url, but this version of git-annex is not build with support for that."
+#endif
import qualified Remote.Helper.P2P as P2PHelper
import P2P.Address
import P2P.Http.Url
+import P2P.Http.Client
+#ifdef WITH_SERVANT
+import P2P.Http
+#endif
import Annex.Path
import Creds
import Types.NumCopies
proxied <- listProxied proxies rs'
return (proxied++rs')
where
- annexurl r = remoteConfig r "annexurl"
tweakurl c r = do
let n = fromJust $ Git.remoteName r
- case M.lookup (annexurl r) c of
- Just url | not (isP2PHttpProtocolUrl (Git.fromConfigValue url)) ->
+ case getAnnexUrl r c of
+ Just url | not (isP2PHttpProtocolUrl url) ->
inRepo $ \g -> Git.Construct.remoteNamed n $
- Git.Construct.fromRemoteLocation
- (Git.fromConfigValue url)
+ Git.Construct.fromRemoteLocation url
False g
_ -> return r
+getAnnexUrl :: Git.Repo -> M.Map Git.ConfigKey Git.ConfigValue -> Maybe String
+getAnnexUrl r c = Git.fromConfigValue <$> M.lookup (annexUrlConfigKey r) c
+
+annexUrlConfigKey :: Git.Repo -> Git.ConfigKey
+annexUrlConfigKey r = remoteConfig r "annexurl"
+
isGitRemoteAnnex :: Git.Repo -> Bool
isGitRemoteAnnex r = "annex::" `isPrefixOf` Git.repoLocation r
- done each time git-annex is run in a way that uses remotes, unless
- annex-checkuuid is false.
-
- - Conversely, the config of an URL remote is only read when there is no
- - cached UUID value. -}
+ - An annex+http remote's UUID is part of the url,
+ - so the config does not have to be read, but it is verified that
+ - it matches the cached UUID.
+ -
+ - The config of other URL remotes is only read when there is no
+ - cached UUID value.
+ -}
configRead :: Bool -> Git.Repo -> Annex Git.Repo
configRead autoinit r = do
gc <- Annex.getRemoteGitConfig r
hasuuid <- (/= NoUUID) <$> getRepoUUID r
annexignore <- liftIO $ getDynamicConfig (remoteAnnexIgnore gc)
- case (repoCheap r, annexignore, hasuuid) of
- (_, True, _) -> return r
- (True, _, _)
+ c <- fromRepo Git.config
+ case (repoCheap r, annexignore, hasuuid, p2pHttpUUID =<< parseP2PHttpUrl =<< getAnnexUrl r c) of
+ (_, True, _, _) -> return r
+ (True, _, _, _)
| remoteAnnexCheckUUID gc -> tryGitConfigRead autoinit r hasuuid
| otherwise -> return r
- (False, _, False) -> configSpecialGitRemotes r >>= \case
+ (_, _, _, Just p2phttpuuid) -> getRepoUUID r >>= \case
+ u@(UUID {})
+ | u == p2phttpuuid -> return r
+ | otherwise -> do
+ warning $ UnquotedString $ unwords
+ [ "Repository", Git.repoDescribe r
+ , "has different UUIDS in"
+ , Git.fromConfigKey (annexUrlConfigKey r)
+ , "and"
+ , Git.fromConfigKey (configRepoUUID r)
+ ]
+ return r
+ NoUUID -> storeUpdatedRemote $
+ liftIO $ setUUID r p2phttpuuid
+ (False, _, False, _) -> configSpecialGitRemotes r >>= \case
Nothing -> tryGitConfigRead autoinit r False
Just r' -> return r'
_ -> return r
-
gen :: Git.Repo -> UUID -> RemoteConfig -> RemoteGitConfig -> RemoteStateHandle -> Annex (Maybe Remote)
gen r u rc gc rs
-- Remote.GitLFS may be used with a repo that is also encrypted
inAnnex' :: Git.Repo -> Remote -> State -> Key -> Annex Bool
inAnnex' repo rmt st@(State connpool duc _ _ _) key
+ | isP2PHttp rmt = checkp2phttp
| Git.repoIsHttp repo = checkhttp
| Git.repoIsUrl repo = checkremote
| otherwise = checklocal
where
+ checkp2phttp = p2pHttpClient rmt giveup
+#ifdef WITH_SERVANT
+ (clientCheckPresent key)
+#endif
checkhttp = do
gc <- Annex.getGitConfig
Url.withUrlOptionsPromptingCreds $ \uo ->
| Git.GCrypt.isEncrypted r = False
| Git.repoIsLocal r || Git.repoIsLocalUnknown r = False
| otherwise = isNothing (repoP2PAddress r)
+
+isP2PHttp :: Remote -> Bool
+isP2PHttp = isJust . remoteAnnexP2PHttpUrl . gitconfig
+