|
2 | 2 |
|
3 | 3 | namespace CacheWerk\BrefLaravelBridge\Http; |
4 | 4 |
|
5 | | -use RuntimeException; |
6 | | - |
7 | 5 | use Bref\Context\Context; |
8 | 6 | use Bref\Event\Http\HttpRequestEvent; |
9 | | - |
10 | | -use Riverline\MultiPartParser\StreamedPart; |
| 7 | +use Bref\Event\Http\Psr7Bridge; |
| 8 | +use Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory; |
11 | 9 | use Symfony\Component\HttpFoundation\Request; |
12 | | -use Symfony\Component\HttpFoundation\File\UploadedFile; |
13 | 10 |
|
14 | 11 | class SymfonyRequestBridge |
15 | 12 | { |
16 | 13 | /** |
17 | | - * Convert Bref HTTP request to Symfony request. |
| 14 | + * Convert Bref HTTP event to Symfony request. |
18 | 15 | * |
19 | 16 | * @param \Bref\Event\Http\HttpRequestEvent $event |
20 | 17 | * @param \Bref\Context\Context $context |
21 | 18 | * @return \Symfony\Component\HttpFoundation\Request |
22 | 19 | */ |
23 | 20 | public static function convertRequest(HttpRequestEvent $event, Context $context): Request |
24 | 21 | { |
25 | | - $headers = $event->getHeaders(); |
26 | | - |
27 | | - [$user, $password] = self::parseBasicAuthorization($headers); |
| 22 | + $psr7Request = Psr7Bridge::convertRequest($event, $context); |
| 23 | + $httpFoundationFactory = new HttpFoundationFactory(); |
| 24 | + $symfonyRequest = $httpFoundationFactory->createRequest($psr7Request); |
28 | 25 |
|
29 | | - $server = array_filter([ |
30 | | - 'AUTH_TYPE' => $headers['auth-type'] ?? null, |
31 | | - 'CONTENT_LENGTH' => $headers['content-length'][0] ?? null, |
32 | | - 'CONTENT_TYPE' => $event->getContentType(), |
33 | | - 'DOCUMENT_ROOT' => getcwd(), |
34 | | - 'GATEWAY_INTERFACE' => 'FastCGI/1.0', |
35 | | - 'QUERY_STRING' => $event->getQueryString(), |
36 | | - 'REQUEST_METHOD' => $event->getMethod(), |
37 | | - 'SCRIPT_FILENAME' => $_SERVER['_HANDLER'] ?? $_SERVER['SCRIPT_FILENAME'], |
38 | | - 'SERVER_ADDR' => '127.0.0.1', |
39 | | - 'SERVER_NAME' => $event->getServerName(), |
40 | | - 'SERVER_PORT' => $event->getServerPort(), |
41 | | - 'SERVER_PROTOCOL' => $event->getProtocol(), |
42 | | - 'PATH_INFO' => $event->getPath(), |
43 | | - 'REMOTE_PORT' => $event->getRemotePort(), |
44 | | - 'REQUEST_TIME' => time(), |
45 | | - 'REQUEST_TIME_FLOAT' => microtime(true), |
46 | | - 'REQUEST_URI' => $event->getUri(), |
47 | | - 'REMOTE_ADDR' => '127.0.0.1', |
| 26 | + $symfonyRequest->server->add([ |
| 27 | + 'HTTP_X_REQUEST_ID' => $context->getAwsRequestId(), |
48 | 28 | 'LAMBDA_INVOCATION_CONTEXT' => json_encode($context), |
49 | 29 | 'LAMBDA_REQUEST_CONTEXT' => json_encode($event->getRequestContext()), |
50 | | - 'HTTP_X_SOURCE_IP' => $event->getSourceIp(), |
51 | | - 'HTTP_X_REQUEST_ID' => $context->getAwsRequestId(), |
52 | | - 'PHP_AUTH_USER' => $user, |
53 | | - 'PHP_AUTH_PW' => $password, |
54 | | - ], fn ($value) => ! is_null($value)); |
55 | | - |
56 | | - foreach ($headers as $name => $values) { |
57 | | - $server['HTTP_' . strtoupper(str_replace('-', '_', $name))] = $values[0]; |
58 | | - } |
59 | | - |
60 | | - [$files, $parsedBody, $bodyString] = self::parseBodyAndUploadedFiles($event); |
61 | | - |
62 | | - return new Request( |
63 | | - $event->getQueryParameters(), |
64 | | - $parsedBody ?? [], |
65 | | - [], |
66 | | - $event->getCookies(), |
67 | | - $files, |
68 | | - $server, |
69 | | - $bodyString |
70 | | - ); |
71 | | - } |
72 | | - |
73 | | - /** |
74 | | - * Parse request body and uploaded files. |
75 | | - * |
76 | | - * @param \Bref\Event\Http\HttpRequestEvent $event |
77 | | - * @return array |
78 | | - */ |
79 | | - protected static function parseBodyAndUploadedFiles(HttpRequestEvent $event): array |
80 | | - { |
81 | | - $bodyString = $event->getBody(); |
82 | | - $files = []; |
83 | | - $parsedBody = null; |
84 | | - $contentType = $event->getContentType(); |
85 | | - |
86 | | - if (null !== $contentType && 'POST' === $event->getMethod()) { |
87 | | - if ('application/x-www-form-urlencoded' === $contentType) { |
88 | | - parse_str($bodyString, $parsedBody); |
89 | | - } else { |
90 | | - $stream = fopen('php://temp', 'rw'); |
91 | | - fwrite($stream, "Content-type: {$contentType}\r\n\r\n{$bodyString}"); |
92 | | - rewind($stream); |
93 | | - |
94 | | - $document = new StreamedPart($stream); |
95 | | - |
96 | | - if ($document->isMultiPart()) { |
97 | | - $bodyString = ''; |
98 | | - $parsedBody = []; |
99 | | - foreach ($document->getParts() as $part) { |
100 | | - if ($part->isFile()) { |
101 | | - $tmpPath = tempnam(sys_get_temp_dir(), 'bref_upload_'); |
102 | | - if (false === $tmpPath) { |
103 | | - throw new RuntimeException('Unable to create a temporary directory'); |
104 | | - } |
105 | | - file_put_contents($tmpPath, $part->getBody()); |
106 | | - if (0 !== filesize($tmpPath) && '' !== $part->getFileName()) { |
107 | | - $file = new UploadedFile( |
108 | | - $tmpPath, |
109 | | - $part->getFileName(), |
110 | | - $part->getMimeType(), |
111 | | - UPLOAD_ERR_OK, |
112 | | - true |
113 | | - ); |
114 | | - } else { |
115 | | - $file = null; |
116 | | - } |
117 | | - |
118 | | - self::parseKeyAndInsertValueInArray($files, $part->getName(), $file); |
119 | | - } else { |
120 | | - self::parseKeyAndInsertValueInArray($parsedBody, $part->getName(), $part->getBody()); |
121 | | - } |
122 | | - } |
123 | | - } |
124 | | - } |
125 | | - } |
126 | | - |
127 | | - return [$files, $parsedBody, $bodyString]; |
128 | | - } |
129 | | - |
130 | | - /** |
131 | | - * Parse a string key like "files[id_cards][jpg][]" and do $array['files']['id_cards']['jpg'][] = $value. |
132 | | - * |
133 | | - * @param array $value |
134 | | - * @param string $key |
135 | | - * @param mixed $value |
136 | | - * @return void |
137 | | - */ |
138 | | - protected static function parseKeyAndInsertValueInArray(array &$array, string $key, $value): void |
139 | | - { |
140 | | - if (false === strpos($key, '[')) { |
141 | | - $array[$key] = $value; |
142 | | - |
143 | | - return; |
144 | | - } |
145 | | - |
146 | | - $parts = explode('[', $key); // files[id_cards][jpg][] => [ 'files', 'id_cards]', 'jpg]', ']' ] |
147 | | - $pointer = &$array; |
148 | | - |
149 | | - foreach ($parts as $k => $part) { |
150 | | - if (0 === $k) { |
151 | | - $pointer = &$pointer[$part]; |
152 | | - |
153 | | - continue; |
154 | | - } |
155 | | - |
156 | | - // Skip two special cases: |
157 | | - // [[ in the key produces empty string |
158 | | - // [test : starts with [ but does not end with ] |
159 | | - if ('' === $part || ']' !== substr($part, -1)) { |
160 | | - // Malformed key, we use it "as is" |
161 | | - $array[$key] = $value; |
162 | | - |
163 | | - return; |
164 | | - } |
165 | | - |
166 | | - $part = substr($part, 0, -1); // The last char is a ] => remove it to have the real key |
167 | | - |
168 | | - if ('' === $part) { // [] case |
169 | | - $pointer = &$pointer[]; |
170 | | - } else { |
171 | | - $pointer = &$pointer[$part]; |
172 | | - } |
173 | | - } |
174 | | - |
175 | | - $pointer = $value; |
176 | | - } |
177 | | - |
178 | | - /** |
179 | | - * Parse the username and password from the `Authorization` header. |
180 | | - * Only "Basic" is supported. |
181 | | - * |
182 | | - * @param array $headers |
183 | | - * @return string[]|null[] |
184 | | - */ |
185 | | - protected static function parseBasicAuthorization(array $headers) |
186 | | - { |
187 | | - $authorization = trim($headers['authorization'][0] ?? ''); |
188 | | - |
189 | | - if (! str_starts_with($authorization, 'Basic ')) { |
190 | | - return [null, null]; |
191 | | - } |
192 | | - |
193 | | - $auth = base64_decode(trim(explode(' ', $authorization)[1])); |
194 | | - |
195 | | - if (! $auth || ! strpos($auth, ':')) { |
196 | | - return [null, null]; |
197 | | - } |
| 30 | + ]); |
198 | 31 |
|
199 | | - return [ |
200 | | - strstr($auth, ':', true), |
201 | | - substr(strstr($auth, ':'), 1), |
202 | | - ]; |
| 32 | + return $symfonyRequest; |
203 | 33 | } |
204 | 34 | } |
0 commit comments