1
0
mirror of https://github.com/moparisthebest/curl synced 2025-03-11 07:39:50 -04:00

http: add --strip-path-slash and CURLOPT_STRIP_PATH_SLASH

... to enable sending "OPTIONS *" which wasn't possible previously.

This option currently only works for HTTP.

Added test cases 1298 + 1299 to verify

Fixes #1280
Closes #1462
This commit is contained in:
Daniel Stenberg 2017-06-19 14:10:33 +02:00
parent 176ec51382
commit b778ae4c5e
21 changed files with 220 additions and 25 deletions

View File

@ -69,7 +69,6 @@
5.9 Improve formpost API 5.9 Improve formpost API
5.10 Leave secure cookies alone 5.10 Leave secure cookies alone
5.11 Chunked transfer multipart formpost 5.11 Chunked transfer multipart formpost
5.12 OPTIONS *
6. TELNET 6. TELNET
6.1 ditch stdin 6.1 ditch stdin
@ -558,23 +557,6 @@ This is not detailed in any FTP specification.
https://github.com/curl/curl/issues/1139 https://github.com/curl/curl/issues/1139
5.12 OPTIONS *
HTTP defines an OPTIONS method that can be sent with an asterisk option like
"OPTIONS *" to ask about options from the server and not a specific URL
resource. https://tools.ietf.org/html/rfc7230#section-5.3.4
libcurl as it currently works will always sent HTTP methods with a path that
starts with a slash so there's no way for an application to send a proper
"OPTIONS *" using libcurl. This should be fixed.
I can't think of any other non-slash paths we should support so it will
probably make sense to add a new boolean option for issuign an "OPTIONS *"
request. CURLOPT_OPTIONSASTERISK perhaps (and a corresponding command line
option)?
See https://github.com/curl/curl/issues/1280
6. TELNET 6. TELNET

View File

@ -42,6 +42,6 @@ DPAGES = abstract-unix-socket.d anyauth.d append.d basic.d cacert.d capath.d cer
tlsauthtype.d tlspassword.d tlsuser.d tlsv1.0.d tlsv1.1.d tlsv1.2.d \ tlsauthtype.d tlspassword.d tlsuser.d tlsv1.0.d tlsv1.1.d tlsv1.2.d \
tlsv1.3.d tlsv1.d trace-ascii.d trace.d trace-time.d tr-encoding.d \ tlsv1.3.d tlsv1.d trace-ascii.d trace.d trace-time.d tr-encoding.d \
unix-socket.d upload-file.d url.d use-ascii.d user-agent.d user.d \ unix-socket.d upload-file.d url.d use-ascii.d user-agent.d user.d \
verbose.d version.d write-out.d xattr.d verbose.d version.d write-out.d xattr.d strip-path-slash.d
OTHERPAGES = page-footer page-header OTHERPAGES = page-footer page-header

View File

@ -0,0 +1,7 @@
Long: strip-path-slash
Help: Strip off the first slash of the path
Protocols: HTTP
---
Tells curl to strip the leading slash from the path when it sends the path to
the server. Useful when wanting to issue HTTP requests without leading slash,
like "OPTIONS *".

View File

@ -5,7 +5,7 @@
.\" * | (__| |_| | _ <| |___ .\" * | (__| |_| | _ <| |___
.\" * \___|\___/|_| \_\_____| .\" * \___|\___/|_| \_\_____|
.\" * .\" *
.\" * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al. .\" * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
.\" * .\" *
.\" * This software is licensed as described in the file COPYING, which .\" * This software is licensed as described in the file COPYING, which
.\" * you should have received as part of this distribution. The terms .\" * you should have received as part of this distribution. The terms
@ -303,6 +303,8 @@ Start a new cookie session. See \fICURLOPT_COOKIESESSION(3)\fP
Add or control cookies. See \fICURLOPT_COOKIELIST(3)\fP Add or control cookies. See \fICURLOPT_COOKIELIST(3)\fP
.IP CURLOPT_HTTPGET .IP CURLOPT_HTTPGET
Do a HTTP GET request. See \fICURLOPT_HTTPGET(3)\fP Do a HTTP GET request. See \fICURLOPT_HTTPGET(3)\fP
.IP CURLOPT_STRIP_PATH_SLASH
Cut off the leading slash from the path. \fICURLOPT_STRIP_PATH_SLASH(3)\fP
.IP CURLOPT_HTTP_VERSION .IP CURLOPT_HTTP_VERSION
HTTP version to use. \fICURLOPT_HTTP_VERSION(3)\fP HTTP version to use. \fICURLOPT_HTTP_VERSION(3)\fP
.IP CURLOPT_IGNORE_CONTENT_LENGTH .IP CURLOPT_IGNORE_CONTENT_LENGTH

