Perfect-CURL

Get Involed with Perfect!

Star Perfect On Github Stack Overflow Follow Perfect on Twitter Join the Perfect Slack

Swift 4.0 Platforms OS X | Linux License Apache PerfectlySoft Twitter Slack Status

本软件包为 Swift 提供 curl 的支持。本软件包使用 Swift Package Manager 构建,并且是 Perfect 项目的一部分。

构建

请确保您已安装并激活最新的 Swift 4.0+ 工具链。

将此项目作为依赖项添加到您的 Package.swift 文件中。

.Package(url: "https://github.com/PerfectlySoft/Perfect-CURL.git", majorVersion: 3)

Linux 构建注意事项

请确保您已安装 libcurl。

sudo apt-get install libcurl4-openssl-dev

用法

import PerfectCURL

本软件包使用简单的请求/响应模型来访问 URL 内容。首先创建一个 CURLRequest 对象并根据您的需求进行配置,然后要求它执行请求并返回响应。响应由 CURLResponse 对象表示。

请求可以同步执行 - 阻塞调用线程直到请求完成;异步执行 - 将响应传递给回调函数;或者通过 Promise 对象执行 - 在后台线程上执行请求,让您能够链式执行其他任务、轮询或等待完成。

创建请求

可以使用 URL 和一系列选项创建 CURLRequest 对象,也可以创建空白对象,然后完全配置。CURLRequest 提供以下初始化器

open class CURLRequest {
	// Init with a url and options array.
	public convenience init(_ url: String, options: [Option] = [])
	/// Init with url and one or more options.
	public convenience init(_ url: String, _ option1: Option, _ options: Option...)
	/// Init with array of options.
	public init(options: [Option] = [])
}

可以使用 Array<Option> 或可变参数提供选项。也可以在执行请求之前将选项直接添加到 CURLRequest.options 属性。

配置请求

CURLRequest 选项由 CURLRequest.Option 枚举表示。每个枚举案例将具有零个或多个关联值,这些值指示特定选项的参数。例如,请求的 URL 可以使用选项 .url("https://httpbin.org/post") 指示。curl 提供的大多数选项都在 CURLRequest.Option 枚举中表示。可用选项的完整列表在本文档末尾附近给出。

POST 数据

POST 字段数据,包括文件上传,以与其他选项相同的方式添加到请求中。.postField(POSTField).postData([UInt8]).postString(String) 枚举案例将设置请求的 POST 内容。可以将 .postField 案例多次添加到请求中,因为每个实例代表一组名称/值对。.postData.postString 案例应被认为是与其他 post 案例互斥的,因为添加任何一个都将覆盖任何先前设置的 POST 内容。添加任何类型的 POST 内容数据将自动将 HTTP 方法设置为 POST。

CURLRequest.POSTField 结构定义如下

open class CURLRequest {
	public struct POSTField {
		/// Init with a name, value and optional mime-type.
		public init(name: String, value: String, mimeType: String? = nil)
		/// Init with a name, file path and optional mime-type.
		public init(name: String, filePath: String, mimeType: String? = nil)
	}
}

下面的示例创建了一个 POST 请求,并添加了几个名称/值对以及一个文件。执行的请求将自动具有 "multipart/form-data" 内容类型。

let json = try CURLRequest(url, .failOnError,
			               .postField(.init(name: "key1", value: "value1")),
			               .postField(.init(name: "key2", value: "value2")),
			               .postField(.init(name: "file1", filePath: testFile.path, mimeType: "text/plain")))
										.perform().bodyJSON

获取响应

要执行请求,请调用 CURLRequest.performCURLRequest.promise 函数之一。如果请求成功,您将获得一个 CURLResponse 对象,该对象可用于获取响应数据。如果请求失败,将抛出 CURLResponse.Error。如果无法连接、超时、收到格式错误的响应或收到 HTTP 响应的状态码等于或大于 400(当给定 .failOnError 选项时),则请求可能会失败。如果未给出 .failOnError 选项,则任何有效的 HTTP 响应都将成功,无论响应状态码如何。

用于获取响应的三个函数如下

public extension CURLRequest {
	/// Execute the request synchronously. 
	/// Returns the response or throws an Error.
	func perform() throws -> CURLResponse
	/// Execute the request asynchronously.
	/// The parameter passed to the completion callback must be called to obtain the response or throw an Error.
	func perform(_ completion: @escaping (CURLResponse.Confirmation) -> ())
	/// Execute the request asynchronously. 
	/// Returns a Promise object which can be used to monitor the operation.
	func promise() -> Promise<CURLResponse>
}

