Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Adding support for compressed responses
  • Loading branch information
Toby Brain committed Apr 18, 2011
1 parent 9a0296c commit 5d223bd
Show file tree
Hide file tree
Showing 3 changed files with 105 additions and 12 deletions.
2 changes: 1 addition & 1 deletion class/base_classes.php
Expand Up @@ -4,7 +4,7 @@
require_once dirname(__FILE__).'/transport.php';
require_once dirname(__FILE__).'/log.php';

define('CS_REST_WRAPPER_VERSION', '1.0.6');
define('CS_REST_WRAPPER_VERSION', '1.0.7');

define('CS_REST_WEBHOOK_FORMAT_JSON', 'json');
define('CS_REST_WEBHOOK_FORMAT_XML', 'xml');
Expand Down
57 changes: 51 additions & 6 deletions class/transport.php
Expand Up @@ -19,6 +19,19 @@ function get_available_transport($requires_ssl, $log) {
'ensure the cURL extension is loaded', E_USER_ERROR);
}
}

function split_and_inflate($response, $may_be_compressed, $log) {
list( $headers, $result ) = explode("\r\n\r\n", $response, 2);
if($may_be_compressed && preg_match('/^Content-Encoding:\s+gzip\s+$/im', $headers)) {
$original_length = strlen($response);
$result = gzinflate(substr($result, 10, -8));

$log->log_message('Inflated gzipped response: '.$original_length.' bytes ->'.
strlen($result).' bytes', get_class(), CS_REST_LOG_VERBOSE);
}

return array($headers, $result);
}
}