View File

@ -108,3 +108,4 @@ Returns CURLE_OK if the option is supported, CURLE_UNKNOWN_OPTION if not, or
CURLE_OUT_OF_MEMORY if there was insufficient heap space. CURLE_OUT_OF_MEMORY if there was insufficient heap space.
.SH "SEE ALSO" .SH "SEE ALSO"
.BR CURLOPT_HTTPHEADER "(3), " CURLOPT_NOBODY "(3), " .BR CURLOPT_HTTPHEADER "(3), " CURLOPT_NOBODY "(3), "
.BR CURLOPT_STRIP_PATH_SLASH "(3), "

View File

@ -5,7 +5,7 @@
.\" * | (__| |_| | _ <| |___ .\" * | (__| |_| | _ <| |___
.\" * \___|\___/|_| \_\_____| .\" * \___|\___/|_| \_\_____|
.\" * .\" *
.\" * Copyright (C) 1998 - 2014, Daniel Stenberg, <daniel@haxx.se>, et al. .\" * Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
.\" * .\" *
.\" * This software is licensed as described in the file COPYING, which .\" * This software is licensed as described in the file COPYING, which
.\" * you should have received as part of this distribution. The terms .\" * you should have received as part of this distribution. The terms
@ -44,10 +44,10 @@ curl = curl_easy_init();
if(curl) { if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, "http://example.com"); curl_easy_setopt(curl, CURLOPT_URL, "http://example.com");
/* get us the resource without a body! */ /* get us the resource without a body! */
curl_easy_setopt(curl, CURLOPT_NOBODY, 1L); curl_easy_setopt(curl, CURLOPT_NOBODY, 1L);
/* Perform the request */ /* Perform the request */
curl_easy_perform(curl); curl_easy_perform(curl);
} }
.fi .fi
@ -57,3 +57,4 @@ Always
Returns CURLE_OK Returns CURLE_OK
.SH "SEE ALSO" .SH "SEE ALSO"
.BR CURLOPT_HTTPGET "(3), " CURLOPT_POST "(3), " .BR CURLOPT_HTTPGET "(3), " CURLOPT_POST "(3), "
.BR CURLOPT_STRIP_PATH_SLASH "(3), "

View File

@ -0,0 +1,56 @@
.\" **************************************************************************
.\" * _ _ ____ _
.\" * Project ___| | | | _ \| |
.\" * / __| | | | |_) | |
.\" * | (__| |_| | _ <| |___
.\" * \___|\___/|_| \_\_____|
.\" *
.\" * Copyright (C) 1998 - 2017, 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.
.\" *
.\" **************************************************************************
.\"
.TH CURLOPT_STRIP_PATH_SLASH 3 "17 Jun 2014" "libcurl 7.37.0" "curl_easy_setopt options"
.SH NAME
CURLOPT_STRIP_PATH_SLASH \- strip the leading slash from the path
.SH SYNOPSIS
#include <curl/curl.h>
CURLcode curl_easy_setopt(CURL *handle, CURLOPT_STRIP_PATH_SLASH, value);
.SH DESCRIPTION
Pass a long set to 1 to tell libcurl to strip out the leading slash from the
path when used on the server.
.SH DEFAULT
0 - use the leading slash.
.SH PROTOCOLS
HTTP
.SH EXAMPLE
.nf
curl = curl_easy_init();
if(curl) {
curl_easy_setopt(curl, CURLOPT_URL, "http://example.com/*");
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "OPTIONS");
/* issue an OPTIONS * request (no leading slash) */
curl_easy_setopt(curl, CURLOPT_STRIP_PATH_SLASH, 1L);
/* Perform the request */
curl_easy_perform(curl);
}
.fi
.SH AVAILABILITY
Added in 7.55.0
.SH RETURN VALUE
Returns CURLE_OK if the option is supported, and CURLE_UNKNOWN_OPTION if not.
.SH "SEE ALSO"
.BR CURLOPT_CUSTOMREQUEST "(3), " CURLOPT_HTTPGET "(3), "

