Passed
Pull Request — master (#1104)
by Maxim
12:14
created

Http::handle()   A

Complexity

Conditions 5
Paths 2

Size

Total Lines 49
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 34
CRAP Score 5.0005

Importance

Changes 0
Metric Value
eloc 29
dl 0
loc 49
ccs 34
cts 35
cp 0.9714
rs 9.1448
c 0
b 0
f 0
cc 5
nc 2
nop 1
crap 5.0005
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Spiral\Http;
6
7
use Psr\Container\ContainerInterface;
8
use Psr\EventDispatcher\EventDispatcherInterface;
9
use Psr\Http\Message\ResponseFactoryInterface;
10
use Psr\Http\Message\ResponseInterface;
11
use Psr\Http\Message\ServerRequestInterface;
12
use Psr\Http\Server\RequestHandlerInterface;
13
use Spiral\Core\Container;
14
use Spiral\Core\ScopeInterface;
15
use Spiral\Http\Config\HttpConfig;
16
use Spiral\Http\Event\RequestHandled;
17
use Spiral\Http\Event\RequestReceived;
18
use Spiral\Http\Exception\HttpException;
19
use Spiral\Telemetry\NullTracerFactory;
20
use Spiral\Telemetry\SpanInterface;
21
use Spiral\Telemetry\TraceKind;
22
use Spiral\Telemetry\TracerFactoryInterface;
23
24
final class Http implements RequestHandlerInterface
25
{
26
    private ?RequestHandlerInterface $handler = null;
27
    private readonly TracerFactoryInterface $tracerFactory;
28
29 106
    public function __construct(
30
        private readonly HttpConfig $config,
31
        private readonly Pipeline $pipeline,
32
        private readonly ResponseFactoryInterface $responseFactory,
33
        private readonly ContainerInterface $container,
34
        ?TracerFactoryInterface $tracerFactory = null,
35
        private readonly ?EventDispatcherInterface $dispatcher = null,
36
    ) {
37 106
        foreach ($this->config->getMiddleware() as $middleware) {
38 59
            $this->pipeline->pushMiddleware($this->container->get($middleware));
39
        }
40
41 106
        $scope = $this->container instanceof ScopeInterface ? $this->container : new Container();
42 106
        $this->tracerFactory = $tracerFactory ?? new NullTracerFactory($scope);
0 ignored issues
show
Bug introduced by
It seems like $scope can also be of type Psr\Container\ContainerInterface; however, parameter $scope of Spiral\Telemetry\NullTracerFactory::__construct() does only seem to accept Spiral\Core\ScopeInterface|null, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

42
        $this->tracerFactory = $tracerFactory ?? new NullTracerFactory(/** @scrutinizer ignore-type */ $scope);
Loading history...
Bug introduced by
The property tracerFactory is declared read-only in Spiral\Http\Http.
Loading history...
43
    }
44
45 28
    public function getPipeline(): Pipeline
46
    {
47 28
        return $this->pipeline;
48
    }
49
50 104
    public function setHandler(callable|RequestHandlerInterface $handler): self
51
    {
52 104
        $this->handler = $handler instanceof RequestHandlerInterface
53 49
            ? $handler
54 72
            : new CallableHandler($handler, $this->responseFactory);
55
56 104
        return $this;
57
    }
58
59
    /**
60
     * @throws HttpException
61
     */
62 104
    public function handle(ServerRequestInterface $request): ResponseInterface
63
    {
64 104
        $callback = function (SpanInterface $span, CurrentRequest $currentRequest) use ($request): ResponseInterface {
65 104
            $currentRequest->set($request);
66
67 104
            $this->dispatcher?->dispatch(new RequestReceived($request));
68
69 104
            if ($this->handler === null) {
70 1
                throw new HttpException('Unable to run HttpCore, no handler is set.');
71
            }
72
73 103
            $response = $this->pipeline->withHandler($this->handler)->handle($request);
74
75 96
            $span
76 96
                ->setAttribute(
77 96
                    'http.status_code',
78 96
                    $response->getStatusCode()
79 96
                )
80 96
                ->setAttribute(
81 96
                    'http.response_content_length',
82 96
                    $response->getHeaderLine('Content-Length') ?: $response->getBody()->getSize()
83 96
                )
84 96
                ->setStatus($response->getStatusCode() < 500 ? 'OK' : 'ERROR');
85
86 96
            $this->dispatcher?->dispatch(new RequestHandled($request, $response));
87
88 96
            return $response;
89 104
        };
90
91 104
        $tracer = $this->tracerFactory->make($request->getHeaders());
92
93
        /** @var ResponseInterface $response */
94 104
        $response = $tracer->trace(
95 104
            name: \sprintf('%s %s', $request->getMethod(), (string)$request->getUri()),
96 104
            callback: $callback,
97 104
            attributes: [
98 104
                'http.method' => $request->getMethod(),
99 104
                'http.url' => $request->getUri(),
100 104
                'http.headers' => $request->getHeaders(),
101 104
            ],
102 104
            scoped: true,
103 104
            traceKind: TraceKind::SERVER
104 104
        );
105
106 96
        foreach ($tracer->getContext() as $key => $value) {
107
            $response = $response->withHeader($key, $value);
108
        }
109
110 96
        return $response;
111
    }
112
}
113