mirror of
https://github.com/YGGverse/gemini-php.git
synced 2025-03-13 06:01:58 +00:00
implement TLS/socket client
This commit is contained in:
parent
0973828a82
commit
edf0234056
52
README.md
52
README.md
@ -5,7 +5,57 @@ PHP 8 Library for Gemini Protocol
|
|||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
```
|
```
|
||||||
composer require yggverse/gemini:dev-main
|
composer require yggverse/gemini
|
||||||
|
```
|
||||||
|
|
||||||
|
## Client
|
||||||
|
|
||||||
|
PHP interface for Gemini protocol queries by TLS socket connection
|
||||||
|
|
||||||
|
### Request
|
||||||
|
|
||||||
|
```
|
||||||
|
$request = new \Yggverse\Gemini\Client\Request(
|
||||||
|
'gemini://betahowto.duckdns.org:1965/archive'
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Request::setHost
|
||||||
|
#### Request::getHost
|
||||||
|
#### Request::setPort
|
||||||
|
#### Request::getPort
|
||||||
|
#### Request::setPath
|
||||||
|
#### Request::getPath
|
||||||
|
#### Request::setQuery
|
||||||
|
#### Request::getQuery
|
||||||
|
#### Request::getResponse
|
||||||
|
|
||||||
|
Execute requested URL and return raw response
|
||||||
|
|
||||||
|
```
|
||||||
|
var_dump(
|
||||||
|
$request->getResponse()
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Response
|
||||||
|
|
||||||
|
This class provides additional features for the raw response operations
|
||||||
|
|
||||||
|
```
|
||||||
|
$response = new \Yggverse\Gemini\Client\Response(
|
||||||
|
$request->getResponse()
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Response::getCode
|
||||||
|
#### Response::getMeta
|
||||||
|
#### Response::getBody
|
||||||
|
|
||||||
|
```
|
||||||
|
var_dump(
|
||||||
|
$response->getBody()
|
||||||
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
## DokuWiki
|
## DokuWiki
|
||||||
|
178
src/Client/Request.php
Normal file
178
src/Client/Request.php
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Yggverse\Gemini\Client;
|
||||||
|
|
||||||
|
class Request
|
||||||
|
{
|
||||||
|
private string $_host;
|
||||||
|
private int $_port;
|
||||||
|
private string $_path;
|
||||||
|
private string $_query;
|
||||||
|
|
||||||
|
public function __construct(string $url)
|
||||||
|
{
|
||||||
|
if ($host = parse_url($url, PHP_URL_HOST))
|
||||||
|
{
|
||||||
|
$this->setHost(
|
||||||
|
$host
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception(); // @TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($port = parse_url($url, PHP_URL_PORT))
|
||||||
|
{
|
||||||
|
$this->setPort(
|
||||||
|
$port
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$this->setPort(
|
||||||
|
1965
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($path = parse_url($url, PHP_URL_PATH))
|
||||||
|
{
|
||||||
|
$this->setPath(
|
||||||
|
$path
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$this->setPath(
|
||||||
|
''
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($query = parse_url($url, PHP_URL_QUERY))
|
||||||
|
{
|
||||||
|
$this->setQuery(
|
||||||
|
$query
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$this->setQuery(
|
||||||
|
''
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setHost(string $value): void
|
||||||
|
{
|
||||||
|
$this->_host = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getHost(): string
|
||||||
|
{
|
||||||
|
return $this->_host;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPort(int $value): void
|
||||||
|
{
|
||||||
|
$this->_port = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPort(): int
|
||||||
|
{
|
||||||
|
return $this->_port;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPath(string $value): void
|
||||||
|
{
|
||||||
|
$this->_path = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPath(): string
|
||||||
|
{
|
||||||
|
return $this->_path;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setQuery(string $value): void
|
||||||
|
{
|
||||||
|
$this->_query = $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getQuery(): string
|
||||||
|
{
|
||||||
|
return $this->_query;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getResponse(
|
||||||
|
int $timeout = 30, // socket timeout, useful for offline resources
|
||||||
|
?int $limit = null, // content length, null for unlimited
|
||||||
|
int $chunk = 1024, // chunk size, it's better change it later to 1 @TODO
|
||||||
|
int &$length = 0, // current response length, do not change without special needs
|
||||||
|
?int &$code = null, // error code for debug
|
||||||
|
?string &$message = null, // error message for debug
|
||||||
|
string &$response = '' // response init, also returning by this method
|
||||||
|
): ?string
|
||||||
|
{
|
||||||
|
$connection = stream_socket_client(
|
||||||
|
sprintf(
|
||||||
|
'tls://%s:%d',
|
||||||
|
$this->_host,
|
||||||
|
$this->_port
|
||||||
|
),
|
||||||
|
$code,
|
||||||
|
$message,
|
||||||
|
$timeout,
|
||||||
|
STREAM_CLIENT_CONNECT,
|
||||||
|
stream_context_create(
|
||||||
|
[
|
||||||
|
'ssl' =>
|
||||||
|
[
|
||||||
|
'verify_peer' => false,
|
||||||
|
'verify_peer_name' => false
|
||||||
|
]
|
||||||
|
]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!is_resource($connection))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
fwrite(
|
||||||
|
$connection,
|
||||||
|
sprintf(
|
||||||
|
"gemini://%s:%d%s%s\r\n",
|
||||||
|
$this->_host,
|
||||||
|
$this->_port,
|
||||||
|
$this->_path,
|
||||||
|
$this->_query
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
while ($part = fgets($connection, $chunk))
|
||||||
|
{
|
||||||
|
$length = $length + mb_strlen(
|
||||||
|
$part
|
||||||
|
);
|
||||||
|
|
||||||
|
if ($limit && $length > $limit)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$response .= $part;
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(
|
||||||
|
$connection
|
||||||
|
);
|
||||||
|
|
||||||
|
return $response;
|
||||||
|
}
|
||||||
|
}
|
58
src/Client/Response.php
Normal file
58
src/Client/Response.php
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Yggverse\Gemini\Client;
|
||||||
|
|
||||||
|
class Response
|
||||||
|
{
|
||||||
|
private ?int $_code = null;
|
||||||
|
private ?string $_meta = null;
|
||||||
|
private ?string $_body = null;
|
||||||
|
|
||||||
|
public function __construct(string $data)
|
||||||
|
{
|
||||||
|
$match = [];
|
||||||
|
|
||||||
|
preg_match(
|
||||||
|
'/(?<status>\d{2})\s(?<meta>.*)\r\n(?<body>.*)/su',
|
||||||
|
$data,
|
||||||
|
$match
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isset($match['code']))
|
||||||
|
{
|
||||||
|
$code = (int) $match['code'];
|
||||||
|
|
||||||
|
if ($code >= 10 && $code <= 69)
|
||||||
|
{
|
||||||
|
$this->_code = $match['code'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($match['meta']) && mb_strlen($match['meta']) <= 1024)
|
||||||
|
{
|
||||||
|
$this->_meta = (string) $match['meta'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($match['body']))
|
||||||
|
{
|
||||||
|
$this->_body = (string) $match['body'];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCode(): ?int
|
||||||
|
{
|
||||||
|
return $this->_code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMeta(): ?string
|
||||||
|
{
|
||||||
|
return $this->_meta;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getBody(): ?string
|
||||||
|
{
|
||||||
|
return $this->_body;
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user