Skip to content

Commit

Permalink
writeout: support to generate JSON output
Browse files Browse the repository at this point in the history
This commit adds support to generate JSON via the writeout feature:

    -w "%{json}"

It leverages the existing infrastructure as much as possible. Thus,
generating the JSON on STDERR is possible by:

    -w "%{stderr}%{json}"

This implements a variant of https://github.com/curl/curl/wiki/JSON#--write-out-json.
  • Loading branch information
mgumz committed Feb 14, 2020
1 parent 28b5b1c commit ed2951f
Show file tree
Hide file tree
Showing 6 changed files with 262 additions and 0 deletions.
3 changes: 3 additions & 0 deletions docs/cmdline-opts/write-out.d
Expand Up @@ -50,6 +50,9 @@ curl CONNECT request. (Added in 7.12.4)
.B http_version
The http version that was effectively used. (Added in 7.50.0)
.TP
.B json
A JSON object with all available keys.
.TP
.B local_ip
The IP address of the local end of the most recently done connection - can be
either IPv4 or IPv6 (Added in 7.29.0)
Expand Down
1 change: 1 addition & 0 deletions packages/Symbian/group/curl.mmp
Expand Up @@ -45,6 +45,7 @@ SOURCE \
tool_vms.c \
tool_writeenv.c \
tool_writeout.c \
tool_writeout_json.c \
tool_xattr.c

SOURCEPATH ../../../lib
Expand Down
2 changes: 2 additions & 0 deletions src/Makefile.inc
Expand Up @@ -62,6 +62,7 @@ CURL_CFILES = \
tool_util.c \
tool_vms.c \
tool_writeout.c \
tool_writeout_json.c \
tool_xattr.c

CURL_HFILES = \
Expand Down Expand Up @@ -107,6 +108,7 @@ CURL_HFILES = \
tool_version.h \
tool_vms.h \
tool_writeout.h \
tool_writeout_json.h \
tool_xattr.h

CURL_RCFILES = curl.rc
Expand Down
5 changes: 5 additions & 0 deletions src/tool_writeout.c
Expand Up @@ -25,6 +25,7 @@
#include "curlx.h"
#include "tool_cfgable.h"
#include "tool_writeout.h"
#include "tool_writeout_json.h"

#include "memdebug.h" /* keep this as LAST include */

Expand Down Expand Up @@ -62,6 +63,7 @@ typedef enum {
VAR_SCHEME,
VAR_STDOUT,
VAR_STDERR,
VAR_JSON,
VAR_NUM_OF_VARS /* must be the last */
} replaceid;

Expand Down Expand Up @@ -105,6 +107,7 @@ static const struct variable replacements[]={
{"scheme", VAR_SCHEME},
{"stdout", VAR_STDOUT},
{"stderr", VAR_STDERR},
{"json", VAR_JSON},
{NULL, VAR_NONE}
};

Expand Down Expand Up @@ -334,6 +337,8 @@ void ourWriteOut(CURL *curl, struct OutStruct *outs, const char *writeinfo)
case VAR_STDERR:
stream = stderr;
break;
case VAR_JSON:
ourWriteOutJSON(curl, outs, stream);
default:
break;
}
Expand Down
223 changes: 223 additions & 0 deletions src/tool_writeout_json.c
@@ -0,0 +1,223 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.haxx.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
***************************************************************************/
#include "tool_setup.h"

#define ENABLE_CURLX_PRINTF

/* use our own printf() functions */
#include "curlx.h"
#include "tool_cfgable.h"
#include "tool_writeout_json.h"


typedef enum {
JSON_NONE,
JSON_STRING,
JSON_LONG,
JSON_TIME,
JSON_VERSION,
JSON_FILENAME
} jsontype;

struct mapping {
const char *key;
jsontype type;
CURLINFO cinfo;
};

