mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 12:05:37 +00:00
Merge pull request #2436 from mateusz/restfulservice-headers
BUG Do not rely on broken curl header size calculation.
This commit is contained in:
commit
5c82609802
@ -308,7 +308,8 @@ class RestfulService extends ViewableData {
|
||||
}
|
||||
|
||||
/**
|
||||
* Extracts the response body and headers from a full curl response
|
||||
* Build the response from raw data. The response could have multiple redirection
|
||||
* 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 string $rawResponse The raw response text
|
||||
@ -322,15 +323,21 @@ class RestfulService extends ViewableData {
|
||||
$curlError = curl_error($ch);
|
||||
//normalise the status code
|
||||
if(curl_error($ch) !== '' || $statusCode == 0) $statusCode = 500;
|
||||
//calculate the length of the header and extract it
|
||||
$headerLength = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
|
||||
$rawHeaders = substr($rawResponse, 0, $headerLength);
|
||||
//extract the body
|
||||
$body = substr($rawResponse, $headerLength);
|
||||
//parse the headers
|
||||
$headers = $this->parseRawHeaders($rawHeaders);
|
||||
//return the response object
|
||||
return new RestfulService_Response($body, $statusCode, $headers);
|
||||
|
||||
// Parse the headers and body from the response.
|
||||
// We cannot rely on CURLINFO_HEADER_SIZE here, it's miscalculated when connecting via
|
||||
// a proxy (see http://sourceforge.net/p/curl/bugs/1204/). This is fixed in curl 7.30.0.
|
||||
$headerParts = array();
|
||||
$parts = explode("\r\n\r\n", $rawResponse);
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -196,6 +196,120 @@ class RestfulServiceTest extends SapphireTest {
|
||||
);
|
||||
}
|
||||
|
||||
public function testExtractResponseRedirectionAndProxy() {
|
||||
// This is an example of real raw response for a request via a proxy that gets redirected.
|
||||
$rawResponse =
|
||||
"HTTP/1.0 200 Connection established\r\n" .
|
||||
"\r\n" .
|
||||
"HTTP/1.1 301 Moved Permanently\r\n" .
|
||||
"Server: nginx\r\n" .
|
||||
"Date: Fri, 20 Sep 2013 01:53:07 GMT\r\n" .
|
||||
"Content-Type: text/html\r\n" .
|
||||
"Content-Length: 178\r\n" .
|
||||
"Connection: keep-alive\r\n" .
|
||||
"Location: https://www.foobar.org.nz/\r\n" .
|
||||
"\r\n" .
|
||||
"HTTP/1.0 200 Connection established\r\n" .
|
||||
"\r\n" .
|
||||
"HTTP/1.1 200 OK\r\n" .
|
||||
"Server: nginx\r\n" .
|
||||
"Date: Fri, 20 Sep 2013 01:53:08 GMT\r\n" .
|
||||
"Content-Type: text/html; charset=utf-8\r\n" .
|
||||
"Transfer-Encoding: chunked\r\n" .
|
||||
"Connection: keep-alive\r\n" .
|
||||
"X-Frame-Options: SAMEORIGIN\r\n" .
|
||||
"Cache-Control: no-cache, max-age=0, must-revalidate, no-transform\r\n" .
|
||||
"Vary: Accept-Encoding\r\n" .
|
||||
"\r\n" .
|
||||
"<!doctype html></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></html>', 'Body is correctly extracted.');
|
||||
$this->assertEquals(
|
||||
$response->getHeaders(),
|
||||
array(
|
||||
'Server' => "nginx",
|
||||
'Date' => "Fri, 20 Sep 2013 01:53:08 GMT",
|
||||
'Content-Type' => "text/html; charset=utf-8",
|
||||
'Transfer-Encoding' => "chunked",
|
||||
'Connection' => "keep-alive",
|
||||
'X-Frame-Options' => "SAMEORIGIN",
|
||||
'Cache-Control' => "no-cache, max-age=0, must-revalidate, no-transform",
|
||||
'Vary' => "Accept-Encoding"
|
||||
),
|
||||
'Only last header is extracted and parsed.'
|
||||
);
|
||||
}
|
||||
|
||||
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() {
|
||||
// Malformed response.
|
||||
$rawResponse = "I am a malformed response";
|
||||
|
||||
$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(), "I am a malformed response", 'Body is correctly extracted.');
|
||||
$this->assertEquals($response->getHeaders(), array(), 'Headers are correctly extracted.');
|
||||
}
|
||||
}
|
||||
|
||||
class RestfulServiceTest_Controller extends Controller implements TestOnly {
|
||||
|
Loading…
x
Reference in New Issue
Block a user