mirror of
https://github.com/swift-server/async-http-client.git
synced 2026-06-02 07:37:34 +00:00
3b57e00556
Motivation In machines with more complex network topologies it is possible for us to have multiple possible NICs we might want to use for a request. Users may wish to vary this on a per-request or even a per-client basis. This control can typically be expressed by offering a local address to bind to before making the connection attempt. Modifications Allow users to express a preferred local address at request or client scope. Make this part of the connection pool key. Bind the local address when specified. Test all of this. Results More capable clients.
134 lines
4.3 KiB
Swift
134 lines
4.3 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This source file is part of the AsyncHTTPClient open source project
|
|
//
|
|
// Copyright (c) 2026 Apple Inc. and the AsyncHTTPClient project authors
|
|
// Licensed under Apache License v2.0
|
|
//
|
|
// See LICENSE.txt for license information
|
|
// See CONTRIBUTORS.txt for the list of AsyncHTTPClient project authors
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
import NIOHTTP1
|
|
import Testing
|
|
|
|
import struct Foundation.URL
|
|
|
|
@testable import AsyncHTTPClient
|
|
|
|
struct LocalAddressOverrideTests {
|
|
// MARK: - Pool Key with localAddress
|
|
|
|
@Test func poolKeysWithDifferentLocalAddressesAreNotEqual() {
|
|
let key1 = ConnectionPool.Key(
|
|
scheme: .https,
|
|
connectionTarget: .domain(name: "example.com", port: 443),
|
|
serverNameIndicatorOverride: nil,
|
|
localAddress: "192.168.1.10"
|
|
)
|
|
let key2 = ConnectionPool.Key(
|
|
scheme: .https,
|
|
connectionTarget: .domain(name: "example.com", port: 443),
|
|
serverNameIndicatorOverride: nil,
|
|
localAddress: "10.0.0.1"
|
|
)
|
|
let keyNil = ConnectionPool.Key(
|
|
scheme: .https,
|
|
connectionTarget: .domain(name: "example.com", port: 443),
|
|
serverNameIndicatorOverride: nil,
|
|
localAddress: nil
|
|
)
|
|
#expect(key1 != key2)
|
|
#expect(key1 != keyNil)
|
|
#expect(key2 != keyNil)
|
|
}
|
|
|
|
@Test func poolKeysWithSameLocalAddressAreEqual() {
|
|
let key1 = ConnectionPool.Key(
|
|
scheme: .https,
|
|
connectionTarget: .domain(name: "example.com", port: 443),
|
|
serverNameIndicatorOverride: nil,
|
|
localAddress: "192.168.1.10"
|
|
)
|
|
let key2 = ConnectionPool.Key(
|
|
scheme: .https,
|
|
connectionTarget: .domain(name: "example.com", port: 443),
|
|
serverNameIndicatorOverride: nil,
|
|
localAddress: "192.168.1.10"
|
|
)
|
|
#expect(key1 == key2)
|
|
}
|
|
|
|
@Test func poolKeyWithNilLocalAddressMatchesDefault() {
|
|
let key1 = ConnectionPool.Key(
|
|
scheme: .https,
|
|
connectionTarget: .domain(name: "example.com", port: 443),
|
|
serverNameIndicatorOverride: nil
|
|
)
|
|
let key2 = ConnectionPool.Key(
|
|
scheme: .https,
|
|
connectionTarget: .domain(name: "example.com", port: 443),
|
|
serverNameIndicatorOverride: nil,
|
|
localAddress: nil
|
|
)
|
|
#expect(key1 == key2)
|
|
}
|
|
|
|
// MARK: - Per-request localAddress override
|
|
|
|
@Test func perRequestLocalAddressOverridesConfig() throws {
|
|
var request = HTTPClientRequest(url: "https://example.com/get")
|
|
request.localAddress = "10.0.0.1"
|
|
|
|
let prepared = try HTTPClientRequest.Prepared(
|
|
request,
|
|
localAddress: "192.168.1.10"
|
|
)
|
|
|
|
#expect(prepared.poolKey.localAddress == "10.0.0.1")
|
|
}
|
|
|
|
@Test func configLocalAddressUsedWhenRequestHasNone() throws {
|
|
let request = HTTPClientRequest(url: "https://example.com/get")
|
|
|
|
let prepared = try HTTPClientRequest.Prepared(
|
|
request,
|
|
localAddress: "192.168.1.10"
|
|
)
|
|
|
|
#expect(prepared.poolKey.localAddress == "192.168.1.10")
|
|
}
|
|
|
|
@Test func noLocalAddressWhenNeitherSet() throws {
|
|
let request = HTTPClientRequest(url: "https://example.com/get")
|
|
|
|
let prepared = try HTTPClientRequest.Prepared(request)
|
|
|
|
#expect(prepared.poolKey.localAddress == nil)
|
|
}
|
|
|
|
// MARK: - Redirect preserves localAddress
|
|
|
|
@Test func redirectPreservesLocalAddress() {
|
|
var request = HTTPClientRequest(url: "https://example.com/redirect/301")
|
|
request.localAddress = "192.168.1.10"
|
|
|
|
let redirected = request.followingRedirect(
|
|
from: URL(string: "https://example.com/redirect/301")!,
|
|
to: URL(string: "https://other.com/ok")!,
|
|
status: .movedPermanently,
|
|
config: .init(
|
|
max: 5,
|
|
allowCycles: false,
|
|
retainHTTPMethodAndBodyOn301: false,
|
|
retainHTTPMethodAndBodyOn302: false
|
|
)
|
|
)
|
|
|
|
#expect(redirected.localAddress == "192.168.1.10")
|
|
}
|
|
}
|