Ejemplos

Ejemplo #1 Cliente HTTP simple

<?php
// Lectura de la función de retrollamada
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;
}
}

// Función de retrollamada del evento
function eventcb($bev, $events, $base) {
if (
$events & EventBufferEvent::CONNECTED) {
echo
"Conectado.\n";
} elseif (
$events & (EventBufferEvent::ERROR | EventBufferEvent::EOF)) {
if (
$events & EventBufferEvent::ERROR) {
echo
"error DNS : ", $bev->getDnsErrorString(), PHP_EOL;
}

echo
"Cierre\n";
$base->exit();
exit(
"¡Hecho!\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); // Se utiliza una resolución DNS asíncrona
if (!$dns_base) {
exit(
"Fallo en la inicialización 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(
"Fallo en la creación del 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(
"Fallo en la adición de la petición en el buffer de salida\n");
}

if (!
$bev->connectHost($dns_base, $argv[1], 80, EventUtil::AF_UNSPEC)) {
exit(
"Conexión imposible al host {$argv[1]}\n");
}

$base->dispatch();
?>

El resultado del ejemplo sería algo similar a:

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

Ejemplo #2 Cliente HTTP utilizando una resolución DNS asíncrona

<?php
/*
* 1. Conexión a 127.0.0.1 en el puerto 80
* utilizando EventBufferEvent::connect().
*
* 2. Petición /index.cphp vía HTTP/1.0
* utilizando el buffer de salida.
*
* 3. Lectura asíncrona de la respuesta y la muestra en stdout.
*/

// Lectura de la función de retrollamada
function readcb($bev, $base) {
$input = $bev->getInput();

while ((
$n = $input->remove($buf, 1024)) > 0) {
echo
$buf;
}
}

// Función de retrollamada del evento
function eventcb($bev, $events, $base) {
if (
$events & EventBufferEvent::CONNECTED) {
echo
"Conectado.\n";
} elseif (
$events & (EventBufferEvent::ERROR | EventBufferEvent::EOF)) {
if (
$events & EventBufferEvent::ERROR) {
echo
"Error DNS : ", $bev->getDnsErrorString(), PHP_EOL;
}

echo
"Cierre\n";
$base->exit();
exit(
"¡Hecho!\n");
}
}

$base = new EventBase();

echo
"paso n°1\n";
$bev = new EventBufferEvent($base, /* use internal socket */ NULL,
EventBufferEvent::OPT_CLOSE_ON_FREE | EventBufferEvent::OPT_DEFER_CALLBACKS);
if (!
$bev) {
exit(
"Fallo en la creación del socket bufferevent\n");
}

echo
"paso n°2\n";
$bev->setCallbacks("readcb", /* writecb */ NULL, "eventcb", $base);
$bev->enable(Event::READ | Event::WRITE);

echo
"paso n°3\n";
// Envío de la petición
$output = $bev->getOutput();
if (!
$output->add(
"GET /index.cphp HTTP/1.0\r\n".
"Connection: Close\r\n\r\n"
)) {
exit(
"Fallo en la adición de la petición en el buffer de salida\n");
}

/* Conexión al host de forma síncrona.
Conocemos la IP, por lo que no se necesita resolución DNS. */
if (!$bev->connect("127.0.0.1:80")) {
exit(
"Imposible conectar al host\n");
}

// Difunde los eventos pendientes
$base->dispatch();
?>

Ejemplo #3 Servidor de visualización

<?php
/*
* Servidor de visualización simple basado en las escuchas de conexión libevent.
*
* Uso:
* 1) En un terminal, ejecute:
*
* $ php listener.php 9881
*
* 2) En otro terminal, abra una conexión, i.e. :
*
* $ nc 127.0.0.1 9881
*
* 3) Comience a escribir. El servidor debería repetir las entradas.
*/

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
"Fallo en la activación de READ\n";
return;
}
}

public function
echoReadCallback($bev, $ctx) {
// Copia todos los datos desde el buffer de entrada hacia el buffer de salida

// Variante #1
$bev->output->addBuffer($bev->input);

/* Variante #2 */
/*
$input = $bev->getInput();
$output = $bev->getOutput();
$output->addBuffer($input);
*/
}

public function
echoEventCallback($bev, $events, $ctx) {
if (
$events & EventBufferEvent::ERROR) {
echo
"Error desde 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
"Imposible abrir la base del evento";
exit(
1);
}

// Variante #1
/*
$this->socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
if (!socket_bind($this->socket, '0.0.0.0', $port)) {
echo "Imposible enlazar el 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);
*/

// Variante #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
"Imposible crear el oyente";
exit(
1);
}

$this->listener->setErrorCallback(array($this, "accept_error_cb"));
}

