summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/cmdline-opts/write-out.d2
-rw-r--r--src/tool_operate.c7
-rw-r--r--src/tool_writeout.c549
-rw-r--r--src/tool_writeout.h21
-rw-r--r--src/tool_writeout_json.c134
-rw-r--r--src/tool_writeout_json.h6
-rw-r--r--tests/data/test9702
7 files changed, 305 insertions, 416 deletions
diff --git a/docs/cmdline-opts/write-out.d b/docs/cmdline-opts/write-out.d
index af5d0cf22..be8f75ad5 100644
--- a/docs/cmdline-opts/write-out.d
+++ b/docs/cmdline-opts/write-out.d
@@ -33,7 +33,7 @@ The Content-Type of the requested document, if there was any.
The error message. (Added in 7.75.0)
.TP
.B exitcode
-The numerical exitcode. (Added in 7.75.0)
+The numerical exitcode of the transfer. (Added in 7.75.0)
.TP
.B filename_effective
The ultimate filename that curl writes out to. This is only meaningful if curl
diff --git a/src/tool_operate.c b/src/tool_operate.c
index 140142a32..2b23680f2 100644
--- a/src/tool_operate.c
+++ b/src/tool_operate.c
@@ -625,9 +625,6 @@ static CURLcode post_per_transfer(struct GlobalConfig *global,
newline here */
fputs("\n", per->progressbar.out);
- if(config->writeout)
- ourWriteOut(per->curl, per, config->writeout, result);
-
/* Close the outs file */
if(outs->fopened && outs->stream) {
int rc = fclose(outs->stream);
@@ -647,6 +644,10 @@ static CURLcode post_per_transfer(struct GlobalConfig *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 */
if(per->heads.fopened && per->heads.stream)
fclose(per->heads.stream);
diff --git a/src/tool_writeout.c b/src/tool_writeout.c
index c548bd8f2..e914dfa7b 100644
--- a/src/tool_writeout.c
+++ b/src/tool_writeout.c
@@ -29,77 +29,288 @@
#include "memdebug.h" /* keep this as LAST include */
+static int writeTime(FILE *stream, const struct writeoutvar *wovar,
+ struct per_transfer *per, CURLcode per_result,
+ bool use_json);
+
+static int writeString(FILE *stream, const struct writeoutvar *wovar,
+ struct per_transfer *per, CURLcode per_result,
+ bool use_json);
+
+static int writeLong(FILE *stream, const struct writeoutvar *wovar,
+ struct per_transfer *per, CURLcode per_result,
+ bool use_json);
+
+static int writeOffset(FILE *stream, const struct writeoutvar *wovar,
+ struct per_transfer *per, CURLcode per_result,
+ bool use_json);
+
+static const char *http_version[] = {
+ "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 */
+};
+
+/* 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, 0, CURLINFO_CONTENT_TYPE, JSON_STRING},
- {"filename_effective", VAR_EFFECTIVE_FILENAME, 0, 0, JSON_FILENAME},
- {"exitcode", VAR_EXITCODE, 0, 0, JSON_LONG},
- {"errormsg", VAR_ERRORMSG, 0, 0, JSON_STRING},
- {"ftp_entry_path", VAR_FTP_ENTRY_PATH, 0, CURLINFO_FTP_ENTRY_PATH,
- JSON_STRING},
- {"http_code", VAR_HTTP_CODE, 0, CURLINFO_RESPONSE_CODE, JSON_LONG},
- {"http_connect", VAR_HTTP_CODE_PROXY, 0, CURLINFO_HTTP_CONNECTCODE,
- JSON_LONG},
- {"http_version", VAR_HTTP_VERSION, 0, CURLINFO_HTTP_VERSION, JSON_VERSION},
- {"json", VAR_JSON, 1, 0, JSON_NONE},
- {"local_ip", VAR_LOCAL_IP, 0, CURLINFO_LOCAL_IP, JSON_STRING},
- {"local_port", VAR_LOCAL_PORT, 0, CURLINFO_LOCAL_PORT, JSON_LONG},
- {"method", VAR_EFFECTIVE_METHOD, 0, CURLINFO_EFFECTIVE_METHOD, JSON_STRING},
- {"num_connects", VAR_NUM_CONNECTS, 0, CURLINFO_NUM_CONNECTS, JSON_LONG},
- {"num_headers", VAR_NUM_HEADERS, 0, 0, JSON_LONG},
- {"num_redirects", VAR_REDIRECT_COUNT, 0, CURLINFO_REDIRECT_COUNT, JSON_LONG},
- {"onerror", VAR_ONERROR, 1, 0, JSON_NONE},
- {"proxy_ssl_verify_result", VAR_PROXY_SSL_VERIFY_RESULT, 0,
- CURLINFO_PROXY_SSL_VERIFYRESULT, JSON_LONG},
- {"redirect_url", VAR_REDIRECT_URL, 0, CURLINFO_REDIRECT_URL, JSON_STRING},
- {"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}
+ {"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 void us2sec(FILE *stream, curl_off_t us)
+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;
- us %= 1000000;
- fprintf(stream, "%" CURL_FORMAT_CURL_OFF_TU ".%06" CURL_FORMAT_CURL_OFF_TU,
- secs, us);
+ bool valid = false;
+ curl_off_t us = 0;
+
+ (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,
- CURLcode result)
+static int writeString(FILE *stream, const struct writeoutvar *wovar,
+ 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;
const char *ptr = writeinfo;
- char *stringp = NULL;
- long longinfo;
- curl_off_t offinfo;
bool done = FALSE;
while(ptr && *ptr && !done) {
@@ -129,217 +340,10 @@ void ourWriteOut(CURL *curl, struct per_transfer *per, const char *writeinfo,
match = TRUE;
switch(variables[i].id) {
case VAR_ONERROR:
- if(result == CURLE_OK)
+ if(per_result == CURLE_OK)
/* this isn't error so skip the rest */
done = TRUE;
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:
stream = stdout;
break;
@@ -347,8 +351,11 @@ void ourWriteOut(CURL *curl, struct per_transfer *per, const char *writeinfo,
stream = stderr;
break;
case VAR_JSON:
- ourWriteOutJSON(variables, curl, per, stream);
+ ourWriteOutJSON(stream, variables, per, per_result);
+ break;
default:
+ (void)variables[i].writefunc(stream, &variables[i],
+ per, per_result, false);
break;
}
break;
diff --git a/src/tool_writeout.h b/src/tool_writeout.h
index 02858d8c1..2cf7d012a 100644
--- a/src/tool_writeout.h
+++ b/src/tool_writeout.h
@@ -69,25 +69,16 @@ typedef enum {
VAR_NUM_OF_VARS /* must be the last */
} writeoutid;
-typedef enum {
- JSON_NONE,
- JSON_STRING,
- JSON_LONG,
- JSON_OFFSET,
- JSON_TIME,
- JSON_VERSION,
- JSON_FILENAME
-} jsontype;
-
struct writeoutvar {
const char *name;
writeoutid id;
- int is_ctrl;
- CURLINFO cinfo;
- jsontype jsontype;
+ CURLINFO ci;
+ int (*writefunc)(FILE *stream, const struct writeoutvar *wovar,
+ struct per_transfer *per, CURLcode per_result,
+ bool use_json);
};
-void ourWriteOut(CURL *curl, struct per_transfer *per, const char *writeinfo,
- CURLcode exitcode);
+void ourWriteOut(const char *writeinfo, struct per_transfer *per,
+ CURLcode per_result);
#endif /* HEADER_CURL_TOOL_WRITEOUT_H */
diff --git a/src/tool_writeout_json.c b/src/tool_writeout_json.c
index 186ed6851..ca1e70868 100644
--- a/src/tool_writeout_json.c
+++ b/src/tool_writeout_json.c
@@ -30,15 +30,7 @@
#include "tool_writeout.h"
-static const char *http_version[] = {
- "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)
+void jsonWriteString(FILE *stream, const char *in)
{
const char *i = 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)
-{
- 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)
+void ourWriteOutJSON(FILE *stream, const struct writeoutvar mappings[],
+ struct per_transfer *per, CURLcode per_result)
{
int i;
fputs("{", stream);
- for(i = 0; mappings[i].name != NULL; i++) {
- const struct writeoutvar *wovar = &mappings[i];
- const char *name = mappings[i].name;
- 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) {
+ for(i = 0; mappings[i].name != NULL; i++) {
+ if(mappings[i].writefunc &&
+ mappings[i].writefunc(stream, &mappings[i], per, per_result, true))
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, "\"}");
}
diff --git a/src/tool_writeout_json.h b/src/tool_writeout_json.h
index cc8c4e585..3eb75243c 100644
--- a/src/tool_writeout_json.h
+++ b/src/tool_writeout_json.h
@@ -24,7 +24,9 @@
#include "tool_setup.h"
#include "tool_writeout.h"
-void ourWriteOutJSON(const struct writeoutvar mappings[], CURL *curl,
- struct per_transfer *per, FILE *stream);
+void ourWriteOutJSON(FILE *stream, const struct writeoutvar mappings[],
+ struct per_transfer *per, CURLcode per_result);
+
+void jsonWriteString(FILE *stream, const char *in);
#endif /* HEADER_CURL_TOOL_WRITEOUT_H */
diff --git a/tests/data/test970 b/tests/data/test970
index d2f9aa7b0..6a89a5455 100644
--- a/tests/data/test970
+++ b/tests/data/test970
@@ -59,7 +59,7 @@ Accept: */*
</protocol>
<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>
</verify>
</testcase>