Guzzle and react Promise cause infine loop - promise

I use Guzzle 6 to call asynchrous request and I then use React Promise/Deferred and Event loop , then I use php-react-block to get result by resolved.
Firstly I send http Request as following:
public function callService($endpoint){
$requestDeferred = new React\Promise\Deferred();
$requestParams = new Request("POST", $endpoint, $header, $message);
$client = new Client();//Guzzle http client
$client->sendAsync($requestParams)->then(
function (ResponseInterface $res) use($requestDeferred) {
// $res->getStatusCode() . "\n";
$requestDeferred->resolve($res->getBody()->getContents());
},
function (RequestException $e) use($requestDeferred) {
$requestDeferred->reject();
}
);
return $requestDeferred->promise();
}
After I call this method as following
$loop = React\EventLoop\Factory::create();
$requestPromise = $this->callService( $endpoint);
$responseXml = Clue\React\Block\await($requestPromise, $loop);// I want to block/wait to until promise is resolved and get resolved value.
But when I call Clue\React\Block\await($requestPromise, $loop) , the system loops infinetly and any promise can not be resolved. Also I added queue->run() method to run method of related event (LibEvent). But system still loops infinetly.
Why the system loops infinetly?
Thanks for your helps

Related

Get websocket data in laravel controller

I have an external WebSocket link where im getting data. My requirement is to receive those data in laravel controller and handle them.
How to listen in controller.
Finally i found the solution.
this has helped me.
$clientWebSoket = new \WebSocket\Client(
'wss://somelink'
);
$clientWebSoket->send(
'{"method":"test","symbols":"some data"}'
);
while ($i) {
try {
$message = $clientWebSoket->receive();
dump($message);
} catch (\WebSocket\ConnectionException $e) {
dd($e);
}
}
$clientWebSoket->close();

Event listener never asserts while testing on Laravel / PHPUnit

I'm trying to send a test email and assert (using events) that it was sent.
The test runs fine and the event in fact happen (I'm logging the event handler method on LogSendingMessage class), but the expectsEvents never assert, neither before, neither after the Mail::send method.
public function testSendSimpleMailOverSMTP()
{
// Send methods
$fromMail = 'my#mail.com';
$fromName = 'Tiago Gouvêa';
$toMail = 'myanother#mail.com';
$toName = 'Tiago Gouvêa';
$subject = 'Mail testing123';
$view = 'SimpleMail';
$data = array('body' => "Olá fulano");
$this->expectsEvents(Illuminate\Mail\Events\MessageSending::class);
$this->expectsEvents(Illuminate\Mail\Events\MessageSent::class);
Mail::send($view, $data, function (Illuminate\Mail\Message $message) use ($toMail, $toName, $fromMail, $fromName, $subject) {
$message->to($toMail, $toName)->subject($subject);
$message->from($fromMail, $fromName);
});
}
Here on stack has another questions some like, but, none of then worked for me. I appreciate any help. :)

Return Image as http-response from Listener Symfony2

This listener sends me reports on all kinds of exceptions that occur on the website. Sometimes I get reports of images that have been deleted but are still consulted by search engines and others.
I want to do, instead of displaying an error message " 404 Not Found " return the correct image. To do this I created a database table that stores the old links and new links of the images that have been deleted, moved or changed its name.
then, this listener find in db the links to fallen and gets the new links of images . The goal is to return the image as http-response with header content-type as image.
My code is:
class ExceptionListener
{
private $service_container;
private $router;
function __construct(Container $service_container, $router){
$this->service_container = $service_container;
$this->router = $router;
}
public function onKernelException(GetResponseForExceptionEvent $event){
$exception = $event->getException();
$request = $this->service_container->get('request');
...
$document_root = $request->server->get('DOCUMENT_ROOT');
$filename = realpath($document_root . '/'. '/path/to/new_image.jpg');
$response = new \Symfony\Component\HttpFoundation\Response();
$response->headers->set('Content-type', 'image/jpeg');
$response->headers->set('Content-Disposition', 'inline; filename="' . basename($filename) . '";');
$response->headers->set('Content-length', filesize($filename));
$response->sendHeaders();
$response->setContent(file_get_contents($filename));
return $response;
...
}
}
The following error occurs:
In the browser you can see a small box , it is as if trying to show the image but the image source could not be obtained . But if the same code is testing on controller , its working properly and the image is displayed .
What can I do to return image from a listener ? thanks
There's few things wrong about the code snippet from your question.
Firstly, you should never use the request service. It's deprecated since Symfony 2.4 and was removed in Symfony 3.0. Use the request stack (request_stack) instead.
Secondly, do not send the response yourself, but let the framework do it. Symfony events system is designed for flexibility (see the docs). In your case it's enough to set the response on the event object.
Finally, you don't need the service container to access the request at all, as it's available on the event.
Moreover, instead of the standard Response class you can use the BinaryFileResponse. It's purpose is to serve files (have a look at the docs).
You can greatly simplify your listener:
use Symfony\Component\HttpFoundation\BinaryFileResponse;
class ExceptionListener
{
private $router;
function __construct($router)
{
$this->router = $router;
}
public function onKernelException(GetResponseForExceptionEvent $event)
{
$exception = $event->getException();
// request is avialable on the event
$request = $event->getRequest();
// ...
$file = 'path/to/file.txt';
$response = new BinaryFileResponse($file);
// ... set response parameters ...
// finally, set the response on your event
$event->setResponse($response);
}
}

