in libs/curl/lib/http.c [2479:2732]
CURLcode Curl_http(struct Curl_easy *data, bool *done)
{
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 */
/* 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;
switch(conn->alpn) {
case CURL_HTTP_VERSION_3:
DEBUGASSERT(Curl_conn_is_http3(data, conn, FIRSTSOCKET));
break;
case CURL_HTTP_VERSION_2:
#ifndef CURL_DISABLE_PROXY
if(!Curl_conn_is_http2(data, conn, FIRSTSOCKET) &&
conn->bits.proxy && !conn->bits.tunnel_proxy
) {
result = Curl_http2_switch(data, conn, FIRSTSOCKET);
if(result)
goto fail;
}
else
#endif
DEBUGASSERT(Curl_conn_is_http2(data, conn, FIRSTSOCKET));
break;
case CURL_HTTP_VERSION_1_1:
/* continue with HTTP/1.x when explicitly requested */
break;
default:
/* Check if user wants to use HTTP/2 with clear TCP */
if(Curl_http2_may_switch(data, conn, FIRSTSOCKET)) {
DEBUGF(infof(data, "HTTP/2 over clean TCP"));
result = Curl_http2_switch(data, conn, FIRSTSOCKET);
if(result)
goto fail;
}
break;
}
/* 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;
result = Curl_http_host(data, conn);
if(result)
goto fail;
result = Curl_http_useragent(data);
if(result)
goto fail;
Curl_http_method(data, conn, &request, &httpreq);
/* 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;
}
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;
}
if(!Curl_checkheaders(data, STRCONST("Accept-Encoding")) &&
data->set.str[STRING_ENCODING]) {
Curl_safefree(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);
#ifdef HAVE_LIBZ
/* we only consider transfer-encoding magic if libz support is built-in */
result = Curl_transferencode(data);
if(result)
goto fail;
#endif
result = Curl_http_req_set_reader(data, httpreq, &te);
if(result)
goto fail;
p_accept = Curl_checkheaders(data,
STRCONST("Accept"))?NULL:"Accept: */*\r\n";
result = Curl_http_range(data, httpreq);
if(result)
goto fail;
httpstring = get_http_string(data, conn);
/* initialize a dynamic send-buffer */
Curl_dyn_init(&req, DYN_HTTP_REQUEST);
/* make sure the header buffer is reset - if there are leftovers from a
previous transfer */
Curl_dyn_reset(&data->state.headerb);
/* add the main request stuff */
/* GET/HEAD/POST/PUT */
result = Curl_dyn_addf(&req, "%s ", request);
if(!result)
result = Curl_http_target(data, conn, &req);
if(result) {
Curl_dyn_free(&req);
goto fail;
}
#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) {
Curl_dyn_free(&req);
return CURLE_OUT_OF_MEMORY;
}
}
#endif
result =
Curl_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 : ""
);
/* 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);
if(result) {
Curl_dyn_free(&req);
goto fail;
}
if(!(conn->handler->flags&PROTOPT_SSL) &&
conn->httpversion < 20 &&
(data->state.httpwant == CURL_HTTP_VERSION_2)) {
/* 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) {
Curl_dyn_free(&req);
return result;
}
}
result = Curl_http_cookies(data, conn, &req);
#ifdef USE_WEBSOCKETS
if(!result && conn->handler->protocol&(CURLPROTO_WS|CURLPROTO_WSS))
result = Curl_ws_request(data, &req);
#endif
if(!result)
result = Curl_add_timecondition(data, &req);
if(!result)
result = Curl_add_custom_headers(data, FALSE, &req);
if(!result) {
/* req_send takes ownership of the 'req' memory on success */
result = Curl_http_req_complete(data, &req, httpreq);
if(!result)
result = Curl_req_send(data, &req);
}
Curl_dyn_free(&req);
if(result)
goto fail;
if((conn->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:
if(CURLE_TOO_LARGE == result)
failf(data, "HTTP request too large");
return result;
}