为webman增加长轮询响应的支持

chaz6chez
  1. 增加一个LongPollingResponse类,继承support\Response

namespace support;

use Respect\Validation\Rules\Time;
use Workerman\Timer;

/**
 * Class LongPollingResponse
 * @package support
 */
class LongPollingResponse extends Response
{
    /** @var float 等待时长 */
    protected float $_wait;
    /** @var Request 长轮询请求对象 */
    protected Request $_request;
    /** @var int 长轮询定时器计数 */
    protected static int $_count = 0;

    /**
     * @param Request $request
     * @param $status
     * @param $headers
     * @param $body
     * @param float $wait 等待时长
     */
    public function __construct(Request $request, $status = 200, $headers = [], $body = '', float $wait = 0)
    {
        $this->_wait = $wait;
        $this->_request = $request;
        parent::__construct($status, $headers, $body);

        if ($this->_wait > 0) {
            Timer::add($wait, function () {
                self::$_count ++;
                \Webman\App::send($this->_request->connection, $this, $this->_request);
                self::$_count --;
            }, [], false);
        }
    }

    /**
     * @return int
     */
    public static function getLongPollingCount(): int
    {
        return self::$_count;
    }
}
  1. 修改Webman\App::onMessage
    /**
     * OnMessage.
     * @param TcpConnection|mixed $connection
     * @param Request|mixed $request
     * @return null
     */
    public function onMessage($connection, $request)
    {
        try {
            Context::set(Request::class, $request);
            $path = $request->path();
            $key = $request->method() . $path;
            if (isset(static::$callbacks[$key])) {
                [$callback, $request->plugin, $request->app, $request->controller, $request->action, $request->route] = static::$callbacks[$key];
                $response = $callback($request);
                if ($response instanceof LongPollingResponse) {
                    return;
                }
                static::send($connection, $response, $request);
                return null;
            }
            $routeRes = static::findRoute($connection, $path, $key, $request);
            if (
                static::unsafeUri($connection, $path, $request) ||
                static::findFile($connection, $path, $key, $request) ||
                $routeRes !== false
            ) {
                if ($routeRes === null) {
                    return;
                }
                return null;
            }

            $controllerAndAction = static::parseControllerAction($path);
            $plugin = $controllerAndAction['plugin'] ?? static::getPluginByPath($path);
            if (!$controllerAndAction || Route::hasDisableDefaultRoute($plugin)) {
                $request->plugin = $plugin;
                $callback = static::getFallback($plugin);
                $request->app = $request->controller = $request->action = '';
                $response = $callback($request);
                if ($response instanceof LongPollingResponse) {
                    return;
                }
                static::send($connection, $response, $request);
                return null;
            }
            $app = $controllerAndAction['app'];
            $controller = $controllerAndAction['controller'];
            $action = $controllerAndAction['action'];
            $callback = static::getCallback($plugin, $app, [$controller, $action]);
            static::collectCallbacks($key, [$callback, $plugin, $app, $controller, $action, null]);
            [$callback, $request->plugin, $request->app, $request->controller, $request->action, $request->route] = static::$callbacks[$key];
            $response = $callback($request);
            if ($response instanceof LongPollingResponse) {
                return;
            }
            static::send($connection, $response, $request);
        } catch (Throwable $e) {
            $response =static::exceptionResponse($e, $request);
            if ($response instanceof LongPollingResponse) {
                return;
            }
            static::send($connection, static::exceptionResponse($e, $request), $request);
        }
        return null;
    }
  1. 修改Webman\App::findRoute
    /**
     * Find Route.
     * @param TcpConnection $connection
     * @param string $path
     * @param string $key
     * @param Request|mixed $request
     * @return bool|null
     * @throws ContainerExceptionInterface
     * @throws NotFoundExceptionInterface
     * @throws ReflectionException
     */
    protected static function findRoute(TcpConnection $connection, string $path, string $key, $request): ?bool
    {
        $routeInfo = Route::dispatch($request->method(), $path);
        if ($routeInfo[0] === Dispatcher::FOUND) {
            $routeInfo[0] = 'route';
            $callback = $routeInfo[1]['callback'];
            $route = clone $routeInfo[1]['route'];
            $app = $controller = $action = '';
            $args = !empty($routeInfo[2]) ? $routeInfo[2] : null;
            if ($args) {
                $route->setParams($args);
            }
            if (is_array($callback)) {
                $controller = $callback[0];
                $plugin = static::getPluginByClass($controller);
                $app = static::getAppByController($controller);
                $action = static::getRealMethod($controller, $callback[1]) ?? '';
            } else {
                $plugin = static::getPluginByPath($path);
            }
            $callback = static::getCallback($plugin, $app, $callback, $args, true, $route);
            static::collectCallbacks($key, [$callback, $plugin, $app, $controller ?: '', $action, $route]);
            [$callback, $request->plugin, $request->app, $request->controller, $request->action, $request->route] = static::$callbacks[$key];
            $response = $callback($request);
            if ($response instanceof LongPollingResponse) {
                return null;
            }
            static::send($connection, $response, $request);
            return true;
        }
        return false;
    }
  1. 将Webman\App::send设置为public方法
  2. 在想要长轮询响应的控制器中返回LongPollingResponse
    public function test(Request $request): Response
    {
        return (new LongPollingResponse($request,200, [], '{"info": "this is long polling response. "}',  20));
    }

PS:
1. 此方法需要修改框架库webman-framework
2. 目前是一个思路demo,还有比较多的功能性需要完善
3. PR地址:https://github.com/walkor/webman-framework/pull/88

希望各位能够广提意见,包括一些可能存在的使用场景!

1165 0 0
0个评论

chaz6chez

4634
积分
0
获赞数
0
粉丝数
2018-11-16 加入
×
🔝