# HTTP/HTTPS ***Copyright © Quectel Wireless Solutions Co., Ltd. 2026. All rights reserved.*** --- # 概述 本章节介绍HTTP协议的基本概念、要素、流程和消息结构,帮助开发者理解HTTP在嵌入式/物联网场景中的应用基础。 ## HTTP/HTTPS简介 超文本传输协议(Hyper Text Transfer Protocol,HTTP)是一个简单的客户端发送请求,服务器进行响应的应用层协议,它通常运行在 [TCP]() 之上,指定了客户端可能发送给服务器什么样的消息以及得到什么样的响应,主要用于在 Web 浏览器和网站服务器之间传递信息。 在嵌入式/物联网场景中,HTTP常见用途: - **GET**:从服务器上获取数据。 - **POST**:提交数据到服务器上,多次执行同一个POST会在服务器上生成多个副本。 - **PUT**:提交数据到服务器上,会替换服务器上的资源。 - **PATCH**:对资源进行部分更新。它的主要特点是只更新资源的部分属性,而不是像PUT那样替换整个资源。 ### 基本要素 HTTP协议包括以下几个基本要素: 1. **协议版本**:HTTP协议经历了多个版本的演进,包括HTTP/0.9、HTTP/1.0、HTTP/1.1、HTTP/2 和 HTTP/3,每个版本都有其特定的特性和改进。 2. **请求/响应模型**:HTTP采用请求/响应交互模型,当用户在浏览器输入URL后,浏览器会向服务器发送请求,服务器处理后返回响应,具体包括DNS解析、TCP连接建立、HTTP请求与服务器响应等步骤。 3. **HTTP方法**:定义了多种请求方法来表示不同的操作意图,常见的有GET、POST、PUT、PATCH、HEAD、DELETE和OPTIONS等。 4. **状态码**:服务器通过状态码告知客户端请求的处理结果,分为1xx(信息性)、2xx(成功)、3xx(重定向)、4xx(客户端错误)和 5xx(服务器错误)五大类。 5. **报文结构**:HTTP报文由起始行、头部字段、空行和可选的主体组成。请求报文和响应报文的结构略有不同,但都遵循这一基本格式。 6. **媒体类型支持**:HTTP支持传输多种类型的资源,通过Content-Type标头来标识,如text/html、application/json、image/jpeg 等。 7. **连接方式**: 1. 非持久性连接(HTTP/1.0 及之前):每次发起一个请求就创建新连接,传输完毕后释放,效率较低。 1. 持久性连接(HTTP/1.1 及之后):一个连接可处理多次请求/响应,减少连接建立/释放开销,提升效率。 8. **无状态性**:HTTP协议本身不记录客户端访问状态,每个请求独立处理。 9. **安全性**:HTTP以明文方式传输数据,不提供加密,安全性较差,不适合传输敏感信息(如密码、信用卡号等)。为解决此问题,HTTPS(基于SSL/TLS加密的HTTP)被开发出来,通过证书验证和数据加密保障通信安全。 10. **基于 TCP 协议**:HTTP通常基于TCP协议来保证数据传输的可靠性,默认使用端口80 (HTTP) 或443 (HTTPS)。 ### 流程概述 HTTP流程是指网络设备通过蜂窝网络发送HTTP请求并接收服务器响应的端到端过程。这个过程的核心在于HTTP请求包的组包以及对HTTP回应包的接收解析,确保数据交互的准确。 HTTP 的流程通常包含以下几个步骤: 1. **qurl 初始化** - `qurl_global_init()` qurl库全局初始化 - `qurl_core_create()` 创建一个新的qurl实例 2. **配置qurl相关参数** - `qurl_core_setopt()` 为给定的qurl实例设置选项 3. **配置TLS相关参数(选配)** - `qurl_tls_cfg_init()` qurl使用的TLS参数初始化为默认值 - 配置TLS相关的参数 - 通过 `qurl_core_setopt()` 和 `QURL_OPT_TLS_CFG()` 配置TLS相关的参数 4. **发起HTTP请求** - `qurl_core_perform()` 开始执行阻塞式的网络传输 - 外部等待退出阻塞后执行qurl去初始化即可,所有数据交互都在回调中执行 5. **激活网络** - 等待注网成功 - PDP通道激活 6. **DNS域名解析** 7. **发起TCP连接** - 创建socket - 绑定local地址 - 发起socket连接与HTTP服务器进行三次握手 8. **组装并发送的HTTP请求header** - 如果配置了自定义请求header(`QURL_OPT_UPLOAD_HEAD_RAW()` 使能)。有三种方式去获取自定义的HTTP请求头: - `QURL_OPT_READ_HEAD_CB()` 通过回调函数去获取请求header。 - `QURL_OPT_UPLOAD_HEAD_DATA()` 通过地址去获取请求header。 - `QURL_OPT_UPLOAD_HEAD_FILE()` 通过文件名去获取请求header。 - 如果没有配置自定义请求header,使用模组默认的HTTP头信息,结合用户设置的请求头(`QURL_OPT_HTTP_HEADER()` 设置),组装发送请求header。 9. **读取并发送HTTP请求body** - 有三种方式获取HTTP请求body: - `QURL_OPT_READ_CB()` 通过回调函数去获取请求body。 - `QURL_OPT_UPLOAD_DATA()` 通过指针去获取请求body。 - `QURL_OPT_UPLOAD_FILE()` 通过文件名去获取请求body。 10. **读取并解析HTTP回应header** - 通过 `QURL_OPT_WRITE_HEAD_CB()` 配置处理HTTP回应header的回调函数,在回调函数内部可以通过 `qurl_core_getinfo()` 获取HTTP响应header中的相关信息。 11. **读取HTTP回应body** - 通过 `QURL_OPT_WRITE_CB()` 配置处理HTTP回应body的回调函数,客户可以自行在回调函数中对接收到的HTTP回应body数据进行处理。通常会不断调用回调函数将收到HTTP响应body保存到文件系统或者内存。 12. **HTTP请求完成,模块主动断开TCP连接** - 是否在数据传输完成后立即自动断开连接与 `QURL_OPT_REUSE_HOLD()` 的配置相关。默认使用后不去立即关闭连接,通过复用qurl实例以减少连接开销。 13. **qurl去初始化** - `qurl_slist_del_all()` 释放之前申请的单向链表 - `qurl_core_delete()` 删除之前创建的qurl实例 - `qurl_global_deinit()` qurl库全局去初始化 ```{image} images/board_Q6ttwLrr3hlrTLbAyKWcNGPxntd.jpg :width: 794px :height: 1845px :align: center ``` ### 消息结构 HTTP消息结构主要分为 **请求消息** 和 **响应消息** 两部分。以下使用Mermaid图表直观展示其基本结构: #### HTTP请求消息结构 ```{figure} images/board_AXt5wtMM8hExX8bvniNcMwBpnDc.jpg :align: center :alt: image ``` #### HTTP响应消息结构 ```{figure} images/board_GZUWwHZ4HhXci5b9MIPchuNDngh.jpg :align: center :alt: image ``` #### 说明 HTTP消息由起始行(请求行或状态行)、头部字段和可选的消息体组成,各部分之间用空行(CRLF)分隔。 请求行包含HTTP方法、请求URI和HTTP版本,而状态行包含HTTP版本、状态码和状态短语。 - **起始行**:请求消息为请求行(包含方法、URI、协议版本);响应消息为状态行(包含协议版本、状态码、原因短语)。 - **头部字段**:零个或多个键值对,每行一个字段,用于传递元数据。 - **空行**:CRLF(\r\n)分隔头部和主体,表示头部结束。 - **消息体**:可选,用于携带实际数据(如POST请求的表单或响应中的HTML/JSON)。GET/HEAD等方法通常无主体。 ## qurl 库简介 模组的HTTP功能是基于底层的qurl库去实现的。 qurl是一个易于使用的客户端URL传输库,参考libcurl库的实现,支持多种协议来实现简单的短连接的服务,目前支持 HTTP(S)、FTP(S)、SMTP(S) 等服务。它支持在多种操作系统上运行,如RTOS、Windows、Linux、macOS等。qurl提供了一套简单的API,使开发者能够更方便地发起网络传输,处理网络响应等。 qurl 是简化版的curl,提供了基本的网络协议栈功能,忽略了一些复杂以及不常用的配置项,相较于libcurl,qurl提供了一种轻量级的实现,以降低内存和CPU的使用率,适用于内存受限的模组平台。 开发者无需关心内部实现,只需要对qurl进行初始化,并创建一个qurl实例,为qurl实例设置选项,发起网络传输。然后通过配置的回调来处理网络的响应。 # API说明 ## 头文件 *qurl.h* ## 函数列表 | **函数** | **描述** | | --- | --- | | *qurl_global_init()* | qurl库全局初始化 | | *qurl_core_create()* | 创建一个新的qurl实例 | | *qurl_core_reset()* | 重置之前已经创建好的qurl实例的所有选项 | | *qurl_core_setopt()* | 为给定的qurl实例设置选项 | | *qurl_slist_add_strdup()* | 拷贝字符串的方式插入链表 | | *qurl_tls_cfg_init()* | qurl相关的tls参数初始化 | | *qurl_core_perform()* | 开始执行阻塞式的网络传输 | | *qurl_core_abort()* | 提前终止网络传输流程 | | *qurl_core_getinfo()* | 获取给定的qurl实例的信息 | | *qurl_slist_del_all()* | 释放整个单向链表 | | *qurl_core_get_last_close_event()* | 获取qurl实例最后关闭的原因 | | *qurl_core_delete()* | 删除之前创建的qurl实例 | | *qurl_global_deinit()* | qurl库全局去初始化 | ## qurl API 函数 本章节详细说明qurl库提供的API函数,包括初始化、控制操作、获取信息、去初始化以及选项配置。每个函数包括原型、功能描述、参数说明、返回值、使用注意事项和示例。 ### qurl初始化 #### qurl_global_init ##### **函数原型** ```c qurl_ecode_t qurl_global_init(void); ``` ##### **功能描述** 此函数会进行qurl库全局初始化。用于设置qurl所需的程序环境。可将其视为库的加载器。 ##### **参数说明** 无 ##### **返回值说明** 成功返回 `QURL_OK` ,否则返回 `qurl_ecode_t` 枚举值(详见附录)。 ```{note} **注意:** 1. 在程序调用qurl中的任何其他函数之前,必须在程序内至少调用一次此函数。 2. 它设置的环境主要指的是库运行必要的资源的创建以及公共的全局变量的初始化,它创建的资源在整个程序运行周期内是不变的,并且对于每个qurl实例都是相同的,因此调用多次或者一次效果是相同的。 3. 该API线程不安全。需要外部调用的时候去保证线程安全。当有其他线程正在运行某个qurl实例的时候,不能调用此函数。 ``` ##### **示例** ```c int main(void) { qurl_global_init(); /* use qurl, then before exiting... */ qurl_global_deinit(); } ``` #### qurl_core_create ##### **函数原型** ```c qurl_ecode_t qurl_core_create(qurl_core_t *core_ptr); ``` ##### **功能描述** 此函数会创建一个新的qurl实例,并通过 `core_ptr` 返回一个qurl实例的操作句柄。 对这个qurl实例进行操作的时候需要使用到这个句柄作为输入参数。 ##### **参数说明** | **参数名** | **类型** | **是否必填** | **范围/单位** | **说明** | | --- | --- | --- | --- | --- | | core_ptr | `qurl_core_t *` | 是 | 无 | qurl 实例的操作句柄 | ##### **返回值说明** 成功返回 `QURL_OK` ,否则返回 `qurl_ecode_t` 枚举值(详见附录)。 ```{note} **注意:** 1. 操作完成后,必须调用 `qurl_core_delete()` 删除对应的qurl实例。 2. qurl实例的操作句柄用于保持和控制网络传输,建议使用同一个qurl实例进行多次网络传输。如果下一次网络传输需要重新配置选项,必须调用 `qurl_core_reset()` 重置qurl实例并进行新的选项配置。 3. 调用 `qurl_core_create()` 之前必须调用过一次 `qurl_global_init()`。 4. 如果调用 `qurl_core_create()` 返回其他 `qurl_ecode_t` 枚举类型的值,表示创建qurl实例的时候出了问题,请不要使用该qurl实例的操作句柄执行其他qurl函数。 ``` ##### **示例** ```c int main(void) { qurl_core_t core = QOSA_NULL; qurl_global_init(); if (QURL_OK == qurl_core_create(&core)) { if (QURL_OK == qurl_core_setopt(core, QURL_OPT_URL, "https://example.com")) { qurl_core_perform(core); } qurl_core_delete(core); } qurl_global_deinit(); } ``` #### qurl_tls_cfg_init ##### **函数原型** ```c void qurl_tls_cfg_init(qurl_tls_cfg_t *tls_cfg_ptr); ``` ##### **功能描述** 该函数用于qurl相关的TLS参数初始化。 使用TLS的连接需要调用 `qurl_tls_cfg_init()` 对 TLS 的配置进行初始化,并使用配置项 `QURL_OPT_TLS_CFG()` 将最终的TLS参数配置到qurl内部。 ##### **参数说明** | **参数名** | **类型** | **是否必填** | **范围/单位** | **说明** | | --- | --- | --- | --- | --- | | tls_cfg_ptr | `qurl_tls_cfg_t *` | 是 | 无 | TLS 配置参数 | ##### **返回值说明** 无 ##### **示例** ```c int main(void) { qurl_core_t core = QOSA_NULL; qurl_tls_cfg_t tls_cfg = {0}; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://112.31.84.164:8301/9X07-QAT/1K.txt"); qurl_tls_cfg_init(&tls_cfg); tls_cfg.negotiate_timeout = 30; qurl_core_setopt(core, QURL_OPT_TLS_CFG, &tls_cfg); qurl_core_perform(core); } } ``` ### qurl控制操作 #### qurl_core_reset ##### **函数原型** ```c qurl_ecode_t qurl_core_reset(qurl_core_t core); ``` ##### **功能描述** 该函数用于重置之前已经创建好的qurl实例,将之前在指定qurl实例上设置的所有选项重新初始化为默认值。 这将使qurl实例恢复到使用 `qurl_core_create()` 创建时的状态。 ##### **参数说明** | **参数名** | **类型** | **是否必填** | **范围/单位** | **说明** | | --- | --- | --- | --- | --- | | core | `qurl_core_t` | 是 | 无 | qurl 实例的操作句柄 | ##### **返回值说明** 成功返回 `QURL_OK` ,否则返回 `qurl_ecode_t` 枚举值(详见附录)。 ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { /* ... the core is used and options are set ... */ qurl_core_reset(core); } } ``` #### qurl_slist_add_strdup ##### **函数原型** ```c qurl_slist_t qurl_slist_add_strdup(qurl_slist_t slist, char *str); ``` ##### **功能描述** 该函数用于拷贝字符串插入到字符串链表中。 应将现有列表作为第一个参数传递,并从此函数返回新列表。我们一开始在 `slist` 参数中传入 `QOSA_NULL` 以创建一个新的列表。此函数返回时,已附加指定的字符串。`qurl_slist_add_strdup` 内部会自动去复制字符串。 ##### **参数说明** | **参数名** | **类型** | **是否必填** | **范围/单位** | **说明** | | --- | --- | --- | --- | --- | | slist | `qurl_slist_t` | 是 | 无 | 要插入的单向链表 | | str | `char *` | 是 | 无 | 要添加的字符串 | ##### **返回值说明** 如果成功则返回插入完成的新链表,如果失败返回NULL。 ```{note} **注意:** 1. 调用 `qurl_core_setopt()` 将 `slist` 链表配置到qurl内部,使用之后应该调用 `qurl_slist_del_all()` 释放整个单向链表。 2. 为了避免在失败时返回空覆盖现有的非空列表,建议将新列表返回给一个临时变量,该变量可以在更新原始列表指针之前判断NULL。 ``` ##### **示例** ```c int main(void){ qurl_slist_t headers = QOSA_NULL; qurl_slist_t temp = QOSA_NULL; qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { headers = qurl_slist_add_strdup(headers, "Content-Type: 05"); if(headers == QOSA_NULL) { return -1; } temp = qurl_slist_add_strdup(headers, "Accept-Charset: ascii"); if(temp == QOSA_NULL) { qurl_slist_del_all(headers); return -1; } headers = temp; qurl_core_setopt(core, QURL_OPT_HTTP_HEADER, headers); qurl_core_perform(core); qurl_slist_del_all(headers); } } ``` #### qurl_slist_del_all ##### **函数原型** ```c void qurl_slist_del_all(qurl_slist_t list); ``` ##### **功能描述** 该函数用于释放整个单向链表。 它会删除先前 `qurl_slist_add_strdup()` 构建的链表的所有痕迹。 ##### **参数说明** | **参数名** | **类型** | **是否必填** | **范围/单位** | **说明** | | --- | --- | --- | --- | --- | | slist | `qurl_slist_t` | 是 | 无 | 要释放的单向链表 | ##### **返回值说明** 无 ```{note} **注意:** 1. 对list传递 `QOSA_NULL` 指针会使此函数立即返回而不执行任何操作。 2. 调用此函数并返回后对链表的任何使用都是非法的。 ``` ##### **示例** ```c int main(void){ qurl_slist_t headers = QOSA_NULL; qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { headers = qurl_slist_add_strdup(headers, "Content-Type: 05"); if(headers == QOSA_NULL) { return -1; } qurl_core_setopt(core, QURL_OPT_HTTP_HEADER, headers); qurl_core_perform(core); qurl_slist_del_all(headers); } } ``` #### qurl_core_perform ##### **函数原型** ```c qurl_ecode_t qurl_core_perform(qurl_core_t core); ``` ##### **功能描述** 该函数用于执行阻塞式的网络传输,并在完成时返回,如果失败,则更早返回。 我们可以使用相同的qurl实例的操作句柄对 `qurl_core_perform()` 进行任意数量的调用。如果您打算传输多个文件,我们鼓励您这样做。在接下来的网络传输中qurl库会尝试重用现有的连接,从而使操作更快、CPU 占用更少、使用更少的网络资源。您可能希望在调用之间使用 `qurl_core_setopt()` 来设置以下 `qurl_core_perform()` 调用的选项。我们可能需要在两次调用 `qurl_core_perform()` 之间调用 `qurl_core_setopt()` 来进行新的选项配置。 网络传输将数据传输到对等端或从对等端获取数据。应用程序通过配置 `QURL_OPT_WRITE_HEAD_CB`,`QURL_OPT_WRITE_CB` 和 `QURL_OPT_WRITE_CB_ARG` 选项来告诉qurl我们应用程序要如何接收数据。要告诉qurl我们应用程序要发送什么数据,有几种选择,其中常见的组合是 `QURL_OPT_READ_CB`,`QURL_OPT_READ_CB_ARG` 和 `QURL_OPT_UPLOAD_SIZE`。 ##### **参数说明** | **参数名** | **类型** | **是否必填** | **范围/单位** | **说明** | | --- | --- | --- | --- | --- | | core | `qurl_core_t` | 是 | 无 | qurl实例的操作句柄 | ##### **返回值说明** 成功返回 `QURL_OK` ,否则返回 `qurl_ecode_t` 枚举值(详见附录)。 ```{note} **注意:** 1. 在 `qurl_core_create()` 和所有 `qurl_core_setopt()` 调用完成后再调用此函数,它将按照选项中的描述执行传输。必须使用和 `qurl_core_create()` 返回的相同的qurl实例的操作句柄作为输入来调用它。 2. 我们要避免使用相同的qurl实例的操作句柄从两个地方同时调用此函数。让函数在下次调用之前先返回。如果你想要并行传输,你必须使用多个qurl实例。 ``` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; qurl_ecode_t ret = QURL_OK; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); ret = qurl_core_perform(core); qurl_core_delete(core); } } ``` #### qurl_core_abort ##### **函数原型** ```c qurl_ecode_t qurl_core_abort(qurl_core_t *core); ``` ##### **功能描述** 该函数可以提前终止网络传输流程。 使用此函数,我们可以终止正在运行的连接。 ##### **参数说明** | **参数名** | **类型** | **是否必填** | **范围/单位** | **说明** | | --- | --- | --- | --- | --- | | core | `qurl_core_t *` | 是 | 无 | qurl实例的操作句柄 | ##### **返回值说明** 成功返回 `QURL_OK`,否则返回 `qurl_ecode_t` 枚举值(详见附录)。 ```{note} **注意:** 我们可以在其他线程执行此函数去终止网络传输流程。与大多数其他qurl函数不同,我们也可以在回调函数中调用 `qurl_core_abort()`。 ``` ##### **示例** ```c int thread1(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { if (QURL_OK == qurl_core_setopt(core, QURL_OPT_URL, "https://example.com")) { qurl_core_perform(core); } qurl_core_delete(core); } } //在其他线程或者配置的回调函数里执行 qurl_core_abort int thread2(qurl_core_t *core){ if (core != QOSA_NULL) { qurl_core_abort(core); } } ``` ### 获取qurl信息 #### qurl_core_getinfo ##### **函数原型** ```c qurl_ecode_t qurl_core_getinfo(qurl_core_t core, qurl_info_e opt, ...); ``` ##### **功能描述** 该函数可以获取保存在qurl实例中的信息。 如果您想获取与传输相关的数据,请在执行传输后使用此函数。 ##### **参数说明** | **参数名** | **类型** | **是否必填** | **范围/单位** | **说明** | | --- | --- | --- | --- | --- | | core | `qurl_core_t` | 是 | 无 | qurl实例的操作句柄 | | opt | `qurl_info_e` | 是 | 参考 `qurl_info_e` 枚举 | 对应要获取的信息选项 | ##### **返回值说明** 成功返回 `QURL_OK`,否则返回 `qurl_ecode_t` 枚举值(详见附录)。 ```{note} **注意:** 1. 第三个参数必须指向所用选项的特定类型。 2. 数据会相应地存储在第三个参数中而且只有当此函数返回 `QURL_OK` 时得到的信息才是正确的。 3. 当第三个参数传入的是指针的指针的时候,函数内部会进行内存的拷贝,所以外部使用完成之后必须自行释放对应的内存。 ``` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; long resp_code = 0; //响应状态码 if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); if (QURL_OK == qurl_core_perform(core)) { if (QURL_OK == qurl_core_getinfo(core, QURL_INFO_RESP_CODE, &resp_code)) { printf("We received http resp_code: %d\n", resp_code); } } qurl_core_delete(core); } } ``` #### qurl_core_get_last_close_event ##### **函数原型** ```c int qurl_core_get_last_close_event(qurl_core_t core); ``` ##### **功能描述** 该函数可以获取qurl实例最后关闭的原因。 ##### **参数说明** | **参数名** | **类型** | **是否必填** | **范围/单位** | **说明** | | --- | --- | --- | --- | --- | | core | `qurl_core_t` | 是 | 无 | qurl实例的操作句柄 | ##### **返回值说明** 正常关闭返回 `CLOSE_EVENT_NORMAL`,否则返回 `close_event_t` 枚举值(详见附录)。 ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; qurl_ecode_t ret = QURL_OK; int http_last_close_event = 0; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); ret = qurl_core_perform(core); http_last_close_event = qurl_core_get_last_close_event(core); printf("http_last_close_event : %d\n", http_last_close_event); qurl_core_delete(core); } } ``` ### qurl去初始化 #### qurl_core_delete ##### **函数原型** ```c qurl_ecode_t qurl_core_delete(qurl_core_t core); ``` ##### **功能描述** 该函数用于删除之前创建的qurl实例。 此函数与 `qurl_core_create()` 相反。它关闭并释放之前创建的qurl实例相关的所有资源。 ##### **参数说明** | **参数名** | **类型** | **是否必填** | **范围/单位** | **说明** | | --- | --- | --- | --- | --- | | core | `qurl_core_t` | 是 | 无 | qurl实例的操作句柄 | ##### **返回值说明** 成功返回 `QURL_OK` ,否则返回 `qurl_ecode_t` 枚举值(详见附录)。 ```{note} **注意:** 1. 此调用将关闭这个qurl实例使用过的所有连接。如果您打算进行更多的网络传输,请不要调用此函数,重复使用同一个qurl实例是qurl获得良好性能的关键。 2. 在调用此函数并返回后再去使用这个qurl实例是非法的。 3. 在句柄中传递 QOSA_NULL 指针会使此函数立即返回而不执行任何操作。 ``` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { if (QURL_OK == qurl_core_setopt(core, QURL_OPT_URL, "https://example.com")) { qurl_core_perform(core); } qurl_core_delete(core); } } ``` #### qurl_global_deinit ##### **函数原型** ```c qurl_ecode_t qurl_global_deinit(void); ``` ##### **功能描述** 该函数用于qurl库全局去初始化。 释放 `qurl_global_init()` 获取的资源。 ##### **返回值说明** 成功返回 `QURL_OK`,否则返回 `qurl_ecode_t` 枚举值(详见附录)。 ```{note} **注意:** 1. 当我们开始使用qurl,每调用一次 `qurl_global_init()`,就应该调用一次 `qurl_global_deinit()`。当调用到对应次数的 `qurl_global_deinit()`,才会真正去释放 `qurl_global_init()` 获取的资源。 2. 该API线程不安全。需要外部调用的时候去保证线程安全。当有其他线程正在运行某个qurl实例的时候,不能调用此函数。 3. 调用 `qurl_global_deinit()` 不会等待所有qurl实例运行完才去执行,这样可能会出现dump或者其他问题,所以不建议随意调用 `qurl_global_deinit()` 去初始化。 ``` ##### **示例** ```c int main(void){ qurl_global_init(); /* use qurl, then before exiting... */ qurl_global_deinit(); } ``` ### qurl相关选项配置 #### qurl_core_setopt ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, qurl_opt_e opt, ...); ``` ##### **功能描述** 该函数用于为给定的qurl实例设置选项。 它告诉qurl该如何表现。通过设置适当的选项,应用程序可以更改qurl的行为。 所有选项都是用一个选项跟上后面的可变参数来设置的。该参数可以是long、字符串类型、函数指针、结构体指针等,具体取决于特定选项的预期。 ##### **参数说明** | **参数名** | **类型** | **是否必填** | **范围/单位** | **说明** | | --- | --- | --- | --- | --- | | core | `qurl_core_t` | 是 | 无 | qurl 实例的操作句柄 | | opt | `qurl_opt_e` | 是 | 参考 `qurl_opt_e` 枚举 | qurl 配置选项 | ##### **返回值说明** 成功返回 `QURL_OK` ,否则返回 `qurl_ecode_t` 枚举值(详见附录)。 ```{note} **注意:** 1. 请仔细阅读本文档的 `opt` 介绍,因为错误的输入值可能会导致qurl表现不佳。 2. 在每个函数调用中只能设置一个选项。典型的应用程序在设置阶段会调用很多次 `qurl_core_setopt()`。 3. `core` 是 `qurl_core_create()` 调用返回的操作句柄。 4. 使用此函数设置的选项具有粘性。多次调用 `qurl_core_setopt()` 配置新的选项之后,之前配置的选项依旧存在。当我们执行下一次网络传输的时候,这些配置项也不会自动重置,如果我们下一次网络传输需要不同的配置,那么必须在传输之间进行更改。也可以选择使用 `qurl_core_reset()` 将所有选项重置回内部默认值。 5. 设置选项的顺序不会影响 `qurl_core_perform()` 执行的结果。 6. 传入 `qurl_core_setopt()` 的char类型字符串,会在qurl内部进行拷贝,在 `qurl_core_setopt()` 返回后,上层的字符串内存即可修改或释放。qurl几乎不验证输入的字符串内容。但是我们尽量不要去输入特殊字符,可能会引发意想不到的结果。 ``` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { if (QURL_OK == qurl_core_setopt(core, QURL_OPT_URL, "https://example.com")) { qurl_core_perform(core); } qurl_core_delete(core); } } ``` ## qurl 配置项详解 本章节详细说明qurl配置项,包括通用配置项、中间件配置项、Socket 配置项、TLS 配置项和HTTP协议配置项。每个配置项包括函数原型、功能描述、默认值、适用协议、依赖项、使用注意事项和示例。 ### 通用配置项 #### QURL_OPT_URL 配置发起网络传输使用到的 URL ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_URL, char *URL); ``` ##### **功能描述** 传递要使用的URL的指针。 传递指向要使用的URL的指针。参数应该是一个以null结尾的char类型的字符串,该字符串必须以以下格式进行URL编码: `scheme://host:port/path` 有关格式的更多解释,请参阅RFC 3986。 qurl在网络传输开始之前不会验证语法或使用URL。即使您在此处设置了一个疯狂的值,`qurl_core_setopt()` 仍可能返回 `QURL_OK`。 qurl不支持根据URL中的IP地址自动猜测出scheme以及默认scheme,所以URL必须带上scheme,如果给定的URL缺少scheme名称(如 “http://” 或 “ftp://” 等),虽然调用 `qurl_core_setopt()` 配置URL成功,但是执行 `qurl_core_perform()` 的时候会返回错误码 `QURL_ECODE_URL_MALFORMED_INPUT`。目前支持的scheme有:http,https,ftp,ftps,smtp,smtps。 在开始传输之前,必须设置 `QURL_OPT_URL`。 设置完此选项后,应用程序不必保留字符串。 多次配置此选项会用最后一组字符串覆盖前面的字符串。将其设置为 `QOSA_NULL` 可以对之前配置的URL进行清空。但是请注意,qurl需要设置一个URL才能执行传输。 ##### **默认值** QOSA_NULL。如果未设置此选项,则无法执行网络传输。 ##### **适用协议** 所有协议 ##### **依赖项** 无 ##### **安全问题** 从外部不受信任的一方获取URL会带来几个安全问题: 在Linux或者Windows系统运行qurl的时候,由于qurl配置URL的时候不会对URL进行任何过滤,如果你的qurl运行在一个有服务器运行的环境中,获得一个未经过滤的URL可以很容易地欺骗你的qurl程序去访问本地资源而不是远程资源。在接受用户提供的URL时,很难保证设备免受本地程序访问。 此类自定义URL还可以访问您计划之外的其他端口,因为端口号是常规URL格式的一部分。本地主机和自定义端口号的组合可以让外部用户随意访问您的本地服务。 ```{note} **注意:** 由于 `qurl_core_setopt()` 不会去解析URL是否是正确的,所以直到执行 `qurl_core_perform()` 才会发现URL不正确。 ``` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { if (QURL_OK == qurl_core_setopt(core, QURL_OPT_URL, "https://example.com")) { qurl_core_perform(core); } qurl_core_delete(core); } } ``` #### QURL_OPT_USERNAME 配置用户名用于登录需要身份认证的服务器 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_USERNAME, char *username); ``` ##### **功能描述** 传递一个char指针作为参数,该指针应指向用于传输的以null结尾的用户名。 `QURL_OPT_USERNAME` 设置在协议身份验证中要使用的用户名。 设置此选项后,应用程序不必保留字符串。 多次使用此选项会使最后一组字符串覆盖前面的字符串。设置为 `QOSA_NULL` 可以将其清空。 ##### **默认值** QOSA_NULL ##### **适用协议** 所有协议 ##### **依赖项** 通常与 `QURL_OPT_PASSWORD` 结合使用。 ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com/foo.bin"); qurl_core_setopt(core, QURL_OPT_USERNAME, "clark"); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_PASSWORD 配置密码用于登录需要身份认证的服务器 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_PASSWORD, char *password); ``` ##### **功能描述** 传递一个 char 指针作为参数,该指针应指向用于传输的以 null 结尾的密码。 `QURL_OPT_PASSWORD` 选项应与 `QURL_OPT_USERNAME` 选项结合使用。 设置此选项后,应用程序不必保留字符串。 多次使用此选项会使最后一组字符串覆盖前面的字符串。设置为 `QOSA_NULL` 可以将其清空。 ##### **默认值** QOSA_NULL ##### **适用协议** 所有协议 ##### **依赖项** 通常与 `QURL_OPT_USERNAME` 结合使用。 ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com/foo.bin"); qurl_core_setopt(core, QURL_OPT_PASSWORD, "qwerty"); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_NETWORK_ID 配置网络传输使用的网络 ID ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_NETWORK_ID, long nw_id); ``` ##### **功能描述** 传递一个long类型的网络ID作为参数,该参数表示发起网络传输时使用的是哪个PDP通道。 < 0表示不指定,>= 0则需要参考平台适配说明,一般指的就是使用的PDP通道。 这个参数默认< 0不指定网络ID,所以如果没有配置此参数,直接执行 `qurl_core_perform()` 发起网络传输会失败返回 `QURL_ECODE_NETWORK_ERR`。所以在发起网络传输之前一定要根据平台的实际情况去手动配置此参数。 ##### **默认值** 默认 < 0 不指定 ##### **适用协议** 所有协议 ##### **依赖项** 与平台相关 ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_NETWORK_ID, 1); qurl_core_setopt(core, QURL_OPT_URL, "https://example.com/foo.bin"); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_PORT 配置用户指定端口号去连接远端服务器 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_PORT, long port); ``` ##### **功能描述** 通常不建议使用此选项,因为这个配置项对所有协议有效且会覆盖URL的端口号。改为在URL中设置首选端口号。 此选项用来设置要连接的远程端口号,而不是URL中指定的端口号或所用协议的默认端口号。 通常,您只需让URL决定使用哪个端口,但这个配置项允许应用程序覆盖该端口。 虽然此选项输入的参数是long类型的,但端口号是一个无符号的16位数字,因此输入小于零或大于65535的端口号会返回 `QURL_ECODE_PARAM_INVALID` 错误。 ##### **默认值** 默认值为0,即不指定端口号,使用URL中的端口号。 ##### **适用协议** 所有协议 ##### **依赖项** 无 ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com/foo.bin"); qurl_core_setopt(core, QURL_OPT_PORT, 8080L); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_TIMEOUT_MS 配置整个网络传输的超时时间 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_TIMEOUT_MS, long timeout); ``` ##### **功能描述** 传递一个long类型的 `timeout` 时间,即允许qurl传输操作花费的最长时间(毫秒)。也就是从执行 `qurl_core_perform()`,到此接口返回的最大时间。 这个时间需要配置为合适的值,如果这个超时时间配置过小,可能会中止完全正常的操作。 这个选项以毫秒为单位进行设置。 如果多次设置了 `QURL_OPT_TIMEOUT_MS`,则使用最后设置的值。 由于此选项对允许请求花费的时间进行了严格限制,因此它在具有不同传输时间的动态用例中的作用有限,除非你每次发起网络传输的时候都重新去配置它并且能获取到适当的值,这很难做到。当使用多qurl实例并行传输的时候,这一点尤其明显,因为多qurl实例并发可能会对传输进行排队,这时候消耗的时间也包括在内。 建议 `QURL_OPT_TIMEOUT_MS` 配置的超时时间要大于 `QURL_OPT_IDLE_TIMEOUT_MS` 和 `QURL_OPT_ROUSE_CHECK_TIME_MS`。如果配置 `QURL_OPT_IDLE_TIMEOUT_MS` 为 5000 ms,`QURL_OPT_TIMEOUT_MS` 配置为 2000 ms,则网络传输的持续时间永远不会超过 2000 ms。 ##### **默认值** 30 秒 ##### **适用协议** 所有协议 ##### **依赖项** 无 ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); /* complete within 60000 milliseconds */ qurl_core_setopt(core, QURL_OPT_TIMEOUT_MS, 60 * 1000); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_IDLE_TIMEOUT_MS 配置网络传输过程中内部空闲超时时间 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_IDLE_TIMEOUT_MS, long timeout); ``` ##### **功能描述** 传递一个long类型的 `timeout` 时间,即允许没有任何网络数据传输的最长时间(毫秒)。不管是发送网络数据还是接收到网络数据,都会去重置这个idle定时器。 这个时间需要配置为合适的值,如果这个超时时间配置过小,出现网络波动的情况下,可能会中止完全正常的操作。 这个选项以毫秒为单位进行设置。 如果多次设置了 `QURL_OPT_IDLE_TIMEOUT_MS`,则使用最后设置的值。 根据周围网络环境的好坏,这个超时时间应该做相应的调整。当使用多qurl实例并行传输的时候,多qurl实例并发可能会对传输进行排队,这也可能会对 idle 超时造成一定的影响。 建议 `QURL_OPT_IDLE_TIMEOUT_MS` 配置的超时时间要小于 `QURL_OPT_TIMEOUT_MS`。否则 `QURL_OPT_IDLE_TIMEOUT_MS` 这个参数配置的超时将不起作用。 ##### **默认值** 0。默认不开启。 ##### **适用协议** 所有协议 ##### **依赖项** 无 ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); /* Network transmission is required within 5000 milliseconds */ qurl_core_setopt(core, QURL_OPT_IDLE_TIMEOUT_MS, 5 * 1000); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_ROUSE_CHECK_TIME_MS 定时检查qurl的状态 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_ROUSE_CHECK_TIME_MS, long rouse_check_time); ``` ##### **功能描述** 该函数可以配置系统周期性唤醒检查的时间,当执行 `qurl_core_perform()` 长时间阻塞的时候,会按这个时间值定时唤醒检查其他事件,如检查是否收到调用 `qurl_core_abort()` 触发的事件。 ##### **默认值** 1 秒 ##### **适用协议** 所有协议 ##### **依赖项** 无 ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); /* Wake up and check for other events within 500 milliseconds */ qurl_core_setopt(core, QURL_OPT_ROUSE_CHECK_TIME_MS, 500); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_RESUME_FROM 配置恢复传输的起始点。 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_RESUME_FROM, long from); ``` ##### **功能描述** 该配置项可以指定恢复传输的起始点。若为负数,表示从最后开始算起,如-100,表示需要下载/上传最后的100 字节。单位:byte 传递一个long as参数。它包含您希望传输开始的字节数偏移量,也就是上传或者下载文件的起始位置。将此选项设置为0,使传输从头开始(禁用恢复)。 使用FTP进行上传时,恢复位置是qurl应尝试从本地/源文件中恢复上传的位置,然后将源文件附加到远程目标文件。 使用FTP进行下载时,恢复位置是远端服务器文件中恢复下载的位置,此参数作为起始位置将文件下载到qurl。 此配置和 `QURL_OPT_RANGE` 类似,当两个配置项都进行配置的时候,以 `QURL_OPT_RESUME_FROM` 优先。 ##### **默认值** 0。不使用,从头开始传输。 ##### **适用协议** FTP、HTTP ##### **依赖项** 无 ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; long start_pos = 100; long file_maxsize = 1024; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "ftp://example.com"); if (start_pos != 0) { /* download at byte index 100 */ qurl_core_setopt(core, QURL_OPT_RESUME_FROM, start_pos); } /* Maximum SIZE for downloading files */ qurl_core_setopt(core, QURL_OPT_MAXFILESIZE, file_maxsize); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_RANGE 配置下载范围 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_RANGE, char *range); ``` ##### **功能描述** 该配置项可以配置下载范围,比如设置HTTP Range字符串,或者FTP指定范围。 传递一个char类型的指针作为参数,该指针应包含要检索的指定范围。它采用 “X-Y” 格式,其中X或Y可以省略,X和Y是字节索引。 如果此配置项应用于HTTP传输还支持多个间隔,用逗号分隔,如 “X-Y,N-M”。使用这种多间隔会导致HTTP服务器将响应文档分块发送(使用标准MIME分隔技术),作为qurl按原样返回的多部分响应。除了请求的字节外,它还包含元信息。解析或以其他方式转换此响应是调用者的责任。 有一个点要注意,HTTP标准(RFC 7233第3.1 节)允许服务器忽略范围请求,因此即使您为请求设置了 `QURL_OPT_RANGE`,您最终也可能会收到完整的响应。 对于HTTP PUT上传,不建议使用此选项,因为它可能与其他选项冲突。 多次使用此选项会使最后一组字符串覆盖前面的字符串。设置为 `QOSA_NULL` 可以将其清空。 设置此选项后,应用程序不必保留字符串。 ##### **默认值** NULL,不使用指定范围。 ##### **适用协议** HTTP、FTP ##### **依赖项** 无 ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); qurl_core_setopt(core, QURL_OPT_HTTP_GET, 1L); qurl_core_setopt(core, QURL_OPT_RANGE, "100-200"); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_UPLOAD_HEAD_RAW 配置HTTP请求使用自定义请求头 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_UPLOAD_HEAD_RAW, long onoff); ``` ##### **功能描述** 这个配置项只适用于具有请求头的协议。 将 `onoff` 设置为 1,打开自定义请求头功能。 标记上传HEAD为原始数据,不需要内部提供合成。内部会从 `QURL_OPT_UPLOAD_HEAD_DATA`/`QURL_OPT_UPLOAD_HEAD_FILE`/`QURL_OPT_HTTP_HEADER` 这三种方式中去获取自定义的请求头,我们也是选择其中一种方式去配置我们的自定义请求头数据获取方式。如果三者均未设置也不会返回异常,则会尝试从请求body中获取请求header,这会被判定为从 `QURL_OPT_UPLOAD_DATA`/`QURL_OPT_UPLOAD_FILE`/`QURL_OPT_READ_CB` 中一同获取HEAD和BODY。 通常使用 `QURL_OPT_HTTP_HEADER` 的方式去添加自定义请求头。 ##### **默认值** 0,不使用自定义请求头,内部自动合成。 ##### **适用协议** HTTP、SMTP ##### **依赖项** `QURL_OPT_UPLOAD_HEAD_DATA` 或 `QURL_OPT_UPLOAD_HEAD_FILE` 或 `QURL_OPT_HTTP_HEADER` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); qurl_core_setopt(core, QURL_OPT_HTTP_GET, 1L); qurl_core_setopt(core, QURL_OPT_UPLOAD_HEAD_RAW, 1L); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_WRITE_HEAD_CB 配置处理响应header的回调函数 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_WRITE_HEAD_CB, qurl_write_head_cb *cb); ``` ##### **功能描述** 该配置项用于配置qurl客户端的响应头回调函数,类型:`qurl_write_head_cb()`。 用户侧设置的用户接收header数据处理函数,如HTTP的响应头。 此配置项和 `QURL_OPT_WRITE_HEAD_CB_ARG` 配合使用。 `qurl_write_head_cb()` 定义为 `typedef long (*qurl_write_head_cb)(unsigned char *buf, long size, void *arg);`,其中 `buf` 就是接收的header数据,`size` 表示head的长度,`arg` 表示 `QURL_OPT_WRITE_HEAD_CB_ARG` 配置的用户参数,我们可以根据自己的需要去配置这个值。客户可以自行在回调函数中对接收到的header数据进行处理。 ##### **默认值** QOSA_NULL ##### **适用协议** 所有协议 ##### **依赖项** `QURL_OPT_WRITE_HEAD_CB_ARG` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); qurl_core_setopt(core, QURL_OPT_WRITE_HEAD_CB, header_write_callback); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_WRITE_HEAD_CB_ARG 配置响应header的回调函数的用户参数 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_WRITE_HEAD_CB_ARG, void *arg); ``` ##### **功能描述** 该配置项用于配置qurl->client的响应header回调函数的用户输入参数,类型为 `void`。 此配置项和 `QURL_OPT_WRITE_HEAD_CB` 配合使用。 配置的用户输入参数就是 `typedef long (*qurl_write_head_cb)(unsigned char *buf, long size, void *arg)` 中的 `void *arg`。 ##### **默认值** QOSA_NULL ##### **适用协议** 所有协议 ##### **依赖项** `QURL_OPT_WRITE_HEAD_CB` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); qurl_core_setopt(core, QURL_OPT_WRITE_HEAD_CB, header_write_callback); /* pass in custom data to the callback */ qurl_core_setopt(core, QURL_OPT_WRITE_HEAD_CB_ARG, header_write_arg); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_WRITE_CB 配置处理响应body的回调函数 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_WRITE_CB, qurl_write_cb *cb); ``` ##### **功能描述** 该配置项用于配置qurl客户端的响应body回调函数,类型:`qurl_write_cb`。 用户侧设置的用户接收body处理函数。 此配置项和 `QURL_OPT_WRITE_CB_ARG` 配合使用。 `qurl_write_cb` 定义为 `typedef long (*qurl_write_cb)(unsigned char *buf, long size, void *arg);`,其中 `buf` 就是接收的body数据,`size` 表示body的长度,`arg` 表示 `QURL_OPT_WRITE_CB_ARG` 配置的用户参数,我们可以根据自己的需要去配置这个值。客户可以自行在回调函数中对接收到的body数据进行处理。 ##### **默认值** QOSA_NULL ##### **适用协议** 所有协议 ##### **依赖项** `QURL_OPT_WRITE_CB_ARG` ##### **示例** ```c static char g_buff[2049] = {0}; struct qurl_app_r_buf_s{ char *buf_ptr; long valid_len; }; typedef struct qurl_app_r_buf_s qurl_app_r_buf_t; static long http_write_callback(char *buf, long size, void *arg){ long len = size; long log_len = len > 2048 ? 2048 : len; memcpy(g_buff, buf, log_len); g_buff[log_len] = 0; printf("%s", g_buff); return len; } int main(void){ qurl_core_t core = QOSA_NULL; long start_pos = 100; long file_maxsize = 1024; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); qurl_core_setopt(core, QURL_OPT_HTTP_GET, 1L); /* send all data to this function */ qurl_core_setopt(core, QURL_OPT_WRITE_CB, http_write_callback); /* we pass our 'header_write_arg' struct to the callback function */ qurl_core_setopt(core, QURL_OPT_WRITE_CB_ARG, http_write_arg); /* send a request */ qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_WRITE_CB_ARG 配置响应body的回调函数的用户参数。 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_WRITE_CB_ARG , void *arg); ``` ##### **功能描述** 该配置项用于配置qurl->client的响应body回调函数的用户输入参数,类型为 `void`。 此配置项和 `QURL_OPT_WRITE_CB` 配合使用。 配置的用户输入参数就是 `typedef long (*qurl_write_cb)(unsigned char *buf, long size, void *arg)` 中的 `void *arg`。 ##### **默认值** QOSA_NULL ##### **适用协议** 所有协议 ##### **依赖项** `QURL_OPT_WRITE_CB` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); /* send all data to this function */ qurl_core_setopt(core, QURL_OPT_WRITE_CB, http_write_callback); /* we pass our 'header_write_arg' struct to the callback function */ qurl_core_setopt(core, QURL_OPT_WRITE_CB_ARG, http_write_arg); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_READ_HEAD_CB 配置读取请求header的回调函数 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_READ_HEAD_CB, qurl_read_head_cb *cb); ``` ##### **功能描述** 读取请求header的一种方式:通过回调函数去读取请求header。 该配置项用于配置qurl客户端读取请求 header 的回调函数,类型:`qurl_read_head_cb`。 用户侧设置的用户发送 head 数据处理函数 此配置项和 `QURL_OPT_READ_HEAD_CB_ARG` 配合使用。 `qurl_read_head_cb` 定义为 `typedef long (*qurl_read_head_cb)(unsigned char *buf, long size, void *arg);`,其中 `buf` 就是读取的header数据,`size` 表示header的长度,`arg` 表示 `QURL_OPT_READ_HEAD_CB_ARG` 配置的用户参数,我们可以根据自己的需要去配置这个值。客户可以在回调函数中自行读取header数据传递到 `buf`。 ##### **默认值** QOSA_NULL ##### **适用协议** HTTP、SMTP ##### **依赖项** `QURL_OPT_UPLOAD_HEAD_RAW` `QURL_OPT_READ_HEAD_CB_ARG` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); qurl_core_setopt(core, QURL_OPT_HTTP_GET, 1L); qurl_core_setopt(core, QURL_OPT_UPLOAD_HEAD_RAW, 1L); qurl_core_setopt(core, QURL_OPT_READ_HEAD_CB, read_head_callback); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_READ_HEAD_CB_ARG 配置读取请求header的回调函数的用户参数。 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_READ_HEAD_CB_ARG, void *arg); ``` ##### **功能描述** 该配置项用于配置qurl->client的请求header回调函数的用户输入参数,类型为 `void *`。 此配置项和 `QURL_OPT_READ_HEAD_CB` 配合使用。 配置的用户输入参数就是 `typedef long (*qurl_read_head_cb)(unsigned char *buf, long size, void *arg)` 中的 `void *arg`。 ##### **默认值** QOSA_NULL ##### **适用协议** HTTP、SMTP ##### **依赖项** `QURL_OPT_UPLOAD_HEAD_RAW` `QURL_OPT_READ_HEAD_CB` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); qurl_core_setopt(core, QURL_OPT_HTTP_GET, 1L); qurl_core_setopt(core, QURL_OPT_UPLOAD_HEAD_RAW, 1L); qurl_core_setopt(core, QURL_OPT_READ_HEAD_CB, read_head_callback); /* we pass our 'read_head_arg' struct to the callback function */ qurl_core_setopt(core, QURL_OPT_READ_HEAD_CB_ARG, read_head_arg); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_READ_CB 配置读取请求body的回调函数 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_READ_CB, void *arg); ``` ##### **功能描述** 读取请求body的一种方式:配置读取请求body的回调函数。 该配置项用于配置qurl客户端读取请求body的回调函数,类型:`qurl_read_cb`。 用户侧设置的用户发送body数据处理函数 此配置项和 `QURL_OPT_READ_CB_ARG` 配合使用。 `qurl_read_cb` 定义为 `typedef long (*qurl_read_cb)(unsigned char *buf, long size, void *arg);`,其中 `buf` 就是读取的body数据,`size` 表示body的长度,`arg` 表示 `QURL_OPT_READ_CB_ARG()` 配置的用户参数,我们可以根据自己的需要去配置这个值。客户可以在回调函数中自行读取body数据传递到 `buf`。 ##### **默认值** QOSA_NULL ##### **适用协议** 所有协议 ##### 依赖项 `QURL_OPT_READ_CB` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "http://example.com/foo.bin"); qurl_core_setopt(core, QURL_OPT_HTTP_POST, 1L); qurl_core_setopt(lbs_ptr->http_hd, QURL_OPT_UPLOAD_SIZE, 1024); qurl_core_setopt(lbs_ptr->http_hd, QURL_OPT_READ_CB, read_callback); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_READ_CB_ARG 配置读取请求body的回调函数的用户参数。 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_READ_CB_ARG, void *arg); ``` ##### **功能描述** 该配置项用于配置qurl->client的请求body回调函数的用户输入参数,类型为 `void`。 此配置项和 `QURL_OPT_READ_CB` 配合使用。 配置的用户输入参数就是 `typedef long (*qurl_read_cb)(unsigned char *buf, long size, void *arg)` 中的 `void *arg`。 ##### **默认值** QOSA_NULL ##### **适用协议** 所有协议 ##### 依赖项 `QURL_OPT_READ_CB` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if(QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "http://example.com/foo.bin"); qurl_core_setopt(core, QURL_OPT_HTTP_POST, 1L); qurl_core_setopt(core, QURL_OPT_UPLOAD_SIZE, 1024); qurl_core_setopt(core, QURL_OPT_READ_CB, read_callback); qurl_core_setopt(core, QURL_OPT_READ_CB_ARG, read_arg); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_UPLOAD_HEAD_DATA 配置读取请求header的指针地址 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_UPLOAD_HEAD_DATA, void *upload_head_data_ptr); ``` ##### **功能描述** 读取请求header的一种方式:通过地址去获取请求header。 该配置项用于配置qurl客户端读取请求 header 的指针地址,类型:`void`。 用户侧设置的该指针指定上传的header数据的地址。内部会自动读取指针中的数据作为请求header发送到对端。 直到达到 `QURL_OPT_UPLOAD_HEAD_SIZE` 中配置的 `size`。 此配置项和 `QURL_OPT_UPLOAD_HEAD_SIZE` 配合使用。 ##### **默认值** QOSA_NULL ##### 适用协议 HTTP、SMTP ##### 依赖项 `QURL_OPT_UPLOAD_HEAD_RAW` `QURL_OPT_UPLOAD_HEAD_SIZE` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; static char data1[] = "zzb lzh djt jamie qurl\n"; if(QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); qurl_core_setopt(core, QURL_OPT_HTTP_GET, 1L); qurl_core_setopt(core, QURL_OPT_UPLOAD_HEAD_RAW, 1L); qurl_core_setopt(core, QURL_OPT_UPLOAD_HEAD_DATA, data1); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_UPLOAD_HEAD_SIZE 配置上传的请求头size ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_UPLOAD_HEAD_SIZE, long size); ``` ##### **功能描述** 该配置项用于配置qurl客户端以指针的方式读取的请求header的size,类型:long。 指定上传的请求头size,如HTTP的header大小等等。单位:byte。 内部会自动读取 `QURL_OPT_UPLOAD_HEAD_DATA` 配置的指针中的数据作为请求header发送到对端。直到达到我们配置的size。 此配置项和 `QURL_OPT_UPLOAD_HEAD_DATA` 配合使用。 ##### **默认值** -1,不使用 ##### **适用协议** HTTP、SMTP ##### 依赖项 `QURL_OPT_UPLOAD_HEAD_RAW` `QURL_OPT_UPLOAD_HEAD_DATA` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; static char data1[] = "zzb lzh djt jamie qurl\n"; if(QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); qurl_core_setopt(core, QURL_OPT_HTTP_GET, 1L); qurl_core_setopt(core, QURL_OPT_UPLOAD_HEAD_RAW, 1L); qurl_core_setopt(core, QURL_OPT_UPLOAD_HEAD_DATA, data1); qurl_core_setopt(core, QURL_OPT_UPLOAD_HEAD_SIZE , strlen(data1)); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_UPLOAD_HEAD_FILE 配置读取哪个文件作为请求header的内容 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_UPLOAD_HEAD_FILE, char *filename_ptr); ``` ##### **功能描述** 读取请求header的一种方式:通过文件名去获取请求header。 该配置项用于配置qurl客户端读取请求header使用的文件,指定上传的文件,类型:`char`。 指定上传的请求头数据,输入文件名,上传其文件内容。内部会读完整个文件作为请求header发送到对端。 ##### **默认值** QOSA_NULL ##### 适用协议 HTTP、SMTP ##### 依赖项 `QURL_OPT_UPLOAD_HEAD_RAW` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; static char data1[] = "zzb lzh djt jamie qurl\n"; if(QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); qurl_core_setopt(core, QURL_OPT_HTTP_GET, 1L); qurl_core_setopt(core, QURL_OPT_UPLOAD_HEAD_RAW, 1L); qurl_core_setopt(core, QURL_OPT_UPLOAD_HEAD_FILE, "UFS:test.txt"); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_UPLOAD_DATA 配置读取请求body的指针地址 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_UPLOAD_DATA, void *upload_data_ptr); ``` ##### **功能描述** 读取请求body的一种方式:通过指针去获取请求body。 该配置项用于配置qurl客户端读取请求 body 使用的指针地址,类型:`void`。 用户侧设置的该指针指定上传的body数据的地址。内部会自动读取指针中的数据作为请求body发送到对端。 直到达到 `QURL_OPT_UPLOAD_SIZE` 中配置的 `size`。 此配置项和 `QURL_OPT_UPLOAD_SIZE` 配合使用。 ##### **默认值** QOSA_NULL ##### **适用协议** 所有协议 ##### 依赖项 `QURL_OPT_UPLOAD_SIZE` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; static char data1[] = "zzb lzh djt jamie qurl\n"; if(QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "http://example.com/foo.bin"); qurl_core_setopt(core, QURL_OPT_HTTP_POST, 1L); qurl_core_setopt(core, QURL_OPT_UPLOAD_DATA, data1); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_UPLOAD_SIZE 配置上传的请求body的 `size` ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_UPLOAD_SIZE, long size); ``` ##### **功能描述** 该配置项用于配置qurl客户端以指针的方式读取的请求body的size,类型:long。 指定上传的请求body的size,如HTTP的body,ftp的文件大小等等。单位:byte。 内部会自动读取 `QURL_OPT_UPLOAD_DATA` 配置的指针中的数据作为请求body发送到对端。直到达到我们配置的size。 此配置项和 `QURL_OPT_UPLOAD_DATA` 配合使用。 ##### **默认值** -1,不使用 ##### 适用协议 所有协议 ##### 依赖项 `QURL_OPT_UPLOAD_DATA` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; static char data1[] = "zzb lzh djt jamie qurl\n"; if(QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "http://example.com/foo.bin"); qurl_core_setopt(core, QURL_OPT_HTTP_POST, 1L); qurl_core_setopt(core, QURL_OPT_UPLOAD_DATA, data1); qurl_core_setopt(core, QURL_OPT_UPLOAD_SIZE, strlen(data1)); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_UPLOAD_FILE 配置读取哪个文件作为请求body的内容 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_UPLOAD_FILE, char *filename_ptr); ``` ##### **功能描述** 读取请求body的一种方式:通过文件名去获取请求body。 该配置项用于配置qurl客户端读取请求 body 使用的文件,指定上传的文件,类型:`char`。 指定上传的请求body数据,输入文件名,上传其文件内容。 内部会读完整个文件作为请求body发送到对端。 ##### **默认值** QOSA_NULL ##### 适用协议 所有协议 ##### 依赖项 无 ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; static char data1[] = "zzb lzh djt jamie qurl\n"; if(QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "http://example.com/foo.bin"); qurl_core_setopt(core, QURL_OPT_HTTP_POST, 1L); qurl_core_setopt(core, QURL_OPT_UPLOAD_FILE, "UFS:test.txt"); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_NOBODY 配置是否不需要下载body ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_NOBODY, long nobody); ``` ##### **功能描述** 该配置项用于配置这次请求是否需要下载body,类型:long。 设置为1告诉qurl在进行下载时不要在输出中包含 body 部分。对于HTTP(S),这使得qurl执行HEAD请求。对于大多数其他协议,这意味着不要求传输body数据。 当HTTP请求方法一直设置为HEAD时,对于设置了 `QURL_OPT_NOBODY` 的HTTP操作,当再次禁用此选项 (0) 则会使其再次成为GET请求。如果你想要执行GET请求,请直接配置 `QURL_OPT_HTTP_GET`。 启用 `QURL_OPT_NOBODY` 意味着要求下载没有body的文件。 如果你没有开启此配置项而且你使用的是HTTP进行传输,正常除了HEAD方法,其他方法你都会得到body (除非资源和服务器为你请求的特定URL发送了一个零字节的正文)。 因为不管你配置的HTTP请求方法什么,这个设置打开会修改HTTP的方法为head,所以建议先设置这个配置项,再去配置HTTP方法。 ##### **默认值** 0,需要下载body。 ##### 适用协议 所有协议 ##### 依赖项 无 ##### **示例** ```c int main(void) { qurl_core_t core = QOSA_NULL; if(QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); qurl_core_setopt(core, QURL_OPT_HTTP_GET, 1L); /* get us the resource without a body - use HEAD */ qurl_core_setopt(core, QURL_OPT_NOBODY, 1L); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_DIRLISTONLY 配置FTP或者sftp读取目录的时候只读取文件名称 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_DIRLISTONLY, long list_only); ``` ##### **功能描述** 对于基于FTP和SFTP的URL,设置为1告诉qurl库只列出目录中文件的名称,而不列出其他细节信息,包括文件大小、日期等完整目录列表。 `QURL_OPT_DIRLISTONLY` 配置为1,FTP发送的是NLST指令。不配置的情况下发送的是LIST指令。 注意:对于FTP,这会导致向FTP服务器发送NLST命令。请注意,一些FTP服务器在对NLST的响应中只列出文件;它们可能不包括子目录和符号链接。 将此选项设置为1也意味着即使URL没有以斜线结尾,也会显示目录列表,如果没有配置这个配置项,那么URL就必须以斜线结尾。 如果您还使用 `QURL_OPT_WILDCARDMATCH`,请不要使用此选项,因为它会让该配置项无效。 ##### **默认值** 0,不使能此功能。 ##### 适用协议 FTP、FTPS、SFTP ##### 依赖项 `QURL_OPT_WILDCARDMATCH` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if(QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "ftp://example.com/dir/"); qurl_core_setopt(core, QURL_OPT_DIRLISTONLY, 1L); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_IGNORE_CONTENT_LENGTH 配置下载的时候忽略数据长度 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_IGNORE_CONTENT_LENGTH, long ignore_cl); ``` ##### **功能描述** 忽略下载的内容长度(ignore content length)。 这个配置项主要是针对一些特殊服务器或者特殊资源而配置的,提供一些特殊的支持。 对于HTTP请求,如果 `ignore_cl` 设置为1,启用此配置项,则忽略HTTP响应中的Content-Length标头。 对于FTP请求,如果 `ignore_cl` 设置为1,启用此配置项,则不会在FTP传输中请求或者使用文件大小。 这个配置项对比较旧的web服务器进行的HTTP传输非常有用,因为这些服务器传输超过2 GB的文件内容会报告错误的文件长度。如果使用此选项,qurl将无法知道文件的长度,那么就可以在服务器传输完整个文件结束连接时停止下载。不受这个错误的CONTENT_LENGTH影响。 这个配置项可以支持FTP下载增长中的文件。它阻止状态机从服务器请求文件大小。如果文件大小未知,则下载将继续,直到服务器终止它;否则,如果接收的字节数超过报告的文件大小,客户端将停止,报告qurl错误码。 注意:对于 “TYPE A” 传输请求大小是没有意义的,因为服务器不报告转换后的大小。因此无需关心这个配置项。 没必要的时候不要使用此配置项。 ##### **默认值** 0,不使能此功能。 ##### **适用协议** HTTP、HTTPS、FTP、FTPS ##### 依赖项 无 ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if(QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); /* we know the server is silly, ignore content-length */ qurl_core_setopt(core, QURL_OPT_IGNORE_CONTENT_LENGTH, 1L); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_TRANSFERTEXT 配置FTP传输的数据格式 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_TRANSFERTEXT, long prefer_ascii); ``` ##### **功能描述** 指定首选的数据传输方式。ASCII or 二进制 FTP传输的数据格式:0表示二进制模式(默认),1表示ASCII模式。 参数设置为1告诉qurl库使用 ASCII 模式进行FTP传输,而不是默认的二进制传输。 当我们在两个不同系统间传输文本数据的时候,如果我们传输的某些字符(如换行符)在两个系统之间的格式不一样,可以使用这个配置项配置为ASCII模式进行传输。 通过FTP进行ASCII传输时,qurl不会进行完整的ASCII转换。这是一个已知限制。qurl只是将模式设置为ASCII并执行标准传输。 ##### **默认值** 0,不使能,使用二进制传。 ##### 适用协议 FTP、FTPS、SFTP ##### 依赖项 无 ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if(QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "ftp://example.com/textfile"); qurl_core_setopt(core, QURL_OPT_TRANSFERTEXT, 1L); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_APPEND 配置以增量的方式上传文件到远端 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_APPEND, long remote_append); ``` ##### **功能描述** 该配置项设置为1告诉qurl库要以附加的方式写入到远程文件而不是覆盖它。这仅在 FTP 上传文件时有用。 ##### **默认值** 0,不使能,使用覆盖的方式上传文件。 ##### **适用协议** FTP、FTPS、SFTP ##### 依赖项 无 ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; long start_pos = 100; long file_maxsize = 1024; if(QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "ftp://example.com/dir/to/newfile"); qurl_core_setopt(core, QURL_OPT_APPEND, 1L); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_FILETIME 配置获取远程文档的文件修改时间 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_FILETIME, long get_filetime); ``` ##### **功能描述** 使能该配置项,将尝试获取远程文档的文件修改时间。之后,我们可以使用 `qurl_core_getinfo()` 函数来获取该时间。 如果为1,qurl将尝试在此操作中获取远程文档的修改时间。这要求远程服务器发送时间或回复时间查询命令。带有 `QURL_INFO_FILETIME` 参数的 `qurl_core_getinfo()` 函数可以在传输后用于提取接收时间(如果有的话)。 ##### **默认值** 0,不使能。 ##### **适用协议** HTTP、HTTPS、FTP、FTPS ##### 依赖项 无 ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; long filetime = -1; if(QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com/path.html"); qurl_core_setopt(core, QURL_OPT_FILETIME, 1L); if(qurl_core_perform(core) == QURL_OK); { if(qurl_core_getinfo(qurl, QURL_INFO_FILETIME, &filetime) == QURL_OK) { if(filetime >= 0) { time_t tmp_time = (time_t)filetime; const struct tm *tm; tm = gmtime(&tmp_time); debug_printf("%d-%d-%d %d:%d:%d\n\n", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec); tmp_file_time_len = qosa_snprintf( tmp_file_time, 64, "%04d%02d%02d%02d%02d%02d", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec ); } } } qurl_core_delete(core); } } ``` #### QURL_OPT_TIMEVALUE 配置需要重新下载文档的时间 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_TIMEVALUE, long timevalue); ``` ##### **功能描述** 该配置项用于设置一个时间值,这应该是自1970年1月1日以来以秒计的时间,该时间值将与远程文档的最后修改时间进行比较,以确定是否需要重新下载该文档。 该选项通常与 `QURL_OPT_TIMECONDITION` 选项一起使用。 在具有32位 “long” 变量的系统(如Windows)上,此选项不能设置2038年之后的日期。 ##### **默认值** 0 ##### **适用协议** HTTP、HTTPS ##### 依赖项 `QURL_OPT_TIMECONDITION` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if(QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); /* January 1, 2020 is 1577833200 */ qurl_core_setopt(core, QURL_OPT_TIMEVALUE, 1577833200L); /* If-Modified-Since the above time stamp */ qurl_core_setopt(core, QURL_OPT_TIMECONDITION, QURL_TIMECOND_IFMODSINCE); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_TIMECONDITION 配置获取文件的时间条件 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_TIMECONDITION, long timecondition); ``` ##### **功能描述** 该配置项用于配置获取文件的时间条件,参考 `qurl_timecond_t` 定义(详见附录)。 ```{image} images/image_TvljbfgkLoXbclxYRFlcFJhbn8e.webp :width: 1460px :height: 220px ``` 传递一个long类型的参数。`timecondition` 定义了如何处理 `CURLOPT_TIMEVALUE` 时间值。您可以将此参数设置为 `QURL_TIMECOND_IFMODSINCE` 或 `QURL_TIMECOND_IFUNMODSINCE` 或 `QURL_TIMECOND_LASTMOD`。 文件的最后修改时间并不总是已知的,在这种情况下,即使满足给定的时间条件,此功能也不起作用。 该选项通常与 `QURL_OPT_TIMEVALUE` 选项一起使用。 ##### **默认值** QURL_TIMECOND_NONE(0) ##### **适用协议** HTTP、HTTPS ##### 依赖项 `QURL_OPT_TIMEVALUE` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if(QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); /* January 1, 2020 is 1577833200 */ qurl_core_setopt(core, QURL_OPT_TIMEVALUE, 1577833200L); /* If-Modified-Since the above time stamp */ qurl_core_setopt(core, QURL_OPT_TIMECONDITION, QURL_TIMECOND_IFMODSINCE); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_MAXFILESIZE 配置文件下载允许的最大SIZE ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_MAXFILESIZE, long max_filesize); ``` ##### **功能描述** 设置下载文件的最大SIZE。x < 0 :无效,返回参数设置错误;x = 0 :无限制(缺省值);x > 0 :指定限制值 。 传递一个long参数。这指定了要下载的文件的最大可接受大小(以字节为单位)。如果发现请求的文件大于此值,则传输将中止,并返回 `QURL_ECODE_FILESIZE_EXCEEDED`。传递零大小会禁用此功能,传递负数会报错 `QURL_ECODE_PARAM_INVALID`。 在下载开始之前,文件大小并不总是已知的,对于此类传输,此选项无效,即使文件传输最终大于此给定限制。 即使传输已经开始,如果已下载的文件大小达到这个最大值,此选项也会停止正在进行的传输。 ##### **默认值** 0,没有限制。 ##### **适用协议** 所有协议 ##### 依赖项 无 ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if(QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); qurl_core_setopt(core, QURL_OPT_HTTP_GET, 1L); /* refuse to download if larger than 1000 bytes */ qurl_core_setopt(core, QURL_OPT_MAXFILESIZE, 1000L); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_CUSTOMREQUEST 配置用户自定义的请求命令 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_CUSTOMREQUEST, char *customrequest_ptr); ``` ##### **功能描述** 用户自定义的请求命令,如HTTP的POST、GET方法;FTP替换 LIST 或 NLIST 命令。 将指向以null结尾的字符串的指针作为参数传递。 当通过设置 `CURLOPT_CUSTOMREQUEST` 来更改请求方法时,您实际上并没有改变qurl的行为或动作:您只更改了请求中发送的实际字符串。 qurl在其请求中传递逐字字符串,而不使用任何过滤器或其他安全防护措施。这包括空格和控制字符。 设置此选项后,应用程序不必保留字符串。 多次使用此选项会使最后一组字符串覆盖前面的字符串。将其设置为QOSA_NULL可对其清空。 此选项可用于指定请求: - **HTTP**:在执行基于HTTP的请求时,替换掉GET或HEAD。这对于执行HTTP DELETE请求特别有用。 - **FTP**:执行FTP目录列表时,替换掉LIST和NLST。 - **SMTP**:在发出基于 SMTP 的请求时,替换掉HELP或VRFY。 ##### **默认值** QOSA_NULL ##### **适用协议** HTTP、HTTPS、FTP、FTPS、SMTP、SMTPS ##### 依赖项 无 ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if(QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "ftps://112.31.84.164/jamie_test/"); qurl_core_setopt(core, QURL_OPT_PORT, 8311L); qurl_core_setopt(core, QURL_OPT_USERNAME, "test"); qurl_core_setopt(core, QURL_OPT_PASSWORD, "test"); qurl_core_setopt(core, QURL_OPT_CUSTOMREQUEST, "LIST ."); qurl_core_perform(core); qurl_core_delete(core); } } ``` ### 中间件配置项 #### QURL_OPT_BOUND_THREAD 配置将qurl业务绑定到用户自定义的线程 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_BOUND_THREAD , long thread_id); ``` ##### **功能描述** 传递一个long参数作为线程id。将qurl实例锁定到我们指定的线程。0:锁定到当前调用 `qurl_core_setopt` 的线程。 用于线程限制,对qurl实例的资源进行保护,其他线程调用该qurl实例的资源会报错 `QURL_ECODE_NO_PERMISSION`。 该选项通常与 `QURL_OPT_BOUND_THREAD_CTRL` 选项一起使用。 ##### **默认值** 0。在未配置 `QURL_OPT_BOUND_THREAD_CTRL` 的情况下,此配置项不生效。 ##### **适用协议** 所有协议 ##### 依赖项 `QURL_OPT_BOUND_THREAD_CTRL` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if(QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); qurl_core_setopt(core, QURL_OPT_HTTP_GET, 1L); /* 绑定到当前线程 */ qurl_core_setopt(core, QURL_OPT_BOUND_THREAD, 0); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_BOUND_THREAD_CTRL 配置qurl业务绑定线程的方式 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_BOUND_THREAD_CTRL , long arg); ``` ##### **功能描述** 传递一个long参数表示qurl业务绑定线程的方式。 - 0:解绑。 - 1:绑定用户指定线程。 - 2:绑定qurl业务创建线程,也就是调用 `qurl_core_create()` 的线程。 该选项通常与 `QURL_OPT_BOUND_THREAD` 选项一起使用。用于线程限制,对qurl实例的资源进行保护,其他线程调用该qurl实例的资源会报错 `QURL_ECODE_NO_PERMISSION`。 ##### **默认值** 0。不绑定线程。 ##### **适用协议** 所有协议 ##### 依赖项 `QURL_OPT_BOUND_THREAD` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if(QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); qurl_core_setopt(core, QURL_OPT_HTTP_GET, 1L); /* 绑定到调用qurl_core_create创建qurl实例的线程,也就是当前线程 */ qurl_core_setopt(core, QURL_OPT_BOUND_THREAD_CTRL , 2); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_REUSE_HOLD 配置是否在使用后立即关闭连接 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_REUSE_HOLD, long reuse_hold); ``` ##### **功能描述** 此配置项用于配置是否持久占用conn。用于解绑conn逻辑。 传递一个long类型的参数。设置为1,使qurl在传输完成时显式关闭连接。通常,qurl在完成一次传输后会保持所有连接的活动状态,以防后续的传输可以重用它们。 应谨慎使用此选项,并且只有当您了解它的作用时才能使用,因为它会严重影响性能。 设置为0,使qurl不去显式关闭连接,以便以后可能重用(默认行为)。 ##### **默认值** 0。默认使用后不去立即关闭连接。 ##### **适用协议** 所有协议 ##### 依赖项 无 ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if(QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); qurl_core_setopt(core, QURL_OPT_HTTP_GET, 1L); qurl_core_setopt(core, QURL_OPT_REUSE_HOLD, 1L); /* 多次执行qurl_core_perform会创建一个新的conn */ qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_CONN_IDLE_TIMEOUT_MS 配置允许重用连接的最大空闲时间 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_CONN_IDLE_TIMEOUT_MS, long timeout); ``` ##### **功能描述** 传递一个long参数作为超时时间。 允许现有连接空闲的最长时间(秒),在这个时间内可以请求重用这个conn,超过这个时间删除这个conn,需要重新建立新的连接。 “连接缓存”保存以前使用的连接。当要执行新请求时,qurl会考虑任何匹配的连接以供重用。`QURL_OPT_CONN_IDLE_TIMEOUT_MS` 限制可防止qurl尝试太旧的连接以供重用,因为旧连接不起作用的风险更高,因此尝试它们会导致性能损失,有时还会因为难以弄清楚情况而导致服务损失。如果在缓存中发现的连接时间早于此设置的时间,则会将其关闭。 ##### **默认值** 120 s ##### **适用协议** 所有协议 ##### 依赖项 无 ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if(QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); qurl_core_setopt(core, QURL_OPT_HTTP_GET, 1L); qurl_core_setopt(core, QURL_OPT_CONN_IDLE_TIMEOUT_MS, 120 *1000L); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_CONN_MAXLIFETIME_MS 配置conn的最长生命周期 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_CONN_MAXLIFETIME_MS, long timeout); ``` ##### **功能描述** 传递一个long参数作为超时时间。 允许现有连接的最大连接时长(秒),计算的是从conn开始建立到现在的时间差,超过这个超时时间删除这个conn。 定期清除长时间占据资源的旧的conn。更新conn防止连接太旧导致使用出现异常。 ##### **默认值** 0。没有最大连接时长的限制。 ##### **适用协议** 所有协议 ##### 依赖项 无 ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if(QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); qurl_core_setopt(core, QURL_OPT_HTTP_GET, 1L); qurl_core_setopt(core, QURL_OPT_CONN_MAXLIFETIME_MS, 0); qurl_core_perform(core); qurl_core_delete(core); } } ``` ### Socket 配置项 #### QURL_OPT_SOCKET_SO_LINGER 配置 socket SO_LINGER 选项 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_SOCKET_SO_LINGER, qurl_so_linger_t *linger); ``` ##### **功能描述** 该配置项用于配置socket `SO_LINGER` 选项,参考 `qurl_so_linger_t` 定义(详见附录)。 不同的 `l_onoff` 和 `l_linger` 对socket close的不同影响如下所示: ```{image} images/image_JpkKbyDEOoUha8xyzAkc5HLfn5g.webp :width: 1460px :height: 220px ``` ##### **默认值** 0。不使用 `SO_LINGER` 选项。 ##### **适用协议** TCP ##### **依赖项** 无 ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; qurl_so_linger_t so_linger = {0}; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); qurl_core_setopt(core, QURL_OPT_HTTP_GET, 1L); /* close socket的时候,会尝试把剩余的数据发送出去,阻塞直到5秒超时或者数据发送完成 */ so_linger.l_onoff = 1; so_linger.l_linger = 5; qurl_core_setopt(core, QURL_OPT_SOCKET_SO_LINGER, &so_linger); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_SOCKET_SO_KEEPALIVE 配置开启 TCP 的保活功能。 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_SOCKET_SO_KEEPALIVE , long so_keepalive); ``` ##### **功能描述** `SO_KEEPALIVE` 配置,开启或关闭TCP的保活功能。 传递一个long参数,如果设置为1,则开启TCP保活功能。发送探针的延迟和频率可以通过 `QURL_OPT_SOCKET_TCP_KEEPIDLE`、`QURL_OPT_SOCKET_TCP_KEEPINTVL` 和 `QURL_OPT_SOCKET_TCP_KEEPCNT` 选项进行控制。 设置为 0 (默认行为)以禁用TCP保活探测。 ##### **默认值** 0。不开启TCP保活功能。 ##### **适用协议** TCP ##### **依赖项** `QURL_OPT_SOCKET_TCP_KEEPIDLE`、`QURL_OPT_SOCKET_TCP_KEEPINTVL`、`QURL_OPT_SOCKET_TCP_KEEPCNT` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); qurl_core_setopt(core, QURL_OPT_SOCKET_SO_KEEPALIVE, 1L); qurl_core_setopt(core, QURL_OPT_SOCKET_TCP_KEEPIDLE, 200L); qurl_core_setopt(core, QURL_OPT_SOCKET_TCP_KEEPINTVL, 10L); qurl_core_setopt(core, QURL_OPT_SOCKET_TCP_KEEPCNT, 5L); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_SOCKET_TCP_KEEPIDLE 配置 TCP 保活功能空闲时间。 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_SOCKET_TCP_KEEPIDLE , long tcp_keepidle); ``` ##### **功能描述** `TCP_KEEPIDLE` 配置,保活功能空闲时间。 传递一个long参数设置在连接空闲时等待发送保活探测的延迟(秒)。并非所有操作系统都支持此选项。 如果开启了TCP保活功能,在这段时间内TCP层没有任何数据收发,就会发送保活探测确认对端是否还在。 它接受的最大值是2147483648。 ##### **默认值** 0,不使能。 ##### **适用协议** TCP ##### **依赖项** `QURL_OPT_SOCKET_SO_KEEPALIVE` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); qurl_core_setopt(core, QURL_OPT_SOCKET_SO_KEEPALIVE, 1L); qurl_core_setopt(core, QURL_OPT_SOCKET_TCP_KEEPIDLE, 200L); qurl_core_setopt(core, QURL_OPT_SOCKET_TCP_KEEPINTVL, 10L); qurl_core_setopt(core, QURL_OPT_SOCKET_TCP_KEEPCNT, 5L); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_SOCKET_TCP_KEEPINTVL 配置 TCP 保活功能报文间隔。 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_SOCKET_TCP_KEEPINTVL , long tcp_keepintvl); ``` ##### **功能描述** `TCP_KEEPINTVL` 配置,保活功能报文间隔。 传递一个long参数设置发送保活探测之间的等待间隔(秒)。并非所有操作系统都支持此选项。 配置好了 `tcp_keepintvl`,发完一个探针在 `tcp_keepintvl` 时间内没有收到对端对探针的回应,就会再次发起新的探针。 它接受的最大值是2147483648。 ##### **默认值** 0,不使能。 ##### **适用协议** TCP ##### **依赖项** `QURL_OPT_SOCKET_SO_KEEPALIVE` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); qurl_core_setopt(core, QURL_OPT_SOCKET_SO_KEEPALIVE, 1L); qurl_core_setopt(core, QURL_OPT_SOCKET_TCP_KEEPIDLE, 200L); qurl_core_setopt(core, QURL_OPT_SOCKET_TCP_KEEPINTVL, 10L); qurl_core_setopt(core, QURL_OPT_SOCKET_TCP_KEEPCNT, 5L); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_SOCKET_TCP_KEEPCNT 配置保活功能报文次数 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_SOCKET_TCP_KEEPCNT , long tcp_keepcnt); ``` ##### **功能描述** `TCP_KEEPCNT` 配置,保活功能报文次数。 传递一个long参数设置在断开连接之前要发送的探测数。并非所有操作系统都支持此选项。 此选项接受的最大值是您的系统允许的任何值。 ##### **默认值** 0,不使能。 ##### **适用协议** TCP ##### **依赖项** `QURL_OPT_SOCKET_SO_KEEPALIVE` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); qurl_core_setopt(core, QURL_OPT_SOCKET_SO_KEEPALIVE, 1L); qurl_core_setopt(core, QURL_OPT_SOCKET_TCP_KEEPIDLE, 200L); qurl_core_setopt(core, QURL_OPT_SOCKET_TCP_KEEPINTVL, 10L); qurl_core_setopt(core, QURL_OPT_SOCKET_TCP_KEEPCNT, 5L); qurl_core_perform(core); qurl_core_delete(core); } } ``` ### TLS配置项 #### QURL_OPT_TLS_CFG 配置 TLS 连接用到的配置信息 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_TLS_CFG , qurl_tls_cfg_t * tls_cfg_ptr); ``` ##### **功能描述** 该配置项可以配置TLS连接用到的相关参数,传入一个 `qurl_tls_cfg_t` 类型的指针,里面保存着我们设置好的SSL配置参数,内部会利用这个指针保存的的TLS相关参数发起TLS连接。TLS的配置参数参考 `qurl_tls_cfg_t`。 ```{note} **注意:** 传的是指针,实体资源由用户维护。 ``` ##### **默认值** `version` = QURL_TLS_VERSION_ALL; `ciphersuites` = QOSA_NULL; `verify` = QURL_TLS_VERIFY_NONE; `ca_cert_path_slist` = QOSA_NULL; `own_cert_path_ptr` = QOSA_NULL; `own_key_path_ptr` = QOSA_NULL; `own_key_pwd_ptr` = QOSA_NULL; `negotiate_timeout` = 30; `bits.sni_enable` = false; `bits.session_matc` h = true; `bits.session_share` = true; ##### **适用协议** HTTPS、FTPS、SMTPS ##### **依赖项** `qurl_tls_cfg_init` ##### **示例** ```c int main(void) { qurl_core_t core = QOSA_NULL; qurl_tls_cfg_t tls_cfg = {0}; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "ftp://example.com/dir/file.ext"); qurl_tls_cfg_init(&tls_cfg); tls_cfg.negotiate_timeout = 30; qurl_core_setopt(core, QURL_OPT_TLS_CFG, &tls_cfg); qurl_core_setopt(core, QURL_OPT_TLS_USETLS, QURL_USETLS_ALL); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_TLS_USETLS 配置使用 SSL/TLS 进行传输。 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_TLS_USETLS , long use_tls); ``` ##### **功能描述** 该配置项可以指定连接使用TLS。 传递一个long参数设置qurl在传输时使用的SSL级别。传递的参数参考 `qurl_usetls_e` 定义(详见附录)。 这些协议都是以纯文本开始并使用STARTTLS命令升级到SSL协议。 ##### **默认值** QURL_USETLS_NONE ##### **适用协议** FTP、SMTP(升级为 TLS) ##### **依赖项** `QURL_OPT_TLS_CFG` ##### **示例** ```c int main(void) { qurl_core_t core = QOSA_NULL; qurl_tls_cfg_t tls_cfg = {0}; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "ftp://example.com/dir/file.ext"); qurl_tls_cfg_init(&tls_cfg); tls_cfg.negotiate_timeout = 30; qurl_core_setopt(core, QURL_OPT_TLS_CFG, &tls_cfg); qurl_core_setopt(core, QURL_OPT_TLS_USETLS, QURL_USETLS_ALL); qurl_core_perform(core); qurl_core_delete(core); } } ``` ### HTTP 协议配置项 #### QURL_OPT_REDIRS_CNT_MAX 配置HTTP允许的最大重定向数 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_REDIRS_CNT_MAX , long amount); ``` ##### **功能描述** 此配置项用于配置HTTP重定向的限制次数。如果达到了最大次数的重定向,下一个重定向会触发错误返回 `QURL_TRANS_FOLLOWTYPE_FAKE`。只有同时使用 `QURL_OPT_FOLLOWLOCATION` 时,此选项才有意义。 将这个配置项设置为 0 会使qurl拒绝任何重定向。 将其设置为-1,表示重定向次数无限。这使得您的应用程序陷入永无止境的重定向循环。 ##### **默认值** 1,表示允许一次HTTP重定向 ##### **适用协议** HTTP ##### **依赖项** `QURL_OPT_FOLLOWLOCATION` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); qurl_core_setopt(core, QURL_OPT_HTTP_GET, 1L); qurl_core_setopt(core, QURL_OPT_FOLLOWLOCATION, 1L); qurl_core_setopt(core, QURL_OPT_REDIRS_CNT_MAX, 3); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_HTTP_VERSION 配置HTTP协议的版本。 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_HTTP_VERSION , long version); ``` ##### **功能描述** 传递一个long类型的参数,通过设置 `QURL_OPT_HTTP_VERSION` 选项,你可以指定要使用的特定HTTP协议版本。这个选项的值参考 `qurl_http_version_e` 的定义(详见附录)。 请注意,HTTP版本只是一个请求。qurl仍然优先重用现有连接,因此它可能会使用您没有要求的HTTP版本重用连接。 ##### **默认值** QURL_HTTP_VERSION_NONE ##### **适用协议** HTTP ##### **依赖项** 无 ##### **示例** ```c int main(void) { qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "http://example.com"); qurl_core_setopt(core, QURL_OPT_HTTP_GET, 1L); qurl_core_setopt(core, QURL_OPT_HTTP_VERSION, QURL_HTTP_VERSION_NONE); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_HTTP_GET 配置为HTTP GET请求方法。 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_HTTP_GET , long useget); ``` ##### **功能描述** 传递一个long参数。如果 `useget` 设置为 1,则HTTP请求方法使用GET。如果POST、HEAD、PUT等请求之前使用过相同的URL,则配置完GET请求后可直接使用。 当将 `QURL_OPT_HTTP_GET` 设置为1时,qurl 会自动将 `QURL_OPT_NOBODY` 设置为 0。 将此选项设置为零无效。应用程序需要明确选择要使用的HTTP请求方法,没办法取消掉已选择的HTTP方法。要将句柄重置为默认的请求方法,请考虑 `qurl_core_reset()` 接口。 ##### **默认值** 0。不使能。 ##### **适用协议** HTTP ##### **依赖项** 无 ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); qurl_core_setopt(core, QURL_OPT_HTTP_GET, 1L); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_HTTP_POST 配置为HTTP POST请求方法。 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_HTTP_POST, long post); ``` ##### **功能描述** 配置项设置为1告诉qurl要做一个常规的HTTP POST请求。qurl默认使用 “Content-Type:application/x-www-form-urlencoded” 标头。这是最常用的POST方法。 使用 `QURL_OPT_READ_CB`/`QURL_OPT_UPLOAD_DATA`/`QURL_OPT_UPLOAD_FILE` 选项之一指定要POST的数据,并使用 `QURL_OPT_UPLOAD_SIZE` 设置数据大小,文件的方式会读完整个文件,不需要设置数据大小。 您可以通过使用 `QURL_OPT_HTTP_HEADER` 设置自己的POST Content-Type来覆盖默认的POST Content-Type。 在HTTP 1.1中使用POST意味着使用 “Expect:100 continue” 标头。您可以使用 `QURL_OPT_HTTP_HEADER` 禁用此标头。 如果你使用POST发送数据到HTTP 1.1服务器,如果你使用分块编码,在开始POST之前,你可以在不知道数据大小的情况下发送数据。您可以通过使用 `QURL_OPT_HTTP_HEADER` 添加 “Transfer Encoding:chunked” 的标头来启用此功能。使用HTTP 1.0不支持分块传输,您必须在请求中指定大小。 当设置 `QURL_OPT_HTTP_POST` 为 1 时,qurl会自动将 `QURL_OPT_NOBODY` 和 `QURL_OPT_HTTP_GET` 设置为 0。 如果您发出POST请求,然后想使用相同的句柄进行HEAD或GET请求,则必须使用 `QURL_OPT_NOBODY` 或 `QURL_OPT_HTTP_GET` 或类似方法显式设置新的请求类型。 当将 `QURL_OPT_HTTP_POST` 设置为 0 时,qurl会将请求类型重置为默认值以禁用POST。通常这意味着重置为GET。通常,您应该如上所述显式设置一个新的请求类型。 ##### **默认值** 0。不使能。 ##### **适用协议** HTTP ##### **依赖项** `QURL_OPT_READ_CB` 或 `QURL_OPT_UPLOAD_DATA` 或 `QURL_OPT_UPLOAD_FILE`, `QURL_OPT_UPLOAD_SIZE` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "http://example.com/foo.bin"); qurl_core_setopt(core, QURL_OPT_HTTP_POST, 1L); qurl_core_setopt(core, QURL_OPT_UPLOAD_SIZE, 1024); qurl_core_setopt(core, QURL_OPT_READ_CB, read_from_user_func); qurl_core_setopt(core, QURL_OPT_READ_CB_ARG, read_from_user_arg); qurl_core_setopt(core, QURL_OPT_WRITE_CB, write_to_user_func); qurl_core_setopt(core, QURL_OPT_WRITE_CB_ARG, write_to_user_arg); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_HTTP_POST_FORM 配置为 HTTP POST 多媒体表单请求方法。 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_HTTP_POST_FORM, long post_form); ``` ##### **功能描述** 指定为HTTP POST请求方法,专用于多媒体表单。 设置为1告诉qurl进行multipart/formdata类型的HTTP POST。创建多个多媒体表单组成列表的最简单方法是使用 `QURL_OPT_FORM`。只要qurl正在传输并且正在使用它,此列表中的数据就必须保持完整。 在HTTP 1.1中使用POST意味着使用 “Expect:100 continue” 标头。您可以使用 `QURL_OPT_HTTP_HEADER` 禁用此标头。 设置 `QURL_OPT_HTTP_POST_FORM` 时,qurl会自动将 `QURL_OPT_NOBODY` 设置为 0。 ##### **默认值** 0。不使能。 ##### **适用协议** HTTP ##### **依赖项** `QURL_OPT_FORM` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; qurl_http_form_cfg_t form_cfg = {0}; qurl_slist_t form1_headers = QOSA_NULL; char *form1_header_ct_ptr = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "http://example.com/foo.bin"); form_cfg.name = "file"; form_cfg.filename = "1.txt"; form_cfg.content_len = 1024; form_cfg.content_type = QURL_HTTP_FORM_CONTENT_CB; form_cfg.content_ptr = read_from_user_arg; form_cfg.read_content_func = read_from_user_func; int ct_len = strlen("Content-Type: ") + strlen("text/plain"); form1_header_ct_ptr = malloc(ct_len + 2); if (form1_header_ct_ptr == QOSA_NULL) { return -1; // TODO: 替换为合适的错误码 } form1_header_ct_ptr[ct_len + 1] = 0x00; snprintf(form1_header_ct_ptr, ct_len + 1, "Content-Type: %s", "text/plain"); form1_headers = qurl_slist_add_strdup(form1_headers, form1_header_ct_ptr); form_cfg.headers_slist = form1_headers; qurl_core_setopt(core, QURL_OPT_FORM, 1L, &form_cfg); qurl_core_setopt(core, QURL_OPT_HTTP_POST, 0L); qurl_core_setopt(core, QURL_OPT_HTTP_POST_FORM, 1L); qurl_core_perform(core); qurl_slist_del_all(form1_headers); free(form1_header_ct_ptr); qurl_core_delete(core); } } ``` #### QURL_OPT_HTTP_PUT 配置为HTTP PUT请求方法。 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_HTTP_PUT, long put); ``` ##### **功能描述** 设置为1的参数告诉qurl库使用HTTP PUT传输数据。 使用 `QURL_OPT_READ_CB`/`QURL_OPT_UPLOAD_DATA`/`QURL_OPT_UPLOAD_FILE` 选项之一指定要 PUT 的数据,并使用 `QURL_OPT_UPLOAD_SIZE` 设置数据大小,文件的方式会读完整个文件,不需要设置数据大小。 在HTTP 1.1中使用 PUT 意味着使用 “Expect:100 continue” 标头。您可以使用 `QURL_OPT_HTTP_HEADER` 禁用此标头。 如果你使用PUT发送数据到HTTP 1.1服务器,如果你使用分块编码,在开始PUT之前,你可以在不知道数据大小的情况下发送数据。您可以通过使用 `QURL_OPT_HTTP_HEADER` 添加 “Transfer Encoding:chunked” 的标头来启用此功能。使用HTTP 1.0不支持分块传输,您必须在请求中指定大小。 ##### **默认值** 0。不使能。 ##### **适用协议** HTTP ##### **依赖项** `QURL_OPT_READ_CB` 或 `QURL_OPT_UPLOAD_DATA` 或 `QURL_OPT_UPLOAD_FILE`, `QURL_OPT_UPLOAD_SIZE` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "http://example.com/foo.bin"); qurl_core_setopt(core, QURL_OPT_HTTP_PUT, 1L); qurl_core_setopt(core, QURL_OPT_UPLOAD_SIZE, 1024); qurl_core_setopt(core, QURL_OPT_READ_CB, read_from_user_func); qurl_core_setopt(core, QURL_OPT_READ_CB_ARG, read_from_user_arg); qurl_core_setopt(core, QURL_OPT_WRITE_CB, write_to_user_func); qurl_core_setopt(core, QURL_OPT_WRITE_CB_ARG, write_to_user_arg); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_HTTP_PUT_FORM 配置为HTTP PUT多媒体表单请求方法。 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_HTTP_PUT_FORM, long put_form); ``` ##### **功能描述** 指定为HTTP PUT请求方法,专用于多媒体表单。 设置为1告诉qurl您希望进行 multipart/formdata 类型的HTTP PUT。创建多个多媒体表单组成列表的最简单方法是使用 `QURL_OPT_FORM`。只要qurl正在传输并且正在使用它,此列表中的数据就必须保持完整。 在HTTP 1.1中使用PUT意味着使用 “Expect:100 continue” 标头。您可以使用 `QURL_OPT_HTTP_HEADER` 禁用此标头。 设置 `QURL_OPT_HTTP_PUT_FORM` 时,qurl 会自动将 `QURL_OPT_NOBODY` 设置为 0。 ##### **默认值** 0。不使能。 ##### **适用协议** HTTP ##### **依赖项** `QURL_OPT_FORM` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; qurl_http_form_cfg_t form_cfg = {0}; qurl_slist_t form1_headers = QOSA_NULL; char *form1_header_ct_ptr = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "http://example.com/foo.bin"); form_cfg.name = "file"; form_cfg.filename = "1.txt"; form_cfg.content_len = 1024; form_cfg.content_type = QURL_HTTP_FORM_CONTENT_CB; form_cfg.content_ptr = read_from_user_arg; form_cfg.read_content_func = read_from_user_func; int ct_len = strlen("Content-Type: ") + strlen("text/plain"); form1_header_ct_ptr = malloc(ct_len + 2); if (form1_header_ct_ptr == QOSA_NULL) { return -1; // TODO: 替换为合适的错误码 } form1_header_ct_ptr[ct_len + 1] = 0x00; snprintf(form1_header_ct_ptr, ct_len + 1, "Content-Type: %s", "text/plain"); form1_headers = qurl_slist_add_strdup(form1_headers, form1_header_ct_ptr); form_cfg.headers_slist = form1_headers; qurl_core_setopt(core, QURL_OPT_FORM, 1L, &form_cfg); qurl_core_setopt(core, QURL_OPT_HTTP_PUT, 0L); qurl_core_setopt(core, QURL_OPT_HTTP_PUT_FORM, 1L); qurl_core_perform(core); qurl_slist_del_all(form1_headers); free(form1_header_ct_ptr); qurl_core_delete(core); } } ``` #### QURL_OPT_HTTP_PATCH 配置为 HTTP PATCH 请求方法。 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_HTTP_PATCH, long patch); ``` ##### **功能描述** 设置为1的参数告诉qurl库使用HTTP PATCH传输数据。 使用 `QURL_OPT_READ_CB`/`QURL_OPT_UPLOAD_DATA`/`QURL_OPT_UPLOAD_FILE` 选项之一指定要 PATCH 的数据,并使用 `QURL_OPT_UPLOAD_SIZE` 设置数据大小,文件的方式会读完整个文件,不需要设置数据大小。 在HTTP 1.1中使用PATCH意味着使用 “Expect:100 continue” 标头。您可以使用 `QURL_OPT_HTTP_HEADER` 禁用此标头。 如果你使用PATCH发送数据到HTTP 1.1服务器,如果你使用分块编码,在开始PATCH之前,你可以在不知道数据大小的情况下发送数据。您可以通过使用 `QURL_OPT_HTTP_HEADER` 添加 “Transfer Encoding:chunked” 的标头来启用此功能。使用HTTP 1.0不支持分块传输,您必须在请求中指定大小。 ##### **默认值** 0。不使能。 ##### **适用协议** HTTP ##### **依赖项** `QURL_OPT_READ_CB` 或 `QURL_OPT_UPLOAD_DATA` 或 `QURL_OPT_UPLOAD_FILE`, `QURL_OPT_UPLOAD_SIZE` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "http://example.com/foo.bin"); qurl_core_setopt(core, QURL_OPT_HTTP_PATCH, 1L); qurl_core_setopt(core, QURL_OPT_UPLOAD_SIZE, 1024); qurl_core_setopt(core, QURL_OPT_READ_CB, read_from_user_func); qurl_core_setopt(core, QURL_OPT_READ_CB_ARG, read_from_user_arg); qurl_core_setopt(core, QURL_OPT_WRITE_CB, write_to_user_func); qurl_core_setopt(core, QURL_OPT_WRITE_CB_ARG, write_to_user_arg); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_HTTP_PATCH_FORM 配置为HTTP PATCH多媒体表单请求方法。 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_HTTP_PATCH_FORM , long patch_form); ``` ##### **功能描述** 指定为HTTP PATCH请求方法,专用于多媒体表单。 设置为1告诉qurl您希望进行multipart/formdata类型的HTTP PATCH。创建多个多媒体表单组成列表的最简单方法是使用 `QURL_OPT_FORM`。只要qurl正在传输并且正在使用它,此列表中的数据就必须保持完整。 在HTTP 1.1中使用PATCH意味着使用 “Expect:100 continue” 标头。您可以使用 `QURL_OPT_HTTP_HEADER` 禁用此标头。 设置 `QURL_OPT_HTTP_PATCH_FORM` 时,qurl 会自动将 `QURL_OPT_NOBODY` 设置为 0。 ##### **默认值** 0。不使能。 ##### **适用协议** HTTP ##### **依赖项** `QURL_OPT_FORM` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; qurl_http_form_cfg_t form_cfg = {0}; qurl_slist_t form1_headers = QOSA_NULL; char *form1_header_ct_ptr = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "http://example.com/foo.bin"); form_cfg.name = "file"; form_cfg.filename = "1.txt"; form_cfg.content_len = 1024; form_cfg.content_type = QURL_HTTP_FORM_CONTENT_CB; form_cfg.content_ptr = read_from_user_arg; form_cfg.read_content_func = read_from_user_func; int ct_len = strlen("Content-Type: ") + strlen("text/plain"); form1_header_ct_ptr = malloc(ct_len + 2); if (form1_header_ct_ptr == QOSA_NULL) { return -1; // TODO: 替换为合适的错误码 } form1_header_ct_ptr[ct_len + 1] = 0x00; snprintf(form1_header_ct_ptr, ct_len + 1, "Content-Type: %s", "text/plain"); form1_headers = qurl_slist_add_strdup(form1_headers, form1_header_ct_ptr); form_cfg.headers_slist = form1_headers; qurl_core_setopt(core, QURL_OPT_FORM, 1L, &form_cfg); qurl_core_setopt(core, QURL_OPT_HTTP_PATCH, 0L); qurl_core_setopt(core, QURL_OPT_HTTP_PATCH_FORM, 1L); qurl_core_perform(core); qurl_slist_del_all(form1_headers); free(form1_header_ct_ptr); qurl_core_delete(core); } } ``` #### QURL_OPT_HTTP_AUTH 配置 HTTP 身份验证方案。 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_HTTP_AUTH, long http_auth); ``` ##### **功能描述** 配置HTTP身份验证方案。选项参考 `qurl_http_auth_e` 的定义(详见附录)。 传入一个long类型的掩码参数,告诉qurl您希望它使用哪种身份验证方法与远程服务器通信。 下面列出了可用位。如果设置了多个位,qurl会首先查询主机以查看它支持哪些身份验证方法,然后选择您允许它使用的最佳方法。对于某些方法,这会导致额外的网络往返。 - **QURL_HTTP_AUTH_BASIC**:HTTP基本身份验证。这是默认选择,也是唯一一种被广泛使用并几乎在任何地方都得到支持的方法。这将以纯文本形式通过网络发送用户名和密码,很容易被其他人捕获。 - **QURL_HTTP_AUTH_DIGEST**:HTTP摘要式身份验证。摘要式身份验证在RFC 2617中定义,是在公共网络上进行身份验证的一种,比常规老式的Basic方法更安全的方法。 - **QURL_HTTP_AUTH_ONLY**:将此值与单个特定的身份验证值一起使用,以强制qurl探测无限制的身份验证,如果没有,则只接受该单个身份验证算法。 ##### **默认值** QURL_HTTP_AUTH_BASIC ##### **适用协议** HTTP ##### **依赖项** `QURL_OPT_USERNAME`、`QURL_OPT_PASSWORD` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); qurl_core_setopt(core, QURL_OPT_HTTP_GET, 1L); qurl_core_setopt(core, QURL_OPT_HTTP_AUTH, QURL_HTTP_AUTH_BASIC); qurl_core_setopt(core, QURL_OPT_USERNAME, "test"); qurl_core_setopt(core, QURL_OPT_PASSWORD, "test"); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_FOLLOWLOCATION 配置使能跟随重定向。 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_FOLLOWLOCATION, long follow); ``` ##### **功能描述** 此配置项置 1 使能跟随重定向功能,HTTP 服务器在 30x 响应中发送该重定向。"Location: new_url" 可以指定要遵循的相对或绝对 URL。 当遵循重定向时,qurl 会发出另一个新URL请求,并遵循后续的新的 "Location: new_url" 中重定向的 URL,直到不再返回此类标头或达到最大限制。`QURL_OPT_REDIRS_CNT_MAX` 用于限制qurl遵循的重定向次数。 在重定向后,特定的 30x 响应代码还指示qurl在后续请求中使用哪种请求方法:对于 301、302 和 303 响应,qurl 将方法从 POST 切换到 GET,除非 `QURL_OPT_POSTREDIR` 另有指示。所有其他重定向响应代码使qurl再次使用相同的方法。 当qurl将方法切换为 GET 时,它将使用该方法而不发送任何请求正文。如果不改变方法,则以与前一个请求相同的方式发送后续请求;如果提供了请求正文,则包括请求正文。 由于 HTTP 的工作方式,几乎任何标头都可以包含客户端可能不想传递给除最初预期主机之外的其他服务器的数据,当qurl被告知跟随重定向时,就没有其他任何保护措施。 ##### **默认值** 0。不使能。 ##### **适用协议** HTTP ##### **依赖项** `QURL_OPT_REDIRS_CNT_MAX` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); qurl_core_setopt(core, QURL_OPT_HTTP_GET, 1L); qurl_core_setopt(core, QURL_OPT_FOLLOWLOCATION, 1L); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_UNRESTRICTED_AUTH 配置允许重定向跟随后对新的地址还提供用户名和密码进行身份验证。 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_UNRESTRICTED_AUTH, long allow_auth_to_other_hosts); ``` ##### **功能描述** 配置此参数 1 使qurl在重定向时继续发送身份验证(用户+密码)凭据,即使主机发生更改。 仅当设置 `QURL_OPT_FOLLOWLOCATION` 时,此选项才有意义。 此外,当不使用此选项或将其设置为 0L 时,qurl 不会在向初始URL所用主机以外的其他主机发出请求时发送自定义或内部生成的 Authentication。另一个主机意味着主机名、scheme 或端口号中的一个或多个发生了变化。 默认情况下,qurl 只向原始URL中给出的初始主机发送 Authentication,以避免将用户名+密码泄露给其他网站。 应谨慎使用此选项:当qurl遵循重定向时,它会按照服务器的指示盲目获取下一个URL。将 `QURL_OPT_UNRESTRICTED_AUTH` 设置为 1 会使qurl信任服务器,并向服务器指向的任何主机发送可能敏感的凭据,可能会一次又一次,因为接下来又可以继续重定向到新主机。 由于HTTP的工作方式,几乎任何标头都可以包含客户端可能不想传递给除最初预期主机之外的其他服务器的数据,当qurl被告知遵循重定向时,就没有其他任何保护措施。 ##### **默认值** 0。不使能。 ##### **适用协议** HTTP ##### **依赖项** `QURL_OPT_FOLLOWLOCATION`、`QURL_OPT_USERNAME`、`QURL_OPT_PASSWORD` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); qurl_core_setopt(core, QURL_OPT_HTTP_GET, 1L); qurl_core_setopt(core, QURL_OPT_FOLLOWLOCATION, 1L); qurl_core_setopt(core, QURL_OPT_UNRESTRICTED_AUTH , 1L); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_AUTOREFERER 配置跟随重定向后是否提供 Referer 字段。 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_AUTOREFERER, long auto_referer); ``` ##### **功能描述** 将long参数设置为1以启用此功能。启用后,当qurl遵循 "Location: new_url" 重定向到新目标时,它会自动将HTTP请求中的Referer字段设置为完整的 URL。 即使重定向是跨域进行的,或者重定向到不安全的协议,自动提供的referer也会设置为完整的前一个URL。一些人认为这是轻微的隐私泄露。 使用 `QURL_OPT_REFERER`,应用程序可以在传输后提取实际使用的REFERER标头。 ##### **默认值** 0。不使能。 ##### **适用协议** HTTP ##### **依赖项** `QURL_OPT_FOLLOWLOCATION` ##### **示例** ```c int main(void) { qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); qurl_core_setopt(core, QURL_OPT_HTTP_GET, 1L); qurl_core_setopt(core, QURL_OPT_FOLLOWLOCATION, 1L); qurl_core_setopt(core, QURL_OPT_AUTOREFERER, 1L); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_POSTREDIR 配置在重定向时仍保持 POST 请求。 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_POSTREDIR, long keep_post); ``` ##### **功能描述** 此配置项配置如何处理HTTP POST重定向,使能后在 30x 请求后保持 POST 请求为 POST 请求。 每个位代表一个请求,从 301 到 303。变量参考 `qurl_http_redir_e` 的定义(详见附录)。 传递一个位掩码来控制qurl如何在 POST 返回 301、302 或 303 响应后对重定向进行操作。 设置了位 0 的参数(QURL_HTTP_REDIR_POST_301)告诉qurl库遵循 RFC 7231(第 6.4.2 节至第 6.4.4 节) 的规定,在 301 重定向之后不要将 POST 请求转换为 GET 请求。 设置位 1(QURL_HTTP_REDIR_POST_302) 使qurl在 302 重定向后仍然保持 POST 请求方法。 而设置位 2(QURL_HTTP_REDIR_POST_303) 使qurl在 303 重定向后仍然保持 POST 请求方法。 值 `QURL_HTTP_REDIR_POST_ALL` 是一个方便的定义,用于设置所有三个位。 仅当设置 `QURL_OPT_FOLLOWLOCATION` 时,此选项才有意义。 ##### **默认值** 0。不使能。 ##### **适用协议** HTTP ##### **依赖项** `QURL_OPT_FOLLOWLOCATION` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "http://example.com/foo.bin"); qurl_core_setopt(core, QURL_OPT_HTTP_POST, 1L); qurl_core_setopt(core, QURL_OPT_FOLLOWLOCATION, 1L); qurl_core_setopt(core, QURL_OPT_POSTREDIR, 1L); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_FORM 配置多媒体表单的内容。 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_FORM, form_num, qurl_http_form_cfg_t* form_cfg_ptr); ``` ##### **功能描述** 该配置项用于多媒体表单的设置,参考 `qurl_http_form_cfg_t` 的定义(详见附录)。 通过传入表单配置数据结构的指针去配置多媒体表单的内容。 如果需要发送多个多媒体表单,可以通过 `form_num` 去区分不同的多媒体表单。 ##### **默认值** QOSA_NULL ##### **适用协议** HTTP ##### **依赖项** `QURL_OPT_HTTP_POST_FORM` 或 `QURL_OPT_HTTP_PUT_FORM` 或 `QURL_OPT_HTTP_PATCH_FORM` ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; qurl_http_form_cfg_t form_cfg = {0}; qurl_slist_t form1_headers = QOSA_NULL; char *form1_header_ct_ptr = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "http://example.com/foo.bin"); form_cfg.name = "file"; form_cfg.filename = "1.txt"; form_cfg.content_len = 1024; form_cfg.content_type = QURL_HTTP_FORM_CONTENT_CB; form_cfg.content_ptr = read_from_user_arg; form_cfg.read_content_func = read_from_user_func; int ct_len = strlen("Content-Type: ") + strlen("text/plain"); form1_header_ct_ptr = malloc(ct_len + 2); if (form1_header_ct_ptr == QOSA_NULL) { return -1; // TODO: 替换为合适的错误码 } form1_header_ct_ptr[ct_len + 1] = 0x00; snprintf(form1_header_ct_ptr, ct_len + 1, "Content-Type: %s", "text/plain"); form1_headers = qurl_slist_add_strdup(form1_headers, form1_header_ct_ptr); form_cfg.headers_slist = form1_headers; qurl_core_setopt(core, QURL_OPT_FORM, 1L, &form_cfg); qurl_core_setopt(core, QURL_OPT_HTTP_POST, 0L); qurl_core_setopt(core, QURL_OPT_HTTP_POST_FORM, 1L); qurl_core_perform(core); qurl_slist_del_all(form1_headers); free(form1_header_ct_ptr); qurl_core_delete(core); } } ``` #### QURL_OPT_HTTP_HEADER 配置用户自定义的HTTP的请求头 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_HTTP_HEADER, qurl_slist_t http_headers_slist); ``` ##### **功能描述** 配置的是用户自定义的HTTP请求头各个字段。在请求头中拥有最高优先级。 传递一个指向HTTP header链表的指针,以在HTTP请求中传递给服务器或代理。 此选项可以添加新标头、会替换掉内部标头。 链表应该是正确添加 `qurl_slist_t` 条目的有效列表。使用 `qurl_slist_add_strdup()` 创建列表,使用 `qurl_slist_del_all()` 在使用后释放它。 如果您提供了一个定义的HTTP请求头,则将使用您的标头替代qurl内部生成标头。如果您提供了一个没有内容的标头(冒号右侧没有数据),则内部的标头将被删除。 链表中包含的标头不得以 CRLF 结尾,因为qurl会自动在每个标头项之后添加 CRLF。不遵守这一点可能会导致奇怪的问题。qurl 将会传递你给它的逐字字符串,没有任何过滤器或其他安全防护。包括空格和控制字符。 HTTP 请求中的第一行(包含方法,通常是 GET 或 POST)不是标头,不能使用此选项。只有请求行之后的行是标头。在此标头列表中添加HTTP方法行只会导致您的请求发送无效的标头。 当此选项传递给 `qurl_core_setopt()` 时,qurl 不会在内部复制整个列表,因此您必须保留它,直到您不再使用此句柄进行传输,然后才能调用 `qurl_slist_del_all()` 释放列表。 多次使用此选项会使最后一组列表覆盖之前的列表。将其设置为 QOSA_NULL 可以清除之前配置的自定义请求头。 ##### **默认值** QOSA_NULL ##### **适用协议** HTTP ##### **依赖项** 无 ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; qurl_slist_t headers = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); qurl_core_setopt(core, QURL_OPT_HTTP_GET, 1L); headers = qurl_slist_add_strdup(headers, "Content-Type: application/json"); headers = qurl_slist_add_strdup(headers, "Accept-Charset: utf-8"); qurl_core_setopt(core, QURL_OPT_HTTP_HEADER, headers); qurl_core_perform(core); qurl_slist_del_all(headers); qurl_core_delete(core); } } ``` #### QURL_OPT_REFERER 配置HTTP Referer字符串。 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_REFERER, char * referer_ptr); ``` ##### **功能描述** HTTP的Referer字段是一个可选的请求头字段,用于指示当前请求是从哪个页面链接过来的。Referer字段通常包含发起请求的页面的URL,可以帮助服务器了解用户是如何找到当前资源的。 传入指向以null结尾的字符串的指针。它用于设置发送到远程服务器的HTTP请求中的 “Referer:” 字段。您也可以使用 `QURL_OPT_HTTP_HEADER` 设置任何自定义标头。 设置此选项后,应用程序不必保留字符串。 多次使用此选项会使最后一组字符串覆盖前面的字符串。设置为 `QOSA_NULL` 可以将其清除。 ##### **默认值** QOSA_NULL ##### **适用协议** HTTP ##### **依赖项** 无 ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); qurl_core_setopt(core, QURL_OPT_HTTP_GET, 1L); qurl_core_setopt(core, QURL_OPT_REFERER, "https://example.org/me.html"); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_ACCEPT_ENCODING 配置HTTP Accept-Encoding字符串。 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_ACCEPT_ENCODING, char * accept_encoding_ptr); ``` ##### **功能描述** 该配置项传递一个char指针参数,设置HTTP请求中发送的 “Accept-Encoding:” 的内容,指定您想要的编码方式。并在收到 “Content-Encoding:” 时启用对响应的解码。您还可以使用 `QURL_OPT_HTTP_HEADER` 设置任何自定义标头。 HTTP 的Accept-Encoding请求头字段用于指示客户端支持的内容编码(压缩算法),以便服务器在响应中可以选择一种合适的编码方式。内容编码可以减少传输数据的大小,从而提高传输效率。 以下是一些常见的内容编码: 1. **gzip**:GNU Zip是一种广泛使用的压缩算法,可以显著减小传输数据的大小。 2. **deflate**:Deflate是一种基于zlib的压缩算法,通常比gzip压缩效果更好,但压缩和解压速度较慢。 3. **br**:Brotli是一种较新的压缩算法,由Google开发,具有更好的压缩率和压缩速度。 qurl支持的压缩编码取决于库的编译配置。 设置此选项后,应用程序不必保留字符串。 多次使用此选项会使最后一组字符串覆盖前面的字符串。设置为 `QOSA_NULL` 可以将其清除。 ##### **默认值** QOSA_NULL ##### **适用协议** HTTP ##### **依赖项** 无 ##### **示例** ```c int main(void){ qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); qurl_core_setopt(core, QURL_OPT_HTTP_GET, 1L); qurl_core_setopt(core, QURL_OPT_ACCEPT_ENCODING, "gzip, deflate, br"); qurl_core_perform(core); qurl_core_delete(core); } } ``` #### QURL_OPT_USER_AGENT 配置HTTP User-Agent字符串。 ##### **函数原型** ```c qurl_ecode_t qurl_core_setopt(qurl_core_t core, QURL_OPT_USER_AGENT, char *user_agent_ptr); ``` ##### **功能描述** HTTP的User-Agent请求头字段用于标识发出请求的客户端应用程序的类型和版本。User-Agent字段通常包含有关浏览器、操作系统、设备类型等信息,有助于服务器了解客户端的能力和特性,从而提供更合适的内容。 将指向以null结尾的字符串的指针作为参数传递。它用于设置发送到远程服务器的HTTP请求中的 “User-Agent:” 字段。您还可以使用 `QURL_OPT_HTTP_HEADER` 设置任何自定义标头。 设置此选项后,应用程序不必保留字符串。 多次使用此选项会使最后一组字符串覆盖前面的字符串。设置为 `QOSA_NULL` 可以将其清除。 ##### **默认值** QOSA_NULL ##### **适用协议** HTTP ##### **依赖项** 无 ##### **示例** ```c int main(void) { qurl_core_t core = QOSA_NULL; if (QURL_OK == qurl_core_create(&core)) { qurl_core_setopt(core, QURL_OPT_URL, "https://example.com"); qurl_core_setopt(core, QURL_OPT_HTTP_GET, 1L); qurl_core_setopt(core, QURL_OPT_USER_AGENT, "MyEmbeddedDevice/1.0"); qurl_core_perform(core); qurl_core_delete(core); } } ``` ## 结构体定义 #### qurl_tls_cfg_t ```c /** * @struct qurl_tls_cfg_t * @brief tls配置参数 */ struct qurl_tls_cfg_s { qurl_tls_version_e version; /*!< tls 版本 */ int *ciphersuites; /*!< 加密套件列表,遇到 0x0000 为止,如果指针为NULL,表示支持所有加密套件。 */ qurl_tls_verify_e verify; /*!< 证书验证模式 */ qurl_slist_t ca_cert_path_slist; /*!< CA证书路径单向链表 */ char *own_cert_path_ptr; /*!< 客户端公钥数字证书路径 */ char *own_key_path_ptr; /*!< 客户端私钥文件路径 */ char *own_key_pwd_ptr; /*!< 客户端私钥密码串 */ long negotiate_timeout; /*!< 握手超时时间,单位:s */ int ignore_invalid_certsign; /*!< 忽略无效的证书签名 */ int ignore_multi_certchain_verify; /*!< 忽略多级证书链校验 */ unsigned int ignore_certitem; /*!< 是否忽略对证书的某项校验 */ int ssl_log_debug; /*!< 是否打印调试日志 */ int mfl_support; /*!< 是否支持mfl */ int mfl_size; /*!< mfl长度 */ qurl_tls_cfg_bits_t bits; }; typedef struct qurl_tls_cfg_s qurl_tls_cfg_t; ``` #### qurl_so_linger_t ```c /** * @struct qurl_so_linger_t * @brief socket opt SO_LINGER 选项配置 */ struct qurl_so_linger_s { int l_onoff; /* 0 = off, nozero = on */ int l_linger; /* linger time, second */ }; typedef struct qurl_so_linger_s qurl_so_linger_t; ``` #### qurl_http_form_cfg_t ```c /** * @struct qurl_http_form_cfg_t * @brief qurlhttp 表单配置数据结构 */ struct qurl_http_form_cfg_s { qurl_http_form_e content_type; /*!< content_ptr的类型 */ long content_len; /*!< 发送 content 字段的长度 */ char *name_ptr; /*!< 表单的 name 字段 */ char *filename_ptr; /*!< 表单的 filename 字段 */ void *content_ptr; /*!< 表单的内容read_content_func的参数 */ qurl_http_form_read_cb read_content_func; /*!< 单表单的内容,仅在QURL_HTTP_FORM_CONTENT_CB时有效 */ qurl_slist_t headers_slist; /*!< 表单的用户自定义头。资源由用户维护,所以请用户确保其运行安全性。 */ }; typedef struct qurl_http_form_cfg_s qurl_http_form_cfg_t; ``` ## 枚举定义 #### qurl_ecode_e ```c /** * @enum qurl_ecode_e * @brief qurl全局错误码 */ typedef enum { QURL_OK = 0, QURL_ECODE_OK = 0, QURL_ECODE_FAILURE = (1 | QURL_CFG_ECODE_BASIC_NUM | 0x80000000), QURL_ECODE_PARAM_INVALID, QURL_ECODE_NO_MEMORY, QURL_ECODE_NO_PERMISSION, QURL_ECODE_OVER_LIMIT, QURL_ECODE_BUF_TOO_SMALL, QURL_ECODE_NO_URL, QURL_ECODE_NO_DATA_SEND, QURL_ECODE_PERFORM_TIMEOUT, QURL_ECODE_LOCK_TIMEOUT, QURL_ECODE_EVENT_TIMEOUT, QURL_ECODE_TLS_BAD_CONTENT_ENCODING, QURL_ECODE_TRANS_TOO_MANY_REDIRECTS, /*!< 重定向次数太多了 */ QURL_ECODE_CLIENT_WRITE_FAILD, /*!< 把数据写给上层用户时失败 */ QURL_ECODE_PROT_NO_MATCH, /*!< 没匹配到协议接口 */ QURL_ECODE_NO_SUPPORT, /*!< 不支持该功能或配置,如不支持该协议 */ QURL_ECODE_NOT_FIND, QURL_ECODE_UNKNOWN_OPTION, QURL_ECODE_FILESIZE_EXCEEDED, QURL_ECODE_DOWNLOAD_RESUME_ERR, /*!< 续传错误 */ QURL_ECODE_UPLOAD_ERR, /*!< 上传时间错误 */ QURL_ECODE_RANGE_ERR, /*!< range错误 */ QURL_ECODE_WRITE_HEAD_TO_CLIENT_FAILED, QURL_ECODE_NETWORK_ERR, /*!< 网络错误 */ QURL_ECODE_STATE_INVALID, /*!< 状态无效 */ QURL_ECODE_ABORT, /*!< 被终止了 */ QURL_ECODE_THREAD_CREATE_FAILED = (QURL_ECODE_FAILURE + 0x0020), QURL_ECODE_LOCK_CREATE_FAILD, QURL_ECODE_LOCK_DELETE_FAILD, QURL_ECODE_LOCK_LOCK_FAILD, QURL_ECODE_LOCK_UNLOCK_FAILD, QURL_ECODE_EVENT_FAILD, QURL_ECODE_CONN_CONNECT_FAILD = (QURL_ECODE_FAILURE + 0x0030), QURL_ECODE_CONN_CACHE_NOT_MATCH, QURL_ECODE_CONN_ALREADY_ATTACH, QURL_ECODE_CONN_NOT_ATTACH, QURL_ECODE_CONN_NOT_RESOLVE, QURL_ECODE_CONN_NOT_FOUND_IP, QURL_ECODE_CONN_CONNECT_FAILED, QURL_ECODE_CONN_FAMILY_NOT_SUPPORTED, QURL_ECODE_CONN_NOT_FOUND_SOCKFD, QURL_ECODE_CONN_SEND_FAILD, QURL_ECODE_URL_MALFORMED = (QURL_ECODE_FAILURE + 0x0040), QURL_ECODE_URL_MALFORMED_INPUT, QURL_ECODE_URL_TOO_LONG, QURL_ECODE_URL_BAD_PORT_NUMBER, QURL_ECODE_URL_USER_NOT_ALLOWED, QURL_ECODE_URL_NO_SCHEME, QURL_ECODE_URL_NO_USER, QURL_ECODE_URL_NO_PASSWORD, QURL_ECODE_URL_NO_OPTIONS, QURL_ECODE_URL_NO_HOST, QURL_ECODE_URL_NO_PORT, QURL_ECODE_URL_NO_QUERY, QURL_ECODE_URL_NO_FRAGMENT, QURL_ECODE_URL_DECODE, QURL_ECODE_SOCK_SOCK_FAILED = (QURL_ECODE_FAILURE + 0x0050), QURL_ECODE_SOCK_BIND_FAILED, QURL_ECODE_SOCK_CTRL_FAILED, QURL_ECODE_SOCK_LISTEN_FAILED, QURL_ECODE_SOCK_ACCEPT_FAILED, QURL_ECODE_SOCK_CONNECT_FAILED, QURL_ECODE_SOCK_SELECT_FAILED, QURL_ECODE_SOCK_WRITE_FAILED, QURL_ECODE_SOCK_READ_FAILED, QURL_ECODE_SOCK_CLOSE_FAILED, QURL_ECODE_SOCK_CONN_CLOSED, /*!< 连接关闭了 */ QURL_ECODE_SOCK_SETOPT_FAILED, QURL_ECODE_FS_OPEN_ERR = (QURL_ECODE_FAILURE + 0x0060), /*!< 文件打开错误 */ QURL_ECODE_FS_READ_ERR, QURL_ECODE_PP_ERR = (QURL_ECODE_FAILURE + 0x0070), /*!< pingpong错误 */ QURL_ECODE_PP_TIMEOUT, /*!< pingpong超时 */ QURL_ECODE_TLS_INIT_FAILED = (QURL_ECODE_FAILURE + 0x0080), /*!< TLS初始化失败 */ QURL_ECODE_TLS_CREATE_FAILED, /*!< TLS创建失败 */ QURL_ECODE_TLS_READ_ERR, /*!< TLS读取错误 */ QURL_ECODE_TLS_WRITE_ERR, /*!< TLS写错误 */ QURL_ECODE_TLS_WRITE_LEN_ERR, /*!< TLS写入数据的长度有误 */ QURL_ECODE_TLS_READ_AGAIN, /*!< 需要更多的数据才能继续读取。如块解密 */ QURL_ECODE_TLS_WRITE_AGAIN, /*!< 需要更多的空间才能写入发送。如块解密 */ QURL_ECODE_TLS_NEGOTIATE_ERR, /*!< 握手错误 */ QURL_ECODE_TLS_NEGOTIATE_TIMEOUT, /*!< 握手超时 */ QURL_ECODE_TLS_RANDOM_ERR, /*!< 随机数错误 */ QURL_ECODE_TLS_CONFIG_ERR, /*!< 配置错误 */ QURL_ECODE_TLS_CONNECT_ERR, /*!< 连接错误 */ QURL_ECODE_TLS_GET_SESSION_FAILED, /*!< 获取SESSION失败 */ QURL_ECODE_TLS_SET_SESSION_FAILED, /*!< 设置SESSION失败 */ QURL_ECODE_TLS_PEER_CLOSE_NOTIFY, /*!< 通知准备关闭,正常结果码 */ QURL_ECODE_HTTP_VER_ERR = (QURL_ECODE_FAILURE + 0x0090), /*!< http 版本错误 */ QURL_ECODE_HTTP_METHOD_ERR, /*!< http 请求方法错误 */ QURL_ECODE_HTTP_RESP_HEADER_ERR, /*!< http 响应头错误 */ QURL_ECODE_HTTP_UNSUPPORTED_PROTOCOL, /*!< http 响应不支持的协议 */ QURL_ECODE_HTTP_AUTH_FAILED, /*!< http 身份认证失败 */ QURL_ECODE_HTTP_CHUNKED_BAD, /*!< chunked 错误 */ QURL_ECODE_HTTP_CHUNKED_HEX_TOO_LONG, /*!< chunked hex 字段太长 */ QURL_ECODE_HTTP_CHUNKED_HEX_ILLEGAL, /*!< chunked hex 字段非法 */ QURL_ECODE_HTTP_MULTIFORM_ERR, /*!< 多表单运行异常 */ QURL_ECODE_HTTP_MULTIFORM_STATE_ERR, /*!< 多表单状态机运行异常 */ QURL_ECODE_FTP_ERR = (QURL_ECODE_FAILURE + 0x00A0), /*!< 通用错误 */ QURL_ECODE_FTP_WEIRD_SERVER_REPLY, /*!< 收到了不正常或无法解析的服务器回复 */ QURL_ECODE_FTP_LOGIN_DENIED, /*!< 登录失败,检查用户名、密码和账户信息 */ QURL_ECODE_FTP_REMOTE_ACCESS_DENIED, /*!< 服务器拒绝访问某项服务,原因是缺乏权限。当登录失败时,通常不会返回这个错误代码 */ QURL_ECODE_FTP_REMOTE_FILE_NOT_FOUND, /*!< 未找到远端文件 */ QURL_ECODE_FTP_COULDNT_USE_REST, /*!< 不能使用REST */ QURL_ECODE_FTP_WEIRD_PASV_REPLY, /*!< RASV响应失败 */ QURL_ECODE_FTP_WEIRD_227_FORMAT, /*!< 227响应码格式错误 */ QURL_ECODE_FTP_CMD_QUOTE_ERR, /*!< QUOTE 命令失败 */ QURL_ECODE_FTP_CMD_TYPE_ERR, /*!< TYPE 命令失败 */ QURL_ECODE_FTP_CMD_PRET_ERR, /*!< PRET 命令失败 */ QURL_ECODE_FTP_CMD_PROT_ERR, /*!< PROT 命令失败 */ QURL_ECODE_FTP_CANT_GET_HOST, /*!< 拿不到主机名 */ QURL_ECODE_FTP_COULDNT_RETR_FILE, /*!< 无法从 FTP 服务器上检索(retrieve)文件 */ QURL_ECODE_FTP_REMOTE_DISK_FULL, /*!< 服务器磁盘空间不足 */ QURL_ECODE_FTP_PARTIAL_FILE, /*!< 文件只有部分被下载或上传。这可能是由于网络中断或传输过程中出现其他问题导致的 */ 现其他问题导致的 */ QURL_ECODE_FTP_ACCEPT_ERR, /*!< 主动模式数据连接accept失败 */ QURL_ECODE_FTP_QUOTE_ERR, /*!< QUOTE发送失败 */ QURL_ECODE_SMTP_ERR = (QURL_ECODE_FAILURE + 0x00C0), /*!< 通用错误 */ QURL_ECODE_SMTP_URL_MALFORMAT, /*!< 收到了不正常或无法解析的服务器回复 */ QURL_ECODE_SMTP_AUTH_FAILED, QURL_ECODE_SMTP_UNSPPORTED_PROTOCOL, QURL_ECODE_SMTP_WEIRD_SERVER_REPLY, QURL_ECODE_SMTP_REMOTE_ACCESS_DENIED, QURL_ECODE_SMTP_LOGIN_DENIED, QURL_ECODE_SMTP_RECV_ERROR, QURL_ECODE_SMTP_SEND_ERROR, } qurl_ecode_e; ``` #### close_event_t ```c /** * @enum close_event_t * @brief qurl实例最后关闭的原因 */ typedef enum { // Ordinary TCP Connection Termination Event CLOSE_EVENT_NORMAL = 0, // TCP connection disconnection event caused by the server actively sending a FIN packet CLOSE_EVENT_FIN, // The server actively sends an RST packet, causing the TCP connection to be disconnected event. CLOSE_EVENT_RST, // Event of TCP connection disconnection caused by internal SYN retransmission timeout CLOSE_EVENT_SYN_TIMEOUT, // TCP connection disconnection event caused by internal ACK retransmission timeout CLOSE_EVENT_ACK_TIMEOUT, } close_event_t; ``` #### qurl_info_e ```c /** * @enum qurl_info_e * @briefqurl信息选项 */ typedef enum { QURL_INFO_URL = 0x2000, /*!< URL */ QURL_INFO_RESP_CODE, QURL_INFO_RESP_CONTENT_LENGTH, /*!< http:是头字段中的 Content-Length,-1表示没有该字段,如chunck。ftp:SIZE命令等。 */ QURL_INFO_FILETIME, /*!< 格林尼治时间 */ QURL_INFO_RESP_DATE, /*!< http:是头字段中的 Date */ QURL_INFO_START_POS, /*!< ftp:分段传输的起始位置 */ } qurl_info_e; ``` #### qurl_opt_e ```c /** * @enum qurl_opt_e * @briefqurl配置选项 */ typedef enum { QURL_OPT_URL = 0x1000, /*!< URL */ QURL_OPT_USERNAME, /*!< 用户名:"user" */ QURL_OPT_PASSWORD, /*!< 密码:"password" */ QURL_OPT_ACCOUNT, /*!< 账户:"xxx" */ QURL_OPT_NETWORK_ID, /*!< 用户指定网络id。<0表示不指定。>=0:参考平台适配说明。 */ QURL_OPT_PORT, /*!< 用户指定端口号 */ QURL_OPT_TIMEOUT_MS, /*!< 整个操作超时时间 */ QURL_OPT_IDLE_TIMEOUT_MS, /*!< 操作时内部空闲超时时间 */ QURL_OPT_ROUSE_CHECK_TIME_MS, /*!< 唤醒检查时间。默认1000ms。主要用于长时间阻塞时,会按这个时间值定时唤醒检查其他事件,如检查是否收到调用qurl_core_abort()触发的事件。 */ QURL_OPT_RESUME_FROM, /*!< 在指定的偏移量处恢复传输,单位:byte。<0时,表示从最后开始往前算。 */ QURL_OPT_RANGE, /*!< 下载范围:如设置 HTTP Range 字符串;如ftp指定范围 */ QURL_OPT_REDIRS_CNT_MAX, /*!< 最大的重定向跟随次数 */ QURL_OPT_UPLOAD_HEAD_RAW, /*!< 标记上传HEAD原始数据,不需要内部提供合成。内部会从HEAD_DATA/HEAD_FILE/HEAD_CB中获取,如果三者均未设置也不会抛出异常,这会被判定为从DATA/FILE/CB中一同获取HEAD和BODY。 */ QURL_OPT_WRITE_HEAD_CB, /*! client 的响应头回调函数,类型:qurl_write_head_cb */ QURL_OPT_WRITE_HEAD_CB_ARG, /*! client 的响应头回调函数的参数,类型:void * */ QURL_OPT_WRITE_CB, /*! client 的回调函数,类型:qurl_write_cb */ QURL_OPT_WRITE_CB_ARG, /*! client 的回调函数的参数,类型:void * */ QURL_OPT_READ_HEAD_CB, /*!< client -->qurl 的请求头回调函数,类型:qurl_read_head_cb */ QURL_OPT_READ_HEAD_CB_ARG, /*!< client -->qurl 的请求头回调函数的参数,类型:void * */ QURL_OPT_READ_CB, /*!< client -->qurl 的回调函数,类型:qurl_read_cb */ QURL_OPT_READ_CB_ARG, /*!< client -->qurl 的回调函数的参数,类型:void * */ QURL_OPT_UPLOAD_HEAD_DATA, /*!< 指定上传的请求头数据,传指针。 */ QURL_OPT_UPLOAD_HEAD_RAW, /*!< 标记上传HEAD原始数据,不需要内部提供合成。内部会从HEAD_DATA/HEAD_FILE/HEAD_CB中获取,如果三者均未设置也不会抛出异常,这会被判定为从DATA/FILE/CB中一同获取HEAD和BODY。 */ QURL_OPT_WRITE_HEAD_CB, /*! client 的响应头回调函数,类型:qurl_write_head_cb */ QURL_OPT_WRITE_HEAD_CB_ARG, /*! client 的响应头回调函数的参数,类型:void * */ QURL_OPT_WRITE_CB, /*! client 的回调函数,类型:qurl_write_cb */ QURL_OPT_WRITE_CB_ARG, /*! client 的回调函数的参数,类型:void * */ QURL_OPT_READ_HEAD_CB, /*!< client -->qurl 的请求头回调函数,类型:qurl_read_head_cb */ QURL_OPT_READ_HEAD_CB_ARG, /*!< client -->qurl 的请求头回调函数的参数,类型:void * */ QURL_OPT_READ_CB, /*!< client -->qurl 的回调函数,类型:qurl_read_cb */ QURL_OPT_READ_CB_ARG, /*!< client -->qurl 的回调函数的参数,类型:void * */ QURL_OPT_UPLOAD_HEAD_DATA, /*!< 指定上传的请求头数据,传指针。 */ /* 中间件相关 */ QURL_OPT_BOUND_THREAD = (QURL_OPT_URL + 0x0100), /*!< 锁定指定线程。NULL:锁定当前线程。注:这里只是更新可以锁定的线程,是否锁定会按维持原状态。 */ QURL_OPT_BOUND_THREAD_CTRL, /*!< qurl业务锁定线程:0:解绑。1:绑定用户指定线程。2:绑定qurl业务创建线程。 */ QURL_OPT_REUSE_FRESH, /*!< 强制新建conn。即不会从conn池中寻找现存连接。 */ QURL_OPT_REUSE_FORBID, /*!< 强制释放conn。即不会把当前conn共享到conn池中。 */ QURL_OPT_REUSE_HOLD, /*!< 持久占用连接标志。 */ QURL_OPT_CONN_IDLE_TIMEOUT_MS, /*!< conn未被使用的超时时间。类似curl中的CURLOPT_MAXAGE_CONN */ QURL_OPT_CONN_MAXLIFETIME_MS, /*!< conn最长生命 */ /* 协议相关 */ /** 多协议相关 */ /** socket */ QURL_OPT_SOCKET_SO_LINGER = (QURL_OPT_URL + 0x0200), /*!< SO_LINGER 配置,传入参数为qurl_so_linger_t。 */ QURL_OPT_SOCKET_SO_KEEPALIVE, /*!< SO_KEEPALIVE 配置,开启或关闭TCP的保活功能。 */ QURL_OPT_SOCKET_TCP_KEEPIDLE, /*!< TCP_KEEPIDLE 配置,保活功能空闲时间。 */ QURL_OPT_SOCKET_TCP_KEEPINTVL, /*!< TCP_KEEPINTVL 配置,保活功能报文间隔。 */ QURL_OPT_SOCKET_TCP_KEEPCNT, /*!< TCP_KEEPINTVL 配置,保活功能报文次数。 */ /** TLS */ QURL_OPT_TLS_CFG = (QURL_OPT_URL + 0x0280), /*!< 配置TLS CFG。注意:传的是指针,实体资源由用户维护。 */ QURL_OPT_TLS_USETLS, /*!< 指定连接使用TLS,参考 qurl_usetls_e */ /** http 协议 */ QURL_OPT_HTTP_VERSION = (QURL_OPT_URL + 0x0300 ), /*!< 通过设置QURL_OPT_HTTP_VERSION选项,你可以指定要使用的特定HTTP协议版本。这个选项的值应该是下面列出的QURL_HTTP_VERSION*枚举之一。 */ QURL_OPT_HTTP_GET, /*!< 指定为http get请求方法 */ QURL_OPT_HTTP_POST, /*!< 指定为http post请求方法 */ QURL_OPT_HTTP_POST_FORM, /*!< 指定为http post请求方法,专用于多表单 */ QURL_OPT_HTTP_PUT, /*!< 指定为http put请求方法 */ QURL_OPT_HTTP_PUT_FORM, /*!< 指定为http put请求方法,专用于多表单 */ QURL_OPT_HTTP_PATCH, /*!< 指定为http patch请求方法 */ QURL_OPT_HTTP_PATCH_FORM, /*!< 指定为http patch请求方法,专用于多表单 */ QURL_OPT_HTTP_AUTH, /*!< HTTP 身份验证方案。选项参考:qurl_http_auth_e */ QURL_OPT_FOLLOWLOCATION, /*!< 是否允许跟随重定向 */ QURL_OPT_UNRESTRICTED_AUTH, /*!< 是否允许重定向跟随后对新的地址还提供用(户名和密码)进行身份验证 */ QURL_OPT_AUTOREFERER, /*!< 跟随重定向后是否提供 Referer 字段 */ QURL_OPT_POSTREDIR, /*!< 在30x请求后保持POST请求为POST请求;每个位代表一个请求,从301到303。变量参考:qurl_http_redir_e */ QURL_OPT_FORM, /*!< 表单设置,参考 qurl_http_form_cfg_t */ QURL_OPT_HTTP_HEADER, /*!< 用户自定义的http的请求头 */ QURL_OPT_REFERER, /*!< 设置 HTTP Referer 字符串 */ QURL_OPT_ACCEPT_ENCODING, /*!< 设置 HTTP Accept-Encoding 字符串 */ QURL_OPT_USER_AGENT, /*!< 设置 HTTP User-Agent 字符串 */ /** ftp 协议 */ QURL_OPT_FTP_FILEMETHOD = (QURL_OPT_URL + 0x0400), /*!< 指定FTP 获取文件方式,参考:qurl_ftp_filemethod_e */ QURL_OPT_FTP_AUTH, /*!< 指定FTP AUTH 认证机制,参考:qurl_ftp_auth_e */ QURL_OPT_FTP_SKIP_PASV_IP, /*!< PASV模式下是否忽略服务器下发的指定的数据连接IP。默认开启 */ QURL_OPT_FTP_PORT, /*!< 是否启用主动模式并指定端口号。(FTP引擎默认被动)。接收格式:(ipv4|ipv6|域名|网络接口)?(:端口(-范围)?)? 注:网络接口暂未实现 */ QURL_OPT_FTP_USE_EPRT, /*!< 表示启用或禁用FTP引擎的EPRT命令。默认情况下,它会尝试使用EPRT,然后尝试使用传统的PORT命令。 */ QURL_OPT_FTP_USE_EPSV, /*!< 表示启用或禁用FTP引擎的EPSV命令。默认情况下FTP引擎会先使用EPSV,再考虑PASV。 */ QURL_OPT_FTP_USE_PRET, /*!< 发送PASV前先发送PRET。 */ QURL_OPT_FTP_TLS_CCC, /*!< 控制FTP连接是否使用CCC以及切换到何种状态,参考:qurl_ftp_ccc_e */ QURL_OPT_QUOTE, /*!< 用户输入的命令链表。在FTP连接建立后,且在每次文件传输之前都发送指定的QUOTE命令 */ QURL_OPT_POSTQUOTE, /*!< 用户输入的命令链表。在FTP传输完成之后,但仍处于连接状态时,发送指定的QUOTE命令。 */ QURL_OPT_PREQUOTE, /*!< 用户输入的命令链表。在FTP连接建立后,但还未进行实际文件传输之前,发送指定的QUOTE命令。 */ QURL_OPT_SMTP_MAIL_FROM, /*!< 设置邮件的发送者 */ QURL_OPT_SMTP_MAIL_RCPT, /*!< 设置邮件的接收者 */ QURL_OPT_SMTP_MAILAUTH, /*!< 设置邮件的接收者 */ QURL_OPT_SMTP_MAIl_RCPT_ALLOWFAILS, /*!< 设置邮件的接收者 */ QURL_OPT_SMTP_LOGIN_OPTIONS, /*!< LOGIN 鉴权 */ QURL_OPT_SMTP_XOAUTH2_BEARER, /*!< XOAUTH2 鉴权 */ /** smtp 协议 */ } qurl_opt_e; ``` #### qurl_http_version_e ```c /** * @struct qurl_http_version_e * @brief http支持的版本 */ typedef enum { QURL_HTTP_VERSION_NONE, /*!< 不指定任何HTTP版本,让库自动选择最优版本 */ QURL_HTTP_VERSION_1_0, /*!< 在请求中使用HTTP 1.0协议版本 */ QURL_HTTP_VERSION_1_1, /*!< 在请求中使用HTTP 1.1协议版本 */ QURL_HTTP_VERSION_2_0, /*!< 在请求中使用HTTP 2.0协议版本 */ QURL_HTTP_VERSION_2TLS, /*!< 对于HTTPS使用HTTP 2.0,对于HTTP使用HTTP 1.1 */ QURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE, /*!< 不使用HTTP/1.1升级,直接使用HTTP 2.0协议版本 */ QURL_HTTP_VERSION_3, /*!< 明确地使用HTTP/3协议版本,支持HTTP/3的服务器将使用该版本响应 */ QURL_HTTP_VERSION_LAST /*!< 非法的HTTP版本 */ } qurl_http_version_e; ``` #### qurl_http_auth_e ```c /** * @struct qurl_http_auth_e * @brief http 身份认证方案选项 */ typedef enum { QURL_HTTP_AUTH_NONE = ((unsigned long)0), /*!< 无HTTP身份认证。 */ QURL_HTTP_AUTH_BASIC = (((unsigned long)1) << 0), /*!< HTTP基本身份认证(默认)。 */ QURL_HTTP_AUTH_DIGEST = (((unsigned long)1) << 1), /*!< HTTP摘要身份认证。 */ QURL_HTTP_AUTH_ONLY = (((unsigned long)1) << 31), /*!< 与其他单个类型一起使用,强制不进行身份认证或仅使用该单个类型。 */ QURL_HTTP_AUTH_ALL = (QURL_HTTP_AUTH_BASIC | QURL_HTTP_AUTH_DIGEST), /*!< 除了CURLAUTH_DIGEST_IE之外的所有可用身份认证类型的位掩码。 */ } qurl_http_auth_e; ``` #### qurl_usetls_e ```c /** * @struct qurl_usetls_e * @brief 连接是否使用TLS */ typedef enum { QURL_USETLS_NONE, /*!< 不尝试使用 TLS,即明文通信。 */ QURL_USETLS_TRY, /*!< 尝试使用 TLS,如果 TLS 连接失败,则继续进行非 TLS 连接。 */ QURL_USETLS_CONTROL, /*!< 仅对控制连接使用 TLS,如果 TLS 连接失败,则操作失败。 */ QURL_USETLS_ALL, /*!< 对所有通信(包括控制连接和数据连接)都使用 TLS,如果 TLS 连接失败,则操作失败。 */ QURL_USETLS_LAST, /*!< 仅做溢出判断 */ } qurl_usetls_e; ``` #### qurl_timecond_t ```c /** * @struct qurl_timecond_t * @brief 获取文件的时间条件 */ typedef enum { QURL_TIMECOND_NONE, /*!< 无时间条件。 */ QURL_TIMECOND_IFMODSINCE, /*!< 如果文件自指定日期或更近以来已被修改,则传输文件。 */ QURL_TIMECOND_IFUNMODSINCE, /*!< 如果文件自指定日期或更近以来未被修改,则传输文件。 */ QURL_TIMECOND_LASTMOD, /*!< 传输文件,如果文件的最后修改日期比指定日期要晚。 */ QURL_TIMECOND_LAST /*!< 仅做溢出判断 */ } qurl_timecond_t; ``` #### qurl_http_redir_e ```c /** * @struct qurl_http_redir_e * @brief HTTP重定向的处理方式 */ typedef enum { QURL_HTTP_REDIR_GET_ALL = ((unsigned long)0), QURL_HTTP_REDIR_POST_301 = (((unsigned long)1) << 0), QURL_HTTP_REDIR_POST_302 = (((unsigned long)1) << 1), QURL_HTTP_REDIR_POST_303 = (((unsigned long)1) << 2), QURL_HTTP_REDIR_POST_ALL = (QURL_HTTP_REDIR_POST_301 | QURL_HTTP_REDIR_POST_302 | QURL_HTTP_REDIR_POST_303), } qurl_http_redir_e; ``` # 应用示例 提供完整的、可直接编译运行的示例代码,帮助开发者快速上手 ## http_get_demo 该函数实现了一个HTTP GET操作的完整流程,展示了如何基于qurl系统发起HTTP GET请求去获取http服务器上的资源。 源码请查看 https://github.com/UniRTOS/UniRTOS-Doc-Examples/blob/main/network/network_protocol/http/http_get_demo.c ## http_post_demo 该函数实现了一个HTTP POST操作的完整流程,展示了如何基于qurl系统发起HTTP POST请求去向服务器提交数据。 源码请查看 https://github.com/UniRTOS/UniRTOS-Doc-Examples/blob/main/network/network_protocol/http/http_post_demo.c ## http_post_form_demo 该函数实现了一个HTTP POST多媒体表单操作的完整流程,展示了如何基于qurl系统发起HTTP POST请求去向服务器提交多媒体表单。 源码请查看 https://github.com/UniRTOS/UniRTOS-Doc-Examples/blob/main/network/network_protocol/http/http_post_form_demo.c ## http_put_demo 该函数实现了一个HTTP PUT操作的完整流程,展示了如何基于qurl系统发起HTTP PUT请求向服务器上传数据并替换指定资源。 源码请查看 https://github.com/UniRTOS/UniRTOS-Doc-Examples/blob/main/network/network_protocol/http/http_put_demo.c ## https_get_demo 该函数实现了一个HTTPS GET操作的完整流程,展示了如何基于qurl系统发起HTTPS GET请求去获取HTTP服务器上的资源。 源码请查看 https://github.com/UniRTOS/UniRTOS-Doc-Examples/blob/main/network/network_protocol/http/https_get_demo.c # 常见问题排查指南 ## 错误码 | **问题现象** | **可能原因** | **建议解决方案** | | --- | --- | --- | | `qurl_core_perform` 返回 `QURL_ECODE_NETWORK_ERR` | 未配置 `QURL_OPT_NETWORK_ID` 或网络未激活 | 确保先注网成功,并根据平台文档正确设置网络 ID(通常为 1 或 0) | | `qurl_core_perform` 返回 `QURL_ECODE_URL_MALFORMED_INPUT` | URL未带 scheme(如 http:// 或 https://) | 检查URL格式,必须完整包含 scheme、主机、端口(可选)和路径 | | HTTPS请求失败,返回TLS相关错误 | TLS 配置缺失或证书验证失败 | 使用 `qurl_tls_cfg_init()` 初始化 TLS 配置;若需忽略证书验证,可设置 verify 为 `QURL_TLS_VERIFY_NONE`(仅测试用) | | 响应码为4xx/5xx | 服务器端拒绝或错误 | 使用 `qurl_core_getinfo()` 获取响应码,检查请求头、参数是否正确 | | 请求超时 | 超时时间设置过短或网络状况差 | 适当增大 `QURL_OPT_TIMEOUT_MS` 和 `QURL_OPT_IDLE_TIMEOUT_MS` | | 数据接收不完整或乱码 | 未正确处理回调返回的字节数 | 回调函数必须返回实际处理的字节数(通常等于 `size`),否则传输会中断 | | 内存泄漏 | 未释放 `slist` 或未调用 delete/deinit | 每次使用 `qurl_slist_add_strdup()` 后必须 `qurl_slist_del_all()`;每个 core 必须 `qurl_core_delete()` | # 附录 本章节提供数据结构和枚举定义以及术语缩写表,作为文档参考。 ### 术语缩写表 | **缩写** | **全称** | **说明** | | --- | --- | --- | | HTTP | Hyper Text Transfer Protocol | 超文本传输协议 | | HTTPS | Hyper Text Transfer Protocol Secure | 安全超文本传输协议(HTTP + TLS) | | TLS | Transport Layer Security | 传输层安全协议 | | SSL | Secure Sockets Layer | 安全套接字层(TLS 的前身) | | PDP | Packet Data Protocol | 分组数据协议(蜂窝网络上下文) | | DNS | Domain Name System | 域名系统 | | TCP | Transmission Control Protocol | 传输控制协议 | | URL | Uniform Resource Locator | 统一资源定位符 | | URI | Uniform Resource Identifier | 统一资源标识符 | | CRLF | Carriage Return Line Feed | 回车换行(\r\n) |