From ac887eedbc17a0d78b11ff467858c76a5127d1f4 Mon Sep 17 00:00:00 2001 From: Daniel Stenberg Date: Fri, 31 Jan 2014 08:10:07 +0100 Subject: [PATCH] CURLOPT_PROXYHEADER: set headers for proxy-only Includes docs and new test cases: 1525, 1526 and 1527 Co-written-by: Vijay Panghal --- docs/libcurl/curl_easy_setopt.3 | 27 +++++++++ docs/libcurl/symbols-in-versions | 1 + include/curl/curl.h | 9 ++- lib/http.c | 84 ++++++++++++++++++--------- lib/http.h | 9 ++- lib/http_proxy.c | 8 +-- lib/rtsp.c | 26 ++++----- lib/url.c | 14 +++++ lib/urldata.h | 1 + tests/data/Makefile.am | 2 + tests/data/test1525 | 76 ++++++++++++++++++++++++ tests/data/test1526 | 76 ++++++++++++++++++++++++ tests/data/test1527 | 76 ++++++++++++++++++++++++ tests/libtest/Makefile.inc | 16 +++++- tests/libtest/lib1525.c | 96 +++++++++++++++++++++++++++++++ tests/libtest/lib1526.c | 99 ++++++++++++++++++++++++++++++++ tests/libtest/lib1527.c | 95 ++++++++++++++++++++++++++++++ 17 files changed, 667 insertions(+), 48 deletions(-) create mode 100644 tests/data/test1525 create mode 100644 tests/data/test1526 create mode 100644 tests/data/test1527 create mode 100644 tests/libtest/lib1525.c create mode 100644 tests/libtest/lib1526.c create mode 100644 tests/libtest/lib1527.c diff --git a/docs/libcurl/curl_easy_setopt.3 b/docs/libcurl/curl_easy_setopt.3 index 5422bca8f..bda293576 100644 --- a/docs/libcurl/curl_easy_setopt.3 +++ b/docs/libcurl/curl_easy_setopt.3 @@ -1541,6 +1541,33 @@ Pass a NULL to this to reset back to no custom headers. The most commonly replaced headers have "shortcuts" in the options \fICURLOPT_COOKIE\fP, \fICURLOPT_USERAGENT\fP and \fICURLOPT_REFERER\fP. + +Starting in 7.36.0, libcurl offers an alternative option that sets or replaces +headers only for requests that are sent to a proxy: +\fICURLOPT_PROXYHEADER\fP. If \fICURLOPT_PROXYHEADER\fP is not used at all by +an application, the \fICURLOPT_HTTPHEADER headers\fP will be used for proxy +requests as well! +.IP CURLOPT_PROXYHEADER +Pass a pointer to a linked list of HTTP headers to pass in your HTTP request +sent to a proxy. The rules for this list is identical to the +\fICURLOPT_HTTPHEADER\fP option's. + +The headers set with this option is only ever used in requests sent to a +proxy. + +As a special quirk to stay backwards compatible with the libcurl versions +released before this option existed, all headers set with +\fICURLOPT_HTTPHEADER\fP will also be used for proxies unless you set one or +more headers (or even just NULL) with \fICURLOPT_PROXYHEADER\fP. + +The first line in a request (containing the method, usually a GET or POST) is +not a header and cannot be replaced using this option. Only the lines +following the request-line are headers. Adding this method line in this list +of headers will only cause your request to send an invalid header. + +Pass a NULL to this to reset back to no custom headers. + +This option was added in libcurl 7.36.0. .IP CURLOPT_HTTP200ALIASES Pass a pointer to a linked list of aliases to be treated as valid HTTP 200 responses. Some servers respond with a custom header response line. For diff --git a/docs/libcurl/symbols-in-versions b/docs/libcurl/symbols-in-versions index 2e9fc4da0..925e47923 100644 --- a/docs/libcurl/symbols-in-versions +++ b/docs/libcurl/symbols-in-versions @@ -439,6 +439,7 @@ CURLOPT_PROGRESSFUNCTION 7.1 7.32.0 CURLOPT_PROTOCOLS 7.19.4 CURLOPT_PROXY 7.1 CURLOPT_PROXYAUTH 7.10.7 +CURLOPT_PROXYHEADER 7.36.0 CURLOPT_PROXYPASSWORD 7.19.1 CURLOPT_PROXYPORT 7.1 CURLOPT_PROXYTYPE 7.10 diff --git a/include/curl/curl.h b/include/curl/curl.h index 8038d8d25..efdd2e842 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2013, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2014, 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 @@ -903,7 +903,8 @@ typedef enum { /* Set cookie in request: */ CINIT(COOKIE, OBJECTPOINT, 22), - /* This points to a linked list of headers, struct curl_slist kind */ + /* This points to a linked list of headers, struct curl_slist kind. This + list is also used for RTSP (in spite of its name) */ CINIT(HTTPHEADER, OBJECTPOINT, 23), /* This points to a linked list of post entries, struct curl_httppost */ @@ -1581,6 +1582,10 @@ typedef enum { * Expect: 100-continue header before sending the data anyway. */ CINIT(EXPECT_100_TIMEOUT_MS, LONG, 227), + /* This points to a linked list of headers used for proxy requests only, + struct curl_slist kind */ + CINIT(PROXYHEADER, OBJECTPOINT, 228), + CURLOPT_LASTENTRY /* the last unused */ } CURLoption; diff --git a/lib/http.c b/lib/http.c index f803e58f8..c0cb4c069 100644 --- a/lib/http.c +++ b/lib/http.c @@ -169,12 +169,40 @@ CURLcode Curl_http_setup_conn(struct connectdata *conn) * * Returns a pointer to the first matching header or NULL if none matched. */ -char *Curl_checkheaders(struct SessionHandle *data, const char *thisheader) +char *Curl_checkheaders(const struct connectdata *conn, + const char *thisheader) { struct curl_slist *head; size_t thislen = strlen(thisheader); + struct SessionHandle *data = conn->data; - for(head = data->set.headers; head; head=head->next) { + for(head = data->set.headers;head; head=head->next) { + if(Curl_raw_nequal(head->data, thisheader, thislen)) + return head->data; + } + return NULL; +} + +/* + * checkProxyHeaders() checks the linked list of custom proxy headers + * if proxy headers are not available, then it will lookup into http header + * link list + * + * It takes a connectdata struct as input instead of the SessionHandle simply + * to know if this is a proxy request or not, as it then might check a + * different header list. + * + */ +char *Curl_checkProxyheaders(const struct connectdata *conn, + const char *thisheader) +{ + struct curl_slist *head; + size_t thislen = strlen(thisheader); + struct SessionHandle *data = conn->data; + + for(head = (conn->bits.proxy && data->set.proxyheaders)? + data->set.proxyheaders:data->set.headers; + head; head=head->next) { if(Curl_raw_nequal(head->data, thisheader, thislen)) return head->data; } @@ -584,9 +612,9 @@ output_auth_headers(struct connectdata *conn, if(authstatus->picked == CURLAUTH_BASIC) { /* Basic */ if((proxy && conn->bits.proxy_user_passwd && - !Curl_checkheaders(data, "Proxy-authorization:")) || + !Curl_checkProxyheaders(conn, "Proxy-authorization:")) || (!proxy && conn->bits.user_passwd && - !Curl_checkheaders(data, "Authorization:"))) { + !Curl_checkheaders(conn, "Authorization:"))) { auth="Basic"; result = http_output_basic(conn, proxy); if(result) @@ -1501,7 +1529,7 @@ static CURLcode expect100(struct SessionHandle *data, /* if not doing HTTP 1.0 or disabled explicitly, we add a Expect: 100-continue to the headers which actually speeds up post operations (as there is one packet coming back from the web server) */ - ptr = Curl_checkheaders(data, "Expect:"); + ptr = Curl_checkheaders(conn, "Expect:"); if(ptr) { data->state.expect100header = Curl_compareheader(ptr, "Expect:", "100-continue"); @@ -1517,10 +1545,13 @@ static CURLcode expect100(struct SessionHandle *data, } CURLcode Curl_add_custom_headers(struct connectdata *conn, - Curl_send_buffer *req_buffer) + bool is_proxy, + Curl_send_buffer *req_buffer) { char *ptr; - struct curl_slist *headers=conn->data->set.headers; + struct curl_slist *headers= + (is_proxy && conn->data->set.proxyheaders)? + conn->data->set.proxyheaders:conn->data->set.headers; while(headers) { ptr = strchr(headers->data, ':'); @@ -1736,7 +1767,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) it might have been used in the proxy connect, but if we have got a header with the user-agent string specified, we erase the previously made string here. */ - if(Curl_checkheaders(data, "User-Agent:") && conn->allocptr.uagent) { + if(Curl_checkheaders(conn, "User-Agent:") && conn->allocptr.uagent) { free(conn->allocptr.uagent); conn->allocptr.uagent=NULL; } @@ -1757,7 +1788,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) conn->bits.authneg = FALSE; Curl_safefree(conn->allocptr.ref); - if(data->change.referer && !Curl_checkheaders(data, "Referer:")) { + if(data->change.referer && !Curl_checkheaders(conn, "Referer:")) { conn->allocptr.ref = aprintf("Referer: %s\r\n", data->change.referer); if(!conn->allocptr.ref) return CURLE_OUT_OF_MEMORY; @@ -1765,10 +1796,10 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) else conn->allocptr.ref = NULL; - if(data->set.str[STRING_COOKIE] && !Curl_checkheaders(data, "Cookie:")) + if(data->set.str[STRING_COOKIE] && !Curl_checkheaders(conn, "Cookie:")) addcookies = data->set.str[STRING_COOKIE]; - if(!Curl_checkheaders(data, "Accept-Encoding:") && + if(!Curl_checkheaders(conn, "Accept-Encoding:") && data->set.str[STRING_ENCODING]) { Curl_safefree(conn->allocptr.accept_encoding); conn->allocptr.accept_encoding = @@ -1780,13 +1811,14 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) #ifdef HAVE_LIBZ /* we only consider transfer-encoding magic if libz support is built-in */ - if(!Curl_checkheaders(data, "TE:") && data->set.http_transfer_encoding) { + if(!Curl_checkheaders(conn, "TE:") && + data->set.http_transfer_encoding) { /* When we are to insert a TE: header in the request, we must also insert TE in a Connection: header, so we need to merge the custom provided Connection: header and prevent the original to get sent. Note that if the user has inserted his/hers own TE: header we don't do this magic but then assume that the user will handle it all! */ - char *cptr = Curl_checkheaders(data, "Connection:"); + char *cptr = Curl_checkheaders(conn, "Connection:"); #define TE_HEADER "TE: gzip\r\n" Curl_safefree(conn->allocptr.te); @@ -1804,7 +1836,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) /* In HTTP2 forbids Transfer-Encoding: chunked */ ptr = NULL; else { - ptr = Curl_checkheaders(data, "Transfer-Encoding:"); + ptr = Curl_checkheaders(conn, "Transfer-Encoding:"); if(ptr) { /* Some kind of TE is requested, check if 'chunked' is chosen */ data->req.upload_chunky = @@ -1838,7 +1870,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) Curl_safefree(conn->allocptr.host); - ptr = Curl_checkheaders(data, "Host:"); + ptr = Curl_checkheaders(conn, "Host:"); if(ptr && (!data->state.this_is_a_follow || Curl_raw_equal(data->state.first_host, conn->host.name))) { #if !defined(CURL_DISABLE_COOKIES) @@ -1983,13 +2015,13 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) /* we must build the whole post sequence first, so that we have a size of the whole transfer before we start to send it */ result = Curl_getformdata(data, &http->sendit, data->set.httppost, - Curl_checkheaders(data, "Content-Type:"), + Curl_checkheaders(conn, "Content-Type:"), &http->postsize); if(result) return result; } - http->p_accept = Curl_checkheaders(data, "Accept:")?NULL:"Accept: */*\r\n"; + http->p_accept = Curl_checkheaders(conn, "Accept:")?NULL:"Accept: */*\r\n"; if(( (HTTPREQ_POST == httpreq) || (HTTPREQ_POST_FORM == httpreq) || @@ -2069,7 +2101,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) * ones if any such are specified. */ if(((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD)) && - !Curl_checkheaders(data, "Range:")) { + !Curl_checkheaders(conn, "Range:")) { /* if a line like this was already allocated, free the previous one */ if(conn->allocptr.rangeline) free(conn->allocptr.rangeline); @@ -2077,7 +2109,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) data->state.range); } else if((httpreq != HTTPREQ_GET) && - !Curl_checkheaders(data, "Content-Range:")) { + !Curl_checkheaders(conn, "Content-Range:")) { /* if a line like this was already allocated, free the previous one */ if(conn->allocptr.rangeline) @@ -2179,7 +2211,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) conn->allocptr.ref:"" /* Referer: */, (conn->bits.httpproxy && !conn->bits.tunnel_proxy && - !Curl_checkheaders(data, "Proxy-Connection:"))? + !Curl_checkProxyheaders(conn, "Proxy-Connection:"))? "Proxy-Connection: Keep-Alive\r\n":"", te ); @@ -2264,7 +2296,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) return result; } - result = Curl_add_custom_headers(conn, req_buffer); + result = Curl_add_custom_headers(conn, FALSE, req_buffer); if(result) return result; @@ -2314,7 +2346,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) http->sending = HTTPSEND_BODY; if(!data->req.upload_chunky && - !Curl_checkheaders(data, "Content-Length:")) { + !Curl_checkheaders(conn, "Content-Length:")) { /* only add Content-Length if not uploading chunked */ result = Curl_add_bufferf(req_buffer, "Content-Length: %" CURL_FORMAT_CURL_OFF_T @@ -2386,7 +2418,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) postsize = data->set.infilesize; if((postsize != -1) && !data->req.upload_chunky && - !Curl_checkheaders(data, "Content-Length:")) { + !Curl_checkheaders(conn, "Content-Length:")) { /* only add Content-Length if not uploading chunked */ result = Curl_add_bufferf(req_buffer, "Content-Length: %" CURL_FORMAT_CURL_OFF_T @@ -2438,7 +2470,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) we don't upload data chunked, as RFC2616 forbids us to set both kinds of headers (Transfer-Encoding: chunked and Content-Length) */ if((postsize != -1) && !data->req.upload_chunky && - !Curl_checkheaders(data, "Content-Length:")) { + !Curl_checkheaders(conn, "Content-Length:")) { /* we allow replacing this header if not during auth negotiation, although it isn't very wise to actually set your own */ result = Curl_add_bufferf(req_buffer, @@ -2448,7 +2480,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) return result; } - if(!Curl_checkheaders(data, "Content-Type:")) { + if(!Curl_checkheaders(conn, "Content-Type:")) { result = Curl_add_bufferf(req_buffer, "Content-Type: application/" "x-www-form-urlencoded\r\n"); @@ -2460,7 +2492,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) the somewhat bigger ones we allow the app to disable it. Just make sure that the expect100header is always set to the preferred value here. */ - ptr = Curl_checkheaders(data, "Expect:"); + ptr = Curl_checkheaders(conn, "Expect:"); if(ptr) { data->state.expect100header = Curl_compareheader(ptr, "Expect:", "100-continue"); diff --git a/lib/http.h b/lib/http.h index 407f7b621..7cf183101 100644 --- a/lib/http.h +++ b/lib/http.h @@ -39,9 +39,13 @@ extern const struct Curl_handler Curl_handler_https; bool Curl_compareheader(const char *headerline, /* line to check */ const char *header, /* header keyword _with_ colon */ const char *content); /* content string to find */ -char *Curl_checkheaders(struct SessionHandle *data, const char *thisheader); + +char *Curl_checkheaders(const struct connectdata *conn, + const char *thisheader); char *Curl_copy_header_value(const char *header); +char *Curl_checkProxyheaders(const struct connectdata *conn, + const char *thisheader); /* ------------------------------------------------------------------------- */ /* * The add_buffer series of functions are used to build one large memory chunk @@ -67,7 +71,8 @@ CURLcode Curl_add_buffer_send(Curl_send_buffer *in, CURLcode Curl_add_timecondition(struct SessionHandle *data, Curl_send_buffer *buf); CURLcode Curl_add_custom_headers(struct connectdata *conn, - Curl_send_buffer *req_buffer); + bool is_connect, + Curl_send_buffer *req_buffer); /* protocol-specific functions set up to be called by the main engine */ CURLcode Curl_http(struct connectdata *conn, bool *done); diff --git a/lib/http_proxy.c b/lib/http_proxy.c index adcfe42dd..0ee1a73d5 100644 --- a/lib/http_proxy.c +++ b/lib/http_proxy.c @@ -165,7 +165,7 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn, return CURLE_OUT_OF_MEMORY; } - if(!Curl_checkheaders(data, "Host:")) { + if(!Curl_checkProxyheaders(conn, "Host:")) { host = aprintf("Host: %s\r\n", hostheader); if(!host) { free(hostheader); @@ -173,10 +173,10 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn, return CURLE_OUT_OF_MEMORY; } } - if(!Curl_checkheaders(data, "Proxy-Connection:")) + if(!Curl_checkProxyheaders(conn, "Proxy-Connection:")) proxyconn = "Proxy-Connection: Keep-Alive\r\n"; - if(!Curl_checkheaders(data, "User-Agent:") && + if(!Curl_checkProxyheaders(conn, "User-Agent:") && data->set.str[STRING_USERAGENT]) useragent = conn->allocptr.uagent; @@ -200,7 +200,7 @@ CURLcode Curl_proxyCONNECT(struct connectdata *conn, free(hostheader); if(CURLE_OK == result) - result = Curl_add_custom_headers(conn, req_buffer); + result = Curl_add_custom_headers(conn, TRUE, req_buffer); if(CURLE_OK == result) /* CRLF terminate the request */ diff --git a/lib/rtsp.c b/lib/rtsp.c index 5cd6cb467..e746fb1b1 100644 --- a/lib/rtsp.c +++ b/lib/rtsp.c @@ -341,7 +341,7 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) } /* Transport Header for SETUP requests */ - p_transport = Curl_checkheaders(data, "Transport:"); + p_transport = Curl_checkheaders(conn, "Transport:"); if(rtspreq == RTSPREQ_SETUP && !p_transport) { /* New Transport: setting? */ if(data->set.str[STRING_RTSP_TRANSPORT]) { @@ -365,11 +365,11 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) /* Accept Headers for DESCRIBE requests */ if(rtspreq == RTSPREQ_DESCRIBE) { /* Accept Header */ - p_accept = Curl_checkheaders(data, "Accept:")? + p_accept = Curl_checkheaders(conn, "Accept:")? NULL:"Accept: application/sdp\r\n"; /* Accept-Encoding header */ - if(!Curl_checkheaders(data, "Accept-Encoding:") && + if(!Curl_checkheaders(conn, "Accept-Encoding:") && data->set.str[STRING_ENCODING]) { Curl_safefree(conn->allocptr.accept_encoding); conn->allocptr.accept_encoding = @@ -386,18 +386,18 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) it might have been used in the proxy connect, but if we have got a header with the user-agent string specified, we erase the previously made string here. */ - if(Curl_checkheaders(data, "User-Agent:") && conn->allocptr.uagent) { + if(Curl_checkheaders(conn, "User-Agent:") && conn->allocptr.uagent) { Curl_safefree(conn->allocptr.uagent); conn->allocptr.uagent = NULL; } - else if(!Curl_checkheaders(data, "User-Agent:") && + else if(!Curl_checkheaders(conn, "User-Agent:") && data->set.str[STRING_USERAGENT]) { p_uagent = conn->allocptr.uagent; } /* Referrer */ Curl_safefree(conn->allocptr.ref); - if(data->change.referer && !Curl_checkheaders(data, "Referer:")) + if(data->change.referer && !Curl_checkheaders(conn, "Referer:")) conn->allocptr.ref = aprintf("Referer: %s\r\n", data->change.referer); else conn->allocptr.ref = NULL; @@ -414,7 +414,7 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) (rtspreq & (RTSPREQ_PLAY | RTSPREQ_PAUSE | RTSPREQ_RECORD))) { /* Check to see if there is a range set in the custom headers */ - if(!Curl_checkheaders(data, "Range:") && data->state.range) { + if(!Curl_checkheaders(conn, "Range:") && data->state.range) { Curl_safefree(conn->allocptr.rangeline); conn->allocptr.rangeline = aprintf("Range: %s\r\n", data->state.range); p_range = conn->allocptr.rangeline; @@ -424,11 +424,11 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) /* * Sanity check the custom headers */ - if(Curl_checkheaders(data, "CSeq:")) { + if(Curl_checkheaders(conn, "CSeq:")) { failf(data, "CSeq cannot be set as a custom header."); return CURLE_RTSP_CSEQ_ERROR; } - if(Curl_checkheaders(data, "Session:")) { + if(Curl_checkheaders(conn, "Session:")) { failf(data, "Session ID cannot be set as a custom header."); return CURLE_BAD_FUNCTION_ARGUMENT; } @@ -484,7 +484,7 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) return result; } - result = Curl_add_custom_headers(conn, req_buffer); + result = Curl_add_custom_headers(conn, FALSE, req_buffer); if(result) return result; @@ -507,7 +507,7 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) if(putsize > 0 || postsize > 0) { /* As stated in the http comments, it is probably not wise to * actually set a custom Content-Length in the headers */ - if(!Curl_checkheaders(data, "Content-Length:")) { + if(!Curl_checkheaders(conn, "Content-Length:")) { result = Curl_add_bufferf(req_buffer, "Content-Length: %" CURL_FORMAT_CURL_OFF_T"\r\n", (data->set.upload ? putsize : postsize)); @@ -517,7 +517,7 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) if(rtspreq == RTSPREQ_SET_PARAMETER || rtspreq == RTSPREQ_GET_PARAMETER) { - if(!Curl_checkheaders(data, "Content-Type:")) { + if(!Curl_checkheaders(conn, "Content-Type:")) { result = Curl_add_bufferf(req_buffer, "Content-Type: text/parameters\r\n"); if(result) @@ -526,7 +526,7 @@ static CURLcode rtsp_do(struct connectdata *conn, bool *done) } if(rtspreq == RTSPREQ_ANNOUNCE) { - if(!Curl_checkheaders(data, "Content-Type:")) { + if(!Curl_checkheaders(conn, "Content-Type:")) { result = Curl_add_bufferf(req_buffer, "Content-Type: application/sdp\r\n"); if(result) diff --git a/lib/url.c b/lib/url.c index ebd38ccee..25bb04115 100644 --- a/lib/url.c +++ b/lib/url.c @@ -1067,6 +1067,20 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, data->set.headers = va_arg(param, struct curl_slist *); break; + case CURLOPT_PROXYHEADER: + /* + * Set a list with proxy headers to use (or replace internals with) + * + * Since CURLOPT_HTTPHEADER was the only way to set HTTP headers for a + * long time we remain doing it this way until CURLOPT_PROXYHEADER is + * used. As soon as this option has been used, if set to anything but + * NULL, custom headers for proxies are only picked from this list. + * + * Set this option to NULL to restore the previous behavior. + */ + data->set.proxyheaders = va_arg(param, struct curl_slist *); + break; + case CURLOPT_HTTP200ALIASES: /* * Set a list of aliases for HTTP 200 in response header diff --git a/lib/urldata.h b/lib/urldata.h index b24562a4d..fd13eb981 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -1465,6 +1465,7 @@ struct UserDefined { download */ curl_off_t set_resume_from; /* continue [ftp] transfer from here */ struct curl_slist *headers; /* linked list of extra headers */ + struct curl_slist *proxyheaders; /* linked list of extra CONNECT headers */ struct curl_httppost *httppost; /* linked list of POST data */ bool cookiesession; /* new cookie session? */ bool crlf; /* convert crlf on ftp upload(?) */ diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index 5c520c4f6..82c7fa123 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -127,6 +127,8 @@ test1500 test1501 test1502 test1503 test1504 test1505 test1506 test1507 \ test1508 test1509 test1510 test1511 test1512 test1513 test1514 test1515 \ test1516 \ \ +test1525 test1526 test1527 \ +\ test1900 test1901 test1902 test1903 \ \ test2000 test2001 test2002 test2003 test2004 test2005 test2006 test2007 \ diff --git a/tests/data/test1525 b/tests/data/test1525 new file mode 100644 index 000000000..13e0d1489 --- /dev/null +++ b/tests/data/test1525 @@ -0,0 +1,76 @@ + + + +HTTP +HTTP GET +HTTP CONNECT +HTTP proxy +proxytunnel +CURLOPT_PROXYHEADER + + + +# Server-side + + +HTTP/1.1 200 OK +Content-Length: 17 + + + +HTTP/1.1 200 OK swsclose +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" + + +HTTP/1.1 200 OK +Content-Length: 17 + +HTTP/1.1 200 OK swsclose +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" + + + +# Client-side + + +http +http-proxy + + +lib1525 + + +CURLOPT_PROXYHEADER: same headers for host and proxy + + + http://the.old.moo.1525:%HTTPPORT/1525 %HOSTIP:%PROXYPORT + + + +# Verify data after the test has been "shot" + + +CONNECT the.old.moo.1525:%HTTPPORT HTTP/1.1 +Host: the.old.moo.1525:%HTTPPORT +Proxy-Connection: Keep-Alive +User-Agent: Http Agent + + + +PUT /1525 HTTP/1.1 +Host: the.old.moo.1525:%HTTPPORT +Accept: */* +User-Agent: Http Agent +Content-Length: 13 +Expect: 100-continue + +Hello Cloud! + + + diff --git a/tests/data/test1526 b/tests/data/test1526 new file mode 100644 index 000000000..aa111c890 --- /dev/null +++ b/tests/data/test1526 @@ -0,0 +1,76 @@ + + + +HTTP +HTTP GET +HTTP CONNECT +HTTP proxy +proxytunnel +CURLOPT_PROXYHEADER + + + +# Server-side + + +HTTP/1.1 200 OK +Server: present + + + +HTTP/1.1 200 OK swsclose +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" + + +HTTP/1.1 200 OK +Server: present + +HTTP/1.1 200 OK swsclose +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" + + + +# Client-side + + +http +http-proxy + + +lib1526 + + +CURLOPT_PROXYHEADER: separate host/proxy headers + + + http://the.old.moo.1526:%HTTPPORT/1526 %HOSTIP:%PROXYPORT + + + +# Verify data after the test has been "shot" + + +CONNECT the.old.moo.1526:%HTTPPORT HTTP/1.1 +Host: the.old.moo.1526:%HTTPPORT +Proxy-Connection: Keep-Alive +User-Agent: Proxy Agent + + + +PUT /1526 HTTP/1.1 +Host: the.old.moo.1526:%HTTPPORT +Accept: */* +User-Agent: Http Agent +Content-Length: 13 +Expect: 100-continue + +Hello Cloud! + + + diff --git a/tests/data/test1527 b/tests/data/test1527 new file mode 100644 index 000000000..ee4887ac5 --- /dev/null +++ b/tests/data/test1527 @@ -0,0 +1,76 @@ + + + +HTTP +HTTP GET +HTTP CONNECT +HTTP proxy +proxytunnel + + + +# Server-side + + +HTTP/1.1 200 OK +We-are: good + + + +HTTP/1.1 200 OK swsclose +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" + + +HTTP/1.1 200 OK +We-are: good + +HTTP/1.1 200 OK swsclose +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" + + + +# Client-side + + +http +http-proxy + + +lib1527 + + +Check same headers are generated without CURLOPT_PROXYHEADER + + + http://the.old.moo.1527:%HTTPPORT/1527 %HOSTIP:%PROXYPORT + + + +# Verify data after the test has been "shot" + + +CONNECT the.old.moo.1527:%HTTPPORT HTTP/1.1 +Host: the.old.moo.1527:%HTTPPORT +Proxy-Connection: Keep-Alive +User-Agent: Http Agent +Expect: 100-continue + + + +PUT /1527 HTTP/1.1 +Host: the.old.moo.1527:%HTTPPORT +Accept: */* +User-Agent: Http Agent +Expect: 100-continue +Content-Length: 13 + +Hello Cloud! + + + diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc index 4aeb46cc1..bc243590f 100644 --- a/tests/libtest/Makefile.inc +++ b/tests/libtest/Makefile.inc @@ -22,6 +22,8 @@ noinst_PROGRAMS = chkhostname libauthretry libntlmconnect \ lib583 lib585 lib586 lib587 lib590 lib591 lib597 lib598 lib599 \ lib1500 lib1501 lib1502 lib1503 lib1504 lib1505 lib1506 lib1507 lib1508 \ lib1509 lib1510 lib1511 lib1512 lib1513 lib1514 lib1515 \ + lib1509 lib1510 lib1511 lib1512 lib1513 lib1514 lib1515 \ + lib1525 lib1526 lib1527 \ lib1900 \ lib2033 @@ -353,7 +355,19 @@ lib1514_CPPFLAGS = $(AM_CPPFLAGS) -DLIB1514 lib1515_SOURCES = lib1515.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS) lib1515_LDADD = $(TESTUTIL_LIBS) -lib1515_CPPFLAGS = $(AM_CPPFLAGS) +lib1515_CPPFLAGS = $(AM_CPPFLAGS) -DLIB1515 + +lib1525_SOURCES = lib1525.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS) +lib1525_LDADD = $(TESTUTIL_LIBS) +lib1525_CPPFLAGS = $(AM_CPPFLAGS) -DLIB1525 + +lib1526_SOURCES = lib1526.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS) +lib1526_LDADD = $(TESTUTIL_LIBS) +lib1526_CPPFLAGS = $(AM_CPPFLAGS) -DLIB1526 + +lib1527_SOURCES = lib1527.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS) +lib1527_LDADD = $(TESTUTIL_LIBS) +lib1527_CPPFLAGS = $(AM_CPPFLAGS) -DLIB1527 lib1900_SOURCES = lib1900.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS) lib1900_LDADD = $(TESTUTIL_LIBS) diff --git a/tests/libtest/lib1525.c b/tests/libtest/lib1525.c new file mode 100644 index 000000000..45e914e69 --- /dev/null +++ b/tests/libtest/lib1525.c @@ -0,0 +1,96 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2014, Daniel Stenberg, , et al. + * Copyright (C) 2014, Vijay Panghal, , 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 http://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. + * + ***************************************************************************/ + +/* + * This unit test PUT http data over proxy. Proxy header will be different + * from server http header + */ + +#include "test.h" + +#include "memdebug.h" + +static char data [] = "Hello Cloud!\n"; + +static size_t read_callback(void *ptr, size_t size, size_t nmemb, void *stream) +{ + size_t amount = nmemb * size; /* Total bytes curl wants */ + if (amount < strlen(data)) { + return strlen(data); + } + (void)stream; + memcpy(ptr, data, strlen(data)); + return strlen(data); +} + + +int test(char *URL) +{ + CURL *curl = NULL; + CURLcode res = CURLE_FAILED_INIT; + /* http and proxy header list*/ + struct curl_slist *hhl = NULL; + + if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) { + fprintf(stderr, "curl_global_init() failed\n"); + return TEST_ERR_MAJOR_BAD; + } + + if((curl = curl_easy_init()) == NULL) { + fprintf(stderr, "curl_easy_init() failed\n"); + curl_global_cleanup(); + return TEST_ERR_MAJOR_BAD; + } + + hhl = curl_slist_append(hhl, "User-Agent: Http Agent"); + + if (!hhl) { + goto test_cleanup; + } + + test_setopt(curl, CURLOPT_URL, URL); + test_setopt(curl, CURLOPT_PROXY, libtest_arg2); + test_setopt(curl, CURLOPT_HTTPHEADER, hhl); + test_setopt(curl, CURLOPT_PROXYHEADER, NULL); + test_setopt(curl, CURLOPT_POST, 0L); + test_setopt(curl, CURLOPT_UPLOAD, 1L); + test_setopt(curl, CURLOPT_VERBOSE, 1L); + test_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); + test_setopt(curl, CURLOPT_HEADER, 1L); + test_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite); + test_setopt(curl, CURLOPT_READFUNCTION, read_callback); + test_setopt(curl, CURLOPT_HTTPPROXYTUNNEL, 1L); + test_setopt(curl, CURLOPT_INFILESIZE, strlen(data)); + + res = curl_easy_perform(curl); + +test_cleanup: + + curl_easy_cleanup(curl); + + curl_slist_free_all(hhl); + + curl_global_cleanup(); + + return (int)res; +} diff --git a/tests/libtest/lib1526.c b/tests/libtest/lib1526.c new file mode 100644 index 000000000..e71512854 --- /dev/null +++ b/tests/libtest/lib1526.c @@ -0,0 +1,99 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2014, Vijay Panghal, , 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 http://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. + * + ***************************************************************************/ + +/* + * This unit test PUT http data over proxy. Proxy header will be different + * from server http header + */ + +#include "test.h" + +#include "memdebug.h" + +static char data [] = "Hello Cloud!\n"; + +static size_t read_callback(void *ptr, size_t size, size_t nmemb, void *stream) +{ + size_t amount = nmemb * size; /* Total bytes curl wants */ + if (amount < strlen(data)) { + return strlen(data); + } + (void)stream; + memcpy(ptr, data, strlen(data)); + return strlen(data); +} + +int test(char *URL) +{ + CURL *curl = NULL; + CURLcode res = CURLE_FAILED_INIT; + /* http and proxy header list*/ + struct curl_slist *hhl = NULL, *phl = NULL; + + if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) { + fprintf(stderr, "curl_global_init() failed\n"); + return TEST_ERR_MAJOR_BAD; + } + + if((curl = curl_easy_init()) == NULL) { + fprintf(stderr, "curl_easy_init() failed\n"); + curl_global_cleanup(); + return TEST_ERR_MAJOR_BAD; + } + + hhl = curl_slist_append(hhl, "User-Agent: Http Agent"); + phl = curl_slist_append(phl, "User-Agent: Proxy Agent"); + phl = curl_slist_append(phl, "Expect:"); + + if (!hhl || !phl) { + goto test_cleanup; + } + + test_setopt(curl, CURLOPT_URL, URL); + test_setopt(curl, CURLOPT_PROXY, libtest_arg2); + test_setopt(curl, CURLOPT_HTTPHEADER, hhl); + test_setopt(curl, CURLOPT_PROXYHEADER, phl); + test_setopt(curl, CURLOPT_POST, 0L); + test_setopt(curl, CURLOPT_UPLOAD, 1L); + test_setopt(curl, CURLOPT_VERBOSE, 1L); + test_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); + test_setopt(curl, CURLOPT_HEADER, 1L); + test_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite); + test_setopt(curl, CURLOPT_READFUNCTION, read_callback); + test_setopt(curl, CURLOPT_HTTPPROXYTUNNEL, 1L); + test_setopt(curl, CURLOPT_INFILESIZE, strlen(data)); + + res = curl_easy_perform(curl); + +test_cleanup: + + curl_easy_cleanup(curl); + + curl_slist_free_all(hhl); + + curl_slist_free_all(phl); + + curl_global_cleanup(); + + return (int)res; +} + diff --git a/tests/libtest/lib1527.c b/tests/libtest/lib1527.c new file mode 100644 index 000000000..41792f14a --- /dev/null +++ b/tests/libtest/lib1527.c @@ -0,0 +1,95 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2014, Vijay Panghal, , 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 http://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. + * + ***************************************************************************/ + +/* + * This unit test PUT http data over proxy. Same http header will be generated + * for server and proxy + */ + +#include "test.h" + +#include "memdebug.h" + +static char data [] = "Hello Cloud!\n"; + +static size_t read_callback(void *ptr, size_t size, size_t nmemb, void *stream) +{ + size_t amount = nmemb * size; /* Total bytes curl wants */ + if (amount < strlen(data)) { + return strlen(data); + } + (void)stream; + memcpy(ptr, data, strlen(data)); + return strlen(data); +} + + +int test(char *URL) +{ + CURL *curl = NULL; + CURLcode res = CURLE_FAILED_INIT; + /* http header list*/ + struct curl_slist *hhl = NULL; + + if(curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) { + fprintf(stderr, "curl_global_init() failed\n"); + return TEST_ERR_MAJOR_BAD; + } + + if((curl = curl_easy_init()) == NULL) { + fprintf(stderr, "curl_easy_init() failed\n"); + curl_global_cleanup(); + return TEST_ERR_MAJOR_BAD; + } + + hhl = curl_slist_append(hhl, "User-Agent: Http Agent"); + hhl = curl_slist_append(hhl, "Expect: 100-continue"); + + if (!hhl) { + goto test_cleanup; + } + + test_setopt(curl, CURLOPT_URL, URL); + test_setopt(curl, CURLOPT_PROXY, libtest_arg2); + test_setopt(curl, CURLOPT_HTTPHEADER, hhl); + test_setopt(curl, CURLOPT_POST, 0L); + test_setopt(curl, CURLOPT_UPLOAD, 1L); + test_setopt(curl, CURLOPT_VERBOSE, 1L); + test_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP); + test_setopt(curl, CURLOPT_HEADER, 1L); + test_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite); + test_setopt(curl, CURLOPT_READFUNCTION, read_callback); + test_setopt(curl, CURLOPT_HTTPPROXYTUNNEL, 1L); + test_setopt(curl, CURLOPT_INFILESIZE, strlen(data)); + + res = curl_easy_perform(curl); + +test_cleanup: + + curl_easy_cleanup(curl); + + curl_slist_free_all(hhl); + + curl_global_cleanup(); + + return (int)res; +}