public function
dispatch() {
$this->base->dispatch();
}

// Esta función de retrollamada es llamada cuando hay datos para leer en $bev
public function acceptConnCallback($listener, $fd, $address, $ctx) {
// ¡Tenemos una nueva conexión! Se define un bufferevent para ella. */
$base = $this->base;
$this->conn[] = new MyListenerConnection($base, $fd);
}

public function
accept_error_cb($listener, $ctx) {
$base = $this->base;

fprintf(STDERR, "Se recibe un error %d (%s) en el oyente. "
."Cerrando.\n",
EventUtil::getLastSocketErrno(),
EventUtil::getLastSocketError());

$base->exit(NULL);
}
}

$port = 9808;

if (
$argc > 1) {
$port = (int) $argv[1];
}
if (
$port <= 0 || $port > 65535) {
exit(
"Puerto inválido");
}

$l = new MyListener($port);
$l->dispatch();
?>

Ejemplo #4 Servidor de visualización SSL

<?php
/*
* Servidor de visualización SSL
*
* Para probar:
* 1) Ejecute:
* $ php examples/ssl-echo-server/server.php 9998
*
* 2) en otro terminal, ejecute:
* $ 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(
"Fallo en la creación del contexto SSL\n");
}

$this->base = new EventBase();
if (!
$this->base) {
exit(
"Imposible abrir la base del evento\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(
"Imposible crear el oyente\n");
}

$this->listener->setErrorCallback(array($this, "accept_error_cb"));
}
function
dispatch() {
$this->base->dispatch();
}

// Esta función de retrollamada es llamada cuando hay datos para leer en $bev.
function ssl_read_cb($bev, $ctx) {
$in = $bev->input; //$bev->getInput();

printf("Recepción de %zu bytes\n", $in->length);
printf("----- datos ----\n");
printf("%ld:\t%s\n", (int) $in->length, $in->pullup(-1));

$bev->writeBuffer($in);
}

// Esta función de retrollamada es llamada cuando ocurren errores en el oyente de eventos,
// i.e. la conexión se cierra, o ocurre un error
function ssl_event_cb($bev, $events, $ctx) {
if (
$events & EventBufferEvent::ERROR) {
// Recupera los errores desde la pila de errores SSL
while ($err = $bev->sslError()) {
fprintf(STDERR, "Error Bufferevent %s.\n", $err);
}
}

if (
$events & (EventBufferEvent::EOF | EventBufferEvent::ERROR)) {
$bev->free();
}
}

// Esta función de retrollamada es llamada cuando un cliente acepta una nueva conexión
function ssl_accept_cb($listener, $fd, $address, $ctx) {
// ¡Tenemos una nueva conexión! Se define un bufferevent para ella.
$this->bev = EventBufferEvent::sslSocket($this->base, $fd, $this->ctx,
EventBufferEvent::SSL_ACCEPTING, EventBufferEvent::OPT_CLOSE_ON_FREE);

if (!
$this->bev) {
echo
"Fallo en la creación del 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);
}

// Esta función de retrollamada es llamada cuando se falla en definir una nueva conexión para un cliente
function accept_error_cb($listener, $ctx) {
fprintf(STDERR, "Se recibe un error %d (%s) en el oyente. "
."Cerrando.\n",
EventUtil::getLastSocketErrno(),
EventUtil::getLastSocketError());

$this->base->exit(NULL);
}

// Inicializa las estructuras SSL; crea un EventSslContext
// Opcionalmente, crea certificados autofirmados
function init_ssl() {
// Necesitamos la entropía. De lo contrario, no tiene sentido cifrar.
if (!EventUtil::sslRandPoll()) {
exit(
"Fallo de EventUtil::sslRandPoll\n");
}

$local_cert = __DIR__."/cert.pem";
$local_pk = __DIR__."/privkey.pem";

if (!
file_exists($local_cert) || !file_exists($local_pk)) {
echo
"Imposible leer $local_cert ni $local_pk archivo. Para generar una clave\n",
"y un certificado autofirmado, ejecute :\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;
}
}

// Permite el sobrescrito del puerto
$port = 9999;
if (
$argc > 1) {
$port = (int) $argv[1];
}
if (
$port <= 0 || $port > 65535) {
exit(
"Puerto inválido\n");
}

$l = new MySslEchoServer($port);
$l->dispatch();
?>

Ejemplo #5 Manejador de señal

<?php
/*
En un terminal, ejecute:

$ php examples/signal.php

En otro terminal, encuentre el pid y envíe la señal 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

En el primer terminal, debería capturar lo siguiente:

Se captura la señal 15
*/
class MyEventSignal {
private
$base;

function
__construct($base) {
$this->base = $base;
}

function
eventSighandler($no, $c) {
echo
"Se captura la señal $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);
?>

Ejemplo #6 Uso de un bucle libevent para realizar las peticiones de la extensión `eio'

<?php
// Función de retrollamada para eio_nop()
function my_nop_cb($d, $r) {
echo
"paso 6\n";
}

$dir = "/tmp/abc-eio-temp";
if (
file_exists($dir)) {
rmdir($dir);
}

echo
"paso 1\n";

$base = new EventBase();

echo
"paso 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
"paso 5\n";

while (
eio_nreqs()) {
eio_poll();
}

$base->stop();
},
$base);

