diff options
author | Brad King <brad.king@kitware.com> | 2019-05-22 18:15:06 (GMT) |
---|---|---|
committer | Brad King <brad.king@kitware.com> | 2019-05-22 18:15:06 (GMT) |
commit | a39138ef9a7f3e3ec94ae4fd99602ca711bbcf5f (patch) | |
tree | a4d546a554025fb11ec6cc32e7491b150cde35f3 /Utilities/cmcurl/lib/http.c | |
parent | 2de8af0121c3ca64dcb82a1220d2ba255aab3553 (diff) | |
parent | b26487c663ec29d972fd61adc2b14ac5880b78c7 (diff) | |
download | CMake-a39138ef9a7f3e3ec94ae4fd99602ca711bbcf5f.zip CMake-a39138ef9a7f3e3ec94ae4fd99602ca711bbcf5f.tar.gz CMake-a39138ef9a7f3e3ec94ae4fd99602ca711bbcf5f.tar.bz2 |
Merge branch 'upstream-curl' into update-curl
* upstream-curl:
curl 2019-05-22 (885ce314)
Diffstat (limited to 'Utilities/cmcurl/lib/http.c')
-rw-r--r-- | Utilities/cmcurl/lib/http.c | 492 |
1 files changed, 318 insertions, 174 deletions
diff --git a/Utilities/cmcurl/lib/http.c b/Utilities/cmcurl/lib/http.c index 46ac15a..338c59a 100644 --- a/Utilities/cmcurl/lib/http.c +++ b/Utilities/cmcurl/lib/http.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2018, Daniel Stenberg, <daniel@haxx.se>, et al. + * Copyright (C) 1998 - 2019, 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 @@ -73,10 +73,10 @@ #include "http_proxy.h" #include "warnless.h" #include "non-ascii.h" -#include "pipeline.h" #include "http2.h" #include "connect.h" #include "strdup.h" +#include "altsvc.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" @@ -92,7 +92,9 @@ static int http_getsock_do(struct connectdata *conn, int numsocks); static int http_should_fail(struct connectdata *conn); +#ifndef CURL_DISABLE_PROXY static CURLcode add_haproxy_protocol_header(struct connectdata *conn); +#endif #ifdef USE_SSL static CURLcode https_connecting(struct connectdata *conn, bool *done); @@ -102,13 +104,14 @@ static int https_getsock(struct connectdata *conn, #else #define https_connecting(x,y) CURLE_COULDNT_CONNECT #endif +static CURLcode http_setup_conn(struct connectdata *conn); /* * HTTP handler interface. */ const struct Curl_handler Curl_handler_http = { "HTTP", /* scheme */ - Curl_http_setup_conn, /* setup_connection */ + http_setup_conn, /* setup_connection */ Curl_http, /* do_it */ Curl_http_done, /* done */ ZERO_NULL, /* do_more */ @@ -133,7 +136,7 @@ const struct Curl_handler Curl_handler_http = { */ const struct Curl_handler Curl_handler_https = { "HTTPS", /* scheme */ - Curl_http_setup_conn, /* setup_connection */ + http_setup_conn, /* setup_connection */ Curl_http, /* do_it */ Curl_http_done, /* done */ ZERO_NULL, /* do_more */ @@ -153,7 +156,7 @@ const struct Curl_handler Curl_handler_https = { }; #endif -CURLcode Curl_http_setup_conn(struct connectdata *conn) +static CURLcode http_setup_conn(struct connectdata *conn) { /* allocate the HTTP-specific struct for the Curl_easy, only to survive during this request */ @@ -175,7 +178,7 @@ CURLcode Curl_http_setup_conn(struct connectdata *conn) return CURLE_OK; } - +#ifndef CURL_DISABLE_PROXY /* * checkProxyHeaders() checks the linked list of custom proxy headers * if proxy headers are not available, then it will lookup into http header @@ -202,6 +205,10 @@ char *Curl_checkProxyheaders(const struct connectdata *conn, return NULL; } +#else +/* disabled */ +#define Curl_checkProxyheaders(x,y) NULL +#endif /* * Strip off leading and trailing whitespace from the value in the @@ -256,6 +263,7 @@ char *Curl_copy_header_value(const char *header) return value; } +#ifndef CURL_DISABLE_HTTP_AUTH /* * http_output_basic() sets up an Authorization: header (or the proxy version) * for HTTP Basic authentication. @@ -337,6 +345,8 @@ static CURLcode http_output_bearer(struct connectdata *conn) return result; } +#endif + /* pickoneauth() selects the most favourable authentication method from the * ones available and the ones we want. * @@ -415,7 +425,7 @@ static CURLcode http_perhapsrewind(struct connectdata *conn) break; } - bytessent = http->writebytecount; + bytessent = data->req.writebytecount; if(conn->bits.authneg) { /* This is a state where we are known to be negotiating and we don't send @@ -456,8 +466,8 @@ static CURLcode http_perhapsrewind(struct connectdata *conn) (data->state.authproxy.picked == CURLAUTH_NTLM_WB) || (data->state.authhost.picked == CURLAUTH_NTLM_WB)) { if(((expectsend - bytessent) < 2000) || - (conn->ntlm.state != NTLMSTATE_NONE) || - (conn->proxyntlm.state != NTLMSTATE_NONE)) { + (conn->http_ntlm_state != NTLMSTATE_NONE) || + (conn->proxy_ntlm_state != NTLMSTATE_NONE)) { /* The NTLM-negotiation has started *OR* there is just a little (<2K) data left to send, keep on sending. */ @@ -479,8 +489,36 @@ static CURLcode http_perhapsrewind(struct connectdata *conn) (curl_off_t)(expectsend - bytessent)); } #endif +#if defined(USE_SPNEGO) + /* There is still data left to send */ + if((data->state.authproxy.picked == CURLAUTH_NEGOTIATE) || + (data->state.authhost.picked == CURLAUTH_NEGOTIATE)) { + if(((expectsend - bytessent) < 2000) || + (conn->http_negotiate_state != GSS_AUTHNONE) || + (conn->proxy_negotiate_state != GSS_AUTHNONE)) { + /* The NEGOTIATE-negotiation has started *OR* + there is just a little (<2K) data left to send, keep on sending. */ + + /* rewind data when completely done sending! */ + if(!conn->bits.authneg && (conn->writesockfd != CURL_SOCKET_BAD)) { + conn->bits.rewindaftersend = TRUE; + infof(data, "Rewind stream after send\n"); + } + + return CURLE_OK; + } - /* This is not NTLM or many bytes left to send: close */ + if(conn->bits.close) + /* this is already marked to get closed */ + return CURLE_OK; + + infof(data, "NEGOTIATE send, close instead of sending %" + CURL_FORMAT_CURL_OFF_T " bytes\n", + (curl_off_t)(expectsend - bytessent)); + } +#endif + + /* This is not NEGOTIATE/NTLM or many bytes left to send: close */ streamclose(conn, "Mid-auth HTTP and much data left to send"); data->req.size = 0; /* don't download any more than 0 bytes */ @@ -526,6 +564,12 @@ CURLcode Curl_http_auth_act(struct connectdata *conn) pickhost = pickoneauth(&data->state.authhost, authmask); if(!pickhost) data->state.authproblem = TRUE; + if(data->state.authhost.picked == CURLAUTH_NTLM && + conn->httpversion > 11) { + infof(data, "Forcing HTTP/1.1 for NTLM"); + connclose(conn, "Force HTTP/1.1 connection"); + conn->data->set.httpversion = CURL_HTTP_VERSION_1_1; + } } if(conn->bits.proxy_user_passwd && ((data->req.httpcode == 407) || @@ -576,6 +620,7 @@ CURLcode Curl_http_auth_act(struct connectdata *conn) return result; } +#ifndef CURL_DISABLE_HTTP_AUTH /* * Output the correct authentication header depending on the auth type * and whether or not it is to a proxy. @@ -592,10 +637,6 @@ output_auth_headers(struct connectdata *conn, #if !defined(CURL_DISABLE_VERBOSE_STRINGS) || defined(USE_SPNEGO) struct Curl_easy *data = conn->data; #endif -#ifdef USE_SPNEGO - struct negotiatedata *negdata = proxy ? - &data->state.proxyneg : &data->state.negotiate; -#endif #ifdef CURL_DISABLE_CRYPTO_AUTH (void)request; @@ -603,15 +644,11 @@ output_auth_headers(struct connectdata *conn, #endif #ifdef USE_SPNEGO - negdata->state = GSS_AUTHNONE; - if((authstatus->picked == CURLAUTH_NEGOTIATE) && - negdata->context && !GSS_ERROR(negdata->status)) { + if((authstatus->picked == CURLAUTH_NEGOTIATE)) { auth = "Negotiate"; result = Curl_output_negotiate(conn, proxy); if(result) return result; - authstatus->done = TRUE; - negdata->state = GSS_AUTHSENT; } else #endif @@ -697,7 +734,7 @@ output_auth_headers(struct connectdata *conn, * * @param conn all information about the current connection * @param request pointer to the request keyword - * @param path pointer to the requested path + * @param path pointer to the requested path; should include query part * @param proxytunnel boolean if this is the request setting up a "proxy * tunnel" * @@ -744,7 +781,7 @@ Curl_http_output_auth(struct connectdata *conn, #ifndef CURL_DISABLE_PROXY /* Send proxy authentication header if needed */ if(conn->bits.httpproxy && - (conn->bits.tunnel_proxy == proxytunnel)) { + (conn->bits.tunnel_proxy == (bit)proxytunnel)) { result = output_auth_headers(conn, authproxy, request, path, TRUE); if(result) return result; @@ -772,6 +809,22 @@ Curl_http_output_auth(struct connectdata *conn, return result; } +#else +/* when disabled */ +CURLcode +Curl_http_output_auth(struct connectdata *conn, + const char *request, + const char *path, + bool proxytunnel) +{ + (void)conn; + (void)request; + (void)path; + (void)proxytunnel; + return CURLE_OK; +} +#endif + /* * Curl_http_input_auth() deals with Proxy-Authenticate: and WWW-Authenticate: * headers. They are dealt with both in the transfer.c main loop and in the @@ -787,8 +840,8 @@ CURLcode Curl_http_input_auth(struct connectdata *conn, bool proxy, struct Curl_easy *data = conn->data; #ifdef USE_SPNEGO - struct negotiatedata *negdata = proxy? - &data->state.proxyneg:&data->state.negotiate; + curlnegotiate *negstate = proxy ? &conn->proxy_negotiate_state : + &conn->http_negotiate_state; #endif unsigned long *availp; struct auth *authp; @@ -827,21 +880,18 @@ CURLcode Curl_http_input_auth(struct connectdata *conn, bool proxy, authp->avail |= CURLAUTH_NEGOTIATE; if(authp->picked == CURLAUTH_NEGOTIATE) { - if(negdata->state == GSS_AUTHSENT || - negdata->state == GSS_AUTHNONE) { - CURLcode result = Curl_input_negotiate(conn, proxy, auth); - if(!result) { - DEBUGASSERT(!data->req.newurl); - data->req.newurl = strdup(data->change.url); - if(!data->req.newurl) - return CURLE_OUT_OF_MEMORY; - data->state.authproblem = FALSE; - /* we received a GSS auth token and we dealt with it fine */ - negdata->state = GSS_AUTHRECV; - } - else - data->state.authproblem = TRUE; + CURLcode result = Curl_input_negotiate(conn, proxy, auth); + if(!result) { + DEBUGASSERT(!data->req.newurl); + data->req.newurl = strdup(data->change.url); + if(!data->req.newurl) + return CURLE_OUT_OF_MEMORY; + data->state.authproblem = FALSE; + /* we received a GSS auth token and we dealt with it fine */ + *negstate = GSS_AUTHRECV; } + else + data->state.authproblem = TRUE; } } } @@ -869,19 +919,10 @@ CURLcode Curl_http_input_auth(struct connectdata *conn, bool proxy, *availp |= CURLAUTH_NTLM_WB; authp->avail |= CURLAUTH_NTLM_WB; - /* Get the challenge-message which will be passed to - * ntlm_auth for generating the type 3 message later */ - while(*auth && ISSPACE(*auth)) - auth++; - if(checkprefix("NTLM", auth)) { - auth += strlen("NTLM"); - while(*auth && ISSPACE(*auth)) - auth++; - if(*auth) { - conn->challenge_header = strdup(auth); - if(!conn->challenge_header) - return CURLE_OUT_OF_MEMORY; - } + result = Curl_input_ntlm_wb(conn, proxy, auth); + if(result) { + infof(data, "Authentication problem. Ignoring this.\n"); + data->state.authproblem = TRUE; } } #endif @@ -1111,14 +1152,13 @@ void Curl_add_buffer_free(Curl_send_buffer **inp) CURLcode Curl_add_buffer_send(Curl_send_buffer **inp, struct connectdata *conn, - /* add the number of sent bytes to this - counter */ - long *bytes_written, + /* add the number of sent bytes to this + counter */ + curl_off_t *bytes_written, - /* how much of the buffer contains body data */ + /* how much of the buffer contains body data */ size_t included_body_bytes, int socketindex) - { ssize_t amount; CURLcode result; @@ -1214,7 +1254,8 @@ CURLcode Curl_add_buffer_send(Curl_send_buffer **inp, if(http) { /* if we sent a piece of the body here, up the byte counter for it accordingly */ - http->writebytecount += bodylen; + data->req.writebytecount += bodylen; + Curl_pgrsSetUploadCounter(data, data->req.writebytecount); if((size_t)amount != size) { /* The whole request could not be sent in one system call. We must @@ -1255,7 +1296,6 @@ CURLcode Curl_add_buffer_send(Curl_send_buffer **inp, This needs FIXing. */ return CURLE_SEND_ERROR; - Curl_pipeline_leave_write(conn); } } Curl_add_buffer_free(&in); @@ -1432,12 +1472,14 @@ CURLcode Curl_http_connect(struct connectdata *conn, bool *done) /* nothing else to do except wait right now - we're not done here. */ return CURLE_OK; +#ifndef CURL_DISABLE_PROXY if(conn->data->set.haproxyprotocol) { /* add HAProxy PROXY protocol header */ result = add_haproxy_protocol_header(conn); if(result) return result; } +#endif if(conn->given->protocol & CURLPROTO_HTTPS) { /* perform SSL initialization */ @@ -1464,6 +1506,7 @@ static int http_getsock_do(struct connectdata *conn, return GETSOCK_WRITESOCK(0); } +#ifndef CURL_DISABLE_PROXY static CURLcode add_haproxy_protocol_header(struct connectdata *conn) { char proxy_header[128]; @@ -1479,14 +1522,14 @@ static CURLcode add_haproxy_protocol_header(struct connectdata *conn) strcpy(tcp_version, "TCP4"); } - snprintf(proxy_header, - sizeof(proxy_header), - "PROXY %s %s %s %li %li\r\n", - tcp_version, - conn->data->info.conn_local_ip, - conn->data->info.conn_primary_ip, - conn->data->info.conn_local_port, - conn->data->info.conn_primary_port); + msnprintf(proxy_header, + sizeof(proxy_header), + "PROXY %s %s %s %li %li\r\n", + tcp_version, + conn->data->info.conn_local_ip, + conn->data->info.conn_primary_ip, + conn->data->info.conn_local_port, + conn->data->info.conn_primary_port); req_buffer = Curl_add_buffer_init(); if(!req_buffer) @@ -1504,6 +1547,7 @@ static CURLcode add_haproxy_protocol_header(struct connectdata *conn) return result; } +#endif #ifdef USE_SSL static CURLcode https_connecting(struct connectdata *conn, bool *done) @@ -1547,20 +1591,6 @@ CURLcode Curl_http_done(struct connectdata *conn, Curl_unencode_cleanup(conn); -#ifdef USE_SPNEGO - if(data->state.proxyneg.state == GSS_AUTHSENT || - data->state.negotiate.state == GSS_AUTHSENT) { - /* add forbid re-use if http-code != 401/407 as a WA only needed for - * 401/407 that signal auth failure (empty) otherwise state will be RECV - * with current code. - * Do not close CONNECT_ONLY connections. */ - if((data->req.httpcode != 401) && (data->req.httpcode != 407) && - !data->set.connect_only) - streamclose(conn, "Negotiate transfer completed"); - Curl_cleanup_negotiate(data); - } -#endif - /* set the proper values (possibly modified on POST) */ conn->seek_func = data->set.seek_func; /* restore */ conn->seek_client = data->set.seek_client; /* restore */ @@ -1576,16 +1606,6 @@ CURLcode Curl_http_done(struct connectdata *conn, Curl_mime_cleanpart(&http->form); - switch(data->set.httpreq) { - case HTTPREQ_PUT: - case HTTPREQ_POST_FORM: - case HTTPREQ_POST_MIME: - data->req.bytecount = http->readbytecount + http->writebytecount; - break; - default: - break; - } - if(status) return status; @@ -1593,7 +1613,7 @@ CURLcode Curl_http_done(struct connectdata *conn, entire operation is complete */ !conn->bits.retry && !data->set.connect_only && - (http->readbytecount + + (data->req.bytecount + data->req.headerbytecount - data->req.deductheadercount) <= 0) { /* If this connection isn't simply closed to be retried, AND nothing was @@ -1676,6 +1696,50 @@ enum proxy_use { HEADER_CONNECT /* sending CONNECT to a proxy */ }; +/* used to compile the provided trailers into one buffer + will return an error code if one of the headers is + not formatted correctly */ +CURLcode Curl_http_compile_trailers(struct curl_slist *trailers, + Curl_send_buffer *buffer, + struct Curl_easy *handle) +{ + char *ptr = NULL; + CURLcode result = CURLE_OK; + const char *endofline_native = NULL; + const char *endofline_network = NULL; + + if( +#ifdef CURL_DO_LINEEND_CONV + (handle->set.prefer_ascii) || +#endif + (handle->set.crlf)) { + /* \n will become \r\n later on */ + endofline_native = "\n"; + endofline_network = "\x0a"; + } + else { + endofline_native = "\r\n"; + endofline_network = "\x0d\x0a"; + } + + while(trailers) { + /* only add correctly formatted trailers */ + ptr = strchr(trailers->data, ':'); + if(ptr && *(ptr + 1) == ' ') { + result = Curl_add_bufferf(&buffer, "%s%s", trailers->data, + endofline_native); + if(result) + return result; + } + else + infof(handle, "Malformatted trailing header ! Skipping trailer."); + trailers = trailers->next; + } + result = Curl_add_buffer(&buffer, endofline_network, + strlen(endofline_network)); + return result; +} + CURLcode Curl_add_custom_headers(struct connectdata *conn, bool is_connect, Curl_send_buffer *req_buffer) @@ -1737,9 +1801,16 @@ CURLcode Curl_add_custom_headers(struct connectdata *conn, } else { if(*(--ptr) == ';') { - /* send no-value custom header if terminated by semicolon */ - *ptr = ':'; - semicolonp = ptr; + /* copy the source */ + semicolonp = strdup(headers->data); + if(!semicolonp) { + Curl_add_buffer_free(&req_buffer); + return CURLE_OUT_OF_MEMORY; + } + /* put a colon where the semicolon is */ + semicolonp[ptr - headers->data] = ':'; + /* point at the colon */ + optr = &semicolonp [ptr - headers->data]; } } ptr = optr; @@ -1755,35 +1826,37 @@ CURLcode Curl_add_custom_headers(struct connectdata *conn, if(*ptr || semicolonp) { /* only send this if the contents was non-blank or done special */ CURLcode result = CURLE_OK; + char *compare = semicolonp ? semicolonp : headers->data; if(conn->allocptr.host && /* a Host: header was sent already, don't pass on any custom Host: header as that will produce *two* in the same request! */ - checkprefix("Host:", headers->data)) + checkprefix("Host:", compare)) ; else if(data->set.httpreq == HTTPREQ_POST_FORM && /* this header (extended by formdata.c) is sent later */ - checkprefix("Content-Type:", headers->data)) + checkprefix("Content-Type:", compare)) ; else if(data->set.httpreq == HTTPREQ_POST_MIME && /* this header is sent later */ - checkprefix("Content-Type:", headers->data)) + checkprefix("Content-Type:", compare)) ; else if(conn->bits.authneg && /* while doing auth neg, don't allow the custom length since we will force length zero then */ - checkprefix("Content-Length:", headers->data)) + checkprefix("Content-Length:", compare)) ; else if(conn->allocptr.te && /* when asking for Transfer-Encoding, don't pass on a custom Connection: */ - checkprefix("Connection:", headers->data)) + checkprefix("Connection:", compare)) ; else if((conn->httpversion == 20) && - checkprefix("Transfer-Encoding:", headers->data)) + checkprefix("Transfer-Encoding:", compare)) /* HTTP/2 doesn't support chunked requests */ ; - else if(checkprefix("Authorization:", headers->data) && + else if((checkprefix("Authorization:", compare) || + checkprefix("Cookie:", compare)) && /* be careful of sending this potentially sensitive header to other hosts */ (data->state.this_is_a_follow && @@ -1792,10 +1865,10 @@ CURLcode Curl_add_custom_headers(struct connectdata *conn, !strcasecompare(data->state.first_host, conn->host.name))) ; else { - result = Curl_add_bufferf(&req_buffer, "%s\r\n", headers->data); + result = Curl_add_bufferf(&req_buffer, "%s\r\n", compare); } if(semicolonp) - *semicolonp = ';'; /* put back the semicolon */ + free(semicolonp); if(result) return result; } @@ -1807,6 +1880,7 @@ CURLcode Curl_add_custom_headers(struct connectdata *conn, return CURLE_OK; } +#ifndef CURL_DISABLE_PARSEDATE CURLcode Curl_add_timecondition(struct Curl_easy *data, Curl_send_buffer *req_buffer) { @@ -1850,21 +1924,31 @@ CURLcode Curl_add_timecondition(struct Curl_easy *data, */ /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */ - snprintf(datestr, sizeof(datestr), - "%s: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n", - condp, - Curl_wkday[tm->tm_wday?tm->tm_wday-1:6], - tm->tm_mday, - Curl_month[tm->tm_mon], - tm->tm_year + 1900, - tm->tm_hour, - tm->tm_min, - tm->tm_sec); + msnprintf(datestr, sizeof(datestr), + "%s: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n", + condp, + Curl_wkday[tm->tm_wday?tm->tm_wday-1:6], + tm->tm_mday, + Curl_month[tm->tm_mon], + tm->tm_year + 1900, + tm->tm_hour, + tm->tm_min, + tm->tm_sec); result = Curl_add_buffer(&req_buffer, datestr, strlen(datestr)); return result; } +#else +/* disabled */ +CURLcode Curl_add_timecondition(struct Curl_easy *data, + Curl_send_buffer *req_buffer) +{ + (void)data; + (void)req_buffer; + return CURLE_OK; +} +#endif /* * Curl_http() gets called from the generic multi_do() function when a HTTP @@ -1916,6 +2000,13 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) #ifdef USE_NGHTTP2 if(conn->data->set.httpversion == CURL_HTTP_VERSION_2_PRIOR_KNOWLEDGE) { + if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) { + /* We don't support HTTP/2 proxies yet. Also it's debatable whether + or not this setting should apply to HTTP/2 proxies. */ + infof(data, "Ignoring HTTP/2 prior knowledge due to proxy\n"); + break; + } + DEBUGF(infof(data, "HTTP/2 over clean TCP\n")); conn->httpversion = 20; @@ -1947,7 +2038,6 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) data->state.first_remote_port = conn->remote_port; } - http->writebytecount = http->readbytecount = 0; if((conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_FTP)) && data->set.upload) { @@ -1995,11 +2085,21 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) } /* setup the authentication headers */ - result = Curl_http_output_auth(conn, request, path, FALSE); - if(result) - return result; + { + char *pq = NULL; + if(query && *query) { + pq = aprintf("%s?%s", path, query); + if(!pq) + return CURLE_OUT_OF_MEMORY; + } + result = Curl_http_output_auth(conn, request, (pq ? pq : path), FALSE); + free(pq); + if(result) + return result; + } - if((data->state.authhost.multipass || data->state.authproxy.multipass) && + if(((data->state.authhost.multipass && !data->state.authhost.done) + || (data->state.authproxy.multipass && !data->state.authproxy.done)) && (httpreq != HTTPREQ_GET) && (httpreq != HTTPREQ_HEAD)) { /* Auth is required and we are not authenticated yet. Make a PUT or POST @@ -2084,6 +2184,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) http->sendit = NULL; } +#ifndef CURL_DISABLE_MIME if(http->sendit) { const char *cthdr = Curl_checkheaders(conn, "Content-Type"); @@ -2108,6 +2209,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) return result; http->postsize = Curl_mime_size(http->sendit); } +#endif ptr = Curl_checkheaders(conn, "Transfer-Encoding"); if(ptr) { @@ -2293,8 +2395,8 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) if(!*data->state.up.path && path[strlen(path) - 1] != '/') { *p++ = '/'; } - snprintf(p, sizeof(ftp_typecode) - 1, ";type=%c", - data->set.prefer_ascii ? 'a' : 'i'); + msnprintf(p, sizeof(ftp_typecode) - 1, ";type=%c", + data->set.prefer_ascii ? 'a' : 'i'); } } if(conn->bits.user_passwd && !conn->bits.userpwd_in_url) @@ -2635,9 +2737,8 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) failf(data, "Failed sending PUT request"); else /* prepare for transfer */ - Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE, - &http->readbytecount, postsize?FIRSTSOCKET:-1, - postsize?&http->writebytecount:NULL); + Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, + postsize?FIRSTSOCKET:-1); if(result) return result; break; @@ -2657,12 +2758,11 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) failf(data, "Failed sending POST request"); else /* setup variables for the upcoming transfer */ - Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE, &http->readbytecount, - -1, NULL); + Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, -1); break; } - postsize = http->postsize; + data->state.infilesize = postsize = http->postsize; /* We only set Content-Length and allow a custom Content-Length if we don't upload data chunked, as RFC2616 forbids us to set both @@ -2678,6 +2778,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) return result; } +#ifndef CURL_DISABLE_MIME /* Output mime-generated headers. */ { struct curl_slist *hdr; @@ -2688,6 +2789,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) return result; } } +#endif /* For really small posts we don't use Expect: headers at all, and for the somewhat bigger ones we allow the app to disable it. Just make @@ -2726,9 +2828,8 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) failf(data, "Failed sending POST request"); else /* prepare for transfer */ - Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE, - &http->readbytecount, postsize?FIRSTSOCKET:-1, - postsize?&http->writebytecount:NULL); + Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, + postsize?FIRSTSOCKET:-1); if(result) return result; @@ -2882,9 +2983,8 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) if(result) failf(data, "Failed sending HTTP POST request"); else - Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE, - &http->readbytecount, http->postdata?FIRSTSOCKET:-1, - http->postdata?&http->writebytecount:NULL); + Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, + http->postdata?FIRSTSOCKET:-1); break; default: @@ -2900,33 +3000,30 @@ CURLcode Curl_http(struct connectdata *conn, bool *done) failf(data, "Failed sending HTTP request"); else /* HTTP GET/HEAD download: */ - Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE, &http->readbytecount, - http->postdata?FIRSTSOCKET:-1, - http->postdata?&http->writebytecount:NULL); + Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, + http->postdata?FIRSTSOCKET:-1); } if(result) return result; - if(http->writebytecount) { + if(data->req.writebytecount) { /* if a request-body has been sent off, we make sure this progress is noted properly */ - Curl_pgrsSetUploadCounter(data, http->writebytecount); + Curl_pgrsSetUploadCounter(data, data->req.writebytecount); if(Curl_pgrsUpdate(conn)) result = CURLE_ABORTED_BY_CALLBACK; - if(http->writebytecount >= postsize) { + if(data->req.writebytecount >= postsize) { /* already sent the entire request body, mark the "upload" as complete */ infof(data, "upload completely sent off: %" CURL_FORMAT_CURL_OFF_T " out of %" CURL_FORMAT_CURL_OFF_T " bytes\n", - http->writebytecount, postsize); + data->req.writebytecount, postsize); data->req.upload_done = TRUE; data->req.keepon &= ~KEEP_SEND; /* we're done writing */ data->req.exp100 = EXP100_SEND_DATA; /* already sent */ Curl_expire_done(data, EXPIRE_100_TIMEOUT); } - else - data->req.writebytecount = http->writebytecount; } if((conn->httpversion == 20) && data->req.upload_chunky) @@ -3161,6 +3258,10 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, k->header = FALSE; k->badheader = HEADER_ALLBAD; streamclose(conn, "bad HTTP: No end-of-message indicator"); + if(!data->set.http09_allowed) { + failf(data, "Received HTTP/0.9 when not allowed\n"); + return CURLE_UNSUPPORTED_PROTOCOL; + } break; } } @@ -3194,6 +3295,10 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, if(st == STATUS_BAD) { streamclose(conn, "bad HTTP: No end-of-message indicator"); /* this is not the beginning of a protocol first header line */ + if(!data->set.http09_allowed) { + failf(data, "Received HTTP/0.9 when not allowed\n"); + return CURLE_UNSUPPORTED_PROTOCOL; + } k->header = FALSE; if(*nread) /* since there's more, this is a partial bad header */ @@ -3306,14 +3411,31 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, #if defined(USE_NTLM) if(conn->bits.close && (((data->req.httpcode == 401) && - (conn->ntlm.state == NTLMSTATE_TYPE2)) || + (conn->http_ntlm_state == NTLMSTATE_TYPE2)) || ((data->req.httpcode == 407) && - (conn->proxyntlm.state == NTLMSTATE_TYPE2)))) { + (conn->proxy_ntlm_state == NTLMSTATE_TYPE2)))) { infof(data, "Connection closure while negotiating auth (HTTP 1.0?)\n"); data->state.authproblem = TRUE; } #endif - +#if defined(USE_SPNEGO) + if(conn->bits.close && + (((data->req.httpcode == 401) && + (conn->http_negotiate_state == GSS_AUTHRECV)) || + ((data->req.httpcode == 407) && + (conn->proxy_negotiate_state == GSS_AUTHRECV)))) { + infof(data, "Connection closure while negotiating auth (HTTP 1.0?)\n"); + data->state.authproblem = TRUE; + } + if((conn->http_negotiate_state == GSS_AUTHDONE) && + (data->req.httpcode != 401)) { + conn->http_negotiate_state = GSS_AUTHSUCC; + } + if((conn->proxy_negotiate_state == GSS_AUTHDONE) && + (data->req.httpcode != 407)) { + conn->proxy_negotiate_state = GSS_AUTHSUCC; + } +#endif /* * When all the headers have been parsed, see if we should give * up and return an error. @@ -3549,6 +3671,10 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, if(conn->httpversion != 20) infof(data, "Lying server, not serving HTTP/2\n"); } + if(conn->httpversion < 20) { + conn->bundle->multiuse = BUNDLE_NO_MULTIUSE; + infof(data, "Mark bundle as not supporting multiuse\n"); + } } else if(!nc) { /* this is the real world, not a Nirvana @@ -3586,7 +3712,6 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, conn->httpversion = 11; /* For us, RTSP acts like HTTP 1.1 */ } else { - /* TODO: do we care about the other cases here? */ nc = 0; } } @@ -3639,26 +3764,12 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, } else if(conn->httpversion >= 11 && !conn->bits.close) { - /* If HTTP version is >= 1.1 and connection is persistent - server supports pipelining. */ + /* If HTTP version is >= 1.1 and connection is persistent */ DEBUGF(infof(data, - "HTTP 1.1 or later with persistent connection, " - "pipelining supported\n")); - /* Activate pipelining if needed */ - if(conn->bundle) { - if(!Curl_pipeline_site_blacklisted(data, conn)) - conn->bundle->multiuse = BUNDLE_PIPELINING; - } + "HTTP 1.1 or later with persistent connection\n")); } switch(k->httpcode) { - case 204: - /* (quote from RFC2616, section 10.2.5): The server has - * fulfilled the request but does not need to return an - * entity-body ... The 204 response MUST NOT include a - * message-body, and thus is always terminated by the first - * empty line after the header fields. */ - /* FALLTHROUGH */ case 304: /* (quote from RFC2616, section 10.3.5): The 304 response * MUST NOT contain a message-body, and thus is always @@ -3666,6 +3777,13 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, * fields. */ if(data->set.timecondition) data->info.timecond = TRUE; + /* FALLTHROUGH */ + case 204: + /* (quote from RFC2616, section 10.2.5): The server has + * fulfilled the request but does not need to return an + * entity-body ... The 204 response MUST NOT include a + * message-body, and thus is always terminated by the first + * empty line after the header fields. */ k->size = 0; k->maxdownload = 0; k->ignorecl = TRUE; /* ignore Content-Length headers */ @@ -3733,19 +3851,6 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, data->info.contenttype = contenttype; } } - else if(checkprefix("Server:", k->p)) { - if(conn->httpversion < 20) { - /* only do this for non-h2 servers */ - char *server_name = Curl_copy_header_value(k->p); - - /* Turn off pipelining if the server version is blacklisted */ - if(conn->bundle && (conn->bundle->multiuse == BUNDLE_PIPELINING)) { - if(Curl_pipeline_server_blacklisted(data, server_name)) - conn->bundle->multiuse = BUNDLE_NO_MULTIUSE; - } - free(server_name); - } - } else if((conn->httpversion == 10) && conn->bits.httpproxy && Curl_compareheader(k->p, @@ -3859,7 +3964,9 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, here, or else use real peer host name. */ conn->allocptr.cookiehost? conn->allocptr.cookiehost:conn->host.name, - data->state.up.path); + data->state.up.path, + (conn->handler->protocol&CURLPROTO_HTTPS)? + TRUE:FALSE); Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); } #endif @@ -3888,6 +3995,22 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, if(result) return result; } + #ifdef USE_SPNEGO + else if(checkprefix("Persistent-Auth", k->p)) { + struct negotiatedata *negdata = &conn->negotiate; + struct auth *authp = &data->state.authhost; + if(authp->picked == CURLAUTH_NEGOTIATE) { + char *persistentauth = Curl_copy_header_value(k->p); + if(!persistentauth) + return CURLE_OUT_OF_MEMORY; + negdata->noauthpersist = checkprefix("false", persistentauth); + negdata->havenoauthpersist = TRUE; + infof(data, "Negotiate: noauthpersist -> %d, header part: %s", + negdata->noauthpersist, persistentauth); + free(persistentauth); + } + } + #endif else if((k->httpcode >= 300 && k->httpcode < 400) && checkprefix("Location:", k->p) && !data->req.location) { @@ -3915,6 +4038,27 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, } } } +#ifdef USE_ALTSVC + /* If enabled, the header is incoming and this is over HTTPS */ + else if(data->asi && checkprefix("Alt-Svc:", k->p) && + ((conn->handler->flags & PROTOPT_SSL) || +#ifdef CURLDEBUG + /* allow debug builds to circumvent the HTTPS restriction */ + getenv("CURL_ALTSVC_HTTP") +#else + 0 +#endif + )) { + /* the ALPN of the current request */ + enum alpnid id = (conn->httpversion == 20) ? ALPN_h2 : ALPN_h1; + result = Curl_altsvc_parse(data, data->asi, + &k->p[ strlen("Alt-Svc:") ], + id, conn->host.name, + curlx_uitous(conn->remote_port)); + if(result) + return result; + } +#endif else if(conn->handler->protocol & CURLPROTO_RTSP) { result = Curl_rtsp_parseheader(conn, k->p); if(result) |