RouteExplorerClient: send browser-shaped headers (fix 403 on /api/token)
The route-explorer.com proxy gates /api/token and /api/flight-search by Origin/Referer in production. Without those headers iOS got 403. Mirror the existing United pattern (applyUnitedBrowserHeaders): set User-Agent, Accept-Language, Referer, Origin on every request. Also log the token response body on non-200 so future failures are diagnosable. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -109,11 +109,14 @@ actor RouteExplorerClient {
|
||||
let url = baseURL.appendingPathComponent("api/token")
|
||||
var req = URLRequest(url: url)
|
||||
req.httpMethod = "GET"
|
||||
Self.applyBrowserHeaders(to: &req)
|
||||
req.setValue("application/json", forHTTPHeaderField: "Accept")
|
||||
|
||||
let (data, response) = try await session.data(for: req)
|
||||
let status = (response as? HTTPURLResponse)?.statusCode ?? -1
|
||||
guard status == 200 else {
|
||||
if status != 200 {
|
||||
let bodyStr = String(data: data, encoding: .utf8) ?? "<no body>"
|
||||
print("[RouteExplorer] /api/token failed status=\(status) body=\(bodyStr.prefix(300))")
|
||||
throw ClientError.tokenFetchFailed(status: status)
|
||||
}
|
||||
|
||||
@@ -127,6 +130,21 @@ actor RouteExplorerClient {
|
||||
}
|
||||
}
|
||||
|
||||
/// Browser-shaped headers — `/api/token` and `/api/flight-search` are
|
||||
/// gated by Origin/Referer in production. Without these the server
|
||||
/// returns 403 even though the endpoints are otherwise anonymous.
|
||||
private static func applyBrowserHeaders(to request: inout URLRequest) {
|
||||
request.setValue(
|
||||
"Mozilla/5.0 (iPhone; CPU iPhone OS 17_4 like Mac OS X) "
|
||||
+ "AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.4 "
|
||||
+ "Mobile/15E148 Safari/604.1",
|
||||
forHTTPHeaderField: "User-Agent"
|
||||
)
|
||||
request.setValue("en-US,en;q=0.9", forHTTPHeaderField: "Accept-Language")
|
||||
request.setValue("https://route-explorer.com/", forHTTPHeaderField: "Referer")
|
||||
request.setValue("https://route-explorer.com", forHTTPHeaderField: "Origin")
|
||||
}
|
||||
|
||||
// MARK: - Flight search proxy
|
||||
|
||||
private func callFlightSearch(
|
||||
@@ -144,6 +162,7 @@ actor RouteExplorerClient {
|
||||
|
||||
var req = URLRequest(url: url)
|
||||
req.httpMethod = "POST"
|
||||
Self.applyBrowserHeaders(to: &req)
|
||||
req.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||
req.setValue("application/json", forHTTPHeaderField: "Accept")
|
||||
req.setValue(token, forHTTPHeaderField: "X-API-Token")
|
||||
@@ -181,6 +200,7 @@ actor RouteExplorerClient {
|
||||
|
||||
var req = URLRequest(url: url)
|
||||
req.httpMethod = "POST"
|
||||
Self.applyBrowserHeaders(to: &req)
|
||||
req.setValue("application/json", forHTTPHeaderField: "Content-Type")
|
||||
req.setValue(token, forHTTPHeaderField: "X-API-Token")
|
||||
req.httpBody = bodyData
|
||||
|
||||
Reference in New Issue
Block a user