echo
"paso 3\n";

$event->add();

echo
"paso 4\n";

$base->dispatch();

echo
"¡Hecho!\n";
?>

Ejemplo #7 Diversos

<?php
/* {{{ Configuración y métodos soportados */
echo "Métodos soportados :\n";
foreach (
Event::getSupportedMethods() as $m) {
echo
$m, PHP_EOL;
}

// Se desactiva el método "select"
$cfg = new EventConfig();
if (
$cfg->avoidMethod("select")) {
echo
"El método 'select' ha sido desactivado\n";
}

// Crea un event_base asociado a la configuración
$base = new EventBase($cfg);
echo
"Método de evento utilizado : ", $base->getMethod(), PHP_EOL;

echo
"Características :\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/deleting events\n";
(
$features & EventConfig::FEATURE_FDS) and print "FDS - arbitrary file descriptor types, and not just sockets\n";

// Requiere la característica FDS
if ($cfg->requireFeatures(EventConfig::FEATURE_FDS)) {
echo
"La característica FDS es ahora requerida\n";

$base = new EventBase($cfg);
(
$base->getFeatures() & EventConfig::FEATURE_FDS)
and print
"FDS - tipo de descriptor de archivo arbitrario, y no solo los 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) {
/* se sale después de 5 iteraciones con un tiempo máximo de espera de 2.33 segundos */
echo "Detención...\n";
$arg[0]->exit(2.33);
}

echo
fgets($fd);
}, array (&
$base));

$event->add();
$base->loop();
/* Base }}} */
?>

Ejemplo #8 Servidor HTTP simple

<?php
/*
* Servidor HTTP simple.
*
* Para probarlo:
* 1) Ejecútelo en el puerto de su elección, i.e. :
* $ php examples/http.php 8010
* 2) En otro terminal, conéctese a una dirección de este puerto
* y realice peticiones GET o POST (los otros tipos están desactivados en este ejemplo), i.e. :
* $ nc -t 127.0.0.1 8010
* POST /about HTTP/1.0
* Content-Type: text/plain
* Content-Length: 4
* Connection: close
* (presione Enter)
*
* Debería mostrar:
* 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
* (presione Enter)
*
* Debería mostrar:
* HTTP/1.0 200 OK
* Content-Type: text/html; charset=ISO-8859-1
* Connection: close
*
* $ nc -t 127.0.0.1 8010
* GET /unknown HTTP/1.0
* Connection: close
*
* Debería mostrar:
* HTTP/1.0 200 OK
* Content-Type: text/html; charset=ISO-8859-1
* Connection: close
*
* 3) Ver lo que el servidor muestra en el terminal anterior.
*/

function _http_dump($req, $data) {
static
$counter = 0;
static
$max_requests = 2;

if (++
$counter >= $max_requests) {
echo
"El contador ha alcanzado el número máximo de peticiones ($max_requests). ¡Salida!\n";
exit();
}

echo
__METHOD__, " llamada\n";
echo
"Petición :"; var_dump($req);
echo
"Datos :"; var_dump($data);

echo
"\n===== DUMP =====\n";
echo
"Comando :", $req->getCommand(), PHP_EOL;
echo
"URI :", $req->getUri(), PHP_EOL;
echo
"Encabezados de entrada :"; var_dump($req->getInputHeaders());
echo
"Encabezados de salida :"; var_dump($req->getOutputHeaders());

echo
"\n >> Enviando la respuesta ...";
$req->sendReply(200, "OK");
echo
"OK\n";

echo
"\n >> Lectura del buffer de entrada ...\n";
$buf = $req->getInputBuffer();
while (
$s = $buf->readLine(EventBuffer::EOL_ANY)) {
echo
$s, PHP_EOL;
}
echo
"No hay más datos en el buffer\n";
}

function
_http_about($req) {
echo
__METHOD__, PHP_EOL;
echo
"URI : ", $req->getUri(), PHP_EOL;
echo
"\n >> Enviando la respuesta ...";
$req->sendReply(200, "OK");
echo
"OK\n";
}

function
_http_default($req, $data) {
echo
__METHOD__, PHP_EOL;
echo
"URI: ", $req->getUri(), PHP_EOL;
echo
"\n >> Enviando la respuesta ...";
$req->sendReply(200, "OK");
echo
"OK\n";
}

$port = 8010;
if (
$argc > 1) {
$port = (int) $argv[1];
}
if (
$port <= 0 || $port > 65535) {
exit(
"Puerto inválido");
}

$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", "valor de datos personalizados");

$http->bind("0.0.0.0", 8010);
$base->loop();
?>

El resultado del ejemplo sería algo similar a:

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
(presione Enter)

HTTP/1.0 200 OK
Content-Type: text/html; charset=ISO-8859-1
Connection: close

Ejemplo #9 Servidor HTTPS simple

<?php
/*
* Servidor HTTPS simple.
*
* 1) Ejecute el servidor: `php examples/https.php 9999`
* 2) Pruébelo: `php examples/ssl-connection.php 9999`
*/

function _http_dump($req, $data) {
static
$counter = 0;
static
$max_requests = 200;

if (++
$counter >= $max_requests) {
echo
"El contador ha alcanzado el número máximo de peticiones ($max_requests). Salida\n";
exit();
}

echo
__METHOD__, " llamada\n";
echo
"Petición :"; var_dump($req);
echo
"Datos :"; var_dump($data);

echo
"\n===== DUMP =====\n";
echo
"Comando :", $req->getCommand(), PHP_EOL;
echo
"URI :", $req->getUri(), PHP_EOL;
echo
"Encabezados de entrada :"; var_dump($req->getInputHeaders());
echo
"Encabezados de salida :"; var_dump($req->getOutputHeaders());

echo
"\n >> Enviando la respuesta ...";
$req->sendReply(200, "OK");
echo
"OK\n";

$buf = $req->getInputBuffer();
echo
"\n >> Lectura del buffer de entrada (", $buf->length, ") ...\n";
while (
$s = $buf->read(1024)) {
echo
$s;
}
echo
"\nNo hay más datos en el buffer\n";
}

