Skip to content

Commit

Permalink
version 1.2
Browse files Browse the repository at this point in the history
major update to rest_proxy.php, version release.
  • Loading branch information
perrytew committed Apr 2, 2015
1 parent 6eb1938 commit 02ca1c6
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 66 deletions.
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,24 @@ https://secure.ultracart.com/merchant/integrationcenter/checkoutapi_v3/demo1.htm
It gives examples of the REST API calls used in the responsive checkout.


responsive_checkout (version 1.2)
======================================
This release is considered a mandatory upgrade. Of this release, the changes in the rest_proxy.php script are most important.
Please upgrade your rest_proxy.php scripts as soon as possible. Doing so will prevent issues with your site. Additionally,
we've added a proxy version header that will allow us to track which merchants might have out of date proxy scripts in the
future. This could prove vital to rapidly addressing any compatibility issues that might arise from future server updates.

rest_proxy.php changes:
* Fixes for content-length being sent down when original response was gziped. Would cause the client problem if the server running the proxy wasn't gziping it as well
* We have disabled gzip upstream until 4/15/2015 at which point everyone should have their proxy scripts upgraded.
* Added a flag that can be set to enable debugging to the error_log instead of having to uncomment all the statements.
* Change SSL certificate verify flag.
* Set an empty Expect header in the request to prevent curl from sending the Expect: 100-Continue header.
* Simplify the HTTP 100 Continue header trimming and allow for multiple of them
* Close out the curl handle sooner.
* Add a proxy version number to the header so we can tell from the server side if people are running out of date proxies


responsive_checkout (version 1.1)
======================================

Expand Down
91 changes: 58 additions & 33 deletions cart_implementations/backbone/rest_proxy.php
Original file line number Diff line number Diff line change
@@ -1,14 +1,33 @@
<?php
// Version 0.9 03/21/2015
// Fixes for content-length being sent down when original response was gziped. Would cause the client problem if the server running the proxy wasn't gziping it as well
// We have disabled gzip upstream until 4/15/2015 at which point everyone should have their proxy scripts upgraded.
// Added a flag that can be set to enable debugging to the error_log instead of having to uncomment all the statements.
// Change SSL certificate verify flag.
// Set an empty Expect header in the request to prevent curl from sending the Expect: 100-Continue header.
// Simplify the HTTP 100 Continue header trimming and allow for multiple of them
// Close out the curl handle sooner.
// Add a proxy version number to the header so we can tell from the server side if people are running out of date proxies

// Version 0.8 11/27/2013
// The cleaning of the _url variable within gzdecode was removing dashes, underscores and spaces, which are valid in urls. Fixed.

// Version 0.7. 08/15/2013
// Some of the PUT/POST requests were returning back 100 Continues. That was wrecking havoc with the parser below causing aborted calls.

// Version 0.6. 06/22/2013
// Headers weren't being handled correctly. Server http status wasn't being passed along.
// Headers with multiple values weren't iterated correctly and were being mangled (think multiple 'Set-Cookie')

// Version 0.5. 02/07/2013 Initial Version.

//error_log("$_SERVER[REQUEST_URI]");
$rest_proxy_version = "0.9";

// Set this variable to true if you want to troubleshoot output in the PHP error_log location
// The location of this log file is dependant on your php.ini file. Check the location with the phpinfo function.
$proxyDebug = false;

if ($proxyDebug) error_log("$_SERVER[REQUEST_URI]");

function http_parse_headers($header)
{
Expand Down Expand Up @@ -94,13 +113,16 @@ function gzdecode($data)
$header[] = "Content-Type: " . $content_type;
$header[] = "Content-Length: " . strlen($post_data);
$header[] = "X-UC-Forwarded-For: " . $_SERVER['REMOTE_ADDR'];
$header[] = "X-UC-Proxy-Version: " . $rest_proxy_version;
// Force curl to send an empty Expect header on the request to prevent it form sending an Expect 100 (StackOverflow FTW)
$header[] = "Expect: ";

$ch = curl_init($server_get_url);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $_SERVER['REQUEST_METHOD']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 100);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, TRUE);
curl_setopt($ch, CURLOPT_VERBOSE, 1);
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_ENCODING, 1);
Expand All @@ -110,69 +132,72 @@ function gzdecode($data)
}

$response = curl_exec($ch);
//error_log("start response ===========================================");
//error_log("start raw response ===============");
//error_log($response);
//error_log("end raw response ===============");

if ($proxyDebug) error_log("start response ===========================================");
if ($proxyDebug) error_log("start raw response ===============");
if ($proxyDebug) error_log($response);
if ($proxyDebug) error_log("end raw response ===============");


// Trim off HTTP 100 response headers if they exist
$delimiter = "\r\n\r\n"; // HTTP header delimiter
while ( preg_match('#^HTTP/[0-9\\.]+\s+100\s+Continue#i',$response) ) {
$tmp = explode($delimiter,$response,2); // grab the 100 Continue header
$response = $tmp[1]; // update the response, purging the most recent 100 Continue header
} // repeat

// now we just have the normal header and the body
$parts = explode($delimiter,$response,2);
$header = $parts[0];
$body = $parts[1];

// grab the status code and set the proxy request result to that.
$first_line = '';
$beginning_of_real_http_status = 0; // a index marker for the second http status if the server returns 100 Continue (PUTS/POSTS)
if (strlen($response) > 0) {
$first_line = substr($response, 0, strpos($response, "\n") - 1);
$first_line = trim($first_line);

// Is the first line an HTTP/1.1 100 Continue?
// If so, search for the next empty line and begin there.
preg_match("/100\s+Continue/i", $first_line, $output_array);
if (count($output_array) > 0) {
// we have an HTTP Continue. Skip down to the next status code.

if (preg_match('#^\s*$#m', $response, $matches, PREG_OFFSET_CAPTURE)) {
$beginning_of_real_http_status = $matches[0][1] + 2;
}

$real_headers = explode("\n", substr($response, $beginning_of_real_http_status));
$first_line = $real_headers[0];
// $first_line = substr($response, $beginning_of_real_http_status, strpos($response, "\n", $beginning_of_real_http_status) - 1);
$first_line = trim($first_line);
}

//error_log('$first_line:[' . $first_line . ']');
if ($proxyDebug) error_log('$first_line:[' . $first_line . ']');
header($first_line);
}


//error_log('$beginning_of_real_http_status:' . $beginning_of_real_http_status);

if (curl_errno($ch)) {
print curl_error($ch);
curl_close($ch);
} else {
$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$header = substr($response, $beginning_of_real_http_status, $header_size - $beginning_of_real_http_status);
// We're through with curl at this point. Close it out as soon as possible
curl_close($ch);

// Send the rest of the headers that we are interested in
$response_headers = http_parse_headers($header);
foreach ($response_headers as $header_key => $header_value) {
if ($header_key != 'Content-Encoding' && $header_key != 'Vary' && $header_key != 'Connection' && $header_key != 'Transfer-Encoding' && $header_key != 'User-Agent') {
if ($header_key == 'Content-Length' && $header_value == "0") {
/* ignore this, it's from an HTTP 1.1 100 Continue and will destroy the result if passed along. */
} else if ($header_key == 'Content-Length') {
// Skip sending the content length header because the upstream response could have been gziped
if ($proxyDebug) error_log("Skip sending client header - $header_key: $header_value");
} else {
if (is_array($header_value)) {
foreach ($header_value as $val) {
//error_log("$header_key: $val");
if ($proxyDebug) error_log("$header_key: $val");
header("$header_key: $val", false);
}
} else {
//error_log("$header_key: $header_value");
if ($proxyDebug) error_log("$header_key: $header_value");
header("$header_key: $header_value", false);
}

}
} else {
if ($proxyDebug) error_log("Skip sending client header - $header_key: $header_value");
}
}
$body = substr($response, $header_size);
if ($proxyDebug) error_log("Outputing body");
if ($proxyDebug) error_log(strlen($body));
// Send the body
echo $body;
curl_close($ch);
}
//error_log("end response ===========================================");
if ($proxyDebug) error_log("end response ===========================================");
?>
91 changes: 58 additions & 33 deletions rest_proxy.php
Original file line number Diff line number Diff line change
@@ -1,14 +1,33 @@
<?php
// Version 0.9 03/21/2015
// Fixes for content-length being sent down when original response was gziped. Would cause the client problem if the server running the proxy wasn't gziping it as well
// We have disabled gzip upstream until 4/15/2015 at which point everyone should have their proxy scripts upgraded.
// Added a flag that can be set to enable debugging to the error_log instead of having to uncomment all the statements.
// Change SSL certificate verify flag.
// Set an empty Expect header in the request to prevent curl from sending the Expect: 100-Continue header.
// Simplify the HTTP 100 Continue header trimming and allow for multiple of them
// Close out the curl handle sooner.
// Add a proxy version number to the header so we can tell from the server side if people are running out of date proxies

// Version 0.8 11/27/2013
// The cleaning of the _url variable within gzdecode was removing dashes, underscores and spaces, which are valid in urls. Fixed.

// Version 0.7. 08/15/2013
// Some of the PUT/POST requests were returning back 100 Continues. That was wrecking havoc with the parser below causing aborted calls.

// Version 0.6. 06/22/2013
// Headers weren't being handled correctly. Server http status wasn't being passed along.
// Headers with multiple values weren't iterated correctly and were being mangled (think multiple 'Set-Cookie')

// Version 0.5. 02/07/2013 Initial Version.

//error_log("$_SERVER[REQUEST_URI]");
$rest_proxy_version = "0.9";

// Set this variable to true if you want to troubleshoot output in the PHP error_log location
// The location of this log file is dependant on your php.ini file. Check the location with the phpinfo function.
$proxyDebug = false;

if ($proxyDebug) error_log("$_SERVER[REQUEST_URI]");

