mirror of
https://github.com/curl/curl.git
synced 2025-04-18 16:30:45 +08:00
tool: support --show-headers AND --remote-header-name
By keeping the headers in memory until we know the target file name, then output them all. Previously this option combination would cause an error. Add test 1310 and 1492 to verify. Adjusted test 1460 to work in the new conditions. Closes #15110
This commit is contained in:
parent
bc6072d245
commit
b0c82239c2
@ -62,6 +62,25 @@ static void write_linked_location(CURL *curl, const char *location,
|
||||
size_t loclen, FILE *stream);
|
||||
#endif
|
||||
|
||||
int tool_write_headers(struct HdrCbData *hdrcbdata, FILE *stream)
|
||||
{
|
||||
struct curl_slist *h = hdrcbdata->headlist;
|
||||
int rc = 1;
|
||||
while(h) {
|
||||
/* not "handled", just show it */
|
||||
size_t len = strlen(h->data);
|
||||
if(len != fwrite(h->data, 1, len, stream))
|
||||
goto fail;
|
||||
h = h->next;
|
||||
}
|
||||
rc = 0; /* success */
|
||||
fail:
|
||||
curl_slist_free_all(hdrcbdata->headlist);
|
||||
hdrcbdata->headlist = NULL;
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
** callback for CURLOPT_HEADERFUNCTION
|
||||
*/
|
||||
@ -164,63 +183,90 @@ size_t tool_header_cb(char *ptr, size_t size, size_t nmemb, void *userdata)
|
||||
* Content-Disposition header specifying a filename property.
|
||||
*/
|
||||
|
||||
else if(hdrcbdata->honor_cd_filename &&
|
||||
(cb > 20) && checkprefix("Content-disposition:", str)) {
|
||||
const char *p = str + 20;
|
||||
else if(hdrcbdata->honor_cd_filename) {
|
||||
if((cb > 20) && checkprefix("Content-disposition:", str)) {
|
||||
const char *p = str + 20;
|
||||
|
||||
/* look for the 'filename=' parameter
|
||||
(encoded filenames (*=) are not supported) */
|
||||
for(;;) {
|
||||
char *filename;
|
||||
size_t len;
|
||||
/* look for the 'filename=' parameter
|
||||
(encoded filenames (*=) are not supported) */
|
||||
for(;;) {
|
||||
char *filename;
|
||||
size_t len;
|
||||
|
||||
while((p < end) && *p && !ISALPHA(*p))
|
||||
p++;
|
||||
if(p > end - 9)
|
||||
break;
|
||||
|
||||
if(memcmp(p, "filename=", 9)) {
|
||||
/* no match, find next parameter */
|
||||
while((p < end) && *p && (*p != ';'))
|
||||
while((p < end) && *p && !ISALPHA(*p))
|
||||
p++;
|
||||
if((p < end) && *p)
|
||||
continue;
|
||||
else
|
||||
if(p > end - 9)
|
||||
break;
|
||||
}
|
||||
p += 9;
|
||||
|
||||
len = cb - (size_t)(p - str);
|
||||
filename = parse_filename(p, len);
|
||||
if(filename) {
|
||||
if(outs->stream) {
|
||||
/* indication of problem, get out! */
|
||||
free(filename);
|
||||
return CURL_WRITEFUNC_ERROR;
|
||||
if(memcmp(p, "filename=", 9)) {
|
||||
/* no match, find next parameter */
|
||||
while((p < end) && *p && (*p != ';'))
|
||||
p++;
|
||||
if((p < end) && *p)
|
||||
continue;
|
||||
else
|
||||
break;
|
||||
}
|
||||
p += 9;
|
||||
|
||||
if(per->config->output_dir) {
|
||||
outs->filename = aprintf("%s/%s", per->config->output_dir,
|
||||
filename);
|
||||
free(filename);
|
||||
if(!outs->filename)
|
||||
len = cb - (size_t)(p - str);
|
||||
filename = parse_filename(p, len);
|
||||
if(filename) {
|
||||
if(outs->stream) {
|
||||
/* indication of problem, get out! */
|
||||
free(filename);
|
||||
return CURL_WRITEFUNC_ERROR;
|
||||
}
|
||||
|
||||
if(per->config->output_dir) {
|
||||
outs->filename = aprintf("%s/%s", per->config->output_dir,
|
||||
filename);
|
||||
free(filename);
|
||||
if(!outs->filename)
|
||||
return CURL_WRITEFUNC_ERROR;
|
||||
}
|
||||
else
|
||||
outs->filename = filename;
|
||||
|
||||
outs->is_cd_filename = TRUE;
|
||||
outs->s_isreg = TRUE;
|
||||
outs->fopened = FALSE;
|
||||
outs->alloc_filename = TRUE;
|
||||
hdrcbdata->honor_cd_filename = FALSE; /* done now! */
|
||||
if(!tool_create_output_file(outs, per->config))
|
||||
return CURL_WRITEFUNC_ERROR;
|
||||
if(tool_write_headers(&per->hdrcbdata, outs->stream))
|
||||
return CURL_WRITEFUNC_ERROR;
|
||||
}
|
||||
else
|
||||
outs->filename = filename;
|
||||
|
||||
outs->is_cd_filename = TRUE;
|
||||
outs->s_isreg = TRUE;
|
||||
outs->fopened = FALSE;
|
||||
outs->alloc_filename = TRUE;
|
||||
hdrcbdata->honor_cd_filename = FALSE; /* done now! */
|
||||
if(!tool_create_output_file(outs, per->config))
|
||||
return CURL_WRITEFUNC_ERROR;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
if(!outs->stream && !tool_create_output_file(outs, per->config))
|
||||
return CURL_WRITEFUNC_ERROR;
|
||||
if(tool_write_headers(&per->hdrcbdata, outs->stream))
|
||||
return CURL_WRITEFUNC_ERROR;
|
||||
} /* content-disposition handling */
|
||||
|
||||
if(hdrcbdata->honor_cd_filename &&
|
||||
hdrcbdata->config->show_headers) {
|
||||
/* still awaiting the Content-Disposition header, store the header in
|
||||
memory. Since it is not zero terminated, we need an extra dance. */
|
||||
char *clone = aprintf("%.*s", (int)cb, (char *)str);
|
||||
if(clone) {
|
||||
struct curl_slist *old = hdrcbdata->headlist;
|
||||
hdrcbdata->headlist = curl_slist_append(old, clone);
|
||||
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 */
|
||||
}
|
||||
if(!outs->stream && !tool_create_output_file(outs, per->config))
|
||||
return CURL_WRITEFUNC_ERROR;
|
||||
}
|
||||
}
|
||||
if(hdrcbdata->config->writeout) {
|
||||
|
@ -46,9 +46,12 @@ struct HdrCbData {
|
||||
struct OutStruct *outs;
|
||||
struct OutStruct *heads;
|
||||
struct OutStruct *etag_save;
|
||||
struct curl_slist *headlist;
|
||||
bool honor_cd_filename;
|
||||
};
|
||||
|
||||
int tool_write_headers(struct HdrCbData *hdrcbdata, FILE *stream);
|
||||
|
||||
/*
|
||||
** callback for CURLOPT_HEADERFUNCTION
|
||||
*/
|
||||
|
@ -345,7 +345,13 @@ size_t tool_write_cb(char *buffer, size_t sz, size_t nmemb, void *userdata)
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
if(per->hdrcbdata.headlist) {
|
||||
if(tool_write_headers(&per->hdrcbdata, outs->stream))
|
||||
return CURL_WRITEFUNC_ERROR;
|
||||
}
|
||||
rc = fwrite(buffer, sz, nmemb, outs->stream);
|
||||
}
|
||||
|
||||
if(bytes == rc)
|
||||
/* we added this amount of data to the output */
|
||||
|
@ -2763,9 +2763,7 @@ ParameterError parse_args(struct GlobalConfig *global, int argc,
|
||||
}
|
||||
|
||||
if(!result && config->content_disposition) {
|
||||
if(config->show_headers)
|
||||
result = PARAM_CONTDISP_SHOW_HEADER;
|
||||
else if(config->resume_from_current)
|
||||
if(config->resume_from_current)
|
||||
result = PARAM_CONTDISP_RESUME_FROM;
|
||||
}
|
||||
|
||||
|
@ -342,7 +342,6 @@ typedef enum {
|
||||
PARAM_NO_PREFIX,
|
||||
PARAM_NUMBER_TOO_LARGE,
|
||||
PARAM_NO_NOT_BOOLEAN,
|
||||
PARAM_CONTDISP_SHOW_HEADER, /* --include and --remote-header-name */
|
||||
PARAM_CONTDISP_RESUME_FROM, /* --continue-at and --remote-header-name */
|
||||
PARAM_READ_ERROR,
|
||||
PARAM_EXPAND_ERROR, /* --expand problem */
|
||||
|
@ -67,8 +67,6 @@ const char *param2text(ParameterError error)
|
||||
return "too large number";
|
||||
case PARAM_NO_NOT_BOOLEAN:
|
||||
return "used '--no-' for option that is not a boolean";
|
||||
case PARAM_CONTDISP_SHOW_HEADER:
|
||||
return "showing headers and --remote-header-name cannot be combined";
|
||||
case PARAM_CONTDISP_RESUME_FROM:
|
||||
return "--continue-at and --remote-header-name cannot be combined";
|
||||
case PARAM_READ_ERROR:
|
||||
|
@ -770,7 +770,8 @@ skip:
|
||||
free(per->uploadfile);
|
||||
if(global->parallel)
|
||||
free(per->errorbuffer);
|
||||
|
||||
curl_slist_free_all(per->hdrcbdata.headlist);
|
||||
per->hdrcbdata.headlist = NULL;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -75,6 +75,7 @@
|
||||
# 1021 re-added here due to flakiness
|
||||
1021
|
||||
1417
|
||||
1460
|
||||
1533
|
||||
1540
|
||||
1591
|
||||
|
@ -170,7 +170,7 @@ test1271 test1272 test1273 test1274 test1275 test1276 test1277 test1278 \
|
||||
test1279 test1280 test1281 test1282 test1283 test1284 test1285 test1286 \
|
||||
test1287 test1288 test1289 test1290 test1291 test1292 test1293 test1294 \
|
||||
test1295 test1296 test1297 test1298 test1299 test1300 test1301 test1302 \
|
||||
test1303 test1304 test1305 test1306 test1307 test1308 test1309 \
|
||||
test1303 test1304 test1305 test1306 test1307 test1308 test1309 test1310 \
|
||||
test1311 test1312 test1313 test1314 test1315 test1316 test1317 test1318 \
|
||||
test1319 test1320 test1321 test1322 test1323 test1324 test1325 test1326 \
|
||||
test1327 test1328 test1329 test1330 test1331 test1332 test1333 test1334 \
|
||||
@ -193,7 +193,7 @@ test1455 test1456 test1457 test1458 test1459 test1460 test1461 test1462 \
|
||||
test1463 test1464 test1465 test1466 test1467 test1468 test1469 test1470 \
|
||||
test1471 test1472 test1473 test1474 test1475 test1476 test1477 test1478 \
|
||||
test1479 test1480 test1481 test1482 test1483 test1484 test1485 test1486 \
|
||||
test1487 test1488 test1489 test1490 test1491 \
|
||||
test1487 test1488 test1489 test1490 test1491 test1492 \
|
||||
\
|
||||
test1500 test1501 test1502 test1503 test1504 test1505 test1506 test1507 \
|
||||
test1508 test1509 test1510 test1511 test1512 test1513 test1514 test1515 \
|
||||
|
63
tests/data/test1310
Normal file
63
tests/data/test1310
Normal file
@ -0,0 +1,63 @@
|
||||
<testcase>
|
||||
<info>
|
||||
<keywords>
|
||||
HTTP
|
||||
HTTP GET
|
||||
-J
|
||||
--show-headers
|
||||
</keywords>
|
||||
</info>
|
||||
|
||||
#
|
||||
<reply>
|
||||
<data nocheck="yes">
|
||||
HTTP/1.1 200 OK
|
||||
Date: Tue, 09 Nov 2010 14:49:00 GMT
|
||||
Server: test-server/fake
|
||||
Content-Length: 6
|
||||
Connection: close
|
||||
Content-Disposition: inline; filename="name%TESTNUMBER"
|
||||
Content-Type: text/html
|
||||
|
||||
12345
|
||||
</data>
|
||||
</reply>
|
||||
|
||||
#
|
||||
# Client-side
|
||||
<client>
|
||||
<server>
|
||||
http
|
||||
</server>
|
||||
<name>
|
||||
HTTP GET with -J + --show-headers
|
||||
</name>
|
||||
<command option="no-output,no-include">
|
||||
http://%HOSTIP:%HTTPPORT/junk -J -O --show-headers --output-dir %LOGDIR
|
||||
</command>
|
||||
</client>
|
||||
|
||||
#
|
||||
# Verify data after the test has been "shot"
|
||||
<verify>
|
||||
<protocol>
|
||||
GET /junk HTTP/1.1
|
||||
Host: %HOSTIP:%HTTPPORT
|
||||
User-Agent: curl/%VERSION
|
||||
Accept: */*
|
||||
|
||||
</protocol>
|
||||
<file name="%LOGDIR/name%TESTNUMBER">
|
||||
HTTP/1.1 200 OK
|
||||
Date: Tue, 09 Nov 2010 14:49:00 GMT
|
||||
Server: test-server/fake
|
||||
Content-Length: 6
|
||||
Connection: close
|
||||
Content-Disposition: inline; filename="name%TESTNUMBER"
|
||||
Content-Type: text/html
|
||||
|
||||
12345
|
||||
</file>
|
||||
|
||||
</verify>
|
||||
</testcase>
|
@ -11,33 +11,26 @@ HTTP GET
|
||||
<reply>
|
||||
<data nocheck="yes">
|
||||
HTTP/1.1 200 swsclose
|
||||
12345
|
||||
fooo
|
||||
54 3 2 1
|
||||
Date: Tue, 09 Nov 2010 14:49:00 GMT
|
||||
Server: test-server/fake
|
||||
Content-Disposition: filename=name%TESTNUMBER; charset=funny; option=strange
|
||||
Content-Length: 4
|
||||
|
||||
hej
|
||||
</data>
|
||||
</reply>
|
||||
|
||||
#
|
||||
# Client-side
|
||||
<client>
|
||||
# this relies on the debug feature to allow us to set directory to store the
|
||||
# -J output in
|
||||
<features>
|
||||
Debug
|
||||
</features>
|
||||
<server>
|
||||
http
|
||||
</server>
|
||||
<name>
|
||||
HTTP GET with -Ji and Content-Disposition with existing file
|
||||
</name>
|
||||
<setenv>
|
||||
CURL_TESTDIR=%LOGDIR
|
||||
</setenv>
|
||||
<command option="no-output,no-include">
|
||||
http://%HOSTIP:%HTTPPORT/%TESTNUMBER -Ji -O
|
||||
http://%HOSTIP:%HTTPPORT/%TESTNUMBER -Ji -O --output-dir %LOGDIR
|
||||
</command>
|
||||
<file name="%LOGDIR/name%TESTNUMBER">
|
||||
initial content
|
||||
@ -47,9 +40,9 @@ initial content
|
||||
#
|
||||
# Verify data after the test has been "shot"
|
||||
<verify>
|
||||
# Warning: --include and --remote-header-name cannot be combined.
|
||||
# Warning: Failed to open the file log/name1460: File exists
|
||||
<errorcode>
|
||||
2
|
||||
23
|
||||
</errorcode>
|
||||
<file name="%LOGDIR/name%TESTNUMBER">
|
||||
initial content
|
||||
|
63
tests/data/test1492
Normal file
63
tests/data/test1492
Normal file
@ -0,0 +1,63 @@
|
||||
<testcase>
|
||||
<info>
|
||||
<keywords>
|
||||
HTTP
|
||||
HTTP GET
|
||||
-J
|
||||
--show-headers
|
||||
</keywords>
|
||||
</info>
|
||||
|
||||
#
|
||||
<reply>
|
||||
<data nocheck="yes">
|
||||
HTTP/1.1 200 OK
|
||||
Date: Tue, 09 Nov 2010 14:49:00 GMT
|
||||
Server: test-server/fake
|
||||
Content-Length: 6
|
||||
Connection: close
|
||||
Content-Jisposition: inline; filename="name%TESTNUMBER"
|
||||
Content-Type: text/html
|
||||
|
||||
12345
|
||||
</data>
|
||||
</reply>
|
||||
|
||||
#
|
||||
# Client-side
|
||||
<client>
|
||||
<server>
|
||||
http
|
||||
</server>
|
||||
<name>
|
||||
HTTP GET with -J + --show-headers but no Content-Disposition:
|
||||
</name>
|
||||
<command option="no-output,no-include">
|
||||
http://%HOSTIP:%HTTPPORT/junk%TESTNUMBER -J -O --show-headers --output-dir %LOGDIR
|
||||
</command>
|
||||
</client>
|
||||
|
||||
#
|
||||
# Verify data after the test has been "shot"
|
||||
<verify>
|
||||
<protocol>
|
||||
GET /junk%TESTNUMBER HTTP/1.1
|
||||
Host: %HOSTIP:%HTTPPORT
|
||||
User-Agent: curl/%VERSION
|
||||
Accept: */*
|
||||
|
||||
</protocol>
|
||||
<file name="%LOGDIR/junk%TESTNUMBER">
|
||||
HTTP/1.1 200 OK
|
||||
Date: Tue, 09 Nov 2010 14:49:00 GMT
|
||||
Server: test-server/fake
|
||||
Content-Length: 6
|
||||
Connection: close
|
||||
Content-Jisposition: inline; filename="name%TESTNUMBER"
|
||||
Content-Type: text/html
|
||||
|
||||
12345
|
||||
</file>
|
||||
|
||||
</verify>
|
||||
</testcase>
|
Loading…
x
Reference in New Issue
Block a user