fetch( $request, rtrim($this->transferBaseUrl(), '/').'/q/'.$slug.'/'.$normalizedPath, ); } if ($this->isEventsRegistrationPath($normalizedPath)) { return $this->forwardToQrHub($request, $slug, $normalizedPath, $this->eventsBaseUrl()); } $platformResponse = $this->forwardToQrHub($request, $slug, $normalizedPath, $this->platformBaseUrl()); if ($platformResponse->getStatusCode() !== 404) { return $platformResponse; } return $this->forwardToQrHub($request, $slug, $normalizedPath, $this->eventsBaseUrl()); } private function forwardToQrHub(Request $request, string $slug, string $normalizedPath, string $hubBase): Response { $target = rtrim($hubBase, '/').'/q/'.$slug; if ($normalizedPath !== '') { $target .= '/'.$normalizedPath; } $response = $this->fetch($request, $target); if ($response->isRedirection()) { $location = $response->headers->get('Location'); if (is_string($location)) { if ($followUp = $this->followLadillLinkRedirect($request, $slug, $location)) { return $followUp; } $response->headers->set('Location', $this->rewriteLocation($location, $slug)); } } return $response; } private function fetch(Request $request, string $target): Response { if ($qs = $request->getQueryString()) { $target .= (str_contains($target, '?') ? '&' : '?').$qs; } $method = strtoupper($request->method()); $client = Http::withOptions(['allow_redirects' => false]) ->timeout(15) ->withHeaders($this->forwardHeaders($request)); $pending = match ($method) { 'POST' => $this->forwardPost($client, $request, $target), 'PATCH' => $client->patch($target, $request->all()), 'DELETE' => $client->delete($target), default => $client->get($target), }; $upstream = $pending->toPsrResponse(); return new Response( (string) $upstream->getBody(), $upstream->getStatusCode(), $this->sanitizeHeaders($upstream->getHeaders()), ); } private function followLadillLinkRedirect(Request $request, string $slug, string $location): ?Response { $parsed = parse_url($location); if (! is_array($parsed)) { return null; } $host = strtolower((string) ($parsed['host'] ?? '')); $linkHost = strtolower((string) parse_url(LadillLink::baseUrl(), PHP_URL_HOST)); if ($host !== $linkHost) { return null; } $path = ltrim((string) ($parsed['path'] ?? ''), '/'); $slugPrefix = $slug.'/'; if (! str_starts_with($path, $slugPrefix)) { return null; } return $this->forward($request, $slug, substr($path, strlen($slugPrefix))); } private function isEventsRegistrationPath(string $path): bool { return $path === 'register' || str_starts_with($path, 'register/') || str_starts_with($path, 'registered/') || str_starts_with($path, 'speaker/'); } private function platformBaseUrl(): string { return rtrim((string) config('app.platform_url', 'https://ladill.com'), '/'); } private function eventsBaseUrl(): string { $domain = (string) config('app.events_domain', 'events.ladill.com'); return 'https://'.$domain; } private function transferBaseUrl(): string { $domain = (string) config('app.transfer_domain', 'transfer.ladill.com'); return 'https://'.$domain; } /** @return array */ private function forwardHeaders(Request $request): array { return array_filter([ 'Accept' => $request->header('Accept'), 'Accept-Language' => $request->header('Accept-Language'), 'Content-Type' => $request->header('Content-Type'), 'User-Agent' => $request->header('User-Agent'), 'Referer' => $request->header('Referer'), self::INTERNAL_HEADER => '1', ]); } private function forwardPost(\Illuminate\Http\Client\PendingRequest $client, Request $request, string $target): \Illuminate\Http\Client\Response { if ($this->requestSendsJson($request)) { return $client->asJson()->post($target, $request->all()); } return $client->asForm()->post($target, $request->post()); } private function requestSendsJson(Request $request): bool { return str_contains(strtolower((string) $request->header('Content-Type', '')), 'application/json'); } /** @param array> $headers */ private function sanitizeHeaders(array $headers): array { $flat = []; foreach ($headers as $name => $values) { if (strtolower($name) === 'transfer-encoding') { continue; } $flat[$name] = $values[0] ?? ''; } return $flat; } private function rewriteLocation(string $location, string $slug): string { foreach ([$this->platformBaseUrl(), $this->eventsBaseUrl()] as $base) { $prefix = $base.'/q/'.$slug; if (str_starts_with($location, $prefix)) { $suffix = substr($location, strlen($prefix)); return LadillLink::url($slug).$suffix; } } $transfer = rtrim($this->transferBaseUrl(), '/').'/q/'.$slug; if (str_starts_with($location, $transfer)) { $suffix = substr($location, strlen($transfer)); return LadillLink::url($slug).$suffix; } return $location; } }