mirror of
https://github.com/sigmasternchen/php-doc-en
synced 2025-03-15 16:38:54 +00:00

Patch by carusogabriel. Closes php/doc-en#5. git-svn-id: https://svn.php.net/repository/phpdoc/en/trunk@347753 c90b9560-bf6c-de11-be94-00142212c4b1
1317 lines
34 KiB
XML
1317 lines
34 KiB
XML
<?xml version="1.0" encoding="utf-8"?>
|
|
<!-- $Revision$ -->
|
|
<chapter xml:id="event.examples" xmlns="http://docbook.org/ns/docbook" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
&reftitle.examples;
|
|
<example>
|
|
<title>Simple HTTP client</title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?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();
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
&example.outputs.similar;
|
|
<screen>
|
|
<![CDATA[
|
|
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
|
|
]]>
|
|
</screen>
|
|
</example>
|
|
<example>
|
|
<title>HTTP client using asynchronous DNS resolver</title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?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();
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</example>
|
|
<example>
|
|
<title>Echo server</title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?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();
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</example>
|
|
<example>
|
|
<title>SSL echo server</title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?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 occurred
|
|
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();
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</example>
|
|
<example>
|
|
<title>Signal handler</title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?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);
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</example>
|
|
<example>
|
|
<title>Use libevent's loop to process requests of `eio' extension</title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?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";
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</example>
|
|
<example>
|
|
<title>Miscellaneous</title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?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 }}} */
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</example>
|
|
<example>
|
|
<title>Simple HTTP server</title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?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();
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
&example.outputs.similar;
|
|
<screen>
|
|
<![CDATA[
|
|
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
|
|
]]>
|
|
</screen>
|
|
</example>
|
|
<example>
|
|
<title>Simple HTTPS server</title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?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();
|
|
]]>
|
|
</programlisting>
|
|
</example>
|
|
<example>
|
|
<title>OpenSSL connection</title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?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";
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</example>
|
|
<example>
|
|
<title>
|
|
<function>EventHttpConnection::makeRequest</function> example</title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?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();
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
&example.outputs.similar;
|
|
<screen>
|
|
<![CDATA[
|
|
_request_handler
|
|
Success: 200
|
|
Body:
|
|
PHP, date:
|
|
2013-03-13T20:27:52+05:00
|
|
]]>
|
|
</screen>
|
|
</example>
|
|
<example>
|
|
<title>
|
|
Connection listener based on a UNIX domain socket</title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?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();
|
|
?>
|
|
]]>
|
|
</programlisting>
|
|
</example>
|
|
<example xml:id="event.example.smtp">
|
|
<title>Simple SMTP server</title>
|
|
<programlisting role="php">
|
|
<![CDATA[
|
|
<?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();
|
|
]]>
|
|
</programlisting>
|
|
</example>
|
|
</chapter>
|
|
<!-- Keep this comment at the end of the file
|
|
Local variables:
|
|
mode: sgml
|
|
sgml-omittag:t
|
|
sgml-shorttag:t
|
|
sgml-minimize-attributes:nil
|
|
sgml-always-quote-attributes:t
|
|
sgml-indent-step:1
|
|
sgml-indent-data:t
|
|
indent-tabs-mode:nil
|
|
sgml-parent-document:nil
|
|
sgml-default-dtd-file:"~/.phpdoc/manual.ced"
|
|
sgml-exposed-tags:nil
|
|
sgml-local-catalogs:nil
|
|
sgml-local-ecat-files:nil
|
|
End:
|
|
vim600: syn=xml fen fdm=syntax fdl=2 si
|
|
vim: et tw=78 syn=sgml
|
|
vi: ts=1 sw=1
|
|
-->
|