Skip to content

Commit 19e83a3

Browse files
authored
Set host on new request correctly (#536)
### Motivation If we follow a redirect which changes the origin e.g. from `127.0.0.1` to `localhost` we didn't change the `Host` header to the appropriate new origin and port combination. ### Changes Use the original request which does not include the host instead of the prepared request to form a new request to the redirect URL. ### Alternatives If the user defines a `Host` header themselves on the original `HTTPClientRequest` we currently never touch it, even in the redirect case. Maybe we should change our strategy and do one of the following: 1. We could always override the user defined `Host` header 2. We could only remove the user defined `Host` header on redirect and set it to the new origin and port combination
1 parent e531961 commit 19e83a3

File tree

4 files changed

+44
-6
lines changed

4 files changed

+44
-6
lines changed

Diff for: Sources/AsyncHTTPClient/AsyncAwait/HTTPClient+execute.swift

+5-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,11 @@ extension HTTPClient {
7575
try redirectState.redirect(to: redirectURL.absoluteString)
7676
currentRedirectState = redirectState
7777

78-
let newRequest = preparedRequest.followingRedirect(to: redirectURL, status: response.status)
78+
let newRequest = currentRequest.followingRedirect(
79+
from: preparedRequest.url,
80+
to: redirectURL,
81+
status: response.status
82+
)
7983

8084
guard newRequest.body.canBeConsumedMultipleTimes else {
8185
// we already send the request body and it cannot be send again

Diff for: Sources/AsyncHTTPClient/AsyncAwait/HTTPClientRequest+Prepared.swift

+9-5
Original file line numberDiff line numberDiff line change
@@ -75,12 +75,16 @@ extension RequestBodyLength {
7575
}
7676

7777
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
78-
extension HTTPClientRequest.Prepared {
79-
func followingRedirect(to redirectURL: URL, status: HTTPResponseStatus) -> HTTPClientRequest {
78+
extension HTTPClientRequest {
79+
func followingRedirect(
80+
from originalURL: URL,
81+
to redirectURL: URL,
82+
status: HTTPResponseStatus
83+
) -> HTTPClientRequest {
8084
let (method, headers, body) = transformRequestForRedirect(
81-
from: self.url,
82-
method: self.head.method,
83-
headers: self.head.headers,
85+
from: originalURL,
86+
method: self.method,
87+
headers: self.headers,
8488
body: self.body,
8589
to: redirectURL,
8690
status: status

Diff for: Tests/AsyncHTTPClientTests/AsyncAwaitEndToEndTests+XCTest.swift

+1
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ extension AsyncAwaitEndToEndTests {
3939
("testDeadline", testDeadline),
4040
("testImmediateDeadline", testImmediateDeadline),
4141
("testInvalidURL", testInvalidURL),
42+
("testRedirectChangesHostHeader", testRedirectChangesHostHeader),
4243
]
4344
}
4445
}

Diff for: Tests/AsyncHTTPClientTests/AsyncAwaitEndToEndTests.swift

+29
Original file line numberDiff line numberDiff line change
@@ -400,6 +400,35 @@ final class AsyncAwaitEndToEndTests: XCTestCase {
400400
}
401401
#endif
402402
}
403+
404+
func testRedirectChangesHostHeader() {
405+
#if compiler(>=5.5) && canImport(_Concurrency)
406+
guard #available(macOS 12.0, iOS 15.0, watchOS 8.0, tvOS 15.0, *) else { return }
407+
XCTAsyncTest {
408+
let bin = HTTPBin(.http2(compress: false))
409+
defer { XCTAssertNoThrow(try bin.shutdown()) }
410+
let client = makeDefaultHTTPClient()
411+
defer { XCTAssertNoThrow(try client.syncShutdown()) }
412+
let logger = Logger(label: "HTTPClient", factory: StreamLogHandler.standardOutput(label:))
413+
var request = HTTPClientRequest(url: "https://127.0.0.1:\(bin.port)/redirect/target")
414+
request.headers.replaceOrAdd(name: "X-Target-Redirect-URL", value: "https://localhost:\(bin.port)/echohostheader")
415+
416+
guard let response = await XCTAssertNoThrowWithResult(
417+
try await client.execute(request, deadline: .now() + .seconds(10), logger: logger)
418+
) else {
419+
return
420+
}
421+
guard let body = await XCTAssertNoThrowWithResult(try await response.body.collect()) else { return }
422+
var maybeRequestInfo: RequestInfo?
423+
XCTAssertNoThrow(maybeRequestInfo = try JSONDecoder().decode(RequestInfo.self, from: body))
424+
guard let requestInfo = maybeRequestInfo else { return }
425+
426+
XCTAssertEqual(response.status, .ok)
427+
XCTAssertEqual(response.version, .http2)
428+
XCTAssertEqual(requestInfo.data, "localhost:\(bin.port)")
429+
}
430+
#endif
431+
}
403432
}
404433

405434
#if compiler(>=5.5.2) && canImport(_Concurrency)

0 commit comments

Comments
 (0)