|
Examples
Example #1 Simple HTTP client
<?php // Read callback 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; } }
// Event callback function eventcb($bev, $events, $base) { if ($events & EventBufferEvent::CONNECTED) { echo "Connected.\n"; } elseif ($events & (EventBufferEvent::ERROR | EventBufferEvent::EOF)) { if ($events & EventBufferEvent::ERROR) { echo "DNS error: ", $bev->getDnsErrorString(), PHP_EOL; }
echo "Closing\n"; $base->exit(); exit("Done\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); // We'll use async DNS resolving if (!$dns_base) { exit("Failed to init DNS Base\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("Failed creating bufferevent socket\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("Failed adding request to output buffer\n"); }
if (!$bev->connectHost($dns_base, $argv[1], 80, EventUtil::AF_UNSPEC)) { exit("Can't connect to host {$argv[1]}\n"); }
$base->dispatch(); ?>
The above example will output
something similar to:
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
Example #2 HTTP client using asynchronous DNS resolver
<?php /* * 1. Connect to 127.0.0.1 at port 80 * by means of EventBufferEvent::connect(). * * 2. Request /index.cphp via HTTP/1.0 * using the output buffer. * * 3. Asyncronously read the response and print it to stdout. */
// Read callback function readcb($bev, $base) { $input = $bev->getInput();
while (($n = $input->remove($buf, 1024)) > 0) { echo $buf; } }
// Event callback function eventcb($bev, $events, $base) { if ($events & EventBufferEvent::CONNECTED) { echo "Connected.\n"; } elseif ($events & (EventBufferEvent::ERROR | EventBufferEvent::EOF)) { if ($events & EventBufferEvent::ERROR) { echo "DNS error: ", $bev->getDnsErrorString(), PHP_EOL; }
echo "Closing\n"; $base->exit(); exit("Done\n"); } }
$base = new EventBase();
echo "step 1\n"; $bev = new EventBufferEvent($base, /* use internal socket */ NULL, EventBufferEvent::OPT_CLOSE_ON_FREE | EventBufferEvent::OPT_DEFER_CALLBACKS); if (!$bev) { exit("Failed creating bufferevent socket\n"); }
echo "step 2\n"; $bev->setCallbacks("readcb", /* writecb */ NULL, "eventcb", $base); $bev->enable(Event::READ | Event::WRITE);
echo "step 3\n"; // Send request $output = $bev->getOutput(); if (!$output->add( "GET /index.cphp HTTP/1.0\r\n". "Connection: Close\r\n\r\n" )) { exit("Failed adding request to output buffer\n"); }
/* Connect to the host syncronously. We know the IP, and don't need to resolve DNS. */ if (!$bev->connect("127.0.0.1:80")) { exit("Can't connect to host\n"); }
// Dispatch pending events $base->dispatch(); ?>
Example #3 Echo server
<?php /* * Simple echo server based on libevent's connection listener. * * Usage: * 1) In one terminal window run: * * $ php listener.php 9881 * * 2) In another terminal window open up connection, e.g.: * * $ nc 127.0.0.1 9881 * * 3) start typing. The server should repeat the input. */
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 "Failed to enable READ\n"; return; } }
public function echoReadCallback($bev, $ctx) { // Copy all the data from the input buffer to the output buffer
// 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 "Error from 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 "Couldn't open event base"; 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 "Unable to bind 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 "Couldn't create listener"; exit(1); }
$this->listener->setErrorCallback(array($this, "accept_error_cb")); }
public function dispatch() { $this->base->dispatch(); }
// This callback is invoked when there is data to read on $bev public function acceptConnCallback($listener, $fd, $address, $ctx) { // We got a new connection! Set up a bufferevent for it. */ $base = $this->base; $this->conn[] = new MyListenerConnection($base, $fd); }
public function accept_error_cb($listener, $ctx) { $base = $this->base;
fprintf(STDERR, "Got an error %d (%s) on the listener. " ."Shutting down.\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(); ?>
Example #4 SSL echo server
<?php /* * SSL echo server * * To test: * 1) Run: * $ php examples/ssl-echo-server/server.php 9998 * * 2) in another terminal window run: * $ 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("Failed creating SSL context\n"); }
$this->base = new EventBase(); if (!$this->base) { exit("Couldn't open event base\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("Couldn't create listener\n"); }
$this->listener->setErrorCallback(array($this, "accept_error_cb")); } function dispatch() { $this->base->dispatch(); }
// This callback is invoked when there is data to read on $bev. function ssl_read_cb($bev, $ctx) { $in = $bev->input; //$bev->getInput();
printf("Received %zu bytes\n", $in->length); printf("----- data ----\n"); printf("%ld:\t%s\n", (int) $in->length, $in->pullup(-1));
$bev->writeBuffer($in); }
// This callback is invoked when some even occurs on the event listener, // e.g. connection closed, or an error occured function ssl_event_cb($bev, $events, $ctx) { if ($events & EventBufferEvent::ERROR) { // Fetch errors from the SSL error stack while ($err = $bev->sslError()) { fprintf(STDERR, "Bufferevent error %s.\n", $err); } }
if ($events & (EventBufferEvent::EOF | EventBufferEvent::ERROR)) { $bev->free(); } }
// This callback is invoked when a client accepts new connection function ssl_accept_cb($listener, $fd, $address, $ctx) { // We got a new connection! Set up a bufferevent for it. $this->bev = EventBufferEvent::sslSocket($this->base, $fd, $this->ctx, EventBufferEvent::SSL_ACCEPTING, EventBufferEvent::OPT_CLOSE_ON_FREE);
if (!$this->bev) { echo "Failed creating ssl buffer\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); }
// This callback is invoked when we failed to setup new connection for a client function accept_error_cb($listener, $ctx) { fprintf(STDERR, "Got an error %d (%s) on the listener. " ."Shutting down.\n", EventUtil::getLastSocketErrno(), EventUtil::getLastSocketError());
$this->base->exit(NULL); }
// Initialize SSL structures, create an EventSslContext // Optionally create self-signed certificates function init_ssl() { // We *must* have entropy. Otherwise there's no point to crypto. if (!EventUtil::sslRandPoll()) { exit("EventUtil::sslRandPoll failed\n"); }
$local_cert = __DIR__."/cert.pem"; $local_pk = __DIR__."/privkey.pem";
if (!file_exists($local_cert) || !file_exists($local_pk)) { echo "Couldn't read $local_cert or $local_pk file. To generate a key\n", "and self-signed certificate, run:\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; } }
// Allow to override the port $port = 9999; if ($argc > 1) { $port = (int) $argv[1]; } if ($port <= 0 || $port > 65535) { exit("Invalid port\n"); }
$l = new MySslEchoServer($port); $l->dispatch(); ?>
Example #5 Signal handler
<?php /* Launch it in a terminal window:
$ php examples/signal.php
In another terminal window find out the pid and send SIGTERM, e.g.:
$ 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
At the first terminal window you should catch the following:
Caught signal 15 */ class MyEventSignal { private $base;
function __construct($base) { $this->base = $base; }
function eventSighandler($no, $c) { echo "Caught 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); ?>
Example #6 Use libevent's loop to process requests of `eio' extension
<?php // Callback for eio_nop() function my_nop_cb($d, $r) { echo "step 6\n"; }
$dir = "/tmp/abc-eio-temp"; if (file_exists($dir)) { rmdir($dir); }
echo "step 1\n";
$base = new EventBase();
echo "step 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 "step 3\n";
$event->add();
echo "step 4\n";
$base->dispatch();
echo "Done\n"; ?>
Example #7 Miscellaneous
<?php /* {{{ Config & supported stuff */ echo "Supported methods:\n"; foreach (Event::getSupportedMethods() as $m) { echo $m, PHP_EOL; }
// Avoiding "select" method $cfg = new EventConfig(); if ($cfg->avoidMethod("select")) { echo "`select' method avoided\n"; }
// Create event_base associated with the config $base = new EventBase($cfg); echo "Event method used: ", $base->getMethod(), PHP_EOL;
echo "Features:\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");
// Require FDS feature if ($cfg->requireFeatures(EventConfig::FEATURE_FDS)) { echo "FDS feature is now requried\n";
$base = new EventBase($cfg); ($base->getFeatures() & EventConfig::FEATURE_FDS) and print("FDS - arbitrary file descriptor types, and not just 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) { /* exit after 5 iterations with timeout of 2.33 seconds */ echo "Stopping...\n"; $arg[0]->exit(2.33); }
echo fgets($fd); }, array (&$base));
$event->add(); $base->loop(); /* Base }}} */ ?>
Example #8 Simple HTTP server
<?php /* * Simple HTTP server. * * To test it: * 1) Run it on a port of your choice, e.g.: * $ php examples/http.php 8010 * 2) In another terminal connect to some address on this port * and make GET or POST request(others are turned off here), e.g.: * $ nc -t 127.0.0.1 8010 * POST /about HTTP/1.0 * Content-Type: text/plain * Content-Length: 4 * Connection: close * (press Enter) * * It will output * 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) * * It will output: * 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 * * It will output: * HTTP/1.0 200 OK * Content-Type: text/html; charset=ISO-8859-1 * Connection: close * * 3) See what the server outputs on the previous terminal window. */
function _http_dump($req, $data) { static $counter = 0; static $max_requests = 2;
if (++$counter >= $max_requests) { echo "Counter reached max requests $max_requests. Exiting\n"; exit(); }
echo __METHOD__, " called\n"; echo "request:"; var_dump($req); echo "data:"; var_dump($data);
echo "\n===== DUMP =====\n"; echo "Command:", $req->getCommand(), PHP_EOL; echo "URI:", $req->getUri(), PHP_EOL; echo "Input headers:"; var_dump($req->getInputHeaders()); echo "Output headers:"; var_dump($req->getOutputHeaders());
echo "\n >> Sending reply ..."; $req->sendReply(200, "OK"); echo "OK\n";
echo "\n >> Reading input buffer ...\n"; $buf = $req->getInputBuffer(); while ($s = $buf->readLine(EventBuffer::EOL_ANY)) { echo $s, PHP_EOL; } echo "No more data in the buffer\n"; }
function _http_about($req) { echo __METHOD__, PHP_EOL; echo "URI: ", $req->getUri(), PHP_EOL; echo "\n >> Sending reply ..."; $req->sendReply(200, "OK"); echo "OK\n"; }
function _http_default($req, $data) { echo __METHOD__, PHP_EOL; echo "URI: ", $req->getUri(), PHP_EOL; echo "\n >> Sending reply ..."; $req->sendReply(200, "OK"); echo "OK\n"; }
$port = 8010; if ($argc > 1) { $port = (int) $argv[1]; } if ($port <= 0 || $port > 65535) { exit("Invalid port"); }
$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", "custom data value");
$http->bind("0.0.0.0", 8010); $base->loop(); ?>
The above example will output
something similar to:
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
Example #9 Simple HTTPS server
<?php /* * Simple HTTPS server. * * 1) Run the server: `php examples/https.php 9999` * 2) Test it: `php examples/ssl-connection.php 9999` */
function _http_dump($req, $data) { static $counter = 0; static $max_requests = 200;
if (++$counter >= $max_requests) { echo "Counter reached max requests $max_requests. Exiting\n"; exit(); }
echo __METHOD__, " called\n"; echo "request:"; var_dump($req); echo "data:"; var_dump($data);
echo "\n===== DUMP =====\n"; echo "Command:", $req->getCommand(), PHP_EOL; echo "URI:", $req->getUri(), PHP_EOL; echo "Input headers:"; var_dump($req->getInputHeaders()); echo "Output headers:"; var_dump($req->getOutputHeaders());
echo "\n >> Sending reply ..."; $req->sendReply(200, "OK"); echo "OK\n";
$buf = $req->getInputBuffer(); echo "\n >> Reading input buffer (", $buf->length, ") ...\n"; while ($s = $buf->read(1024)) { echo $s; } echo "\nNo more data in the buffer\n"; }
function _http_about($req) { echo __METHOD__, PHP_EOL; echo "URI: ", $req->getUri(), PHP_EOL; echo "\n >> Sending reply ..."; $req->sendReply(200, "OK"); echo "OK\n"; }
function _http_default($req, $data) { echo __METHOD__, PHP_EOL; echo "URI: ", $req->getUri(), PHP_EOL; echo "\n >> Sending reply ..."; $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("Invalid port"); } $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", "custom data value");
$http->bind($ip, $port); $base->dispatch();
Example #10 OpenSSL connection
<?php /* * Sample OpenSSL client. * * Usage: * 1) Launch a server, e.g.: * $ php examples/https.php 9999 * * 2) Launch the client in another terminal: * $ php examples/ssl-connection.php 9999 */
function _request_handler($req, $base) { echo __FUNCTION__, PHP_EOL;
if (is_null($req)) { echo "Timed out\n"; } else { $response_code = $req->getResponseCode();
if ($response_code == 0) { echo "Connection refused\n"; } elseif ($response_code != 200) { echo "Unexpected response: $response_code\n"; } else { echo "Success: $response_code\n"; $buf = $req->getInputBuffer(); echo "Body:\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; }
// Allow to override the port $port = 9999; if ($argc > 1) { $port = (int) $argv[1]; } if ($port <= 0 || $port > 65535) { exit("Invalid port\n"); } $host = '127.0.0.1';
$ctx = _init_ssl(); if (!$ctx) { trigger_error("Failed creating SSL context", E_USER_ERROR); }
$base = new EventBase(); if (!$base) { trigger_error("Failed to initialize event base", 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 "END\n"; ?>
Example #11
EventHttpConnection::makeRequest example
<?php function _request_handler($req, $base) { echo __FUNCTION__, PHP_EOL;
if (is_null($req)) { echo "Timed out\n"; } else { $response_code = $req->getResponseCode();
if ($response_code == 0) { echo "Connection refused\n"; } elseif ($response_code != 200) { echo "Unexpected response: $response_code\n"; } else { echo "Success: $response_code\n"; $buf = $req->getInputBuffer(); echo "Body:\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(); ?>
The above example will output
something similar to:
_request_handler
Success: 200
Body:
PHP, date:
2013-03-13T20:27:52+05:00
Example #12
Connection listener based on a UNIX domain socket
<?php /* * Simple echo server based on libevent's connection listener. * * Usage: * 1) In one terminal window run: * * $ php unix-domain-listener.php [path-to-socket] * * 2) In another terminal window open up connection * to the socket, e.g.: * * $ socat - GOPEN:/tmp/1.sock * * 3) Start typing. The server should repeat the input. */
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 "Failed to enable READ\n"; return; } }
public function echoReadCallback($bev, $ctx) { // Copy all the data from the input buffer to the output buffer $bev->output->addBuffer($bev->input); }
public function echoEventCallback($bev, $events, $ctx) { if ($events & EventBufferEvent::ERROR) { echo "Error from 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("Couldn't create listener", E_USER_ERROR); }
$this->listener->setErrorCallback(array($this, "accept_error_cb")); }
public function dispatch() { $this->base->dispatch(); }
// This callback is invoked when there is data to read on $bev public function acceptConnCallback($listener, $fd, $address, $ctx) { // We got a new connection! Set up a bufferevent for it. */ $base = $this->base; $this->conn[] = new MyListenerConnection($base, $fd); }
public function accept_error_cb($listener, $ctx) { $base = $this->base;
fprintf(STDERR, "Got an error %d (%s) on the listener. " ."Shutting down.\n", EventUtil::getLastSocketErrno(), EventUtil::getLastSocketError());
$base->exit(NULL); } }
if ($argc <= 1) { exit("Socket path is not provided\n"); } $sock_path = $argv[1];
$l = new MyListener($sock_path); $l->dispatch(); ?>
Example #13 Simple SMTP server
<?php /* * Author: Andrew Rose <hello at andrewrose dot co dot uk> * * Usage: * 1) Prepare cert.pem certificate and privkey.pem private key files. * 2) Launch the server script * 3) Open TLS connection, e.g.: * $ openssl s_client -connect localhost:25 -starttls smtp -crlf * 4) Start testing the commands listed in `cmd` method below. */
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, // change to true with authentic cert EventSslContext::OPT_ALLOW_SELF_SIGNED => true // change to false with authentic cert ]);
$this->base = new EventBase(); if (!$this->base) { exit("Couldn't open event base\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("Couldn't create listener\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 "Failed creating 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, "Got an error %d (%s) on the listener. Shutting down.\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") { // remove the trailing \r\n $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 'unknown command: '.$line."\n"; break; } } }
new Handler();
|