api-swift / Sources / IrdAPI / Core / HTTPTransport.swift
340 words
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
import Foundation

/// A completed HTTP exchange.
public struct HTTPResponse: Sendable {
    public var status: Int
    public var headers: [String: String]
    public var body: Data

    public init(status: Int, headers: [String: String] = [:], body: Data = Data()) {
        self.status = status
        self.headers = headers
        self.body = body
    }

    public var isSuccess: Bool { (200..<300).contains(status) }
}

/// The HTTP layer, injectable so tests run offline and apps can add instrumentation.
public protocol HTTPTransport: Sendable {
    func send(_ request: URLRequest) async throws -> HTTPResponse
    /// Sends `request` with the file's bytes as its body (large IPFS uploads).
    func upload(_ request: URLRequest, fromFile url: URL) async throws -> HTTPResponse
}

public extension HTTPTransport {
    /// Default: read the file into memory and `send` it, so fakes implement one method.
    func upload(_ request: URLRequest, fromFile url: URL) async throws -> HTTPResponse {
        var r = request
        r.httpBody = try Data(contentsOf: url)
        return try await send(r)
    }
}

/// `URLSession`-backed transport. Every network failure, timeouts included, surfaces
/// as `IrdError.transport`; `.timeout` is reserved for poll deadlines.
public struct URLSessionHTTPTransport: HTTPTransport {
    let session: URLSession

    public init(session: URLSession = .shared) { self.session = session }

    public func send(_ request: URLRequest) async throws -> HTTPResponse {
        do {
            let (data, response) = try await session.data(for: request)
            return try Self.wrap(data, response)
        } catch let e as IrdError { throw e } catch { throw IrdError.transport(error.localizedDescription) }
    }

    public func upload(_ request: URLRequest, fromFile url: URL) async throws -> HTTPResponse {
        do {
            let (data, response) = try await session.upload(for: request, fromFile: url)
            return try Self.wrap(data, response)
        } catch let e as IrdError { throw e } catch { throw IrdError.transport(error.localizedDescription) }
    }

    static func wrap(_ data: Data, _ response: URLResponse) throws -> HTTPResponse {
        guard let http = response as? HTTPURLResponse else { throw IrdError.transport("non-HTTP response") }
        var headers: [String: String] = [:]
        for (k, v) in http.allHeaderFields {
            if let k = k as? String, let v = v as? String { headers[k.lowercased()] = v }
        }
        return HTTPResponse(status: http.statusCode, headers: headers, body: data)
    }
}

static mirror of HEAD · about · clone: git clone https://ird.cod-sursa.ro/api-swift.git