Exemple #1 Client HTTP simple
<?php
// Lecture de la fonction de rappel
function readcb($bev, $base) {
//$input = $bev->input; //$bev->getInput();
//$pos = $input->search("TTP");
$pos = $bev->input->search("TTP");
while (($n = $bev->input->remove($buf, 1024)) > 0) {
echo $buf;
}
}
// Fonction de rappel de l'événement
function eventcb($bev, $events, $base) {
if ($events & EventBufferEvent::CONNECTED) {
echo "Connecté.\n";
} elseif ($events & (EventBufferEvent::ERROR | EventBufferEvent::EOF)) {
if ($events & EventBufferEvent::ERROR) {
echo "erreur DNS : ", $bev->getDnsErrorString(), PHP_EOL;
}
echo "Fermeture\n";
$base->exit();
exit("Fait !\n");
}
}
if ($argc != 3) {
echo <<<EOS
Trivial HTTP 0.x client
Syntax: php {$argv[0]} [hostname] [resource]
Example: php {$argv[0]} www.google.com /
EOS;
exit();
}
$base = new EventBase();
$dns_base = new EventDnsBase($base, TRUE); // Nous utilisons une résolution DNS asynchrone
if (!$dns_base) {
exit("Echec dans l'initialisation de la base DNS\n");
}
$bev = new EventBufferEvent($base, /* use internal socket */ NULL,
EventBufferEvent::OPT_CLOSE_ON_FREE | EventBufferEvent::OPT_DEFER_CALLBACKS,
"readcb", /* writecb */ NULL, "eventcb"
);
if (!$bev) {
exit("Echec dans la création du socket bufferevent\n");
}
//$bev->setCallbacks("readcb", /* writecb */ NULL, "eventcb", $base);
$bev->enable(Event::READ | Event::WRITE);
$output = $bev->output; //$bev->getOutput();
if (!$output->add(
"GET {$argv[2]} HTTP/1.0\r\n".
"Host: {$argv[1]}\r\n".
"Connection: Close\r\n\r\n"
)) {
exit("Echec dans l'ajout de la requête dans le buffer de sortie\n");
}
if (!$bev->connectHost($dns_base, $argv[1], 80, EventUtil::AF_UNSPEC)) {
exit("Connexion impossible à l'hôte {$argv[1]}\n");
}
$base->dispatch();
?>
L'exemple ci-dessus va afficher quelque chose de similaire à :
Connected. HTTP/1.1 301 Moved Permanently Date: Fri, 01 Mar 2013 18:47:48 GMT Location: http://www.google.co.uk/ Content-Type: text/html; charset=UTF-8 Cache-Control: public, max-age=2592000 Server: gws Content-Length: 221 X-XSS-Protection: 1; mode=block X-Frame-Options: SAMEORIGIN Age: 133438 Expires: Sat, 30 Mar 2013 05:39:28 GMT Connection: close <HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8"> <TITLE>301 Moved</TITLE></HEAD><BODY> <H1>301 Moved</H1> The document has moved <A HREF="http://www.google.co.uk/">here</A>. </BODY></HTML> Closing Done
Exemple #2 Client HTTP en utilisant une résolution DNS asynchrone
<?php
/*
* 1. Connexion à 127.0.0.1 sur le port 80
* en utilisant EventBufferEvent::connect().
*
* 2. Requête /index.cphp via HTTP/1.0
* en utilisant le buffer de sortie.
*
* 3. Lecture asynchrone de la réponse et l'affiche dans stdout.
*/
// Lecture de la fonction de rappel
function readcb($bev, $base) {
$input = $bev->getInput();
while (($n = $input->remove($buf, 1024)) > 0) {
echo $buf;
}
}
// Fonction de rappel de l'événement
function eventcb($bev, $events, $base) {
if ($events & EventBufferEvent::CONNECTED) {
echo "Connecté.\n";
} elseif ($events & (EventBufferEvent::ERROR | EventBufferEvent::EOF)) {
if ($events & EventBufferEvent::ERROR) {
echo "Erreur DNS : ", $bev->getDnsErrorString(), PHP_EOL;
}
echo "Fermeture\n";
$base->exit();
exit("Fait !\n");
}
}
$base = new EventBase();
echo "étape n°1\n";
$bev = new EventBufferEvent($base, /* use internal socket */ NULL,
EventBufferEvent::OPT_CLOSE_ON_FREE | EventBufferEvent::OPT_DEFER_CALLBACKS);
if (!$bev) {
exit("Echec lors de la création du socket bufferevent\n");
}
echo "étape n°2\n";
$bev->setCallbacks("readcb", /* writecb */ NULL, "eventcb", $base);
$bev->enable(Event::READ | Event::WRITE);
echo "étape n°3\n";
// Envoi de la requête
$output = $bev->getOutput();
if (!$output->add(
"GET /index.cphp HTTP/1.0\r\n".
"Connection: Close\r\n\r\n"
)) {
exit("Echec lors de l'ajout de la requête dans le buffer de sortie\n");
}
/* COnnexion à l'hôte de façon synchrone.
Nous connaissons l'IP, nous n'avons donc pas besoin de résolution DNS. */
if (!$bev->connect("127.0.0.1:80")) {
exit("Impossible de se connecter à l'hôte\n");
}
// Diffuse les événements en attente
$base->dispatch();
?>
Exemple #3 Serveur d'affichage
<?php
/*
* Serveur d'affichage simpple basé sur les écoutes de connexion libevent.
*
* Utilisation :
* 1) Dans un terminal, exécutez :
*
* $ php listener.php 9881
*
* 2) Dans un autre terminal, ouvrez une connexion, i.e. :
*
* $ nc 127.0.0.1 9881
*
* 3) Commencez à taper. Le serveur devrait répéter les entrées.
*/
class MyListenerConnection {
private $bev, $base;
public function __destruct() {
$this->bev->free();
}
public function __construct($base, $fd) {
$this->base = $base;
$this->bev = new EventBufferEvent($base, $fd, EventBufferEvent::OPT_CLOSE_ON_FREE);
$this->bev->setCallbacks(array($this, "echoReadCallback"), NULL,
array($this, "echoEventCallback"), NULL);
if (!$this->bev->enable(Event::READ)) {
echo "Echec dans l'activation de READ\n";
return;
}
}
public function echoReadCallback($bev, $ctx) {
// Copie toutes les données depuis le buffer d'entrée vers le buffer de sortie
// Variant #1
$bev->output->addBuffer($bev->input);
/* Variant #2 */
/*
$input = $bev->getInput();
$output = $bev->getOutput();
$output->addBuffer($input);
*/
}
public function echoEventCallback($bev, $events, $ctx) {
if ($events & EventBufferEvent::ERROR) {
echo "Erreur depuis bufferevent\n";
}
if ($events & (EventBufferEvent::EOF | EventBufferEvent::ERROR)) {
//$bev->free();
$this->__destruct();
}
}
}
class MyListener {
public $base,
$listener,
$socket;
private $conn = array();
public function __destruct() {
foreach ($this->conn as &$c) $c = NULL;
}
public function __construct($port) {
$this->base = new EventBase();
if (!$this->base) {
echo "Impossible d'ouvrir la base de l'événement";
exit(1);
}
// Variant #1
/*
$this->socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if (!socket_bind($this->socket, '0.0.0.0', $port)) {
echo "Impossible de lier le socket\n";
exit(1);
}
$this->listener = new EventListener($this->base,
array($this, "acceptConnCallback"), $this->base,
EventListener::OPT_CLOSE_ON_FREE | EventListener::OPT_REUSEABLE,
-1, $this->socket);
*/
// Variant #2
$this->listener = new EventListener($this->base,
array($this, "acceptConnCallback"), $this->base,
EventListener::OPT_CLOSE_ON_FREE | EventListener::OPT_REUSEABLE, -1,
"0.0.0.0:$port");
if (!$this->listener) {
echo "Impossible de créer l'écouteur";
exit(1);
}
$this->listener->setErrorCallback(array($this, "accept_error_cb"));
}
public function dispatch() {
$this->base->dispatch();
}
// Cette fonction de rappel est appelée lorsqu'il y a des données à lire sur $bev
public function acceptConnCallback($listener, $fd, $address, $ctx) {
// Nous avons une nouvelle connexion ! On définit un bufferevent pour elle. */
$base = $this->base;
$this->conn[] = new MyListenerConnection($base, $fd);
}
public function accept_error_cb($listener, $ctx) {
$base = $this->base;
fprintf(STDERR, "On recoit une erreur %d (%s) sur l'écouteur. "
."Arrêt.\n",
EventUtil::getLastSocketErrno(),
EventUtil::getLastSocketError());
$base->exit(NULL);
}
}
$port = 9808;
if ($argc > 1) {
$port = (int) $argv[1];
}
if ($port <= 0 || $port > 65535) {
exit("Invalid port");
}
$l = new MyListener($port);
$l->dispatch();
?>
Exemple #4 Serveur d'affichage SSL
<?php
/*
* Serveur d'affichage SSL
*
* Pour tester :
* 1) Exécutez :
* $ php examples/ssl-echo-server/server.php 9998
*
* 2) dans un autre terminal, exécutez :
* $ socat - SSL:127.0.0.1:9998,verify=1,cafile=examples/ssl-echo-server/cert.pem
*/
class MySslEchoServer {
public $port,
$base,
$bev,
$listener,
$ctx;
function __construct ($port, $host = "127.0.0.1") {
$this->port = $port;
$this->ctx = $this->init_ssl();
if (!$this->ctx) {
exit("Echec lors de la création du contexte SSL\n");
}
$this->base = new EventBase();
if (!$this->base) {
exit("Impossible d'ouvrir la base de l'événement\n");
}
$this->listener = new EventListener($this->base,
array($this, "ssl_accept_cb"), $this->ctx,
EventListener::OPT_CLOSE_ON_FREE | EventListener::OPT_REUSEABLE,
-1, "$host:$port");
if (!$this->listener) {
exit("Impossible de créer l'écouteur\n");
}
$this->listener->setErrorCallback(array($this, "accept_error_cb"));
}
function dispatch() {
$this->base->dispatch();
}
// Cette fonction de rappel est appelée lorsqu'il y a des données à lire sur $bev.
function ssl_read_cb($bev, $ctx) {
$in = $bev->input; //$bev->getInput();
printf("Réception de %zu octets\n", $in->length);
printf("----- données ----\n");
printf("%ld:\t%s\n", (int) $in->length, $in->pullup(-1));
$bev->writeBuffer($in);
}
// Cette fonction de rappel est appelée lorsque des erreurs surviennent sur l'écouteur d'événement,
// i.e. la connexion se ferme, ou une erreur survient
function ssl_event_cb($bev, $events, $ctx) {
if ($events & EventBufferEvent::ERROR) {
// Récupère les erreurs depuis la pile d'erreur SSL
while ($err = $bev->sslError()) {
fprintf(STDERR, "Erreur Bufferevent %s.\n", $err);
}
}
if ($events & (EventBufferEvent::EOF | EventBufferEvent::ERROR)) {
$bev->free();
}
}
// Cette fonction de rappel est appelée lorsqu'un client accepte une nouvelle connexion
function ssl_accept_cb($listener, $fd, $address, $ctx) {
// Nous avons une nouvelle connexion ! On définit un bufferevent pour elle.
$this->bev = EventBufferEvent::sslSocket($this->base, $fd, $this->ctx,
EventBufferEvent::SSL_ACCEPTING, EventBufferEvent::OPT_CLOSE_ON_FREE);
if (!$this->bev) {
echo "Echec lors de la création du buffer SSL\n";
$this->base->exit(NULL);
exit(1);
}
$this->bev->enable(Event::READ);
$this->bev->setCallbacks(array($this, "ssl_read_cb"), NULL,
array($this, "ssl_event_cb"), NULL);
}
// Cette fonction de rappel est appelée lorsqu'on échoue à définir une nouvelle connexion pour un client
function accept_error_cb($listener, $ctx) {
fprintf(STDERR, "On reçoit une erreur %d (%s) sur l'écouteur. "
."Arrêt.\n",
EventUtil::getLastSocketErrno(),
EventUtil::getLastSocketError());
$this->base->exit(NULL);
}
// Initialise les structures SSL ; crée un EventSslContext
// Optionnellement, crée des certificats auto-signés
function init_ssl() {
// Nous *devons* avoir l'entropie. Sinon, il n'y a aucun intérêt à crypter.
if (!EventUtil::sslRandPoll()) {
exit("Echec de EventUtil::sslRandPoll\n");
}
$local_cert = __DIR__."/cert.pem";
$local_pk = __DIR__."/privkey.pem";
if (!file_exists($local_cert) || !file_exists($local_pk)) {
echo "Impossible de lire $local_cert ni $local_pk file. Pour générer une clé\n",
"et un certificat auto-signé, exécutez :\n",
" openssl genrsa -out $local_pk 2048\n",
" openssl req -new -key $local_pk -out cert.req\n",
" openssl x509 -req -days 365 -in cert.req -signkey $local_pk -out $local_cert\n";
return FALSE;
}
$ctx = new EventSslContext(EventSslContext::SSLv3_SERVER_METHOD, array (
EventSslContext::OPT_LOCAL_CERT => $local_cert,
EventSslContext::OPT_LOCAL_PK => $local_pk,
//EventSslContext::OPT_PASSPHRASE => "echo server",
EventSslContext::OPT_VERIFY_PEER => true,
EventSslContext::OPT_ALLOW_SELF_SIGNED => false,
));
return $ctx;
}
}
// Autorise l'écrasement du port
$port = 9999;
if ($argc > 1) {
$port = (int) $argv[1];
}
if ($port <= 0 || $port > 65535) {
exit("Port invalide\n");
}
$l = new MySslEchoServer($port);
$l->dispatch();
?>
Exemple #5 Gestionnaire de signal
<?php
/*
Dans un terminal, exécutez :
$ php examples/signal.php
Dans un autre terminal, trouvez le pid et envoyé le signal SIGTERM, i.e. :
$ ps aux | grep examp
ruslan 3976 0.2 0.0 139896 11256 pts/1 S+ 10:25 0:00 php examples/signal.php
ruslan 3978 0.0 0.0 9572 864 pts/2 S+ 10:26 0:00 grep --color=auto examp
$ kill -TERM 3976
Dans le premier terminal, vous devriez attrapper ce qui suit :
On attrappe le signal 15
*/
class MyEventSignal {
private $base;
function __construct($base) {
$this->base = $base;
}
function eventSighandler($no, $c) {
echo "On attrappe le signal $no\n";
event_base_loopexit($c->base);
}
}
$base = event_base_new();
$c = new MyEventSignal($base);
$no = SIGTERM;
$ev = evsignal_new($base, $no, array($c,'eventSighandler'), $c);
evsignal_add($ev);
event_base_loop($base);
?>
Exemple #6 Utilisation d'une boucle libevent pour réaliser les requêtes de l'extension `eio'
<?php
// Fonction de rappel pour eio_nop()
function my_nop_cb($d, $r) {
echo "étape 6\n";
}
$dir = "/tmp/abc-eio-temp";
if (file_exists($dir)) {
rmdir($dir);
}
echo "étape 1\n";
$base = new EventBase();
echo "étape 2\n";
eio_init();
eio_mkdir($dir, 0750, EIO_PRI_DEFAULT, "my_nop_cb");
$event = new Event($base, eio_get_event_stream(),
Event::READ | Event::PERSIST, function ($fd, $events, $base) {
echo "step 5\n";
while (eio_nreqs()) {
eio_poll();
}
$base->stop();
}, $base);
echo "étape 3\n";
$event->add();
echo "étape 4\n";
$base->dispatch();
echo "Fait !\n";
?>
Exemple #7 Divers
<?php
/* {{{ Configuration & méthodes supportées */
echo "Méthodes supportées :\n";
foreach (Event::getSupportedMethods() as $m) {
echo $m, PHP_EOL;
}
// On désactive la méthode "select"
$cfg = new EventConfig();
if ($cfg->avoidMethod("select")) {
echo "La méthode `select' a été désactivée\n";
}
// Crée un event_base associé à la configuration
$base = new EventBase($cfg);
echo "Méthode d'événement utilisée : ", $base->getMethod(), PHP_EOL;
echo "fonctionalités :\n";
$features = $base->getFeatures();
($features & EventConfig::FEATURE_ET) and print("ET - edge-triggered IO\n");
($features & EventConfig::FEATURE_O1) and print("O1 - O(1) operation for adding/deletting events\n");
($features & EventConfig::FEATURE_FDS) and print("FDS - arbitrary file descriptor types, and not just sockets\n");
// Nécessite la fonctionalité FDS
if ($cfg->requireFeatures(EventConfig::FEATURE_FDS)) {
echo "La fonctionalité FDS est maintenant requise\n";
$base = new EventBase($cfg);
($base->getFeatures() & EventConfig::FEATURE_FDS)
and print("FDS - type de descripteur de fichiers arbitraire, et non uniquement les sockets\n");
}
/* }}} */
/* {{{ Base */
$base = new EventBase();
$event = new Event($base, STDIN, Event::READ | Event::PERSIST, function ($fd, $events, $arg) {
static $max_iterations = 0;
if (++$max_iterations >= 5) {
/* on sort après 5 itérations avec un délai maximal d'attente de 2.33 secondes */
echo "Arrêt...\n";
$arg[0]->exit(2.33);
}
echo fgets($fd);
}, array (&$base));
$event->add();
$base->loop();
/* Base }}} */
?>
Exemple #8 Serveur HTTP simple
<?php
/*
* Serveur HTTP simple.
*
* Pour le tester :
* 1) Exécutez-le sur le port de votre choix, i.e. :
* $ php examples/http.php 8010
* 2) Dans un autre terminal, connectez-vous sur une adresse de ce port
* et effectuez des requêtes GET ou POST (les autres types sont désactivées dans cet exemple), i.e. :
* $ nc -t 127.0.0.1 8010
* POST /about HTTP/1.0
* Content-Type: text/plain
* Content-Length: 4
* Connection: close
* (press Enter)
*
* Il devrait afficher :
* a=12
* HTTP/1.0 200 OK
* Content-Type: text/html; charset=ISO-8859-1
* Connection: close
*
* $ nc -t 127.0.0.1 8010
* GET /dump HTTP/1.0
* Content-Type: text/plain
* Content-Encoding: UTF-8
* Connection: close
* (press Enter)
*
* Il devrait afficher :
* HTTP/1.0 200 OK
* Content-Type: text/html; charset=ISO-8859-1
* Connection: close
* (press Enter)
*
* $ nc -t 127.0.0.1 8010
* GET /unknown HTTP/1.0
* Connection: close
*
* Il devrait afficher :
* HTTP/1.0 200 OK
* Content-Type: text/html; charset=ISO-8859-1
* Connection: close
*
* 3) Voir ce que le serveur affiche dans le terminal précédent.
*/
function _http_dump($req, $data) {
static $counter = 0;
static $max_requests = 2;
if (++$counter >= $max_requests) {
echo "Le compteur a atteint le nombre maximal de requête ($max_requests). Sortie !\n";
exit();
}
echo __METHOD__, " called\n";
echo "Requête :"; var_dump($req);
echo "Données :"; var_dump($data);
echo "\n===== DUMP =====\n";
echo "Commande :", $req->getCommand(), PHP_EOL;
echo "URI :", $req->getUri(), PHP_EOL;
echo "En-têtes en entrée :"; var_dump($req->getInputHeaders());
echo "En-têtes en sortie :"; var_dump($req->getOutputHeaders());
echo "\n >> Envoi de la réponse ...";
$req->sendReply(200, "OK");
echo "OK\n";
echo "\n >> Lecture du buffer d'entrée ...\n";
$buf = $req->getInputBuffer();
while ($s = $buf->readLine(EventBuffer::EOL_ANY)) {
echo $s, PHP_EOL;
}
echo "Il n'y a plus de données dans le buffer\n";
}
function _http_about($req) {
echo __METHOD__, PHP_EOL;
echo "URI : ", $req->getUri(), PHP_EOL;
echo "\n >> Envoi de la réponse ...";
$req->sendReply(200, "OK");
echo "OK\n";
}
function _http_default($req, $data) {
echo __METHOD__, PHP_EOL;
echo "URI: ", $req->getUri(), PHP_EOL;
echo "\n >> Envoi de la réponse ...";
$req->sendReply(200, "OK");
echo "OK\n";
}
$port = 8010;
if ($argc > 1) {
$port = (int) $argv[1];
}
if ($port <= 0 || $port > 65535) {
exit("Port invalide");
}
$base = new EventBase();
$http = new EventHttp($base);
$http->setAllowedMethods(EventHttpRequest::CMD_GET | EventHttpRequest::CMD_POST);
$http->setCallback("/dump", "_http_dump", array(4, 8));
$http->setCallback("/about", "_http_about");
$http->setDefaultCallback("_http_default", "valeur de données personnalisées");
$http->bind("0.0.0.0", 8010);
$base->loop();
?>
L'exemple ci-dessus va afficher quelque chose de similaire à :
a=12 HTTP/1.0 200 OK Content-Type: text/html; charset=ISO-8859-1 Connection: close HTTP/1.0 200 OK Content-Type: text/html; charset=ISO-8859-1 Connection: close (press Enter) HTTP/1.0 200 OK Content-Type: text/html; charset=ISO-8859-1 Connection: close
Exemple #9 Serveur HTTPS simple
<?php
/*
* Serveur HTTPS simple.
*
* 1) Exécutez le serveur : `php examples/https.php 9999`
* 2) Testez le : `php examples/ssl-connection.php 9999`
*/
function _http_dump($req, $data) {
static $counter = 0;
static $max_requests = 200;
if (++$counter >= $max_requests) {
echo "Le compteur a atteint le nombre maximal de requêtes ($max_requests). Sortie\n";
exit();
}
echo __METHOD__, " appelée\n";
echo "Requête :"; var_dump($req);
echo "Données :"; var_dump($data);
echo "\n===== DUMP =====\n";
echo "Commande :", $req->getCommand(), PHP_EOL;
echo "URI :", $req->getUri(), PHP_EOL;
echo "En-têtes d'entrée :"; var_dump($req->getInputHeaders());
echo "En-têtes de sortie :"; var_dump($req->getOutputHeaders());
echo "\n >> Envoi de la réponse ...";
$req->sendReply(200, "OK");
echo "OK\n";
$buf = $req->getInputBuffer();
echo "\n >> Lecture du buffer d'entrée (", $buf->length, ") ...\n";
while ($s = $buf->read(1024)) {
echo $s;
}
echo "\nIl n'y a plus de données dans le buffer\n";
}
function _http_about($req) {
echo __METHOD__, PHP_EOL;
echo "URI : ", $req->getUri(), PHP_EOL;
echo "\n >> Envoi de la réponse ...";
$req->sendReply(200, "OK");
echo "OK\n";
}
function _http_default($req, $data) {
echo __METHOD__, PHP_EOL;
echo "URI: ", $req->getUri(), PHP_EOL;
echo "\n >> Envoi de la réponse ...";
$req->sendReply(200, "OK");
echo "OK\n";
}
function _http_400($req) {
$req->sendError(400);
}
function _init_ssl() {
$local_cert = __DIR__."/ssl-echo-server/cert.pem";
$local_pk = __DIR__."/ssl-echo-server/privkey.pem";
$ctx = new EventSslContext(EventSslContext::SSLv3_SERVER_METHOD, array (
EventSslContext::OPT_LOCAL_CERT => $local_cert,
EventSslContext::OPT_LOCAL_PK => $local_pk,
//EventSslContext::OPT_PASSPHRASE => "test",
EventSslContext::OPT_ALLOW_SELF_SIGNED => true,
));
return $ctx;
}
$port = 9999;
if ($argc > 1) {
$port = (int) $argv[1];
}
if ($port <= 0 || $port > 65535) {
exit("Port invalide");
}
$ip = '0.0.0.0';
$base = new EventBase();
$ctx = _init_ssl();
$http = new EventHttp($base, $ctx);
$http->setAllowedMethods(EventHttpRequest::CMD_GET | EventHttpRequest::CMD_POST);
$http->setCallback("/dump", "_http_dump", array(4, 8));
$http->setCallback("/about", "_http_about");
$http->setCallback("/err400", "_http_400");
$http->setDefaultCallback("_http_default", "Valeur des données personnalisées");
$http->bind($ip, $port);
$base->dispatch();
Exemple #10 Connexion OpenSSL
<?php
/*
* Exemple de Client OpenSSL.
*
* Utilisation :
* 1) Exécutez un serveur, i.e. :
* $ php examples/https.php 9999
*
* 2) Lancez le client dans un autre terminal :
* $ php examples/ssl-connection.php 9999
*/
function _request_handler($req, $base) {
echo __FUNCTION__, PHP_EOL;
if (is_null($req)) {
echo "Délai d'attente maximal atteint\n";
} else {
$response_code = $req->getResponseCode();
if ($response_code == 0) {
echo "Connexion refusée\n";
} elseif ($response_code != 200) {
echo "Réponse innatendue : $response_code\n";
} else {
echo "Succès : $response_code\n";
$buf = $req->getInputBuffer();
echo "Corps :\n";
while ($s = $buf->readLine(EventBuffer::EOL_ANY)) {
echo $s, PHP_EOL;
}
}
}
$base->exit(NULL);
}
function _init_ssl() {
$ctx = new EventSslContext(EventSslContext::SSLv3_CLIENT_METHOD, array ());
return $ctx;
}
// Autorise l'écrasement du port
$port = 9999;
if ($argc > 1) {
$port = (int) $argv[1];
}
if ($port <= 0 || $port > 65535) {
exit("Port invalide\n");
}
$host = '127.0.0.1';
$ctx = _init_ssl();
if (!$ctx) {
trigger_error("Echec lors de la création du contexte SSL", E_USER_ERROR);
}
$base = new EventBase();
if (!$base) {
trigger_error("Echec dans l'initialisation de la base d'événement", E_USER_ERROR);
}
$conn = new EventHttpConnection($base, NULL, $host, $port, $ctx);
$conn->setTimeout(50);
$req = new EventHttpRequest("_request_handler", $base);
$req->addHeader("Host", $host, EventHttpRequest::OUTPUT_HEADER);
$buf = $req->getOutputBuffer();
$buf->add("<html>HTML TEST</html>");
//$req->addHeader("Content-Length", $buf->length, EventHttpRequest::OUTPUT_HEADER);
//$req->addHeader("Connection", "close", EventHttpRequest::OUTPUT_HEADER);
$conn->makeRequest($req, EventHttpRequest::CMD_POST, "/dump");
$base->dispatch();
echo "FIN\n";
?>
Exemple #11 Exemple avec EventHttpConnection::makeRequest()
<?php
function _request_handler($req, $base) {
echo __FUNCTION__, PHP_EOL;
if (is_null($req)) {
echo "Délai d'attente maximal atteint\n";
} else {
$response_code = $req->getResponseCode();
if ($response_code == 0) {
echo "Connexion refusée\n";
} elseif ($response_code != 200) {
echo "Réponse innatendue : $response_code\n";
} else {
echo "Succès : $response_code\n";
$buf = $req->getInputBuffer();
echo "Corps :\n";
while ($s = $buf->readLine(EventBuffer::EOL_ANY)) {
echo $s, PHP_EOL;
}
}
}
$base->exit(NULL);
}
$address = "127.0.0.1";
$port = 80;
$base = new EventBase();
$conn = new EventHttpConnection($base, NULL, $address, $port);
$conn->setTimeout(5);
$req = new EventHttpRequest("_request_handler", $base);
$req->addHeader("Host", $address, EventHttpRequest::OUTPUT_HEADER);
$req->addHeader("Content-Length", "0", EventHttpRequest::OUTPUT_HEADER);
$conn->makeRequest($req, EventHttpRequest::CMD_GET, "/index.cphp");
$base->loop();
?>
L'exemple ci-dessus va afficher quelque chose de similaire à :
_request_handler Succès : 200 Corps : PHP, date: 2013-03-13T20:27:52+05:00
Exemple #12 Ecoute d'une connexion en se basant sur un socket de domaine Unix
<?php
/*
* Serveur d'écoute en se basant sur un écouteur de connexion libevent.
*
* Utilisation :
* 1) Dans un terminal, exécutez :
*
* $ php unix-domain-listener.php [path-to-socket]
*
* 2) Dans un autre terminal, ouvrez la connexion
* vers le socket, i.e. :
*
* $ socat - GOPEN:/tmp/1.sock
*
* 3) Commencez à taper. Le serveur doit répéter les entrées.
*/
class MyListenerConnection {
private $bev, $base;
public function __destruct() {
if ($this->bev) {
$this->bev->free();
}
}
public function __construct($base, $fd) {
$this->base = $base;
$this->bev = new EventBufferEvent($base, $fd, EventBufferEvent::OPT_CLOSE_ON_FREE);
$this->bev->setCallbacks(array($this, "echoReadCallback"), NULL,
array($this, "echoEventCallback"), NULL);
if (!$this->bev->enable(Event::READ)) {
echo "Echec dans l'activation de READ\n";
return;
}
}
public function echoReadCallback($bev, $ctx) {
// Copie toutes les données depuis le buffer d'entrée dans le buffer de sortie
$bev->output->addBuffer($bev->input);
}
public function echoEventCallback($bev, $events, $ctx) {
if ($events & EventBufferEvent::ERROR) {
echo "Erreur depuis bufferevent\n";
}
if ($events & (EventBufferEvent::EOF | EventBufferEvent::ERROR)) {
$bev->free();
$bev = NULL;
}
}
}
class MyListener {
public $base,
$listener,
$socket;
private $conn = array();
public function __destruct() {
foreach ($this->conn as &$c) $c = NULL;
}
public function __construct($sock_path) {
$this->base = new EventBase();
if (!$this->base) {
echo "Couldn't open event base";
exit(1);
}
if (file_exists($sock_path)) {
unlink($sock_path);
}
$this->listener = new EventListener($this->base,
array($this, "acceptConnCallback"), $this->base,
EventListener::OPT_CLOSE_ON_FREE | EventListener::OPT_REUSEABLE, -1,
"unix:$sock_path");
if (!$this->listener) {
trigger_error("Impossible de créer l'écouteur", E_USER_ERROR);
}
$this->listener->setErrorCallback(array($this, "accept_error_cb"));
}
public function dispatch() {
$this->base->dispatch();
}
// Cette fonction de rappel sera appelée lorsqu'il y aura des données à lire sur $bev
public function acceptConnCallback($listener, $fd, $address, $ctx) {
// Nous avons une nouvelle connexion ! On définit un bufferevent pour elle. */
$base = $this->base;
$this->conn[] = new MyListenerConnection($base, $fd);
}
public function accept_error_cb($listener, $ctx) {
$base = $this->base;
fprintf(STDERR, "On reçoit une erreur %d (%s) sur l'écouteur. "
."Shutting down.\n",
EventUtil::getLastSocketErrno(),
EventUtil::getLastSocketError());
$base->exit(NULL);
}
}
if ($argc <= 1) {
exit("Le chemin vers le socket n'est pas fourni\n");
}
$sock_path = $argv[1];
$l = new MyListener($sock_path);
$l->dispatch();
?>
Exemple #13 Exemple de serveur SMTP
<?php
/*
* Auteur : Andrew Rose <hello at andrewrose dot co dot uk>
*
* Utilisation :
* 1) On prépare le certificat cert.pem et la clé privée privkey.pem.
* 2) On démarre le script du serveur
* 3) On ouvre la connexion TLS, i.e. :
* $ openssl s_client -connect localhost:25 -starttls smtp -crlf
* 4) On commence à tester les commandes listées dans la méthode `cmd` ci-dessous.
*/
class Handler {
public $domainName = FALSE;
public $connections = [];
public $buffers = [];
public $maxRead = 256000;
public function __construct() {
$this->ctx = new EventSslContext(EventSslContext::SSLv3_SERVER_METHOD, [
EventSslContext::OPT_LOCAL_CERT => 'cert.pem',
EventSslContext::OPT_LOCAL_PK => 'privkey.pem',
//EventSslContext::OPT_PASSPHRASE => '',
EventSslContext::OPT_VERIFY_PEER => false, // modifier en TRUE avec les certificats authentiques
EventSslContext::OPT_ALLOW_SELF_SIGNED => true // modifier en FALSE avec les certificats authentiques
]);
$this->base = new EventBase();
if (!$this->base) {
exit("Impossible d'ouvrir la base de l'événement\n");
}
if (!$this->listener = new EventListener($this->base,
[$this, 'ev_accept'],
$this->ctx,
EventListener::OPT_CLOSE_ON_FREE | EventListener::OPT_REUSEABLE,
-1,
'0.0.0.0:25'))
{
exit("Impossible de créer l'écouteur\n");
}
$this->listener->setErrorCallback([$this, 'ev_error']);
$this->base->dispatch();
}
public function ev_accept($listener, $fd, $address, $ctx) {
static $id = 0;
$id += 1;
$this->connections[$id]['clientData'] = '';
$this->connections[$id]['cnx'] = new EventBufferEvent($this->base, $fd,
EventBufferEvent::OPT_CLOSE_ON_FREE);
if (!$this->connections[$id]['cnx']) {
echo "Echec lors de la création du buffer\n";
$this->base->exit(NULL);
exit(1);
}
$this->connections[$id]['cnx']->setCallbacks([$this, "ev_read"], NULL,
[$this, 'ev_error'], $id);
$this->connections[$id]['cnx']->enable(Event::READ | Event::WRITE);
$this->ev_write($id, '220 '.$this->domainName." wazzzap?\r\n");
}
function ev_error($listener, $ctx) {
$errno = EventUtil::getLastSocketErrno();
fprintf(STDERR, "On reçoit l'erreur %d (%s) sur l'écouteur. On stoppe.\n",
$errno, EventUtil::getLastSocketError());
if ($errno != 0) {
$this->base->exit(NULL);
exit();
}
}
public function ev_close($id) {
$this->connections[$id]['cnx']->disable(Event::READ | Event::WRITE);
unset($this->connections[$id]);
}
protected function ev_write($id, $string) {
echo 'S('.$id.'): '.$string;
$this->connections[$id]['cnx']->write($string);
}
public function ev_read($buffer, $id) {
while($buffer->input->length > 0) {
$this->connections[$id]['clientData'] .= $buffer->input->read($this->maxRead);
$clientDataLen = strlen($this->connections[$id]['clientData']);
if($this->connections[$id]['clientData'][$clientDataLen-1] == "\n"
&& $this->connections[$id]['clientData'][$clientDataLen-2] == "\r")
{
// Supprime les caractères \r\n à la fin
$line = substr($this->connections[$id]['clientData'], 0,
strlen($this->connections[$id]['clientData']) - 2);
$this->connections[$id]['clientData'] = '';
$this->cmd($buffer, $id, $line);
}
}
}
protected function cmd($buffer, $id, $line) {
switch ($line) {
case strncmp('EHLO ', $line, 4):
$this->ev_write($id, "250-STARTTLS\r\n");
$this->ev_write($id, "250 OK ehlo\r\n");
break;
case strncmp('HELO ', $line, 4):
$this->ev_write($id, "250-STARTTLS\r\n");
$this->ev_write($id, "250 OK helo\r\n");
break;
case strncmp('QUIT', $line, 3):
$this->ev_write($id, "250 OK quit\r\n");
$this->ev_close($id);
break;
case strncmp('STARTTLS', $line, 3):
$this->ev_write($id, "220 Ready to start TLS\r\n");
$this->connections[$id]['cnx'] = EventBufferEvent::sslFilter($this->base,
$this->connections[$id]['cnx'], $this->ctx,
EventBufferEvent::SSL_ACCEPTING,
EventBufferEvent::OPT_CLOSE_ON_FREE);
$this->connections[$id]['cnx']->setCallbacks([$this, "ev_read"], NULL, [$this, 'ev_error'], $id);
$this->connections[$id]['cnx']->enable(Event::READ | Event::WRITE);
break;
default:
echo 'Commande inconnue : '.$line."\n";
break;
}
}
}
new Handler();