Skip to content

Instantly share code, notes, and snippets.

@lisachenko
Created November 8, 2016 15:02
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save lisachenko/825e1042b7152c3fc32f655b7e818e8f to your computer and use it in GitHub Desktop.
Save lisachenko/825e1042b7152c3fc32f655b7e818e8f to your computer and use it in GitHub Desktop.
Swoole FCGI server
<?php
use Protocol\FCGI;
use Protocol\FCGI\FrameParser;
use Protocol\FCGI\Record\BeginRequest;
use Protocol\FCGI\Record\EndRequest;
use Protocol\FCGI\Record\Params;
use Protocol\FCGI\Record\Stdin;
use Protocol\FCGI\Record\Stdout;
include __DIR__ . '/vendor/autoload.php';
class FCGIRequest
{
public $id = null;
public $role = 0;
public $flags = 0;
public $params = [];
public $stdin = '';
}
class FCGIServer extends swoole_server
{
/**
* List of requests
*
* @var array|FCGIRequest[]
*/
private $requests = [];
public function __construct($host, $port)
{
parent::__construct($host, $port);
$this->on('connect', (new ReflectionMethod($this, 'onConnect'))->getClosure($this));
$this->on('close', (new ReflectionMethod($this, 'onClose'))->getClosure($this));
$this->on('receive', (new ReflectionMethod($this, 'onReceive'))->getClosure($this));
}
protected function onConnect($server, $id)
{
$this->requests[$id] = new FCGIRequest();
}
protected function onReceive($server, $id, $fromId, $data)
{
while (FrameParser::hasFrame($data)) {
$message = FrameParser::parseFrame($data);
if ($message instanceof BeginRequest) {
$this->requests[$id]->id = $message->getRequestId();
$this->requests[$id]->role = $message->getRole();
$this->requests[$id]->flags = $message->getFlags();
}
if ($message instanceof Params) {
$this->requests[$id]->params += $message->getValues();
}
if ($message instanceof Stdin) {
$isLastParam = $message->getContentLength() == 0;
if (!$isLastParam) {
$this->requests[$id]->stdin .= $message->getContentData();
} else {
$this->onRequest($id);
}
}
}
}
protected function onRequest($id)
{
$request = $this->requests[$id];
$body = print_r($request, true); // let's response with content of all FCGI params from the request
$bodySize = strlen($body);
/** @var FCGI\Record[] $messages */
$messages = [
// we can also split responses into several chunks for streaming large response
new Stdout("HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: {$bodySize}\r\n\r\n{$body}"),
new Stdout(''), // empty one, according to the specification
new EndRequest(FCGI::REQUEST_COMPLETE, $appStatus = 0), // normal request termination
];
$responseContent = '';
foreach ($messages as $message) {
$message->setRequestId($request->id);
$responseContent .= $message;
}
$this->send($id, $responseContent);
$this->close($id);
}
protected function onClose($server, $id)
{
unset($this->requests[$id]);
}
}
$server = new FCGIServer("127.0.0.1", 9501);
$server->start();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment