mirror of
https://github.com/curl/curl.git
synced 2025-03-31 16:00:35 +08:00
tool_getparam: initial --json support
Adds these test cases: 383 - simple single command line option 384 - reading it from stdin 385 - getting two --json options on command line 386 - --next works after --json Closes #8314
This commit is contained in:
parent
1ce1f0b5a0
commit
32160cae84
@ -5,7 +5,7 @@
|
||||
# | (__| |_| | _ <| |___
|
||||
# \___|\___/|_| \_\_____|
|
||||
#
|
||||
# Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
# Copyright (C) 1998 - 2022, 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
|
||||
@ -111,6 +111,7 @@ DPAGES = \
|
||||
interface.d \
|
||||
ipv4.d \
|
||||
ipv6.d \
|
||||
json.d \
|
||||
junk-session-cookies.d \
|
||||
keepalive-time.d \
|
||||
key-type.d \
|
||||
|
32
docs/cmdline-opts/json.d
Normal file
32
docs/cmdline-opts/json.d
Normal file
@ -0,0 +1,32 @@
|
||||
Long: json
|
||||
Arg: <data>
|
||||
Help: HTTP POST JSON
|
||||
Protocols: HTTP
|
||||
See-also: data-binary data-raw
|
||||
Mutexed: form head upload-file
|
||||
Category: http post upload
|
||||
Example: --json '{ "drink": "coffe" }' $URL
|
||||
Example: --json '{ "drink":' --json ' "coffe" }' $URL
|
||||
Example: --json @prepared $URL
|
||||
Example: --json @- $URL < json.txt
|
||||
Added: 7.82.0
|
||||
---
|
||||
Sends the specified JSON data in a POST request to the HTTP server. --json
|
||||
works as a shortcut for passing on these three options:
|
||||
|
||||
--data [arg]
|
||||
--header "Content-Type: application/json"
|
||||
--header "Accept: application/json"
|
||||
|
||||
There is **no verification** that the passed in data is actual JSON or that
|
||||
the syntax is correct.
|
||||
|
||||
If you start the data with the letter @, the rest should be a file name to
|
||||
read the data from, or a single dash (-) if you want curl to read the data
|
||||
from stdin. Posting data from a file named \&'foobar' would thus be done with
|
||||
--json @foobar and to instead read the data from stdin, use --json @-.
|
||||
|
||||
If this option is used more than once on the same command line, the additional
|
||||
data pieces will be concatenated to the previous before sending.
|
||||
|
||||
The headers this option sets can be overriden with --header as usual.
|
@ -99,6 +99,7 @@
|
||||
--interface 7.3
|
||||
--ipv4 (-4) 7.10.8
|
||||
--ipv6 (-6) 7.10.8
|
||||
--json 7.82.0
|
||||
--junk-session-cookies (-j) 7.9.7
|
||||
--keepalive-time 7.18.0
|
||||
--key 7.9.3
|
||||
|
@ -7,7 +7,7 @@
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
* Copyright (C) 1998 - 2022, 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
|
||||
@ -189,6 +189,7 @@ struct OperationConfig {
|
||||
bool proxydigest;
|
||||
bool proxybasic;
|
||||
bool proxyanyauth;
|
||||
bool jsoned; /* added json content-type */
|
||||
char *writeout; /* %-styled format string to output */
|
||||
struct curl_slist *quote;
|
||||
struct curl_slist *postquote;
|
||||
|
@ -230,6 +230,7 @@ static const struct LongShort aliases[]= {
|
||||
{"da", "data-ascii", ARG_STRING},
|
||||
{"db", "data-binary", ARG_STRING},
|
||||
{"de", "data-urlencode", ARG_STRING},
|
||||
{"df", "json", ARG_STRING},
|
||||
{"D", "dump-header", ARG_FILENAME},
|
||||
{"e", "referer", ARG_STRING},
|
||||
{"E", "cert", ARG_FILENAME},
|
||||
@ -1386,7 +1387,7 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */
|
||||
size_t size = 0;
|
||||
bool raw_mode = (subletter == 'r');
|
||||
|
||||
if(subletter == 'e') { /* --data-urlencode*/
|
||||
if(subletter == 'e') { /* --data-urlencode */
|
||||
/* [name]=[content], we encode the content part only
|
||||
* [name]@[file name]
|
||||
*
|
||||
@ -1489,7 +1490,8 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */
|
||||
"an empty POST.\n", nextarg);
|
||||
}
|
||||
|
||||
if(subletter == 'b')
|
||||
if((subletter == 'b') || /* --data-binary */
|
||||
(subletter == 'f') /* --json */)
|
||||
/* forced binary */
|
||||
err = file2memory(&postdata, &size, file);
|
||||
else {
|
||||
@ -1516,6 +1518,8 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */
|
||||
if(postdata)
|
||||
size = strlen(postdata);
|
||||
}
|
||||
if(subletter == 'f')
|
||||
config->jsoned = TRUE;
|
||||
|
||||
#ifdef CURL_DOES_CONVERSIONS
|
||||
if(subletter != 'b') {
|
||||
@ -1540,13 +1544,21 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */
|
||||
return PARAM_NO_MEM;
|
||||
}
|
||||
memcpy(config->postfields, oldpost, (size_t)oldlen);
|
||||
/* use byte value 0x26 for '&' to accommodate non-ASCII platforms */
|
||||
config->postfields[oldlen] = '\x26';
|
||||
memcpy(&config->postfields[oldlen + 1], postdata, size);
|
||||
config->postfields[oldlen + 1 + size] = '\0';
|
||||
if(subletter != 'f') {
|
||||
/* skip this treatment for --json */
|
||||
/* use byte value 0x26 for '&' to accommodate non-ASCII platforms */
|
||||
config->postfields[oldlen] = '\x26';
|
||||
memcpy(&config->postfields[oldlen + 1], postdata, size);
|
||||
config->postfields[oldlen + 1 + size] = '\0';
|
||||
config->postfieldsize += size + 1;
|
||||
}
|
||||
else {
|
||||
memcpy(&config->postfields[oldlen], postdata, size);
|
||||
config->postfields[oldlen + size] = '\0';
|
||||
config->postfieldsize += size;
|
||||
}
|
||||
Curl_safefree(oldpost);
|
||||
Curl_safefree(postdata);
|
||||
config->postfieldsize += size + 1;
|
||||
}
|
||||
else {
|
||||
config->postfields = postdata;
|
||||
@ -2367,6 +2379,7 @@ ParameterError parse_args(struct GlobalConfig *global, int argc,
|
||||
: NULL;
|
||||
|
||||
result = getparameter(orig_opt, nextarg, &passarg, global, config);
|
||||
|
||||
curlx_unicodefree(nextarg);
|
||||
config = global->last;
|
||||
if(result == PARAM_NEXT_OPERATION) {
|
||||
|
@ -5,7 +5,7 @@
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel.se>, et al.
|
||||
* Copyright (C) 1998 - 2022, Daniel Stenberg, <daniel.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
|
||||
@ -298,6 +298,9 @@ const struct helptxt helptext[] = {
|
||||
{"-6, --ipv6",
|
||||
"Resolve names to IPv6 addresses",
|
||||
CURLHELP_CONNECTION | CURLHELP_DNS},
|
||||
{" --json <data>",
|
||||
"HTTP POST JSON",
|
||||
CURLHELP_HTTP | CURLHELP_POST | CURLHELP_UPLOAD},
|
||||
{"-j, --junk-session-cookies",
|
||||
"Ignore session cookies read from file",
|
||||
CURLHELP_HTTP},
|
||||
|
@ -5,7 +5,7 @@
|
||||
* | (__| |_| | _ <| |___
|
||||
* \___|\___/|_| \_\_____|
|
||||
*
|
||||
* Copyright (C) 1998 - 2021, Daniel Stenberg, <daniel@haxx.se>, et al.
|
||||
* Copyright (C) 1998 - 2022, 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
|
||||
@ -548,11 +548,45 @@ static char *my_useragent(void)
|
||||
return strdup(CURL_NAME "/" CURL_VERSION);
|
||||
}
|
||||
|
||||
#define isheadersep(x) ((((x)==':') || ((x)==';')))
|
||||
|
||||
/*
|
||||
* inlist() returns true if the given 'checkfor' header is present in the
|
||||
* header list.
|
||||
*/
|
||||
static bool inlist(const struct curl_slist *head,
|
||||
const char *checkfor)
|
||||
{
|
||||
size_t thislen = strlen(checkfor);
|
||||
DEBUGASSERT(thislen);
|
||||
DEBUGASSERT(checkfor[thislen-1] != ':');
|
||||
|
||||
for(; head; head = head->next) {
|
||||
if(curl_strnequal(head->data, checkfor, thislen) &&
|
||||
isheadersep(head->data[thislen]) )
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
CURLcode get_args(struct OperationConfig *config, const size_t i)
|
||||
{
|
||||
CURLcode result = CURLE_OK;
|
||||
bool last = (config->next ? FALSE : TRUE);
|
||||
|
||||
if(config->jsoned) {
|
||||
ParameterError err = PARAM_OK;
|
||||
/* --json also implies json Content-Type: and Accept: headers - if
|
||||
they are not set with -H */
|
||||
if(!inlist(config->headers, "Content-Type"))
|
||||
err = add2list(&config->headers, "Content-Type: application/json");
|
||||
if(!err && !inlist(config->headers, "Accept"))
|
||||
err = add2list(&config->headers, "Accept: application/json");
|
||||
if(err)
|
||||
return CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
/* Check we have a password for the given host user */
|
||||
if(config->userpwd && !config->oauth_bearer) {
|
||||
result = checkpasswd("host", i, last, &config->userpwd);
|
||||
|
@ -63,7 +63,8 @@ test352 test353 test354 test355 test356 test357 test358 test359 test360 \
|
||||
test361 test362 test363 test364 test365 test366 test367 test368 test369 \
|
||||
test370 test371 test372 test373 test374 \
|
||||
\
|
||||
test380 test381 \
|
||||
test380 test381 test383 test384 test385 test386 \
|
||||
\
|
||||
test392 test393 test394 test395 test396 test397 \
|
||||
\
|
||||
test400 test401 test402 test403 test404 test405 test406 test407 test408 \
|
||||
|
56
tests/data/test383
Normal file
56
tests/data/test383
Normal file
@ -0,0 +1,56 @@
|
||||
<testcase>
|
||||
<info>
|
||||
<keywords>
|
||||
HTTP
|
||||
HTTP POST
|
||||
--json
|
||||
</keywords>
|
||||
</info>
|
||||
#
|
||||
# Server-side
|
||||
<reply>
|
||||
<data>
|
||||
HTTP/1.1 200 OK
|
||||
Date: Tue, 09 Nov 2010 14:49:00 GMT
|
||||
Server: test-server/fake
|
||||
Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT
|
||||
ETag: "21025-dc7-39462498"
|
||||
Accept-Ranges: bytes
|
||||
Content-Length: 6
|
||||
Connection: close
|
||||
Content-Type: text/html
|
||||
Funny-head: yesyes
|
||||
|
||||
-foo-
|
||||
</data>
|
||||
</reply>
|
||||
|
||||
#
|
||||
# Client-side
|
||||
<client>
|
||||
<server>
|
||||
http
|
||||
</server>
|
||||
<name>
|
||||
HTTP with --json
|
||||
</name>
|
||||
<command>
|
||||
--json '{ "drink": "coffe" }' http://%HOSTIP:%HTTPPORT/%TESTNUMBER
|
||||
</command>
|
||||
</client>
|
||||
|
||||
#
|
||||
# Verify data after the test has been "shot"
|
||||
<verify>
|
||||
<protocol nonewline="yes">
|
||||
POST /%TESTNUMBER HTTP/1.1
|
||||
Host: %HOSTIP:%HTTPPORT
|
||||
User-Agent: curl/%VERSION
|
||||
Content-Type: application/json
|
||||
Accept: application/json
|
||||
Content-Length: 20
|
||||
|
||||
{ "drink": "coffe" }
|
||||
</protocol>
|
||||
</verify>
|
||||
</testcase>
|
59
tests/data/test384
Normal file
59
tests/data/test384
Normal file
@ -0,0 +1,59 @@
|
||||
<testcase>
|
||||
<info>
|
||||
<keywords>
|
||||
HTTP
|
||||
HTTP POST
|
||||
--json
|
||||
</keywords>
|
||||
</info>
|
||||
#
|
||||
# Server-side
|
||||
<reply>
|
||||
<data>
|
||||
HTTP/1.1 200 OK
|
||||
Date: Tue, 09 Nov 2010 14:49:00 GMT
|
||||
Server: test-server/fake
|
||||
Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT
|
||||
ETag: "21025-dc7-39462498"
|
||||
Accept-Ranges: bytes
|
||||
Content-Length: 6
|
||||
Connection: close
|
||||
Content-Type: text/html
|
||||
Funny-head: yesyes
|
||||
|
||||
-foo-
|
||||
</data>
|
||||
</reply>
|
||||
|
||||
#
|
||||
# Client-side
|
||||
<client>
|
||||
<server>
|
||||
http
|
||||
</server>
|
||||
<name>
|
||||
HTTP with --json from stdin
|
||||
</name>
|
||||
<stdin>
|
||||
{ "drink": "coffe" }
|
||||
</stdin>
|
||||
<command>
|
||||
--json @- http://%HOSTIP:%HTTPPORT/%TESTNUMBER -H "Accept: foobar/*"
|
||||
</command>
|
||||
</client>
|
||||
|
||||
#
|
||||
# Verify data after the test has been "shot"
|
||||
<verify>
|
||||
<protocol>
|
||||
POST /%TESTNUMBER HTTP/1.1
|
||||
Host: %HOSTIP:%HTTPPORT
|
||||
User-Agent: curl/%VERSION
|
||||
Accept: foobar/*
|
||||
Content-Type: application/json
|
||||
Content-Length: 21
|
||||
|
||||
{ "drink": "coffe" }
|
||||
</protocol>
|
||||
</verify>
|
||||
</testcase>
|
56
tests/data/test385
Normal file
56
tests/data/test385
Normal file
@ -0,0 +1,56 @@
|
||||
<testcase>
|
||||
<info>
|
||||
<keywords>
|
||||
HTTP
|
||||
HTTP POST
|
||||
--json
|
||||
</keywords>
|
||||
</info>
|
||||
#
|
||||
# Server-side
|
||||
<reply>
|
||||
<data>
|
||||
HTTP/1.1 200 OK
|
||||
Date: Tue, 09 Nov 2010 14:49:00 GMT
|
||||
Server: test-server/fake
|
||||
Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT
|
||||
ETag: "21025-dc7-39462498"
|
||||
Accept-Ranges: bytes
|
||||
Content-Length: 6
|
||||
Connection: close
|
||||
Content-Type: text/html
|
||||
Funny-head: yesyes
|
||||
|
||||
-foo-
|
||||
</data>
|
||||
</reply>
|
||||
|
||||
#
|
||||
# Client-side
|
||||
<client>
|
||||
<server>
|
||||
http
|
||||
</server>
|
||||
<name>
|
||||
HTTP with --json x 2
|
||||
</name>
|
||||
<command>
|
||||
--json '{ "drink": "coffe",' --json ' "crunch": "cookie" }' http://%HOSTIP:%HTTPPORT/%TESTNUMBER -H "Content-Type: drinks/hot"
|
||||
</command>
|
||||
</client>
|
||||
|
||||
#
|
||||
# Verify data after the test has been "shot"
|
||||
<verify>
|
||||
<protocol nonewline="yes">
|
||||
POST /%TESTNUMBER HTTP/1.1
|
||||
Host: %HOSTIP:%HTTPPORT
|
||||
User-Agent: curl/%VERSION
|
||||
Content-Type: drinks/hot
|
||||
Accept: application/json
|
||||
Content-Length: 40
|
||||
|
||||
{ "drink": "coffe", "crunch": "cookie" }
|
||||
</protocol>
|
||||
</verify>
|
||||
</testcase>
|
74
tests/data/test386
Normal file
74
tests/data/test386
Normal file
@ -0,0 +1,74 @@
|
||||
<testcase>
|
||||
<info>
|
||||
<keywords>
|
||||
HTTP
|
||||
HTTP POST
|
||||
--json
|
||||
</keywords>
|
||||
</info>
|
||||
#
|
||||
# Server-side
|
||||
<reply>
|
||||
<data>
|
||||
HTTP/1.1 200 OK
|
||||
Date: Tue, 09 Nov 2010 14:49:00 GMT
|
||||
Server: test-server/fake
|
||||
Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT
|
||||
ETag: "21025-dc7-39462498"
|
||||
Accept-Ranges: bytes
|
||||
Content-Length: 6
|
||||
Connection: close
|
||||
Content-Type: text/html
|
||||
Funny-head: yesyes
|
||||
|
||||
-foo-
|
||||
</data>
|
||||
<data2>
|
||||
HTTP/1.1 200 OK
|
||||
Date: Tue, 09 Nov 2010 14:49:00 GMT
|
||||
Server: test-server/fake
|
||||
Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT
|
||||
ETag: "21025-dc7-39462498"
|
||||
Accept-Ranges: bytes
|
||||
Content-Length: 6
|
||||
Connection: close
|
||||
Content-Type: text/html
|
||||
Funny-head: yesyes
|
||||
|
||||
hello
|
||||
</data>
|
||||
</reply>
|
||||
|
||||
#
|
||||
# Client-side
|
||||
<client>
|
||||
<server>
|
||||
http
|
||||
</server>
|
||||
<name>
|
||||
HTTP with --json + --next
|
||||
</name>
|
||||
<command>
|
||||
--json '{ "drink": "coffe" }' http://%HOSTIP:%HTTPPORT/%TESTNUMBER --next http://%HOSTIP:%HTTPPORT/%TESTNUMBER0002
|
||||
</command>
|
||||
</client>
|
||||
|
||||
#
|
||||
# Verify data after the test has been "shot"
|
||||
<verify>
|
||||
<protocol>
|
||||
POST /%TESTNUMBER HTTP/1.1
|
||||
Host: %HOSTIP:%HTTPPORT
|
||||
User-Agent: curl/%VERSION
|
||||
Content-Type: application/json
|
||||
Accept: application/json
|
||||
Content-Length: 20
|
||||
|
||||
{ "drink": "coffe" }GET /%TESTNUMBER0002 HTTP/1.1
|
||||
Host: %HOSTIP:%HTTPPORT
|
||||
User-Agent: curl/%VERSION
|
||||
Accept: */*
|
||||
|
||||
</protocol>
|
||||
</verify>
|
||||
</testcase>
|
Loading…
x
Reference in New Issue
Block a user