💾 Archived View for code.lanterne.chilliet.eu › vendor › mcmic › gemini-server › src › Request.php captured on 2023-09-08 at 15:57:29.
⬅️ Previous capture (2021-12-17)
-=-=-=-=-=-=-
<?php declare(strict_types=1); namespace MCMic\Gemini; class Request { public ?string $scheme; public ?string $host; public ?string $punycodedHost; public ?int $port; public ?string $user; public ?string $pass; public ?string $path; public ?string $query; public ?string $fragment; public function __construct(string $request) { $url = parse_url(trim($request)); if ($url === false) { throw new Exception('Malformed URL', 59); } $this->scheme = $url['scheme'] ?? null; $this->host = $url['host'] ?? null; $this->port = $url['port'] ?? null; $this->user = $url['user'] ?? null; $this->pass = $url['pass'] ?? null; $this->path = $url['path'] ?? null; $this->query = $url['query'] ?? null; $this->fragment = $url['fragment'] ?? null; if (isset($this->scheme)) { $this->scheme = mb_strtolower($this->scheme); } if (isset($this->path)) { $this->path = rawurldecode($this->path); } if (isset($this->query)) { $this->query = rawurldecode($this->query); } if (isset($this->host)) { $this->host = mb_strtolower($this->host); $idn = \idn_to_utf8($this->host); $punycode = \idn_to_ascii($this->host); if (($idn === false) || ($punycode === false)) { throw new Exception('Error while attempting to decode IDN', 59); } $this->host = $idn; $this->punycodedHost = $punycode; } } /** * @throws \MCMic\Gemini\Exception */ public function assertValid(): void { if (!isset($this->scheme)) { throw new Exception('Missing scheme', 59); } if ($this->scheme !== 'gemini') { throw new Exception('Bad protocol', 53); } } public function __toString(): string { return $this->buildUrl(); } public static function urlEncode(string $str): string { return join('/', array_map('rawurlencode', explode('/', ($str)))); } public function resolveLink(string $link): Request { $request = new Request($link); $request->scheme ??= $this->scheme; $request->host ??= $this->host; if (!isset($request->path)) { $request->path = $this->path; } elseif (!isset($request->path[0]) || ($request->path[0] != '/')) { $request->path = $this->path . '/' . $request->path; } return $request; } public function buildUrl(?string $path = null): string { $url = ''; if (isset($this->scheme)) { $url .= $this->scheme . '://'; } if (isset($this->user)) { $url .= $this->user; } if (isset($this->pass)) { $url .= ':' . $this->pass; } if (isset($this->user) || isset($this->pass)) { $url .= '@'; } if (isset($this->host)) { $url .= $this->host; } if (isset($this->port)) { $url .= ':' . $this->port; } $url .= static::urlEncode($path ?? $this->path ?? ''); if (isset($this->query)) { $url .= '?' . rawurlencode($this->query); } if (isset($this->fragment)) { $url .= '#' . $this->fragment; } return $url; } public function sendTo(?string $host = null, ?int $port = null): Response { $host = $host ?? $this->host; $port = $port ?? $this->port ?? 1965; $context = stream_context_create( [ 'ssl' => [ 'verify_peer' => true, 'verify_peer_name' => false, 'allow_self_signed' => true, 'verify_depth' => 0, 'SNI_enabled' => true, 'capture_peer_cert' => true, ] ] ); $timeout = ini_get('default_socket_timeout'); if (($timeout === false) || $timeout === '') { $timeout = 60; } $socket = stream_socket_client( 'tcp://' . $host . ':' . $port, $errno, $errstr, (float) $timeout, STREAM_CLIENT_CONNECT, $context, ); if ($socket === false) { throw new \Exception($errstr, $errno); } else { $tlsSuccess = stream_socket_enable_crypto( $socket, true, STREAM_CRYPTO_METHOD_TLS_CLIENT & ~STREAM_CRYPTO_METHOD_TLSv1_0_CLIENT & ~STREAM_CRYPTO_METHOD_TLSv1_1_CLIENT | 1 ); if ($tlsSuccess !== true) { fclose($socket); throw new \Exception('Failed TLS'); } fwrite($socket, $this->buildUrl() . "\r\n"); $header = fgets($socket, 1024); if ($header === false) { throw new \Exception('No answer'); } [$status, $meta] = explode(' ', $header, 2); $status = (int)$status; $body = null; if (($status >= 20) && ($status < 30)) { $body = ''; while (!feof($socket)) { $body .= fgets($socket, 1024); } } fclose($socket); return new Response($status, $meta, $body); } } }