PHP Secure Websocket Client Trouble, Needs to be Non Blocking - php

I'm building a dashboard that allows me to visualise my crontab as it runs (Think a queue of upcoming tasks, those that are running currently and those that have finished and whether the outcome was successful.) To do this, I need to send messages from the tasks (running or monitored by PHP) on my server, to the client browsers that run the dashboard using javascript. It also has to be secure.
To solve this issue I implemented a Twisted/Autobahn socket server in Python, which worked fine once I had paid for proper security certificates. The problem I have however is getting the PHP running the crontasks to be able to send messages to the webSocket server which passes them on to the client browsers, so far I have hacked this by writing a Python client that accepts the message to send as an argument and run this as an exec from PHP.
Obviously this is not a robust solution (which is also relatively slow to execute) and I'd now like to send logfile entries from the crontasks over websockets to my dashboards so I can see what's happening on my servers as tasks run. I've been looking for a while and tried various approaches (most are too long to post) however they range from tutorials, to segments from the PHP website to libraries such as Thruway (which seems too over-engineered for my use case, specialised and hard to adapt).
The best progress I have so far is Pawl, and using the following code I'm able to successfully send three messages to the Python Socket Server using wss:
<?php
require __DIR__ . '/vendor/autoload.php';
\Ratchet\Client\connect('wss://127.0.0.1:9000')->then(function($conn) {
$conn->on('message', function($msg) use ($conn) {
echo "Received: {$msg}\n";
$conn->close();
});
$conn->send('MSG 1');
$conn->send('MSG 2');
$conn->send('MSG 3');
}, function ($e) {
echo "Could not connect: {$e->getMessage()}\n";
});
?>
(Note that this depends on the libraries found here)
The problem I have is that I would like to be able to open and close the connection and send messages as separate steps, in the code example (which I've had difficulty adapting) it seems that as open, send and close are all wrapped in the then method and anonymous function I cannot call these methods seperately. Ideally I'd like to open the connection at the beginning of my crontask execution, each time a message is logged call the send method and close the connection at the end without wasting time opening and closing a connection to my socket server for each and every message. Please note that listening for replies isn't necessary.
Also, any solutions that work to 127.0.0.1:9000 over WSS and don't need libraries or use a different one I'm happy to consider. Please also note (after seeing other posts) this question specifically refers to a websocket client, not a server.
Many thanks,
James

Leaving this in case anybody else finds this final solution welcome:
In the end I wrapped a module called Textalk by Fredrik Liljegren et al in a small class to make it more accesible and this solved my issue.
Here is the code I used in the end:
require('vendor/autoload.php');
use WebSocket\Client;
class secureSocketClient {
private $OClient;
function __construct($VProtocol, $VLocation, $VPort, $VDir) {
$this->OClient = new Client("$VProtocol://$VLocation:$VPort" . ($VDir != null ? "/$VDir" : ""));
}
function sendMessage($ORequestData) {
$VLocalMessage = json_encode($ORequestData);
$this->OClient->send($VLocalMessage);
}
function __destruct() {
$this->OClient->close();
}
}
Which can be invoked as so:
require_once <class location>
$this->OSecureSocketClient = new secureSocketClient("wss", "127.0.0.1", "9000", null);
$this->OSecureSocketClient->sendMessage($OMSG1);
$this->OSecureSocketClient->sendMessage($OMSG2);
$this->OSecureSocketClient->sendMessage($OMSG3);
To install textTalk (on linux), you can use the following commands in the directory where the class will reside:
curl -sS https://getcomposer.org/installer | php
add the following to composer.json (in the same directory):
{
"require": {
"textalk/websocket": "1.0.*"
}
}
then run the following:
sudo php composer.phar install
Regards,
James

Related

ftp_rawlist always fails on FTPES server with passive mode

I have to connect to a FTPES server to retrieve data. Connecting and logging in works just fine, but my call with ftp_rawlist always fails and returns "false".
I am using this code for debugging purposes:
$ftp = ftp_ssl_connect($ftp_host);
if (ftp_login($ftp, $ftp_user, $ftp_pass)) {
$p = ftp_pasv($ftp, true);
var_dump($p);
$r = ftp_rawlist($ftp, '/', true);
var_dump($r);
} else {
echo 'Could not login';
}
$p is always true, $r always false.
When I connect to the server through Filezilla everything works fine and I can list directory content and more.
Update #1: Tried to not only list '/' but various subfolders on the server, they all fail through the script.
Update #2: Also tried to use ftp_raw with the commands to get a list, but the LIST command runs for some time and then does not return any result at all. But HELP lists LIST as a valid command for the server... Strange...
Update #3: I tried phpseclib now, but while I can connect, I can't login with the user/password combination. Support from the maintainer of the FTPES server is not happening ("works fine for $somebody else..."), so I need to figure this out another way... :-)
To come to an end with this: As the deadline for this project came closer a solution had to be found. And although this is no real answer in the sense of a question, I'd like to show what I have done to have this fixed. Maybe someone stumbles upon this through googling.
Next to the things mentioned in the OP, I also tried connecting to FTPS using PHP and certificate as auth which didn't work either. As nothing works as it is supposed to, I wonder if the FTPS server is really configured correctly after all.
The people who run the server told me that everything is fine and their CLI CURL-call works fine for them, so they have no need to further investigate issues.
As a result of this I set up a sandbox account on a server which has shell_exec() enabled. There is now a script running which gets a file listing via CURL and then downloads the files via CURL with the commands provided by the server provider. That server can be accessed through normal SFTP and therefore acts as a "Proxy FTP" which regularly mirrors the remote FTPS server file structure.
Although I find this "solution" quite "hacky" it seems to run robust, stable and fast for the moment. We will therefore be able to have the operation running this way in this year (it only runs around three months before christmas) and will have a look into it in the new year and develop a more stable solution.
Maybe the server guys are also less stressed then and willing to help... ;-)
Add the following call to ftp_set_option() in a line before the call to ftp_pasv
ftp_set_option($ftp, FTP_USEPASVADDRESS, false);
ftp_pasv($ftp, true);

