header('x-forwarded-proto', '')); if ($proto === '') { $proto = strtolower((string) $request->header('x-forwarded-protocol', '')); } $isHttps = $proto === 'https' || strtolower((string) $request->header('x-forwarded-ssl', '')) === 'on' || strtolower((string) $request->header('x-scheme', '')) === 'https'; $scheme = $isHttps ? 'wss' : 'ws'; $host = trim((string) $request->header('host', '')); if ($host === '') { $host = trim((string) $request->header('x-forwarded-host', '')); } if ($host !== '') { return $scheme . '://' . $host . '/ws/'; } } return 'ws://127.0.0.1:3131/ws/'; } /** * 在基础 ws_url 上拼接握手鉴权 Query: * - 后台用:auth_token + admin_ws_token(可观测全量主题,无 user_id 过滤) * - H5 用:调用方传 user_token;与 auth_token 一起拼上去 * * @param array{auth_token?: string, user_token?: string, admin_ws_token?: string} $tokens */ public static function appendTokensToWsUrl(string $wsUrl, array $tokens): string { $wsUrl = trim($wsUrl); if ($wsUrl === '') { return $wsUrl; } $pairs = []; foreach (['auth_token', 'user_token', 'admin_ws_token'] as $key) { $val = isset($tokens[$key]) && is_string($tokens[$key]) ? trim($tokens[$key]) : ''; if ($val !== '') { $pairs[] = $key . '=' . rawurlencode($val); } } if ($pairs === []) { return $wsUrl; } $sep = str_contains($wsUrl, '?') ? '&' : '?'; return $wsUrl . $sep . implode('&', $pairs); } private static function isLoopbackWsUrl(string $url): bool { $host = parse_url($url, PHP_URL_HOST); if (!is_string($host) || $host === '') { return false; } $host = strtolower($host); return in_array($host, ['127.0.0.1', 'localhost', '::1'], true); } private static function isLoopbackRequestHost(Request $request): bool { $host = strtolower(trim((string) $request->host(true))); if ($host === '') { $host = strtolower(trim((string) $request->header('host', ''))); } if ($host === '') { return false; } $hostOnly = preg_split('/:/', $host)[0] ?? $host; return in_array($hostOnly, ['127.0.0.1', 'localhost', '::1'], true); } }