headers = $headers; $this->requestBody = $requestBody; $this->clientName = $headers[StandardHeaders::CLIENT_NAME->value] ?? null; $this->clientVersion = $headers[StandardHeaders::CLIENT_VERSION->value] ?? null; $this->requestType = RequestType::tryFrom($headers[StandardHeaders::REQUEST_TYPE->value]); $this->identifyAs = $headers[StandardHeaders::IDENTIFY_AS->value] ?? null; $this->sessionUuid = $headers[StandardHeaders::SESSION_UUID->value] ?? null; $this->signature = $headers[StandardHeaders::SIGNATURE->value] ?? null; } /** * Retrieves the headers. * * @return array Returns an array of headers. */ public function getHeaders(): array { return $this->headers; } /** * Checks if the specified header exists in the collection of headers. * * @param StandardHeaders|string $header The header to check, either as a StandardHeaders enum or a string. * @return bool Returns true if the header exists, otherwise false. */ public function headerExists(StandardHeaders|string $header): bool { if(is_string($header)) { return isset($this->headers[$header]); } return isset($this->headers[$header->value]); } /** * Retrieves the value of a specified header. * * @param StandardHeaders|string $header The header to retrieve, provided as either a StandardHeaders enum or a string key. * @return string|null Returns the header value if it exists, or null if the header does not exist. */ public function getHeader(StandardHeaders|string $header): ?string { if(!$this->headerExists($header)) { return null; } if(is_string($header)) { return $this->headers[$header]; } return $this->headers[$header->value]; } /** * Retrieves the request body. * * @return string|null Returns the request body as a string if available, or null if not set. */ public function getRequestBody(): ?string { return $this->requestBody; } /** * Retrieves the name of the client. * * @return string|null Returns the client's name if set, or null if not available. */ public function getClientName(): ?string { return $this->clientName; } /** * Retrieves the client version. * * @return string|null Returns the client version if available, or null if not set. */ public function getClientVersion(): ?string { return $this->clientVersion; } /** * Retrieves the request type associated with the current instance. * * @return RequestType|null Returns the associated RequestType if available, or null if not set. */ public function getRequestType(): ?RequestType { return $this->requestType; } /** * Retrieves the peer address the instance identifies as. * * @return PeerAddress|null Returns a PeerAddress instance if the identification address is set, or null otherwise. */ public function getIdentifyAs(): ?PeerAddress { if($this->identifyAs === null) { return null; } return PeerAddress::fromAddress($this->identifyAs); } /** * Retrieves the UUID of the current session. * * @return string|null Returns the session UUID if available, or null if it is not set. */ public function getSessionUuid(): ?string { return $this->sessionUuid; } /** * Retrieves the current session associated with the session UUID. * * @return SessionRecord|null Returns the associated SessionRecord if the session UUID exists, or null if no session UUID is set. */ public function getSession(): ?SessionRecord { if($this->sessionUuid === null) { return null; } return SessionManager::getSession($this->sessionUuid); } /** * Retrieves the associated peer for the current session. * * @return RegisteredPeerRecord|null Returns the associated RegisteredPeerRecord if available, or null if no session exists. */ public function getPeer(): ?RegisteredPeerRecord { $session = $this->getSession(); if($session === null) { return null; } return RegisteredPeerManager::getPeer($session->getPeerUuid()); } /** * Retrieves the signature value. * * @return string|null The signature value or null if not set */ public function getSignature(): ?string { return $this->signature; } /** * Verifies the signature of the provided decrypted content. * * @param string $decryptedContent The decrypted content to verify the signature against. * @return bool True if the signature is valid, false otherwise. */ private function verifySignature(string $decryptedContent): bool { if($this->getSignature() == null || $this->getSessionUuid() == null) { return false; } try { return Cryptography::verifyMessage( message: $decryptedContent, signature: $this->getSignature(), publicKey: $this->getSession()->getClientPublicSigningKey() ); } catch(CryptographyException) { return false; } } /** * Handles a POST request, returning an array of RpcRequest objects * expects a JSON encoded body with either a single RpcRequest object or an array of RpcRequest objects * * @return RpcRequest[] The parsed RpcRequest objects * @throws RequestException Thrown if the request is invalid */ public function getRpcRequests(string $json): array { $body = json_decode($json, true); if($body === false) { throw new RequestException('Malformed JSON', 400); } // If the body only contains a method, we assume it's a single request if(isset($body['method'])) { return [$this->parseRequest($body)]; } // Otherwise, we assume it's an array of requests return array_map(fn($request) => $this->parseRequest($request), $body); } /** * Parses the raw request data into an RpcRequest object * * @param array $data The raw request data * @return RpcRequest The parsed RpcRequest object * @throws RequestException If the request is invalid */ private function parseRequest(array $data): RpcRequest { if(!isset($data['method'])) { throw new RequestException("Missing 'method' key in request", 400); } if(isset($data['id'])) { if(!is_string($data['id'])) { throw new RequestException("Invalid 'id' key in request: Expected string", 400); } if(strlen($data['id']) === 0) { throw new RequestException("Invalid 'id' key in request: Expected non-empty string", 400); } if(strlen($data['id']) > 8) { throw new RequestException("Invalid 'id' key in request: Expected string of length <= 8", 400); } } if(isset($data['parameters'])) { if(!is_array($data['parameters'])) { throw new RequestException("Invalid 'parameters' key in request: Expected array", 400); } } return new RpcRequest($data['method'], $data['id'] ?? null, $data['parameters'] ?? null); } }