How to connect to ESL from a remote server?

I would like to make a web interface in PHP to see the FreeSWITCH activities (calls, etc), possibly hosted on a different server than the one where FS is running.
I've seen the server status on the FS server using command line (php single_command.php status), but now I would like to see this status from another server.
When I try to copy ESL.php file to this remote server and try to check the status, I get this error message:
Fatal error: Call to undefined function new_ESLconnection() in
/var/www/freeswitch/ESL.php on line 127
This is my index.php file:
<?php
ini_set('display_errors', 1);
$password = "ClueCon";
$port = "8021";
$host = "192.168.2.12";
require_once('ESL.php');
set_time_limit(0); // Remove the PHP time limit of 30 seconds for completion due to loop watching events
// Connect to FreeSWITCH
$sock = new ESLconnection($host, $port, $password);
// We want all Events (probably will want to change this depending on your needs)
$sock->sendRecv("status");
// Grab Events until process is killed
while($sock->connected()){
$event = $sock->recvEvent();
print_r($event->serialize());
}
?>
I undestand that the webserver doesn't have FreeSWITCH installed, so the error message is obvious, but i don't see how to access to this information from this webserver.
Thank you for your help.
Depending upon your need you can use either Inbound or Outbound socket. I do not know much about PHP and FS Event Socket but yeah tried enough with python. I highly recommended to go through thislink.
So if you just want to do small task like initiating a call, bridging any two given number etc i think you should go with Inbound socket(making cli command from your web server to freeswitch server) or mod_xml_rpc.
And if you want to have full control of everything that happens on FS server like showing live call status and modifying their states or say a complete interactive telephony dashboard then you should go with Outbound socket.(Your FS server will send all events to your web server.)
However in your case problem is I think you did not properly build the php ESL module.
this link might help you installing ESL
Rather than using ESL, you might want to consider using the XMLRPC. The connection is very straight forward:
https://wiki.freeswitch.org/wiki/Freeswitch_XML-RPC
The credentials for the XMLRPC are in your autoloads_configs/xml_rpc.conf.xml

Php Serial to Arduino on linux openwrt

