Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds shouldStripHeaderOnRedirectIfOnDifferentHostOnly option on Request #520

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ cabal.sandbox.config
.stack-work/
tarballs/
*~
dist-newstyle/
2 changes: 2 additions & 0 deletions http-client/Network/HTTP/Client.hs
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@ module Network.HTTP.Client
, decompress
, redirectCount
, shouldStripHeaderOnRedirect
, shouldStripHeaderOnRedirectIfOnDifferentHostOnly
, checkResponse
, responseTimeout
, cookieJar
Expand Down Expand Up @@ -264,6 +265,7 @@ responseOpenHistory reqOrig man0 = handle (throwIO . toHttpException reqOrig) $
(responseBody res')
}
case getRedirectedRequest
req
req'
(responseHeaders res)
(responseCookieJar res)
Expand Down
2 changes: 1 addition & 1 deletion http-client/Network/HTTP/Client/Core.hs
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ responseOpen inputReq manager' = do
(req'', res) <- httpRaw' modReq manager
let mreq = if redirectCount modReq == 0
then Nothing
else getRedirectedRequest req'' (responseHeaders res) (responseCookieJar res) (statusCode (responseStatus res))
else getRedirectedRequest modReq req'' (responseHeaders res) (responseCookieJar res) (statusCode (responseStatus res))
return (res, fromMaybe req'' mreq, isJust mreq))
req'

