Support checking presence of content at a http url that redirects to a ftp url.

This commit is contained in:
Joey Hess 2016-07-12 16:30:36 -04:00
parent 604565ae66
commit 79704528c0
Failed to extract signature
5 changed files with 57 additions and 21 deletions

View file

@ -18,6 +18,8 @@ git-annex (6.20160614) UNRELEASED; urgency=medium
* uninit: Fix crash due to trying to write to deleted keys db. * uninit: Fix crash due to trying to write to deleted keys db.
Reversion introduced by v6 mode support, affects v5 too. Reversion introduced by v6 mode support, affects v5 too.
* Fix a similar crash when the webapp is used to delete a repository. * Fix a similar crash when the webapp is used to delete a repository.
* Support checking presence of content at a http url that redirects to
a ftp url.
-- Joey Hess <id@joeyh.name> Mon, 13 Jun 2016 21:52:24 -0400 -- Joey Hess <id@joeyh.name> Mon, 13 Jun 2016 21:52:24 -0400

View file

@ -30,7 +30,7 @@ import Remote.Helper.Http
import qualified Remote.Helper.Chunked.Legacy as Legacy import qualified Remote.Helper.Chunked.Legacy as Legacy
import Creds import Creds
import Utility.Metered import Utility.Metered
import Utility.Url (URLString) import Utility.Url (URLString, matchStatusCodeException)
import Annex.UUID import Annex.UUID
import Remote.WebDAV.DavLocation import Remote.WebDAV.DavLocation
@ -270,12 +270,6 @@ existsDAV l = inLocation l check `catchNonAsync` (\e -> return (Left $ show e))
(const $ ispresent False) (const $ ispresent False)
ispresent = return . Right ispresent = return . Right
matchStatusCodeException :: (Status -> Bool) -> HttpException -> Maybe HttpException
matchStatusCodeException want e@(StatusCodeException s _ _)
| want s = Just e
| otherwise = Nothing
matchStatusCodeException _ _ = Nothing
-- Ignores any exceptions when performing a DAV action. -- Ignores any exceptions when performing a DAV action.
safely :: DAVT IO a -> DAVT IO (Maybe a) safely :: DAVT IO a -> DAVT IO (Maybe a)
safely = eitherToMaybe <$$> tryNonAsync safely = eitherToMaybe <$$> tryNonAsync

View file

@ -25,7 +25,8 @@ module Utility.Url (
assumeUrlExists, assumeUrlExists,
download, download,
downloadQuiet, downloadQuiet,
parseURIRelaxed parseURIRelaxed,
matchStatusCodeException,
) where ) where
import Common import Common
@ -126,6 +127,7 @@ data UrlInfo = UrlInfo
, urlSize :: Maybe Integer , urlSize :: Maybe Integer
, urlSuggestedFile :: Maybe FilePath , urlSuggestedFile :: Maybe FilePath
} }
deriving (Show)
assumeUrlExists :: UrlInfo assumeUrlExists :: UrlInfo
assumeUrlExists = UrlInfo True Nothing Nothing assumeUrlExists = UrlInfo True Nothing Nothing
@ -135,7 +137,14 @@ assumeUrlExists = UrlInfo True Nothing Nothing
getUrlInfo :: URLString -> UrlOptions -> IO UrlInfo getUrlInfo :: URLString -> UrlOptions -> IO UrlInfo
getUrlInfo url uo = case parseURIRelaxed url of getUrlInfo url uo = case parseURIRelaxed url of
Just u -> case parseUrl (show u) of Just u -> case parseUrl (show u) of
Just req -> existsconduit req `catchNonAsync` const dne Just req -> catchJust
-- When http redirects to a protocol which
-- conduit does not support, it will throw
-- a StatusCodeException with found302.
(matchStatusCodeException (== found302))
(existsconduit req)
(const (existscurl u))
`catchNonAsync` (const dne)
-- http-conduit does not support file:, ftp:, etc urls, -- http-conduit does not support file:, ftp:, etc urls,
-- so fall back to reading files and using curl. -- so fall back to reading files and using curl.
Nothing Nothing
@ -147,18 +156,7 @@ getUrlInfo url uo = case parseURIRelaxed url of
sz <- getFileSize' f stat sz <- getFileSize' f stat
found (Just sz) Nothing found (Just sz) Nothing
Nothing -> dne Nothing -> dne
| Build.SysConfig.curl -> do | Build.SysConfig.curl -> existscurl u
output <- catchDefaultIO "" $
readProcess "curl" $ toCommand curlparams
let len = extractlencurl output
let good = found len Nothing
case lastMaybe (lines output) of
Just ('2':_:_) -> good
-- don't try to parse ftp status
-- codes; if curl got a length,
-- it's good
_ | "ftp" `isInfixOf` uriScheme u && isJust len -> good
_ -> dne
| otherwise -> dne | otherwise -> dne
Nothing -> dne Nothing -> dne
where where
@ -201,6 +199,23 @@ getUrlInfo url uo = case parseURIRelaxed url of
liftIO $ closeManager mgr liftIO $ closeManager mgr
return ret return ret
existscurl u = do
output <- catchDefaultIO "" $
readProcess "curl" $ toCommand curlparams
let len = extractlencurl output
let good = found len Nothing
let isftp = or
[ "ftp" `isInfixOf` uriScheme u
-- Check to see if http redirected to ftp.
, "Location: ftp://" `isInfixOf` output
]
case lastMaybe (lines output) of
Just ('2':_:_) -> good
-- don't try to parse ftp status codes; if curl
-- got a length, it's good
_ | isftp && isJust len -> good
_ -> dne
-- Parse eg: attachment; filename="fname.ext" -- Parse eg: attachment; filename="fname.ext"
-- per RFC 2616 -- per RFC 2616
contentDispositionFilename :: String -> Maybe FilePath contentDispositionFilename :: String -> Maybe FilePath
@ -324,3 +339,13 @@ hContentLength = "Content-Length"
hUserAgent :: CI.CI B.ByteString hUserAgent :: CI.CI B.ByteString
hUserAgent = "User-Agent" hUserAgent = "User-Agent"
#endif #endif
{- Use with eg:
-
- > catchJust (matchStatusCodeException (== notFound404))
-}
matchStatusCodeException :: (Status -> Bool) -> HttpException -> Maybe HttpException
matchStatusCodeException want e@(StatusCodeException s _ _)
| want s = Just e
| otherwise = Nothing
matchStatusCodeException _ _ = Nothing

View file

@ -127,3 +127,5 @@ Logging in as anonymous ...
"""]] """]]
[[!meta author=yoh]] [[!meta author=yoh]]
> [[fixed|done]] --[[Joey]]

View file

@ -0,0 +1,13 @@
[[!comment format=mdwn
username="joey"
subject="""comment 1"""
date="2016-07-12T20:00:49Z"
content="""
This only affects http to ftp redirects, because there's a special hack
in place to use curl to check if a ftp url exists.
Seems that http-conduit throws a StatusCodeException with statusCode = 302
when it is redirected to a protocol that it does not support, such as ftp.
So, it can catch that exception and fall back to curl.
"""]]