1
0
mirror of https://github.com/moparisthebest/curl synced 2024-11-11 20:15:03 -05:00

tool_writeout: refactor write-out and write-out json

- Deduplicate the logic used by write-out and write-out json.

Rather than have separate writeLong, writeString, etc, logic for
each of write-out and write-out json instead have respective shared
functions that can output either format and a 'use_json' parameter to
indicate whether it is json that is output.

This will make it easier to maintain. Rather than have to go through
two sets of logic now we only have to go through one.

- Support write-out %{errormsg} and %{exitcode} in json.

- Clarify in the doc that %{exitcode} is the exit code of the transfer.

Prior to this change it just said "The numerical exitcode" which
implies it's the exit code of the tool, and it's not necessarily that.

Closes https://github.com/curl/curl/pull/6544
This commit is contained in:
Jay Satiro 2021-01-28 18:56:50 -05:00
parent b9f11ae875
commit 65ca229461
7 changed files with 306 additions and 417 deletions

View File

@ -33,7 +33,7 @@ The Content-Type of the requested document, if there was any.
The error message. (Added in 7.75.0) The error message. (Added in 7.75.0)
.TP .TP
.B exitcode .B exitcode
The numerical exitcode. (Added in 7.75.0) The numerical exitcode of the transfer. (Added in 7.75.0)
.TP .TP
.B filename_effective .B filename_effective
The ultimate filename that curl writes out to. This is only meaningful if curl The ultimate filename that curl writes out to. This is only meaningful if curl

View File