$q.all() in angularJS

I have a set of ajax calls that should happen asynchronously and after all the ajax calls are made, some function should be executed. I am using $q.all().then() for this.
The problem with $q.all() is that even if one of the ajax calls fails the function won't get executed. My requirement is to execute the function regardless of the success or failure of the individual ajax calls. How can I go about it?
Using $q.allSettled instead of $q.all solves the problem.
Please refer to the following link for decorating the $q.all function
http://www.codeducky.org/q-allsettled/
You can resolve a promise with another promise, which makes it possible for you to do something like this:
var urls = [...];
var promises = urls.map(function (url) {
return $http.get(url).then(null,
// error callback returns a new promise
function (response) { return $q.resolve(response); });
});
$q.all(promises).then(...);
Fiddle: http://jsfiddle.net/j4b7pxmt/
you can use this code to implement this logic.
definition of GroupPromise plugin.
//create a group promise function.
var GroupPromise = function(){
this.allPromise = [];
this.successPromises = [];
this.failurePromises = [];
}
//use this function to add promises which you want to resolve on success or error.
GroupPromise.prototype.add = function(promiseToAdd){
var promise = $q.defer();
var self = this;
promiseToAdd.then(function(){
var args = angular.copy(arguments);
args.push({state: 'success'});
self.successPromises.push(promise);
promise.resolve.apply(this, args);
}, function(){
var args = angular.copy(arguments);
args.push({state = 'failure'});
self.failurePromises.push(promise);
promise.resolve.apply(this, args);
});
this.allPromise.push(promise);
}
//use this to resolve all promises.
GroupPromise.prototype.resolveAll = function(successCallback, errorCallback){
var self = this;
$q.all(this.allPromise).then(function(){
//all API calls processed.
if(self.failurePromises.length === 0){
//no API fails
successCallback.call(self);
}else{
//some API fails
errorCallback.call(self);
}
});
}
usage of GroupPromise plugin.
//create an object of GroupPromise.
var myGroupPromise = new GroupPromise();
//add API call promises to queue.
angular.forEach([1,2,3,4], function(){
myGroupPromise.add($http.get(url));
});
//call for resolve all promises and pass the success and error callback.
myGroupPromise.resolveAll(successCallback, errorCallback);
You have to use individual defers such as:
var myDefer = $q.defer();
myDefer.promise.then(function(result) {
}, function(rejected) {
});

Send user ID from browser to websocket server while opening connection

