Skip to content
Browse files

Merge branch 'hotfix/4215' into develop

Close #4215
  • Loading branch information...
2 parents 6adeba3 + 9d50a29 commit 126400d93b108400e743d04b395ca6336bea9d1b @weierophinney weierophinney committed Apr 22, 2013
Showing with 208 additions and 27 deletions.
  1. +67 −27 library/Zend/Http/Client.php
  2. +141 −0 tests/ZendTest/Http/ClientTest.php
View
94 library/Zend/Http/Client.php
@@ -298,8 +298,18 @@ public function getRedirectionsCount()
public function setUri($uri)
{
if (!empty($uri)) {
+ // remember host of last request
+ $lastHost = $this->getRequest()->getUri()->getHost();
$this->getRequest()->setUri($uri);
+ // if host changed, the HTTP authentication should be cleared for security
+ // reasons, see #4215 for a discussion - currently authentication is also
+ // cleared for peer subdomains due to technical limits
+ $nextHost = $this->getRequest()->getUri()->getHost();
+ if (!preg_match('/' . preg_quote($lastHost, '/') . '$/i', $nextHost)) {
+ $this->clearAuth();
+ }
+
// Set auth if username and password has been specified in the uri
if ($this->getUri()->getUser() && $this->getUri()->getPassword()) {
$this->setAuth($this->getUri()->getUser(), $this->getUri()->getPassword());
@@ -445,6 +455,37 @@ public function setParameterGet(array $query)
}
/**
+ * Reset all the HTTP parameters (request, response, etc)
+ *
+ * @param bool $clearCookies Also clear all valid cookies? (defaults to false)
+ * @param bool $clearAuth Also clear http authentication? (defaults to true)
+ * @return Client
+ */
+ public function resetParameters($clearCookies = false, $clearAuth = true)
+ {
+ $uri = $this->getUri();
+
+ $this->streamName = null;
+ $this->encType = null;
+ $this->request = null;
+ $this->response = null;
+ $this->lastRawRequest = null;
+ $this->lastRawResponse = null;
+
+ $this->setUri($uri);
+
+ if ($clearCookies) {
+ $this->clearCookies();
+ }
+
+ if ($clearAuth) {
+ $this->clearAuth();
+ }
+
+ return $this;
+ }
+
+ /**
* Return the current cookies
*
* @return array
@@ -674,6 +715,14 @@ public function setAuth($user, $password, $type = self::AUTH_BASIC)
}
/**
+ * Clear http authentication
+ */
+ public function clearAuth()
+ {
+ $this->auth = array();
+ }
+
+ /**
* Calculate the response value according to the HTTP authentication type
*
* @see http://www.faqs.org/rfcs/rfc2617.html
@@ -729,31 +778,6 @@ protected function calcAuthDigest($user, $password, $type = self::AUTH_BASIC, $d
}
/**
- * Reset all the HTTP parameters (auth,cookies,request, response, etc)
- *
- * @param bool $clearCookies Also clear all valid cookies? (defaults to false)
- * @return Client
- */
- public function resetParameters($clearCookies = false)
- {
- $uri = $this->getUri();
-
- $this->auth = null;
- $this->streamName = null;
- $this->encType = null;
- $this->request = null;
- $this->response = null;
-
- $this->setUri($uri);
-
- if ($clearCookies) {
- $this->clearCookies();
- }
-
- return $this;
- }
-
- /**
* Dispatch
*
* @param Stdlib\RequestInterface $request
@@ -897,13 +921,15 @@ public function send(Request $request = null)
((! $this->config['strictredirects']) && ($response->getStatusCode() == 302 ||
$response->getStatusCode() == 301))) {
- $this->resetParameters();
+ $this->resetParameters(false, false);
$this->setMethod(Request::METHOD_GET);
}
+
// If we got a well formed absolute URI
if (($scheme = substr($location, 0, 6)) &&
($scheme == 'http:/' || $scheme == 'https:')) {
+ // setURI() clears parameters if host changed, see #4215
$this->setUri($location);
} else {
@@ -933,13 +959,27 @@ public function send(Request $request = null)
break;
}
- } while ($this->redirectCounter < $this->config['maxredirects']);
+ } while ($this->redirectCounter <= $this->config['maxredirects']);
$this->response = $response;
return $response;
}
/**
+ * Fully reset the HTTP client (auth, cookies, request, response, etc.)
+ *
+ * @return Client
+ */
+ public function reset()
+ {
+ $this->resetParameters();
+ $this->clearAuth();
+ $this->clearCookies();
+
+ return $this;
+ }
+
+ /**
* Set a file to upload (using a POST request)
*
* Can be used in two ways:
View
141 tests/ZendTest/Http/ClientTest.php
@@ -18,6 +18,7 @@
use Zend\Http\Header\SetCookie;
use Zend\Http\Request;
use Zend\Http\Response;
+use Zend\Http\Client\Adapter\Test;
class ClientTest extends \PHPUnit_Framework_TestCase
@@ -198,4 +199,144 @@ public function testEncodeAuthHeaderThrowsExceptionWhenInvalidAuthTypeIsUsed()
{
$encoded = Client::encodeAuthHeader('test', 'test', 'test');
}
+
+ public function testIfMaxredirectWorksCorrectly()
+ {
+ $testAdapter = new Test();
+ // first response, contains a redirect
+ $testAdapter->setResponse(
+ "HTTP/1.1 303 See Other\r\n"
+ . "Location: http://www.example.org/part2\r\n\r\n"
+ . "Page #1"
+ );
+ // seconds response, contains a redirect
+ $testAdapter->addResponse(
+ "HTTP/1.1 303 See Other\r\n"
+ . "Location: http://www.example.org/part3\r\n\r\n"
+ . "Page #2"
+ );
+ // third response
+ $testAdapter->addResponse(
+ "HTTP/1.1 303 See Other\r\n\r\n"
+ . "Page #3"
+ );
+
+ // create a client which allows one redirect at most!
+ $client = new Client('http://www.example.org/part1', array(
+ 'adapter' => $testAdapter,
+ 'maxredirects' => 1,
+ 'storeresponse' => true
+ ));
+
+ // do the request
+ $response = $client->setMethod('GET')->send();
+
+ // response should be the second response, since third response should not
+ // be requested, due to the maxredirects = 1 limit
+ $this->assertEquals($response->getContent(), "Page #2");
+ }
+
+ public function testIfClientDoesNotLooseAuthenticationOnRedirect()
+ {
+ // set up user credentials
+ $user = 'username123';
+ $password = 'password456';
+ $encoded = Client::encodeAuthHeader($user, $password, Client::AUTH_BASIC);
+
+ // set up two responses that simulate a redirection
+ $testAdapter = new Test();
+ $testAdapter->setResponse(
+ "HTTP/1.1 303 See Other\r\n"
+ . "Location: http://www.example.org/part2\r\n\r\n"
+ . "The URL of this page has changed."
+ );
+ $testAdapter->addResponse(
+ "HTTP/1.1 200 OK\r\n\r\n"
+ . "Welcome to this Website."
+ );
+
+ // create client with HTTP basic authentication
+ $client = new Client('http://www.example.org/part1', array(
+ 'adapter' => $testAdapter,
+ 'maxredirects' => 1
+ ));
+ $client->setAuth($user, $password, Client::AUTH_BASIC);
+
+ // do request
+ $response = $client->setMethod('GET')->send();
+
+ // the last request should contain the Authorization header
+ $this->assertTrue(strpos($client->getLastRawRequest(), $encoded) !== false);
+ }
+
+ public function testIfClientDoesNotForwardAuthenticationToForeignHost()
+ {
+ // set up user credentials
+ $user = 'username123';
+ $password = 'password456';
+ $encoded = Client::encodeAuthHeader($user, $password, Client::AUTH_BASIC);
+
+ $testAdapter = new Test();
+ $client = new Client(null, array('adapter' => $testAdapter));
+
+ // set up two responses that simulate a redirection from example.org to example.com
+ $testAdapter->setResponse(
+ "HTTP/1.1 303 See Other\r\n"
+ . "Location: http://example.com/part2\r\n\r\n"
+ . "The URL of this page has changed."
+ );
+ $testAdapter->addResponse(
+ "HTTP/1.1 200 OK\r\n\r\n"
+ . "Welcome to this Website."
+ );
+
+ // set auth and do request
+ $client->setUri('http://example.org/part1')
+ ->setAuth($user, $password, Client::AUTH_BASIC);
+ $response = $client->setMethod('GET')->send();
+
+ // the last request should NOT contain the Authorization header,
+ // because example.com is different from example.org
+ $this->assertTrue(strpos($client->getLastRawRequest(), $encoded) === false);
+
+ // set up two responses that simulate a rediration from example.org to sub.example.org
+ $testAdapter->setResponse(
+ "HTTP/1.1 303 See Other\r\n"
+ . "Location: http://sub.example.org/part2\r\n\r\n"
+ . "The URL of this page has changed."
+ );
+ $testAdapter->addResponse(
+ "HTTP/1.1 200 OK\r\n\r\n"
+ . "Welcome to this Website."
+ );
+
+ // set auth and do request
+ $client->setUri('http://example.org/part1')
+ ->setAuth($user, $password, Client::AUTH_BASIC);
+ $response = $client->setMethod('GET')->send();
+
+ // the last request should contain the Authorization header,
+ // because sub.example.org is a subdomain unter example.org
+ $this->assertFalse(strpos($client->getLastRawRequest(), $encoded) === false);
+
+ // set up two responses that simulate a rediration from sub.example.org to example.org
+ $testAdapter->setResponse(
+ "HTTP/1.1 303 See Other\r\n"
+ . "Location: http://example.org/part2\r\n\r\n"
+ . "The URL of this page has changed."
+ );
+ $testAdapter->addResponse(
+ "HTTP/1.1 200 OK\r\n\r\n"
+ . "Welcome to this Website."
+ );
+
+ // set auth and do request
+ $client->setUri('http://sub.example.org/part1')
+ ->setAuth($user, $password, Client::AUTH_BASIC);
+ $response = $client->setMethod('GET')->send();
+
+ // the last request should NOT contain the Authorization header,
+ // because example.org is not a subdomain unter sub.example.org
+ $this->assertTrue(strpos($client->getLastRawRequest(), $encoded) === false);
+ }
}

0 comments on commit 126400d

Please sign in to comment.
Something went wrong with that request. Please try again.