<?php declare(strict_types=1); namespace Epush\Adms; /** * Printing-only ADMS cutover helper: route selected SNs to epush-php, * proxy all other traffic to stock Java Tomcat on an alternate port. */ final class SnGateway { /** * @param array<string, string> $phpSerialToHost SN => forced HTTP Host (tenant key) */ public function __construct( private readonly array $phpSerialToHost, private readonly string $javaUpstreamBase, private readonly FrontController $frontController, ) { } /** * @param array<string, mixed> $server */ public function extractSerial(array $server): string { $qs = (string) ($server['QUERY_STRING'] ?? ''); parse_str($qs, $query); return trim((string) ($query['SN'] ?? '')); } public function shouldHandleInPhp(string $serial): bool { return $serial !== '' && isset($this->phpSerialToHost[$serial]); } public function forcedHostForSerial(string $serial): ?string { return $this->phpSerialToHost[$serial] ?? null; } /** * @param array<string, mixed> $server * @return array{status:int, headers:list<string>, body:string} */ public function proxyToJava(array $server, string $rawBody): array { $uri = (string) ($server['REQUEST_URI'] ?? '/'); $method = strtoupper((string) ($server['REQUEST_METHOD'] ?? 'GET')); $url = rtrim($this->javaUpstreamBase, '/') . $uri; $headers = []; $host = (string) ($server['HTTP_HOST'] ?? ''); if ($host !== '') { $headers[] = 'Host: ' . $host; } $contentType = (string) ($server['CONTENT_TYPE'] ?? $server['HTTP_CONTENT_TYPE'] ?? ''); if ($contentType !== '') { $headers[] = 'Content-Type: ' . $contentType; } if ($rawBody !== '' && $method !== 'GET' && $method !== 'HEAD') { $headers[] = 'Content-Length: ' . (string) strlen($rawBody); } $ch = curl_init($url); if ($ch === false) { return ['status' => 502, 'headers' => [], 'body' => "OK\r\n"]; } curl_setopt_array($ch, [ CURLOPT_CUSTOMREQUEST => $method, CURLOPT_HTTPHEADER => $headers, CURLOPT_POSTFIELDS => ($method === 'GET' || $method === 'HEAD') ? null : $rawBody, CURLOPT_RETURNTRANSFER => true, CURLOPT_HEADER => true, CURLOPT_TIMEOUT => 8, CURLOPT_CONNECTTIMEOUT => 3, ]); $resp = curl_exec($ch); if ($resp === false) { curl_close($ch); return ['status' => 502, 'headers' => [], 'body' => "OK\r\n"]; } $status = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE); $headerSize = (int) curl_getinfo($ch, CURLINFO_HEADER_SIZE); curl_close($ch); $rawHeaders = substr($resp, 0, $headerSize); $body = substr($resp, $headerSize); $outHeaders = []; foreach (preg_split('/\r\n|\n|\r/', $rawHeaders) ?: [] as $line) { $line = trim($line); if ($line === '' || str_starts_with(strtolower($line), 'http/')) { continue; } if (str_starts_with(strtolower($line), 'transfer-encoding:')) { continue; } if (str_starts_with(strtolower($line), 'content-length:')) { continue; } $outHeaders[] = $line; } return ['status' => $status > 0 ? $status : 502, 'headers' => $outHeaders, 'body' => $body]; } /** * @param array<string, mixed> $server */ public function handle(array $server, string $rawBody): void { $serial = $this->extractSerial($server); if ($this->shouldHandleInPhp($serial)) { $forced = $this->forcedHostForSerial($serial); if ($forced !== null && $forced !== '') { $server['HTTP_HOST'] = $forced; } $this->frontController->handle($server, $rawBody); return; } $proxied = $this->proxyToJava($server, $rawBody); http_response_code($proxied['status']); foreach ($proxied['headers'] as $h) { header($h, false); } echo $proxied['body']; } }