libcurl CURLOPT_FAILONERROR:让 HTTP 响应码 ≥ 400 的请求立即失败
【免费下载链接】curlA command line tool and library for transferring data with URL syntax, supporting DICT, FILE, FTP, FTPS, GOPHER, GOPHERS, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, MQTT, MQTTS, POP3, POP3S, RTSP, SCP, SFTP, SMB, SMBS, SMTP, SMTPS, TELNET, TFTP, WS and WSS. libcurl offers a myriad of powerful features项目地址: https://gitcode.com/GitHub_Trending/cu/curl
导读
CURLOPT_FAILONERROR是 libcurl 提供的 HTTP 专用选项:开启后,当服务器返回的 HTTP 状态码大于等于 400 时,传输会被判定为失败,库会关闭连接并以错误码CURLE_HTTP_RETURNED_ERROR结束本次请求,而不是像默认行为那样继续把错误页面正文当作正常数据输出。本文基于 curl 仓库中的 CURLOPT_FAILONERROR.md 官方文档展开,并结合 lib/http.c、lib/setopt.c 等源码剖析其判定逻辑、认证场景下的例外、返回值处理,以及对应的命令行选项--fail,帮助读者在脚本、批处理和集成场景中准确判断 HTTP 层失败。
一、选项概览:函数原型与基本语义
CURLOPT_FAILONERROR从 libcurl 7.1 版本开始提供,仅适用于 HTTP/HTTPS 传输。其原型如下:
#include <curl/curl.h> CURLcode curl_easy_setopt(CURL *handle, CURLOPT_FAILONERROR, long fail);| 参数 | 说明 |
|---|---|
handle | 由curl_easy_init()创建的 easy handle |
fail | 1L表示启用“失败即中止”;0L(默认值)表示忽略 HTTP 错误码,正常返回页面内容 |
官方文档明确描述其语义:
- 设置为 1 时,若服务器返回的 HTTP 状态码等于或大于 400,库会让本次请求失败;
- 默认行为(不设置该选项)是忽略状态码,照常把服务器返回的页面正文交给应用;
- 当判定为失败时,连接会被关闭,
curl_easy_perform()返回CURLE_HTTP_RETURNED_ERROR(其数值为 22,详见 libcurl-errors.md)。
该选项在底层对应 easy handle 中的一个布尔标志位。在 lib/urldata.h 中定义如下:
BIT(http_fail_on_error); /* fail on HTTP error codes >= 400 */而在 lib/setopt.c 的选项解析分支中:
case CURLOPT_FAILONERROR: /* * Do not output the >=400 error code HTML-page, but instead only * return error. */ s->http_fail_on_error = enabled; break;注意该选项接收的是long类型的布尔值,取值 0 或 1,启用后凡 HTTP 状态码 ≥ 400 的响应都会被当作传输错误处理。
二、核心判定逻辑:http_should_fail()源码剖析
选项开关只是第一步,真正决定“是否失败”的判定集中在 lib/http.c 的静态函数http_should_fail()中。该函数接收 easy handle 与收到的 HTTP 状态码,按以下顺序裁决:
- 未开启选项:若
data->set.http_fail_on_error为假,直接返回“不失败”——这是默认行为; - 状态码 < 400:任何 400 以下的响应码(包括 3xx 重定向、2xx 成功等)都不是终止性错误;
- 416 + 断点续传:若当前是 GET 请求且正处于断点续传(
resume_from)状态,收到 416(Range Not Satisfiable)通常意味着文件其实已经完整下载,因此不算失败; - 401/407 之外的状态码:任何 ≥ 400 且不是 401、407 的响应码一律视为终止性错误;
- 401/407 的特殊处理:这两种状态码与认证流程强相关,需要结合当前认证状态判断:
- 收到 401 且本地没有主机端认证凭据(
data->state.creds)→ 失败; - 收到 407 且没有代理认证凭据(
data->conn->http_proxy.creds)→ 失败(在未禁用代理的构建中); - 否则以
data->state.authproblem的取值作为最终结论。
- 收到 401 且本地没有主机端认证凭据(
该函数的头部注释还解释了 401/407 的例外逻辑:函数在收到完整响应头之后才会被调用,如果应用在某个阶段被要求认证且已成功完成认证,则再次收到 401/407 也不应误判为失败;反之,如果已经处于完全认证状态却再次收到 401/407,则属于错误。
http_should_fail()的调用点有两处,分别位于 HTTP 响应头处理流程(lib/http.c)和 WebSocket 升级响应处理(lib/http.c):
if(http_should_fail(data,>if(100 <=>int main(void) { CURL *curl = curl_easy_init(); if(curl) { CURLcode result; curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L); result = curl_easy_perform(curl); if(result == CURLE_HTTP_RETURNED_ERROR) { /* an HTTP response error problem */ } } }更完整的实战版本可以加上初始化清理、错误缓冲区与状态码回读,并搭配CURLINFO_RESPONSE_CODE获取具体状态码:
#include <curl/curl.h> #include <stdio.h> int main(void) { CURL *curl; CURLcode res; char errbuf[CURL_ERROR_SIZE] = { 0 }; curl_global_init(CURL_GLOBAL_DEFAULT); curl = curl_easy_init(); if(curl) { curl_easy_setopt(curl, CURLOPT_URL, "https://example.com/"); /* 状态码 >= 400 即失败,不输出错误页正文 */ curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1L); /* 捕获详细错误信息 */ curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errbuf); res = curl_easy_perform(curl); if(res != CURLE_OK) { fprintf(stderr, "transfer failed: %s\n", errbuf[0] ? errbuf : curl_easy_strerror(res)); if(res == CURLE_HTTP_RETURNED_ERROR) { long http_code = 0; curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); fprintf(stderr, "HTTP status code: %ld\n", http_code); } } curl_easy_cleanup(curl); } curl_global_cleanup(); return 0; }五、实战案例:multi 接口与“失败即保存日志”
仓库中的测试与示例印证了该选项在真实场景中的两种典型用法。
1. 多句柄场景(multi 接口)
测试程序 tests/libtest/lib533.c(用于测试用例 533/534/535/546)展示了在 multi 接口中开启CURLOPT_FAILONERROR的方式:
easy_setopt(curl, CURLOPT_URL, URL); easy_setopt(curl, CURLOPT_VERBOSE, 1L); easy_setopt(curl, CURLOPT_FAILONERROR, 1L); multi_init(multi); multi_add_handle(multi, curl); /* ... multi_perform 循环驱动传输 ... */该测试还演示了句柄复用技巧:在完成一次传输后调用curl_easy_reset(curl),再重新设置 URL、CURLOPT_VERBOSE与CURLOPT_FAILONERROR,然后重新加入 multi 句柄——说明curl_easy_reset()会清空CURLOPT_FAILONERROR等所有选项,复用句柄时必须重新设置。
2. 结合 verbose 日志定位失败传输
示例程序 docs/examples/log_failed_transfers.c 演示了“失败才落盘日志”的运维模式:为每次传输开启CURLOPT_VERBOSE与CURLOPT_DEBUGFUNCTION把日志缓冲在内存中,同时开启:
/* Enable immediate error on HTTP status codes >= 400 in most cases, instead of downloading the body to a file */ curl_easy_setopt(t->curl, CURLOPT_FAILONERROR, 1L);传输完成后,仅当curl_easy_perform()返回错误(例如访问https://httpbin.org/status/400触发的CURLE_HTTP_RETURNED_ERROR)时才把缓冲日志写入磁盘文件,否则删除已下载的正文文件。该模式非常适合批量抓取任务中“静默失败、事后审计”的需求。
六、命令行对应:--fail(-f)与--fail-with-body
libcurl 工具curl提供了与该选项一一对应的命令行开关--fail(短选项-f),其说明位于 docs/cmdline-opts/fail.md:
- 对返回 HTTP 状态码 ≥ 400 的传输,失败并以退出码 22 结束,且完全不输出响应正文;
- 正常情况下,服务器返回 4xx 时会附带一段描述错误原因的正文,
--fail会阻止这段正文被输出,并提前以错误码 22 退出; - 默认情况下,curl 命令行工具同样不把 HTTP 状态码视为失败;
- 若希望“既得到错误码、又保留正文”,改用
--fail-with-body(二者互为互斥选项,参见 fail-with-body.md)。
示例:
curl --fail https://example.com/ # 4xx/5xx 时退出码 22,无正文输出 curl -f https://example.com/ # 短选项形式 curl --fail-with-body https://example.com/ # 保留错误正文从实现角度,命令行工具通过 src/config2setopts.c 将--fail映射到CURLOPT_FAILONERROR,与库 API 走的是同一条代码路径。
七、相关选项与组合建议
官方文档的 See-also 字段推荐了三个相关选项:
| 选项 | 与 FAILONERROR 的关系 |
|---|---|
CURLINFO_RESPONSE_CODE | 无论是否开启 FAILONERROR,都可用curl_easy_getinfo()回读实际 HTTP 状态码,用于精细判断;对应文档 CURLINFO_RESPONSE_CODE.md |
CURLOPT_HTTP200ALIASES | 将某些非 200 状态码“别名”为 200 处理,可与 FAILONERROR 配合定义“哪些码算成功”;对应文档 CURLOPT_HTTP200ALIASES.md |
CURLOPT_KEEP_SENDING_ON_ERROR | 默认在检测到错误时停止发送剩余请求体;开启后即使收到 ≥ 400 响应也继续发送,适用于需要完整上传的场景;对应文档 CURLOPT_KEEP_SENDING_ON_ERROR.md |
组合建议:
- 需要区分“HTTP 层失败”与“网络层失败”:开启
CURLOPT_FAILONERROR后,仅凭CURLE_HTTP_RETURNED_ERROR即可识别 HTTP 错误,其余非零结果码大多对应连接、DNS、TLS 等传输层问题; - 需要拿到错误状态码:在失败分支中调用
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code); - 需要保留错误正文:改用
CURLOPT_KEEP_SENDING_ON_ERROR结合写回调,或使用命令行--fail-with-body; - 认证类接口慎用:涉及 401/407 的认证协商可能延迟或穿透失败判定,应结合响应码回调做最终裁决。
八、测试验证
curl 仓库通过tests/libtest/lib533.c及配套测试用例(533/534/535/546)对CURLOPT_FAILONERROR进行回归验证,覆盖了 easy/multi 两种接口下的“HTTP 错误即失败”行为。感兴趣的读者可以阅读 tests/libtest/lib533.c 了解 multi 场景下如何驱动并断言该选项,或在本地按 tests/README.md(即 docs/tests 目录)的说明运行测试套件验证。
小结
CURLOPT_FAILONERROR是 libcurl 中“以状态码判定传输成败”的开关型选项,核心价值在于:把 HTTP 4xx/5xx 从“正常传输但返回错误页面”转换为“明确的失败结果码CURLE_HTTP_RETURNED_ERROR”,从而简化错误处理分支。使用时需牢记三点:默认关闭(值为 0);401/407 等认证相关响应存在穿透可能;检测到错误前可能已收到部分响应头。结合CURLINFO_RESPONSE_CODE、CURLOPT_KEEP_SENDING_ON_ERROR与命令行--fail,可以构建出精确、可观测的 HTTP 失败处理策略。
【免费下载链接】curlA command line tool and library for transferring data with URL syntax, supporting DICT, FILE, FTP, FTPS, GOPHER, GOPHERS, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, MQTT, MQTTS, POP3, POP3S, RTSP, SCP, SFTP, SMB, SMBS, SMTP, SMTPS, TELNET, TFTP, WS and WSS. libcurl offers a myriad of powerful features项目地址: https://gitcode.com/GitHub_Trending/cu/curl
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考