Completed
Push — master ( fd06e1...a51345 )
by Neomerx
08:18
created

CorsMiddleware::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

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