From 5c4fe0d8264a4a4f591d79cefe9676b099bdecf2 Mon Sep 17 00:00:00 2001 From: Jim Fuller Date: Thu, 1 Nov 2018 19:16:15 +0100 Subject: [PATCH] setopt: add CURLOPT_CURLU Allows an application to pass in a pre-parsed URL via a URL handle. Closes #3227 --- docs/examples/Makefile.inc | 3 +- docs/examples/urlapi.c | 72 +++++++++++++++++++++++++++++ docs/libcurl/curl_easy_setopt.3 | 2 + docs/libcurl/opts/CURLOPT_CURLU.3 | 61 +++++++++++++++++++++++++ docs/libcurl/opts/Makefile.inc | 1 + docs/libcurl/symbols-in-versions | 1 + include/curl/curl.h | 3 ++ include/curl/typecheck-gcc.h | 1 + lib/setopt.c | 6 +++ lib/transfer.c | 15 +++++- lib/url.c | 19 ++++++-- lib/urldata.h | 1 + tests/data/Makefile.inc | 2 +- tests/data/test658 | 50 ++++++++++++++++++++ tests/libtest/Makefile.inc | 6 ++- tests/libtest/lib658.c | 76 +++++++++++++++++++++++++++++++ 16 files changed, 310 insertions(+), 9 deletions(-) create mode 100644 docs/examples/urlapi.c create mode 100644 docs/libcurl/opts/CURLOPT_CURLU.3 create mode 100644 tests/data/test658 create mode 100644 tests/libtest/lib658.c diff --git a/docs/examples/Makefile.inc b/docs/examples/Makefile.inc index f51871f06..8dd55b9df 100644 --- a/docs/examples/Makefile.inc +++ b/docs/examples/Makefile.inc @@ -34,7 +34,8 @@ check_PROGRAMS = 10-at-a-time anyauthput cookie_interface debug fileupload \ imap-tls imap-multi url2file sftpget ftpsget postinmemory http2-download \ http2-upload http2-serverpush getredirect ftpuploadfrommem \ ftpuploadresume sslbackend postit2-formadd multi-formadd \ - shared-connection-cache sftpuploadresume http2-pushinmemory parseurl + shared-connection-cache sftpuploadresume http2-pushinmemory parseurl \ + urlapi # These examples require external dependencies that may not be commonly # available on POSIX systems, so don't bother attempting to compile them here. diff --git a/docs/examples/urlapi.c b/docs/examples/urlapi.c new file mode 100644 index 000000000..594f9420d --- /dev/null +++ b/docs/examples/urlapi.c @@ -0,0 +1,72 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2018, Daniel Stenberg, , 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. + * + ***************************************************************************/ +/* + * Set working URL with CURLU *. + * + */ +#include +#include + +#if !CURL_AT_LEAST_VERSION(7, 62, 0) +#error "this example requires curl 7.62.0 or later" +#endif + +int main(void) +{ + CURL *curl; + CURLcode res; + + CURLU *urlp; + CURLUcode uc; + + /* get a curl handle */ + curl = curl_easy_init(); + + /* init Curl URL */ + urlp = curl_url(); + uc = curl_url_set(urlp, CURLUPART_URL, + "http://example.com/path/index.html", 0); + + if(uc) { + fprintf(stderr, "curl_url_set() failed: %in", uc); + goto cleanup; + } + + if(curl) { + /* set urlp to use as working URL */ + curl_easy_setopt(curl, CURLOPT_CURLU, urlp); + curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); + + res = curl_easy_perform(curl); + /* Check for errors */ + if(res != CURLE_OK) + fprintf(stderr, "curl_easy_perform() failed: %s\n", + curl_easy_strerror(res)); + + goto cleanup; + } + + cleanup: + curl_url_cleanup(urlp); + curl_easy_cleanup(curl); + return 0; +} diff --git a/docs/libcurl/curl_easy_setopt.3 b/docs/libcurl/curl_easy_setopt.3 index bde52f999..f4b89c2f8 100644 --- a/docs/libcurl/curl_easy_setopt.3 +++ b/docs/libcurl/curl_easy_setopt.3 @@ -407,6 +407,8 @@ Range requests. See \fICURLOPT_RANGE(3)\fP Resume a transfer. See \fICURLOPT_RESUME_FROM(3)\fP .IP CURLOPT_RESUME_FROM_LARGE Resume a transfer. See \fICURLOPT_RESUME_FROM_LARGE(3)\fP +.IP CURLOPT_CURLU +Set URL to work on with CURLU *. See \fICURLOPT_CURLU(3)\fP .IP CURLOPT_CUSTOMREQUEST Custom request/method. See \fICURLOPT_CUSTOMREQUEST(3)\fP .IP CURLOPT_FILETIME diff --git a/docs/libcurl/opts/CURLOPT_CURLU.3 b/docs/libcurl/opts/CURLOPT_CURLU.3 new file mode 100644 index 000000000..5c762c9a1 --- /dev/null +++ b/docs/libcurl/opts/CURLOPT_CURLU.3 @@ -0,0 +1,61 @@ +.\" ************************************************************************** +.\" * _ _ ____ _ +.\" * Project ___| | | | _ \| | +.\" * / __| | | | |_) | | +.\" * | (__| |_| | _ <| |___ +.\" * \___|\___/|_| \_\_____| +.\" * +.\" * Copyright (C) 1998 - 2018, Daniel Stenberg, , 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_CURLU 3 "28 Oct 2018" "libcurl 7.63.0" "curl_easy_setopt options" +.SH NAME +CURLOPT_CURLU \- set URL with CURLU * +.SH SYNOPSIS +#include + +CURLcode curl_easy_setopt(CURL *handle, CURLOPT_CURLU, void *pointer); +.SH DESCRIPTION +Pass in a pointer to the \fIURL\fP to work with. The parameter should be a +CURLU *. Setting \fICURLOPT_CURLU(3)\fP will explicitly override \fICURLOPT_URL(3)\fP. + +.SH DEFAULT +The default value of this parameter is NULL. +.SH PROTOCOLS +All +.SH EXAMPLE +.nf +CURL *handle = curl_easy_init(); +CURLU *urlp = curl_url(); +int res = 0; +if(curl) { + + res = curl_url_set(urlp, CURLUPART_URL, "http://example.com", 0); + + curl_easy_setopt(handle, CURLOPT_CURLU, urlp); + + ret = curl_easy_perform(handle); + + curl_url_cleanup(urlp); + curl_easy_cleanup(handle); +} +.fi +.SH AVAILABILITY +Added in 7.63.0. +.SH RETURN VALUE +Returns CURLE_OK if the option is supported, and CURLE_UNKNOWN_OPTION if not. +.SH "SEE ALSO" +.BR CURLOPT_URL "(3), " +.BR curl_url "(3), " curl_url_get "(3), " curl_url_set "(3), " diff --git a/docs/libcurl/opts/Makefile.inc b/docs/libcurl/opts/Makefile.inc index 117842dc3..e58db5f4a 100644 --- a/docs/libcurl/opts/Makefile.inc +++ b/docs/libcurl/opts/Makefile.inc @@ -111,6 +111,7 @@ man_MANS = \ CURLOPT_CRLF.3 \ CURLOPT_CRLFILE.3 \ CURLOPT_CUSTOMREQUEST.3 \ + CURLOPT_CURLU.3 \ CURLOPT_DEBUGDATA.3 \ CURLOPT_DEBUGFUNCTION.3 \ CURLOPT_DEFAULT_PROTOCOL.3 \ diff --git a/docs/libcurl/symbols-in-versions b/docs/libcurl/symbols-in-versions index 2ca08ea3f..6c17c384c 100644 --- a/docs/libcurl/symbols-in-versions +++ b/docs/libcurl/symbols-in-versions @@ -371,6 +371,7 @@ CURLOPT_COOKIESESSION 7.9.7 CURLOPT_COPYPOSTFIELDS 7.17.1 CURLOPT_CRLF 7.1 CURLOPT_CRLFILE 7.19.0 +CURLOPT_CURLU 7.63.0 CURLOPT_CUSTOMREQUEST 7.1 CURLOPT_DEBUGDATA 7.9.6 CURLOPT_DEBUGFUNCTION 7.9.6 diff --git a/include/curl/curl.h b/include/curl/curl.h index 0b66f1462..5177e3571 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -1872,6 +1872,9 @@ typedef enum { /* Time in ms between connection upkeep calls for long-lived connections. */ CINIT(UPKEEP_INTERVAL_MS, LONG, 281), + /* Specify URL using CURL URL API. */ + CINIT(CURLU, OBJECTPOINT, 282), + CURLOPT_LASTENTRY /* the last unused */ } CURLoption; diff --git a/include/curl/typecheck-gcc.h b/include/curl/typecheck-gcc.h index 244336204..01df7b15f 100644 --- a/include/curl/typecheck-gcc.h +++ b/include/curl/typecheck-gcc.h @@ -363,6 +363,7 @@ _CURL_WARNING(_curl_easy_getinfo_err_curl_off_t, (option) == CURLOPT_SSL_CTX_DATA || \ (option) == CURLOPT_WRITEDATA || \ (option) == CURLOPT_RESOLVER_START_DATA || \ + (option) == CURLOPT_CURLU || \ 0) /* evaluates to true if option takes a POST data argument (void* or char*) */ diff --git a/lib/setopt.c b/lib/setopt.c index 22956a20f..1627aba6d 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -1204,6 +1204,12 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, return CURLE_BAD_FUNCTION_ARGUMENT; data->set.low_speed_time = arg; break; + case CURLOPT_CURLU: + /* + * pass CURLU to set URL + */ + data->set.uh = va_arg(param, CURLU *); + break; case CURLOPT_URL: /* * The URL to fetch. diff --git a/lib/transfer.c b/lib/transfer.c index de6043d7d..32ca5a496 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -1353,17 +1353,30 @@ void Curl_init_CONNECT(struct Curl_easy *data) CURLcode Curl_pretransfer(struct Curl_easy *data) { CURLcode result; - if(!data->change.url) { + + if(!data->change.url && !data->set.uh) { /* we can't do anything without URL */ failf(data, "No URL set!"); return CURLE_URL_MALFORMAT; } + /* since the URL may have been redirected in a previous use of this handle */ if(data->change.url_alloc) { /* the already set URL is allocated, free it first! */ Curl_safefree(data->change.url); data->change.url_alloc = FALSE; } + + if(!data->change.url && data->set.uh) { + CURLUcode uc; + uc = curl_url_get(data->set.uh, + CURLUPART_URL, &data->set.str[STRING_SET_URL], 0); + if(uc) { + failf(data, "No URL set!"); + return CURLE_URL_MALFORMAT; + } + } + data->change.url = data->set.str[STRING_SET_URL]; /* Init the SSL session ID cache here. We do it here since we want to do it diff --git a/lib/url.c b/lib/url.c index dd9fa2617..6118177d6 100644 --- a/lib/url.c +++ b/lib/url.c @@ -2026,7 +2026,13 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, Curl_up_free(data); /* cleanup previous leftovers first */ /* parse the URL */ - uh = data->state.uh = curl_url(); + if(data->set.uh) { + uh = data->set.uh; + } + else { + uh = data->state.uh = curl_url(); + } + if(!uh) return CURLE_OUT_OF_MEMORY; @@ -2043,15 +2049,17 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, data->change.url_alloc = TRUE; } - uc = curl_url_set(uh, CURLUPART_URL, data->change.url, + if(!data->set.uh) { + uc = curl_url_set(uh, CURLUPART_URL, data->change.url, CURLU_GUESS_SCHEME | CURLU_NON_SUPPORT_SCHEME | (data->set.disallow_username_in_url ? CURLU_DISALLOW_USER : 0) | (data->set.path_as_is ? CURLU_PATH_AS_IS : 0)); - if(uc) { - DEBUGF(infof(data, "curl_url_set rejected %s\n", data->change.url)); - return Curl_uc_to_curlcode(uc); + if(uc) { + DEBUGF(infof(data, "curl_url_set rejected %s\n", data->change.url)); + return Curl_uc_to_curlcode(uc); + } } uc = curl_url_get(uh, CURLUPART_SCHEME, &data->state.up.scheme, 0); @@ -2193,6 +2201,7 @@ static CURLcode parseurlandfillconn(struct Curl_easy *data, return CURLE_OK; } + /* * If we're doing a resumed transfer, we need to setup our stuff * properly. diff --git a/lib/urldata.h b/lib/urldata.h index 11a6a22c6..eae73a19f 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -1728,6 +1728,7 @@ struct UserDefined { bool doh_get; /* use GET for DoH requests, instead of POST */ multidone_func fmultidone; struct Curl_easy *dohfor; /* this is a DoH request for that transfer */ + CURLU *uh; /* URL handle for the current parsed URL */ }; struct Names { diff --git a/tests/data/Makefile.inc b/tests/data/Makefile.inc index 669a482de..e58fb27fb 100644 --- a/tests/data/Makefile.inc +++ b/tests/data/Makefile.inc @@ -83,7 +83,7 @@ test617 test618 test619 test620 test621 test622 test623 test624 test625 \ test626 test627 test628 test629 test630 test631 test632 test633 test634 \ test635 test636 test637 test638 test639 test640 test641 test642 \ test643 test644 test645 test646 test647 test648 test649 test650 test651 \ -test652 test653 test654 test655 test656 \ +test652 test653 test654 test655 test656 test658 \ \ test700 test701 test702 test703 test704 test705 test706 test707 test708 \ test709 test710 test711 test712 test713 test714 test715 \ diff --git a/tests/data/test658 b/tests/data/test658 new file mode 100644 index 000000000..c75293c6d --- /dev/null +++ b/tests/data/test658 @@ -0,0 +1,50 @@ + + + +HTTP +HTTP GET + + + + +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: 6 +Connection: close +Content-Type: text/html +Funny-head: yesyes + +-foo- + + + + +http + + +lib658 + + +HTTP GET + + +http://%HOSTIP:%HTTPPORT/658 + + + + + +^User-Agent:.* + + +GET /658 HTTP/1.1 +Host: %HOSTIP:%HTTPPORT +Accept: */* + + + + diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc index 7a3cd1665..6dc7511f1 100644 --- a/tests/libtest/Makefile.inc +++ b/tests/libtest/Makefile.inc @@ -21,7 +21,7 @@ noinst_PROGRAMS = chkhostname libauthretry libntlmconnect \ lib559 lib560 lib562 lib564 lib565 lib566 lib567 lib568 lib569 lib570 \ lib571 lib572 lib573 lib574 lib575 lib576 lib578 lib579 lib582 \ lib583 lib585 lib586 lib587 lib589 lib590 lib591 lib597 lib598 lib599 \ - lib643 lib644 lib645 lib650 lib651 lib652 lib653 lib654 lib655 \ + lib643 lib644 lib645 lib650 lib651 lib652 lib653 lib654 lib655 lib658 \ lib1156 \ lib1500 lib1501 lib1502 lib1503 lib1504 lib1505 lib1506 lib1507 lib1508 \ lib1509 lib1510 lib1511 lib1512 lib1513 lib1514 lib1515 lib1517 \ @@ -339,6 +339,10 @@ lib654_CPPFLAGS = $(AM_CPPFLAGS) lib655_SOURCES = lib655.c $(SUPPORTFILES) lib655_CPPFLAGS = $(AM_CPPFLAGS) +lib658_SOURCES = lib658.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS) +lib658_LDADD = $(TESTUTIL_LIBS) +lib658_CPPFLAGS = $(AM_CPPFLAGS) + lib1500_SOURCES = lib1500.c $(SUPPORTFILES) $(TESTUTIL) lib1500_LDADD = $(TESTUTIL_LIBS) lib1500_CPPFLAGS = $(AM_CPPFLAGS) diff --git a/tests/libtest/lib658.c b/tests/libtest/lib658.c new file mode 100644 index 000000000..98e0db4b4 --- /dev/null +++ b/tests/libtest/lib658.c @@ -0,0 +1,76 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2011, 2018, Daniel Stenberg, , 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 "test.h" + +#include "testutil.h" +#include "warnless.h" +#include "memdebug.h" + +/* + * Get a single URL without select(). + */ + +int test(char *URL) +{ + CURL *handle = NULL; + CURLcode res = 0; + CURLU *urlp = NULL; + CURLUcode uc = 0; + + global_init(CURL_GLOBAL_ALL); + easy_init(handle); + + urlp = curl_url(); + + if(!urlp) { + fprintf(stderr, "problem init URL api."); + goto test_cleanup; + } + + uc = curl_url_set(urlp, CURLUPART_URL, URL, 0); + if(uc) { + fprintf(stderr, "problem setting CURLUPART_URL."); + goto test_cleanup; + } + + /* demonstrate override behavior */ + easy_setopt(handle, CURLOPT_URL, "http://www.example.com"); + + easy_setopt(handle, CURLOPT_CURLU, urlp); + easy_setopt(handle, CURLOPT_VERBOSE, 1L); + + res = curl_easy_perform(handle); + + if(res) { + fprintf(stderr, "%s:%d curl_easy_perform() failed with code %d (%s)\n", + __FILE__, __LINE__, res, curl_easy_strerror(res)); + goto test_cleanup; + } + +test_cleanup: + + curl_url_cleanup(urlp); + curl_easy_cleanup(handle); + curl_global_cleanup(); + + return res; +}