function
_http_about($req) {
echo
__METHOD__, PHP_EOL;
echo
"URI : ", $req->getUri(), PHP_EOL;
echo
"\n >> Enviando la respuesta ...";
$req->sendReply(200, "OK");
echo
"OK\n";
}

function
_http_default($req, $data) {
echo
__METHOD__, PHP_EOL;
echo
"URI: ", $req->getUri(), PHP_EOL;
echo
"\n >> Enviando la respuesta ...";
$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(
"Puerto inválido");
}
$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", "Valor de los datos personalizados");

$http->bind($ip, $port);
$base->dispatch();

Ejemplo #10 Conexión OpenSSL

<?php
/*
* Ejemplo de Cliente OpenSSL.
*
* Uso:
* 1) Ejecute un servidor, i.e. :
* $ php examples/https.php 9999
*
* 2) Lance el cliente en otro terminal:
* $ php examples/ssl-connection.php 9999
*/

function _request_handler($req, $base) {
echo
__FUNCTION__, PHP_EOL;

if (
is_null($req)) {
echo
"Tiempo máximo de espera alcanzado\n";
} else {
$response_code = $req->getResponseCode();

if (
$response_code == 0) {
echo
"Conexión rechazada\n";
} elseif (
$response_code != 200) {
echo
"Respuesta inesperada : $response_code\n";
} else {
echo
"Éxito : $response_code\n";
$buf = $req->getInputBuffer();
echo
"Cuerpo :\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;
}

// Permite el sobrescrito del puerto
$port = 9999;
if (
$argc > 1) {
$port = (int) $argv[1];
}
if (
$port <= 0 || $port > 65535) {
exit(
"Puerto inválido\n");
}
$host = '127.0.0.1';

$ctx = _init_ssl();
if (!
$ctx) {
trigger_error("Fallo en la creación del contexto SSL", E_USER_ERROR);
}

$base = new EventBase();
if (!
$base) {
trigger_error("Fallo en la inicialización de la base de evento", 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";
?>

Ejemplo #11 Ejemplo con EventHttpConnection::makeRequest()

<?php
function _request_handler($req, $base) {
echo
__FUNCTION__, PHP_EOL;

if (
is_null($req)) {
echo
"Tiempo máximo de espera alcanzado\n";
} else {
$response_code = $req->getResponseCode();

if (
$response_code == 0) {
echo
"Conexión rechazada\n";
} elseif (
$response_code != 200) {
echo
"Respuesta inesperada : $response_code\n";
} else {
echo
"Éxito : $response_code\n";
$buf = $req->getInputBuffer();
echo
"Cuerpo :\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();
?>

El resultado del ejemplo sería algo similar a:

_request_handler
Éxito : 200
Cuerpo :
PHP, date:
2013-03-13T20:27:52+05:00

Ejemplo #12 Escucha de una conexión basada en un socket de dominio Unix

<?php
/*
* Servidor de escucha basado en un oyente de conexión libevent.
*
* Uso:
* 1) En un terminal, ejecute:
*
* $ php unix-domain-listener.php [ruta-al-socket]
*
* 2) En otro terminal, abra la conexión
* hacia el socket, i.e. :
*
* $ socat - GOPEN:/tmp/1.sock
*
* 3) Comience a escribir. El servidor debería repetir las entradas.
*/

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
"Fallo en la activación de READ\n";
return;
}
}

public function
echoReadCallback($bev, $ctx) {
// Copia todos los datos desde el buffer de entrada hacia el buffer de salida
$bev->output->addBuffer($bev->input);
}

public function
echoEventCallback($bev, $events, $ctx) {
if (
$events & EventBufferEvent::ERROR) {
echo
"Error desde 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
"No se pudo abrir la base de eventos";
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("Imposible crear el oyente", E_USER_ERROR);
}

$this->listener->setErrorCallback(array($this, "accept_error_cb"));
}

public function
dispatch() {
$this->base->dispatch();
}

// Esta función de retrollamada será llamada cuando haya datos para leer en $bev
public function acceptConnCallback($listener, $fd, $address, $ctx) {
// ¡Tenemos una nueva conexión! Se define un bufferevent para ella. */
$base = $this->base;
$this->conn[] = new MyListenerConnection($base, $fd);
}

public function
accept_error_cb($listener, $ctx) {
$base = $this->base;

fprintf(STDERR, "Se recibe un error %d (%s) en el oyente. "
."Cerrando.\n",
EventUtil::getLastSocketErrno(),
EventUtil::getLastSocketError());

$base->exit(NULL);
}
}

if (
$argc <= 1) {
exit(
"La ruta hacia el socket no está proporcionada\n");
}
$sock_path = $argv[1];

$l = new MyListener($sock_path);
$l->dispatch();
?>

Ejemplo #13 Ejemplo de servidor SMTP

<?php
/*
* Autor: Andrew Rose <hello at andrewrose dot co dot uk>
*
* Uso:
* 1) Prepare el certificado cert.pem y la clave privada privkey.pem.
* 2) Inicie el script del servidor
* 3) Abra la conexión TLS, i.e. :
* $ openssl s_client -connect localhost:25 -starttls smtp -crlf
* 4) Comience a probar los comandos listados en el método `cmd` a continuación.
*/

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, // cambiar a TRUE con certificados auténticos
EventSslContext::OPT_ALLOW_SELF_SIGNED => true // cambiar a FALSE con certificados auténticos
]);

$this->base = new EventBase();
if (!
$this->base) {
exit(
"Imposible abrir la base del evento\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(
"Imposible crear el oyente\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
"Fallo en la creación del 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, "Se recibe el error %d (%s) en el oyente. Detención.\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")
{
// Elimina los caracteres \r\n al final
$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
'Comando desconocido : '.$line."\n";
break;
}
}
}

new
Handler();
add a note

User Contributed Notes 1 note

up
4
Bas Vijfwinkel
9 years ago
In order to use certain features used in the examples above here you need a very recent version of libevent (>= 2.1).
Although 'pecl install event' will not show any errors, certain features are disabled and certain function calls might use a different number of parameters.
For example EventHttp will throw a warning that the number of parameters should be 1 instead of 2 (when using it with a SSL context) and as a bonus cause a segmentation fault.

On some distributions of Linux, the stable libevent library might not be recent enough for all features to be enabled and you might need to use an alpha version.

You can install an alpha version of libevent next to the stable version that might already be on your machine.
The basic steps are:
- download the alpha tarball to a folder e.g libevent-2.1.3-alpha.tar.gz
- tar zxvf libevent-2.1.3-alpha.tar.gz
- cd libevent-2.1.3-alpha
- ./configure --prefix=/opt/libevent-alpha
- make
- make install

Now the alpha version of libevent is created in /opt/libevent-alpha.

Before running pecl, first export the library folder of libevent so that pecl knows where your most recent libevent stuff is:
export LD_LIBRARY_PATH=/opt/libevent-2.1.3-alpha/lib
Without this export I couldn't get pecl to use the correct libevent

Now run 'pecl install event'
and when asked libevent installation prefix [/usr] :
enter : /opt/libevent-2.1.3-alpha
Now pecl will use your alpha version of libevent instead of the default version.

If everything goes well then you should be able to enjoy the full glory of this wonderfull extension.

If you try to install a very recent version of libevent on a system with a old version of openssl (0.9), you also need to update that because there are some dependencies in libevent that do not work with 0.9
To Top