Simple Spark PHP Proxy


#1

There's at least a half dozen PHP proxies/wrappers in the forums already, but I wanted one that was a little more flexible. This one should take whatever GET or POST request you throw at it, append your access token, and then pass it on to the Spark Cloud. The code is pasted below, but is also tracked in this gist.

<UPDATE> I have added an updated version that's a little more transparent if you are using Apache mod_rewrite. </UPDATE>

proxy.php without Apache mod_rewrite:

<?
// Set your access token here
define('ACCESS_TOKEN', 'your_access_token_here');

// All responses should be JSON
header('Content-type: application/json');

// Security check!
if(!isset($_SERVER['HTTP_REFERER']) || substr_count($_SERVER['HTTP_REFERER'], $_SERVER['SERVER_NAME'])==0)
        die(json_encode(array(
                'error' => 'Invalid request'
        )));

// Build the URL.  Since it's possible to accidentally have an
// extra / or two in $_SERVER['QUERY_STRING], replace "//" with "/"
// using str_replace().  This also appends the access token to the URL.
$url = 'https://'.str_replace('//', '/', 'api.spark.io/v1/devices/'.$_SERVER['QUERY_STRING'].'?access_token='.ACCESS_TOKEN);


// HTTP GET requests are easy!
if(strtoupper($_SERVER['REQUEST_METHOD'])=='GET')
        echo file_get_contents($url);

// HTTP POST requires the use of cURL
elseif (strtoupper($_SERVER['REQUEST_METHOD'])=='POST') {
        $c = curl_init();

        curl_setopt_array($c, array(
                // Set the URL to access
                CURLOPT_URL => $url,
                // Tell cURL it's an HTTP POST request
                CURLOPT_POST => TRUE,
                // Include the POST data
                // $HTTP_RAW_POST_DATA may work on some servers, but it's deprecated in favor of php://input
                CURLOPT_POSTFIELDS => file_get_contents('php://input'),
                // Return the output to a variable instead of automagically echoing it (probably a little redundant)
                CURLOPT_RETURNTRANSFER => TRUE
        ));

        // Make the cURL call and echo the response
        echo curl_exec($c);

        // Close the cURL resource
        curl_close($c);
}
?>

Time for an example! I'm going to assume you name the file proxy.php and put it in the root of your web directory (/proxy.php). Also, make sure you change the define('ACCESS_TOKEN', 'your_access_token_here'); to match your access token.

Simple Ajax GET and POST requests using jQuery might look something like this:

var CORE_ID = 'your_core_id';

$.get('/proxy.php?'+CORE_ID+'/variable_name', function(response) {
        console.log(response);
});

$.post('/proxy.php?'+CORE_ID+'/function_name', {'args': 'function_arguments'}, function(response) {
        console.log(response);
});

If you want to make the Ajax calls a little less awkward, you can use Apache's mod_rewrite to make the URLs a little more transparent. Instead of making calls to /path/to/proxy.php?YOUR_CORE_ID/yourVariableOrFunction, you can use it almost exactly like you use the real cloud API like /v1/devices/YOUR_CORE_ID/yourVariableOrFunction.

proxy.php with Apache mod_rewrite:

<?
define('ACCESS_TOKEN', 'your_access_token_here');

header('Content-type: application/json');

// Security check!
if(!isset($_SERVER['HTTP_REFERER']) || substr_count($_SERVER['HTTP_REFERER'], $_SERVER['SERVER_NAME'])==0)
        die(json_encode(array(
                'error' => 'Invalid request'
        )));

$url = 'https://api.spark.io'.$_SERVER['REQUEST_URI'].'?access_token='.ACCESS_TOKEN;

if(strtoupper($_SERVER['REQUEST_METHOD'])=='GET')
	echo file_get_contents($url);
elseif (strtoupper($_SERVER['REQUEST_METHOD'])=='POST') {
	$c = curl_init();
	curl_setopt_array($c, array(
		CURLOPT_URL => $url,
		CURLOPT_POST => TRUE,
		CURLOPT_POSTFIELDS => file_get_contents('php://input'),
		CURLOPT_RETURNTRANSFER => TRUE
	));

	echo curl_exec($c);

	curl_close($c);
}
?>

.htaccess:

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^proxy\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . proxy.php [L]
</IfModule>

And here's what those jQuery Ajax examples would look like using the mod_rewrite version:

var CORE_ID = 'your_core_id';

$.get('/v1/devices/'+CORE_ID+'/variable_name', function(response) {
        console.log(response);
});

$.post('/v1/devices/'+CORE_ID+'/function_name', {'args': 'function_arguments'}, function(response) {
        console.log(response);
});

^-- That looks familiar, doesn't it? --^