I'm trying to get a web page based serial communication working with an Arduino which is connected to a router running Openwrt, it does work but only when either screen is running or remotely connected via putty, for some reason the php to serial is not starting a session properly? I use ser2net to manage the serial with the following setting
1000:raw:0:/dev/ttyACM0:9600
I have also tried stty with various settings advised on this forum
Update 1
also tried 80:raw:0:/dev/ttyACM0:9600 as setting for ser2net assuming port 80 is web/http port
my php code is
if (isset($_GET['action']))
{
$serial = new phpSerial();
$serial->deviceSet('/dev/ttyACM0');
$serial->confBaudRate(9600);
$serial->confParity('none');
$serial->confCharacterLength(8);
$serial->confStopBits(1);
$serial->confFlowControl('none');
$serial->deviceOpen();
if ($_GET['action'] == "PIN_12_HIGH")
{
$serial->sendMessage("A");
}
if ($_GET['action'] == "PIN_12_LOW")
{
$serial->sendMessage("B");
}
if ($_GET['action'] == "PIN_11_HIGH")
{
$serial->sendMessage("C");
}
if ($_GET['action'] == "PIN_11_LOW")
{
$serial->sendMessage("D");
}
$serial->deviceClose();
}
update 2
if i add sleep(1); to my php code it seems to solve some of the problems
a few points
adding sleep to php code allows the commands to reach the arduino after a reset caused by the serial connection, but if i turn on led 1 when i turn led 2 on led 1 gets reset to off. so this would mean a more complicated code in the form of logging an array of choices and sending the whole updated array to arduino, i would like to avoid this and just send 1 command at a time.
again when screen or putty are active the arduino does not have this reset problem? so the question is what does screen and putty do to keep the connection alive that stty and php serial does'nt?
Update 3
After finding this helpful post i seem to have discovered a possible solution with no reset hacking needed to the Arduino.
Adding cat /dev/ttyACM0 & to the start up config of the Openwrt router enables serial communication with the Arduino without reseting it on every transmission of data.
problems left:
I think this code is binding arduino to a session with the router? But im unsure.
It seems to be eating the return value from the Arduino stopping my php page getting the data return?
My System Log file contains the missing returned data from Arduino?
Update 4
As i needed to get this working, i used a capacitor to stop the auto reset from serial communication on the arduino.
As stated screen and putty dont create this reset problem when communicating with arduino, i tried many different settings with STTY but couldnt find a solution , but i am still trying and keen to discover how screen/putty does this.
It looks like your PHP is attempting to directly connect to the tty. Where the ser2net is likely already connected to the tty. Only one application at a time can be connected to the tty. Either stop the ser2net or your PHP should connect to the tcp listener of the desired tty as per your ser2net.conf

node.js running alongside Apache PHP?

I am trying to get my head round node.js...
I am very happy with my LAMP set up as it currently fulfils my requirements. Although I want to add some real-time features into my PHP app. Such as showing all users currently logged into my site and possible chat features.
I don't want to replace my PHP backend, but I do want scalable real-time solutions.
1. Can I throw node.js into the mix to serve my needs without rebuilding the whole application server-side script?
2. How best could node.js serve my 'chat' and 'currently logged in' features?
Great to hear your views!
W.
I suggest you use Socket.io along side node.js. Install and download the libs from http://socket.io/. You can run it along side your Apache server no problems.
First create a node server:
var http = require('http')
, url = require('url')
, fs = require('fs')
, io = require('../')//path to your socket.io lib
, sys = require(process.binding('natives').util ? 'util' : 'sys')
, server;
server = http.createServer(function(req, res){
var path = url.parse(req.url).pathname;
}),
server.listen(8084);//This could be almost any port number
Second, run your server from the command line using:
node /path/to/your/server.js
Third, connect to the socket using client side js:
var socket = new io.Socket(null, {port: 8084, rememberTransport: false});
socket.connect();
You will have to have include the socket.io lib client side aswell.
Send data from client side to the node server using:
socket.send({data:data});
Your server.js should also have functions for processing requests:
io.on('connection', function(client){
//action when client connets
client.on('message', function(message){
//action when client sends msg
});
client.on('disconnect', function(){
//action when client disconnects
});
});
There are two main ways to send data from the server to the client:
client.send({ data: data});//sends it back to the client making the request
and
client.broadcast({ data: data});//sends it too every client connected to the server
I suspect the chat as well as the logged in listing would work via Ajax.
The chat part would be pretty easy to program in Node.js, use one of the mysql modules for Node to connect to your existing database and query login information and such and then do all the actual chatting via Node.js, I recommend you to check out Socket.io since it makes Browser/Node.js communcation really trivial, this should allow you to focus on the actual chat logic.
Also, you could take a look at the "official" chat demo of Node.js, for some inspiration.
As far as the currently online part goes, this is never easy to implement since all you can do is to display something along the lines of "5 users active in the last X minutes".
Of course you could easily add some Ajax that queries the chat server and display the userlist from that on the homepage.
Or you completely crazy and establish a Socket.io connection for every visitor and monitor it this way, although it's questionable whether this is worth the effort.
What about using a socket file
just like pedro did with ngnx ?
http://nodetuts.com/tutorials/25-nginx-and-nodejs.html
You can run php from node js using node-php: https://github.com/mkschreder/siteboot_php
I'm running a wss (secure websocket) server alongside my LAMP setup.
Node.js can easily run alongside any other web server (apache) you want. In #KitCarrau example, he lets node run on port 8084 - that's where it's running and listening to, not 80 or 443 etc (those are usually taken by apache anyway). But you can still use the same port to also serve http/https (in my case just stating some conf and general info that the service is up).
Starting from the console isn't the best way (remotely, node stops when closing the console).
I recommend taking a look at Running node as service
Easy to track log in realtime (log with console.log("hello"); in your application) with:
tail -f /var/.../SocketServer.log
Sample script (node-server.conf):
author ....
description "node.js server"
# used to be: start on startup
# until we found some mounts weren't ready yet while booting:
start on started mountall
stop on shutdown
# Automatically Respawn:
respawn
respawn limit 99 5
# Max open files are # 1024 by default. Bit few.
limit nofile 32768 32768
script
# Not sure why $HOME is needed, but we found that it is:
export HOME="/root"
exec node /var/.../SocketServer.js >> /var/www/node/.../SocketServer.log 2>&1
end script
post-start script
# Optionally put a script here that will notifiy you node has (re)started
# /root/bin/hoptoad.sh "node.js has started!"
echo "\n*********\nServer started\n$(date)\n*********" >> /var/.../SocketServer.log
end script

