summaryrefslogtreecommitdiff
path: root/Utility/Url.hs
diff options
context:
space:
mode:
authorGravatar Joey Hess <joey@kitenet.net>2012-02-10 21:42:46 -0400
committerGravatar Joey Hess <joey@kitenet.net>2012-02-10 21:54:25 -0400
commitecfcb41abe5c2903ca80d26365afdf20faaf9989 (patch)
treebdc4fde7dbf6f0ab564d786732e2baf79663b9f3 /Utility/Url.hs
parent6335abcab2c0b48132b04011acbd01fb99bc5b53 (diff)
work around Network.Browser bug that converts a HEAD to a GET when following a redirect
The code explicitly switches from HEAD to GET for most redirects. Possibly because someone misread a spec (which does require switching from POST to GET for 303 redirects). Or possibly because the spec really is that bad. Upstream bug: https://github.com/haskell/HTTP/issues/24 Since we absolutely don't want to download entire (large) files from the web when checking that they exist with HEAD, I wrote my own redirect follower, based closely on the one used by Network.Browser, but without this misfeature. Note that Network.Browser checks that the redirect url is a http url and fails if not. I don't, because I want to not need to change this code when it gets https support (related: I'm surprised to see it doesn't support https yet..). The check does not seem security significant; it doesn't support file:// urls for example. If a http url is redirected to https, the Network.Browser will actually make a http connection again. This could loop, but only up to 5 times.
Diffstat (limited to 'Utility/Url.hs')
-rw-r--r--Utility/Url.hs33
1 files changed, 27 insertions, 6 deletions
diff --git a/Utility/Url.hs b/Utility/Url.hs
index efd6ad16d..dfdebaf06 100644
--- a/Utility/Url.hs
+++ b/Utility/Url.hs
@@ -19,6 +19,7 @@ import Control.Monad
import qualified Network.Browser as Browser
import Network.HTTP
import Network.URI
+import Data.Maybe
import Utility.SafeCommand
import Utility.Path
@@ -87,12 +88,32 @@ get url =
{- Makes a http request of an url. For example, HEAD can be used to
- check if the url exists, or GET used to get the url content (best for
- - small urls). -}
+ - small urls).
+ -
+ - This does its own redirect following because Browser's is buggy for HEAD
+ - requests.
+ -}
request :: URI -> RequestMethod -> IO (Response String)
-request url requesttype = Browser.browse $ do
- Browser.setErrHandler ignore
- Browser.setOutHandler ignore
- Browser.setAllowRedirects True
- snd <$> Browser.request (mkRequest requesttype url :: Request_String)
+request url requesttype = go 5 url
where
+ go :: Int -> URI -> IO (Response String)
+ go 0 _ = error "Too many redirects "
+ go n u = do
+ rsp <- Browser.browse $ do
+ Browser.setErrHandler ignore
+ Browser.setOutHandler ignore
+ Browser.setAllowRedirects False
+ snd <$> Browser.request (mkRequest requesttype u :: Request_String)
+ case rspCode rsp of
+ (3,0,x) | x /= 5 -> redir (n - 1) u rsp
+ _ -> return rsp
ignore = const $ return ()
+ redir n u rsp = do
+ case retrieveHeaders HdrLocation rsp of
+ [] -> return rsp
+ (Header _ newu:_) ->
+ case parseURIReference newu of
+ Nothing -> return rsp
+ Just newURI -> go n newURI_abs
+ where
+ newURI_abs = fromMaybe newURI (newURI `relativeTo` u)