Skip to content
This repository was archived by the owner on Apr 9, 2024. It is now read-only.
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 610685c

Browse files
author
Sam Nguyen
committedFeb 13, 2012
adding HTTP server
1 parent 2c93d54 commit 610685c

4 files changed

+245
-67
lines changed
 

‎AsyncHTTPServer.php

+114
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
<?php namespace Async\HTTP;
2+
require_once "./AsyncTCPServer.php";
3+
4+
class Request {
5+
private $parseIndex = 0;
6+
private $contentLength = 0;
7+
8+
function entityIsReady() {
9+
return
10+
(isset($this->method) && $this->method === "GET");
11+
}
12+
13+
function isValid() {
14+
$headerBoundaryPos = strpos($this->buffer, "\r\n\r\n");
15+
if($headerBoundaryPos !== FALSE) {
16+
echo "HEADER RECEIVED\n";
17+
$this->parseHeader(substr($this->buffer, 0, $headerBoundaryPos));
18+
$this->buffer = substr($this->buffer, $headerBoundaryPos + 2);
19+
}
20+
21+
if($this->entityIsReady()) {
22+
return TRUE;
23+
}
24+
25+
return FALSE;
26+
}
27+
28+
function parseHeader($headerString) {
29+
$lines = explode("\r\n", $headerString);
30+
31+
$statusLine = $lines[0];
32+
$statusLineParts = explode(" ", $statusLine);
33+
$this->method = $statusLineParts[0];
34+
$this->uri = $statusLineParts[1];
35+
$this->version = $statusLineParts[2];
36+
$this->headers = array();
37+
38+
$lines = array_slice($lines, 1);
39+
$i = 1;
40+
$numLines = count($lines);
41+
$loop = TRUE;
42+
foreach($lines as $line) {
43+
$headerParts = explode(":", $line, 2);
44+
$key = $headerParts[0];
45+
$val = $headerParts[1];
46+
$this->headers[$key] = trim($val);
47+
}
48+
49+
return TRUE;
50+
}
51+
52+
}
53+
54+
class Response {
55+
private $statusSent = FALSE;
56+
private $headersSent = FALSE;
57+
private $connection;
58+
59+
public function __construct($connection) {
60+
$this->connection = $connection;
61+
}
62+
63+
public function status($code) {
64+
$this->connection->write("HTTP/1.1 $code\r\n");
65+
}
66+
67+
public function header($key, $value) {
68+
$this->connection->write("$key: $value\r\n");
69+
}
70+
71+
public function body($data) {
72+
$this->connection->write("\r\n$data");
73+
}
74+
75+
public function close() {
76+
$this->connection->close();
77+
}
78+
}
79+
80+
class Server {
81+
public $tcpServer;
82+
public $requestCallback;
83+
public $eventBase;
84+
private $count;
85+
86+
function __construct($address) {
87+
$this->tcpServer = new \Async\TCP\Server($address);
88+
$self = $this;
89+
90+
$this->tcpServer->onConnect(function($connection) use ($self){
91+
$rsp = new Response($connection);
92+
$req = new Request();
93+
$req->buffer = "";
94+
95+
$connection->onData(function($data) use ($self, $req, $rsp) {
96+
$cb = $self->requestCallback;
97+
if(!$cb) return;
98+
99+
$req->buffer .= $data;
100+
if($req->isValid()) {
101+
$cb($req, $rsp);
102+
}
103+
});
104+
});
105+
}
106+
107+
function onRequest($function) {
108+
$this->requestCallback = $function;
109+
}
110+
111+
function run() {
112+
$this->tcpServer->run();
113+
}
114+
}

‎AsyncTCPServer.php

+101-62
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,115 @@
1-
<?php namespace Async;
1+
<?php namespace Async\TCP;
22

