news 2026/9/11 2:05:27

libcurl CURLOPT_FAILONERROR:让 HTTP 响应码 ≥ 400 的请求立即失败

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
libcurl CURLOPT_FAILONERROR:让 HTTP 响应码 ≥ 400 的请求立即失败

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);
参数说明
handlecurl_easy_init()创建的 easy handle
fail1L表示启用“失败即中止”;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 状态码,按以下顺序裁决:

  1. 未开启选项:若data->set.http_fail_on_error为假,直接返回“不失败”——这是默认行为;
  2. 状态码 < 400:任何 400 以下的响应码(包括 3xx 重定向、2xx 成功等)都不是终止性错误;
  3. 416 + 断点续传:若当前是 GET 请求且正处于断点续传(resume_from)状态,收到 416(Range Not Satisfiable)通常意味着文件其实已经完整下载,因此不算失败
  4. 401/407 之外的状态码:任何 ≥ 400 且不是 401、407 的响应码一律视为终止性错误;
  5. 401/407 的特殊处理:这两种状态码与认证流程强相关,需要结合当前认证状态判断:
    • 收到 401 且本地没有主机端认证凭据(data->state.creds)→ 失败;
    • 收到 407 且没有代理认证凭据(data->conn->http_proxy.creds)→ 失败(在未禁用代理的构建中);
    • 否则以data->state.authproblem的取值作为最终结论。

该函数的头部注释还解释了 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_VERBOSECURLOPT_FAILONERROR,然后重新加入 multi 句柄——说明curl_easy_reset()会清空CURLOPT_FAILONERROR等所有选项,复用句柄时必须重新设置。

2. 结合 verbose 日志定位失败传输

示例程序 docs/examples/log_failed_transfers.c 演示了“失败才落盘日志”的运维模式:为每次传输开启CURLOPT_VERBOSECURLOPT_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_CODECURLOPT_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),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/11 2:05:00

智能制造培训PPT设计:从技术转化到落地实践

1. 项目概述 "智能制造建设培训方案&#xff08;PPT&#xff09;"这个项目标题看似简单&#xff0c;实则包含了一个正在蓬勃发展的行业需求。作为在工业数字化转型领域摸爬滚打多年的从业者&#xff0c;我见过太多企业投入巨资购买智能设备后&#xff0c;却因为员工认…

作者头像 李华
网站建设 2026/9/11 2:03:57

Linux进程生命周期与管理深度解析

1. Linux进程生命周期全景图在Linux系统中&#xff0c;进程的生命周期远比简单的"创建-运行-终止"模型复杂得多。理解进程的完整生命周期&#xff0c;对于系统调优、故障排查和程序设计都至关重要。我们先来看一个典型的进程状态转换图&#xff1a;新建(NEW) → 就绪…

作者头像 李华
网站建设 2026/9/11 2:03:09

工作照片归档与命名规范:施工现场留痕的完整实操方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华