Gearman return value not working

I am trying to get gearman to return a value to php from a function after php makes a request to it using $gmclient->do("somefunction", "somedata"). However, the php client simply times out. The exact code I am using is straight from the php manual. I am using example #1 from http://docs.php.net/manual/en/gearmanclient.do.php
The browser gives me this message:
This webpage is not available.
The webpage at
http://yoursite.com/client.php
might be temporarily down or it may
have moved permanently to a new web
address.
More information on this error. Below
is the original error message
Error 324 (net::ERR_EMPTY_RESPONSE):
Unknown error.
The browser is Chrome if that helps to elaborate the error message.
In case it makes a difference, the worker.php file is being executed in a terminal window using the command "php worker.php". I am running on Ubuntu 9.10 Karmic Koala. I installed gearman using the directions found at http://blog.stuartherbert.com/php/2010/02/26/getting-gearman-up-and-running-on-ubuntu-karmic/
I checked the terminal window and gearman is getting the request and echos the results into the terminal - it just not sent back to the client.
The end goal is to get gearman to return to the client the return value from the function that was executed and display that value to the user.
UPDATE:
As requested, the code is below:
worker.php (the worker)
<?php
echo "Starting\n";
# Create our worker object.
$gmworker= new GearmanWorker();
# Add default server (localhost).
$gmworker->addServer();
# Register function "reverse" with the server. Change the worker function to
# "reverse_fn_fast" for a faster worker with no output.
$gmworker->addFunction("reverse", "reverse_fn");
print "Waiting for job...\n";
while($gmworker->work())
{
if ($gmworker->returnCode() != GEARMAN_SUCCESS)
{
echo "return_code: " . $gmworker->returnCode() . "\n";
break;
}
}
function reverse_fn($job)
{
return strrev($job->workload());
}
?>
client.php (client code - this is the page I am loading the browser)
<?php
# Client code
echo "Starting\n";
# Create our client object.
$gmclient= new GearmanClient();
# Add default server (localhost).
$gmclient->addServer();
echo "Sending job\n";
$result = $gmclient->do("reverse", "Hello!");
echo "Success: $result\n";
?>
The comments below where it said it was working.. I repeat, it was NOT working. It only appeared to work because I changed $gmclient->do to $gmclient->doBackground which output the job ID, not the actual result from the function.
FINAL UPDATE (WITH SOLUTION)
After some work, I've figured out that it was not a coding error. Gearman was improperly installed. Instead of using apt-get install, I decided to do things manually. I downloaded the gearmand (c) from the gearman site (http://gearman.org/index.php?id=download). I then used the tutorials on the gearman site as well starting with http://gearman.org/index.php?id=getting_started and then http://gearman.org/index.php?id=gearman_php_extension
Change your code and use GearmanClient::addTask. You can use its return value to implement monitoring process. Check on the other functions that you can use on the GearmanTask class.
//Wrong
$gmworker->addServer();
//Correct
$gmworker->addServer("localhost",4730);

Categories