@ -625,9 +625,6 @@ static CURLcode post_per_transfer(struct GlobalConfig *global,
newline here */ newline here */
fputs("\n", per->progressbar.out); fputs("\n", per->progressbar.out);
if(config->writeout)
ourWriteOut(per->curl, per, config->writeout, result);
/* Close the outs file */ /* Close the outs file */
if(outs->fopened && outs->stream) { if(outs->fopened && outs->stream) {
int rc = fclose(outs->stream); int rc = fclose(outs->stream);
@ -647,6 +644,10 @@ static CURLcode post_per_transfer(struct GlobalConfig *global,
setfiletime(filetime, outs->filename, global); setfiletime(filetime, outs->filename, global);
} }
/* Write the --write-out data before cleanup but after result is final */
if(config->writeout)
ourWriteOut(config->writeout, per, result);
/* Close function-local opened file descriptors */ /* Close function-local opened file descriptors */
if(per->heads.fopened && per->heads.stream) if(per->heads.fopened && per->heads.stream)
fclose(per->heads.stream); fclose(per->heads.stream);

View File

@ -29,77 +29,288 @@
#include "memdebug.h" /* keep this as LAST include */ #include "memdebug.h" /* keep this as LAST include */
static const struct writeoutvar variables[] = { static int writeTime(FILE *stream, const struct writeoutvar *wovar,
{"content_type", VAR_CONTENT_TYPE, 0, CURLINFO_CONTENT_TYPE, JSON_STRING}, struct per_transfer *per, CURLcode per_result,
{"filename_effective", VAR_EFFECTIVE_FILENAME, 0, 0, JSON_FILENAME}, bool use_json);
{"exitcode", VAR_EXITCODE, 0, 0, JSON_LONG},
{"errormsg", VAR_ERRORMSG, 0, 0, JSON_STRING}, static int writeString(FILE *stream, const struct writeoutvar *wovar,
{"ftp_entry_path", VAR_FTP_ENTRY_PATH, 0, CURLINFO_FTP_ENTRY_PATH, struct per_transfer *per, CURLcode per_result,
JSON_STRING}, bool use_json);
{"http_code", VAR_HTTP_CODE, 0, CURLINFO_RESPONSE_CODE, JSON_LONG},
{"http_connect", VAR_HTTP_CODE_PROXY, 0, CURLINFO_HTTP_CONNECTCODE, static int writeLong(FILE *stream, const struct writeoutvar *wovar,
JSON_LONG}, struct per_transfer *per, CURLcode per_result,
{"http_version", VAR_HTTP_VERSION, 0, CURLINFO_HTTP_VERSION, JSON_VERSION}, bool use_json);
{"json", VAR_JSON, 1, 0, JSON_NONE},
{"local_ip", VAR_LOCAL_IP, 0, CURLINFO_LOCAL_IP, JSON_STRING}, static int writeOffset(FILE *stream, const struct writeoutvar *wovar,
{"local_port", VAR_LOCAL_PORT, 0, CURLINFO_LOCAL_PORT, JSON_LONG}, struct per_transfer *per, CURLcode per_result,
{"method", VAR_EFFECTIVE_METHOD, 0, CURLINFO_EFFECTIVE_METHOD, JSON_STRING}, bool use_json);
{"num_connects", VAR_NUM_CONNECTS, 0, CURLINFO_NUM_CONNECTS, JSON_LONG},
{"num_headers", VAR_NUM_HEADERS, 0, 0, JSON_LONG}, static const char *http_version[] = {
{"num_redirects", VAR_REDIRECT_COUNT, 0, CURLINFO_REDIRECT_COUNT, JSON_LONG}, "0", /* CURL_HTTP_VERSION_NONE */
{"onerror", VAR_ONERROR, 1, 0, JSON_NONE}, "1", /* CURL_HTTP_VERSION_1_0 */
{"proxy_ssl_verify_result", VAR_PROXY_SSL_VERIFY_RESULT, 0, "1.1", /* CURL_HTTP_VERSION_1_1 */
CURLINFO_PROXY_SSL_VERIFYRESULT, JSON_LONG}, "2", /* CURL_HTTP_VERSION_2 */
{"redirect_url", VAR_REDIRECT_URL, 0, CURLINFO_REDIRECT_URL, JSON_STRING}, "3" /* CURL_HTTP_VERSION_3 */
{"remote_ip", VAR_PRIMARY_IP, 0, CURLINFO_PRIMARY_IP, JSON_STRING},
{"remote_port", VAR_PRIMARY_PORT, 0, CURLINFO_PRIMARY_PORT, JSON_LONG},
{"response_code", VAR_HTTP_CODE, 0, CURLINFO_RESPONSE_CODE, JSON_LONG},
{"scheme", VAR_SCHEME, 0, CURLINFO_SCHEME, JSON_STRING},
{"size_download", VAR_SIZE_DOWNLOAD, 0, CURLINFO_SIZE_DOWNLOAD_T,
JSON_OFFSET},
{"size_header", VAR_HEADER_SIZE, 0, CURLINFO_HEADER_SIZE, JSON_LONG},
{"size_request", VAR_REQUEST_SIZE, 0, CURLINFO_REQUEST_SIZE, JSON_LONG},
{"size_upload", VAR_SIZE_UPLOAD, 0, CURLINFO_SIZE_UPLOAD_T, JSON_OFFSET},
{"speed_download", VAR_SPEED_DOWNLOAD, 0, CURLINFO_SPEED_DOWNLOAD_T,
JSON_OFFSET},
{"speed_upload", VAR_SPEED_UPLOAD, 0, CURLINFO_SPEED_UPLOAD_T, JSON_OFFSET},
{"ssl_verify_result", VAR_SSL_VERIFY_RESULT, 0, CURLINFO_SSL_VERIFYRESULT,
JSON_LONG},
{"stderr", VAR_STDERR, 1, 0, JSON_NONE},
{"stdout", VAR_STDOUT, 1, 0, JSON_NONE},
{"time_appconnect", VAR_APPCONNECT_TIME, 0, CURLINFO_APPCONNECT_TIME_T,
JSON_TIME},
{"time_connect", VAR_CONNECT_TIME, 0, CURLINFO_CONNECT_TIME_T, JSON_TIME},
{"time_namelookup", VAR_NAMELOOKUP_TIME, 0, CURLINFO_NAMELOOKUP_TIME_T,
JSON_TIME},
{"time_pretransfer", VAR_PRETRANSFER_TIME, 0, CURLINFO_PRETRANSFER_TIME_T,
JSON_TIME},
{"time_redirect", VAR_REDIRECT_TIME, 0, CURLINFO_REDIRECT_TIME_T, JSON_TIME},
{"time_starttransfer", VAR_STARTTRANSFER_TIME, 0,
CURLINFO_STARTTRANSFER_TIME_T, JSON_TIME},
{"time_total", VAR_TOTAL_TIME, 0, CURLINFO_TOTAL_TIME_T, JSON_TIME},
{"url", VAR_INPUT_URL, 0, 0, JSON_STRING},
{"url_effective", VAR_EFFECTIVE_URL, 0, CURLINFO_EFFECTIVE_URL, JSON_STRING},
{"urlnum", VAR_URLNUM, 0, 0, JSON_LONG},
{NULL, VAR_NONE, 1, 0, JSON_NONE}
}; };
static void us2sec(FILE *stream, curl_off_t us) /* The designated write function should be the same as the CURLINFO return type
with exceptions special cased in the respective function. For example,
http_version uses CURLINFO_HTTP_VERSION which returns the version as a long,
however it is output as a string and therefore is handled in writeString.
Yes: "http_version": "1.1"
No: "http_version": 1.1
Variable names should be in alphabetical order.
*/
static const struct writeoutvar variables[] = {
{"content_type", VAR_CONTENT_TYPE, CURLINFO_CONTENT_TYPE, writeString},
{"errormsg", VAR_ERRORMSG, 0, writeString},
{"exitcode", VAR_EXITCODE, 0, writeLong},
{"filename_effective", VAR_EFFECTIVE_FILENAME, 0, writeString},
{"ftp_entry_path", VAR_FTP_ENTRY_PATH, CURLINFO_FTP_ENTRY_PATH, writeString},
{"http_code", VAR_HTTP_CODE, CURLINFO_RESPONSE_CODE, writeLong},
{"http_connect", VAR_HTTP_CODE_PROXY, CURLINFO_HTTP_CONNECTCODE, writeLong},
{"http_version", VAR_HTTP_VERSION, CURLINFO_HTTP_VERSION, writeString},
{"json", VAR_JSON, 0, NULL},
{"local_ip", VAR_LOCAL_IP, CURLINFO_LOCAL_IP, writeString},
{"local_port", VAR_LOCAL_PORT, CURLINFO_LOCAL_PORT, writeLong},
{"method", VAR_EFFECTIVE_METHOD, CURLINFO_EFFECTIVE_METHOD, writeString},
{"num_connects", VAR_NUM_CONNECTS, CURLINFO_NUM_CONNECTS, writeLong},
{"num_headers", VAR_NUM_HEADERS, 0, writeLong},
{"num_redirects", VAR_REDIRECT_COUNT, CURLINFO_REDIRECT_COUNT, writeLong},
{"onerror", VAR_ONERROR, 0, NULL},
{"proxy_ssl_verify_result", VAR_PROXY_SSL_VERIFY_RESULT,
CURLINFO_PROXY_SSL_VERIFYRESULT, writeLong},
{"redirect_url", VAR_REDIRECT_URL, CURLINFO_REDIRECT_URL, writeString},
{"remote_ip", VAR_PRIMARY_IP, CURLINFO_PRIMARY_IP, writeString},
{"remote_port", VAR_PRIMARY_PORT, CURLINFO_PRIMARY_PORT, writeLong},
{"response_code", VAR_HTTP_CODE, CURLINFO_RESPONSE_CODE, writeLong},
{"scheme", VAR_SCHEME, CURLINFO_SCHEME, writeString},
{"size_download", VAR_SIZE_DOWNLOAD, CURLINFO_SIZE_DOWNLOAD_T, writeOffset},
{"size_header", VAR_HEADER_SIZE, CURLINFO_HEADER_SIZE, writeLong},
{"size_request", VAR_REQUEST_SIZE, CURLINFO_REQUEST_SIZE, writeLong},
{"size_upload", VAR_SIZE_UPLOAD, CURLINFO_SIZE_UPLOAD_T, writeOffset},
{"speed_download", VAR_SPEED_DOWNLOAD, CURLINFO_SPEED_DOWNLOAD_T,
writeOffset},
{"speed_upload", VAR_SPEED_UPLOAD, CURLINFO_SPEED_UPLOAD_T, writeOffset},
{"ssl_verify_result", VAR_SSL_VERIFY_RESULT, CURLINFO_SSL_VERIFYRESULT,
writeLong},
{"stderr", VAR_STDERR, 0, NULL},
{"stdout", VAR_STDOUT, 0, NULL},
{"time_appconnect", VAR_APPCONNECT_TIME, CURLINFO_APPCONNECT_TIME_T,
writeTime},
{"time_connect", VAR_CONNECT_TIME, CURLINFO_CONNECT_TIME_T, writeTime},
{"time_namelookup", VAR_NAMELOOKUP_TIME, CURLINFO_NAMELOOKUP_TIME_T,
writeTime},
{"time_pretransfer", VAR_PRETRANSFER_TIME, CURLINFO_PRETRANSFER_TIME_T,
writeTime},
{"time_redirect", VAR_REDIRECT_TIME, CURLINFO_REDIRECT_TIME_T, writeTime},
{"time_starttransfer", VAR_STARTTRANSFER_TIME, CURLINFO_STARTTRANSFER_TIME_T,
writeTime},
{"time_total", VAR_TOTAL_TIME, CURLINFO_TOTAL_TIME_T, writeTime},
{"url", VAR_INPUT_URL, 0, writeString},
{"url_effective", VAR_EFFECTIVE_URL, CURLINFO_EFFECTIVE_URL, writeString},
{"urlnum", VAR_URLNUM, 0, writeLong},
{NULL, VAR_NONE, 0, NULL}
};
static int writeTime(FILE *stream, const struct writeoutvar *wovar,
struct per_transfer *per, CURLcode per_result,
bool use_json)
{ {
curl_off_t secs = us / 1000000; bool valid = false;
us %= 1000000; curl_off_t us = 0;
fprintf(stream, "%" CURL_FORMAT_CURL_OFF_TU ".%06" CURL_FORMAT_CURL_OFF_TU,
secs, us); (void)per;
(void)per_result;
DEBUGASSERT(wovar->writefunc == writeTime);
if(wovar->ci) {
if(!curl_easy_getinfo(per->curl, wovar->ci, &us))
valid = true;
}
else {
DEBUGASSERT(0);
}
if(valid) {
curl_off_t secs = us / 1000000;
us %= 1000000;
if(use_json)
fprintf(stream, "\"%s\":", wovar->name);
fprintf(stream, "%" CURL_FORMAT_CURL_OFF_TU
".%06" CURL_FORMAT_CURL_OFF_TU, secs, us);
}
else {
if(use_json)
fprintf(stream, "\"%s\":null", wovar->name);
}
return 1; /* return 1 if anything was written */
} }
void ourWriteOut(CURL *curl, struct per_transfer *per, const char *writeinfo, static int writeString(FILE *stream, const struct writeoutvar *wovar,
CURLcode result) struct per_transfer *per, CURLcode per_result,
bool use_json)
{
bool valid = false;
const char *strinfo = NULL;
DEBUGASSERT(wovar->writefunc == writeString);
if(wovar->ci) {
if(wovar->ci == CURLINFO_HTTP_VERSION) {
long version = 0;
if(!curl_easy_getinfo(per->curl, CURLINFO_HTTP_VERSION, &version) &&
(version >= 0) &&
(version < (long)(sizeof(http_version)/sizeof(http_version[0])))) {
strinfo = http_version[version];
valid = true;
}
}
else {
if(!curl_easy_getinfo(per->curl, wovar->ci, &strinfo) && strinfo)
valid = true;
}
}
else {
switch(wovar->id) {
case VAR_ERRORMSG:
if(per_result) {
strinfo = per->errorbuffer[0] ? per->errorbuffer :
curl_easy_strerror(per_result);
valid = true;
}
break;
case VAR_EFFECTIVE_FILENAME:
if(per->outs.filename) {
strinfo = per->outs.filename;
valid = true;
}
break;
case VAR_INPUT_URL:
if(per->this_url) {
strinfo = per->this_url;
valid = true;
}
break;
default:
DEBUGASSERT(0);
break;
}
}
if(valid) {
DEBUGASSERT(strinfo);
if(use_json) {
fprintf(stream, "\"%s\":\"", wovar->name);
jsonWriteString(stream, strinfo);
fputs("\"", stream);
}
else
fputs(strinfo, stream);
}
else {
if(use_json)
fprintf(stream, "\"%s\":null", wovar->name);
}
return 1; /* return 1 if anything was written */
}
static int writeLong(FILE *stream, const struct writeoutvar *wovar,
struct per_transfer *per, CURLcode per_result,
bool use_json)
{
bool valid = false;
long longinfo = 0;
DEBUGASSERT(wovar->writefunc == writeLong);
if(wovar->ci) {
if(!curl_easy_getinfo(per->curl, wovar->ci, &longinfo))
valid = true;
}
else {
switch(wovar->id) {
case VAR_NUM_HEADERS:
longinfo = per->num_headers;
valid = true;
break;
case VAR_EXITCODE:
longinfo = per_result;
valid = true;
break;
case VAR_URLNUM:
if(per->urlnum <= INT_MAX) {
longinfo = (long)per->urlnum;
valid = true;
}
break;
default:
DEBUGASSERT(0);
break;
}
}
if(valid) {
if(use_json)
fprintf(stream, "\"%s\":", wovar->name);
if(wovar->id == VAR_HTTP_CODE || wovar->id == VAR_HTTP_CODE_PROXY)
fprintf(stream, "%03ld", longinfo);
else
fprintf(stream, "%ld", longinfo);
}
else {
if(use_json)
fprintf(stream, "\"%s\":null", wovar->name);
}
return 1; /* return 1 if anything was written */
}
static int writeOffset(FILE *stream, const struct writeoutvar *wovar,
struct per_transfer *per, CURLcode per_result,
bool use_json)
{
bool valid = false;
curl_off_t offinfo = 0;
(void)per;
(void)per_result;
DEBUGASSERT(wovar->writefunc == writeOffset);
if(wovar->ci) {
if(!curl_easy_getinfo(per->curl, wovar->ci, &offinfo))
valid = true;
}
else {
DEBUGASSERT(0);
}
if(valid) {
if(use_json)
fprintf(stream, "\"%s\":", wovar->name);
fprintf(stream, "%" CURL_FORMAT_CURL_OFF_T, offinfo);
}
else {
if(use_json)
fprintf(stream, "\"%s\":null", wovar->name);
}
return 1; /* return 1 if anything was written */
}
void ourWriteOut(const char *writeinfo, struct per_transfer *per,
CURLcode per_result)
{ {
FILE *stream = stdout; FILE *stream = stdout;
const char *ptr = writeinfo; const char *ptr = writeinfo;
char *stringp = NULL;
long longinfo;
curl_off_t offinfo;
bool done = FALSE; bool done = FALSE;
while(ptr && *ptr && !done) { while(ptr && *ptr && !done) {
@ -129,217 +340,10 @@ void ourWriteOut(CURL *curl, struct per_transfer *per, const char *writeinfo,
match = TRUE; match = TRUE;
switch(variables[i].id) { switch(variables[i].id) {
case VAR_ONERROR: case VAR_ONERROR:
if(result == CURLE_OK) if(per_result == CURLE_OK)
/* this isn't error so skip the rest */ /* this isn't error so skip the rest */
done = TRUE; done = TRUE;
break; break;
case VAR_EXITCODE:
fprintf(stream, "%d", (int)result);
break;
case VAR_ERRORMSG:
if(result)
fputs(per->errorbuffer[0] ? per->errorbuffer :
curl_easy_strerror(result), stream);
break;
case VAR_INPUT_URL:
if(per->this_url)
fputs(per->this_url, stream);
break;
case VAR_URLNUM:
fprintf(stream, "%u", per->urlnum);
break;
case VAR_EFFECTIVE_URL:
if((CURLE_OK ==
curl_easy_getinfo(curl, CURLINFO_EFFECTIVE_URL, &stringp))
&& stringp)
fputs(stringp, stream);
break;
case VAR_EFFECTIVE_METHOD:
if((CURLE_OK == curl_easy_getinfo(curl,
CURLINFO_EFFECTIVE_METHOD,
&stringp))
&& stringp)
fputs(stringp, stream);
break;
case VAR_HTTP_CODE:
if(CURLE_OK ==
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &longinfo))
fprintf(stream, "%03ld", longinfo);
break;
case VAR_NUM_HEADERS:
fprintf(stream, "%ld", per->num_headers);
break;
case VAR_HTTP_CODE_PROXY:
if(CURLE_OK ==
curl_easy_getinfo(curl, CURLINFO_HTTP_CONNECTCODE,
&longinfo))
fprintf(stream, "%03ld", longinfo);
break;
case VAR_HEADER_SIZE:
if(CURLE_OK ==
curl_easy_getinfo(curl, CURLINFO_HEADER_SIZE, &longinfo))
fprintf(stream, "%ld", longinfo);
break;
case VAR_REQUEST_SIZE:
if(CURLE_OK ==
curl_easy_getinfo(curl, CURLINFO_REQUEST_SIZE, &longinfo))
fprintf(stream, "%ld", longinfo);
break;
case VAR_NUM_CONNECTS:
if(CURLE_OK ==
curl_easy_getinfo(curl, CURLINFO_NUM_CONNECTS, &longinfo))
fprintf(stream, "%ld", longinfo);
break;
case VAR_REDIRECT_COUNT:
if(CURLE_OK ==
curl_easy_getinfo(curl, CURLINFO_REDIRECT_COUNT, &longinfo))
fprintf(stream, "%ld", longinfo);
break;
case VAR_REDIRECT_TIME:
if(CURLE_OK ==
curl_easy_getinfo(curl, CURLINFO_REDIRECT_TIME_T, &offinfo))
us2sec(stream, offinfo);
break;
case VAR_TOTAL_TIME:
if(CURLE_OK ==
curl_easy_getinfo(curl, CURLINFO_TOTAL_TIME_T, &offinfo))
us2sec(stream, offinfo);
break;
case VAR_NAMELOOKUP_TIME:
if(CURLE_OK ==
curl_easy_getinfo(curl, CURLINFO_NAMELOOKUP_TIME_T,
&offinfo))
us2sec(stream, offinfo);
break;
case VAR_CONNECT_TIME:
if(CURLE_OK ==
curl_easy_getinfo(curl, CURLINFO_CONNECT_TIME_T, &offinfo))
us2sec(stream, offinfo);
break;
case VAR_APPCONNECT_TIME:
if(CURLE_OK ==
curl_easy_getinfo(curl, CURLINFO_APPCONNECT_TIME_T,
&offinfo))
us2sec(stream, offinfo);
break;
case VAR_PRETRANSFER_TIME:
if(CURLE_OK ==
curl_easy_getinfo(curl, CURLINFO_PRETRANSFER_TIME_T,
&offinfo))
us2sec(stream, offinfo);
break;
case VAR_STARTTRANSFER_TIME:
if(CURLE_OK ==
curl_easy_getinfo(curl, CURLINFO_STARTTRANSFER_TIME_T,
&offinfo))
us2sec(stream, offinfo);
break;
case VAR_SIZE_UPLOAD:
if(CURLE_OK ==
curl_easy_getinfo(curl, CURLINFO_SIZE_UPLOAD_T, &offinfo))
fprintf(stream, "%" CURL_FORMAT_CURL_OFF_TU, offinfo);
break;
case VAR_SIZE_DOWNLOAD:
if(CURLE_OK ==
curl_easy_getinfo(curl, CURLINFO_SIZE_DOWNLOAD_T,
&offinfo))
fprintf(stream, "%" CURL_FORMAT_CURL_OFF_TU, offinfo);
break;
case VAR_SPEED_DOWNLOAD:
if(CURLE_OK ==
curl_easy_getinfo(curl, CURLINFO_SPEED_DOWNLOAD_T,
&offinfo))
fprintf(stream, "%" CURL_FORMAT_CURL_OFF_TU, offinfo);
break;
case VAR_SPEED_UPLOAD:
if(CURLE_OK ==
curl_easy_getinfo(curl, CURLINFO_SPEED_UPLOAD_T, &offinfo))
fprintf(stream, "%" CURL_FORMAT_CURL_OFF_TU, offinfo);
break;
case VAR_CONTENT_TYPE:
if((CURLE_OK ==
curl_easy_getinfo(curl, CURLINFO_CONTENT_TYPE, &stringp))
&& stringp)
fputs(stringp, stream);
break;
case VAR_FTP_ENTRY_PATH:
if((CURLE_OK ==
curl_easy_getinfo(curl, CURLINFO_FTP_ENTRY_PATH, &stringp))
&& stringp)
fputs(stringp, stream);
break;
case VAR_REDIRECT_URL:
if((CURLE_OK ==
curl_easy_getinfo(curl, CURLINFO_REDIRECT_URL, &stringp))
&& stringp)
fputs(stringp, stream);
break;
case VAR_SSL_VERIFY_RESULT:
if(CURLE_OK ==
curl_easy_getinfo(curl, CURLINFO_SSL_VERIFYRESULT,
&longinfo))
fprintf(stream, "%ld", longinfo);
break;
case VAR_PROXY_SSL_VERIFY_RESULT:
if(CURLE_OK ==
curl_easy_getinfo(curl, CURLINFO_PROXY_SSL_VERIFYRESULT,
&longinfo))
fprintf(stream, "%ld", longinfo);
break;
case VAR_EFFECTIVE_FILENAME:
if(per->outs.filename)
fputs(per->outs.filename, stream);
break;
case VAR_PRIMARY_IP:
if((CURLE_OK == curl_easy_getinfo(curl, CURLINFO_PRIMARY_IP,
&stringp)) && stringp)
fputs(stringp, stream);
break;
case VAR_PRIMARY_PORT:
if(CURLE_OK ==
curl_easy_getinfo(curl, CURLINFO_PRIMARY_PORT,
&longinfo))
fprintf(stream, "%ld", longinfo);
break;
case VAR_LOCAL_IP:
if((CURLE_OK == curl_easy_getinfo(curl, CURLINFO_LOCAL_IP,
&stringp)) && stringp)
fputs(stringp, stream);
break;
case VAR_LOCAL_PORT:
if(CURLE_OK ==
curl_easy_getinfo(curl, CURLINFO_LOCAL_PORT,
&longinfo))
fprintf(stream, "%ld", longinfo);
break;
case VAR_HTTP_VERSION:
if(CURLE_OK ==
curl_easy_getinfo(curl, CURLINFO_HTTP_VERSION,
&longinfo)) {
const char *version = "0";
switch(longinfo) {
case CURL_HTTP_VERSION_1_0:
version = "1.0";
break;
case CURL_HTTP_VERSION_1_1:
version = "1.1";
break;
case CURL_HTTP_VERSION_2_0:
version = "2";
break;
case CURL_HTTP_VERSION_3:
version = "3";
break;
}
fprintf(stream, version);
}
break;
case VAR_SCHEME:
if((CURLE_OK == curl_easy_getinfo(curl, CURLINFO_SCHEME,
&stringp)) && stringp)
fputs(stringp, stream);
break;
case VAR_STDOUT: case VAR_STDOUT:
stream = stdout; stream = stdout;
break; break;
@ -347,8 +351,11 @@ void ourWriteOut(CURL *curl, struct per_transfer *per, const char *writeinfo,
stream = stderr; stream = stderr;
break; break;
case VAR_JSON: case VAR_JSON:
ourWriteOutJSON(variables, curl, per, stream); ourWriteOutJSON(stream, variables, per, per_result);
break;
default: default:
(void)variables[i].writefunc(stream, &variables[i],
per, per_result, false);
break; break;
} }
break; break;

View File

@ -69,25 +69,16 @@ typedef enum {
VAR_NUM_OF_VARS /* must be the last */ VAR_NUM_OF_VARS /* must be the last */
} writeoutid; } writeoutid;
typedef enum {
JSON_NONE,
JSON_STRING,
JSON_LONG,
JSON_OFFSET,
JSON_TIME,
JSON_VERSION,
JSON_FILENAME
} jsontype;
struct writeoutvar { struct writeoutvar {
const char *name; const char *name;
writeoutid id; writeoutid id;
int is_ctrl; CURLINFO ci;
CURLINFO cinfo; int (*writefunc)(FILE *stream, const struct writeoutvar *wovar,
jsontype jsontype; struct per_transfer *per, CURLcode per_result,
bool use_json);
}; };
void ourWriteOut(CURL *curl, struct per_transfer *per, const char *writeinfo, void ourWriteOut(const char *writeinfo, struct per_transfer *per,
CURLcode exitcode); CURLcode per_result);
#endif /* HEADER_CURL_TOOL_WRITEOUT_H */ #endif /* HEADER_CURL_TOOL_WRITEOUT_H */

View File

@ -30,15 +30,7 @@
#include "tool_writeout.h" #include "tool_writeout.h"
static const char *http_version[] = { void jsonWriteString(FILE *stream, const char *in)
"0", /* CURL_HTTP_VERSION_NONE */
"1", /* CURL_HTTP_VERSION_1_0 */
"1.1", /* CURL_HTTP_VERSION_1_1 */
"2", /* CURL_HTTP_VERSION_2 */
"3" /* CURL_HTTP_VERSION_3 */
};
static void jsonEscape(FILE *stream, const char *in)
{ {
const char *i = in; const char *i = in;
const char *in_end = in + strlen(in); const char *in_end = in + strlen(in);
@ -78,126 +70,22 @@ static void jsonEscape(FILE *stream, const char *in)
} }
} }
static int writeTime(FILE *str, CURL *curl, const char *key, CURLINFO ci) void ourWriteOutJSON(FILE *stream, const struct writeoutvar mappings[],
{ struct per_transfer *per, CURLcode per_result)
curl_off_t val = 0;
if(CURLE_OK == curl_easy_getinfo(curl, ci, &val)) {
curl_off_t s = val / 1000000l;
curl_off_t ms = val % 1000000l;
fprintf(str, "\"%s\":%" CURL_FORMAT_CURL_OFF_T
".%06" CURL_FORMAT_CURL_OFF_T, key, s, ms);
return 1;
}
return 0;
}
static int writeString(FILE *str, CURL *curl, const char *key, CURLINFO ci)
{
char *valp = NULL;
if((CURLE_OK == curl_easy_getinfo(curl, ci, &valp)) && valp) {
fprintf(str, "\"%s\":\"", key);
jsonEscape(str, valp);
fprintf(str, "\"");
return 1;
}
return 0;
}
static int writeLong(FILE *str, CURL *curl, const char *key, CURLINFO ci,
struct per_transfer *per, const struct writeoutvar *wovar)
{
if(wovar->id == VAR_NUM_HEADERS) {
fprintf(str, "\"%s\":%ld", key, per->num_headers);
return 1;
}
else {
long val = 0;
if(CURLE_OK == curl_easy_getinfo(curl, ci, &val)) {
fprintf(str, "\"%s\":%ld", key, val);
return 1;
}
}
return 0;
}
static int writeOffset(FILE *str, CURL *curl, const char *key, CURLINFO ci)
{
curl_off_t val = 0;
if(CURLE_OK == curl_easy_getinfo(curl, ci, &val)) {
fprintf(str, "\"%s\":%" CURL_FORMAT_CURL_OFF_T, key, val);
return 1;
}
return 0;
}
static int writeFilename(FILE *str, const char *key, const char *filename)
{
if(filename) {
fprintf(str, "\"%s\":\"", key);
jsonEscape(str, filename);
fprintf(str, "\"");
}
else {
fprintf(str, "\"%s\":null", key);
}
return 1;
}
static int writeVersion(FILE *str, CURL *curl, const char *key, CURLINFO ci)
{
long version = 0;
if(CURLE_OK == curl_easy_getinfo(curl, ci, &version) &&
(version >= 0) &&
(version < (long)(sizeof(http_version)/sizeof(char *)))) {
fprintf(str, "\"%s\":\"%s\"", key, http_version[version]);
return 1;
}
return 0;
}
void ourWriteOutJSON(const struct writeoutvar mappings[], CURL *curl,
struct per_transfer *per, FILE *stream)
{ {
int i; int i;
fputs("{", stream); fputs("{", stream);
for(i = 0; mappings[i].name != NULL; i++) { for(i = 0; mappings[i].name != NULL; i++) {
const struct writeoutvar *wovar = &mappings[i]; if(mappings[i].writefunc &&
const char *name = mappings[i].name; mappings[i].writefunc(stream, &mappings[i], per, per_result, true))
CURLINFO cinfo = mappings[i].cinfo;
int ok = 0;
if(mappings[i].is_ctrl == 1) {
continue;
}
switch(mappings[i].jsontype) {
case JSON_STRING:
ok = writeString(stream, curl, name, cinfo);
break;
case JSON_LONG:
ok = writeLong(stream, curl, name, cinfo, per, wovar);
break;
case JSON_OFFSET:
ok = writeOffset(stream, curl, name, cinfo);
break;
case JSON_TIME:
ok = writeTime(stream, curl, name, cinfo);
break;
case JSON_FILENAME:
ok = writeFilename(stream, name, per->outs.filename);
break;
case JSON_VERSION:
ok = writeVersion(stream, curl, name, cinfo);
break;
default:
break;
}
if(ok) {
fputs(",", stream); fputs(",", stream);
}
} }
fprintf(stream, "\"curl_version\":\"%s\"}", curl_version()); /* The variables are sorted in alphabetical order but as a special case
curl_version (which is not actually a --write-out variable) is last. */
fprintf(stream, "\"curl_version\":\"");
jsonWriteString(stream, curl_version());
fprintf(stream, "\"}");
} }

View File

@ -24,7 +24,9 @@
#include "tool_setup.h" #include "tool_setup.h"
#include "tool_writeout.h" #include "tool_writeout.h"
void ourWriteOutJSON(const struct writeoutvar mappings[], CURL *curl, void ourWriteOutJSON(FILE *stream, const struct writeoutvar mappings[],
struct per_transfer *per, FILE *stream); struct per_transfer *per, CURLcode per_result);
void jsonWriteString(FILE *stream, const char *in);
#endif /* HEADER_CURL_TOOL_WRITEOUT_H */ #endif /* HEADER_CURL_TOOL_WRITEOUT_H */

View File

@ -59,7 +59,7 @@ Accept: */*
</protocol> </protocol>
<stdout nonewline="yes"> <stdout nonewline="yes">
{"content_type":"text/html","filename_effective":"log/out970","http_code":200,"http_connect":0,"http_version":"1.1","local_ip":"127.0.0.1","local_port":13,"method":"GET","num_connects":1,"num_headers":9,"num_redirects":0,"proxy_ssl_verify_result":0,"remote_ip":"%HOSTIP","remote_port":%HTTPPORT,"response_code":200,"scheme":"HTTP","size_download":445,"size_header":4019,"size_request":4019,"size_upload":0,"speed_download":13,"speed_upload":13,"ssl_verify_result":0,"time_appconnect":0.000013,"time_connect":0.000013,"time_namelookup":0.000013,"time_pretransfer":0.000013,"time_redirect":0.000013,"time_starttransfer":0.000013,"time_total":0.000013,"url_effective":"http://%HOSTIP:%HTTPPORT/970","curl_version":"curl-unit-test-fake-version"} {"content_type":"text/html","errormsg":null,"exitcode":0,"filename_effective":"log/out970","ftp_entry_path":null,"http_code":200,"http_connect":000,"http_version":"1.1","local_ip":"127.0.0.1","local_port":13,"method":"GET","num_connects":1,"num_headers":9,"num_redirects":0,"proxy_ssl_verify_result":0,"redirect_url":null,"remote_ip":"%HOSTIP","remote_port":%HTTPPORT,"response_code":200,"scheme":"HTTP","size_download":445,"size_header":4019,"size_request":4019,"size_upload":0,"speed_download":13,"speed_upload":13,"ssl_verify_result":0,"time_appconnect":0.000013,"time_connect":0.000013,"time_namelookup":0.000013,"time_pretransfer":0.000013,"time_redirect":0.000013,"time_starttransfer":0.000013,"time_total":0.000013,"url":"http://%HOSTIP:%HTTPPORT/970","url_effective":"http://%HOSTIP:%HTTPPORT/970","urlnum":0,"curl_version":"curl-unit-test-fake-version"}
</stdout> </stdout>
</verify> </verify>
</testcase> </testcase>