Created
January 29, 2014 06:25
-
-
Save brad-jones/8682878 to your computer and use it in GitHub Desktop.
Daemonized Scgi Server
This file contains 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 | |
/** | |
* Class: MyConnection | |
* ============================================================================= | |
* This class works with an individual connection to our server. | |
* And deals more with the actual SCGI protocol. | |
*/ | |
class MyConnection | |
{ | |
/** | |
* Property: bev | |
* ========================================================================= | |
* This is where we store the EventBufferEvent object. | |
*/ | |
private $bev; | |
/** | |
* Method: __construct | |
* ========================================================================= | |
* This creates the EventBufferEvent object along with some call backs. | |
* | |
* Parameters | |
* ------------------------------------------------------------------------- | |
* $base - A copy of EventBase | |
* $fd - The file descriptor or a resource associated with the listener. | |
* | |
* Returns: | |
* ------------------------------------------------------------------------- | |
* void | |
*/ | |
public function __construct($base, $fd, $log) | |
{ | |
$this->log = $log; | |
$this->bev = new EventBufferEvent | |
( | |
$base, $fd, EventBufferEvent::OPT_CLOSE_ON_FREE | |
); | |
$this->bev->setCallbacks | |
( | |
[$this, "readCallback"], | |
[$this, "writeCallback"], | |
[$this, "eventCallback"], | |
NULL | |
); | |
if (!$this->bev->enable(Event::READ)) | |
{ | |
file_put_contents($this->log, "ERROR: Failed to enable READ\n", FILE_APPEND); | |
return; | |
} | |
} | |
/** | |
* Method: readCallback | |
* ========================================================================= | |
* This is where we read in an actual request. | |
* For details on the protocol see: http://www.python.ca/scgi/protocol.txt | |
* | |
* Parameters | |
* ------------------------------------------------------------------------- | |
* $bev - The buffer event | |
* | |
* Returns: | |
* ------------------------------------------------------------------------- | |
* void | |
*/ | |
public function readCallback($bev) | |
{ | |
// Grab the scgi request | |
$request = $bev->input->read($bev->input->length); | |
// Grab the length of the netstring | |
$length = substr($request, 0, strpos($request, ':')); | |
// Read in the headers | |
$headers_string = substr($request, strpos($request, ':')+1, $length); | |
// Parse the headers | |
$headers = []; | |
preg_match_all('/(.*?)\x00(.*?)\x00/s', $headers_string, $matches); | |
foreach ($matches[1] as $x => $name) $headers[$name] = $matches[2][$x]; | |
// Output the headers to console for debug purposes | |
file_put_contents($this->log, 'STATUS: Request Headers'."\n", FILE_APPEND); | |
file_put_contents($this->log, print_r($headers, true), FILE_APPEND); | |
// Write the response | |
$bev->write | |
( | |
'Status: 200 OK'."\r\n". | |
'Content-Type: text/plain'."\r\n". | |
"\r\n". | |
time() | |
); | |
} | |
/** | |
* Method: writeCallback | |
* ========================================================================= | |
* This is called after we have written to the buffer. Because the SCGI | |
* protocol calls for the connection to be closed. We must then destroy | |
* ourselves. | |
* | |
* Parameters | |
* ------------------------------------------------------------------------- | |
* $bev - The buffer event | |
* | |
* Returns: | |
* ------------------------------------------------------------------------- | |
* void | |
*/ | |
public function writeCallback($bev) | |
{ | |
file_put_contents($this->log, "STATUS: Response Sent\n\n", FILE_APPEND); | |
$this->__destruct(); | |
} | |
/** | |
* Method: eventCallback | |
* ========================================================================= | |
* Some basic error handling. | |
* | |
* Parameters | |
* ------------------------------------------------------------------------- | |
* $bev - A copy of the buffer event. | |
* $events - Bit mask of events | |
* | |
* Returns: | |
* ------------------------------------------------------------------------- | |
* void | |
*/ | |
public function eventCallback($bev, $events, $log) | |
{ | |
$this->log = $log; | |
if ($events & EventBufferEvent::ERROR) | |
{ | |
file_put_contents($this->log, "ERROR: Error from bufferevent\n", FILE_APPEND); | |
} | |
if ($events & (EventBufferEvent::EOF | EventBufferEvent::ERROR)) | |
{ | |
$this->__destruct(); | |
} | |
} | |
/** | |
* Method: __destruct | |
* ========================================================================= | |
* Destroy ourselves. | |
* | |
* Parameters | |
* ------------------------------------------------------------------------- | |
* n/a | |
* | |
* Returns: | |
* ------------------------------------------------------------------------- | |
* void | |
*/ | |
public function __destruct() | |
{ | |
$this->bev->free(); | |
} | |
} |
This file contains 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 | |
/** | |
* Class: MyListener | |
* ============================================================================= | |
* This sets up the underlying socket server. | |
* Using all this nice new LibEvent code which is now part of PHP. | |
*/ | |
class MyListener | |
{ | |
/** | |
* Property: base | |
* ========================================================================= | |
* This is where the EventBase instance is stored. | |
*/ | |
public $base; | |
/** | |
* Property: listener | |
* ========================================================================= | |
* This is where the EventListener instance is stored. | |
*/ | |
public $listener; | |
/** | |
* Property: connections | |
* ========================================================================= | |
* This is where we store an array of MyConnection objects. | |
*/ | |
private $connections = array(); | |
/** | |
* Method: __construct | |
* ========================================================================= | |
* Creates the EventBase and EventListener objects, sets up some call backs. | |
* Along with some very basic error handling. It finally dispatches the | |
* events. | |
* | |
* Parameters | |
* ------------------------------------------------------------------------- | |
* $who - Who are we listening too, can be IP:PORT or unix:/tmp/my.sock | |
* | |
* Returns: | |
* ------------------------------------------------------------------------- | |
* void | |
*/ | |
public function __construct($who, $log) | |
{ | |
$this->log = $log; | |
// Create the base | |
$this->base = new EventBase(); | |
// Check the base got created okay | |
if (!$this->base) | |
{ | |
file_put_contents($this->log, "ERROR: Couldn't open event base. Shutting down...\n", FILE_APPEND); | |
exit(1); | |
} | |
// Create the listener | |
$this->listener = new EventListener | |
( | |
$this->base, | |
[$this, "acceptConnectionCb"], | |
null, | |
EventListener::OPT_CLOSE_ON_FREE | EventListener::OPT_REUSEABLE, | |
-1, | |
$who | |
); | |
// Check the listener got created okay | |
if (!$this->listener) | |
{ | |
file_put_contents($this->log, "ERROR: Couldn't create listener. Shutting down...\n", FILE_APPEND); | |
exit(1); | |
} | |
// Add an error handler for the listener | |
$this->listener->setErrorCallback([$this, "acceptErrorCb"]); | |
// Start the server basically | |
$this->base->dispatch(); | |
} | |
/** | |
* Method: acceptConnectionCb | |
* ========================================================================= | |
* When ever a new connection to our server comes in this is called. | |
* It creates a new instance of MyConnection and it then takes over the | |
* processing of the request. | |
* | |
* Parameters | |
* ------------------------------------------------------------------------- | |
* $listener - A copy of EventListener | |
* $fd - The file descriptor or a resource associated with the listener. | |
* | |
* Returns: | |
* ------------------------------------------------------------------------- | |
* void | |
*/ | |
public function acceptConnectionCb($listener, $fd) | |
{ | |
$this->connections[] = new MyConnection($this->base, $fd, $this->log); | |
} | |
/** | |
* Method: acceptErrorCb | |
* ========================================================================= | |
* When ever the listener has some sort of error this will be called. | |
* | |
* Parameters | |
* ------------------------------------------------------------------------- | |
* n/a | |
* | |
* Returns: | |
* ------------------------------------------------------------------------- | |
* void | |
*/ | |
public function acceptErrorCb() | |
{ | |
// Output the error | |
file_put_contents($this->log, "ERROR: Got an error ".EventUtil::getLastSocketError()." (".EventUtil::getLastSocketErrno().") on the listener. Shutting down...\n", FILE_APPEND); | |
// And stop the server. | |
$this->base->exit(NULL); | |
} | |
/** | |
* Method: __destruct | |
* ========================================================================= | |
* Destroy all the connections | |
* | |
* Parameters | |
* ------------------------------------------------------------------------- | |
* n/a | |
* | |
* Returns: | |
* ------------------------------------------------------------------------- | |
* void | |
*/ | |
public function __destruct() | |
{ | |
foreach ($this->connections as &$c) $c = NULL; | |
} | |
} |
This file contains 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
#!/bin/sh | |
# | |
# scgiserver Startup script for scgiserver | |
# | |
# chkconfig: - 85 15 | |
# processname: scgiserver | |
# description: scgiserver is a daemon | |
# | |
### BEGIN INIT INFO | |
# Provides: scgiserver | |
# Required-Start: $local_fs $remote_fs $network | |
# Required-Stop: $local_fs $remote_fs $network | |
# Default-Start: 2 3 4 5 | |
# Default-Stop: 0 1 6 | |
# Short-Description: start and stop scgiserver | |
### END INIT INFO | |
# Source function library. | |
. /etc/init.d/functions | |
# Some variables | |
name=scgiserver | |
bin="/usr/bin/php /opt/scgiserver/scgiserver.php" | |
lockfile=/var/lock/subsys/scgiserver | |
RETVAL=0 | |
start() { | |
echo -n $"Starting $name: " | |
daemon ${bin} | |
RETVAL=$? | |
echo | |
[ $RETVAL = 0 ] && touch ${lockfile} | |
return $RETVAL | |
} | |
stop() { | |
echo -n $"Stopping $name: " | |
killproc ${bin} | |
RETVAL=$? | |
echo | |
[ $RETVAL = 0 ] && rm -f ${lockfile} | |
} | |
restart() { | |
stop | |
start | |
} | |
reload() { | |
restart | |
} | |
status_at() { | |
status $bin | |
} | |
case "$1" in | |
start) | |
start | |
;; | |
stop) | |
stop | |
;; | |
reload|restart) | |
restart | |
;; | |
condrestart) | |
if [ -f $proc ]; then | |
restart | |
fi | |
;; | |
status) | |
status_at | |
;; | |
*) | |
echo $"Usage: $0 {start|stop|restart|condrestart|status}" | |
exit 1 | |
esac | |
exit $? | |
exit $RETVAL |
This file contains 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
#!/usr/bin/env php | |
<?php | |
// clear the shebang and anyother possible previous buffer | |
@ob_end_clean(); | |
// turn on full error reporting | |
error_reporting(E_ALL); | |
// include our classes | |
require('MyListener.php'); | |
require('MyConnection.php'); | |
// define some variables | |
$log = '/var/log/scgiserver.log'; | |
$address = '127.0.0.1'; | |
$port = 9000; | |
// Tell the world we are starting up | |
file_put_contents($log, "STATUS: starting up.\n", FILE_APPEND); | |
// daemonize ourselves | |
$pid = pcntl_fork(); | |
if($pid == -1) | |
{ | |
// error | |
file_put_contents($log, "ERROR: could not daemonize process. Shutting down...\n", FILE_APPEND); | |
return 1; | |
} | |
else if($pid) | |
{ | |
// success | |
file_put_contents($log, "STATUS: daemonized process.\n", FILE_APPEND); | |
return 0; | |
} | |
else | |
{ | |
// start the server | |
new MyListener($address.':'.$port, $log); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment