Skip to content

Commit 9493fe8

Browse files
committed
Init commit
1 parent 164a84c commit 9493fe8

File tree

5 files changed

+168
-0
lines changed

5 files changed

+168
-0
lines changed

cgi.php

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
4+
/**
5+
* Requires enabling the "spckets" extentions:
6+
* `extension=sockets`
7+
*/
8+
9+
10+
require './src/Request.php';
11+
require './src/Server.php';
12+
13+
//
14+
// Place to store public files
15+
const PUBLIC_DIR = __DIR__ . '/public';
16+
17+
18+
(new Server('127.0.0.1', 8080, PUBLIC_DIR))->run();

public/Index.php

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8">
5+
<title>CGI Example</title>
6+
</head>
7+
<body>
8+
9+
<h1>CGI example</h1>
10+
11+
<p>Paragraph.</p>
12+
13+
</body>
14+
</html>

readme.md

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
## CGI Server based on PHP
2+
3+
This repo provides simple CGI server.
4+
5+
6+
### Example
7+
8+
In your web browser open:
9+
```
10+
http://localhost:8080/index.php
11+
12+
```
13+

src/Request.php

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
<?php
2+
3+
/**
4+
* Simper Requset object
5+
*/
6+
readonly class Request
7+
{
8+
private function __construct(
9+
public string $method,
10+
public string $path,
11+
public string $query,
12+
public string $body,
13+
)
14+
{
15+
}
16+
17+
public static function fromString(string $request): self
18+
{
19+
$request = explode("\r\n\r\n", $request);
20+
$headers = explode("\r\n", $request[0]);
21+
$body = $request[1] ?? '';
22+
$headerLine = array_shift($headers);
23+
preg_match('/(GET|POST) (\/\S*) HTTP\/1.\d/', $headerLine, $matches);
24+
$method = strtolower($matches[1]);
25+
$path = $matches[2];
26+
$query = parse_url($path);
27+
28+
return new self(
29+
$method,
30+
$query['path'] ?? '',
31+
$query['query'] ?? '',
32+
$body ?? '',
33+
);
34+
}
35+
}

src/Server.php

+88
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
<?php
2+
3+
/**
4+
* Provides CGI server
5+
*/
6+
class Server
7+
{
8+
private Socket $server;
9+
10+
public function __construct(
11+
public readonly string $address,
12+
public readonly int $port,
13+
public readonly string $public,
14+
)
15+
{
16+
}
17+
18+
public function run()
19+
{
20+
$this->server = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
21+
$server = $this->server;
22+
socket_set_option($server, SOL_SOCKET, SO_REUSEADDR, 1);
23+
socket_bind($server, $this->address, $this->port);
24+
socket_listen($server);
25+
26+
echo "Server started on {$this->address}:{$this->port}\n";
27+
28+
while (true) {
29+
$client = socket_accept($server);
30+
$request = socket_read($client, 4096);
31+
$requestObject = Request::fromString($request);
32+
$this->handleRequest($client, $requestObject);
33+
socket_close($client);
34+
}
35+
}
36+
37+
38+
private function handleRequest(false|Socket $client, Request $request): void
39+
{
40+
$env = [
41+
'REQUEST_METHOD' => $request->method,
42+
'CONTENT_LENGTH' => strlen($request->body),
43+
'CONTENT_TYPE' => 'application/x-www-form-urlencoded',
44+
'QUERY_STRING' => $request->query,
45+
];
46+
47+
$cmd = $request->path;
48+
$params = str_replace('+', ' ', $request->query);
49+
50+
$descriptor_spec = [
51+
0 => ['pipe', 'r'], // stdin
52+
1 => ['pipe', 'w'], // stdout
53+
2 => ['pipe', 'w'], // stderr
54+
];
55+
var_dump($cmd);
56+
$process = proc_open($cmd . ' ' . $params, $descriptor_spec, $pipes, $this->public , $env);
57+
if (!is_resource($process)) {
58+
echo 'Error opening CGI program: ' . $cmd . "\n";
59+
return;
60+
}
61+
62+
if ($request->body) {
63+
fwrite($pipes[0], $request->body);
64+
}
65+
fclose($pipes[0]);
66+
67+
$output = stream_get_contents($pipes[1]);
68+
$errors = stream_get_contents($pipes[2]);
69+
70+
if ($errors) {
71+
echo $errors;
72+
}
73+
74+
fclose($pipes[1]);
75+
fclose($pipes[2]);
76+
proc_close($process);
77+
78+
// Sending response to browser
79+
$statusCode = str_contains($output, 'Location') ? '302 Found' : '200 OK';
80+
$response = "HTTP/1.1 $statusCode\r\n$output";
81+
socket_write($client, $response);
82+
}
83+
84+
public function __destruct()
85+
{
86+
socket_close($this->server);
87+
}
88+
}

0 commit comments

Comments
 (0)