I hope someone finds this useful. I swear I've written this thing 3+ 4+ times and keep losing it or deleting it as I build new projects and delete old ones!


Looking for some help to control spark via web
Controlling the Spark Core pins with webpage buttons
Tutorial: Getting Started with Spark.publish()
Outdoor Lighting Relay Control
SCRIPTs in the SPARK world
Colorpicking php site
Feedback on PHP Proxy remote control implementation
Putting Access_Token to rest - inside the core - Most Secure
Outdoor Lighting Relay Control
Alternate Android & IOS dev platforms
Mobile Web Spark Control Panel
Best practices for Spark Variable update frequency?
A public spark.function, with no token
#2

Thanks for sharing this file. How we can use this file can you write an example ?


#3

Sure!

I updated the top post to include jQuery examples. I also added a version that makes API calls through the proxy.php look a little more transparent (it looks like regular Spark Cloud API calls).


#4

I just updated the script with a security check to make sure that the HTTP_REFERER matches the SERVER_NAME. It's not bullet-proof, but hopefully it will add a little layer of security through obscurity. It does make it a little tougher to debug if you aren't using something that automagically sends the HTTP_REFERER.

I'll keep thinking about it to see if I can't come up with a better solution.


#5

// Security check!
if(!isset($_SERVER['HTTP_REFERER']) || substr_count($_SERVER['HTTP_REFERER'], $_SERVER['SERVER_NAME'])==0)
        die(json_encode(array(
                'error' =&gt; 'Invalid request'
        )));

@wgbartley thanks for putting this together! I'm using this as a relay for a different purpose. Can you explain how the SECURITY CHECK works? What is it looking for exactly from the original request. I'm not a PHP expert so I'm kind of guessing it's something needed in the header but I don't know what.


#6

It checks for two things. First, it checks if the HTTP_REFERER is set. Next to that, it checks that the HTTP_REFERER contains the SERVER_NAME (domain name). If it doesn't have a referrer and the referrer is not from the same domain name as the PHP script, it bombs out. The referrer is a header that can be easily set in the headers to circumvent this, but it's just one extra layer of annoyance for a potential "hacker" to figure out.


#7

Well that's pretty good security when I know what it's looking for and I still can't make it work. I'm running the script on my website, and making test calls from hurl.it. There is a box for headers.. and I've defined it like this:

I know it's expecting "technobly.com" as the SERVER_NAME since I added it to the error message like this 'error' => 'Invalid request on '.$_SERVER['SERVER_NAME'] obviously I'll remove that later wink

But it's not seeing the HTTP_REFERER at all since this error message is reported:

if(!isset($_SERVER['HTTP_REFERER']))
        die(json_encode(array(
                'error' => 'Sorry no HTTP_REFERER'
        )));

These are the types of problems that get you stuck for hours just staring at it wondering DO I KNOW HOW TO SPELL!???


#8

BAH, I got it... google search for HTTP_REFERER header and bob's my uncle:

Hopefully this helps noob-proof your example wink lol


#9

Hi @wgbartley, I'm trying to use your example to send a data string to my spark. I've tested it with the Spark Simple Web Interface, inputting the function name rgb and data (i.e. 255,0,0) and the core responds as it should.

I've got the following html:

<html>
<head>
  <meta charset="utf-8">
  <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js" type="text/javascript" charset="utf-8"></script>
</head>
<body>

	<a href='#!' onclick="rgb('255,0,0')">Red</a><br></br>
	<a href='#!' onclick="rgb('0,255,0')">Green</a><br></br>
	<a href='#!' onclick="rgb('0,0,255')">Blue</a>

	<script type="text/javascript">
  		var CORE_ID = 'XXXXXXXXXXXXXXXXXXXXXXXX';
			function rgb {
  				$.post('/proxy.php?'+CORE_ID+'/function_name', {'args': 'function_arguments'}, function(response) {
          		  console.log(response);
  				});
			}
  	</script>

</body>
</html>

I've got the proxy.php file in the same place (both in root directory) and have filled in the Core ID and Auth Token. I've tried a couple of different AJAX methods I found online, however just can't seem to get the data sent to the spark.


#10

Just to be sure: you are changing "function_name" to be your actual Spark.function() name, right?


#11

That's one thing I was confused about, and thought that it would be derived from my clickable object. My function is called rgb, so should that part read:

'/proxy.php?'+CORE_ID+'/rgb'

I'm also confused about the {'args': 'function_arguments'} for the same reason. Should this be fine or will I have to change it toy value (eg. 255,0,0)?


#12

