dispatcher = $dispatcher; $this->token = $token; $this->path = '/' . trim($path, '/'); } public function handle(): void { if (parse_url($_SERVER['REQUEST_URI'] ?? '/', PHP_URL_PATH) !== $this->path) { $this->sendJson(404, ['error' => 'not found']); return; } if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') { $this->sendJson(405, ['error' => 'method not allowed']); return; } if (!$this->authorized()) { header('WWW-Authenticate: Bearer'); $this->sendJson(401, ['error' => 'unauthorized']); return; } $raw = file_get_contents('php://input'); $decoded = json_decode(is_string($raw) ? $raw : '', true); if (!is_array($decoded)) { $this->sendJson(400, $this->errorResponse(null, -32700, 'Invalid JSON.')); return; } if (array_is_list($decoded)) { $responses = []; foreach ($decoded as $message) { if (!is_array($message)) { $responses[] = $this->errorResponse(null, -32600, 'Invalid request.'); continue; } $response = $this->dispatcher->handleMessage($message); if ($response !== null) { $responses[] = $response; } } if ($responses === []) { http_response_code(202); return; } $this->sendJson(200, $responses); return; } $response = $this->dispatcher->handleMessage($decoded); if ($response === null) { http_response_code(202); return; } $this->sendJson(200, $response); } private function authorized(): bool { $header = $_SERVER['HTTP_AUTHORIZATION'] ?? ''; if (!is_string($header) || !str_starts_with($header, 'Bearer ')) { return false; } return hash_equals($this->token, substr($header, 7)); } /** * @param mixed $payload */ private function sendJson(int $status, $payload): void { http_response_code($status); header('Content-Type: application/json'); echo json_encode($payload, JSON_UNESCAPED_SLASHES); } /** * @return array */ private function errorResponse(mixed $id, int $code, string $message): array { return [ 'jsonrpc' => '2.0', 'id' => $id, 'error' => [ 'code' => $code, 'message' => $message, ], ]; } }