View File

@ -278,6 +278,7 @@ man_MANS = \
CURLOPT_STREAM_DEPENDS.3 \ CURLOPT_STREAM_DEPENDS.3 \
CURLOPT_STREAM_DEPENDS_E.3 \ CURLOPT_STREAM_DEPENDS_E.3 \
CURLOPT_STREAM_WEIGHT.3 \ CURLOPT_STREAM_WEIGHT.3 \
CURLOPT_STRIP_PATH_SLASH.3 \
CURLOPT_SUPPRESS_CONNECT_HEADERS.3 \ CURLOPT_SUPPRESS_CONNECT_HEADERS.3 \
CURLOPT_TCP_FASTOPEN.3 \ CURLOPT_TCP_FASTOPEN.3 \
CURLOPT_TCP_KEEPALIVE.3 \ CURLOPT_TCP_KEEPALIVE.3 \

View File

@ -409,6 +409,7 @@ CURLOPT_HTTPPOST 7.1
CURLOPT_HTTPPROXYTUNNEL 7.3 CURLOPT_HTTPPROXYTUNNEL 7.3
CURLOPT_HTTPREQUEST 7.1 - 7.15.5 CURLOPT_HTTPREQUEST 7.1 - 7.15.5
CURLOPT_HTTP_CONTENT_DECODING 7.16.2 CURLOPT_HTTP_CONTENT_DECODING 7.16.2
CURLOPT_STRIP_PATH_SLASH 7.55.0
CURLOPT_HTTP_TRANSFER_DECODING 7.16.2 CURLOPT_HTTP_TRANSFER_DECODING 7.16.2
CURLOPT_HTTP_VERSION 7.9.1 CURLOPT_HTTP_VERSION 7.9.1
CURLOPT_IGNORE_CONTENT_LENGTH 7.14.1 CURLOPT_IGNORE_CONTENT_LENGTH 7.14.1

View File

@ -1780,6 +1780,9 @@ typedef enum {
/* Suppress proxy CONNECT response headers from user callbacks */ /* Suppress proxy CONNECT response headers from user callbacks */
CINIT(SUPPRESS_CONNECT_HEADERS, LONG, 265), CINIT(SUPPRESS_CONNECT_HEADERS, LONG, 265),
/* Strip the initial slash from the path taken from the URL */
CINIT(STRIP_PATH_SLASH, LONG, 266),
CURLOPT_LASTENTRY /* the last unused */ CURLOPT_LASTENTRY /* the last unused */
} CURLoption; } CURLoption;

View File