Yes, I think you need to change '/function_name' to '/rgb'. Spark.functions always take a String and return an int, you need to supply the 'function_arguments' string on the web side with whatever you want to send to the core.

	<script type="text/javascript">
  		var CORE_ID = 'XXXXXXXXXXXXXXXXXXXXXXXX';
			function rgb(paramStr) {
  				$.post('/proxy.php?'+CORE_ID+'/rgb', {'args': paramStr}, function(response) {
          		  console.log(response);
  				});
			}
  	</script>

Sorry I didn't get a chance to test this, but you should get the idea.


#13

Ah, that makes a lot more sense. I'll give it a crack after work today, thanks @bko


#14

I've had such a long crack at it, but looks like there is a deeper issue. The proxy php code doesn't pass on the args value to the server. No wonder it's not working!

I've tried, but so far can't seem to figure out how to modify the code to have it pass on the args value...


#15

Let's see if @wgbartley can help us out here--maybe I got it wrong!


#16

Hey @bko – I've done some digging and packet sniffing – you were perfectly right with your script advice. The HTML is definitely passing the message across to the PHP script, which is where it kept failing.

I thought, surely if it was an error in the PHP script which @wgbartley posted in his gist that someone else would have noticed it and fixed it, so I kept on digging. I did some packet sniffing on the output of my server (Raspberry Pi) and found that it wasn't outputting anything. Checked the Apache log files, and found that it was faulting on one of the lines in the PHP file. Not being familiar with the error, I googled it, and found that it was an issue which happened to several users on a particular version of PHP on Raspberry Pi.

So, I checked to see if there were any software updates for PHP, and indeed there was. Installed the update, and all works! No need to bother @wgbartley anymore smile


#17

Nobody bothered me! @bko kept replying before I even saw the posts!


#18

My proxy.php gist has been updated to support proxying for SSE. It works on my laptop, but output buffering in PHP can be a tricky thing on some server configs, so your mileage may vary.

Take note that the update is not backwards-compatible. The original proxy used to automagically add /v1/devices/ to the URL. Since the SSE stuff may also use /v1/events/ or /v1/devices/, I had to take it out. Now when you use the proxy, you will need to specify the full API path (after the domain name) to include /v1/devices/... or /v1/events/....


#19

I’m trying to get this to work but keep getting a 404 error response in the dev console in microsoft edge.

<h1>Object not found!</h1>

The requested URL was not found on this server.



The link on the
<a href="http://***WEB ROOT***/">referring
page</a> seems to be wrong or outdated. Please inform the author of
<a href="http://***WEB ROOT***/">that page</a>
about the error.

This is my script:

function doMethod(method, data){
var CORE_ID = '4c002b000951343334363138';

$.get('/proxy.php?'+CORE_ID+'/variable_name', function(response) {
        console.log(response);
});

$.post('/proxy.php?'+CORE_ID+'/'+method, {'args': data};, function(response) {
        console.log(response);
});
}

I gather from the error that it’s not the php that is the problem? regardless, here’s the snip from my php showing the bits that I’ve changed

    // Set your access token here
define('ACCESS_TOKEN', '123456789876543321abcdefghijk');
// Make sure we have an HTTP_ACCEPT header,
// and if so, make it lower-case for easier string matching
if(isset($_SERVER['HTTP_ACCEPT']))
	$_SERVER['HTTP_ACCEPT'] = strtolower($_SERVER['HTTP_ACCEPT']);
else
	$_SERVER['HTTP_ACCEPT'] = '';
// Build the URL.  Since it's possible to accidentally have an
// extra / or two in $_SERVER['QUERY_STRING], replace "//" with "/"
// using str_replace().  This also appends the access token to the URL.
$url = 'https://'.str_replace('//', '/', 'api.particle.io/v1/devices/'.$_SERVER['QUERY_STRING'].'?access_token='.ACCESS_TOKEN);

#20

Sorry to revive this thread; thanks for this–So far, this php proxy been extremely helpful, and I’ve gotten variables, methods, and published events to my devices without a problem!

I’m trying to build a pretty web AJAX interface to control an LED matrix attached to my photon. I’m using publish/subscribe events to communicate. I own the webserver so I’m able to use the php proxy.

Two questions:
1.) How can I subscribe to an event using your SSE? I’ve tried a jQuery “get” and an EventSource (thx Bko) but neither gave me any output when using the proxy (although I could do it with a hard-link including my access_token).

2.) given the age of this topic, is a proxy still considered a good methods for making a webpage with javascript event pub/sub? Am I better off with webhooks?

Code that doesn’t work:

  function eventGet(){
  	$.get('/files/particle_proxy.php?events',function(e){
  		console.log('event recieved');
        var parsedData = JSON.parse(e.data);
        var datDiv = document.getElementById("container");
        datDiv.innerHTML = parsedData["data"];
  	});
  }