Passed
Push — master ( 574e0c...813e35 )
by Dominik
01:02 queued 11s
created

CorsMiddleware::addAllowMethod()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 7
ccs 4
cts 4
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Chubbyphp\Cors;
6
7
use Chubbyphp\Cors\Negotiation\HeadersNegotiatorInterface;
8
use Chubbyphp\Cors\Negotiation\MethodNegotiatorInterface;
9
use Chubbyphp\Cors\Negotiation\Origin\OriginNegotiatorInterface;
10
use Psr\Http\Message\ResponseFactoryInterface;
11
use Psr\Http\Message\ResponseInterface;
12
use Psr\Http\Message\ServerRequestInterface;
13
use Psr\Http\Server\MiddlewareInterface;
14
use Psr\Http\Server\RequestHandlerInterface;
15
16
final class CorsMiddleware implements MiddlewareInterface
17
{
18
    /**
19
     * @var ResponseFactoryInterface
20
     */
21
    private $responseFactory;
22
23
    /**
24
     * @var OriginNegotiatorInterface
25
     */
26
    private $originNegotiator;
27
28
    /**
29
     * @var MethodNegotiatorInterface
30
     */
31
    private $methodNegotiator;
32
33
    /**
34
     * @var HeadersNegotiatorInterface
35
     */
36
    private $headersNegotiator;
37
38
    /**
39
     * @var array
40
     */
41
    private $exposeHeaders;
42
43
    /**
44
     * @var bool
45
     */
46
    private $allowCredentials;
47
48
    /**
49
     * @var int
50
     */
51
    private $maxAge;
52
53
    /**
54
     * @param ResponseFactoryInterface   $responseFactory
55
     * @param OriginNegotiatorInterface  $originNegotiator
56
     * @param MethodNegotiatorInterface  $methodNegotiator
57
     * @param HeadersNegotiatorInterface $headersNegotiator
58
     * @param string[]                   $exposeHeaders
59
     * @param bool                       $allowCredentials
60
     * @param int                        $maxAge
61
     */
62 8
    public function __construct(
63
        ResponseFactoryInterface $responseFactory,
64
        OriginNegotiatorInterface $originNegotiator,
65
        MethodNegotiatorInterface $methodNegotiator,
66
        HeadersNegotiatorInterface $headersNegotiator,
67
        array $exposeHeaders = [],
68
        bool $allowCredentials = false,
69
        int $maxAge = 600
70
    ) {
71 8
        $this->responseFactory = $responseFactory;
72 8
        $this->originNegotiator = $originNegotiator;
73 8
        $this->methodNegotiator = $methodNegotiator;
74 8
        $this->headersNegotiator = $headersNegotiator;
75 8
        $this->exposeHeaders = $exposeHeaders;
76 8
        $this->allowCredentials = $allowCredentials;
77 8
        $this->maxAge = $maxAge;
78 8
    }
79
80
    /**
81
     * @param ServerRequestInterface  $request
82
     * @param RequestHandlerInterface $handler
83
     *
84
     * @return ResponseInterface
85
     */
86 8
    public function process(ServerRequestInterface $request, RequestHandlerInterface $handler): ResponseInterface
87
    {
88 8
        $allowOrigin = $this->originNegotiator->negotiate($request);
89
90 8
        if ($this->isPreflight($request)) {
91 5
            return $this->handlePreflight($request, $allowOrigin);
92
        }
93
94 3
        return $this->handle($request, $handler, $allowOrigin);
95
    }
96
97
    /**
98
     * @param ServerRequestInterface $request
99
     *
100
     * @return bool
101
     */
102 8
    private function isPreflight(ServerRequestInterface $request): bool
103
    {
104 8
        return 'OPTIONS' === strtoupper($request->getMethod());
105
    }
106
107
    /**
108
     * @param ServerRequestInterface $request
109
     * @param string|null            $allowOrigin
110
     *
111
     * @return ResponseInterface
112
     */
113 5
    private function handlePreflight(ServerRequestInterface $request, ?string $allowOrigin): ResponseInterface
114
    {
115 5
        $response = $this->responseFactory->createResponse(204);
116
117 5
        if (null === $allowOrigin) {
118 1
            return $response;
119
        }
120
121 4
        $response = $this->addAllowOrigin($response, $allowOrigin);
122 4
        $response = $this->addAllowCredentials($response);
123 4
        $response = $this->addExposeHeaders($response);
124
125 4
        if (!$this->methodNegotiator->negotiate($request)) {
126 1
            return $response;
127
        }
128
129 3
        $response = $this->addAllowMethod($response);
130
131 3
        if (!$this->headersNegotiator->negotiate($request)) {
132 1
            return $response;
133
        }
134
135 2
        $response = $this->addAllowHeaders($response);
136
137 2
        return $this->addMaxAge($response);
138
    }
139
140
    /**
141
     * @param ServerRequestInterface  $request
142
     * @param RequestHandlerInterface $handler
143
     * @param string|null             $allowOrigin
144
     *
145
     * @return ResponseInterface
146
     */
147 3
    private function handle(
148
        ServerRequestInterface $request,
149
        RequestHandlerInterface $handler,
150
        ?string $allowOrigin
151
    ): ResponseInterface {
152 3
        $response = $handler->handle($request);
153
154 3
        if (null === $allowOrigin) {
155 1
            return $response;
156
        }
157
158 2
        $response = $this->addAllowOrigin($response, $allowOrigin);
159 2
        $response = $this->addAllowCredentials($response);
160 2
        $response = $this->addExposeHeaders($response);
161
162 2
        return $response;
163
    }
164
165
    /**
166
     * @param ResponseInterface $response
167
     * @param string            $allowOrigin
168
     *
169
     * @return ResponseInterface
170
     */
171 6
    private function addAllowOrigin(ResponseInterface $response, string $allowOrigin): ResponseInterface
172
    {
173 6
        return $response->withHeader('Access-Control-Allow-Origin', $allowOrigin);
174
    }
175
176
    /**
177
     * @param ResponseInterface $response
178
     *
179
     * @return ResponseInterface
180
     */
181 6
    private function addAllowCredentials(ResponseInterface $response): ResponseInterface
182
    {
183 6
        return $response->withHeader('Access-Control-Allow-Credentials', $this->allowCredentials ? 'true' : 'false');
184
    }
185
186
    /**
187
     * @param ResponseInterface $response
188
     *
189
     * @return ResponseInterface
190
     */
191 6
    private function addExposeHeaders(ResponseInterface $response): ResponseInterface
192
    {
193 6
        if ([] === $this->exposeHeaders) {
194 4
            return $response;
195
        }
196
197 2
        return $response->withHeader('Access-Control-Expose-Headers', implode(', ', $this->exposeHeaders));
198
    }
199
200
    /**
201
     * @param ResponseInterface $response
202
     *
203
     * @return ResponseInterface
204
     */
205 3
    private function addAllowMethod(ResponseInterface $response): ResponseInterface
206
    {
207 3
        return $response->withHeader(
208 3
            'Access-Control-Allow-Methods',
209 3
            implode(', ', $this->methodNegotiator->getAllowedMethods())
210
        );
211
    }
212
213
    /**
214
     * @param ResponseInterface $response
215
     *
216
     * @return ResponseInterface
217
     */
218 2
    private function addAllowHeaders(ResponseInterface $response): ResponseInterface
219
    {
220 2
        return $response->withHeader(
221 2
            'Access-Control-Allow-Headers',
222 2
            implode(', ', $this->headersNegotiator->getAllowedHeaders())
223
        );
224
    }
225
226
    /**
227
     * @param ResponseInterface $response
228
     *
229
     * @return ResponseInterface
230
     */
231 2
    private function addMaxAge(ResponseInterface $response): ResponseInterface
232
    {
233 2
        return $response->withHeader('Access-Control-Max-Age', (string) $this->maxAge);
234
    }
235
}
236