[PATCH 4/4] uclient-fetch: Support of --method, --body-data and --body-file

Sergey Ponomarev stokito at gmail.com
Mon May 9 14:59:23 PDT 2022


The --method allows to execute PUT,DELETE,OPTIONS.

Usage sample:

$ uclient-fetch -O - -q --method=PUT \
--body-data="trololo" \
--header="Content-Type: text/plain" \
http://localhost:8080/cgi-bin/echo.sh

HTTP/1.1 200 OK
Content-Length: 7
Content-Type: text/html
REQUEST_METHOD: PUT
CONTENT_TYPE: text/plain
CONTENT_LENGTH:

trololo

To avoid clashes with the --post-data/file options the --body-data/file must be used.
But internally they stored to the same post_data and post_data fields. The GNU wget does similar.

GNU wget shows an error on mutual usage of the options and we can do this too:

if (method) {
// the method already was populated from getopt()
if (opt_post) {
fprintf(stderr, "--method expects data through --body-data/file\n");
exit(1);
}

But this is an additional code and space for a very exceptional case.
We may change this behaviour later.

Another incompatibility is about Content-Type header.
When --post-data is used then added a header Content-Type: application/x-www-form-urlencoded.
But users may call some API with POST and want to send some other e.g. Content-Type: application/json.
The GNU wget and BusyBox wget allows to override the CT with --header option i.e.

$ wget -O - -q \
--post-data="trololo" \
--header="Content-Type: text/plain" \
http://localhost:8080/cgi-bin/echo.sh

HTTP/1.1 200 OK
Content-Length: 7
Content-Type: text/html
REQUEST_METHOD: POST
CONTENT_TYPE: text/plain
CONTENT_LENGTH:

trololo

In GNU wget even when --method=POST and --body-data are used
but the --header Content-Type wasn't set then the default CT x-www-form-urlencoded is used.

The header overriding is currently not implemented and it may require more of changes.
Also users may want to also override other headers like Authorization and Host.
For me this seems like a dangerous practice.
If someone wants to send a POST request with a custom CT
then instead of --post-data and --header
they should use --method=POST --body-data and --header.
This will make the request explicit.

Signed-off-by: Sergey Ponomarev <stokito at gmail.com>
---
 uclient-fetch.c | 22 +++++++++++++++++++++-
 1 file changed, 21 insertions(+), 1 deletion(-)

diff --git a/uclient-fetch.c b/uclient-fetch.c
index ade40eb..f9f0174 100644
--- a/uclient-fetch.c
+++ b/uclient-fetch.c
@@ -490,6 +490,9 @@ static int usage(const char *progname)
 		"	--user-agent | -U <str>		Set HTTP user agent\n"
 		"	--post-data=STRING		use the POST method; send STRING as the data\n"
 		"	--post-file=FILE		use the POST method; send FILE as the data\n"
+		"	--method=METHOD		use the HTTP method e.g. PUT\n"
+		"	--body-data=STRING		with --method send the STRING in body\n"
+		"	--body-file=FILE		with --method send the FILE content in body\n"
 		"	--spider | -s			Spider mode - only check file existence\n"
 		"	--timeout=N | -T N		Set connect/request timeout to N seconds\n"
 		"	--proxy=on | -Y on		Enable interpretation of proxy env vars (default)\n"
@@ -551,6 +554,9 @@ enum {
 	L_HEADER,
 	L_POST_DATA,
 	L_POST_FILE,
+	L_METHOD,
+	L_BODY_DATA,
+	L_BODY_FILE,
 	L_SPIDER,
 	L_TIMEOUT,
 	L_CONTINUE,
@@ -569,6 +575,9 @@ static const struct option longopts[] = {
 	[L_HEADER] = { "header", required_argument, NULL, 0 },
 	[L_POST_DATA] = { "post-data", required_argument, NULL, 0 },
 	[L_POST_FILE] = { "post-file", required_argument, NULL, 0 },
+	[L_METHOD] = { "method", required_argument, NULL, 0 },
+	[L_BODY_DATA] = { "body-data", required_argument, NULL, 0 },
+	[L_BODY_FILE] = { "body-file", required_argument, NULL, 0 },
 	[L_SPIDER] = { "spider", no_argument, NULL, 0 },
 	[L_TIMEOUT] = { "timeout", required_argument, NULL, 0 },
 	[L_CONTINUE] = { "continue", no_argument, NULL, 0 },
@@ -653,6 +662,15 @@ int main(int argc, char **argv)
 				opt_post = true;
 				post_file = optarg;
 				break;
+			case L_METHOD:
+				method = optarg;
+				break;
+			case L_BODY_DATA:
+				post_data = optarg;
+				break;
+			case L_BODY_FILE:
+				post_file = optarg;
+				break;
 			case L_SPIDER:
 				no_output = true;
 				break;
@@ -716,7 +734,9 @@ int main(int argc, char **argv)
 		}
 	}
 
-	if (opt_post) {
+	if (method) {
+		// the method already was populated from getopt()
+	} else if (opt_post) {
 		method = "POST";
 	} else if (no_output) {
 		/* Note: GNU wget --spider sends a HEAD and if it failed repeats with a GET */
-- 
2.34.1




More information about the openwrt-devel mailing list