3-
class TCPServer {
4-
public $connectCallback;
3+
function eventReadCallback($bufferEvent, $connection) {
4+
$cb = $connection->dataCallback;
5+
if(!$cb)
6+
return;
7+
8+
$dataArray = array();
9+
while($data = event_buffer_read($bufferEvent, 256)) {
10+
$dataArray[] = $data;
11+
}
12+
13+
$cb(implode(NULL, $dataArray));
14+
};
15+
16+
function eventWriteCallback($bufferEvent, $connection) {
17+
$connection->writePending = FALSE;
18+
19+
if($connection->closePending) {
20+
$connection->close();
21+
}
22+
};
23+
24+
function eventErrorCallback($bufferEvent, $events, $connection) {
25+
$connection->close();
26+
27+
$cb = $connection->disconnectCallback;
28+
if($cb) {
29+
$cb();
30+
}
31+
};
32+
33+
34+
class Connection {
35+
public $socket;
36+
public $eventBuffer;
37+
public $tcpServer;
538
public $dataCallback;
6-
public $closeCallback;
7-
public $listenSocket;
39+
public $disconnectCallback;
40+
public $writePending = FALSE;
41+
public $closePending = FALSE;
42+
43+
function __construct($socket, $server) {
44+
$this->socket = $socket;
45+
$this->tcpServer = $server;
46+
47+
stream_set_blocking($socket, 0);
48+
49+
$this->eventBuffer = event_buffer_new(
50+
$socket, // File descriptor to watch
51+
'\Async\TCP\eventReadCallback', // Read event callback
52+
'\Async\TCP\eventWriteCallback', // Write event callback
53+
'\Async\TCP\eventErrorCallback', // Error callback
54+
$this // Custom data to provide to callback
55+
);
56+
57+
event_buffer_base_set($this->eventBuffer, $server->eventBase);
58+
// event_buffer_timeout_set($this->eventBuffer, 30, 30);
59+
event_buffer_watermark_set($this->eventBuffer, EV_READ | EV_WRITE, 0, 0xffffff);
60+
event_buffer_priority_set($this->eventBuffer, 10);
61+
event_buffer_enable($this->eventBuffer, EV_READ | EV_WRITE | EV_PERSIST);
62+
}
63+
64+
function write($bytes) {
65+
$this->writePending = TRUE;
66+
event_buffer_write($this->eventBuffer, $bytes);
67+
}
68+
69+
function close() {
70+
if(!$this->writePending)
71+
$this->_close();
72+
else {
73+
echo "WAITING FOR WRITE\n";
74+
$this->closePending = TRUE;
75+
}
76+
}
77+
78+
function _close() {
79+
event_buffer_disable($this->eventBuffer, EV_READ | EV_WRITE);
80+
event_buffer_free($this->eventBuffer);
81+
fclose($this->socket);
82+
unset($this->eventBuffer, $this->socket);
83+
echo "TCP CONNECTION CLOSED\n";
84+
}
85+
86+
function onData($function) {
87+
$this->dataCallback = $function;
88+
}
89+
90+
function onDisconnect($function) {
91+
$this->disconnectCallback = $function;
92+
}
93+
}
94+
95+
class Server {
96+
public $connectCallback;
97+
public $socket;
898
public $address;
999
public $eventBase;
10-
public $connections;
11-
public $buffers;
12-
public $count;
13100
public $evAccept;
14-
public $evRead;
15-
public $evError;
16101

17102
function __construct($address) {
18103
$this->address = $address;
19-
$this->connections = array();
20-
$this->buffers = array();
21-
$this->count = 0;
104+
$this->eventBase = \event_base_new();
22105

23106
$this->evAccept = function($fd, $events, $server) {
24-
$conn = stream_socket_accept($fd);
25-
stream_set_blocking($conn, 0);
26-
27-
$buffer = event_buffer_new(
28-
$conn, $server->evRead, NULL, $server->evError, array($server, $server->count));
29-
event_buffer_base_set($buffer, $server->eventBase);
30-
event_buffer_timeout_set($buffer, 30, 30);
31-
event_buffer_watermark_set($buffer, EV_READ, 0, 0xffffff);
32-
event_buffer_priority_set($buffer, 10);
33-
event_buffer_enable($buffer, EV_READ | EV_PERSIST);
107+
$socket = stream_socket_accept($fd);
108+
$connection = new Connection($socket, $server);
34109

35-
$server->connections[$server->count] = $conn;
36-
$server->buffers[$server->count] = $buffer;
37-
38-
$server->count += 1;
39110
$cb = $server->connectCallback;
40111
if($cb) {
41-
$cb();
42-
}
43-
};
44-
45-
$this->evRead = function($buffer, $data) {
46-
$server = $data[0];
47-
$cb = $server->dataCallback;
48-
while($read = event_buffer_read($buffer, 256)) {
49-
if($cb) {
50-
$cb($read);
51-
}
52-
}
53-
};
54-
55-
$this->evError = function($buffer, $error, $data) {
56-
$server = $data[0];
57-
$id = $data[1];
58-
event_buffer_disable($server->buffers[$id], EV_READ | EV_WRITE);
59-
event_buffer_free($server->buffers[$id]);
60-
fclose($server->connections[$id]);
61-
unset($server->buffers[$id], $server->connections[$id]);
62-
$cb = $server->closeCallback;
63-
if($cb) {
64-
$cb();
112+
$cb($connection);
65113
}
66114
};
67115
}
@@ -70,23 +118,14 @@ function onConnect($function) {
70118
$this->connectCallback = $function;
71119
}
72120

73-
function onData($function) {
74-
$this->dataCallback = $function;
75-
}
76-
77-
function onClose($function) {
78-
$this->closeCallback = $function;
79-
}
80-
81121
function run() {
82-
$this->listenSocket = stream_socket_server($this->address);
122+
$this->socket = stream_socket_server($this->address);
83123

84-
$this->eventBase = event_base_new();
85124
$event = event_new();
86125

87126
event_set(
88127
$event, // The libevent Event object
89-
$this->listenSocket, // The file descriptor to watch
128+
$this->socket, // The file descriptor to watch
90129
EV_READ|EV_PERSIST, // Watch for READ events and watch the event forever, not just once
91130
$this->evAccept, // Call this function when the event happens
92131
$this); // Pass this as the 3rd argument to the callback

‎httpservertest.php

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?php
2+
require "./AsyncHTTPServer.php";
3+
4+
$server = new \Async\HTTP\Server("tcp://localhost:8000");
5+
6+
$server->onRequest(function($req, $rsp){
7+
print_r($req);
8+
9+
$rsp->status(200);
10+
$rsp->header("Content-Type", "text/plain");
11+
12+
$body = "Hello world";
13+
$rsp->header("Content-Length", strlen($body));
14+
$rsp->body($body);
15+
$rsp->close();
16+
});
17+
18+
echo "Listening on tcp://localhost:8000\n";
19+
$server->run();

‎tcpservertest.php

+11-5
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
11
<?php
22
include "./AsyncTCPServer.php";
33

4-
$server = new \Async\TCPServer("tcp://0.0.0.0:4000");
4+
$server = new \Async\TCP\Server("tcp://0.0.0.0:4000");
55

6-
$server->onConnect(function(){
6+
$server->onConnect(function($conn){
77
echo "CONNECTED\n";
8-
});
98

10-
$server->onData(function($data){
11-
echo $data;
9+
$conn->write("Hello there\n");
10+
$conn->onDisconnect(function(){
11+
echo "CLIENT DISCONNECTED\n";
12+
});
13+
$conn->onData(function($data) use ($conn){
14+
$conn->write(strrev($data)."\n");
15+
});
1216
});
1317

18+
echo "Listening on tcp://0.0.0.0:4000\n";
19+
1420
$server->run();

0 commit comments

Comments
 (0)
This repository has been archived.