legionth / http-server-react
Requires
- php: >=5.3
- evenement/evenement: ~1.0|~2.0
- react/dns: 0.3.*|0.4.*
- react/event-loop: 0.3.*|0.4.*
- react/promise: ~1.0|~2.0
- react/socket: 0.3.*|0.4.*
- react/socket-client: ^0.5
- react/stream: 0.3.*|0.4.*
- ringcentral/psr7: ^1.2
Requires (Dev)
- clue/block-react: ^1.1
- react/socket-client: ^0.5
- dev-master
- v0.5.0
- v0.4.0
- v0.3.0
- v0.2.0
- v0.1.1
- v0.1.0
- dev-support-status-notice
- dev-add-server-request-class
- dev-fix-readme
- dev-proxy-example
- dev-https-example
- dev-close-connection-on-error-response
- dev-streaming-requests
- dev-fix-send-response
- dev-connection-close-protection
- dev-lenght-limited-stream
- dev-streaming
- dev-remove-header-decoder
- dev-add-gitignore
- dev-streaming-content-length
- dev-streaming-chunked-encoding
- dev-client-streaming
- dev-keep-alive
- dev-return-promise-in-middleware
- dev-save-http-body-streaming
This package is auto-updated.
Last update: 2020-02-06 09:33:04 UTC
README
基于 ReactPHP 的 PHP HTTP 服务器。
目录
通知(2017 年 6 月)
此存储库仅是一个原型,将不再提供支持。该原型的每个功能都已迁移到官方的 ReactPHP HTTP-Server。
使用方法
HttpServer
HTTP 服务器需要一个套接字和回调函数才能工作。套接字将用于服务器和客户端之间的通信。回调函数用于对请求做出反应并返回响应。回调函数 必须 返回一个承诺或响应对象。HttpServer
类使用 PSR-7 Middleware 对象。并且这些对象也需要在 回调函数 中使用。
创建回调函数
HttpServer
使用一个回调函数。这个回调函数只有一个参数,即请求对象,并期望返回一个响应对象。
创建自己的回调函数以根据您的意愿对响应做出反应(例如,检查响应,从数据库中获取值并发送响应)。但请注意,数据库或文件操作等阻塞操作会导致服务器变慢。
$callback = function (ServerRequestInterface $request) { $content = '<html> <body> <h1> Hello World! </h1> <p> This is your own little server. Written in PHP :-) </p> </body> </html>'; return new Response( 200, array( 'Content-Length' => strlen($content), 'Content-Type' => 'text/html' ), $content ); }; $socket = new Socket($loop); $socket->listen(10000, 'localhost'); $server = new HttpServer($socket, $callback); $loop->run();
此示例将在将请求发送到此服务器的每个请求上响应一个简单的 HTML 网站。但是,只要请求的头部到达服务器,就会立即向客户端发送响应。如果请求包含正文数据,则会忽略这些数据,并在向客户端发送响应时关闭 TCP 连接。要处理正文数据,您必须使用流。
从 v0.4.0
版本开始,每个版本都将流请求。这意味着您的回调函数中的请求对象的正文。
流式请求使得从客户端到服务器发送大量数据成为可能,这些数据以小块的形式发送。例如,您可以在应用程序收到正文的具体部分时开始处理请求的计算。
您的回调和中间件函数中的请求对象的正文将是一个 ReadableStreamInterface。
每个请求正文流在成功完成流时将发送一个结束事件。我们必须使用 promise 来确保只有在请求流完成时才向客户端发送响应。
下一个示例将与前一个示例做同样的事情,但会等待请求流完成。
$callback = function (ServerRequestInterface $request) { return new Promise(function ($resolve, $reject) use ($request) { $request->getBody->on('end', function () use (&$contentLength, $resolve) { $content = '<html> <body> <h1> Hello World! </h1> <p> This is your own little server. Written in PHP :-) </p> </body> </html>'; return new Response( 200, array( 'Content-Length' => strlen($content), 'Content-Type' => 'text/html' ), $content ); }); }; }
请求体始终是一个 ReactPHP 流。目前不需要使用 StreamInterface 的 PSR-7 方法,并且在此开发阶段它们没有任何功能,但在未来的开发中可能会使用。
以下示例中,将添加一个监听器到 'data' 事件,仅计算传输的字符串数据长度。在请求体流结束时,将通过 HTTP 响应以文本形式发送传输数据的长度给客户端。
$callback = function (ServerRequestInterface $request) { return new Promise(function ($resolve, $reject) use ($body) { $contentLength = 0; $request->getBody()->on('data', function ($chunk) use ($resolve, &$contentLength) { $contentLength += strlen($chunk); }); $request->getBody()->on('end', function () use (&$contentLength, $resolve) { $content = "Transferred data length: " . $contentLength ."\n"; $resolve( new Response( 200, array( 'Content-Length' => strlen($content), 'Content-Type' => 'text/html' ), $content ) ); }); }); };
这只是一个示例,您可以使用 BufferedSink(来自 reactphp/stream
)来避免这些代码行。
此示例仅流式传输请求体。响应体也可以流式传输。请查看 流式响应 章节。
必须在回调和中间件函数中作为第一个参数使用 ServerRequestInterface。请求参数具有 PSR-7 接口定义的默认值,并且可以通过中间件进行更改。
查看 examples
文件夹以了解服务器可能的样子。
ChunkedDecoder
ChunkedDecoder
用于解码 HTTP 请求中通过 Transfer-Encoding: chunked
发送的单独数据块。HTTP 服务器将编码后的请求体发送到回调函数。
此类基于 ReactPHP 流。HttpServer
将保存数据块,直到请求体完成,然后将解码后的请求转发给回调函数。
异常处理
回调函数中的代码可以抛出异常,但这不应影响服务器运行。因此,每个未捕获的异常都将被 HttpServer
捕获,并在发生异常时向客户端发送 'HTTP 500 内部服务器错误' 响应。
示例
<?php $loop = React\EventLoop\Factory::create(); $callback = function (ServerRequestInterface $request) use ($loop) { throw new Exception(); }; $socket = new Socket($loop); $socket->listen(10000, 'localhost'); $server = new HttpServer($socket, $callback); $loop->run();
此示例将在任何请求上导致 'HTTP 500 内部服务器错误'。
提示:这是未捕获异常时的默认响应。如果您希望用户在浏览器中看到比任何空站点更多的内容,请捕获您的异常并创建自己的带有头部和体的 Response 对象。
<?php $callback = function (ServerRequestInterface $request) { try { //something that could go wrong } catch(Exception $exception) { return new Response(500, array('Content-Length' => 5), 'error'); } } $httpServer = new HttpServer($socket, $callback);
回调函数的返回类型
回调函数的返回类型 必须 是一个 响应对象 或一个 promise。
对于重型计算,您应考虑使用 promises。不使用它们可能会导致服务器变慢。
$callback = function (ServerRequestInterface $request) { return new Promise(function ($resolve, $reject) use ($request) { $request->getBody()->on('end', function () { $response = heavyCalculationFunction(); $resolve($response); }); }); };
查看 examples
文件夹以了解如何在回调函数中使用 promises。
promise 必须 返回一个响应对象,否则将导致客户端的 'HTTP 500 内部服务器错误' 响应。
不允许其他类型,否则将导致客户端的 'HTTP 500 内部服务器错误' 响应。
中间件
创建您自己的中间件
您可以创建自己的中间件。这些中间件位于 HttpServer
和用户回调函数之间。当创建响应对象时,HttpServer
会调用回调函数。添加中间件后,HttpServer
首先会调用这个中间件。下一个链环将是另一个中间件或链尾的回调函数。每个中间件都必须返回一个响应对象。否则,HttpServer
将返回“500 内部服务器错误”消息。中间件不仅可以操作请求对象,还可以操作其他添加的中间件或回调函数返回的响应对象。
这类似于fig 标准的概念。
添加尽可能多的中间件,只需遵循以下设计即可。
$callback = function (ServerRequestInterface $request) { return new Response(); } $middleware = function (ServerRequestInterface $request, callable $next) { // check or maninpulate the request object ... // call of next middleware chain link return $next($request); } $server = new HttpServer($socket, $callback); $server->addMiddleware($middleware);
确保在您的中间件代码中添加 return $next($request)
。否则,将返回最后一个调用的中间件的响应。return $next($request)
将调用下一个中间件或用户回调函数,如果它是这个中间件链的最后一部分。
添加的中间件将按照添加的顺序执行。
... $timeBlockingMiddleware = function (ServerRequestInterface $request, callable $next) { // Will call the next middleware from 00:00 till 16:00 // otherwise an 403 Forbidden response will be sent to the client if (((int)date('Hi') < 1600 && (int)date('Hi') > 0) { return $next($request); } return new Response(403); }; $addHeaderToRequest = function (ServerRequestInterface $request, callable $next) { $request = $request->withAddedHeader('Date', date('Y-m-d')); return $next($request); }; $addHeaderToResponse = function (ServerRequestInterface $request, callable $next) { $response = $next($request); $response = $response->withAddedHeader('Age', '12'); return $response; }; $server = new HttpServer($socket, $callback); $server->addMiddleware($timeBlockingMiddleware); $server->addMiddleware($addHeaderToRequest); $server->addMiddleware($addHeaderToResponse);
在这个示例中,首先调用 $timeBlockingMiddleWare
,其次是 $addHeaderToRequest
,然后是 $addHeaderToResponse
。链的最后一部分是 callback
函数。
这个小示例应展示您如何使用中间件,例如检查或操作请求/响应对象。
查看 examples/middleware
了解如何添加多个中间件。
流式响应
由于计算需要一段时间才能完成,因此可以将数据直接流式传输到客户端,而不需要缓冲整个数据。流式传输使得能够将大量数据分小块发送到客户端。
使用 HttpBodyStream
的一个实例,并将其用作您想返回的 Response
对象的正文。
$callback = function (ServerRequestInterface $request) { $input = new ReadableStream(); $responseBody = new HttpBodyStream($input); // your computation // emit via `$input` $promise = new Promise(function ($resolve, $reject) use ($request, $responseBody) { $request->getBody()->on('end', function () use ($resolve, $responseBody){ $resolve(new Response(200, array(), $responseBody)); }); }); return $promise; }
HttpServer
将使用从 ReadableStream
发射的数据直接将此数据发送到客户端。如果您使用 HttpBodyStream
,整个传输将被 分块编码,将忽略为 Transfer-Encoding
设置的其他值。
查看 examples
文件夹了解您的计算可能看起来像什么。
HTTPS 服务器
可以使用 ReactPHP 的 socket 包中的 SecureServer 将 HTTP 服务器设置为 HTTPS 服务器。
以下示例显示了如何使用它
$socket = new Socket($loop); $secureSocket = new SecureServer( $socket, $loop, array('local_cert' => 'secret.pem') ); $secureSocket->listen(10000, 'localhost'); $secureSocket->on('error', function (Exception $e) { echo 'Error: ' . $e->getMessage() . PHP_EOL; }); $server = new HttpServer($secureSocket, $callback);
查看 examples
文件夹了解您的 HTTPS 服务器可能看起来像什么。
要执行此示例,您必须使用自签名证书。您可以使用 脚本 生成自签名证书
安装
这将安装最新支持的版本。
$ composer require legionth/http-server-react:^0.1
有关版本升级的详细信息,请参阅 CHANGELOG。
许可
MIT