Skip to content

Using Vapor HTTPClient as network transport for Swarm

Denys Telezhkin edited this page Feb 1, 2021 · 1 revision

Example of Vapor/HTTPClient implementation of networking transport for Swarm:

import Foundation
import Vapor
import Swarm
import AsyncHTTPClient
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif

class HTTPClientSpider : Spider {
    
    let client : HTTPClient
    
    private var future : EventLoopFuture<HTTPClient.Response>?
    var httpHeaders : [String: String] = [:]
    
    init(eventLoopGroupProvider: AsyncHTTPClient.HTTPClient.EventLoopGroupProvider,
         configuration: AsyncHTTPClient.HTTPClient.Configuration = .init()) {
        client = HTTPClient(eventLoopGroupProvider: eventLoopGroupProvider, configuration: configuration)
    }
    
    deinit {
        client.shutdown { error in
            if let error = error {
                print("❗️ Failed to shut down HTTPClient on HTTPClientSpider instance with error: \(error)")
            }
        }
    }
    
    func request(url: ScrappableURL, completion: @escaping (Data?, URLResponse?, Error?) -> Void) {
        do {
            let request = try HTTPClient.Request(url: url.url, method: .GET, headers: .init(httpHeaders.map { ($0.key, $0.value)}), body: nil)
            future = client.execute(request: request)
                .always { result in
                    switch result {
                        case .success(let response):
                            let data = response.body?.getData(at: 0, length: response.body?.readableBytes ?? 0) ?? .init()
                            let httpResponse = HTTPURLResponse(url: url.url, statusCode: Int(response.status.code), httpVersion: response.version.description, headerFields: response.headers.reduce(into: [:], { result, current in
                                result[current.name] = current.value
                                }))
                            completion(data, httpResponse, nil)
                        case .failure(let error): completion(nil,nil,error)
                    }
            }
        } catch {
            completion(nil,nil,error)
        }
    }
}

Usage for shared event loop:

func spider(for url: ScrappableURL) -> Spider {
    return HTTPClientSpider(eventLoopProvider: .shared(eventLoop))
}
Clone this wiki locally