diff options
Diffstat (limited to 'Utilities/cmcurl/lib/http.c')
-rw-r--r-- | Utilities/cmcurl/lib/http.c | 279 |
1 files changed, 157 insertions, 122 deletions
diff --git a/Utilities/cmcurl/lib/http.c b/Utilities/cmcurl/lib/http.c index 628dd73..d5c36dd 100644 --- a/Utilities/cmcurl/lib/http.c +++ b/Utilities/cmcurl/lib/http.c @@ -504,7 +504,7 @@ static CURLcode http_perhapsrewind(struct Curl_easy *data, /* 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"); + infof(data, "Rewind stream after send"); } return CURLE_OK; @@ -515,7 +515,7 @@ static CURLcode http_perhapsrewind(struct Curl_easy *data, return CURLE_OK; infof(data, "NTLM send, close instead of sending %" - CURL_FORMAT_CURL_OFF_T " bytes\n", + CURL_FORMAT_CURL_OFF_T " bytes", (curl_off_t)(expectsend - bytessent)); } #endif @@ -532,7 +532,7 @@ static CURLcode http_perhapsrewind(struct Curl_easy *data, /* 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"); + infof(data, "Rewind stream after send"); } return CURLE_OK; @@ -543,7 +543,7 @@ static CURLcode http_perhapsrewind(struct Curl_easy *data, return CURLE_OK; infof(data, "NEGOTIATE send, close instead of sending %" - CURL_FORMAT_CURL_OFF_T " bytes\n", + CURL_FORMAT_CURL_OFF_T " bytes", (curl_off_t)(expectsend - bytessent)); } #endif @@ -756,14 +756,14 @@ output_auth_headers(struct Curl_easy *data, if(auth) { #ifndef CURL_DISABLE_PROXY - infof(data, "%s auth using %s with user '%s'\n", + infof(data, "%s auth using %s with user '%s'", proxy ? "Proxy" : "Server", auth, proxy ? (data->state.aptr.proxyuser ? data->state.aptr.proxyuser : "") : (data->state.aptr.user ? data->state.aptr.user : "")); #else - infof(data, "Server auth using %s with user '%s'\n", + infof(data, "Server auth using %s with user '%s'", auth, data->state.aptr.user ? data->state.aptr.user : ""); #endif @@ -850,7 +850,9 @@ Curl_http_output_auth(struct Curl_easy *data, /* To prevent the user+password to get sent to other than the original host due to a location-follow, we do some weirdo checks here */ if(!data->state.this_is_a_follow || +#ifndef CURL_DISABLE_NETRC conn->bits.netrc || +#endif !data->state.first_host || data->set.allow_auth_to_other_hosts || strcasecompare(data->state.first_host, conn->host.name)) { @@ -995,14 +997,14 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy, result = Curl_input_ntlm_wb(data, conn, proxy, auth); if(result) { - infof(data, "Authentication problem. Ignoring this.\n"); + infof(data, "Authentication problem. Ignoring this."); data->state.authproblem = TRUE; } } #endif } else { - infof(data, "Authentication problem. Ignoring this.\n"); + infof(data, "Authentication problem. Ignoring this."); data->state.authproblem = TRUE; } } @@ -1013,7 +1015,7 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy, #ifndef CURL_DISABLE_CRYPTO_AUTH if(checkprefix("Digest", auth) && is_valid_auth_separator(auth[6])) { if((authp->avail & CURLAUTH_DIGEST) != 0) - infof(data, "Ignoring duplicate digest auth header.\n"); + infof(data, "Ignoring duplicate digest auth header."); else if(Curl_auth_is_digest_supported()) { CURLcode result; @@ -1026,7 +1028,7 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy, * Digest */ result = Curl_input_digest(data, proxy, auth); if(result) { - infof(data, "Authentication problem. Ignoring this.\n"); + infof(data, "Authentication problem. Ignoring this."); data->state.authproblem = TRUE; } } @@ -1042,7 +1044,7 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy, anyway, which basically means our name+password isn't valid. */ authp->avail = CURLAUTH_NONE; - infof(data, "Authentication problem. Ignoring this.\n"); + infof(data, "Authentication problem. Ignoring this."); data->state.authproblem = TRUE; } } @@ -1055,7 +1057,7 @@ CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy, /* We asked for Bearer authentication but got a 40X back anyway, which basically means our token isn't valid. */ authp->avail = CURLAUTH_NONE; - infof(data, "Authentication problem. Ignoring this.\n"); + infof(data, "Authentication problem. Ignoring this."); data->state.authproblem = TRUE; } } @@ -1177,6 +1179,7 @@ static size_t readmoredata(char *buffer, data->req.forbidchunk = (http->sending == HTTPSEND_REQUEST)?TRUE:FALSE; if(data->set.max_send_speed && + (data->set.max_send_speed < (curl_off_t)fullsize) && (data->set.max_send_speed < http->postsize)) /* speed limit */ fullsize = (size_t)data->set.max_send_speed; @@ -1537,38 +1540,35 @@ static int http_getsock_do(struct Curl_easy *data, #ifndef CURL_DISABLE_PROXY static CURLcode add_haproxy_protocol_header(struct Curl_easy *data) { - char proxy_header[128]; struct dynbuf req; CURLcode result; - char tcp_version[5]; + const char *tcp_version; DEBUGASSERT(data->conn); + Curl_dyn_init(&req, DYN_HAXPROXY); - /* Emit the correct prefix for IPv6 */ - if(data->conn->bits.ipv6) { - strcpy(tcp_version, "TCP6"); - } +#ifdef USE_UNIX_SOCKETS + if(data->conn->unix_domain_socket) + /* the buffer is large enough to hold this! */ + result = Curl_dyn_add(&req, "PROXY UNKNOWN\r\n"); else { - strcpy(tcp_version, "TCP4"); - } - - msnprintf(proxy_header, - sizeof(proxy_header), - "PROXY %s %s %s %i %i\r\n", - tcp_version, - data->info.conn_local_ip, - data->info.conn_primary_ip, - data->info.conn_local_port, - data->info.conn_primary_port); - - Curl_dyn_init(&req, DYN_HAXPROXY); +#endif + /* Emit the correct prefix for IPv6 */ + tcp_version = data->conn->bits.ipv6 ? "TCP6" : "TCP4"; - result = Curl_dyn_add(&req, proxy_header); - if(result) - return result; + result = Curl_dyn_addf(&req, "PROXY %s %s %s %i %i\r\n", + tcp_version, + data->info.conn_local_ip, + data->info.conn_primary_ip, + data->info.conn_local_port, + data->info.conn_primary_port); - result = Curl_buffer_send(&req, data, &data->info.request_size, - 0, FIRSTSOCKET); +#ifdef USE_UNIX_SOCKETS + } +#endif + if(!result) + result = Curl_buffer_send(&req, data, &data->info.request_size, + 0, FIRSTSOCKET); return result; } #endif @@ -1588,7 +1588,7 @@ static CURLcode https_connecting(struct Curl_easy *data, bool *done) #endif /* perform SSL initialization for this socket */ - result = Curl_ssl_connect_nonblocking(data, conn, FIRSTSOCKET, done); + result = Curl_ssl_connect_nonblocking(data, conn, FALSE, FIRSTSOCKET, done); if(result) connclose(conn, "Failed HTTPS connection"); @@ -1669,8 +1669,8 @@ CURLcode Curl_http_done(struct Curl_easy *data, * - if any server previously contacted to handle this request only supports * 1.0. */ -static bool use_http_1_1plus(const struct Curl_easy *data, - const struct connectdata *conn) +bool Curl_use_http_1_1plus(const struct Curl_easy *data, + const struct connectdata *conn) { if((data->state.httpversion == 10) || (conn->httpversion == 10)) return FALSE; @@ -1696,7 +1696,7 @@ static const char *get_http_string(const struct Curl_easy *data, return "2"; #endif - if(use_http_1_1plus(data, conn)) + if(Curl_use_http_1_1plus(data, conn)) return "1.1"; return "1.0"; @@ -1711,7 +1711,7 @@ static CURLcode expect100(struct Curl_easy *data, CURLcode result = CURLE_OK; data->state.expect100header = FALSE; /* default to false unless it is set to TRUE below */ - if(!data->state.disableexpect && use_http_1_1plus(data, conn) && + if(!data->state.disableexpect && Curl_use_http_1_1plus(data, conn) && (conn->httpversion < 20)) { /* if not doing HTTP 1.0 or version 2, or disabled explicitly, we add an Expect: 100-continue to the headers which actually speeds up post @@ -2348,7 +2348,7 @@ CURLcode Curl_http_body(struct Curl_easy *data, struct connectdata *conn, if(conn->bits.authneg) /* don't enable chunked during auth neg */ ; - else if(use_http_1_1plus(data, conn)) { + else if(Curl_use_http_1_1plus(data, conn)) { if(conn->httpversion < 20) /* HTTP, upload, unknown file size and not HTTP 1.0 */ data->req.upload_chunky = TRUE; @@ -2711,14 +2711,16 @@ CURLcode Curl_http_cookies(struct Curl_easy *data, int count = 0; if(data->cookies && data->state.cookie_engine) { + const char *host = data->state.aptr.cookiehost ? + data->state.aptr.cookiehost : conn->host.name; + const bool secure_context = + conn->handler->protocol&CURLPROTO_HTTPS || + strcasecompare("localhost", host) || + !strcmp(host, "127.0.0.1") || + !strcmp(host, "[::1]") ? TRUE : FALSE; Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); - co = Curl_cookie_getlist(data->cookies, - data->state.aptr.cookiehost? - data->state.aptr.cookiehost: - conn->host.name, - data->state.up.path, - (conn->handler->protocol&CURLPROTO_HTTPS)? - TRUE:FALSE); + co = Curl_cookie_getlist(data->cookies, host, data->state.up.path, + secure_context); Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); } if(co) { @@ -2901,6 +2903,20 @@ CURLcode Curl_http_firstwrite(struct Curl_easy *data, { struct SingleRequest *k = &data->req; DEBUGASSERT(conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_RTSP)); + if(data->req.ignore_cl) { + k->size = k->maxdownload = -1; + } + else if(k->size != -1) { + /* We wait until after all headers have been received to set this so that + we know for sure Content-Length is valid. */ + if(data->set.max_filesize && + k->size > data->set.max_filesize) { + failf(data, "Maximum file size exceeded"); + return CURLE_FILESIZE_EXCEEDED; + } + Curl_pgrsSetDownloadSize(data, k->size); + } + if(data->req.newurl) { if(conn->bits.close) { /* Abort after the headers if "follow Location" is set @@ -2912,7 +2928,7 @@ CURLcode Curl_http_firstwrite(struct Curl_easy *data, /* We have a new url to load, but since we want to be able to re-use this connection properly, we read the full response in "ignore more" */ k->ignorebody = TRUE; - infof(data, "Ignoring the response-body\n"); + infof(data, "Ignoring the response-body"); } if(data->state.resume_from && !k->content_range && (data->state.httpreq == HTTPREQ_GET) && @@ -2947,7 +2963,7 @@ CURLcode Curl_http_firstwrite(struct Curl_easy *data, /* We're simulating a http 304 from server so we return what should have been returned from the server */ data->info.httpcode = 304; - infof(data, "Simulate a HTTP 304 response!\n"); + infof(data, "Simulate a HTTP 304 response!"); /* we abort the transfer before it is completed == we ruin the re-use ability. Close the connection */ connclose(conn, "Simulated 304 handling"); @@ -2958,6 +2974,39 @@ CURLcode Curl_http_firstwrite(struct Curl_easy *data, return CURLE_OK; } +#ifdef HAVE_LIBZ +CURLcode Curl_transferencode(struct Curl_easy *data) +{ + if(!Curl_checkheaders(data, "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/her 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"); +#define TE_HEADER "TE: gzip\r\n" + + Curl_safefree(data->state.aptr.te); + + if(cptr) { + cptr = Curl_copy_header_value(cptr); + if(!cptr) + return CURLE_OUT_OF_MEMORY; + } + + /* Create the (updated) Connection: header */ + data->state.aptr.te = aprintf("Connection: %s%sTE\r\n" TE_HEADER, + cptr ? cptr : "", (cptr && *cptr) ? ", ":""); + + free(cptr); + if(!data->state.aptr.te) + return CURLE_OUT_OF_MEMORY; + } + return CURLE_OK; +} +#endif + #ifndef USE_HYPER /* * Curl_http() gets called from the generic multi_do() function when a HTTP @@ -3004,11 +3053,11 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) 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"); + infof(data, "Ignoring HTTP/2 prior knowledge due to proxy"); break; } #endif - DEBUGF(infof(data, "HTTP/2 over clean TCP\n")); + DEBUGF(infof(data, "HTTP/2 over clean TCP")); conn->httpversion = 20; result = Curl_http2_switched(data, NULL, 0); @@ -3074,33 +3123,9 @@ CURLcode Curl_http(struct Curl_easy *data, 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) { - /* 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/her 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"); -#define TE_HEADER "TE: gzip\r\n" - - Curl_safefree(data->state.aptr.te); - - if(cptr) { - cptr = Curl_copy_header_value(cptr); - if(!cptr) - return CURLE_OUT_OF_MEMORY; - } - - /* Create the (updated) Connection: header */ - data->state.aptr.te = aprintf("Connection: %s%sTE\r\n" TE_HEADER, - cptr ? cptr : "", (cptr && *cptr) ? ", ":""); - - free(cptr); - if(!data->state.aptr.te) - return CURLE_OUT_OF_MEMORY; - } + result = Curl_transferencode(data); + if(result) + return result; #endif result = Curl_http_body(data, conn, httpreq, &te); @@ -3253,7 +3278,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) /* 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", + " out of %" CURL_FORMAT_CURL_OFF_T " bytes", data->req.writebytecount, http->postsize); data->req.upload_done = TRUE; data->req.keepon &= ~KEEP_SEND; /* we're done writing */ @@ -3392,17 +3417,8 @@ CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn, NULL, 10, &contentlength); if(offt == CURL_OFFT_OK) { - if(data->set.max_filesize && - contentlength > data->set.max_filesize) { - failf(data, "Maximum file size exceeded"); - return CURLE_FILESIZE_EXCEEDED; - } k->size = contentlength; k->maxdownload = k->size; - /* we set the progress download size already at this point - just to make it easier for apps/callbacks to extract this - info as soon as possible */ - Curl_pgrsSetDownloadSize(data, k->size); } else if(offt == CURL_OFFT_FLOW) { /* out of range */ @@ -3411,7 +3427,7 @@ CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn, return CURLE_FILESIZE_EXCEEDED; } streamclose(conn, "overflow content-length"); - infof(data, "Overflow Content-Length: value!\n"); + infof(data, "Overflow Content-Length: value!"); } else { /* negative or just rubbish - bad HTTP */ @@ -3443,7 +3459,7 @@ CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn, * Default action for 1.0 is to close. */ connkeep(conn, "Proxy-Connection keep-alive"); /* don't close */ - infof(data, "HTTP/1.0 proxy connection set to keep alive!\n"); + infof(data, "HTTP/1.0 proxy connection set to keep alive!"); } else if((conn->httpversion == 11) && conn->bits.httpproxy && @@ -3453,7 +3469,7 @@ CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn, * close down after this transfer. */ connclose(conn, "Proxy-Connection: asked to close after done"); - infof(data, "HTTP/1.1 proxy connection set close!\n"); + infof(data, "HTTP/1.1 proxy connection set close!"); } #endif else if((conn->httpversion == 10) && @@ -3465,7 +3481,7 @@ CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn, * * [RFC2068, section 19.7.1] */ connkeep(conn, "Connection keep-alive"); - infof(data, "HTTP/1.0 connection set to keep alive!\n"); + infof(data, "HTTP/1.0 connection set to keep alive!"); } else if(Curl_compareheader(headp, "Connection:", "close")) { /* @@ -3493,6 +3509,12 @@ CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn, TRUE); if(result) return result; + if(!k->chunk) { + /* if this isn't chunked, only close can signal the end of this transfer + as Content-Length is said not to be trusted for transfer-encoding! */ + connclose(conn, "HTTP/1.1 transfer-encoding without chunks"); + k->ignore_cl = TRUE; + } } else if(!k->http_bodyless && checkprefix("Content-Encoding:", headp) && data->set.str[STRING_ENCODING]) { @@ -3555,18 +3577,21 @@ CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn, #if !defined(CURL_DISABLE_COOKIES) else if(data->cookies && data->state.cookie_engine && checkprefix("Set-Cookie:", headp)) { + /* If there is a custom-set Host: name, use it here, or else use real peer + host name. */ + const char *host = data->state.aptr.cookiehost? + data->state.aptr.cookiehost:conn->host.name; + const bool secure_context = + conn->handler->protocol&CURLPROTO_HTTPS || + strcasecompare("localhost", host) || + !strcmp(host, "127.0.0.1") || + !strcmp(host, "[::1]") ? TRUE : FALSE; + Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); - Curl_cookie_add(data, - data->cookies, TRUE, FALSE, - headp + strlen("Set-Cookie:"), - /* If there is a custom-set Host: name, use it - here, or else use real peer host name. */ - data->state.aptr.cookiehost? - data->state.aptr.cookiehost:conn->host.name, - data->state.up.path, - (conn->handler->protocol&CURLPROTO_HTTPS)? - TRUE:FALSE); + Curl_cookie_add(data, data->cookies, TRUE, FALSE, + headp + strlen("Set-Cookie:"), host, + data->state.up.path, secure_context); Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); } #endif @@ -3646,10 +3671,10 @@ CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn, Curl_hsts_parse(data->hsts, data->state.up.hostname, headp + strlen("Strict-Transport-Security:")); if(check) - infof(data, "Illegal STS header skipped\n"); + infof(data, "Illegal STS header skipped"); #ifdef DEBUGBUILD else - infof(data, "Parsed STS header fine (%zu entries)\n", + infof(data, "Parsed STS header fine (%zu entries)", data->hsts->list.size); #endif } @@ -3719,12 +3744,12 @@ CURLcode Curl_http_statusline(struct Curl_easy *data, /* Default action for HTTP/1.0 must be to close, unless we get one of those fancy headers that tell us the server keeps it open for us! */ - infof(data, "HTTP 1.0, assume close after body\n"); + infof(data, "HTTP 1.0, assume close after body"); connclose(conn, "HTTP/1.0 close after body"); } else if(conn->httpversion == 20 || (k->upgr101 == UPGR101_REQUESTED && k->httpcode == 101)) { - DEBUGF(infof(data, "HTTP/2 found, allow multiplexing\n")); + DEBUGF(infof(data, "HTTP/2 found, allow multiplexing")); /* HTTP/2 cannot avoid multiplexing since it is a core functionality of the protocol */ conn->bundle->multiuse = BUNDLE_MULTIPLEX; @@ -3733,7 +3758,7 @@ CURLcode Curl_http_statusline(struct Curl_easy *data, !conn->bits.close) { /* If HTTP version is >= 1.1 and connection is persistent */ DEBUGF(infof(data, - "HTTP 1.1 or later with persistent connection\n")); + "HTTP 1.1 or later with persistent connection")); } k->http_bodyless = k->httpcode >= 100 && k->httpcode < 200; @@ -3911,7 +3936,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, /* Switching Protocols */ if(k->upgr101 == UPGR101_REQUESTED) { /* Switching to HTTP/2 */ - infof(data, "Received 101\n"); + infof(data, "Received 101"); k->upgr101 = UPGR101_RECEIVED; /* we'll get more headers (HTTP/2 response) */ @@ -3951,7 +3976,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, assume that the server will close the connection to signal the end of the document. */ infof(data, "no chunk, no close, no size. Assume close to " - "signal end\n"); + "signal end"); streamclose(conn, "HTTP: No end-of-message indicator"); } } @@ -3964,7 +3989,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, (conn->http_ntlm_state == NTLMSTATE_TYPE2)) || ((data->req.httpcode == 407) && (conn->proxy_ntlm_state == NTLMSTATE_TYPE2)))) { - infof(data, "Connection closure while negotiating auth (HTTP 1.0?)\n"); + infof(data, "Connection closure while negotiating auth (HTTP 1.0?)"); data->state.authproblem = TRUE; } #endif @@ -3974,7 +3999,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, (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"); + infof(data, "Connection closure while negotiating auth (HTTP 1.0?)"); data->state.authproblem = TRUE; } if((conn->http_negotiate_state == GSS_AUTHDONE) && @@ -4054,21 +4079,21 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, if((k->httpcode == 417) && data->state.expect100header) { /* 417 Expectation Failed - try again without the Expect header */ - infof(data, "Got 417 while waiting for a 100\n"); + infof(data, "Got 417 while waiting for a 100"); data->state.disableexpect = TRUE; DEBUGASSERT(!data->req.newurl); data->req.newurl = strdup(data->state.url); Curl_done_sending(data, k); } else if(data->set.http_keep_sending_on_error) { - infof(data, "HTTP error before end of send, keep sending\n"); + infof(data, "HTTP error before end of send, keep sending"); if(k->exp100 > EXP100_SEND_DATA) { k->exp100 = EXP100_SEND_DATA; k->keepon |= KEEP_SEND; } } else { - infof(data, "HTTP error before end of send, stop sending\n"); + infof(data, "HTTP error before end of send, stop sending"); streamclose(conn, "Stop sending data before everything sent"); result = Curl_done_sending(data, k); if(result) @@ -4088,7 +4113,7 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, if(conn->bits.rewindaftersend) { /* We rewind after a complete send, so thus we continue sending now */ - infof(data, "Keep sending data to get tossed away!\n"); + infof(data, "Keep sending data to get tossed away!"); k->keepon |= KEEP_SEND; } } @@ -4201,18 +4226,20 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, * https://tools.ietf.org/html/rfc7230#section-3.1.2 * * The response code is always a three-digit number in HTTP as the spec - * says. We try to allow any number here, but we cannot make + * says. We allow any three-digit number here, but we cannot make * guarantees on future behaviors since it isn't within the protocol. */ char separator; char twoorthree[2]; int httpversion = 0; + int digit4 = -1; /* should remain untouched to be good */ nc = sscanf(HEADER1, - " HTTP/%1d.%1d%c%3d", + " HTTP/%1d.%1d%c%3d%1d", &httpversion_major, &httpversion, &separator, - &k->httpcode); + &k->httpcode, + &digit4); if(nc == 1 && httpversion_major >= 2 && 2 == sscanf(HEADER1, " HTTP/%1[23] %d", twoorthree, &k->httpcode)) { @@ -4221,6 +4248,14 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, separator = ' '; } + /* There can only be a 4th response code digit stored in 'digit4' if + all the other fields were parsed and stored first, so nc is 5 when + digit4 is not -1 */ + else if(digit4 != -1) { + failf(data, "Unsupported response code in HTTP response"); + return CURLE_UNSUPPORTED_PROTOCOL; + } + if((nc == 4) && (' ' == separator)) { httpversion += 10 * httpversion_major; switch(httpversion) { @@ -4243,11 +4278,11 @@ CURLcode Curl_http_readwrite_headers(struct Curl_easy *data, if(k->upgr101 == UPGR101_RECEIVED) { /* supposedly upgraded to http2 now */ if(conn->httpversion != 20) - infof(data, "Lying server, not serving HTTP/2\n"); + infof(data, "Lying server, not serving HTTP/2"); } if(conn->httpversion < 20) { conn->bundle->multiuse = BUNDLE_NO_MULTIUSE; - infof(data, "Mark bundle as not supporting multiuse\n"); + infof(data, "Mark bundle as not supporting multiuse"); } } else if(!nc) { |