第一个 CURLRequest.perform 函数在调用线程上同步执行请求。函数调用将阻塞,直到请求成功或失败。失败时,将抛出 CURLResponse.Error

第二个 CURLRequest.perform 函数根据需要在后台线程上异步执行请求。传递给此函数的参数是一个回调,一旦请求完成或失败,将给出 CURLResponse.Confirmation。从回调中调用 confirmation 参数将返回 CURLResponse 或抛出 CURLResponse.Error

第三个函数 CURLRequest.perform 将返回一个 Promise<CURLResponse> 对象,该对象可用于链式执行进一步的活动并轮询或等待请求完成。与其他响应生成函数一样,如果发生错误,将抛出 CURLResponse.Error。有关 Promise 对象的常规信息,可以在 Perfect-Thread 文档中找到。

以下三个示例显示了每个函数的使用方式。每个函数都将执行一个请求,并将生成的响应正文从 JSON 转换为 [String:Any] 字典。

• 同步获取 API 终结点并从 JSON 解码

let url = "https://httpbin.org/get?p1=v1&p2=v2"
let json: [String:Any] = try CURLRequest(url).perform().bodyJSON

• 异步获取 API 终结点并从 JSON 解码

let url = "https://httpbin.org/post"
CURLRequest(url).perform {
	confirmation in
	do {
		let response = try confirmation()
		let json: [String:Any] = response.bodyJSON
		
	} catch let error as CURLResponse.Error {
		print("Failed: response code \(error.response.responseCode)")
	} catch {
		print("Fatal error \(error)")
	}
}

• 使用 Promise 异步获取 API 终结点并从 JSON 解码

let url = "https://httpbin.org/get?p1=v1&p2=v2"
if let json = try CURLRequest(url).promise().then { return try $0().bodyJSON }.wait() {
	...
}

根据效率排序的三个可用函数将按以下顺序排列

  1. 异步 perform
  2. 异步 promise
  3. 同步 perform

在高流量服务器上执行 CURL 请求时,建议使用异步响应函数之一。

重置请求

可以通过调用 .reset 函数,将 CURLRequest 对象重用于后续连接。重置请求将清除任何先前设置的选项,包括目标 URL。.reset 函数接受一个可选参数,用于重新配置请求的新选项。

重置声明如下

public extension CURLRequest {
	/// Reset the request. Clears all options so that the object can be reused.
	/// New options can be provided.
	func reset(_ options: [Option] = [])
	/// Reset the request. Clears all options so that the object can be reused.
	/// New options can be provided.
	func reset(_ option: Option, _ options: Option...)
}

重置请求将使任何先前执行的 CURLResponse 对象无效。应重新执行重新配置的请求以获得更新的 CURLResponse

响应数据

CURLResponse 对象提供对响应的内容正文的访问,可以作为原始字节、字符串或作为 JSON 解码的 [String:Any] 字典。此外,可以检索元信息,例如响应 HTTP 标头和状态码。

响应正文数据通过一系列只读 CURLResponse 属性提供

public extension CURLResponse {
	/// The response's raw content body bytes.
	public var bodyBytes: [UInt8]
	/// Get the response body converted from UTF-8.
	public var bodyString: String
	/// Get the response body decoded from JSON into a [String:Any] dictionary.
	/// Invalid/non-JSON body data will result in an empty dictionary being returned.
	public var bodyJSON: [String:Any]
  /// Get the response body decoded from JSON into a decodable structure
  /// Invalid/non-JSON body data will throw errors.
  public func bodyJSON<T: Decodable>(_ type: T.Type) throws -> T
}

可以通过调用 CURLResponse.get 函数之一并传入与所需数据对应的枚举值来检索其余的响应数据。指示这些值的枚举被分为三组,每组根据将返回的数据类型;String、Int 或 Double。枚举类型为 CURLResponse.Info.StringValueCURLResponse.Info.IntValueCURLResponse.Info.DoubleValue。此外,还提供了 get 函数,用于直接从响应中提取标头值。

public extension CURLResponse {
	/// Get an response info String value.
	func get(_ stringValue: Info.StringValue) -> String?
	/// Get an response info Int value.
	func get(_ intValue: Info.IntValue) -> Int?
	/// Get an response info Double value.
	func get(_ doubleValue: Info.DoubleValue) -> Double?
	/// Get a response header value. Returns the first found instance or nil.
	func get(_ header: Header.Name) -> String?
	/// Get a response header's values. Returns all found instances.
	func get(all header: Header.Name) -> [String]
}

