Add .ini for configuration
This commit is contained in:
parent
906fe39e39
commit
744373e584
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,4 +1,3 @@
|
|||||||
/vendor/
|
|
||||||
/.idea/
|
/.idea/
|
||||||
/var/*
|
/var/*
|
||||||
!/var/.gitignore
|
!/var/.gitignore
|
||||||
|
17
README.md
17
README.md
@ -3,10 +3,9 @@
|
|||||||
This is a simple fileserver written in PHP (7.4+) using [ZeroMQ](https://zeromq.org/).
|
This is a simple fileserver written in PHP (7.4+) using [ZeroMQ](https://zeromq.org/).
|
||||||
|
|
||||||
There are three executable files:
|
There are three executable files:
|
||||||
|
- `$ bin/server` runs the fileserver
|
||||||
- `$ ./server` runs the fileserver
|
- `$ bin/command <arg1> <arg2> ...` sends a command to the server
|
||||||
- `$ ./command <arg1> <arg2> ...` sends a command to the server
|
- `$ bin/query <arg1> <arg2> ...` sends a query to the server and returns the response
|
||||||
- `$ ./query <arg1> <arg2> ...` sends a query to the server and returns the response
|
|
||||||
|
|
||||||
## Commands
|
## Commands
|
||||||
|
|
||||||
@ -15,9 +14,8 @@ There are three executable files:
|
|||||||
- `DELETE_ALL <namespace>` deletes an entire namespace
|
- `DELETE_ALL <namespace>` deletes an entire namespace
|
||||||
|
|
||||||
To save a file to the fileserver you can do:
|
To save a file to the fileserver you can do:
|
||||||
`$ ./command SAVE my-project-namespace 1.html "<!DOCTYPE html><html>...</html>"`
|
`$ bin/command SAVE my-project-namespace 1.html "<!DOCTYPE html><html>...</html>"`
|
||||||
`$ ./command SAVE my-project-namespace 2.xml "$(cat some/xml/file)"`
|
`$ bin/command SAVE my-project-namespace 2.xml "$(cat some/xml/file)"`
|
||||||
|
|
||||||
|
|
||||||
## Queries
|
## Queries
|
||||||
|
|
||||||
@ -25,5 +23,8 @@ To save a file to the fileserver you can do:
|
|||||||
- `LOAD <namespace> <name>` returns the file contents if it exists, else `-1`
|
- `LOAD <namespace> <name>` returns the file contents if it exists, else `-1`
|
||||||
|
|
||||||
To load a file from the fileserver you can do:
|
To load a file from the fileserver you can do:
|
||||||
`$ ./query LOAD my-project-namespace 1.html`
|
`$ bin/query LOAD my-project-namespace 1.html`
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
See `config/config.ini`. It contains an example configuration.
|
||||||
|
13
bin/command
Executable file
13
bin/command
Executable file
@ -0,0 +1,13 @@
|
|||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
array_shift($argv);
|
||||||
|
if (count($argv) === 0) {
|
||||||
|
print('command needs at least one argument' . PHP_EOL);
|
||||||
|
die(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
$ini = parse_ini_file(__DIR__ . '/../config/config.ini', true)['command'];
|
||||||
|
$socket = new ZMQSocket(new ZMQContext, ZMQ::SOCKET_PUSH);
|
||||||
|
$socket->connect($ini['dsn']);
|
||||||
|
$socket->sendmulti($argv);
|
15
bin/query
Executable file
15
bin/query
Executable file
@ -0,0 +1,15 @@
|
|||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
array_shift($argv);
|
||||||
|
if (count($argv) === 0) {
|
||||||
|
print('query needs at least one argument' . PHP_EOL);
|
||||||
|
die(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
$ini = parse_ini_file(__DIR__ . '/../config/config.ini', true)['query'];
|
||||||
|
$socket = new ZMQSocket(new ZMQContext, ZMQ::SOCKET_REQ);
|
||||||
|
$socket->connect($ini['dsn']);
|
||||||
|
$socket->sendmulti($argv);
|
||||||
|
print(implode(PHP_EOL, $socket->recvMulti()));
|
||||||
|
print(PHP_EOL);
|
10
bin/server
Executable file
10
bin/server
Executable file
@ -0,0 +1,10 @@
|
|||||||
|
#!/usr/bin/env php
|
||||||
|
<?php
|
||||||
|
|
||||||
|
require_once __DIR__ . '/../src/FileServer.php';
|
||||||
|
|
||||||
|
$ini = parse_ini_file(__DIR__ . '/../config/config.ini', true)['server'];
|
||||||
|
$ini['storage_dir'] = str_replace('%ROOT_DIR%', realpath(__DIR__ . '/..'), $ini['storage_dir']);
|
||||||
|
|
||||||
|
$server = new FileServer(new ZMQContext, $ini['storage_dir'], $ini['command_dsn'], $ini['query_dsn']);
|
||||||
|
$server->run();
|
13
command
13
command
@ -1,13 +0,0 @@
|
|||||||
#!/usr/bin/env php
|
|
||||||
<?php
|
|
||||||
|
|
||||||
array_shift($argv);
|
|
||||||
if (count($argv) === 0) {
|
|
||||||
print('Command needs at least one argument' . PHP_EOL);
|
|
||||||
die(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
$context = new ZMQContext();
|
|
||||||
$socket = new ZMQSocket($context, ZMQ::SOCKET_PUSH);
|
|
||||||
$socket->connect('ipc:///tmp/storage_server_command.ipc');
|
|
||||||
$socket->sendmulti($argv);
|
|
21
config/config.ini
Normal file
21
config/config.ini
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
[server]
|
||||||
|
; %ROOT_DIR% is replaced by the actual directory of the package
|
||||||
|
storage_dir = %ROOT_DIR%/var/storage
|
||||||
|
|
||||||
|
; Allow network-wide connections for queries
|
||||||
|
query_dsn[] = ipc:///tmp/storage_server_query.ipc
|
||||||
|
query_dsn[] = tcp://0.0.0.0:5678
|
||||||
|
|
||||||
|
; Allow local connections for commands
|
||||||
|
command_dsn[] = ipc:///tmp/storage_server_command.ipc
|
||||||
|
command_dsn[] = tcp://127.0.0.1:5679
|
||||||
|
|
||||||
|
|
||||||
|
[command]
|
||||||
|
; Connect through IPC socket
|
||||||
|
dsn = ipc:///tmp/storage_server_command.ipc
|
||||||
|
|
||||||
|
|
||||||
|
[query]
|
||||||
|
; Connect through TCP socket
|
||||||
|
dsn = tcp://localhost:5678
|
15
query
15
query
@ -1,15 +0,0 @@
|
|||||||
#!/usr/bin/env php
|
|
||||||
<?php
|
|
||||||
|
|
||||||
array_shift($argv);
|
|
||||||
if (count($argv) === 0) {
|
|
||||||
print('Query needs at least one argument' . PHP_EOL);
|
|
||||||
die(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
$context = new ZMQContext();
|
|
||||||
$socket = new ZMQSocket($context, ZMQ::SOCKET_REQ);
|
|
||||||
$socket->connect('ipc:///tmp/storage_server_query.ipc');
|
|
||||||
$socket->sendmulti($argv);
|
|
||||||
print(implode(PHP_EOL, $socket->recvMulti()));
|
|
||||||
print(PHP_EOL);
|
|
10
server
10
server
@ -1,10 +0,0 @@
|
|||||||
#!/usr/bin/env php
|
|
||||||
<?php
|
|
||||||
|
|
||||||
require_once __DIR__ . '/src/FileServer.php';
|
|
||||||
|
|
||||||
$dir = ($argv[1] ?? __DIR__ . '/var/documents');
|
|
||||||
$commandEndpoints = ['ipc:///tmp/storage_server_command.ipc'];
|
|
||||||
$queryEndpoints = ['ipc:///tmp/storage_server_query.ipc'];
|
|
||||||
(new FileServer($dir, new ZMQContext(), $commandEndpoints, $queryEndpoints))->run();
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
|
||||||
class FileServer
|
class FileServer
|
||||||
{
|
{
|
||||||
private ZMQSocket $commandSocket;
|
private ZMQSocket $commandSocket;
|
||||||
@ -8,11 +7,11 @@ class FileServer
|
|||||||
private ZMQPoll $poll;
|
private ZMQPoll $poll;
|
||||||
private string $rootDirectory;
|
private string $rootDirectory;
|
||||||
|
|
||||||
public function __construct(string $rootDirectory, ZMQContext $context, array $commandDsns, array $queryDsns)
|
public function __construct(ZMQContext $context, string $rootDirectory, array $commandDSNs, array $queryDSNs)
|
||||||
{
|
{
|
||||||
|
$this->initializeCommandSocket($context, $commandDSNs);
|
||||||
|
$this->initializeQuerySocket($context, $queryDSNs);
|
||||||
$this->initializeRootDirectory($rootDirectory);
|
$this->initializeRootDirectory($rootDirectory);
|
||||||
$this->initializeCommandSocket($context, $commandDsns);
|
|
||||||
$this->initializeQuerySocket($context, $queryDsns);
|
|
||||||
$this->initializePoll();
|
$this->initializePoll();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -22,28 +21,31 @@ class FileServer
|
|||||||
if (!is_dir($this->rootDirectory)) {
|
if (!is_dir($this->rootDirectory)) {
|
||||||
mkdir($this->rootDirectory, 0777, true);
|
mkdir($this->rootDirectory, 0777, true);
|
||||||
}
|
}
|
||||||
|
if (!is_dir($this->rootDirectory)) {
|
||||||
|
throw new RuntimeException("Unable to create directory '{$this->rootDirectory}'");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function initializeCommandSocket(ZMQContext $context, array $bindings): void
|
private function initializeCommandSocket(ZMQContext $context, array $commandDSNs): void
|
||||||
{
|
{
|
||||||
if (count($bindings) === 0) {
|
if (count($commandDSNs) === 0) {
|
||||||
throw new InvalidArgumentException("At least one binding required");
|
throw new InvalidArgumentException("At least one binding required");
|
||||||
}
|
}
|
||||||
$this->commandSocket = new ZMQSocket($context, ZMQ::SOCKET_PULL);
|
$this->commandSocket = new ZMQSocket($context, ZMQ::SOCKET_PULL);
|
||||||
$this->commandSocket->setSockOpt(ZMQ::SOCKOPT_HWM, 5);
|
$this->commandSocket->setSockOpt(ZMQ::SOCKOPT_HWM, 5);
|
||||||
foreach ($bindings as $dsn) {
|
foreach ($commandDSNs as $dsn) {
|
||||||
$this->commandSocket->bind($dsn);
|
$this->commandSocket->bind($dsn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function initializeQuerySocket(ZMQContext $context, array $bindings): void
|
private function initializeQuerySocket(ZMQContext $context, array $queryDSNs): void
|
||||||
{
|
{
|
||||||
if (count($bindings) === 0) {
|
if (count($queryDSNs) === 0) {
|
||||||
throw new InvalidArgumentException("At least one binding required");
|
throw new InvalidArgumentException("At least one binding required");
|
||||||
}
|
}
|
||||||
$this->querySocket = new ZMQSocket($context, ZMQ::SOCKET_REP);
|
$this->querySocket = new ZMQSocket($context, ZMQ::SOCKET_REP);
|
||||||
$this->querySocket->setSockOpt(ZMQ::SOCKOPT_HWM, 5);
|
$this->querySocket->setSockOpt(ZMQ::SOCKOPT_HWM, 5);
|
||||||
foreach ($bindings as $dsn) {
|
foreach ($queryDSNs as $dsn) {
|
||||||
$this->querySocket->bind($dsn);
|
$this->querySocket->bind($dsn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -75,7 +77,7 @@ class FileServer
|
|||||||
case 'LOAD':
|
case 'LOAD':
|
||||||
if (count($arguments) !== 2) {
|
if (count($arguments) !== 2) {
|
||||||
$this->querySocket->send(-1);
|
$this->querySocket->send(-1);
|
||||||
print("Malformed LOAD query\n");
|
$this->onNotEnoughArguments($query, 2, count($arguments));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$this->querySocket->send(@file_get_contents("{$this->rootDirectory}/{$arguments[0]}/{$arguments[1]}") ?: -1);
|
$this->querySocket->send(@file_get_contents("{$this->rootDirectory}/{$arguments[0]}/{$arguments[1]}") ?: -1);
|
||||||
@ -83,7 +85,7 @@ class FileServer
|
|||||||
case 'CONTAINS':
|
case 'CONTAINS':
|
||||||
if (count($arguments) !== 2) {
|
if (count($arguments) !== 2) {
|
||||||
$this->querySocket->send(-1);
|
$this->querySocket->send(-1);
|
||||||
print("Malformed CONTAINS query\n");
|
$this->onNotEnoughArguments($query, 2, count($arguments));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$this->querySocket->send(file_exists("{$this->rootDirectory}/{$arguments[0]}/{$arguments[1]}") ? 'Y' : 'N');
|
$this->querySocket->send(file_exists("{$this->rootDirectory}/{$arguments[0]}/{$arguments[1]}") ? 'Y' : 'N');
|
||||||
@ -102,21 +104,22 @@ class FileServer
|
|||||||
switch ($command) {
|
switch ($command) {
|
||||||
case 'SAVE':
|
case 'SAVE':
|
||||||
if (count($arguments) !== 3) {
|
if (count($arguments) !== 3) {
|
||||||
print("Malformed SAVE command\n");
|
$this->onNotEnoughArguments($command, 3, count($arguments));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$this->saveFile($arguments[0], $arguments[1], $arguments[2]);
|
$this->saveFile($arguments[0], $arguments[1], $arguments[2]);
|
||||||
break;
|
break;
|
||||||
case 'DELETE':
|
case 'DELETE':
|
||||||
if (count($arguments) !== 2) {
|
if (count($arguments) !== 2) {
|
||||||
print("Malformed DELETE command\n");
|
$this->onNotEnoughArguments($command, 2, count($arguments));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$this->deleteFile($arguments[0], $arguments[1]);
|
$this->deleteFile($arguments[0], $arguments[1]);
|
||||||
break;
|
break;
|
||||||
case 'DELETE_ALL':
|
case 'DELETE_ALL':
|
||||||
if (count($arguments) !== 1) {
|
if (count($arguments) !== 1) {
|
||||||
print("Malformed DELETE_ALL command\n");
|
$this->onNotEnoughArguments($command, 1, count($arguments));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
$this->deleteAll($arguments[0]);
|
$this->deleteAll($arguments[0]);
|
||||||
break;
|
break;
|
||||||
@ -126,10 +129,15 @@ class FileServer
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function onNotEnoughArguments(string $input, int $expected, int $actual): void
|
||||||
|
{
|
||||||
|
printf(date('[H:i:s]') . " Error: unexpected amount of arguments for input '%s' (%d/%d)\n", $input, $actual, $expected);
|
||||||
|
}
|
||||||
|
|
||||||
private function onUnknownInput(string $input, array $arguments): void
|
private function onUnknownInput(string $input, array $arguments): void
|
||||||
{
|
{
|
||||||
$arguments = array_map(fn($v) => substr($v, 0, 12), $arguments);
|
$arguments = array_map(fn($v) => substr($v, 0, 12), $arguments);
|
||||||
printf("Unknown input '%s' with args (%s)\n", $input, implode(', ', $arguments));
|
printf(date('[H:i:s]') . " Error: unknown input '%s' with arguments [%s]\n", $input, implode(', ', $arguments));
|
||||||
}
|
}
|
||||||
|
|
||||||
private function saveFile(string &$namespace, string &$name, string &$content): void
|
private function saveFile(string &$namespace, string &$name, string &$content): void
|
||||||
|
Loading…
Reference in New Issue
Block a user