Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions .github/workflows/release-please.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: Release Please

on:
push:
branches:
- main
workflow_dispatch:

permissions:
contents: write
pull-requests: write

jobs:
release-please:
name: Release Please
runs-on: ubuntu-latest
outputs:
release_created: ${{ steps.release.outputs.release_created }}
tag_name: ${{ steps.release.outputs.tag_name }}
steps:
- uses: googleapis/release-please-action@v4
id: release
with:
config-file: release-please-config.json
manifest-file: .release-please-manifest.json
3 changes: 3 additions & 0 deletions .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
".": "0.4.0"
}
8 changes: 6 additions & 2 deletions Sources/HTTPClient/Interface/Client.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,18 @@ public struct Client: Sendable {
public var middlewares: [any ClientMiddleware]

/// Creates a new client.
// / - Parameters:
/// - serverURL: The URL of the server, used as the base URL for requests made by the client.
/// - transport: A type capable of sending HTTP requests and receiving HTTP responses.
/// - middlewares: The middlewares to be invoked before the transport. If not provided, a default middleware that adds default headers to requests will be used.
public init(
serverURL: URL,
transport: any ClientTransport,
middlewares: [any ClientMiddleware] = []
middlewares: [any ClientMiddleware]? = nil
) {
self.serverURL = serverURL
self.transport = transport
self.middlewares = middlewares
self.middlewares = middlewares ?? [DefaultHeadersMiddleware()]
}

/// Sends the HTTP request and returns the HTTP response.
Expand Down
110 changes: 110 additions & 0 deletions Sources/HTTPClient/Middlewares/DefaultHeadersMiddleware.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import Foundation
import HTTPTypes

public struct DefaultHeadersMiddleware: ClientMiddleware {

let headers = HTTPFields([
HTTPField.defaultUserAgent,
HTTPField.defaultAcceptEncoding,
HTTPField.defaultAcceptLanguage,
])

public func intercept(
_ request: HTTPRequest,
body: sending HTTPBody?,
baseURL: URL,
next: (HTTPRequest, sending HTTPBody?, URL) async throws -> (HTTPResponse, HTTPBody?)
) async throws -> (HTTPResponse, HTTPBody?) {
var request = request
for field in headers {
if !request.headerFields.contains(field.name) {
request.headerFields[field.name] = field.value
}
}
return try await next(request, body, baseURL)
}
}

extension HTTPField {
/// Returns default `Accept-Encoding` header, appropriate for the encodings supported by particular OS versions.
///
/// See the [Accept-Encoding HTTP header documentation](https://tools.ietf.org/html/rfc7230#section-4.2.3) .
static let defaultAcceptEncoding: HTTPField = {
let encodings = ["gzip", "deflate"]
return HTTPField(name: .acceptEncoding, value: encodings.qualityEncoded())
}()

/// Returns default `Accept-Language` header, generated by querying `Locale` for the user's `preferredLanguages`.
///
/// See the [Accept-Language HTTP header documentation](https://tools.ietf.org/html/rfc7231#section-5.3.5).
static let defaultAcceptLanguage: HTTPField = HTTPField(
name: .acceptLanguage, value: Locale.preferredLanguages.prefix(6).qualityEncoded())

/// Returns default `User-Agent` header.
///
/// See the [User-Agent header documentation](https://tools.ietf.org/html/rfc7231#section-5.5.3).
///
/// Example: `iOS Example/1.0 (dev.grds.swift-http-client.iOS-Example; build:1; iOS 16.0.0) swift-http-client/1.0.0`
static let defaultUserAgent: HTTPField = {
let info = Bundle.main.infoDictionary
let executable =
(info?["CFBundleExecutable"] as? String)
?? (ProcessInfo.processInfo.arguments.first?.split(separator: "/").last.map(String.init))
?? "Unknown"
let bundle = info?["CFBundleIdentifier"] as? String ?? "Unknown"
let appVersion = info?["CFBundleShortVersionString"] as? String ?? "Unknown"
let appBuild = info?["CFBundleVersion"] as? String ?? "Unknown"

let osNameVersion: String = {
let version = ProcessInfo.processInfo.operatingSystemVersion
let versionString = "\(version.majorVersion).\(version.minorVersion).\(version.patchVersion)"
let osName: String = {
#if os(iOS)
#if targetEnvironment(macCatalyst)
return "macOS(Catalyst)"
#else
return "iOS"
#endif
#elseif os(watchOS)
return "watchOS"
#elseif os(tvOS)
return "tvOS"
#elseif os(macOS)
#if targetEnvironment(macCatalyst)
return "macOS(Catalyst)"
#else
return "macOS"
#endif
#elseif os(visionOS)
return "visionOS"
#elseif os(Linux)
return "Linux"
#elseif os(Windows)
return "Windows"
#elseif os(Android)
return "Android"
#elseif os(WASI)
return "WASI"
#else
return "Unknown"
#endif
}()

return "\(osName) \(versionString)"
}()

let userAgent =
"\(executable)/\(appVersion) (\(bundle); build:\(appBuild); \(osNameVersion)) swift-http-client/\(version)"

return HTTPField(name: .userAgent, value: userAgent)
}()
}

extension Collection<String> {
func qualityEncoded() -> String {
enumerated().map { index, encoding in
let quality = 1.0 - (Double(index) * 0.1)
return "\(encoding);q=\(quality)"
}.joined(separator: ", ")
}
}
3 changes: 3 additions & 0 deletions Sources/HTTPClient/Version.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// x-release-please-start-version
let version = "0.4.0"
// x-release-please-end
4 changes: 3 additions & 1 deletion Tests/HTTPClientTests/ClientTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,9 @@ struct ClientTests {
let client = Client(serverURL: serverURL, transport: transport)

#expect(client.serverURL == serverURL)
#expect(client.middlewares.isEmpty)
#expect(
client.middlewares.isEmpty == false,
"Default middleware should be added when no middlewares are provided")
}

@Test func clientInitializationWithMiddlewares() {
Expand Down
19 changes: 19 additions & 0 deletions release-please-config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json",
"release-type": "simple",
"packages": {
".": {
"release-type": "simple",
"package-name": "swift-http-client",
"changelog-path": "CHANGELOG.md",
"bump-minor-pre-major": true,
"bump-patch-for-minor-pre-major": true,
"extra-files": [
{
"type": "generic",
"path": "Sources/HTTPClient/Version.swift"
}
]
}
}
}
Loading