Added: ability to set basic authentication on requests (#778)

Motivation:

As an HTTP library, async-http-client should have authentication
support.

Modifications:

This adds a `setBasicAuth()` method to both HTTPClientRequest and
`HTTPClient.Request` and their related unit tests.

Result:

Library users will be able to leverage this method to use basic
authentication on their requests without implementing this in their own
projects.

Note: I also ran the tests (`swift test`) with the
`docker.io/library/swift:6.0-focal` and
`docker.io/library/swift:5.10.1-focal` to ensure linux compatibility.

Signed-off-by: Agam Dua <agam_dua@apple.com>
This commit is contained in:
Agam Dua
2024-10-21 08:27:28 -07:00
committed by GitHub
parent 0a9b72369b
commit acaca2d50d
5 changed files with 93 additions and 0 deletions
@@ -0,0 +1,27 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the AsyncHTTPClient open source project
//
// Copyright (c) 2024 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 Foundation
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
extension HTTPClientRequest {
/// Set basic auth for a request.
///
/// - parameters:
/// - username: the username to authenticate with
/// - password: authentication password associated with the username
public mutating func setBasicAuth(username: String, password: String) {
self.headers.setBasicAuth(username: username, password: password)
}
}
+39
View File
@@ -0,0 +1,39 @@
//===----------------------------------------------------------------------===//
//
// This source file is part of the AsyncHTTPClient open source project
//
// Copyright (c) 2024 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 Foundation
import NIOHTTP1
/// Generates base64 encoded username + password for http basic auth.
///
/// - Parameters:
/// - username: the username to authenticate with
/// - password: authentication password associated with the username
/// - Returns: encoded credentials to use the Authorization: Basic http header.
func encodeBasicAuthCredentials(username: String, password: String) -> String {
var value = Data()
value.reserveCapacity(username.utf8.count + password.utf8.count + 1)
value.append(contentsOf: username.utf8)
value.append(UInt8(ascii: ":"))
value.append(contentsOf: password.utf8)
return value.base64EncodedString()
}
extension HTTPHeaders {
/// Sets the basic auth header
mutating func setBasicAuth(username: String, password: String) {
let encoded = encodeBasicAuthCredentials(username: username, password: password)
self.replaceOrAdd(name: "Authorization", value: "Basic \(encoded)")
}
}
@@ -285,6 +285,15 @@ extension HTTPClient {
return (head, metadata)
}
/// Set basic auth for a request.
///
/// - parameters:
/// - username: the username to authenticate with
/// - password: authentication password associated with the username
public mutating func setBasicAuth(username: String, password: String) {
self.headers.setBasicAuth(username: username, password: password)
}
}
/// Represents an HTTP response.
@@ -57,6 +57,17 @@ class HTTPClientRequestTests: XCTestCase {
}
}
func testBasicAuth() {
XCTAsyncTest {
var request = Request(url: "https://example.com/get")
request.setBasicAuth(username: "foo", password: "bar")
var preparedRequest: PreparedRequest?
XCTAssertNoThrow(preparedRequest = try PreparedRequest(request))
guard let preparedRequest = preparedRequest else { return }
XCTAssertEqual(preparedRequest.head.headers.first(name: "Authorization")!, "Basic Zm9vOmJhcg==")
}
}
func testUnixScheme() {
XCTAsyncTest {
var request = Request(url: "unix://%2Fexample%2Ffolder.sock/some_path")
@@ -3668,4 +3668,11 @@ final class HTTPClientTests: XCTestCaseHTTPClientTestsBaseClass {
let response3 = try await client.execute(request, timeout: /* infinity */ .hours(99))
XCTAssertEqual(.ok, response3.status)
}
@available(macOS 10.15, iOS 13.0, watchOS 6.0, tvOS 13.0, *)
func testRequestBasicAuth() async throws {
var request = try HTTPClient.Request(url: self.defaultHTTPBinURLPrefix)
request.setBasicAuth(username: "foo", password: "bar")
XCTAssertEqual(request.headers.first(name: "Authorization"), "Basic Zm9vOmJhcg==")
}
}