| @@ -105,24 +105,16 @@ static void http_exp100_got100(struct Curl_easy *data); |
| static CURLcode http_firstwrite(struct Curl_easy *data); |
| static CURLcode http_header(struct Curl_easy *data, |
| const char *hd, size_t hdlen); |
| - | static CURLcode http_host(struct Curl_easy *data, struct connectdata *conn); |
| static CURLcode http_range(struct Curl_easy *data, |
| Curl_HttpReq httpreq); |
| - | static CURLcode http_req_complete(struct Curl_easy *data, |
| - | struct dynbuf *r, int httpversion, |
| - | Curl_HttpReq httpreq); |
| - | static CURLcode http_req_set_reader(struct Curl_easy *data, |
| - | Curl_HttpReq httpreq, int httpversion, |
| - | const char **tep); |
| + | static CURLcode http_req_set_TE(struct Curl_easy *data, |
| + | struct dynbuf *req, |
| + | int httpversion); |
| static CURLcode http_size(struct Curl_easy *data); |
| static CURLcode http_statusline(struct Curl_easy *data, |
| struct connectdata *conn); |
| - | static CURLcode http_target(struct Curl_easy *data, struct connectdata *conn, |
| - | struct dynbuf *req); |
| + | static CURLcode http_target(struct Curl_easy *data, struct dynbuf *req); |
| static CURLcode http_useragent(struct Curl_easy *data); |
| - | #ifdef HAVE_LIBZ |
| - | static CURLcode http_transferencode(struct Curl_easy *data); |
| - | #endif |
| |
| |
| /* |
| @@ -1709,10 +1701,8 @@ CURLcode Curl_add_custom_headers(struct Curl_easy *data, |
| we will force length zero then */ |
| curlx_str_casecompare(&name, "Content-Length")) |
| ; |
| - | else if(data->state.aptr.te && |
| - | /* when asking for Transfer-Encoding, do not pass on a custom |
| - | Connection: */ |
| - | curlx_str_casecompare(&name, "Connection")) |
| + | else if(curlx_str_casecompare(&name, "Connection")) |
| + | /* Normal Connection: header generation takes care of this */ |
| ; |
| else if((httpversion >= 20) && |
| curlx_str_casecompare(&name, "Transfer-Encoding")) |
| @@ -1817,14 +1807,14 @@ CURLcode Curl_add_timecondition(struct Curl_easy *data, |
| } |
| #endif |
| |
| - | void Curl_http_method(struct Curl_easy *data, struct connectdata *conn, |
| + | void Curl_http_method(struct Curl_easy *data, |
| const char **method, Curl_HttpReq *reqp) |
| { |
| Curl_HttpReq httpreq = (Curl_HttpReq)data->state.httpreq; |
| const char *request; |
| - | if(conn->handler->protocol&(CURLPROTO_WS|CURLPROTO_WSS)) |
| + | if(data->conn->handler->protocol&(CURLPROTO_WS|CURLPROTO_WSS)) |
| httpreq = HTTPREQ_GET; |
| - | else if((conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_FTP)) && |
| + | else if((data->conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_FTP)) && |
| data->state.upload) |
| httpreq = HTTPREQ_PUT; |
| |
| @@ -1875,10 +1865,12 @@ static CURLcode http_useragent(struct Curl_easy *data) |
| } |
| |
| |
| - | static CURLcode http_host(struct Curl_easy *data, struct connectdata *conn) |
| + | static CURLcode http_set_aptr_host(struct Curl_easy *data) |
| { |
| - | const char *ptr; |
| + | struct connectdata *conn = data->conn; |
| struct dynamically_allocated_data *aptr = &data->state.aptr; |
| + | const char *ptr; |
| + | |
| if(!data->state.this_is_a_follow) { |
| /* Free to avoid leaking memory on multiple requests */ |
| free(data->state.first_host); |
| @@ -1966,7 +1958,6 @@ static CURLcode http_host(struct Curl_easy *data, struct connectdata *conn) |
| * Append the request-target to the HTTP request |
| */ |
| static CURLcode http_target(struct Curl_easy *data, |
| - | struct connectdata *conn, |
| struct dynbuf *r) |
| { |
| CURLcode result = CURLE_OK; |
| @@ -1979,7 +1970,7 @@ static CURLcode http_target(struct Curl_easy *data, |
| } |
| |
| #ifndef CURL_DISABLE_PROXY |
| - | if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) { |
| + | if(data->conn->bits.httpproxy && !data->conn->bits.tunnel_proxy) { |
| /* Using a proxy but does not tunnel through it */ |
| |
| /* The path sent to the proxy is in fact the entire URL. But if the remote |
| @@ -1993,8 +1984,8 @@ static CURLcode http_target(struct Curl_easy *data, |
| if(!h) |
| return CURLE_OUT_OF_MEMORY; |
| |
| - | if(conn->host.dispname != conn->host.name) { |
| - | uc = curl_url_set(h, CURLUPART_HOST, conn->host.name, 0); |
| + | if(data->conn->host.dispname != data->conn->host.name) { |
| + | uc = curl_url_set(h, CURLUPART_HOST, data->conn->host.name, 0); |
| if(uc) { |
| curl_url_cleanup(h); |
| return CURLE_OUT_OF_MEMORY; |
| @@ -2060,8 +2051,6 @@ static CURLcode http_target(struct Curl_easy *data, |
| } |
| |
| else |
| - | #else |
| - | (void)conn; /* not used in disabled-proxy builds */ |
| #endif |
| { |
| result = curlx_dyn_add(r, path); |
| @@ -2247,21 +2236,13 @@ static CURLcode http_resume(struct Curl_easy *data, Curl_HttpReq httpreq) |
| return CURLE_OK; |
| } |
| |
| - | static CURLcode http_req_set_reader(struct Curl_easy *data, |
| - | Curl_HttpReq httpreq, int httpversion, |
| - | const char **tep) |
| + | static CURLcode http_req_set_TE(struct Curl_easy *data, |
| + | struct dynbuf *req, |
| + | int httpversion) |
| { |
| CURLcode result = CURLE_OK; |
| const char *ptr; |
| |
| - | result = set_reader(data, httpreq); |
| - | if(result) |
| - | return result; |
| - | |
| - | result = http_resume(data, httpreq); |
| - | if(result) |
| - | return result; |
| - | |
| ptr = Curl_checkheaders(data, STRCONST("Transfer-Encoding")); |
| if(ptr) { |
| /* Some kind of TE is requested, check if 'chunked' is chosen */ |
| @@ -2295,7 +2276,7 @@ static CURLcode http_req_set_reader(struct Curl_easy *data, |
| } |
| |
| if(data->req.upload_chunky) |
| - | *tep = "Transfer-Encoding: chunked\r\n"; |
| + | result = curlx_dyn_add(req, "Transfer-Encoding: chunked\r\n"); |
| } |
| return result; |
| } |
| @@ -2335,9 +2316,10 @@ static CURLcode addexpect(struct Curl_easy *data, struct dynbuf *r, |
| return CURLE_OK; |
| } |
| |
| - | static CURLcode http_req_complete(struct Curl_easy *data, |
| - | struct dynbuf *r, int httpversion, |
| - | Curl_HttpReq httpreq) |
| + | static CURLcode http_add_content_hds(struct Curl_easy *data, |
| + | struct dynbuf *r, |
| + | int httpversion, |
| + | Curl_HttpReq httpreq) |
| { |
| CURLcode result = CURLE_OK; |
| curl_off_t req_clen; |
| @@ -2405,26 +2387,17 @@ static CURLcode http_req_complete(struct Curl_easy *data, |
| break; |
| } |
| |
| - | /* end of headers */ |
| - | result = curlx_dyn_addn(r, STRCONST("\r\n")); |
| - | if(!result) { |
| - | Curl_pgrsSetUploadSize(data, req_clen); |
| - | if(announced_exp100) |
| - | result = http_exp100_add_reader(data); |
| - | } |
| + | Curl_pgrsSetUploadSize(data, req_clen); |
| + | if(announced_exp100) |
| + | result = http_exp100_add_reader(data); |
| |
| out: |
| - | if(!result) { |
| - | /* setup variables for the upcoming transfer */ |
| - | Curl_xfer_setup_sendrecv(data, FIRSTSOCKET, -1); |
| - | } |
| return result; |
| } |
| |
| #ifndef CURL_DISABLE_COOKIES |
| |
| static CURLcode http_cookies(struct Curl_easy *data, |
| - | struct connectdata *conn, |
| struct dynbuf *r) |
| { |
| CURLcode result = CURLE_OK; |
| @@ -2441,9 +2414,9 @@ static CURLcode http_cookies(struct Curl_easy *data, |
| |
| if(data->cookies && data->state.cookie_engine) { |
| const char *host = data->state.aptr.cookiehost ? |
| - | data->state.aptr.cookiehost : conn->host.name; |
| + | data->state.aptr.cookiehost : data->conn->host.name; |
| Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); |
| - | rc = Curl_cookie_getlist(data, conn, host, &list); |
| + | rc = Curl_cookie_getlist(data, data->conn, host, &list); |
| Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); |
| } |
| if(!rc) { |
| @@ -2494,7 +2467,7 @@ static CURLcode http_cookies(struct Curl_easy *data, |
| return result; |
| } |
| #else |
| - | #define http_cookies(a,b,c) CURLE_OK |
| + | #define http_cookies(a,b) CURLE_OK |
| #endif |
| |
| static CURLcode http_range(struct Curl_easy *data, |
| @@ -2616,63 +2589,13 @@ static CURLcode http_firstwrite(struct Curl_easy *data) |
| return CURLE_OK; |
| } |
| |
| - | #ifdef HAVE_LIBZ |
| - | static CURLcode http_transferencode(struct Curl_easy *data) |
| - | { |
| - | if(!Curl_checkheaders(data, STRCONST("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 do not do this magic |
| - | but then assume that the user will handle it all! */ |
| - | char *cptr = Curl_checkheaders(data, STRCONST("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 |
| - | |
| - | /* |
| - | * Curl_http() gets called from the generic multi_do() function when an HTTP |
| - | * request is to be performed. This creates and sends a properly constructed |
| - | * HTTP request. |
| - | */ |
| - | CURLcode Curl_http(struct Curl_easy *data, bool *done) |
| + | static CURLcode http_check_new_conn(struct Curl_easy *data) |
| { |
| struct connectdata *conn = data->conn; |
| - | CURLcode result = CURLE_OK; |
| - | Curl_HttpReq httpreq; |
| - | const char *te = ""; /* transfer-encoding */ |
| - | const char *request; |
| - | const char *httpstring; |
| - | struct dynbuf req; |
| - | char *altused = NULL; |
| - | const char *p_accept; /* Accept: string */ |
| - | unsigned char httpversion; |
| - | const char *alpn; |
| const char *info_version = NULL; |
| + | const char *alpn; |
| + | CURLcode result; |
| |
| - | /* Always consider the DO phase done after this function call, even if there |
| - | may be parts of the request that are not yet sent, since we can deal with |
| - | the rest of the request in the PERFORM phase. */ |
| - | *done = TRUE; |
| alpn = Curl_conn_get_alpn_negotiated(data, conn); |
| if(alpn && !strcmp("h3", alpn)) { |
| DEBUGASSERT(Curl_conn_http_version(data, conn) == 30); |
| @@ -2684,7 +2607,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) |
| conn->bits.proxy && !conn->bits.tunnel_proxy) { |
| result = Curl_http2_switch(data); |
| if(result) |
| - | goto fail; |
| + | return result; |
| } |
| else |
| #endif |
| @@ -2697,7 +2620,7 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) |
| DEBUGF(infof(data, "HTTP/2 over clean TCP")); |
| result = Curl_http2_switch(data); |
| if(result) |
| - | goto fail; |
| + | return result; |
| info_version = "HTTP/2"; |
| /* There is no ALPN here, but the connection is now definitely h2 */ |
| conn->httpversion_seen = 20; |
| @@ -2706,213 +2629,321 @@ CURLcode Curl_http(struct Curl_easy *data, bool *done) |
| info_version = "HTTP/1.x"; |
| } |
| |
| - | if(info_version && !data->conn->bits.reuse) |
| + | if(info_version) |
| infof(data, "using %s", info_version); |
| + | return CURLE_OK; |
| + | } |
| |
| - | /* Add collecting of headers written to client. For a new connection, |
| - | * we might have done that already, but reuse |
| - | * or multiplex needs it here as well. */ |
| - | result = Curl_headers_init(data); |
| - | if(result) |
| - | goto fail; |
| + | static CURLcode http_add_connection_hd(struct Curl_easy *data, |
| + | struct dynbuf *req) |
| + | { |
| + | char *custom = Curl_checkheaders(data, STRCONST("Connection")); |
| + | char *custom_val = custom ? Curl_copy_header_value(custom) : NULL; |
| + | const char *sep = (custom_val && *custom_val) ? ", " : "Connection: "; |
| + | CURLcode result = CURLE_OK; |
| + | size_t rlen = curlx_dyn_len(req); |
| |
| - | result = http_host(data, conn); |
| - | if(result) |
| - | goto fail; |
| + | if(custom && !custom_val) |
| + | return CURLE_OUT_OF_MEMORY; |
| |
| - | result = http_useragent(data); |
| - | if(result) |
| - | goto fail; |
| + | if(custom_val && *custom_val) |
| + | result = curlx_dyn_addf(req, "Connection: %s", custom_val); |
| + | if(!result && data->state.http_hd_te) { |
| + | result = curlx_dyn_addf(req, "%s%s", sep, "TE"); |
| + | sep = ", "; |
| + | } |
| + | if(!result && data->state.http_hd_upgrade) { |
| + | result = curlx_dyn_addf(req, "%s%s", sep, "Upgrade"); |
| + | sep = ", "; |
| + | } |
| + | if(!result && data->state.http_hd_h2_settings) { |
| + | result = curlx_dyn_addf(req, "%s%s", sep, "HTTP2-Settings"); |
| + | } |
| + | if(rlen < curlx_dyn_len(req)) |
| + | result = curlx_dyn_addn(req, STRCONST("\r\n")); |
| |
| - | Curl_http_method(data, conn, &request, &httpreq); |
| + | free(custom_val); |
| + | return result; |
| + | } |
| |
| - | /* setup the authentication headers */ |
| - | { |
| - | char *pq = NULL; |
| - | if(data->state.up.query) { |
| - | pq = aprintf("%s?%s", data->state.up.path, data->state.up.query); |
| - | if(!pq) |
| - | return CURLE_OUT_OF_MEMORY; |
| - | } |
| - | result = Curl_http_output_auth(data, conn, request, httpreq, |
| - | (pq ? pq : data->state.up.path), FALSE); |
| - | free(pq); |
| - | if(result) |
| - | goto fail; |
| - | } |
| + | /* Header identifier in order we send them by default */ |
| + | typedef enum { |
| + | H1_HD_REQUEST, |
| + | H1_HD_HOST, |
| + | #ifndef CURL_DISABLE_PROXY |
| + | H1_HD_PROXY_AUTH, |
| + | #endif |
| + | H1_HD_USER_AUTH, |
| + | H1_HD_RANGE, |
| + | H1_HD_USER_AGENT, |
| + | H1_HD_ACCEPT, |
| + | H1_HD_TE, |
| + | H1_HD_ACCEPT_ENCODING, |
| + | H1_HD_REFERER, |
| + | #ifndef CURL_DISABLE_PROXY |
| + | H1_HD_PROXY_CONNECTION, |
| + | #endif |
| + | H1_HD_TRANSFER_ENCODING, |
| + | #ifndef CURL_DISABLE_ALTSVC |
| + | H1_HD_ALT_USED, |
| + | #endif |
| + | H1_HD_UPGRADE, |
| + | H1_HD_COOKIES, |
| + | H1_HD_CONDITIONALS, |
| + | H1_HD_CUSTOM, |
| + | H1_HD_CONTENT, |
| + | H1_HD_CONNECTION, |
| + | H1_HD_LAST /* the last, empty header line */ |
| + | } http_hd_t; |
| + | |
| + | static CURLcode http_add_hd(struct Curl_easy *data, |
| + | struct dynbuf *req, |
| + | http_hd_t id, |
| + | unsigned char httpversion, |
| + | const char *method, |
| + | Curl_HttpReq httpreq) |
| + | { |
| + | CURLcode result = CURLE_OK; |
| + | switch(id) { |
| + | case H1_HD_REQUEST: |
| + | /* add the main request stuff */ |
| + | /* GET/HEAD/POST/PUT */ |
| + | result = curlx_dyn_addf(req, "%s ", method); |
| + | if(!result) |
| + | result = http_target(data, req); |
| + | if(!result) |
| + | result = curlx_dyn_addf(req, " HTTP/%s\r\n", |
| + | get_http_string(httpversion)); |
| + | break; |
| |
| - | Curl_safefree(data->state.aptr.ref); |
| - | if(data->state.referer && !Curl_checkheaders(data, STRCONST("Referer"))) { |
| - | data->state.aptr.ref = aprintf("Referer: %s\r\n", data->state.referer); |
| - | if(!data->state.aptr.ref) |
| - | return CURLE_OUT_OF_MEMORY; |
| - | } |
| + | case H1_HD_HOST: |
| + | if(data->state.aptr.host) |
| + | result = curlx_dyn_add(req, data->state.aptr.host); |
| + | break; |
| |
| - | if(!Curl_checkheaders(data, STRCONST("Accept-Encoding")) && |
| - | data->set.str[STRING_ENCODING]) { |
| - | free(data->state.aptr.accept_encoding); |
| - | data->state.aptr.accept_encoding = |
| - | aprintf("Accept-Encoding: %s\r\n", data->set.str[STRING_ENCODING]); |
| - | if(!data->state.aptr.accept_encoding) |
| - | return CURLE_OUT_OF_MEMORY; |
| - | } |
| - | else |
| - | Curl_safefree(data->state.aptr.accept_encoding); |
| + | #ifndef CURL_DISABLE_PROXY |
| + | case H1_HD_PROXY_AUTH: |
| + | if(data->state.aptr.proxyuserpwd) |
| + | result = curlx_dyn_add(req, data->state.aptr.proxyuserpwd); |
| + | break; |
| + | #endif |
| + | |
| + | case H1_HD_USER_AUTH: |
| + | if(data->state.aptr.userpwd) |
| + | result = curlx_dyn_add(req, data->state.aptr.userpwd); |
| + | break; |
| |
| + | case H1_HD_RANGE: |
| + | if(data->state.use_range && data->state.aptr.rangeline) |
| + | result = curlx_dyn_add(req, data->state.aptr.rangeline); |
| + | break; |
| + | |
| + | case H1_HD_USER_AGENT: |
| + | if(data->set.str[STRING_USERAGENT] && /* User-Agent: */ |
| + | *data->set.str[STRING_USERAGENT] && |
| + | data->state.aptr.uagent) |
| + | result = curlx_dyn_add(req, data->state.aptr.uagent); |
| + | break; |
| + | |
| + | case H1_HD_ACCEPT: |
| + | if(!Curl_checkheaders(data, STRCONST("Accept"))) |
| + | result = curlx_dyn_add(req, "Accept: */*\r\n"); |
| + | break; |
| + | |
| + | case H1_HD_TE: |
| #ifdef HAVE_LIBZ |
| - | /* we only consider transfer-encoding magic if libz support is built-in */ |
| - | result = http_transferencode(data); |
| - | if(result) |
| - | goto fail; |
| + | if(!Curl_checkheaders(data, STRCONST("TE")) && |
| + | data->set.http_transfer_encoding) { |
| + | data->state.http_hd_te = TRUE; |
| + | result = curlx_dyn_add(req, "TE: gzip\r\n"); |
| + | } |
| #endif |
| + | break; |
| |
| - | httpversion = http_request_version(data); |
| - | httpstring = get_http_string(httpversion); |
| + | case H1_HD_ACCEPT_ENCODING: |
| + | Curl_safefree(data->state.aptr.accept_encoding); |
| + | if(!Curl_checkheaders(data, STRCONST("Accept-Encoding")) && |
| + | data->set.str[STRING_ENCODING]) |
| + | result = curlx_dyn_addf(req, "Accept-Encoding: %s\r\n", |
| + | data->set.str[STRING_ENCODING]); |
| + | break; |
| |
| - | result = http_req_set_reader(data, httpreq, httpversion, &te); |
| - | if(result) |
| - | goto fail; |
| + | case H1_HD_REFERER: |
| + | Curl_safefree(data->state.aptr.ref); |
| + | if(data->state.referer && !Curl_checkheaders(data, STRCONST("Referer"))) |
| + | result = curlx_dyn_addf(req, "Referer: %s\r\n", data->state.referer); |
| + | break; |
| + | |
| + | #ifndef CURL_DISABLE_PROXY |
| + | case H1_HD_PROXY_CONNECTION: |
| + | if(data->conn->bits.httpproxy && |
| + | !data->conn->bits.tunnel_proxy && |
| + | !Curl_checkheaders(data, STRCONST("Proxy-Connection")) && |
| + | !Curl_checkProxyheaders(data, data->conn, STRCONST("Proxy-Connection"))) |
| + | result = curlx_dyn_add(req, "Proxy-Connection: Keep-Alive\r\n"); |
| + | break; |
| + | #endif |
| |
| - | p_accept = Curl_checkheaders(data, |
| - | STRCONST("Accept")) ? NULL : "Accept: */*\r\n"; |
| + | case H1_HD_TRANSFER_ENCODING: |
| + | result = http_req_set_TE(data, req, httpversion); |
| + | break; |
| |
| - | result = http_range(data, httpreq); |
| - | if(result) |
| - | goto fail; |
| + | #ifndef CURL_DISABLE_ALTSVC |
| + | case H1_HD_ALT_USED: |
| + | if(data->conn->bits.altused && |
| + | !Curl_checkheaders(data, STRCONST("Alt-Used"))) |
| + | result = curlx_dyn_addf(req, "Alt-Used: %s:%d\r\n", |
| + | data->conn->conn_to_host.name, |
| + | data->conn->conn_to_port); |
| + | break; |
| + | #endif |
| + | |
| + | case H1_HD_UPGRADE: |
| + | if(!Curl_conn_is_ssl(data->conn, FIRSTSOCKET) && (httpversion < 20) && |
| + | (data->state.http_neg.wanted & CURL_HTTP_V2x) && |
| + | data->state.http_neg.h2_upgrade) { |
| + | /* append HTTP2 upgrade magic stuff to the HTTP request if it is not done |
| + | over SSL */ |
| + | result = Curl_http2_request_upgrade(req, data); |
| + | } |
| + | #ifndef CURL_DISABLE_WEBSOCKETS |
| + | if(!result && data->conn->handler->protocol&(CURLPROTO_WS|CURLPROTO_WSS)) |
| + | result = Curl_ws_request(data, req); |
| + | #endif |
| + | break; |
| |
| + | case H1_HD_COOKIES: |
| + | result = http_cookies(data, req); |
| + | break; |
| + | |
| + | case H1_HD_CONDITIONALS: |
| + | result = Curl_add_timecondition(data, req); |
| + | break; |
| + | |
| + | case H1_HD_CUSTOM: |
| + | result = Curl_add_custom_headers(data, FALSE, httpversion, req); |
| + | break; |
| + | |
| + | case H1_HD_CONTENT: |
| + | result = http_add_content_hds(data, req, httpversion, httpreq); |
| + | break; |
| + | |
| + | case H1_HD_CONNECTION: { |
| + | result = http_add_connection_hd(data, req); |
| + | break; |
| + | } |
| + | |
| + | case H1_HD_LAST: |
| + | result = curlx_dyn_addn(req, STRCONST("\r\n")); |
| + | break; |
| + | } |
| + | return result; |
| + | } |
| + | |
| + | /* |
| + | * Curl_http() gets called from the generic multi_do() function when an HTTP |
| + | * request is to be performed. This creates and sends a properly constructed |
| + | * HTTP request. |
| + | */ |
| + | CURLcode Curl_http(struct Curl_easy *data, bool *done) |
| + | { |
| + | CURLcode result = CURLE_OK; |
| + | Curl_HttpReq httpreq; |
| + | const char *method; |
| + | struct dynbuf req; |
| + | unsigned char httpversion; |
| + | size_t hd_id; |
| + | |
| + | /* Always consider the DO phase done after this function call, even if there |
| + | may be parts of the request that are not yet sent, since we can deal with |
| + | the rest of the request in the PERFORM phase. */ |
| + | *done = TRUE; |
| /* initialize a dynamic send-buffer */ |
| curlx_dyn_init(&req, DYN_HTTP_REQUEST); |
| - | |
| /* make sure the header buffer is reset - if there are leftovers from a |
| previous transfer */ |
| curlx_dyn_reset(&data->state.headerb); |
| |
| - | /* add the main request stuff */ |
| - | /* GET/HEAD/POST/PUT */ |
| - | result = curlx_dyn_addf(&req, "%s ", request); |
| - | if(!result) |
| - | result = http_target(data, conn, &req); |
| - | if(result) { |
| - | curlx_dyn_free(&req); |
| - | goto fail; |
| + | if(!data->conn->bits.reuse) { |
| + | result = http_check_new_conn(data); |
| + | if(result) |
| + | goto out; |
| } |
| |
| - | #ifndef CURL_DISABLE_ALTSVC |
| - | if(conn->bits.altused && !Curl_checkheaders(data, STRCONST("Alt-Used"))) { |
| - | altused = aprintf("Alt-Used: %s:%d\r\n", |
| - | conn->conn_to_host.name, conn->conn_to_port); |
| - | if(!altused) { |
| - | curlx_dyn_free(&req); |
| - | return CURLE_OUT_OF_MEMORY; |
| - | } |
| - | } |
| - | #endif |
| - | result = |
| - | curlx_dyn_addf(&req, |
| - | " HTTP/%s\r\n" /* HTTP version */ |
| - | "%s" /* host */ |
| - | "%s" /* proxyuserpwd */ |
| - | "%s" /* userpwd */ |
| - | "%s" /* range */ |
| - | "%s" /* user agent */ |
| - | "%s" /* accept */ |
| - | "%s" /* TE: */ |
| - | "%s" /* accept-encoding */ |
| - | "%s" /* referer */ |
| - | "%s" /* Proxy-Connection */ |
| - | "%s" /* transfer-encoding */ |
| - | "%s",/* Alt-Used */ |
| - | |
| - | httpstring, |
| - | (data->state.aptr.host ? data->state.aptr.host : ""), |
| - | #ifndef CURL_DISABLE_PROXY |
| - | data->state.aptr.proxyuserpwd ? |
| - | data->state.aptr.proxyuserpwd : "", |
| - | #else |
| - | "", |
| - | #endif |
| - | data->state.aptr.userpwd ? data->state.aptr.userpwd : "", |
| - | (data->state.use_range && data->state.aptr.rangeline) ? |
| - | data->state.aptr.rangeline : "", |
| - | (data->set.str[STRING_USERAGENT] && |
| - | *data->set.str[STRING_USERAGENT] && |
| - | data->state.aptr.uagent) ? |
| - | data->state.aptr.uagent : "", |
| - | p_accept ? p_accept : "", |
| - | data->state.aptr.te ? data->state.aptr.te : "", |
| - | (data->set.str[STRING_ENCODING] && |
| - | *data->set.str[STRING_ENCODING] && |
| - | data->state.aptr.accept_encoding) ? |
| - | data->state.aptr.accept_encoding : "", |
| - | (data->state.referer && data->state.aptr.ref) ? |
| - | data->state.aptr.ref : "" /* Referer: <data> */, |
| - | #ifndef CURL_DISABLE_PROXY |
| - | (conn->bits.httpproxy && |
| - | !conn->bits.tunnel_proxy && |
| - | !Curl_checkheaders(data, STRCONST("Proxy-Connection")) && |
| - | !Curl_checkProxyheaders(data, conn, |
| - | STRCONST("Proxy-Connection"))) ? |
| - | "Proxy-Connection: Keep-Alive\r\n":"", |
| - | #else |
| - | "", |
| - | #endif |
| - | te, |
| - | altused ? altused : "" |
| - | ); |
| + | /* Add collecting of headers written to client. For a new connection, |
| + | * we might have done that already, but reuse |
| + | * or multiplex needs it here as well. */ |
| + | result = Curl_headers_init(data); |
| + | if(result) |
| + | goto out; |
| |
| - | /* clear userpwd and proxyuserpwd to avoid reusing old credentials |
| - | * from reused connections */ |
| - | Curl_safefree(data->state.aptr.userpwd); |
| - | #ifndef CURL_DISABLE_PROXY |
| - | Curl_safefree(data->state.aptr.proxyuserpwd); |
| - | #endif |
| - | free(altused); |
| + | data->state.http_hd_te = FALSE; |
| + | data->state.http_hd_upgrade = FALSE; |
| + | data->state.http_hd_h2_settings = FALSE; |
| |
| - | if(result) { |
| - | curlx_dyn_free(&req); |
| - | goto fail; |
| - | } |
| + | /* what kind of request do we need to send? */ |
| + | Curl_http_method(data, &method, &httpreq); |
| |
| - | if(!Curl_conn_is_ssl(conn, FIRSTSOCKET) && (httpversion < 20) && |
| - | (data->state.http_neg.wanted & CURL_HTTP_V2x) && |
| - | data->state.http_neg.h2_upgrade) { |
| - | /* append HTTP2 upgrade magic stuff to the HTTP request if it is not done |
| - | over SSL */ |
| - | result = Curl_http2_request_upgrade(&req, data); |
| - | if(result) { |
| - | curlx_dyn_free(&req); |
| - | return result; |
| + | /* select host to send */ |
| + | result = http_set_aptr_host(data); |
| + | if(!result) { |
| + | /* setup the authentication headers, how that method and host are known */ |
| + | char *pq = NULL; |
| + | if(data->state.up.query) { |
| + | pq = aprintf("%s?%s", data->state.up.path, data->state.up.query); |
| + | if(!pq) |
| + | return CURLE_OUT_OF_MEMORY; |
| } |
| + | result = Curl_http_output_auth(data, data->conn, method, httpreq, |
| + | (pq ? pq : data->state.up.path), FALSE); |
| + | free(pq); |
| } |
| + | if(result) |
| + | goto out; |
| |
| - | result = http_cookies(data, conn, &req); |
| - | #ifndef CURL_DISABLE_WEBSOCKETS |
| - | if(!result && conn->handler->protocol&(CURLPROTO_WS|CURLPROTO_WSS)) |
| - | result = Curl_ws_request(data, &req); |
| - | #endif |
| + | result = http_useragent(data); |
| + | if(result) |
| + | goto out; |
| + | |
| + | /* Setup input reader, resume information and ranges */ |
| + | result = set_reader(data, httpreq); |
| if(!result) |
| - | result = Curl_add_timecondition(data, &req); |
| + | result = http_resume(data, httpreq); |
| if(!result) |
| - | result = Curl_add_custom_headers(data, FALSE, httpversion, &req); |
| + | result = http_range(data, httpreq); |
| + | if(result) |
| + | goto out; |
| |
| - | if(!result) { |
| - | /* req_send takes ownership of the 'req' memory on success */ |
| - | result = http_req_complete(data, &req, httpversion, httpreq); |
| - | if(!result) |
| - | result = Curl_req_send(data, &req, httpversion); |
| + | httpversion = http_request_version(data); |
| + | /* Add request line and all headers to `req` */ |
| + | for(hd_id = 0; hd_id <= H1_HD_LAST; ++hd_id) { |
| + | result = http_add_hd(data, &req, (http_hd_t)hd_id, |
| + | httpversion, method, httpreq); |
| + | if(result) |
| + | goto out; |
| } |
| - | curlx_dyn_free(&req); |
| - | if(result) |
| - | goto fail; |
| + | |
| + | /* setup variables for the upcoming transfer and send */ |
| + | Curl_xfer_setup_sendrecv(data, FIRSTSOCKET, -1); |
| + | result = Curl_req_send(data, &req, httpversion); |
| |
| if((httpversion >= 20) && data->req.upload_chunky) |
| /* upload_chunky was set above to set up the request in a chunky fashion, |
| but is disabled here again to avoid that the chunked encoded version is |
| actually used when sending the request body over h2 */ |
| data->req.upload_chunky = FALSE; |
| - | fail: |
| + | |
| + | out: |
| if(CURLE_TOO_LARGE == result) |
| failf(data, "HTTP request too large"); |
| + | |
| + | /* clear userpwd and proxyuserpwd to avoid reusing old credentials |
| + | * from reused connections */ |
| + | Curl_safefree(data->state.aptr.userpwd); |
| + | #ifndef CURL_DISABLE_PROXY |
| + | Curl_safefree(data->state.aptr.proxyuserpwd); |
| + | #endif |
| + | curlx_dyn_free(&req); |
| return result; |
| } |
| |