@ -1851,6 +1851,9 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
case HTTPREQ_PUT: case HTTPREQ_PUT:
request = "PUT"; request = "PUT";
break; break;
case HTTPREQ_OPTIONS:
request = "OPTIONS";
break;
default: /* this should never happen */ default: /* this should never happen */
case HTTPREQ_GET: case HTTPREQ_GET:
request = "GET"; request = "GET";
@ -2266,6 +2269,9 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
if(result) if(result)
return result; return result;
if(data->set.strip_path_slash)
ppath++;
/* url */ /* url */
if(paste_ftp_userpwd) if(paste_ftp_userpwd)
result = Curl_add_bufferf(req_buffer, "ftp://%s:%s@%s", result = Curl_add_bufferf(req_buffer, "ftp://%s:%s@%s",

View File

@ -829,6 +829,10 @@ CURLcode Curl_setopt(struct Curl_easy *data, CURLoption option,
then this can be changed to HEAD later on) */ then this can be changed to HEAD later on) */
data->set.httpreq = HTTPREQ_GET; data->set.httpreq = HTTPREQ_GET;
break; break;
case CURLOPT_STRIP_PATH_SLASH:
arg = va_arg(param, long);
data->set.strip_path_slash = (bool)arg;
break;
case CURLOPT_FILETIME: case CURLOPT_FILETIME:
/* /*
* Try to get the file time of the remote document. The time will * Try to get the file time of the remote document. The time will

View File

@ -1264,6 +1264,7 @@ typedef enum {
HTTPREQ_POST_FORM, /* we make a difference internally */ HTTPREQ_POST_FORM, /* we make a difference internally */
HTTPREQ_PUT, HTTPREQ_PUT,
HTTPREQ_HEAD, HTTPREQ_HEAD,
HTTPREQ_OPTIONS,
HTTPREQ_CUSTOM, HTTPREQ_CUSTOM,
HTTPREQ_LAST /* last in list */ HTTPREQ_LAST /* last in list */
} Curl_HttpReq; } Curl_HttpReq;
@ -1698,6 +1699,7 @@ struct UserDefined {
Curl_HttpReq httpreq; /* what kind of HTTP request (if any) is this */ Curl_HttpReq httpreq; /* what kind of HTTP request (if any) is this */
long httpversion; /* when non-zero, a specific HTTP version requested to long httpversion; /* when non-zero, a specific HTTP version requested to
be used in the library's request(s) */ be used in the library's request(s) */
bool strip_path_slash; /* strip off initial slash from path */
struct ssl_config_data ssl; /* user defined SSL stuff */ struct ssl_config_data ssl; /* user defined SSL stuff */
struct ssl_config_data proxy_ssl; /* user defined SSL stuff for proxy */ struct ssl_config_data proxy_ssl; /* user defined SSL stuff for proxy */
struct ssl_general_config general_ssl; /* general user defined SSL stuff */ struct ssl_general_config general_ssl; /* general user defined SSL stuff */

View File

@ -144,6 +144,7 @@ struct OperationConfig {
bool readbusy; /* set when reading input returns EAGAIN */ bool readbusy; /* set when reading input returns EAGAIN */
bool globoff; bool globoff;
bool use_httpget; bool use_httpget;
bool strip_path_slash;
bool insecure_ok; /* set TRUE to allow insecure SSL connects */ bool insecure_ok; /* set TRUE to allow insecure SSL connects */
bool proxy_insecure_ok; /* set TRUE to allow insecure SSL connects bool proxy_insecure_ok; /* set TRUE to allow insecure SSL connects
for proxy */ for proxy */

View File

@ -257,6 +257,7 @@ static const struct LongShort aliases[]= {
{"Fs", "form-string", ARG_STRING}, {"Fs", "form-string", ARG_STRING},
{"g", "globoff", ARG_BOOL}, {"g", "globoff", ARG_BOOL},
{"G", "get", ARG_NONE}, {"G", "get", ARG_NONE},
{"Ga", "strip-path-slash", ARG_BOOL},
{"h", "help", ARG_BOOL}, {"h", "help", ARG_BOOL},
{"H", "header", ARG_STRING}, {"H", "header", ARG_STRING},
{"Hp", "proxy-header", ARG_STRING}, {"Hp", "proxy-header", ARG_STRING},
@ -1591,7 +1592,11 @@ ParameterError getparameter(const char *flag, /* f or -long-flag */
break; break;
case 'G': /* HTTP GET */ case 'G': /* HTTP GET */
config->use_httpget = TRUE; if(subletter == 'a') { /* --strip-path-slash */
config->strip_path_slash = TRUE;
}
else
config->use_httpget = TRUE;
break; break;
case 'h': /* h for help */ case 'h': /* h for help */

View File

@ -400,6 +400,8 @@ static const struct helptxt helptext[] = {
"Use SSLv3"}, "Use SSLv3"},
{" --stderr", {" --stderr",
"Where to redirect stderr"}, "Where to redirect stderr"},
{" --strip-path-slash",
"Strip off the first slash of the path"},
{" --suppress-connect-headers", {" --suppress-connect-headers",
"Suppress proxy CONNECT response headers"}, "Suppress proxy CONNECT response headers"},
{" --tcp-fastopen", {" --tcp-fastopen",

View File

@ -972,6 +972,8 @@ static CURLcode operate_do(struct GlobalConfig *global,
#endif /* !CURL_DISABLE_PROXY */ #endif /* !CURL_DISABLE_PROXY */
my_setopt(curl, CURLOPT_FAILONERROR, config->failonerror?1L:0L); my_setopt(curl, CURLOPT_FAILONERROR, config->failonerror?1L:0L);
my_setopt(curl, CURLOPT_STRIP_PATH_SLASH,
config->strip_path_slash?1L:0L);
my_setopt(curl, CURLOPT_UPLOAD, uploadfile?1L:0L); my_setopt(curl, CURLOPT_UPLOAD, uploadfile?1L:0L);
my_setopt(curl, CURLOPT_DIRLISTONLY, config->dirlistonly?1L:0L); my_setopt(curl, CURLOPT_DIRLISTONLY, config->dirlistonly?1L:0L);
my_setopt(curl, CURLOPT_APPEND, config->ftp_append?1L:0L); my_setopt(curl, CURLOPT_APPEND, config->ftp_append?1L:0L);

View File

@ -133,7 +133,7 @@ test1260 test1261 test1262 \
\ \
test1280 test1281 test1282 test1283 test1284 test1285 test1286 test1287 \ test1280 test1281 test1282 test1283 test1284 test1285 test1286 test1287 \
test1288 \ test1288 \
\ test1298 test1299 \
test1300 test1301 test1302 test1303 test1304 test1305 test1306 test1307 \ test1300 test1301 test1302 test1303 test1304 test1305 test1306 test1307 \
test1308 test1309 test1310 test1311 test1312 test1313 test1314 test1315 \ test1308 test1309 test1310 test1311 test1312 test1313 test1314 test1315 \
test1316 test1317 test1318 test1319 test1320 test1321 test1322 \ test1316 test1317 test1318 test1319 test1320 test1321 test1322 \

56
tests/data/test1298 Normal file
View File

@ -0,0 +1,56 @@
<testcase>
<info>
<keywords>
HTTP
HTTP GET
--strip-path-slash
</keywords>
</info>
#
# Server-side
<reply>
<data>
HTTP/1.1 200 OK
Date: Thu, 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: 0
Connection: close
Content-Type: text/html
Funny-head: yesyes
</data>
</reply>
#
# Client-side
<client>
<server>
http
</server>
<name>
HTTP GET special path with --strip-path-slash
</name>
<command>
--strip-path-slash "http://%HOSTIP:%HTTPPORT/XXX" -H "Testno: 1298"
</command>
</client>
#
# Verify data after the test has been "shot"
<verify>
<strip>
^User-Agent:.*
</strip>
<protocol>
GET XXX HTTP/1.1
Host: %HOSTIP:%HTTPPORT
Accept: */*
Testno: 1298
</protocol>
</verify>
</testcase>

55
tests/data/test1299 Normal file
View File

@ -0,0 +1,55 @@
<testcase>
<info>
<keywords>
HTTP
--strip-path-slash
</keywords>
</info>
#
# Server-side
<reply>
<data>
HTTP/1.1 200 OK
Date: Thu, 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: 0
Connection: close
Content-Type: text/html
Funny-head: yesyes
</data>
</reply>
#
# Client-side
<client>
<server>
http
</server>
<name>
Send "OPTIONS *" with --strip-path-slash
</name>
<command>
--strip-path-slash -X OPTIONS http://%HOSTIP:%HTTPPORT/* -H "Testno: 1299"
</command>
</client>
#
# Verify data after the test has been "shot"
<verify>
<strip>
^User-Agent:.*
</strip>
<protocol>
OPTIONS * HTTP/1.1
Host: %HOSTIP:%HTTPPORT
Accept: */*
Testno: 1299
</protocol>
</verify>
</testcase>

View File

@ -601,6 +601,14 @@ static int ProcessRequest(struct httprequest *req)
} }
} }
if(req->testno == DOCNUMBER_NOTHING) {
/* check for a Testno: header with the test case number */
char *testno = strstr(line, "\nTestno: ");
if(testno) {
req->testno = strtol(&testno[9], NULL, 10);
logmsg("Found test number %d in Testno: header!", req->testno);
}
}
if(req->testno == DOCNUMBER_NOTHING) { if(req->testno == DOCNUMBER_NOTHING) {
/* Still no test case number. Try to get the the number off the last dot /* Still no test case number. Try to get the the number off the last dot
instead, IE we consider the TLD to be the test number. Test 123 can instead, IE we consider the TLD to be the test number. Test 123 can