static const struct mapping mappings[]={
{"url_effective", JSON_STRING, CURLINFO_EFFECTIVE_URL},
{"http_code", JSON_LONG, CURLINFO_RESPONSE_CODE},
{"response_code", JSON_LONG, CURLINFO_RESPONSE_CODE},
{"http_connect", JSON_LONG, CURLINFO_HTTP_CONNECTCODE},
{"time_total", JSON_TIME, CURLINFO_TOTAL_TIME_T},
{"time_namelookup", JSON_TIME, CURLINFO_NAMELOOKUP_TIME_T},
{"time_connect", JSON_TIME, CURLINFO_CONNECT_TIME_T},
{"time_appconnect", JSON_TIME, CURLINFO_APPCONNECT_TIME_T},
{"time_pretransfer", JSON_TIME, CURLINFO_PRETRANSFER_TIME_T},
{"time_starttransfer", JSON_TIME, CURLINFO_STARTTRANSFER_TIME_T},
{"size_header", JSON_LONG, CURLINFO_HEADER_SIZE},
{"size_request", JSON_LONG, CURLINFO_REQUEST_SIZE},
{"size_download", JSON_LONG, CURLINFO_SIZE_DOWNLOAD_T},
{"size_upload", JSON_LONG, CURLINFO_SIZE_UPLOAD_T},
{"speed_download", JSON_TIME, CURLINFO_SPEED_DOWNLOAD_T},
{"speed_upload", JSON_TIME, CURLINFO_SPEED_UPLOAD_T},
{"content_type", JSON_STRING, CURLINFO_CONTENT_TYPE},
{"num_connects", JSON_LONG, CURLINFO_NUM_CONNECTS},
{"time_redirect", JSON_TIME, CURLINFO_REDIRECT_TIME_T},
{"num_redirects", JSON_LONG, CURLINFO_REDIRECT_COUNT},
{"ftp_entry_path", JSON_STRING, CURLINFO_FTP_ENTRY_PATH},
{"redirect_url", JSON_STRING, CURLINFO_REDIRECT_URL},
{"ssl_verify_result", JSON_LONG, CURLINFO_SSL_VERIFYRESULT},
{"proxy_ssl_verify_result", JSON_LONG, CURLINFO_PROXY_SSL_VERIFYRESULT},
{"filename_effective", JSON_FILENAME, CURLINFO_NONE},
{"remote_ip", JSON_STRING, CURLINFO_PRIMARY_IP},
{"remote_port", JSON_LONG, CURLINFO_PRIMARY_PORT},
{"local_ip", JSON_STRING, CURLINFO_LOCAL_IP},
{"local_port", JSON_LONG, CURLINFO_LOCAL_PORT},
{"http_version", JSON_VERSION, CURLINFO_HTTP_VERSION},
{"scheme", JSON_STRING, CURLINFO_SCHEME},
{NULL, JSON_NONE, CURLINFO_NONE}
};

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 json_escape(FILE *stream, const char *in)
{
const char *i = in;
const char *in_end = in + strlen(in);

for(; i < in_end; i++) {
switch(*i) {
case '\\':
fputs("\\\\", stream);
break;
case '\"':
fputs("\\\"", stream);
break;
case '\b':
fputs("\\b", stream);
break;
case '\f':
fputs("\\f", stream);
break;
case '\n':
fputs("\\n", stream);
break;
case '\r':
fputs("\\r", stream);
break;
case '\t':
fputs("\\t", stream);
break;
default:
if (*i < 32) {
fprintf(stream, "u%04x", *i);
}
else {
fputc(*i, stream);
}
break;
};
}
}

static int write_field_time(FILE *stream, CURL *curl, const char *key, CURLINFO cinfo)
{
long val = 0;
if(CURLE_OK == curl_easy_getinfo(curl, cinfo, &val)) {
long s = val / 1000000l;
long ms = val % 1000000l;
fprintf(stream, "\"%s\":%ld.%06ld,", key, s, ms);
return 1;
}
return 0;
}

static int write_field_string(FILE *stream, CURL *curl, const char *key, CURLINFO cinfo)
{
char *valp = NULL;
if((CURLE_OK == curl_easy_getinfo(curl, cinfo, &valp)) && valp) {
fprintf(stream, "\"%s\":\"", key);
json_escape(stream, valp);
fprintf(stream, "\"");
return 1;
}
return 0;
}

static int write_field_long(FILE *stream, CURL *curl, const char *key, CURLINFO cinfo)
{
long val = 0;
if(CURLE_OK == curl_easy_getinfo(curl, cinfo, &val)) {
fprintf(stream, "\"%s\":%ld", key, val);
return 1;
}
return 0;
}

static int write_field_filename(FILE *stream, const char* key, const char *filename)
{
if(filename) {
fprintf(stream, "\"%s\":\"", key);
json_escape(stream, filename);
fprintf(stream, "\"");
}
else {
fprintf(stream, "\"%s\":null", key);
}
return 1;
}

static int write_field_version(FILE *stream, CURL *curl, const char *key, CURLINFO cinfo)
{
long version = 0;
if(CURLE_OK == curl_easy_getinfo(curl, cinfo, &version) &&
(version >= 0) &&
(version < (long)(sizeof(http_version)/sizeof(char *)))) {
fprintf(stream, "\"%s\":\"%s\"", key, http_version[version]);
return 1;
}
return 0;
}

void ourWriteOutJSON(CURL *curl, struct OutStruct *outs, FILE *stream)
{
int i;

fputs("{", stream);
for(i = 0; mappings[i].key != NULL; i++) {
const char *key = mappings[i].key;
CURLINFO cinfo = mappings[i].cinfo;
int ok = 0;
switch(mappings[i].type) {
case JSON_STRING:
ok = write_field_string(stream, curl, key, cinfo);
break;
case JSON_LONG:
ok = write_field_long(stream, curl, key, cinfo);
break;
case JSON_TIME:
ok = write_field_time(stream, curl, key, cinfo);
break;
case JSON_FILENAME:
ok = write_field_filename(stream, outs->filename, key);
break;
case JSON_VERSION:
ok = write_field_version(stream, curl, key, cinfo);
break;
default:
break;
}

if(ok) {
fputs(",", stream);
}
}
fprintf(stream, "\"curl_version\":\"%s\"}", curl_version());
}
28 changes: 28 additions & 0 deletions src/tool_writeout_json.h
@@ -0,0 +1,28 @@
#ifndef HEADER_CURL_TOOL_WRITEOUT_JSON_H
#define HEADER_CURL_TOOL_WRITEOUT_JSON_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.haxx.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
***************************************************************************/
#include "tool_setup.h"

void ourWriteOutJSON(CURL *curl, struct OutStruct *outs, FILE *stream);

#endif /* HEADER_CURL_TOOL_WRITEOUT_H */

0 comments on commit ed2951f

Please sign in to comment.