为了方便起见,添加了属性,用于从响应中提取常用的请求数据,例如 urlresponseCode

以下示例显示了如何从请求中提取标头和其他元数据

// get the response code
let code = response.get(.responseCode)
// get the response code using the accessor
let code = response.responseCode
// get the "Last-Modified" header from the response
if let lastMod = response.get(.lastModified) {
	...
}

失败

发生故障时,将抛出 CURLResponse.Error 对象。此对象提供生成的 CURL 错误代码(注意,这与任何 HTTP 响应代码都不同,并且是 CURL 特有的)。它还提供对错误消息字符串的访问,以及可用于进一步查询生成错误的 CURLResponse 对象。

请注意,根据请求上设置的选项,在发生错误后获得的响应对象可能没有任何关联的内容正文数据。

CURLResponse.Error 定义如下

open class CURLResponse {
	/// An error thrown while retrieving a response.
	public struct Error: Swift.Error {
		/// The curl specific request response code.
		public let code: Int
		/// The string message for the curl response code.
		public let description: String
		/// The response object for this error.
		public let response: CURLResponse
	}
}

CURLRequest.Option 列表

以下是可以设置的众多 CURLRequest 选项的列表。每个枚举案例指示选项的参数类型。这些枚举值可以在创建新的 CURLRequest 对象时使用,也可以通过将它们添加到现有对象的 .options 数组属性来使用。

CURLRequest.Option 枚举案例 描述
.url(String) 请求的 URL。
.port(Int) 覆盖请求的端口。
.failOnError 在 http 错误代码 >= 400 时失败。
.userPwd(String) 冒号分隔的用户名/密码字符串。
.proxy(String) 代理服务器地址。
.proxyUserPwd(String) 代理服务器用户名/密码组合。
.proxyPort(Int) 代理服务器的端口覆盖。
.timeout(Int) 请求完成的最长时间(以秒为单位)。默认超时时间为从不超时。
.connectTimeout(Int) 请求连接阶段的最长时间(以秒为单位)。默认超时时间为 300 秒。
.lowSpeedLimit(Int) 传输的平均速度(以字节/秒为单位),在该速度之下,传输应在 .lowSpeedLimit 秒内低于此速度,请求才会太慢而被中止。
.lowSpeedTime(Int) 传输速度应低于 .lowSpeedLimit 的时间(以秒为单位),在该时间之后,请求将被认为太慢而被中止。
.range(String) 范围请求值,格式为 "X-Y",其中 X 或 Y 可以省略,X 和 Y 是字节索引
.resumeFrom(Int) 请求应从该偏移量(以字节为单位)开始。
.cookie(String) 为请求设置一个或多个 cookie。应采用 "name=value" 格式。用分号分隔多个 cookie:"name1=value1; name2=value2"。
.cookieFile(String) 保存请求的 cookie 数据的文件的名称。
.cookieJar(String) 将接收到的 cookie 写入的文件名。
.followLocation(Bool) 指示请求应遵循重定向。默认为 false。
.maxRedirects(Int) 请求应遵循的最大重定向数。默认为无限制。
.maxConnects(Int) 可以为请求缓存的最大同时打开的持久连接数。
.autoReferer(Bool) 启用后,当请求遵循 Location: 重定向时,请求将自动在 HTTP 请求中设置 Referer: 标头字段
.krbLevel(KBRLevel) 设置 FTP 的 kerberos 安全级别。值应为以下值之一:.clear、.safe、.confidential 或 .private。
.addHeader(Header.Name, String) 向请求添加标头。
.addHeaders([(Header.Name, String)]) 向请求添加一系列标头。
.replaceHeader(Header.Name, String) 添加或替换标头。
.removeHeader(Header.Name) 删除默认内部添加的标头。
.sslCert(String) 客户端 SSL 证书的路径。
.sslCertType(SSLFileType) 指定客户端 SSL 证书的类型。默认为 .pem
.sslKey(String) 客户端私钥文件的路径。
.sslKeyPwd(String) 如果 SSL 密钥文件受密码保护,则要使用的密码。
.sslKeyType(SSLFileType) 指定 SSL 私钥文件的类型。
.sslVersion(TLSMethod) 强制请求使用特定版本的 TLS 或 SSL。
.sslVerifyPeer(Bool) 指示请求是否应验证对等方的证书的真实性。
.sslVerifyHost(Bool) 指示请求是否应验证服务器证书是否用于它所知的服务器。
.sslCAFilePath(String) 包含一个或多个证书的文件的路径,这些证书将用于验证对等方。
.sslCADirPath(String) 包含一个或多个证书的目录的路径,这些证书将用于验证对等方。
.sslCiphers([String]) 覆盖用于 SSL 连接的密码列表。由一个或多个以冒号分隔的密码字符串组成。逗号或空格也可以作为分隔符,但通常使用冒号。“!”、“-” 和 “+” 可以用作运算符。
.sslPinnedPublicKey(String) 指向已固定的公钥的文件路径。在协商 TLS 或 SSL 连接时,服务器会发送一个证书来表明其身份。从该证书中提取一个公钥,如果该公钥与提供给此选项的公钥不完全匹配,curl 将在发送或接收任何数据之前中止连接。
.ftpPreCommands([String]) 在文件传输之前要运行的 (S)FTP 命令列表。
.ftpPostCommands([String]) 在文件传输之后要运行的 (S)FTP 命令列表。
.ftpPort(String) 指定主动 FTP 传输的本地连接端口。
.ftpResponseTimeout(Int) 请求等待 FTP 服务器响应的秒数。
.sshPublicKey(String) 用于 SSH 连接的公钥文件路径。
.sshPrivateKey(String) 用于 SSH 连接的私钥文件路径。
.httpMethod(HTTPMethod) 用于请求的 HTTP 方法。
.postField(POSTField) 向请求添加单个 POST 字段。通常,为一个请求添加多个 POST 字段。
.postData([UInt8]) 用于 POST 请求的原始字节数据。
.postString(String) 用于 POST 请求的原始字符串数据。
.mailFrom(String) 执行 SMTP 请求时,指定发送者的地址。
.mailRcpt(String) 执行 SMTP 请求时,指定接收者。可以通过多次使用此选项来指定多个接收者。