/**
Expand All @@ -30,9 +43,13 @@ function get_available_transport($requires_ssl, $log) {
class CS_REST_CurlTransport {

var $_log;
var $_curl_zlib;

function CS_REST_CurlTransport($log) {
$this->_log = $log;

$curl_version = curl_version();
$this->_curl_zlib = isset($curl_version['libz_version']);
}

/**
Expand All @@ -59,10 +76,22 @@ function make_call($call_options) {

curl_setopt($ch, CURLOPT_URL, $call_options['route']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HEADER, true);
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($ch, CURLOPT_USERPWD, $call_options['credentials']);
curl_setopt($ch, CURLOPT_USERAGENT, $call_options['userAgent']);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: '.$call_options['contentType']));

$headers = array();
$inflate_response = false;
if($this->_curl_zlib) {
$this->_log->log_message('curl+zlib support available. Requesting gzipped response.',
get_class($this), CS_REST_LOG_VERBOSE);
curl_setopt($ch, CURLOPT_ENCODING, 'gzip');
} else if(function_exists('gzinflate')) {
$headers[] = 'Accept-Encoding: gzip';
$inflate_response = true;
}

if($call_options['protocol'] === 'https') {
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, true);
Expand All @@ -73,7 +102,7 @@ function make_call($call_options) {
switch($call_options['method']) {
case CS_REST_PUT:
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, CS_REST_PUT);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Length: '.strlen($call_options['data'])));
$headers[] = 'Content-Length: '.strlen($call_options['data']);
curl_setopt($ch, CURLOPT_POSTFIELDS, $call_options['data']);
break;
case CS_REST_POST:
Expand All @@ -84,14 +113,21 @@ function make_call($call_options) {
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, CS_REST_DELETE);
break;
}

if(count($headers) > 0) {
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
}

$response = curl_exec($ch);
if(!$response && $response !== '') {
$this->_log->log_message('Error making request with curl_error: '.curl_errno($ch),
get_class($this), CS_REST_LOG_ERROR);
trigger_error('Error making request with curl_error: '.curl_error($ch), E_USER_ERROR);
}


list( $headers, $result ) = @CS_REST_TransportFactory::split_and_inflate(
$response, $inflate_response, $this->_log);

$this->_log->log_message('API Call Info for '.$call_options['method'].' '.
curl_getinfo($ch, CURLINFO_EFFECTIVE_URL).': '.curl_getinfo($ch, CURLINFO_SIZE_UPLOAD).
' bytes uploaded. '.curl_getinfo($ch, CURLINFO_SIZE_DOWNLOAD).' bytes downloaded'.
Expand All @@ -100,7 +136,7 @@ function make_call($call_options) {

$result = array(
'code' => curl_getinfo($ch, CURLINFO_HTTP_CODE),
'response' => $response
'response' => $result
);

curl_close($ch);
Expand Down Expand Up @@ -202,7 +238,9 @@ function make_call($call_options) {
}

if($this->_socket_wrapper->open($domain, $port)) {
$request = $this->_build_request($call_options, $host, $path);
$inflate_response = function_exists('gzinflate');

$request = $this->_build_request($call_options, $host, $path, $inflate_response);
$this->_log->log_message('Sending <pre>'.$request.'</pre> down the socket',
get_class($this), CS_REST_LOG_VERBOSE);

Expand All @@ -215,7 +253,9 @@ function make_call($call_options) {
' bytes uploaded. '.strlen($response).' bytes downloaded',
get_class($this), CS_REST_LOG_VERBOSE);

list( $headers, $result ) = explode("\r\n\r\n", $response, 2);
list( $headers, $result ) = @CS_REST_TransportFactory::split_and_inflate(
$response, $inflate_response, $this->_log);

$this->_log->log_message('Received headers <pre>'.$headers.'</pre>',
get_class($this), CS_REST_LOG_VERBOSE);

Expand All @@ -238,14 +278,19 @@ function _get_status_code($headers) {
trigger_error('Failed to get HTTP status code from request', E_USER_ERROR);
}

function _build_request($call_options, $host, $path) {
function _build_request($call_options, $host, $path, $accept_gzip) {
$request =
$call_options['method'].' '.$path." HTTP/1.1\n".
'Host: '.$host."\n".
'Authorization: Basic '.base64_encode($call_options['credentials'])."\n".
'User-Agent: '.$call_options['userAgent']."\n".
'Content-Type: '.$call_options['contentType']."\n";

if($accept_gzip) {
$request .=
"Accept-Encoding: gzip\n";
}

if(isset($call_options['data'])) {
$request .=
'Content-Length: '.strlen($call_options['data'])."\n\n".
Expand Down
58 changes: 53 additions & 5 deletions tests/class_tests/transport_test.php
Expand Up @@ -60,7 +60,8 @@ function make_call_base($protocol, $port, $domain_prefix) {
array(
new IdenticalExpectation($call_options),
new IdenticalExpectation($host),
new IdenticalExpectation($path)
new IdenticalExpectation($path),
new IdenticalExpectation(true)
)
);

Expand Down Expand Up @@ -110,7 +111,7 @@ function test_get_status_code_404() {
$this->assertIdentical($this->transport->_get_status_code($headers), '404');
}

function test_build_request_no_data() {
function test_build_request_no_data_or_gzip() {
$call_options = array(
'method' => 'CONJURE',
'credentials' => 'Chuck:Norris',
Expand All @@ -128,10 +129,56 @@ function test_build_request_no_data() {
'User-Agent: '.$call_options['userAgent']."\n".
'Content-Type: '.$call_options['contentType']."\n\n\n";

$this->assertIdentical($this->transport->_build_request($call_options, $host, $path), $expected);
$this->assertIdentical($this->transport->_build_request($call_options, $host, $path, false), $expected);
}

function test_build_request_no_data_with_gzip() {
$call_options = array(
'method' => 'CONJURE',
'credentials' => 'Chuck:Norris',
'userAgent' => 'Nozilla/ Firechuck',
'contentType' => 'application/visa'
);

$host = 'api.test.createsend.com';
$path = '/path/to/resource';

$expected =
$call_options['method'].' '.$path." HTTP/1.1\n".
'Host: '.$host."\n".
'Authorization: Basic '.base64_encode($call_options['credentials'])."\n".
'User-Agent: '.$call_options['userAgent']."\n".
'Content-Type: '.$call_options['contentType']."\n".
"Accept-Encoding: gzip\n\n\n";

$this->assertIdentical($this->transport->_build_request($call_options, $host, $path, true), $expected);
}

function test_build_request_with_data_no_gzip() {
$call_options = array(
'method' => 'CONJURE',
'credentials' => 'Chuck:Norris',
'userAgent' => 'Nozilla/ Firechuck',
'contentType' => 'application/visa',
'data' => 'Send this to your bank for a new Credit Card!'
);

$host = 'api.test.createsend.com';
$path = '/path/to/resource';

$expected =
$call_options['method'].' '.$path." HTTP/1.1\n".
'Host: '.$host."\n".
'Authorization: Basic '.base64_encode($call_options['credentials'])."\n".
'User-Agent: '.$call_options['userAgent']."\n".
'Content-Type: '.$call_options['contentType']."\n".
'Content-Length: '.strlen($call_options['data'])."\n\n".
$call_options['data']."\n\n";

$this->assertIdentical($this->transport->_build_request($call_options, $host, $path, false), $expected);
}

function test_build_request_with_data() {
function test_build_request_with_data_and_gzip() {
$call_options = array(
'method' => 'CONJURE',
'credentials' => 'Chuck:Norris',
Expand All @@ -149,9 +196,10 @@ function test_build_request_with_data() {
'Authorization: Basic '.base64_encode($call_options['credentials'])."\n".
'User-Agent: '.$call_options['userAgent']."\n".
'Content-Type: '.$call_options['contentType']."\n".
"Accept-Encoding: gzip\n".
'Content-Length: '.strlen($call_options['data'])."\n\n".
$call_options['data']."\n\n";

$this->assertIdentical($this->transport->_build_request($call_options, $host, $path), $expected);
$this->assertIdentical($this->transport->_build_request($call_options, $host, $path, true), $expected);
}
}

0 comments on commit 5d223bd

Please sign in to comment.