Issues (35)

src/Handlers/ServerWrapper.php (1 issue)

Severity
1
<?php
2
/**
3
 * HTTP server wrapper
4
 * User: moyo
5
 * Date: 29/09/2017
6
 * Time: 3:42 PM
7
 */
8
9
namespace Carno\HRPC\Handlers;
10
11
use Carno\Chain\Layered;
12
use Carno\Coroutine\Context;
13
use Carno\HRPC\Client\Chips\ErrorsHelper;
14
use Carno\HRPC\Client\Contracts\Defined;
15
use Carno\HRPC\Exception\ServerUnavailableException;
16
use Carno\HTTP\Server\Connection;
17
use Carno\HTTP\Standard\Response as HResponse;
18
use Carno\RPC\Errors\GenericError;
19
use Carno\RPC\Exception\IllegalRequestSyntaxException;
20
use Carno\RPC\Exception\RemoteLogicException;
21
use Carno\RPC\Exception\RemoteSystemException;
22
use Carno\RPC\Exception\RequestTargetNotFoundException;
23
use Carno\RPC\Protocol\Request;
24
use Carno\RPC\Protocol\Response;
25
use Throwable;
26
27
class ServerWrapper implements Layered
28
{
29
    use ErrorsHelper;
30
31
    /**
32
     * operated connection
33
     */
34
    public const CONNECTION = 'conn-session';
35
36
    /**
37
     * request/response for connection
38
     */
39
    public const REQUESTING = 'conn-request';
40
    public const RESPONDING = 'conn-response';
41
42
    /**
43
     * @param Connection $ingress
44
     * @param Context $ctx
45
     * @return Request
46
     */
47
    public function inbound($ingress, Context $ctx) : Request
48
    {
49
        $ctx->set(self::CONNECTION, $ingress);
50
        $ctx->set(self::REQUESTING, $sr = $ingress->request());
51
52
        switch ($sr->getMethod()) {
53
            case 'POST':
54
                $content = $sr->getHeaderLine('Content-Type');
55
                $payload = (string) $sr->getBody();
56
                break;
57
            default:
58
                throw new IllegalRequestSyntaxException("METHOD:{$sr->getMethod()}");
59
        }
60
61
        switch (substr($path = $sr->getUri()->getPath(), 1, ($crp = strpos($path, '/', 1)) - 1)) {
62
            case 'invoke':
63
                $uri = substr($path, $crp + 1);
64
                goto COMMAND_IVK;
65
                break;
66
            default:
67
                throw new IllegalRequestSyntaxException("PATH:{$path}");
68
        }
69
70
        COMMAND_IVK:
71
72
        list($service, $method) = explode('/', $uri);
73
74
        return
75
            (new Request($ingress->serviced() ?: $sr->getHeaderLine('Host'), ucfirst($service), lcfirst($method)))
76
                ->setJsonc($content === Defined::V_TYPE_JSON)
77
                ->setPayload($payload)
78
        ;
79
    }
80
81
    /**
82
     * @param Response $response
83
     * @param Context $ctx
84
     * @return HResponse
85
     */
86
    public function outbound($response, Context $ctx) : HResponse
87
    {
88
        if ($response instanceof Response) {
0 ignored issues
show
$response is always a sub-type of Carno\RPC\Protocol\Response.
Loading history...
89
            $http = new HResponse(
90
                200,
91
                [
92
                    'Content-Type' => $response->isJsonc() ? Defined::V_TYPE_JSON : Defined::V_TYPE_PROTO,
93
                ],
94
                $response->getPayload()
95
            );
96
        } else {
97
            $http = new HResponse(500, $this->errorHeaders('Invalid service response'));
98
        }
99
100
        $ctx->set(self::RESPONDING, $http);
101
102
        return $http;
103
    }
104
105
    /**
106
     * @param Throwable $e
107
     * @param Context $ctx
108
     * @throws Throwable
109
     */
110
    public function exception(Throwable $e, Context $ctx) : void
111
    {
112
        /**
113
         * @var Connection $ingress
114
         */
115
        $ingress = $ctx->get(self::CONNECTION);
116
117
        if ($e instanceof IllegalRequestSyntaxException) {
118
            $r = new HResponse(400);
119
        } elseif ($e instanceof RequestTargetNotFoundException) {
120
            $r = new HResponse(404);
121
        } elseif ($e instanceof GenericError) {
122
            $r = new HResponse(
123
                $e instanceof RemoteLogicException ? 500 : 200,
124
                $this->errorHeaders($e->getMessage(), $e->getCode())
125
            );
126
        } elseif ($e instanceof ServerUnavailableException) {
127
            $r = new HResponse(503);
128
        } else {
129
            $r = new HResponse(
130
                500,
131
                $e instanceof RemoteSystemException
132
                    ? $this->errorHeaders($e->getMessage(), $e->getCode())
133
                    : $this->errorHeaders(
134
                        sprintf('%s::%s::%s', $ingress->serviced(), get_class($e), $e->getMessage()),
135
                        $e->getCode()
136
                    )
137
            );
138
        }
139
140
        $ctx->set(self::RESPONDING, $r);
141
142
        throw $e;
143
    }
144
}
145