Created
April 6, 2012 15:24
-
-
Save BraveSirRobin/2320754 to your computer and use it in GitHub Desktop.
Bug demonstration script for PHP/libevent 0.0.4 Segfault.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Run this script as-is and it will segfault. My setup is Ubuntu | |
* 11.10, library details: | |
* | |
* php5-cli : 5.3.6-13ubuntu3.6 (output from dpkg -l) | |
* libevent : 0.0.5 beta (output from pecl list) | |
* php-pear : 5.3.6-13ubuntu3.6 (output from dpkg -l) | |
* | |
* There are 2 things you can do to stop the segfault: | |
* | |
* 1. Comment out the given line in ELoop->checkAddEvent() | |
* 2. Only add a single client to the loop | |
*/ | |
class ELoop | |
{ | |
private $evBase; | |
private $clients = array(); | |
function addClient ($client) { | |
$this->clients[] = $client; | |
} | |
function loop () { | |
$this->initLibevent(); | |
$hasWork = false; | |
foreach ($this->clients as $c) { | |
if ($ev = $this->checkAddEvent($c)) { | |
$hasWork = true; | |
} | |
} | |
if ($hasWork) { | |
event_base_loop($this->evBase); | |
} | |
} | |
private function checkAddEvent ($c, $ev=null) { | |
$flags = 0; | |
if ($c->canRead()) | |
$flags = $flags | EV_READ; | |
if ($c->canWrite()) | |
$flags = $flags | EV_WRITE; | |
if (! $flags) { | |
if ($ev) { | |
event_del($ev); | |
event_free($ev); // Comment this line and the segfault doesn't happen | |
} | |
if (false === ($i = array_search($c, $this->clients))) { | |
throw new \Exception("Failed to locate client within local collection during removal", 9062); | |
} else { | |
unset($this->clients[$i]); | |
} | |
return; | |
} | |
if (! $ev) | |
$ev = event_new(); | |
event_set($ev, $c->getSocketHandle(), $flags, array($this, 'loopEventDispatch'), array($c, $ev)); | |
event_base_set($ev, $this->evBase); | |
event_add($ev); | |
return $ev; | |
} | |
function loopEventDispatch ($fd, $events, $params) { | |
list($client, $event) = $params; | |
if ($events & EV_READ) | |
$client->read(); | |
if ($events & EV_WRITE) | |
$client->write(); | |
$this->checkAddEvent($client, $event); | |
} | |
private function initLibevent () { | |
$this->evBase = event_base_new(); | |
} | |
} | |
class SocketClient | |
{ | |
private $host, $port, $sock; | |
function open ($host, $port) { | |
$this->host = $host; | |
$this->port = $port; | |
if (! ($this->sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP))) { | |
throw new \Exception("Failed to create inet socket", 7895); | |
} else if (! socket_connect($this->sock, $this->host, $this->port)) { | |
throw new \Exception("Failed to connect inet socket ({$this->host}, {$this->port})", 7564); | |
} else if (! socket_set_nonblock($this->sock)) { | |
throw new \Exception("Failed to switch connection in to non-blocking mode.", 2357); | |
} | |
} | |
function getSocketHandle () { | |
return $this->sock; | |
} | |
function read () { | |
$buff = ''; | |
while (@socket_recv($this->sock, $tmp, 4096, MSG_DONTWAIT)) { | |
$buff .= $tmp; | |
} | |
return $buff; | |
} | |
function write ($buff='') { | |
if (($tmp = socket_write($this->sock, $buff)) === false) { | |
throw new \Exception(sprintf("Socket write failed: %s", | |
socket_strerror(socket_last_error($this->sock))), 7854); | |
} | |
} | |
} | |
class HttpClient extends SocketClient | |
{ | |
private $state = 0, $domain; | |
function __construct ($domain) { | |
$ip = gethostbyname($domain); | |
if ($ip == $domain) { | |
throw new \Exception("Failed to resolve HTTP host", 2351); | |
} | |
$this->open($ip, 80); | |
$this->domain = $domain; | |
} | |
function canRead () { | |
switch ($this->state) { | |
case 1: | |
return true; | |
default: | |
return false; | |
} | |
} | |
function canWrite () { | |
switch ($this->state) { | |
case 0: | |
return true; | |
default: | |
return false; | |
} | |
} | |
function read () { | |
$this->state = 2; | |
$buff = parent::read(); | |
printf("[http] Reads response (%d):\n%s\n", strlen($buff), substr($buff, 0, strpos($buff, "\r\n"))); | |
} | |
function write ($buff='') { | |
if ($this->state == 0) { | |
// Write HTTP header. | |
parent::write("GET / HTTP/1.1\r\nHost: {$this->domain}\r\n" . | |
"Connection: close\r\n" . | |
"User-Agent: php libevent test\r\n" . | |
"Accept-Charset: ISO-8859-1,UTF-8;q=0.7,*;q=0.7\r\n" . | |
"Cache-Control: no-cache\r\n" . | |
"Accept-Language: de,en;q=0.7,en-us;q=0.3\r\n\r\n"); | |
$this->state = 1; | |
} | |
} | |
} | |
$el = new ELoop; | |
$el->addClient(new HttpClient('microsoft.com')); | |
$el->addClient(new HttpClient('bluelines.org')); | |
$el->loop(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment