← Commit list

c24a69d8cd

Joshua Rogers
Joshua Rogers
2026-09-17
SUCCESS Duration: 385s

Subject

tool: buffer headers before remote filename selection

Binary

No binary (build failed)

Architecture & Compiler Flags

Architecture: x86_64-linux-gnu Compiler: gcc (Ubuntu 11.4.0-1ubuntu1~22.04.3) 11.4.0

—

Build Log

(log yok)

Commit Diff GitHub →

src/tool_cb_hdr.c+35 -18
@@ -248,6 +248,28 @@ fail:
return rc;
}
+/* Store a header until the output filename has been decided. */
+static size_t buffer_header(struct HdrCbData *hdrcbdata,
+ const char *str, size_t cb)
+{
+ char *clone = curlx_memdup0(str, cb);
+ if(clone) {
+ struct curl_slist *old = hdrcbdata->headlist;
+ hdrcbdata->headlist = curl_slist_append(old, clone);
+ curlx_free(clone);
+ if(!hdrcbdata->headlist) {
+ curl_slist_free_all(old);
+ return CURL_WRITEFUNC_ERROR;
+ }
+ }
+ else {
+ curl_slist_free_all(hdrcbdata->headlist);
+ hdrcbdata->headlist = NULL;
+ return CURL_WRITEFUNC_ERROR;
+ }
+ return cb;
+}
+
/*
* Write etag to file when --etag-save option is given.
*/
@@ -388,24 +410,8 @@ static size_t content_disposition(const char *str, const char *end,
if(hdrcbdata->honor_cd_filename &&
hdrcbdata->config->show_headers) {
- /* still awaiting the Content-Disposition header, store the header in
- memory. Since it is not null-terminated, we need an extra dance. */
- char *clone = curlx_memdup0(str, cb);
- if(clone) {
- struct curl_slist *old = hdrcbdata->headlist;
- hdrcbdata->headlist = curl_slist_append(old, clone);
- curlx_free(clone);
- if(!hdrcbdata->headlist) {
- curl_slist_free_all(old);
- return CURL_WRITEFUNC_ERROR;
- }
- }
- else {
- curl_slist_free_all(hdrcbdata->headlist);
- hdrcbdata->headlist = NULL;
- return CURL_WRITEFUNC_ERROR;
- }
- return cb; /* done for now */
+ /* still awaiting the Content-Disposition header */
+ return buffer_header(hdrcbdata, str, cb);
}
return 0; /* ok */
@@ -427,6 +433,7 @@ size_t tool_header_cb(char *ptr, size_t size, size_t nmemb, void *userdata)
const size_t cb = size * nmemb;
const char *end = ptr + cb;
const char *scheme = NULL;
+ bool cd_checked = FALSE;
if(!per->config)
return CURL_WRITEFUNC_ERROR;
@@ -486,6 +493,9 @@ size_t tool_header_cb(char *ptr, size_t size, size_t nmemb, void *userdata)
size_t rc = content_disposition(str, end, cb, per, response);
if(rc)
return rc;
+ /* remember that this was checked to avoid duplicating it further
+ down */
+ cd_checked = TRUE;
}
}
if(hdrcbdata->config->writeout) {
@@ -505,6 +515,13 @@ size_t tool_header_cb(char *ptr, size_t size, size_t nmemb, void *userdata)
/* bold headers only for selected protocols */
const char *value = NULL;
+ /* Informational responses, and ETag headers handled by --etag-save,
+ bypass the content-disposition handling above. Do not open the output
+ file until the filename decision is complete. Only for HTTP(S). */
+ if(hdrcbdata->honor_cd_filename && !cd_checked &&
+ (scheme == proto_http || scheme == proto_https))
+ return buffer_header(hdrcbdata, str, cb);
+
if(!outs->stream && !tool_create_output_file(outs, per->config))
return CURL_WRITEFUNC_ERROR;
tests/data/Makefile.am+2 -0
@@ -2049,6 +2049,8 @@ TESTCASES = \
test3034 \
test3035 \
test3036 \
+ test3037 \
+ test3038 \
\
test3100 \
test3101 \
tests/data/test3037+62 -0
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="US-ASCII"?>
+<testcase>
+<info>
+<keywords>
+HTTP
+HTTP GET
+103
+Early Hints
+-J
+--show-headers
+</keywords>
+</info>
+
+# Server-side
+<reply>
+<data nocheck="yes">
+HTTP/1.1 103 Early Hints
+
+HTTP/1.1 200 OK
+Content-Length: 6
+Content-Disposition: attachment; filename="victim%TESTNUMBER"
+Connection: close
+
+12345
+</data>
+</reply>
+
+# Client-side
+<client>
+<server>
+http
+</server>
+<features>
+http
+</features>
+<name>
+Do not open output for 103 Early Hints with -J and --show-headers
+</name>
+<file name="%LOGDIR/victim%TESTNUMBER">
+original
+</file>
+<command option="no-output">
+http://%HOSTIP:%HTTPPORT/victim%TESTNUMBER -O -J -i --output-dir %LOGDIR
+</command>
+</client>
+
+<verify>
+<protocol crlf="headers">
+GET /victim%TESTNUMBER HTTP/1.1
+Host: %HOSTIP:%HTTPPORT
+User-Agent: curl/%VERSION
+Accept: */*
+
+</protocol>
+<errorcode>
+23
+</errorcode>
+<file name="%LOGDIR/victim%TESTNUMBER">
+original
+</file>
+</verify>
+</testcase>
tests/data/test3038+64 -0
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="US-ASCII"?>
+<testcase>
+<info>
+<keywords>
+HTTP
+HTTP GET
+ETag
+--etag-save
+-J
+--show-headers
+</keywords>
+</info>
+
+# Server-side
+<reply>
+<data nocheck="yes">
+HTTP/1.1 200 OK
+ETag: "tag%TESTNUMBER"
+Content-Length: 6
+Content-Disposition: attachment; filename="victim%TESTNUMBER"
+Connection: close
+
+12345
+</data>
+</reply>
+
+# Client-side
+<client>
+<server>
+http
+</server>
+<features>
+http
+</features>
+<name>
+Do not open output for ETag with -J, --show-headers, --etag-save
+</name>
+<file name="%LOGDIR/victim%TESTNUMBER">
+original
+</file>
+<command option="no-output">
+http://%HOSTIP:%HTTPPORT/victim%TESTNUMBER -O -J -i --etag-save %LOGDIR/etag%TESTNUMBER --output-dir %LOGDIR
+</command>
+</client>
+
+<verify>
+<protocol crlf="headers">
+GET /victim%TESTNUMBER HTTP/1.1
+Host: %HOSTIP:%HTTPPORT
+User-Agent: curl/%VERSION
+Accept: */*
+
+</protocol>
+<errorcode>
+23
+</errorcode>
+<file1 name="%LOGDIR/victim%TESTNUMBER">
+original
+</file1>
+<file2 name="%LOGDIR/etag%TESTNUMBER">
+"tag%TESTNUMBER"
+</file2>
+</verify>
+</testcase>