Skip to content

Commit

Permalink
Download toolchains using proxy if one is set in the environment (swi…
Browse files Browse the repository at this point in the history
…ftlang#189)

Swiftly doesn't currently handle the standard HTTP proxy server environment variables when making its network connections. It only handles it as a necessity in the current macOS CI environment and only in the tests. Make the built-in http executor read these environment variables if they are present and use those for all http requests. Add documentation in the GSG showing how to use this feature.
  • Loading branch information
vsarunas authored Dec 2, 2024
1 parent ec7887e commit c5518b6
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 46 deletions.
11 changes: 11 additions & 0 deletions Documentation/SwiftlyDocs.docc/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,17 @@ Uninstall this toolchain after you're finished with it:
$ swiftly uninstall main-snapshot
```

# Proxy

Swiftly downloads a list of toolchains from https://www.swift.org/ and retrieves them from Apple/Akamai CDN via https://download.swift.org.
If your environment requires a proxy, Swiftly will attempt to use the standard environment variables `http_proxy`, `HTTP_PROXY`, `https_proxy` or `HTTPS_PROXY` to determine which proxy server to use instead of making a direct connection.

To download latest nightly snapshot using a proxy:
```
$ export https_proxy=http://proxy:3128
$ swiftly install main-snapshot
```

# See Also:

- [Install Toolchains](install-toolchains)
Expand Down
43 changes: 41 additions & 2 deletions Sources/SwiftlyCore/HTTPClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,48 @@ public protocol HTTPRequestExecutor {
}

/// An `HTTPRequestExecutor` backed by the shared `HTTPClient`.
internal struct HTTPRequestExecutorImpl: HTTPRequestExecutor {
internal class HTTPRequestExecutorImpl: HTTPRequestExecutor {
let httpClient: HTTPClient

public init() {
var proxy: HTTPClient.Configuration.Proxy?

func getProxyFromEnv(keys: [String]) -> HTTPClient.Configuration.Proxy? {
let environment = ProcessInfo.processInfo.environment
for key in keys {
if let proxyString = environment[key],
let url = URL(string: proxyString),
let host = url.host,
let port = url.port
{
return .server(host: host, port: port)
}
}
return nil
}

if let httpProxy = getProxyFromEnv(keys: ["http_proxy", "HTTP_PROXY"]) {
proxy = httpProxy
}
if let httpsProxy = getProxyFromEnv(keys: ["https_proxy", "HTTPS_PROXY"]) {
proxy = httpsProxy
}

if proxy != nil {
self.httpClient = HTTPClient(eventLoopGroupProvider: .singleton, configuration: HTTPClient.Configuration(proxy: proxy))
} else {
self.httpClient = HTTPClient.shared
}
}

deinit {
if httpClient !== HTTPClient.shared {
try? httpClient.syncShutdown()
}
}

public func execute(_ request: HTTPClientRequest, timeout: TimeAmount) async throws -> HTTPClientResponse {
try await HTTPClient.shared.execute(request, timeout: timeout)
try await self.httpClient.execute(request, timeout: timeout)
}
}

Expand Down
44 changes: 0 additions & 44 deletions Tests/SwiftlyTests/SwiftlyTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,51 +17,7 @@ struct SwiftlyTestError: LocalizedError {
let message: String
}

var proxyExecutorInstalled = false

/// An `HTTPRequestExecutor` backed by an `HTTPClient` that can take http proxy
/// information from the environment in either HTTP_PROXY or HTTPS_PROXY
class ProxyHTTPRequestExecutorImpl: HTTPRequestExecutor {
let httpClient: HTTPClient
public init() {
var proxy: HTTPClient.Configuration.Proxy?

let environment = ProcessInfo.processInfo.environment
let httpProxy = environment["HTTP_PROXY"]
if let httpProxy, let url = URL(string: httpProxy), let host = url.host, let port = url.port {
proxy = .server(host: host, port: port)
}

let httpsProxy = environment["HTTPS_PROXY"]
if let httpsProxy, let url = URL(string: httpsProxy), let host = url.host, let port = url.port {
proxy = .server(host: host, port: port)
}

if proxy != nil {
self.httpClient = HTTPClient(eventLoopGroupProvider: .singleton, configuration: HTTPClient.Configuration(proxy: proxy))
} else {
self.httpClient = HTTPClient.shared
}
}

public func execute(_ request: HTTPClientRequest, timeout: TimeAmount) async throws -> HTTPClientResponse {
try await self.httpClient.execute(request, timeout: timeout)
}

deinit {
if httpClient !== HTTPClient.shared {
try? httpClient.syncShutdown()
}
}
}

class SwiftlyTests: XCTestCase {
override class func setUp() {
if !proxyExecutorInstalled {
SwiftlyCore.httpRequestExecutor = ProxyHTTPRequestExecutorImpl()
}
}

override class func tearDown() {
#if os(Linux)
let deleteTestGPGKeys = Process()
Expand Down

0 comments on commit c5518b6

Please sign in to comment.