function http_parse_headers($header)
{
Expand Down Expand Up @@ -94,13 +113,16 @@ function gzdecode($data)
$header[] = "Content-Type: " . $content_type;
$header[] = "Content-Length: " . strlen($post_data);
$header[] = "X-UC-Forwarded-For: " . $_SERVER['REMOTE_ADDR'];
$header[] = "X-UC-Proxy-Version: " . $rest_proxy_version;
// Force curl to send an empty Expect header on the request to prevent it form sending an Expect 100 (StackOverflow FTW)
$header[] = "Expect: ";

$ch = curl_init($server_get_url);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $_SERVER['REQUEST_METHOD']);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_TIMEOUT, 100);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, TRUE);
curl_setopt($ch, CURLOPT_VERBOSE, 1);
curl_setopt($ch, CURLOPT_HEADER, 1);
curl_setopt($ch, CURLOPT_ENCODING, 1);
Expand All @@ -110,69 +132,72 @@ function gzdecode($data)
}

$response = curl_exec($ch);
//error_log("start response ===========================================");
//error_log("start raw response ===============");
//error_log($response);
//error_log("end raw response ===============");

if ($proxyDebug) error_log("start response ===========================================");
if ($proxyDebug) error_log("start raw response ===============");
if ($proxyDebug) error_log($response);
if ($proxyDebug) error_log("end raw response ===============");


// Trim off HTTP 100 response headers if they exist
$delimiter = "\r\n\r\n"; // HTTP header delimiter
while ( preg_match('#^HTTP/[0-9\\.]+\s+100\s+Continue#i',$response) ) {
$tmp = explode($delimiter,$response,2); // grab the 100 Continue header
$response = $tmp[1]; // update the response, purging the most recent 100 Continue header
} // repeat

// now we just have the normal header and the body
$parts = explode($delimiter,$response,2);
$header = $parts[0];
$body = $parts[1];

// grab the status code and set the proxy request result to that.
$first_line = '';
$beginning_of_real_http_status = 0; // a index marker for the second http status if the server returns 100 Continue (PUTS/POSTS)
if (strlen($response) > 0) {
$first_line = substr($response, 0, strpos($response, "\n") - 1);
$first_line = trim($first_line);

// Is the first line an HTTP/1.1 100 Continue?
// If so, search for the next empty line and begin there.
preg_match("/100\s+Continue/i", $first_line, $output_array);
if (count($output_array) > 0) {
// we have an HTTP Continue. Skip down to the next status code.

if (preg_match('#^\s*$#m', $response, $matches, PREG_OFFSET_CAPTURE)) {
$beginning_of_real_http_status = $matches[0][1] + 2;
}

$real_headers = explode("\n", substr($response, $beginning_of_real_http_status));
$first_line = $real_headers[0];
// $first_line = substr($response, $beginning_of_real_http_status, strpos($response, "\n", $beginning_of_real_http_status) - 1);
$first_line = trim($first_line);
}

//error_log('$first_line:[' . $first_line . ']');
if ($proxyDebug) error_log('$first_line:[' . $first_line . ']');
header($first_line);
}


//error_log('$beginning_of_real_http_status:' . $beginning_of_real_http_status);

if (curl_errno($ch)) {
print curl_error($ch);
curl_close($ch);
} else {
$header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
$header = substr($response, $beginning_of_real_http_status, $header_size - $beginning_of_real_http_status);
// We're through with curl at this point. Close it out as soon as possible
curl_close($ch);

// Send the rest of the headers that we are interested in
$response_headers = http_parse_headers($header);
foreach ($response_headers as $header_key => $header_value) {
if ($header_key != 'Content-Encoding' && $header_key != 'Vary' && $header_key != 'Connection' && $header_key != 'Transfer-Encoding' && $header_key != 'User-Agent') {
if ($header_key == 'Content-Length' && $header_value == "0") {
/* ignore this, it's from an HTTP 1.1 100 Continue and will destroy the result if passed along. */
} else if ($header_key == 'Content-Length') {
// Skip sending the content length header because the upstream response could have been gziped
if ($proxyDebug) error_log("Skip sending client header - $header_key: $header_value");
} else {
if (is_array($header_value)) {
foreach ($header_value as $val) {
//error_log("$header_key: $val");
if ($proxyDebug) error_log("$header_key: $val");
header("$header_key: $val", false);
}
} else {
//error_log("$header_key: $header_value");
if ($proxyDebug) error_log("$header_key: $header_value");
header("$header_key: $header_value", false);
}

}
} else {
if ($proxyDebug) error_log("Skip sending client header - $header_key: $header_value");
}
}
$body = substr($response, $header_size);
if ($proxyDebug) error_log("Outputing body");
if ($proxyDebug) error_log(strlen($body));
// Send the body
echo $body;
curl_close($ch);
}
//error_log("end response ===========================================");
if ($proxyDebug) error_log("end response ===========================================");
?>

0 comments on commit 02ca1c6

Please sign in to comment.