From 98b7048db7ef298f1b5dbd7a871a0bcdfd6eb826 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mr=C2=B7x?= Date: Fri, 27 Aug 2021 16:49:17 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=B9=E5=90=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- http-server/Message/Message.php | 182 ++++++++++++++++++++++ http-server/Message/Request.php | 177 +++++++++++++++++++++ http-server/Message/Response.php | 53 +++++++ http-server/Message/Stream.php | 180 +++++++++++++++++++++ http-server/Message/Uploaded.php | 103 ++++++++++++ http-server/Message/Uri.php | 260 +++++++++++++++++++++++++++++++ 6 files changed, 955 insertions(+) create mode 100644 http-server/Message/Message.php create mode 100644 http-server/Message/Request.php create mode 100644 http-server/Message/Response.php create mode 100644 http-server/Message/Stream.php create mode 100644 http-server/Message/Uploaded.php create mode 100644 http-server/Message/Uri.php diff --git a/http-server/Message/Message.php b/http-server/Message/Message.php new file mode 100644 index 00000000..30d2a3c3 --- /dev/null +++ b/http-server/Message/Message.php @@ -0,0 +1,182 @@ +version; + } + + + /** + * @param $version + * @return $this + */ + public function withProtocolVersion($version): static + { + $class = clone $this; + $class->version = $version; + return $class; + } + + /** + * @return array + */ + public function getHeaders(): array + { + return $this->headers; + } + + /** + * @param $name + * @return bool + */ + public function hasHeader($name): bool + { + return array_key_exists($name, $this->headers); + } + + + /** + * @param $name + * @return string|array|null + */ + #[Pure] public function getHeader($name): string|null|array + { + if (!$this->hasHeader($name)) { + return null; + } + return $this->headers[$name]; + } + + + /** + * @param $name + * @return string|null + */ + #[Pure] public function getHeaderLine($name): string|null + { + if ($this->hasHeader($name)) { + return implode(';', $this->headers[$name]); + } + return null; + } + + + /** + * @param $name + * @param $value + * @return static + */ + public function withHeader($name, $value): static + { + $class = clone $this; + if (!is_array($value)) { + $value = [$value]; + } + $class->headers[$name] = $value; + return $class; + } + + + /** + * @param $name + * @param $value + * @return static + * @throws + */ + public function withAddedHeader($name, $value): static + { + $class = clone $this; + if (!array_key_exists($name, $class->headers)) { + throw new \Exception('Headers `' . $name . '` not exists.'); + } + $class->headers[$name][] = $value; + return $class; + } + + + /** + * @param $name + * @return $this + */ + public function withoutHeader($name): static + { + $class = clone $this; + unset($class->headers[$name]); + return $class; + } + + + /** + * @return string + */ + public function getBody(): string + { + return $this->stream; + } + + + /** + * @param StreamInterface $body + * @return static + */ + public function withBody(StreamInterface $body): static + { + $class = clone $this; + $class->stream = $body; + return $class; + } + + + /** + * @param StreamInterface $stream + * @return mixed + */ + public function parseBody(StreamInterface $stream): mixed + { + $content = $stream->getContents(); + if (empty($content)) { + return $content; + } + $contentType = $this->getHeaderLine('content-type'); + if (str_contains($contentType, 'json')) { + return json_encode($contentType); + } + if (str_contains($contentType, 'xml')) { + return Xml::toArray($contentType); + } + if (str_contains($contentType, 'x-www-form-urlencoded')) { + parse_str($content, $array); + return $array; + } + if (str_contains($contentType, 'serialize')) { + return unserialize($content); + } + return $content; + } + +} diff --git a/http-server/Message/Request.php b/http-server/Message/Request.php new file mode 100644 index 00000000..536a9c88 --- /dev/null +++ b/http-server/Message/Request.php @@ -0,0 +1,177 @@ +serverRequest->fd; + } + + + /** + * @param \Swoole\Http\Request $request + * @return RequestInterface + */ + public static function parseRequest(\Swoole\Http\Request $request): RequestInterface + { + $message = new Request(); + $message->uri = Uri::parseUri($request); + $message->method = $request->getMethod(); + $message->requestTarget = ''; + $message->serverRequest = $request; + $message->version = $request->server['server_protocol']; + $message->stream = new Stream($request->getContent()); + $message->headers = $request->header; + return $message; + } + + + /** + * @param $name + * @param null $default + * @return mixed + */ + public function input($name, $default = null): mixed + { + if (empty($this->parseBody)) { + $this->parseBody = $this->parseBody($this->stream); + } + if (!is_array($this->parseBody)) { + return $default; + } + return $this->parseBody[$name] ?? $default; + } + + + /** + * @param $name + * @param null $default + * @return mixed + */ + public function post($name, $default = null): mixed + { + return $this->serverRequest->post[$name] ?? $default; + } + + + /** + * @param $name + * @param null $default + * @return mixed + */ + public function query($name, $default = null): mixed + { + return $this->serverRequest->get[$name] ?? $default; + } + + + /** + * @param $name + * @return Uploaded|null + */ + public function file($name): ?Uploaded + { + if (isset($this->serverRequest->files[$name])) { + return new Uploaded($this->serverRequest->files[$name]); + } + return null; + } + + + /** + * @return string + */ + public function getRequestTarget(): string + { + throw new BadMethodCallException('Not Accomplish Method.'); + } + + + /** + * @param mixed $requestTarget + * @return RequestInterface + */ + public function withRequestTarget($requestTarget): RequestInterface + { + $class = clone $this; + $class->requestTarget = $requestTarget; + return $class; + } + + + /** + * @return string + */ + public function getMethod(): string + { + return $this->method; + } + + + /** + * @param string $method + * @return RequestInterface + */ + public function withMethod($method): RequestInterface + { + $class = clone $this; + $class->method = $method; + return $class; + } + + + /** + * @return UriInterface + */ + public function getUri(): UriInterface + { + return $this->uri; + } + + + /** + * @param UriInterface $uri + * @param false $preserveHost + * @return RequestInterface + */ + public function withUri(UriInterface $uri, $preserveHost = false): RequestInterface + { + $class = clone $this; + $class->uri = $uri; + return $class; + } +} diff --git a/http-server/Message/Response.php b/http-server/Message/Response.php new file mode 100644 index 00000000..6546e59c --- /dev/null +++ b/http-server/Message/Response.php @@ -0,0 +1,53 @@ +statusCode; + } + + /** + * @param int $code + * @param string $reasonPhrase + * @return ResponseInterface + */ + public function withStatus($code, $reasonPhrase = ''): ResponseInterface + { + // TODO: Implement withStatus() method. + $class = clone $this; + $class->statusCode = $code; + $class->reasonPhrase = $reasonPhrase; + return $class; + } + + + /** + * @return string + */ + public function getReasonPhrase(): string + { + // TODO: Implement getReasonPhrase() method. + return $this->reasonPhrase; + } +} diff --git a/http-server/Message/Stream.php b/http-server/Message/Stream.php new file mode 100644 index 00000000..175a9d08 --- /dev/null +++ b/http-server/Message/Stream.php @@ -0,0 +1,180 @@ +body = $body; + } + + + /** + * @return string + */ + public function __toString(): string + { + return $this->body; + } + + + /** + * + */ + public function close(): void + { + $this->detach(); + } + + + /** + * + */ + public function detach(): void + { + $this->body = ''; + $this->size = 0; + $this->writable = false; + } + + + /** + * @return int + */ + public function getSize(): int + { + if ($this->size == 0) { + $this->size = strlen($this->body); + } + return $this->size; + } + + + /** + * @return int + */ + public function tell(): int + { + return $this->offset; + } + + + /** + * @return bool + */ + public function eof(): bool + { + return true; + } + + /** + * @return bool + */ + public function isSeekable(): bool + { + return $this->offset == 0; + } + + + /** + * @param int $offset + * @param int $whence + */ + public function seek($offset, $whence = SEEK_SET) + { + $this->offset = $offset; + } + + + /** + * + */ + public function rewind(): void + { + $this->offset = 0; + } + + /** + * @return bool + */ + public function isWritable(): bool + { + return $this->writable; + } + + + /** + * @param string $string + * @return int + */ + public function write($string): int + { + // TODO: Implement write() method. + $this->body .= $string; + $this->size = strlen($this->body); + return $this->size; + } + + + /** + * @return bool + */ + public function isReadable(): bool + { + return true; + } + + + /** + * @param int $length + * @return string + */ + public function read($length): string + { + // TODO: Implement read() method. + return substr($this->body, 0, $length); + } + + + /** + * @return string + */ + public function getContents(): string + { + return $this->body; + } + + + /** + * @param null $key + * @return mixed + */ + public function getMetadata($key = null): mixed + { + throw new \BadMethodCallException('Not Accomplish Method.'); + } +} diff --git a/http-server/Message/Uploaded.php b/http-server/Message/Uploaded.php new file mode 100644 index 00000000..7960fd5e --- /dev/null +++ b/http-server/Message/Uploaded.php @@ -0,0 +1,103 @@ + "There is no error, the file uploaded with success", + 1 => "The uploaded file exceeds the upload_max_filesize directive in php.ini", + 2 => "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form", + 3 => "The uploaded file was only partially uploaded", + 4 => "No file was uploaded", + 6 => "Missing a temporary folder" + ]; + + + /** + * @param mixed $file + */ + public function __construct(array $file) + { + $this->tmp_name = $file['tmp_name']; + $this->name = $file['name']; + $this->type = $file['type']; + $this->size = $file['size']; + $this->error = $file['error']; + } + + + /** + * @return StreamInterface + */ + public function getStream(): StreamInterface + { + return new Stream(file_get_contents($this->tmp_name)); + } + + + /** + * @param string $targetPath + */ + public function moveTo($targetPath) + { + @move_uploaded_file($this->tmp_name, $targetPath); + } + + /** + * @return int + */ + public function getSize(): int + { + return $this->size; + } + + /** + * @return string + */ + public function getError(): string + { + return Uploaded::ERROR[$this->error] ?? 'unknown error'; + } + + + /** + * @return string|null + */ + public function getClientFilename(): string|null + { + return $this->name; + } + + + /** + * @return string|null + */ + public function getClientMediaType(): string|null + { + return $this->type; + } +} diff --git a/http-server/Message/Uri.php b/http-server/Message/Uri.php new file mode 100644 index 00000000..3559622c --- /dev/null +++ b/http-server/Message/Uri.php @@ -0,0 +1,260 @@ +scheme; + } + + public function getAuthority() + { + // TODO: Implement getAuthority() method. + } + + public function getUserInfo() + { + // TODO: Implement getUserInfo() method. + } + + + /** + * @return string + */ + public function getHost(): string + { + return $this->host; + } + + + /** + * @return int + */ + public function getPort(): int + { + return $this->port; + } + + + /** + * @return string + */ + public function getPath(): string + { + return $this->path; + } + + /** + * @return string + */ + public function getQuery(): string + { + return $this->query; + } + + + /** + * @return string + */ + public function getFragment(): string + { + return $this->fragment; + } + + + /** + * @param string $scheme + * @return UriInterface + */ + public function withScheme($scheme): UriInterface + { + $class = clone $this; + $class->scheme = $scheme; + return $class; + } + + /** + * @param string $user + * @param null $password + * @return $this + */ + public function withUserInfo($user, $password = null): UriInterface + { + $class = clone $this; + $class->username = $user; + $class->password = $password; + return $class; + } + + + /** + * @param string $host + * @return UriInterface + */ + public function withHost($host): UriInterface + { + $class = clone $this; + $class->host = $host; + return $class; + } + + + /** + * @return int + */ + public function getDefaultPort(): int + { + return 80; + } + + + /** + * @param int|null $port + * @return UriInterface + */ + public function withPort($port): UriInterface + { + $class = clone $this; + $class->port = $port; + return $class; + } + + + /** + * @param string $path + * @return UriInterface + */ + public function withPath($path): UriInterface + { + $class = clone $this; + $class->path = $path; + return $class; + } + + + /** + * @param string $query + * @return UriInterface + */ + public function withQuery($query): UriInterface + { + $class = clone $this; + $class->query = $query; + return $class; + } + + + /** + * @param string $fragment + * @return UriInterface + */ + public function withFragment($fragment): UriInterface + { + $class = clone $this; + $class->fragment = $fragment; + return $class; + } + + + /** + * @return string + */ + public function __toString(): string + { + return sprintf('%s://%s:%d%s?%s#%s', $this->scheme, $this->host, $this->port, + $this->path, $this->query, $this->fragment); + } + + + /** + * @param \Swoole\Http\Request $request + * @return UriInterface + */ + #[Pure] public static function parseUri(\Swoole\Http\Request $request): UriInterface + { + $server = $request->server; + $header = $request->header; + $uri = new Uri(); + $uri = $uri->withScheme(!empty($server['https']) && $server['https'] !== 'off' ? 'https' : 'http'); + + $hasPort = false; + if (isset($server['http_host'])) { + $hostHeaderParts = explode(':', $server['http_host']); + $uri = $uri->withHost($hostHeaderParts[0]); + if (isset($hostHeaderParts[1])) { + $hasPort = true; + $uri = $uri->withPort($hostHeaderParts[1]); + } + } elseif (isset($server['server_name'])) { + $uri = $uri->withHost($server['server_name']); + } elseif (isset($server['server_addr'])) { + $uri = $uri->withHost($server['server_addr']); + } elseif (isset($header['host'])) { + $hasPort = true; + if (strpos($header['host'], ':')) { + [$host, $port] = explode(':', $header['host'], 2); + if ($port != $uri->getDefaultPort()) { + $uri = $uri->withPort($port); + } + } else { + $host = $header['host']; + } + + $uri = $uri->withHost($host); + } + + if (!$hasPort && isset($server['server_port'])) { + $uri = $uri->withPort($server['server_port']); + } + + $hasQuery = false; + if (isset($server['request_uri'])) { + $requestUriParts = explode('?', $server['request_uri']); + $uri = $uri->withPath($requestUriParts[0]); + if (isset($requestUriParts[1])) { + $hasQuery = true; + $uri = $uri->withQuery($requestUriParts[1]); + } + } + + if (!$hasQuery && isset($server['query_string'])) { + $uri = $uri->withQuery($server['query_string']); + } + + return $uri; + } +}