mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
FIX Improve recent RestfulService fix for proxies
This commit is contained in:
parent
5c82609802
commit
b1f03db4ff
@ -224,8 +224,11 @@ class RestfulService extends ViewableData {
|
|||||||
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
|
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $timeout);
|
||||||
if(!ini_get('open_basedir')) curl_setopt($ch, CURLOPT_FOLLOWLOCATION,1);
|
if(!ini_get('open_basedir')) curl_setopt($ch, CURLOPT_FOLLOWLOCATION,1);
|
||||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
|
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
|
||||||
//include headers in the response
|
|
||||||
curl_setopt($ch, CURLOPT_HEADER, true);
|
|
||||||
|
// Write headers to a temporary file
|
||||||
|
$headerfd = tmpfile();
|
||||||
|
curl_setopt($ch, CURLOPT_WRITEHEADER, $headerfd);
|
||||||
|
|
||||||
// Add headers
|
// Add headers
|
||||||
if($this->customHeaders) {
|
if($this->customHeaders) {
|
||||||
@ -260,8 +263,13 @@ class RestfulService extends ViewableData {
|
|||||||
curl_setopt_array($ch, $curlOptions);
|
curl_setopt_array($ch, $curlOptions);
|
||||||
|
|
||||||
// Run request
|
// Run request
|
||||||
$rawResponse = curl_exec($ch);
|
$body = curl_exec($ch);
|
||||||
$response = $this->extractResponse($ch, $rawResponse);
|
|
||||||
|
rewind($headerfd);
|
||||||
|
$headers = stream_get_contents($headerfd);
|
||||||
|
fclose($headerfd);
|
||||||
|
|
||||||
|
$response = $this->extractResponse($ch, $headers, $body);
|
||||||
curl_close($ch);
|
curl_close($ch);
|
||||||
|
|
||||||
return $response;
|
return $response;
|
||||||
@ -308,36 +316,26 @@ class RestfulService extends ViewableData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build the response from raw data. The response could have multiple redirection
|
* Extracts the response body and headers from a full curl response
|
||||||
* and proxy connect headers, so we are only interested in the last header before the body.
|
|
||||||
*
|
*
|
||||||
* @param curl_handle $ch The curl handle for the request
|
* @param curl_handle $ch The curl handle for the request
|
||||||
* @param string $rawResponse The raw response text
|
* @param string $rawResponse The raw response text
|
||||||
*
|
*
|
||||||
* @return RestfulService_Response The response object
|
* @return RestfulService_Response The response object
|
||||||
*/
|
*/
|
||||||
protected function extractResponse($ch, $rawResponse) {
|
protected function extractResponse($ch, $rawHeaders, $rawBody) {
|
||||||
//get the status code
|
//get the status code
|
||||||
$statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
$statusCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||||
//get a curl error if there is one
|
//get a curl error if there is one
|
||||||
$curlError = curl_error($ch);
|
$curlError = curl_error($ch);
|
||||||
//normalise the status code
|
//normalise the status code
|
||||||
if(curl_error($ch) !== '' || $statusCode == 0) $statusCode = 500;
|
if(curl_error($ch) !== '' || $statusCode == 0) $statusCode = 500;
|
||||||
|
//parse the headers
|
||||||
// Parse the headers and body from the response.
|
$parts = array_filter(explode("\r\n\r\n", $rawHeaders));
|
||||||
// We cannot rely on CURLINFO_HEADER_SIZE here, it's miscalculated when connecting via
|
$lastHeaders = array_pop($parts);
|
||||||
// a proxy (see http://sourceforge.net/p/curl/bugs/1204/). This is fixed in curl 7.30.0.
|
$headers = $this->parseRawHeaders($lastHeaders);
|
||||||
$headerParts = array();
|
//return the response object
|
||||||
$parts = explode("\r\n\r\n", $rawResponse);
|
return new RestfulService_Response($rawBody, $statusCode, $headers);
|
||||||
while (isset($parts[0])) {
|
|
||||||
if (strpos($parts[0], 'HTTP/')===0) $headerParts[] = array_shift($parts);
|
|
||||||
else break; // We have reached the body.
|
|
||||||
}
|
|
||||||
$lastHeader = array_pop($headerParts);
|
|
||||||
$body = implode("\r\n\r\n", $parts);
|
|
||||||
|
|
||||||
$parsedHeader = $this->parseRawHeaders($lastHeader);
|
|
||||||
return new RestfulService_Response($body, $statusCode, $parsedHeader);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -198,7 +198,7 @@ class RestfulServiceTest extends SapphireTest {
|
|||||||
|
|
||||||
public function testExtractResponseRedirectionAndProxy() {
|
public function testExtractResponseRedirectionAndProxy() {
|
||||||
// This is an example of real raw response for a request via a proxy that gets redirected.
|
// This is an example of real raw response for a request via a proxy that gets redirected.
|
||||||
$rawResponse =
|
$rawHeaders =
|
||||||
"HTTP/1.0 200 Connection established\r\n" .
|
"HTTP/1.0 200 Connection established\r\n" .
|
||||||
"\r\n" .
|
"\r\n" .
|
||||||
"HTTP/1.1 301 Moved Permanently\r\n" .
|
"HTTP/1.1 301 Moved Permanently\r\n" .
|
||||||
@ -220,8 +220,8 @@ class RestfulServiceTest extends SapphireTest {
|
|||||||
"X-Frame-Options: SAMEORIGIN\r\n" .
|
"X-Frame-Options: SAMEORIGIN\r\n" .
|
||||||
"Cache-Control: no-cache, max-age=0, must-revalidate, no-transform\r\n" .
|
"Cache-Control: no-cache, max-age=0, must-revalidate, no-transform\r\n" .
|
||||||
"Vary: Accept-Encoding\r\n" .
|
"Vary: Accept-Encoding\r\n" .
|
||||||
"\r\n" .
|
"\r\n"
|
||||||
"<!doctype html></html>";
|
;
|
||||||
|
|
||||||
$headerFunction = new ReflectionMethod('RestfulService', 'extractResponse');
|
$headerFunction = new ReflectionMethod('RestfulService', 'extractResponse');
|
||||||
$headerFunction->setAccessible(true);
|
$headerFunction->setAccessible(true);
|
||||||
@ -230,10 +230,10 @@ class RestfulServiceTest extends SapphireTest {
|
|||||||
$response = $headerFunction->invoke(
|
$response = $headerFunction->invoke(
|
||||||
new RestfulService(Director::absoluteBaseURL(),0),
|
new RestfulService(Director::absoluteBaseURL(),0),
|
||||||
$ch,
|
$ch,
|
||||||
$rawResponse
|
$rawHeaders,
|
||||||
|
''
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->assertEquals($response->getBody(), '<!doctype html></html>', 'Body is correctly extracted.');
|
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
$response->getHeaders(),
|
$response->getHeaders(),
|
||||||
array(
|
array(
|
||||||
@ -250,53 +250,7 @@ class RestfulServiceTest extends SapphireTest {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testExtractResponseNewlinesInBody() {
|
|
||||||
$rawResponse =
|
|
||||||
"HTTP/1.1 200 OK\r\n" .
|
|
||||||
"Server: nginx\r\n" .
|
|
||||||
"\r\n" .
|
|
||||||
"<!doctype html>\r\n" .
|
|
||||||
"\r\n" .
|
|
||||||
"</html>";
|
|
||||||
|
|
||||||
$headerFunction = new ReflectionMethod('RestfulService', 'extractResponse');
|
|
||||||
$headerFunction->setAccessible(true);
|
|
||||||
|
|
||||||
$ch = curl_init();
|
|
||||||
$response = $headerFunction->invoke(
|
|
||||||
new RestfulService(Director::absoluteBaseURL(),0),
|
|
||||||
$ch,
|
|
||||||
$rawResponse
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->assertEquals($response->getBody(), "<!doctype html>\r\n\r\n</html>", 'Body is correctly extracted.');
|
|
||||||
$this->assertEquals($response->getHeaders(), array('Server' => "nginx"), 'Headers are correctly extracted.');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testExtractResponseNoBody() {
|
|
||||||
// For example a response to HEAD request.
|
|
||||||
$rawResponse =
|
|
||||||
"HTTP/1.1 200 OK\r\n" .
|
|
||||||
"Server: nginx";
|
|
||||||
|
|
||||||
$headerFunction = new ReflectionMethod('RestfulService', 'extractResponse');
|
|
||||||
$headerFunction->setAccessible(true);
|
|
||||||
|
|
||||||
$ch = curl_init();
|
|
||||||
$response = $headerFunction->invoke(
|
|
||||||
new RestfulService(Director::absoluteBaseURL(),0),
|
|
||||||
$ch,
|
|
||||||
$rawResponse
|
|
||||||
);
|
|
||||||
|
|
||||||
$this->assertEquals($response->getBody(), "", 'Body is correctly extracted.');
|
|
||||||
$this->assertEquals($response->getHeaders(), array('Server' => "nginx"), 'Headers are correctly extracted.');
|
|
||||||
}
|
|
||||||
|
|
||||||
public function testExtractResponseNoHead() {
|
public function testExtractResponseNoHead() {
|
||||||
// Malformed response.
|
|
||||||
$rawResponse = "I am a malformed response";
|
|
||||||
|
|
||||||
$headerFunction = new ReflectionMethod('RestfulService', 'extractResponse');
|
$headerFunction = new ReflectionMethod('RestfulService', 'extractResponse');
|
||||||
$headerFunction->setAccessible(true);
|
$headerFunction->setAccessible(true);
|
||||||
|
|
||||||
@ -304,10 +258,10 @@ class RestfulServiceTest extends SapphireTest {
|
|||||||
$response = $headerFunction->invoke(
|
$response = $headerFunction->invoke(
|
||||||
new RestfulService(Director::absoluteBaseURL(),0),
|
new RestfulService(Director::absoluteBaseURL(),0),
|
||||||
$ch,
|
$ch,
|
||||||
$rawResponse
|
'',
|
||||||
|
''
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->assertEquals($response->getBody(), "I am a malformed response", 'Body is correctly extracted.');
|
|
||||||
$this->assertEquals($response->getHeaders(), array(), 'Headers are correctly extracted.');
|
$this->assertEquals($response->getHeaders(), array(), 'Headers are correctly extracted.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user