Before asking this question, I did my best by reading severel
questions on SO (tagged Ratchet and dealing with similar issues but to
no avail. I even asked a question which received no attention and I
therefore deleted it to write another one (that hopefully is more
clear).
My final goal is to build a one-to-one private chat application using Ratchet. Everything is working fine except that I can't send message to a specific user.
Every logged in user connects to the websocket server while accessing secured area of website:
$(document).ready(function() {
var conn = new WebSocket('ws://localhost:8080');
conn.onopen = function(e) {
console.log("Connection established!");
// Here I need to send the logged in user_id to websocket server
// and get it in onOpen method so that I can index my array
// of connections with user_id instead of
//$connection->ResourceId, I explain more below
};
conn.onmessage = function(e) {
console.log(e.data);
};
});
When a user writes a message in the chat box, the message is sent via AJAX to web server then pushed to Websocket using ZeroMQ. In the controller:
// Persistence of Message(message_id, sender_id, receiver_id, message_text)
.....
$context = new \ZMQContext();
$socket = $context->getSocket(\ZMQ::SOCKET_PUSH, 'my pusher');
$socket->connect("tcp://localhost:5555");
$pushData = array(
'receiver_id' => $receiver_id,
'sender_id' => $user->getId(),
'message' => $message->getMessageText(),
);
$socket->send(json_encode($pushData));
So at the end, my websocket server is able to know which is the id of receiver using the JSON. But how will he know which is the connection of that user? In other words, I need to store websocket connections in an array that is indexed by the user id.
<?php
namespace RealTime;
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;
use Ratchet\Wamp\WampServerInterface;
class Pusher implements WampServerInterface, MessageComponentInterface{
private $clients;
public function onOpen(ConnectionInterface $conn) {
$this->clients[$conn->resourceId] = $conn;
// I need here to get the user_id received from browser while opening connection
}
public function onMessageEntry($entry) {
$entryData = json_decode($entry, true);
//This is not what I need (It sends to all users in array)
foreach ($this->clients as $key => $client) {
$client->send($entryData['message']);
}
}
public function onMessage(ConnectionInterface $from, $msg) {
echo $msg;
}
}
And the websocket server:
<?php
require dirname(__DIR__) . '/vendor/autoload.php';
use RealTime\Pusher;
$loop = React\EventLoop\Factory::create();
$pusher = new Pusher;
$context = new React\ZMQ\Context($loop);
$pull = $context->getSocket(ZMQ::SOCKET_PULL);
$pull->bind('tcp://127.0.0.1:5555');
$pull->on('message', array($pusher, 'onMessageEntry'));
$webSock = new React\Socket\Server($loop);
$webSock->listen(8080, '0.0.0.0');
$webServer = new Ratchet\Server\IoServer(
new Ratchet\Http\HttpServer(
new Ratchet\WebSocket\WsServer(
new Ratchet\Wamp\WampServer(
$pusher
)
)
),
$webSock
);
$loop->run();
?>
Questions:
How to send the logged in user_id from client side while opening connection.I need to have the value in websocket server so that I can index my array of clients with it ($client[user_id]=$conn instead of $client[recourceId]=$conn). I tried the javascript function send but I don't know where to receive the sent data (even onMessage is not printing anything).
Why the onMessage method is not executing even MessageComponentInterface implemented (Is it because I have onMessageEntry method + $pull->on('message', array($pusher, 'onMessageEntry')); line of code?
Thank you.
Actually on my last try, I gave up on PHP WebSocket (it was so complicated to make this work) and started using SocketIO with nodeJS that solved my entire problem and could give me a functionnal simple Chat system.
This is what I found and any suggestions to enhance this solution are welcome.
One can use the Ratchet SessionProvider. This will require using one of the Symfony Custom Session handlers as indicated. I use in the following code the PdoSessionHandler.
<?php
require dirname(__DIR__) . '/vendor/autoload.php';
use YourDirectory\Pusher;
use Symfony\Component\HttpFoundation\Session\Storage\Handler;
use \Ratchet\Session\SessionProvider;
$pusher = new Pusher;
$pdo = new PDO('mysql:host=localhost;dbname=community', 'root', null);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
//This info is related to you db
$dbOptions = array(
'db_table' => 'session',
'db_id_col' => 'sess_id',
'db_data_col' => 'sess_data',
'db_time_col' => 'sess_time',);
$loop = \React\EventLoop\Factory::create();
$context = new \React\ZMQ\Context($loop);
$pull = $context->getSocket(\ZMQ::SOCKET_PULL);
$pull->bind('tcp://127.0.0.1:5555');
$pull->on('message', array($pusher, 'onMessageEntry'));
$webSock = new React\Socket\Server($loop);
$webSock->listen(8080, '0.0.0.0'); // Binding to 0.0.0.0 means remotes can connect
$webServer = new Ratchet\Server\IoServer(
new Ratchet\Http\HttpServer(
new Ratchet\WebSocket\WsServer(
new SessionProvider(
new Ratchet\Wamp\WampServer(
$pusher
),new Handler\PdoSessionHandler($pdo,$dbOptions)
)
)
),
$webSock
);
$loop->run();
?>
Then my stub class will become:
public function onOpen(ConnectionInterface $conn) {
$this->clients[$conn->Session->get('current_user_id')] = $conn;
}
public function onMessageEntry($entry) {
$entryData = json_decode($entry, true);
$ReceiverConnection=$this->clients[$entryData['receiver_id']];
$ReceiverConnection->send($entryData['message']);
}
But before, I have added the user id to the session in Web server (in the controller that returns the initial page)
$user = $this->getUser();
$request->getSession()->set('current_user_id', $user->getId());
PS:
Moving to PdoSessionHandler can be done by implementing this (Symfony).
I still can't answer 2 but all the logic that can be put onMessage is now moved to onMessageEntry which satisfies temporarly the needs.
As an alternative way to make the association between your clientConnection and his ID you need to send a message using websockets just after opening the connection to your websocket server this message will contain your user id you will use it to index his connection object by his ID in your array.
For the second question as I know the default websocket implementation is not working properly specialy with pubsub protocol you need to use a websocket library for that I suggest to use AutobahnJS it's a good websocket library with a lot of wonderfull features.

Resources