Expand Down
1 change: 1 addition & 0 deletions http-client/Network/HTTP/Client/Request.hs
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,7 @@ defaultRequest = Request
Nothing -> throwIO se
, requestManagerOverride = Nothing
, shouldStripHeaderOnRedirect = const False
, shouldStripHeaderOnRedirectIfOnDifferentHostOnly = False
, proxySecureMode = ProxySecureWithConnect
, redactHeaders = Set.singleton "Authorization"
}
Expand Down
30 changes: 23 additions & 7 deletions http-client/Network/HTTP/Client/Response.hs
Original file line number Diff line number Diff line change
Expand Up @@ -43,21 +43,17 @@ import Data.KeyedPool
-- > (\req' -> do
-- > res <- http req'{redirectCount=0} man
-- > modify (\rqs -> req' : rqs)
-- > return (res, getRedirectedRequest req' (responseHeaders res) (responseCookieJar res) (W.statusCode (responseStatus res))
-- > return (res, getRedirectedRequest req req' (responseHeaders res) (responseCookieJar res) (W.statusCode (responseStatus res))
-- > )
-- > 'lift'
-- > req
-- > applyCheckStatus (checkStatus req) res
-- > return redirectRequests
getRedirectedRequest :: Request -> W.ResponseHeaders -> CookieJar -> Int -> Maybe Request
getRedirectedRequest req hs cookie_jar code
getRedirectedRequest :: Request -> Request -> W.ResponseHeaders -> CookieJar -> Int -> Maybe Request
getRedirectedRequest prevReq req hs cookie_jar code
| 300 <= code && code < 400 = do
l' <- lookup "location" hs
let l = escapeURIString isAllowedInURI (S8.unpack l')
stripHeaders r =
r{requestHeaders =
filter (not . shouldStripHeaderOnRedirect req . fst) $
requestHeaders r}
req' <- fmap stripHeaders <$> setUriRelative req =<< parseURIReference l
return $
if code == 302 || code == 303
Expand All @@ -73,8 +69,28 @@ getRedirectedRequest req hs cookie_jar code
else req' {cookieJar = cookie_jar'}
| otherwise = Nothing
where
cookie_jar' :: Maybe CookieJar
cookie_jar' = fmap (const cookie_jar) $ cookieJar req

hostDiffer :: Bool
hostDiffer = host req /= host prevReq

shouldStripOnlyIfHostDiffer :: Bool
shouldStripOnlyIfHostDiffer = shouldStripHeaderOnRedirectIfOnDifferentHostOnly req

stripHeaders :: Request -> Request
stripHeaders r =
case (hostDiffer, shouldStripOnlyIfHostDiffer) of
(True, True) -> stripHeaders' r
(True, False) -> stripHeaders' r
(False, False) -> stripHeaders' r
(False, True) -> r

stripHeaders' :: Request -> Request
stripHeaders' r = r{requestHeaders =
filter (not . shouldStripHeaderOnRedirect req . fst) $
requestHeaders r}

-- | Convert a 'Response' that has a 'Source' body to one with a lazy
-- 'L.ByteString' body.
lbsResponse :: Response BodyReader -> IO (Response L.ByteString)
Expand Down
7 changes: 7 additions & 0 deletions http-client/Network/HTTP/Client/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,13 @@ data Request = Request
--
-- @since 0.6.2

, shouldStripHeaderOnRedirectIfOnDifferentHostOnly :: Bool
-- ^ Decide whether a header must be stripped from the request
-- when following a redirect, if host differs from previous request
-- in redirect chain. Default: false (always strip regardless of host change)
--
-- @since 0.6.2

, proxySecureMode :: ProxySecureMode
-- ^ How to proxy an HTTPS request.
--
Expand Down
45 changes: 45 additions & 0 deletions http-client/test-nonet/Network/HTTP/ClientSpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,27 @@ silentIOError a = a `E.catch` \e -> do
let _ = e :: IOError
return ()

redirectServerToDifferentHost :: Maybe Int -> (Int -> IO a) -> IO a
redirectServerToDifferentHost maxRedirects inner = bracket
(N.bindRandomPortTCP "*4")
(NS.close . snd)
$ \(port, lsocket) -> withAsync
(N.runTCPServer (N.serverSettingsTCPSocket lsocket) app)
(const $ inner port)
where
redirect ad = do
N.appWrite ad "HTTP/1.1 301 Redirect\r\nLocation: http://example.com\r\ncontent-length: 5\r\n\r\n"
threadDelay 10000
N.appWrite ad "hello\r\n"
threadDelay 10000
app ad = Async.race_
(silentIOError $ forever (N.appRead ad))
(silentIOError $ case maxRedirects of
Nothing -> forever $ redirect ad
Just n ->
replicateM_ n (redirect ad) >>
N.appWrite ad "HTTP/1.1 200 OK\r\ncontent-length: 5\r\n\r\nhello\r\n")

redirectServer :: Maybe Int
-- ^ If Just, stop redirecting after that many hops.
-> (Int -> IO a) -> IO a
Expand Down Expand Up @@ -177,6 +198,30 @@ spec = describe "Client" $ do
print $ map (requestHeaders . fst) $ hrRedirects hr
mapM_ (\r -> requestHeaders r `shouldBe` []) $
map fst $ tail $ hrRedirects hr
it "does strips header on redirect, if hosts are different and set to strip them if host differ" $ redirectServerToDifferentHost (Just 1) $ \port -> do
req' <- parseUrlThrow $ "http://127.0.0.1:" ++ show port
let req = req' { requestHeaders = [(hAuthorization, "abguvatgbfrrurer")]
, redirectCount = 10
, shouldStripHeaderOnRedirect = (== hAuthorization)
, shouldStripHeaderOnRedirectIfOnDifferentHostOnly = True
}
man <- newManager defaultManagerSettings
withResponseHistory req man $ \hr -> do
print $ map (requestHeaders . fst) $ hrRedirects hr
mapM_ (\r -> requestHeaders r `shouldBe` []) $
map fst $ tail $ hrRedirects hr
it "does NOT strips header on redirect, if hosts are same and set to strip them if host differ" $ redirectServerToDifferentHost (Just 1) $ \port -> do
req' <- parseUrlThrow $ "http://127.0.0.1:" ++ show port
let req = req' { requestHeaders = [(hAuthorization, "abguvatgbfrrurer")]
, redirectCount = 10
, shouldStripHeaderOnRedirect = (== hAuthorization)
, shouldStripHeaderOnRedirectIfOnDifferentHostOnly = True
}
man <- newManager defaultManagerSettings
withResponseHistory req man $ \hr -> do
print $ map (requestHeaders . fst) $ hrRedirects hr
mapM_ (\r -> requestHeaders r `shouldBe` [("Authorization","abguvatgbfrrurer")]) $
map fst $ tail $ hrRedirects hr
it "redirecting #41" $ redirectServer Nothing $ \port -> do
req' <- parseUrlThrow $ "http://127.0.0.1:" ++ show port
let req = req' { redirectCount = 1 }
Expand Down
Loading