CorsMiddleware   A
last analyzed

Complexity

Total Complexity 12

Size/Duplication

Total Lines 124
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 6

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
wmc 12
lcom 1
cbo 6
dl 0
loc 124
ccs 40
cts 40
cp 1
rs 10
c 0
b 0
f 0

6 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A handle() 0 30 5
A getResponseOnError() 0 7 2
A getCorsAnalysis() 0 7 1
A getRequestAdapter() 0 4 1
A getPrepareCorsHeaders() 0 9 2
1
<?php declare(strict_types = 1);
2
3
namespace Neomerx\CorsIlluminate;
4
5
/**
6
 * Copyright 2015-2020 [email protected]
7
 *
8
 * Licensed under the Apache License, Version 2.0 (the "License");
9
 * you may not use this file except in compliance with the License.
10
 * You may obtain a copy of the License at
11
 *
12
 * http://www.apache.org/licenses/LICENSE-2.0
13
 *
14
 * Unless required by applicable law or agreed to in writing, software
15
 * distributed under the License is distributed on an "AS IS" BASIS,
16
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
 * See the License for the specific language governing permissions and
18
 * limitations under the License.
19
 */
20
21
use Closure;
22
use Illuminate\Contracts\Container\Container as ContainerInterface;
23
use Illuminate\Http\Request;
24
use Illuminate\Http\Response;
25
use Neomerx\Cors\Contracts\AnalysisResultInterface;
26
use Neomerx\Cors\Contracts\AnalyzerInterface;
27
use Neomerx\Cors\Contracts\Constants\CorsResponseHeaders;
28
use Neomerx\CorsIlluminate\Adapters\IlluminateRequestToPsr7;
29
use Psr\Http\Message\RequestInterface;
30
31
/**
32
 * @package Neomerx\CorsIlluminate
33
 */
34
class CorsMiddleware
35
{
36
    /**
37
     * @var AnalyzerInterface
38
     */
39
    private $analyzer;
40
41
    /**
42
     * @var ContainerInterface
43
     */
44
    private $container;
45
46
    /**
47
     * @param AnalyzerInterface  $analyzer
48 4
     * @param ContainerInterface $container
49
     */
50 4
    public function __construct(AnalyzerInterface $analyzer, ContainerInterface $container)
51 4
    {
52 4
        $this->analyzer  = $analyzer;
53
        $this->container = $container;
54
    }
55
56
    /**
57
     * Handle an incoming request.
58
     *
59
     * @param Request $request
60
     * @param Closure $next
61
     *
62 4
     * @return mixed
63
     */
64 4
    public function handle(Request $request, Closure $next)
65
    {
66 4
        $cors = $this->getCorsAnalysis($request);
67 4
68 1
        switch ($cors->getRequestType()) {
69 1
            case AnalysisResultInterface::TYPE_REQUEST_OUT_OF_CORS_SCOPE:
70
                $response = $next($request);
71 3
                break;
72 1
73 1
            case AnalysisResultInterface::TYPE_PRE_FLIGHT_REQUEST:
74 1
                $headers  = $this->getPrepareCorsHeaders($cors->getResponseHeaders());
75
                $response = new Response(null, Response::HTTP_OK, $headers);
76 2
                break;
77
78 1
            case AnalysisResultInterface::TYPE_ACTUAL_REQUEST:
79
                /** @var Response $response */
80 1
                $response = $next($request);
81 1
                // merge CORS headers to response
82 1
                foreach ($this->getPrepareCorsHeaders($cors->getResponseHeaders()) as $name => $value) {
83 1
                    $response->headers->set($name, $value, false);
84
                }
85 1
                break;
86 1
87 1
            default:
88 4
                $response = $this->getResponseOnError($cors);
89
                break;
90 4
        }
91
92
        return $response;
93
    }
94
95
    /**
96
     * You can override this method in order to customize error reply.
97
     *
98
     * @param AnalysisResultInterface $analysisResult
99
     *
100 1
     * @return Response
101
     */
102
    protected function getResponseOnError(AnalysisResultInterface $analysisResult): Response
103 1
    {
104
        // avoid unused warning
105 1
        $analysisResult ?: null;
106
107
        return new Response(null, Response::HTTP_BAD_REQUEST);
108
    }
109
110
    /**
111
     * This method saves analysis result in Illuminate Container for
112
     * using it in other parts of the application (e.g. in exception handler).
113
     *
114
     * @param Request $request
115
     *
116 4
     * @return AnalysisResultInterface
117
     */
118 4
    protected function getCorsAnalysis(Request $request): AnalysisResultInterface
119 4
    {
120
        $analysis = $this->analyzer->analyze($this->getRequestAdapter($request));
121 4
        $this->container->instance(AnalysisResultInterface::class, $analysis);
122
123
        return $analysis;
124
    }
125
126
    /**
127
     * You can override this method to replace IlluminateRequestToPsr7 adapter with another one.
128
     *
129
     * @param Request $request
130
     *
131 4
     * @return RequestInterface
132
     */
133 4
    protected function getRequestAdapter(Request $request): RequestInterface
134
    {
135
        return new IlluminateRequestToPsr7($request);
136
    }
137
138
    /**
139
     * There is an issue with IE which cannot work with multiple 'Access-Control-Expose-Headers' and
140
     * requires it them to be comma separated. Chrome and Firefox seem to be not affected.
141
     *
142
     * @param array $headers
143
     *
144
     * @return array
145
     *
146 2
     * @see https://github.com/neomerx/cors-psr7/issues/31
147
     */
148 2
    protected function getPrepareCorsHeaders(array $headers): array
149 1
    {
150 1
        if (array_key_exists(CorsResponseHeaders::EXPOSE_HEADERS, $headers) === true) {
151 1
            $headers[CorsResponseHeaders::EXPOSE_HEADERS] =
152
                implode(', ', $headers[CorsResponseHeaders::EXPOSE_HEADERS]);
153 2
        }
154
155
        return $headers;
156
    }
157
}
158