CURLResponse.Info 列表

以下列表描述了与 CURLResponse.get 函数一起使用的 CURLResponse.Info 用例,以检索响应信息。 这些列表根据将返回的数据类型进行分组; 分别为 StringValueIntValueDoubleValue

CURLResponse.Info.StringValue 枚举用例 描述
.url 请求/响应的有效 URL。 这最终是响应数据来源的 URL。 在重定向的情况下,这可能与请求的 URL 不同。
.ftpEntryPath 在登录到 FTP 服务器后,请求最终到达的初始路径。
.redirectURL 请求本来会重定向到的 URL。
.localIP 请求最近使用的本地 IP 地址。
.primaryIP 请求最近连接到的远程 IP 地址。
.contentType 请求的内容类型。 这是从 "Content-Type" 标头读取的。
CURLResponse.Info.IntValue 枚举用例 描述
.responseCode 最后接收到的 HTTP、FTP 或 SMTP 响应代码。
.headerSize 所有接收到的标头的总大小(以字节为单位)。
.requestSize 发出的请求的总大小(以字节为单位)。 在重定向的情况下,这将指示所有已发送请求的累计总数。
.sslVerifyResult SSL 证书验证的结果。
.redirectCount 已跟踪的重定向总数。
.httpConnectCode 最后收到的对 CONNECT 请求的 HTTP 代理响应代码。
.osErrno 可能触发失败的操作系统级别的 errno。
.numConnects 请求必须进行的连接数才能生成响应。
.primaryPort 请求最近连接到的远程端口。
.localPort 请求最近使用的本地端口。
CURLResponse.Info.DoubleValue 枚举用例 描述
.totalTime 先前请求的总时间(以秒为单位)。
.nameLookupTime 从开始到名称解析完成的总时间(以秒为单位)。
.connectTime 从开始到与远程主机或代理的连接完成的总时间(以秒为单位)。
.preTransferTime 从开始到文件传输即将开始所花费的时间(以秒为单位)。
.sizeUpload 已上传的总字节数。
.sizeDownload 已下载的总字节数。
.speedDownload 平均下载速度(以字节/秒为单位)。
.speedUpload 平均上传速度(以字节/秒为单位)。
.contentLengthDownload 下载的内容长度。 此值是从 Content-Length 标头字段获取的。
.contentLengthUpload 指定的上传大小。
.startTransferTime 从请求开始到收到第一个字节所花费的时间(以秒为单位)。
.redirectTime 在最终事务开始之前,所有重定向步骤(包括名称查找、连接、预传输和传输)所花费的总时间(以秒为单位)。
.appConnectTime 从开始到完成与远程主机的 SSL/SSH 连接/握手所花费的时间(以秒为单位)。

更多信息

有关 Perfect 项目的更多信息,请访问 perfect.org