1/***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at https://curl.se/docs/copyright.html.
13 *
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 * SPDX-License-Identifier: curl
22 *
23 ***************************************************************************/
24
25#include "curl_setup.h"
26
27#ifndef CURL_DISABLE_HTTP
28
29#ifdef HAVE_NETINET_IN_H
30#include <netinet/in.h>
31#endif
32
33#ifdef HAVE_NETDB_H
34#include <netdb.h>
35#endif
36#ifdef HAVE_ARPA_INET_H
37#include <arpa/inet.h>
38#endif
39#ifdef HAVE_NET_IF_H
40#include <net/if.h>
41#endif
42#ifdef HAVE_SYS_IOCTL_H
43#include <sys/ioctl.h>
44#endif
45
46#ifdef HAVE_SYS_PARAM_H
47#include <sys/param.h>
48#endif
49
50#ifdef USE_HYPER
51#include <hyper.h>
52#endif
53
54#include "urldata.h"
55#include <curl/curl.h>
56#include "transfer.h"
57#include "sendf.h"
58#include "formdata.h"
59#include "mime.h"
60#include "progress.h"
61#include "curl_base64.h"
62#include "cookie.h"
63#include "vauth/vauth.h"
64#include "vtls/vtls.h"
65#include "vquic/vquic.h"
66#include "http_digest.h"
67#include "http_ntlm.h"
68#include "curl_ntlm_wb.h"
69#include "http_negotiate.h"
70#include "http_aws_sigv4.h"
71#include "url.h"
72#include "share.h"
73#include "hostip.h"
74#include "dynhds.h"
75#include "http.h"
76#include "select.h"
77#include "parsedate.h" /* for the week day and month names */
78#include "strtoofft.h"
79#include "multiif.h"
80#include "strcase.h"
81#include "content_encoding.h"
82#include "http_proxy.h"
83#include "warnless.h"
84#include "http2.h"
85#include "cfilters.h"
86#include "connect.h"
87#include "strdup.h"
88#include "altsvc.h"
89#include "hsts.h"
90#include "ws.h"
91#include "c-hyper.h"
92#include "curl_ctype.h"
93
94/* The last 3 #include files should be in this order */
95#include "curl_printf.h"
96#include "curl_memory.h"
97#include "memdebug.h"
98
99/*
100 * Forward declarations.
101 */
102
103static int http_getsock_do(struct Curl_easy *data,
104 struct connectdata *conn,
105 curl_socket_t *socks);
106static bool http_should_fail(struct Curl_easy *data);
107
108static CURLcode http_setup_conn(struct Curl_easy *data,
109 struct connectdata *conn);
110#ifdef USE_WEBSOCKETS
111static CURLcode ws_setup_conn(struct Curl_easy *data,
112 struct connectdata *conn);
113#endif
114
115/*
116 * HTTP handler interface.
117 */
118const struct Curl_handler Curl_handler_http = {
119 "HTTP", /* scheme */
120 http_setup_conn, /* setup_connection */
121 Curl_http, /* do_it */
122 Curl_http_done, /* done */
123 ZERO_NULL, /* do_more */
124 Curl_http_connect, /* connect_it */
125 ZERO_NULL, /* connecting */
126 ZERO_NULL, /* doing */
127 ZERO_NULL, /* proto_getsock */
128 http_getsock_do, /* doing_getsock */
129 ZERO_NULL, /* domore_getsock */
130 ZERO_NULL, /* perform_getsock */
131 ZERO_NULL, /* disconnect */
132 ZERO_NULL, /* readwrite */
133 ZERO_NULL, /* connection_check */
134 ZERO_NULL, /* attach connection */
135 PORT_HTTP, /* defport */
136 CURLPROTO_HTTP, /* protocol */
137 CURLPROTO_HTTP, /* family */
138 PROTOPT_CREDSPERREQUEST | /* flags */
139 PROTOPT_USERPWDCTRL
140};
141
142#ifdef USE_WEBSOCKETS
143const struct Curl_handler Curl_handler_ws = {
144 "WS", /* scheme */
145 ws_setup_conn, /* setup_connection */
146 Curl_http, /* do_it */
147 Curl_http_done, /* done */
148 ZERO_NULL, /* do_more */
149 Curl_http_connect, /* connect_it */
150 ZERO_NULL, /* connecting */
151 ZERO_NULL, /* doing */
152 ZERO_NULL, /* proto_getsock */
153 http_getsock_do, /* doing_getsock */
154 ZERO_NULL, /* domore_getsock */
155 ZERO_NULL, /* perform_getsock */
156 Curl_ws_disconnect, /* disconnect */
157 ZERO_NULL, /* readwrite */
158 ZERO_NULL, /* connection_check */
159 ZERO_NULL, /* attach connection */
160 PORT_HTTP, /* defport */
161 CURLPROTO_WS, /* protocol */
162 CURLPROTO_HTTP, /* family */
163 PROTOPT_CREDSPERREQUEST | /* flags */
164 PROTOPT_USERPWDCTRL
165};
166#endif
167
168#ifdef USE_SSL
169/*
170 * HTTPS handler interface.
171 */
172const struct Curl_handler Curl_handler_https = {
173 "HTTPS", /* scheme */
174 http_setup_conn, /* setup_connection */
175 Curl_http, /* do_it */
176 Curl_http_done, /* done */
177 ZERO_NULL, /* do_more */
178 Curl_http_connect, /* connect_it */
179 NULL, /* connecting */
180 ZERO_NULL, /* doing */
181 NULL, /* proto_getsock */
182 http_getsock_do, /* doing_getsock */
183 ZERO_NULL, /* domore_getsock */
184 ZERO_NULL, /* perform_getsock */
185 ZERO_NULL, /* disconnect */
186 ZERO_NULL, /* readwrite */
187 ZERO_NULL, /* connection_check */
188 ZERO_NULL, /* attach connection */
189 PORT_HTTPS, /* defport */
190 CURLPROTO_HTTPS, /* protocol */
191 CURLPROTO_HTTP, /* family */
192 PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | PROTOPT_ALPN | /* flags */
193 PROTOPT_USERPWDCTRL
194};
195
196#ifdef USE_WEBSOCKETS
197const struct Curl_handler Curl_handler_wss = {
198 "WSS", /* scheme */
199 ws_setup_conn, /* setup_connection */
200 Curl_http, /* do_it */
201 Curl_http_done, /* done */
202 ZERO_NULL, /* do_more */
203 Curl_http_connect, /* connect_it */
204 NULL, /* connecting */
205 ZERO_NULL, /* doing */
206 NULL, /* proto_getsock */
207 http_getsock_do, /* doing_getsock */
208 ZERO_NULL, /* domore_getsock */
209 ZERO_NULL, /* perform_getsock */
210 Curl_ws_disconnect, /* disconnect */
211 ZERO_NULL, /* readwrite */
212 ZERO_NULL, /* connection_check */
213 ZERO_NULL, /* attach connection */
214 PORT_HTTPS, /* defport */
215 CURLPROTO_WSS, /* protocol */
216 CURLPROTO_HTTP, /* family */
217 PROTOPT_SSL | PROTOPT_CREDSPERREQUEST | /* flags */
218 PROTOPT_USERPWDCTRL
219};
220#endif
221
222#endif
223
224static CURLcode http_setup_conn(struct Curl_easy *data,
225 struct connectdata *conn)
226{
227 /* allocate the HTTP-specific struct for the Curl_easy, only to survive
228 during this request */
229 struct HTTP *http;
230 DEBUGASSERT(data->req.p.http == NULL);
231
232 http = calloc(1, sizeof(struct HTTP));
233 if(!http)
234 return CURLE_OUT_OF_MEMORY;
235
236 data->req.p.http = http;
237 connkeep(conn, "HTTP default");
238
239 if(data->state.httpwant == CURL_HTTP_VERSION_3ONLY) {
240 CURLcode result = Curl_conn_may_http3(data, conn);
241 if(result)
242 return result;
243 }
244
245 return CURLE_OK;
246}
247
248#ifdef USE_WEBSOCKETS
249static CURLcode ws_setup_conn(struct Curl_easy *data,
250 struct connectdata *conn)
251{
252 /* websockets is 1.1 only (for now) */
253 data->state.httpwant = CURL_HTTP_VERSION_1_1;
254 return http_setup_conn(data, conn);
255}
256#endif
257
258#ifndef CURL_DISABLE_PROXY
259/*
260 * checkProxyHeaders() checks the linked list of custom proxy headers
261 * if proxy headers are not available, then it will lookup into http header
262 * link list
263 *
264 * It takes a connectdata struct as input to see if this is a proxy request or
265 * not, as it then might check a different header list. Provide the header
266 * prefix without colon!
267 */
268char *Curl_checkProxyheaders(struct Curl_easy *data,
269 const struct connectdata *conn,
270 const char *thisheader,
271 const size_t thislen)
272{
273 struct curl_slist *head;
274
275 for(head = (conn->bits.proxy && data->set.sep_headers) ?
276 data->set.proxyheaders : data->set.headers;
277 head; head = head->next) {
278 if(strncasecompare(head->data, thisheader, thislen) &&
279 Curl_headersep(head->data[thislen]))
280 return head->data;
281 }
282
283 return NULL;
284}
285#else
286/* disabled */
287#define Curl_checkProxyheaders(x,y,z,a) NULL
288#endif
289
290/*
291 * Strip off leading and trailing whitespace from the value in the
292 * given HTTP header line and return a strdupped copy. Returns NULL in
293 * case of allocation failure. Returns an empty string if the header value
294 * consists entirely of whitespace.
295 */
296char *Curl_copy_header_value(const char *header)
297{
298 const char *start;
299 const char *end;
300 char *value;
301 size_t len;
302
303 /* Find the end of the header name */
304 while(*header && (*header != ':'))
305 ++header;
306
307 if(*header)
308 /* Skip over colon */
309 ++header;
310
311 /* Find the first non-space letter */
312 start = header;
313 while(*start && ISSPACE(*start))
314 start++;
315
316 /* data is in the host encoding so
317 use '\r' and '\n' instead of 0x0d and 0x0a */
318 end = strchr(s: start, c: '\r');
319 if(!end)
320 end = strchr(s: start, c: '\n');
321 if(!end)
322 end = strchr(s: start, c: '\0');
323 if(!end)
324 return NULL;
325
326 /* skip all trailing space letters */
327 while((end > start) && ISSPACE(*end))
328 end--;
329
330 /* get length of the type */
331 len = end - start + 1;
332
333 value = malloc(len + 1);
334 if(!value)
335 return NULL;
336
337 memcpy(dest: value, src: start, n: len);
338 value[len] = 0; /* null-terminate */
339
340 return value;
341}
342
343#ifndef CURL_DISABLE_HTTP_AUTH
344
345#ifndef CURL_DISABLE_BASIC_AUTH
346/*
347 * http_output_basic() sets up an Authorization: header (or the proxy version)
348 * for HTTP Basic authentication.
349 *
350 * Returns CURLcode.
351 */
352static CURLcode http_output_basic(struct Curl_easy *data, bool proxy)
353{
354 size_t size = 0;
355 char *authorization = NULL;
356 char **userp;
357 const char *user;
358 const char *pwd;
359 CURLcode result;
360 char *out;
361
362 /* credentials are unique per transfer for HTTP, do not use the ones for the
363 connection */
364 if(proxy) {
365#ifndef CURL_DISABLE_PROXY
366 userp = &data->state.aptr.proxyuserpwd;
367 user = data->state.aptr.proxyuser;
368 pwd = data->state.aptr.proxypasswd;
369#else
370 return CURLE_NOT_BUILT_IN;
371#endif
372 }
373 else {
374 userp = &data->state.aptr.userpwd;
375 user = data->state.aptr.user;
376 pwd = data->state.aptr.passwd;
377 }
378
379 out = aprintf(format: "%s:%s", user ? user : "", pwd ? pwd : "");
380 if(!out)
381 return CURLE_OUT_OF_MEMORY;
382
383 result = Curl_base64_encode(inputbuff: out, insize: strlen(s: out), outptr: &authorization, outlen: &size);
384 if(result)
385 goto fail;
386
387 if(!authorization) {
388 result = CURLE_REMOTE_ACCESS_DENIED;
389 goto fail;
390 }
391
392 free(*userp);
393 *userp = aprintf(format: "%sAuthorization: Basic %s\r\n",
394 proxy ? "Proxy-" : "",
395 authorization);
396 free(authorization);
397 if(!*userp) {
398 result = CURLE_OUT_OF_MEMORY;
399 goto fail;
400 }
401
402fail:
403 free(out);
404 return result;
405}
406
407#endif
408
409#ifndef CURL_DISABLE_BEARER_AUTH
410/*
411 * http_output_bearer() sets up an Authorization: header
412 * for HTTP Bearer authentication.
413 *
414 * Returns CURLcode.
415 */
416static CURLcode http_output_bearer(struct Curl_easy *data)
417{
418 char **userp;
419 CURLcode result = CURLE_OK;
420
421 userp = &data->state.aptr.userpwd;
422 free(*userp);
423 *userp = aprintf(format: "Authorization: Bearer %s\r\n",
424 data->set.str[STRING_BEARER]);
425
426 if(!*userp) {
427 result = CURLE_OUT_OF_MEMORY;
428 goto fail;
429 }
430
431fail:
432 return result;
433}
434
435#endif
436
437#endif
438
439/* pickoneauth() selects the most favourable authentication method from the
440 * ones available and the ones we want.
441 *
442 * return TRUE if one was picked
443 */
444static bool pickoneauth(struct auth *pick, unsigned long mask)
445{
446 bool picked;
447 /* only deal with authentication we want */
448 unsigned long avail = pick->avail & pick->want & mask;
449 picked = TRUE;
450
451 /* The order of these checks is highly relevant, as this will be the order
452 of preference in case of the existence of multiple accepted types. */
453 if(avail & CURLAUTH_NEGOTIATE)
454 pick->picked = CURLAUTH_NEGOTIATE;
455#ifndef CURL_DISABLE_BEARER_AUTH
456 else if(avail & CURLAUTH_BEARER)
457 pick->picked = CURLAUTH_BEARER;
458#endif
459#ifndef CURL_DISABLE_DIGEST_AUTH
460 else if(avail & CURLAUTH_DIGEST)
461 pick->picked = CURLAUTH_DIGEST;
462#endif
463 else if(avail & CURLAUTH_NTLM)
464 pick->picked = CURLAUTH_NTLM;
465 else if(avail & CURLAUTH_NTLM_WB)
466 pick->picked = CURLAUTH_NTLM_WB;
467#ifndef CURL_DISABLE_BASIC_AUTH
468 else if(avail & CURLAUTH_BASIC)
469 pick->picked = CURLAUTH_BASIC;
470#endif
471#ifndef CURL_DISABLE_AWS
472 else if(avail & CURLAUTH_AWS_SIGV4)
473 pick->picked = CURLAUTH_AWS_SIGV4;
474#endif
475 else {
476 pick->picked = CURLAUTH_PICKNONE; /* we select to use nothing */
477 picked = FALSE;
478 }
479 pick->avail = CURLAUTH_NONE; /* clear it here */
480
481 return picked;
482}
483
484/*
485 * http_perhapsrewind()
486 *
487 * If we are doing POST or PUT {
488 * If we have more data to send {
489 * If we are doing NTLM {
490 * Keep sending since we must not disconnect
491 * }
492 * else {
493 * If there is more than just a little data left to send, close
494 * the current connection by force.
495 * }
496 * }
497 * If we have sent any data {
498 * If we don't have track of all the data {
499 * call app to tell it to rewind
500 * }
501 * else {
502 * rewind internally so that the operation can restart fine
503 * }
504 * }
505 * }
506 */
507static CURLcode http_perhapsrewind(struct Curl_easy *data,
508 struct connectdata *conn)
509{
510 struct HTTP *http = data->req.p.http;
511 curl_off_t bytessent;
512 curl_off_t expectsend = -1; /* default is unknown */
513
514 if(!http)
515 /* If this is still NULL, we have not reach very far and we can safely
516 skip this rewinding stuff */
517 return CURLE_OK;
518
519 switch(data->state.httpreq) {
520 case HTTPREQ_GET:
521 case HTTPREQ_HEAD:
522 return CURLE_OK;
523 default:
524 break;
525 }
526
527 bytessent = data->req.writebytecount;
528
529 if(conn->bits.authneg) {
530 /* This is a state where we are known to be negotiating and we don't send
531 any data then. */
532 expectsend = 0;
533 }
534 else if(!conn->bits.protoconnstart) {
535 /* HTTP CONNECT in progress: there is no body */
536 expectsend = 0;
537 }
538 else {
539 /* figure out how much data we are expected to send */
540 switch(data->state.httpreq) {
541 case HTTPREQ_POST:
542 case HTTPREQ_PUT:
543 if(data->state.infilesize != -1)
544 expectsend = data->state.infilesize;
545 break;
546 case HTTPREQ_POST_FORM:
547 case HTTPREQ_POST_MIME:
548 expectsend = http->postsize;
549 break;
550 default:
551 break;
552 }
553 }
554
555 data->state.rewindbeforesend = FALSE; /* default */
556
557 if((expectsend == -1) || (expectsend > bytessent)) {
558#if defined(USE_NTLM)
559 /* There is still data left to send */
560 if((data->state.authproxy.picked == CURLAUTH_NTLM) ||
561 (data->state.authhost.picked == CURLAUTH_NTLM) ||
562 (data->state.authproxy.picked == CURLAUTH_NTLM_WB) ||
563 (data->state.authhost.picked == CURLAUTH_NTLM_WB)) {
564 if(((expectsend - bytessent) < 2000) ||
565 (conn->http_ntlm_state != NTLMSTATE_NONE) ||
566 (conn->proxy_ntlm_state != NTLMSTATE_NONE)) {
567 /* The NTLM-negotiation has started *OR* there is just a little (<2K)
568 data left to send, keep on sending. */
569
570 /* rewind data when completely done sending! */
571 if(!conn->bits.authneg && (conn->writesockfd != CURL_SOCKET_BAD)) {
572 data->state.rewindbeforesend = TRUE;
573 infof(data, "Rewind stream before next send");
574 }
575
576 return CURLE_OK;
577 }
578
579 if(conn->bits.close)
580 /* this is already marked to get closed */
581 return CURLE_OK;
582
583 infof(data, "NTLM send, close instead of sending %"
584 CURL_FORMAT_CURL_OFF_T " bytes",
585 (curl_off_t)(expectsend - bytessent));
586 }
587#endif
588#if defined(USE_SPNEGO)
589 /* There is still data left to send */
590 if((data->state.authproxy.picked == CURLAUTH_NEGOTIATE) ||
591 (data->state.authhost.picked == CURLAUTH_NEGOTIATE)) {
592 if(((expectsend - bytessent) < 2000) ||
593 (conn->http_negotiate_state != GSS_AUTHNONE) ||
594 (conn->proxy_negotiate_state != GSS_AUTHNONE)) {
595 /* The NEGOTIATE-negotiation has started *OR*
596 there is just a little (<2K) data left to send, keep on sending. */
597
598 /* rewind data when completely done sending! */
599 if(!conn->bits.authneg && (conn->writesockfd != CURL_SOCKET_BAD)) {
600 data->state.rewindbeforesend = TRUE;
601 infof(data, "Rewind stream before next send");
602 }
603
604 return CURLE_OK;
605 }
606
607 if(conn->bits.close)
608 /* this is already marked to get closed */
609 return CURLE_OK;
610
611 infof(data, "NEGOTIATE send, close instead of sending %"
612 CURL_FORMAT_CURL_OFF_T " bytes",
613 (curl_off_t)(expectsend - bytessent));
614 }
615#endif
616
617 /* This is not NEGOTIATE/NTLM or many bytes left to send: close */
618 streamclose(conn, "Mid-auth HTTP and much data left to send");
619 data->req.size = 0; /* don't download any more than 0 bytes */
620
621 /* There still is data left to send, but this connection is marked for
622 closure so we can safely do the rewind right now */
623 }
624
625 if(bytessent) {
626 /* mark for rewind since if we already sent something */
627 data->state.rewindbeforesend = TRUE;
628 infof(data, "Please rewind output before next send");
629 }
630
631 return CURLE_OK;
632}
633
634/*
635 * Curl_http_auth_act() gets called when all HTTP headers have been received
636 * and it checks what authentication methods that are available and decides
637 * which one (if any) to use. It will set 'newurl' if an auth method was
638 * picked.
639 */
640
641CURLcode Curl_http_auth_act(struct Curl_easy *data)
642{
643 struct connectdata *conn = data->conn;
644 bool pickhost = FALSE;
645 bool pickproxy = FALSE;
646 CURLcode result = CURLE_OK;
647 unsigned long authmask = ~0ul;
648
649 if(!data->set.str[STRING_BEARER])
650 authmask &= (unsigned long)~CURLAUTH_BEARER;
651
652 if(100 <= data->req.httpcode && data->req.httpcode <= 199)
653 /* this is a transient response code, ignore */
654 return CURLE_OK;
655
656 if(data->state.authproblem)
657 return data->set.http_fail_on_error?CURLE_HTTP_RETURNED_ERROR:CURLE_OK;
658
659 if((data->state.aptr.user || data->set.str[STRING_BEARER]) &&
660 ((data->req.httpcode == 401) ||
661 (conn->bits.authneg && data->req.httpcode < 300))) {
662 pickhost = pickoneauth(pick: &data->state.authhost, mask: authmask);
663 if(!pickhost)
664 data->state.authproblem = TRUE;
665 if(data->state.authhost.picked == CURLAUTH_NTLM &&
666 conn->httpversion > 11) {
667 infof(data, "Forcing HTTP/1.1 for NTLM");
668 connclose(conn, "Force HTTP/1.1 connection");
669 data->state.httpwant = CURL_HTTP_VERSION_1_1;
670 }
671 }
672#ifndef CURL_DISABLE_PROXY
673 if(conn->bits.proxy_user_passwd &&
674 ((data->req.httpcode == 407) ||
675 (conn->bits.authneg && data->req.httpcode < 300))) {
676 pickproxy = pickoneauth(pick: &data->state.authproxy,
677 mask: authmask & ~CURLAUTH_BEARER);
678 if(!pickproxy)
679 data->state.authproblem = TRUE;
680 }
681#endif
682
683 if(pickhost || pickproxy) {
684 if((data->state.httpreq != HTTPREQ_GET) &&
685 (data->state.httpreq != HTTPREQ_HEAD) &&
686 !data->state.rewindbeforesend) {
687 result = http_perhapsrewind(data, conn);
688 if(result)
689 return result;
690 }
691 /* In case this is GSS auth, the newurl field is already allocated so
692 we must make sure to free it before allocating a new one. As figured
693 out in bug #2284386 */
694 Curl_safefree(data->req.newurl);
695 data->req.newurl = strdup(data->state.url); /* clone URL */
696 if(!data->req.newurl)
697 return CURLE_OUT_OF_MEMORY;
698 }
699 else if((data->req.httpcode < 300) &&
700 (!data->state.authhost.done) &&
701 conn->bits.authneg) {
702 /* no (known) authentication available,
703 authentication is not "done" yet and
704 no authentication seems to be required and
705 we didn't try HEAD or GET */
706 if((data->state.httpreq != HTTPREQ_GET) &&
707 (data->state.httpreq != HTTPREQ_HEAD)) {
708 data->req.newurl = strdup(data->state.url); /* clone URL */
709 if(!data->req.newurl)
710 return CURLE_OUT_OF_MEMORY;
711 data->state.authhost.done = TRUE;
712 }
713 }
714 if(http_should_fail(data)) {
715 failf(data, fmt: "The requested URL returned error: %d",
716 data->req.httpcode);
717 result = CURLE_HTTP_RETURNED_ERROR;
718 }
719
720 return result;
721}
722
723#ifndef CURL_DISABLE_HTTP_AUTH
724/*
725 * Output the correct authentication header depending on the auth type
726 * and whether or not it is to a proxy.
727 */
728static CURLcode
729output_auth_headers(struct Curl_easy *data,
730 struct connectdata *conn,
731 struct auth *authstatus,
732 const char *request,
733 const char *path,
734 bool proxy)
735{
736 const char *auth = NULL;
737 CURLcode result = CURLE_OK;
738 (void)conn;
739
740#ifdef CURL_DISABLE_DIGEST_AUTH
741 (void)request;
742 (void)path;
743#endif
744#ifndef CURL_DISABLE_AWS
745 if(authstatus->picked == CURLAUTH_AWS_SIGV4) {
746 auth = "AWS_SIGV4";
747 result = Curl_output_aws_sigv4(data, proxy);
748 if(result)
749 return result;
750 }
751 else
752#endif
753#ifdef USE_SPNEGO
754 if(authstatus->picked == CURLAUTH_NEGOTIATE) {
755 auth = "Negotiate";
756 result = Curl_output_negotiate(data, conn, proxy);
757 if(result)
758 return result;
759 }
760 else
761#endif
762#ifdef USE_NTLM
763 if(authstatus->picked == CURLAUTH_NTLM) {
764 auth = "NTLM";
765 result = Curl_output_ntlm(data, proxy);
766 if(result)
767 return result;
768 }
769 else
770#endif
771#if defined(USE_NTLM) && defined(NTLM_WB_ENABLED)
772 if(authstatus->picked == CURLAUTH_NTLM_WB) {
773 auth = "NTLM_WB";
774 result = Curl_output_ntlm_wb(data, conn, proxy);
775 if(result)
776 return result;
777 }
778 else
779#endif
780#ifndef CURL_DISABLE_DIGEST_AUTH
781 if(authstatus->picked == CURLAUTH_DIGEST) {
782 auth = "Digest";
783 result = Curl_output_digest(data,
784 proxy,
785 request: (const unsigned char *)request,
786 uripath: (const unsigned char *)path);
787 if(result)
788 return result;
789 }
790 else
791#endif
792#ifndef CURL_DISABLE_BASIC_AUTH
793 if(authstatus->picked == CURLAUTH_BASIC) {
794 /* Basic */
795 if(
796#ifndef CURL_DISABLE_PROXY
797 (proxy && conn->bits.proxy_user_passwd &&
798 !Curl_checkProxyheaders(data, conn, STRCONST("Proxy-authorization"))) ||
799#endif
800 (!proxy && data->state.aptr.user &&
801 !Curl_checkheaders(data, STRCONST("Authorization")))) {
802 auth = "Basic";
803 result = http_output_basic(data, proxy);
804 if(result)
805 return result;
806 }
807
808 /* NOTE: this function should set 'done' TRUE, as the other auth
809 functions work that way */
810 authstatus->done = TRUE;
811 }
812#endif
813#ifndef CURL_DISABLE_BEARER_AUTH
814 if(authstatus->picked == CURLAUTH_BEARER) {
815 /* Bearer */
816 if((!proxy && data->set.str[STRING_BEARER] &&
817 !Curl_checkheaders(data, STRCONST("Authorization")))) {
818 auth = "Bearer";
819 result = http_output_bearer(data);
820 if(result)
821 return result;
822 }
823
824 /* NOTE: this function should set 'done' TRUE, as the other auth
825 functions work that way */
826 authstatus->done = TRUE;
827 }
828#endif
829
830 if(auth) {
831#ifndef CURL_DISABLE_PROXY
832 infof(data, "%s auth using %s with user '%s'",
833 proxy ? "Proxy" : "Server", auth,
834 proxy ? (data->state.aptr.proxyuser ?
835 data->state.aptr.proxyuser : "") :
836 (data->state.aptr.user ?
837 data->state.aptr.user : ""));
838#else
839 infof(data, "Server auth using %s with user '%s'",
840 auth, data->state.aptr.user ?
841 data->state.aptr.user : "");
842#endif
843 authstatus->multipass = (!authstatus->done) ? TRUE : FALSE;
844 }
845 else
846 authstatus->multipass = FALSE;
847
848 return CURLE_OK;
849}
850
851/**
852 * Curl_http_output_auth() setups the authentication headers for the
853 * host/proxy and the correct authentication
854 * method. data->state.authdone is set to TRUE when authentication is
855 * done.
856 *
857 * @param conn all information about the current connection
858 * @param request pointer to the request keyword
859 * @param path pointer to the requested path; should include query part
860 * @param proxytunnel boolean if this is the request setting up a "proxy
861 * tunnel"
862 *
863 * @returns CURLcode
864 */
865CURLcode
866Curl_http_output_auth(struct Curl_easy *data,
867 struct connectdata *conn,
868 const char *request,
869 Curl_HttpReq httpreq,
870 const char *path,
871 bool proxytunnel) /* TRUE if this is the request setting
872 up the proxy tunnel */
873{
874 CURLcode result = CURLE_OK;
875 struct auth *authhost;
876 struct auth *authproxy;
877
878 DEBUGASSERT(data);
879
880 authhost = &data->state.authhost;
881 authproxy = &data->state.authproxy;
882
883 if(
884#ifndef CURL_DISABLE_PROXY
885 (conn->bits.httpproxy && conn->bits.proxy_user_passwd) ||
886#endif
887 data->state.aptr.user ||
888#ifdef USE_SPNEGO
889 authhost->want & CURLAUTH_NEGOTIATE ||
890 authproxy->want & CURLAUTH_NEGOTIATE ||
891#endif
892 data->set.str[STRING_BEARER])
893 /* continue please */;
894 else {
895 authhost->done = TRUE;
896 authproxy->done = TRUE;
897 return CURLE_OK; /* no authentication with no user or password */
898 }
899
900 if(authhost->want && !authhost->picked)
901 /* The app has selected one or more methods, but none has been picked
902 so far by a server round-trip. Then we set the picked one to the
903 want one, and if this is one single bit it'll be used instantly. */
904 authhost->picked = authhost->want;
905
906 if(authproxy->want && !authproxy->picked)
907 /* The app has selected one or more methods, but none has been picked so
908 far by a proxy round-trip. Then we set the picked one to the want one,
909 and if this is one single bit it'll be used instantly. */
910 authproxy->picked = authproxy->want;
911
912#ifndef CURL_DISABLE_PROXY
913 /* Send proxy authentication header if needed */
914 if(conn->bits.httpproxy &&
915 (conn->bits.tunnel_proxy == (bit)proxytunnel)) {
916 result = output_auth_headers(data, conn, authstatus: authproxy, request, path, TRUE);
917 if(result)
918 return result;
919 }
920 else
921#else
922 (void)proxytunnel;
923#endif /* CURL_DISABLE_PROXY */
924 /* we have no proxy so let's pretend we're done authenticating
925 with it */
926 authproxy->done = TRUE;
927
928 /* To prevent the user+password to get sent to other than the original host
929 due to a location-follow */
930 if(Curl_auth_allowed_to_host(data)
931#ifndef CURL_DISABLE_NETRC
932 || conn->bits.netrc
933#endif
934 )
935 result = output_auth_headers(data, conn, authstatus: authhost, request, path, FALSE);
936 else
937 authhost->done = TRUE;
938
939 if(((authhost->multipass && !authhost->done) ||
940 (authproxy->multipass && !authproxy->done)) &&
941 (httpreq != HTTPREQ_GET) &&
942 (httpreq != HTTPREQ_HEAD)) {
943 /* Auth is required and we are not authenticated yet. Make a PUT or POST
944 with content-length zero as a "probe". */
945 conn->bits.authneg = TRUE;
946 }
947 else
948 conn->bits.authneg = FALSE;
949
950 return result;
951}
952
953#else
954/* when disabled */
955CURLcode
956Curl_http_output_auth(struct Curl_easy *data,
957 struct connectdata *conn,
958 const char *request,
959 Curl_HttpReq httpreq,
960 const char *path,
961 bool proxytunnel)
962{
963 (void)data;
964 (void)conn;
965 (void)request;
966 (void)httpreq;
967 (void)path;
968 (void)proxytunnel;
969 return CURLE_OK;
970}
971#endif
972
973/*
974 * Curl_http_input_auth() deals with Proxy-Authenticate: and WWW-Authenticate:
975 * headers. They are dealt with both in the transfer.c main loop and in the
976 * proxy CONNECT loop.
977 */
978
979static int is_valid_auth_separator(char ch)
980{
981 return ch == '\0' || ch == ',' || ISSPACE(ch);
982}
983
984CURLcode Curl_http_input_auth(struct Curl_easy *data, bool proxy,
985 const char *auth) /* the first non-space */
986{
987 /*
988 * This resource requires authentication
989 */
990 struct connectdata *conn = data->conn;
991#ifdef USE_SPNEGO
992 curlnegotiate *negstate = proxy ? &conn->proxy_negotiate_state :
993 &conn->http_negotiate_state;
994#endif
995 unsigned long *availp;
996 struct auth *authp;
997
998 (void) conn; /* In case conditionals make it unused. */
999
1000 if(proxy) {
1001 availp = &data->info.proxyauthavail;
1002 authp = &data->state.authproxy;
1003 }
1004 else {
1005 availp = &data->info.httpauthavail;
1006 authp = &data->state.authhost;
1007 }
1008
1009 /*
1010 * Here we check if we want the specific single authentication (using ==) and
1011 * if we do, we initiate usage of it.
1012 *
1013 * If the provided authentication is wanted as one out of several accepted
1014 * types (using &), we OR this authentication type to the authavail
1015 * variable.
1016 *
1017 * Note:
1018 *
1019 * ->picked is first set to the 'want' value (one or more bits) before the
1020 * request is sent, and then it is again set _after_ all response 401/407
1021 * headers have been received but then only to a single preferred method
1022 * (bit).
1023 */
1024
1025 while(*auth) {
1026#ifdef USE_SPNEGO
1027 if(checkprefix("Negotiate", auth) && is_valid_auth_separator(auth[9])) {
1028 if((authp->avail & CURLAUTH_NEGOTIATE) ||
1029 Curl_auth_is_spnego_supported()) {
1030 *availp |= CURLAUTH_NEGOTIATE;
1031 authp->avail |= CURLAUTH_NEGOTIATE;
1032
1033 if(authp->picked == CURLAUTH_NEGOTIATE) {
1034 CURLcode result = Curl_input_negotiate(data, conn, proxy, auth);
1035 if(!result) {
1036 free(data->req.newurl);
1037 data->req.newurl = strdup(data->state.url);
1038 if(!data->req.newurl)
1039 return CURLE_OUT_OF_MEMORY;
1040 data->state.authproblem = FALSE;
1041 /* we received a GSS auth token and we dealt with it fine */
1042 *negstate = GSS_AUTHRECV;
1043 }
1044 else
1045 data->state.authproblem = TRUE;
1046 }
1047 }
1048 }
1049 else
1050#endif
1051#ifdef USE_NTLM
1052 /* NTLM support requires the SSL crypto libs */
1053 if(checkprefix("NTLM", auth) && is_valid_auth_separator(ch: auth[4])) {
1054 if((authp->avail & CURLAUTH_NTLM) ||
1055 (authp->avail & CURLAUTH_NTLM_WB) ||
1056 Curl_auth_is_ntlm_supported()) {
1057 *availp |= CURLAUTH_NTLM;
1058 authp->avail |= CURLAUTH_NTLM;
1059
1060 if(authp->picked == CURLAUTH_NTLM ||
1061 authp->picked == CURLAUTH_NTLM_WB) {
1062 /* NTLM authentication is picked and activated */
1063 CURLcode result = Curl_input_ntlm(data, proxy, header: auth);
1064 if(!result) {
1065 data->state.authproblem = FALSE;
1066#ifdef NTLM_WB_ENABLED
1067 if(authp->picked == CURLAUTH_NTLM_WB) {
1068 *availp &= ~CURLAUTH_NTLM;
1069 authp->avail &= ~CURLAUTH_NTLM;
1070 *availp |= CURLAUTH_NTLM_WB;
1071 authp->avail |= CURLAUTH_NTLM_WB;
1072
1073 result = Curl_input_ntlm_wb(data, conn, proxy, auth);
1074 if(result) {
1075 infof(data, "Authentication problem. Ignoring this.");
1076 data->state.authproblem = TRUE;
1077 }
1078 }
1079#endif
1080 }
1081 else {
1082 infof(data, "Authentication problem. Ignoring this.");
1083 data->state.authproblem = TRUE;
1084 }
1085 }
1086 }
1087 }
1088 else
1089#endif
1090#ifndef CURL_DISABLE_DIGEST_AUTH
1091 if(checkprefix("Digest", auth) && is_valid_auth_separator(ch: auth[6])) {
1092 if((authp->avail & CURLAUTH_DIGEST) != 0)
1093 infof(data, "Ignoring duplicate digest auth header.");
1094 else if(Curl_auth_is_digest_supported()) {
1095 CURLcode result;
1096
1097 *availp |= CURLAUTH_DIGEST;
1098 authp->avail |= CURLAUTH_DIGEST;
1099
1100 /* We call this function on input Digest headers even if Digest
1101 * authentication isn't activated yet, as we need to store the
1102 * incoming data from this header in case we are going to use
1103 * Digest */
1104 result = Curl_input_digest(data, proxy, header: auth);
1105 if(result) {
1106 infof(data, "Authentication problem. Ignoring this.");
1107 data->state.authproblem = TRUE;
1108 }
1109 }
1110 }
1111 else
1112#endif
1113#ifndef CURL_DISABLE_BASIC_AUTH
1114 if(checkprefix("Basic", auth) &&
1115 is_valid_auth_separator(ch: auth[5])) {
1116 *availp |= CURLAUTH_BASIC;
1117 authp->avail |= CURLAUTH_BASIC;
1118 if(authp->picked == CURLAUTH_BASIC) {
1119 /* We asked for Basic authentication but got a 40X back
1120 anyway, which basically means our name+password isn't
1121 valid. */
1122 authp->avail = CURLAUTH_NONE;
1123 infof(data, "Authentication problem. Ignoring this.");
1124 data->state.authproblem = TRUE;
1125 }
1126 }
1127 else
1128#endif
1129#ifndef CURL_DISABLE_BEARER_AUTH
1130 if(checkprefix("Bearer", auth) &&
1131 is_valid_auth_separator(ch: auth[6])) {
1132 *availp |= CURLAUTH_BEARER;
1133 authp->avail |= CURLAUTH_BEARER;
1134 if(authp->picked == CURLAUTH_BEARER) {
1135 /* We asked for Bearer authentication but got a 40X back
1136 anyway, which basically means our token isn't valid. */
1137 authp->avail = CURLAUTH_NONE;
1138 infof(data, "Authentication problem. Ignoring this.");
1139 data->state.authproblem = TRUE;
1140 }
1141 }
1142#else
1143 ;
1144#endif
1145
1146 /* there may be multiple methods on one line, so keep reading */
1147 while(*auth && *auth != ',') /* read up to the next comma */
1148 auth++;
1149 if(*auth == ',') /* if we're on a comma, skip it */
1150 auth++;
1151 while(*auth && ISSPACE(*auth))
1152 auth++;
1153 }
1154
1155 return CURLE_OK;
1156}
1157
1158/**
1159 * http_should_fail() determines whether an HTTP response has gotten us
1160 * into an error state or not.
1161 *
1162 * @retval FALSE communications should continue
1163 *
1164 * @retval TRUE communications should not continue
1165 */
1166static bool http_should_fail(struct Curl_easy *data)
1167{
1168 int httpcode;
1169 DEBUGASSERT(data);
1170 DEBUGASSERT(data->conn);
1171
1172 httpcode = data->req.httpcode;
1173
1174 /*
1175 ** If we haven't been asked to fail on error,
1176 ** don't fail.
1177 */
1178 if(!data->set.http_fail_on_error)
1179 return FALSE;
1180
1181 /*
1182 ** Any code < 400 is never terminal.
1183 */
1184 if(httpcode < 400)
1185 return FALSE;
1186
1187 /*
1188 ** A 416 response to a resume request is presumably because the file is
1189 ** already completely downloaded and thus not actually a fail.
1190 */
1191 if(data->state.resume_from && data->state.httpreq == HTTPREQ_GET &&
1192 httpcode == 416)
1193 return FALSE;
1194
1195 /*
1196 ** Any code >= 400 that's not 401 or 407 is always
1197 ** a terminal error
1198 */
1199 if((httpcode != 401) && (httpcode != 407))
1200 return TRUE;
1201
1202 /*
1203 ** All we have left to deal with is 401 and 407
1204 */
1205 DEBUGASSERT((httpcode == 401) || (httpcode == 407));
1206
1207 /*
1208 ** Examine the current authentication state to see if this
1209 ** is an error. The idea is for this function to get
1210 ** called after processing all the headers in a response
1211 ** message. So, if we've been to asked to authenticate a
1212 ** particular stage, and we've done it, we're OK. But, if
1213 ** we're already completely authenticated, it's not OK to
1214 ** get another 401 or 407.
1215 **
1216 ** It is possible for authentication to go stale such that
1217 ** the client needs to reauthenticate. Once that info is
1218 ** available, use it here.
1219 */
1220
1221 /*
1222 ** Either we're not authenticating, or we're supposed to
1223 ** be authenticating something else. This is an error.
1224 */
1225 if((httpcode == 401) && !data->state.aptr.user)
1226 return TRUE;
1227#ifndef CURL_DISABLE_PROXY
1228 if((httpcode == 407) && !data->conn->bits.proxy_user_passwd)
1229 return TRUE;
1230#endif
1231
1232 return data->state.authproblem;
1233}
1234
1235/*
1236 * readmoredata() is a "fread() emulation" to provide POST and/or request
1237 * data. It is used when a huge POST is to be made and the entire chunk wasn't
1238 * sent in the first send(). This function will then be called from the
1239 * transfer.c loop when more data is to be sent to the peer.
1240 *
1241 * Returns the amount of bytes it filled the buffer with.
1242 */
1243static size_t readmoredata(char *buffer,
1244 size_t size,
1245 size_t nitems,
1246 void *userp)
1247{
1248 struct HTTP *http = (struct HTTP *)userp;
1249 struct Curl_easy *data = http->backup.data;
1250 size_t fullsize = size * nitems;
1251
1252 if(!http->postsize)
1253 /* nothing to return */
1254 return 0;
1255
1256 /* make sure that an HTTP request is never sent away chunked! */
1257 data->req.forbidchunk = (http->sending == HTTPSEND_REQUEST)?TRUE:FALSE;
1258
1259 if(data->set.max_send_speed &&
1260 (data->set.max_send_speed < (curl_off_t)fullsize) &&
1261 (data->set.max_send_speed < http->postsize))
1262 /* speed limit */
1263 fullsize = (size_t)data->set.max_send_speed;
1264
1265 else if(http->postsize <= (curl_off_t)fullsize) {
1266 memcpy(dest: buffer, src: http->postdata, n: (size_t)http->postsize);
1267 fullsize = (size_t)http->postsize;
1268
1269 if(http->backup.postsize) {
1270 /* move backup data into focus and continue on that */
1271 http->postdata = http->backup.postdata;
1272 http->postsize = http->backup.postsize;
1273 data->state.fread_func = http->backup.fread_func;
1274 data->state.in = http->backup.fread_in;
1275
1276 http->sending++; /* move one step up */
1277
1278 http->backup.postsize = 0;
1279 }
1280 else
1281 http->postsize = 0;
1282
1283 return fullsize;
1284 }
1285
1286 memcpy(dest: buffer, src: http->postdata, n: fullsize);
1287 http->postdata += fullsize;
1288 http->postsize -= fullsize;
1289
1290 return fullsize;
1291}
1292
1293/*
1294 * Curl_buffer_send() sends a header buffer and frees all associated
1295 * memory. Body data may be appended to the header data if desired.
1296 *
1297 * Returns CURLcode
1298 */
1299CURLcode Curl_buffer_send(struct dynbuf *in,
1300 struct Curl_easy *data,
1301 struct HTTP *http,
1302 /* add the number of sent bytes to this
1303 counter */
1304 curl_off_t *bytes_written,
1305 /* how much of the buffer contains body data */
1306 curl_off_t included_body_bytes,
1307 int sockindex)
1308{
1309 ssize_t amount;
1310 CURLcode result;
1311 char *ptr;
1312 size_t size;
1313 struct connectdata *conn = data->conn;
1314 size_t sendsize;
1315 size_t headersize;
1316
1317 DEBUGASSERT(sockindex <= SECONDARYSOCKET && sockindex >= 0);
1318
1319 /* The looping below is required since we use non-blocking sockets, but due
1320 to the circumstances we will just loop and try again and again etc */
1321
1322 ptr = Curl_dyn_ptr(s: in);
1323 size = Curl_dyn_len(s: in);
1324
1325 headersize = size - (size_t)included_body_bytes; /* the initial part that
1326 isn't body is header */
1327
1328 DEBUGASSERT(size > (size_t)included_body_bytes);
1329
1330 if((conn->handler->flags & PROTOPT_SSL
1331#ifndef CURL_DISABLE_PROXY
1332 || IS_HTTPS_PROXY(conn->http_proxy.proxytype)
1333#endif
1334 )
1335 && conn->httpversion < 20) {
1336 /* Make sure this doesn't send more body bytes than what the max send
1337 speed says. The request bytes do not count to the max speed.
1338 */
1339 if(data->set.max_send_speed &&
1340 (included_body_bytes > data->set.max_send_speed)) {
1341 curl_off_t overflow = included_body_bytes - data->set.max_send_speed;
1342 DEBUGASSERT((size_t)overflow < size);
1343 sendsize = size - (size_t)overflow;
1344 }
1345 else
1346 sendsize = size;
1347
1348 /* OpenSSL is very picky and we must send the SAME buffer pointer to the
1349 library when we attempt to re-send this buffer. Sending the same data
1350 is not enough, we must use the exact same address. For this reason, we
1351 must copy the data to the uploadbuffer first, since that is the buffer
1352 we will be using if this send is retried later.
1353 */
1354 result = Curl_get_upload_buffer(data);
1355 if(result) {
1356 /* malloc failed, free memory and return to the caller */
1357 Curl_dyn_free(s: in);
1358 return result;
1359 }
1360 /* We never send more than upload_buffer_size bytes in one single chunk
1361 when we speak HTTPS, as if only a fraction of it is sent now, this data
1362 needs to fit into the normal read-callback buffer later on and that
1363 buffer is using this size.
1364 */
1365 if(sendsize > (size_t)data->set.upload_buffer_size)
1366 sendsize = (size_t)data->set.upload_buffer_size;
1367
1368 memcpy(dest: data->state.ulbuf, src: ptr, n: sendsize);
1369 ptr = data->state.ulbuf;
1370 }
1371 else {
1372#ifdef CURLDEBUG
1373 /* Allow debug builds to override this logic to force short initial
1374 sends
1375 */
1376 char *p = getenv("CURL_SMALLREQSEND");
1377 if(p) {
1378 size_t altsize = (size_t)strtoul(p, NULL, 10);
1379 if(altsize)
1380 sendsize = CURLMIN(size, altsize);
1381 else
1382 sendsize = size;
1383 }
1384 else
1385#endif
1386 {
1387 /* Make sure this doesn't send more body bytes than what the max send
1388 speed says. The request bytes do not count to the max speed.
1389 */
1390 if(data->set.max_send_speed &&
1391 (included_body_bytes > data->set.max_send_speed)) {
1392 curl_off_t overflow = included_body_bytes - data->set.max_send_speed;
1393 DEBUGASSERT((size_t)overflow < size);
1394 sendsize = size - (size_t)overflow;
1395 }
1396 else
1397 sendsize = size;
1398 }
1399
1400 /* We currently cannot send more that this for http here:
1401 * - if sending blocks, it return 0 as amount
1402 * - we then whisk aside the `in` into the `http` struct
1403 * and install our own `data->state.fread_func` that
1404 * on subsequent calls reads `in` empty.
1405 * - when the whisked away `in` is empty, the `fread_func`
1406 * is restored ot its original state.
1407 * The problem is that `fread_func` can only return
1408 * `upload_buffer_size` lengths. If the send we do here
1409 * is larger and blocks, we do re-sending with smaller
1410 * amounts of data and connection filters do not like
1411 * that.
1412 */
1413 if(http && (sendsize > (size_t)data->set.upload_buffer_size))
1414 sendsize = (size_t)data->set.upload_buffer_size;
1415 }
1416
1417 result = Curl_nwrite(data, sockindex, buf: ptr, blen: sendsize, pnwritten: &amount);
1418
1419 if(!result) {
1420 /*
1421 * Note that we may not send the entire chunk at once, and we have a set
1422 * number of data bytes at the end of the big buffer (out of which we may
1423 * only send away a part).
1424 */
1425 /* how much of the header that was sent */
1426 size_t headlen = (size_t)amount>headersize ? headersize : (size_t)amount;
1427 size_t bodylen = amount - headlen;
1428
1429 /* this data _may_ contain binary stuff */
1430 Curl_debug(data, type: CURLINFO_HEADER_OUT, ptr, size: headlen);
1431 if(bodylen)
1432 /* there was body data sent beyond the initial header part, pass that on
1433 to the debug callback too */
1434 Curl_debug(data, type: CURLINFO_DATA_OUT, ptr: ptr + headlen, size: bodylen);
1435
1436 /* 'amount' can never be a very large value here so typecasting it so a
1437 signed 31 bit value should not cause problems even if ssize_t is
1438 64bit */
1439 *bytes_written += (long)amount;
1440
1441 if(http) {
1442 /* if we sent a piece of the body here, up the byte counter for it
1443 accordingly */
1444 data->req.writebytecount += bodylen;
1445 Curl_pgrsSetUploadCounter(data, size: data->req.writebytecount);
1446
1447 if((size_t)amount != size) {
1448 /* The whole request could not be sent in one system call. We must
1449 queue it up and send it later when we get the chance. We must not
1450 loop here and wait until it might work again. */
1451
1452 size -= amount;
1453
1454 ptr = Curl_dyn_ptr(s: in) + amount;
1455
1456 /* backup the currently set pointers */
1457 http->backup.fread_func = data->state.fread_func;
1458 http->backup.fread_in = data->state.in;
1459 http->backup.postdata = http->postdata;
1460 http->backup.postsize = http->postsize;
1461 http->backup.data = data;
1462
1463 /* set the new pointers for the request-sending */
1464 data->state.fread_func = (curl_read_callback)readmoredata;
1465 data->state.in = (void *)http;
1466 http->postdata = ptr;
1467 http->postsize = (curl_off_t)size;
1468
1469 /* this much data is remaining header: */
1470 data->req.pendingheader = headersize - headlen;
1471
1472 http->send_buffer = *in; /* copy the whole struct */
1473 http->sending = HTTPSEND_REQUEST;
1474 return CURLE_OK;
1475 }
1476 http->sending = HTTPSEND_BODY;
1477 /* the full buffer was sent, clean up and return */
1478 }
1479 else {
1480 if((size_t)amount != size)
1481 /* We have no continue-send mechanism now, fail. This can only happen
1482 when this function is used from the CONNECT sending function. We
1483 currently (stupidly) assume that the whole request is always sent
1484 away in the first single chunk.
1485
1486 This needs FIXing.
1487 */
1488 return CURLE_SEND_ERROR;
1489 }
1490 }
1491 Curl_dyn_free(s: in);
1492
1493 /* no remaining header data */
1494 data->req.pendingheader = 0;
1495 return result;
1496}
1497
1498/* end of the add_buffer functions */
1499/* ------------------------------------------------------------------------- */
1500
1501
1502
1503/*
1504 * Curl_compareheader()
1505 *
1506 * Returns TRUE if 'headerline' contains the 'header' with given 'content'.
1507 * Pass headers WITH the colon.
1508 */
1509bool
1510Curl_compareheader(const char *headerline, /* line to check */
1511 const char *header, /* header keyword _with_ colon */
1512 const size_t hlen, /* len of the keyword in bytes */
1513 const char *content, /* content string to find */
1514 const size_t clen) /* len of the content in bytes */
1515{
1516 /* RFC2616, section 4.2 says: "Each header field consists of a name followed
1517 * by a colon (":") and the field value. Field names are case-insensitive.
1518 * The field value MAY be preceded by any amount of LWS, though a single SP
1519 * is preferred." */
1520
1521 size_t len;
1522 const char *start;
1523 const char *end;
1524 DEBUGASSERT(hlen);
1525 DEBUGASSERT(clen);
1526 DEBUGASSERT(header);
1527 DEBUGASSERT(content);
1528
1529 if(!strncasecompare(headerline, header, hlen))
1530 return FALSE; /* doesn't start with header */
1531
1532 /* pass the header */
1533 start = &headerline[hlen];
1534
1535 /* pass all whitespace */
1536 while(*start && ISSPACE(*start))
1537 start++;
1538
1539 /* find the end of the header line */
1540 end = strchr(s: start, c: '\r'); /* lines end with CRLF */
1541 if(!end) {
1542 /* in case there's a non-standard compliant line here */
1543 end = strchr(s: start, c: '\n');
1544
1545 if(!end)
1546 /* hm, there's no line ending here, use the zero byte! */
1547 end = strchr(s: start, c: '\0');
1548 }
1549
1550 len = end-start; /* length of the content part of the input line */
1551
1552 /* find the content string in the rest of the line */
1553 for(; len >= clen; len--, start++) {
1554 if(strncasecompare(start, content, clen))
1555 return TRUE; /* match! */
1556 }
1557
1558 return FALSE; /* no match */
1559}
1560
1561/*
1562 * Curl_http_connect() performs HTTP stuff to do at connect-time, called from
1563 * the generic Curl_connect().
1564 */
1565CURLcode Curl_http_connect(struct Curl_easy *data, bool *done)
1566{
1567 struct connectdata *conn = data->conn;
1568
1569 /* We default to persistent connections. We set this already in this connect
1570 function to make the reuse checks properly be able to check this bit. */
1571 connkeep(conn, "HTTP default");
1572
1573 return Curl_conn_connect(data, FIRSTSOCKET, FALSE, done);
1574}
1575
1576/* this returns the socket to wait for in the DO and DOING state for the multi
1577 interface and then we're always _sending_ a request and thus we wait for
1578 the single socket to become writable only */
1579static int http_getsock_do(struct Curl_easy *data,
1580 struct connectdata *conn,
1581 curl_socket_t *socks)
1582{
1583 /* write mode */
1584 (void)conn;
1585 socks[0] = Curl_conn_get_socket(data, FIRSTSOCKET);
1586 return GETSOCK_WRITESOCK(0);
1587}
1588
1589/*
1590 * Curl_http_done() gets called after a single HTTP request has been
1591 * performed.
1592 */
1593
1594CURLcode Curl_http_done(struct Curl_easy *data,
1595 CURLcode status, bool premature)
1596{
1597 struct connectdata *conn = data->conn;
1598 struct HTTP *http = data->req.p.http;
1599
1600 /* Clear multipass flag. If authentication isn't done yet, then it will get
1601 * a chance to be set back to true when we output the next auth header */
1602 data->state.authhost.multipass = FALSE;
1603 data->state.authproxy.multipass = FALSE;
1604
1605 /* set the proper values (possibly modified on POST) */
1606 conn->seek_func = data->set.seek_func; /* restore */
1607 conn->seek_client = data->set.seek_client; /* restore */
1608
1609 if(!http)
1610 return CURLE_OK;
1611
1612 Curl_dyn_free(s: &http->send_buffer);
1613 Curl_dyn_reset(s: &data->state.headerb);
1614 Curl_hyper_done(data);
1615 Curl_ws_done(data);
1616
1617 if(status)
1618 return status;
1619
1620 if(!premature && /* this check is pointless when DONE is called before the
1621 entire operation is complete */
1622 !conn->bits.retry &&
1623 !data->set.connect_only &&
1624 (data->req.bytecount +
1625 data->req.headerbytecount -
1626 data->req.deductheadercount) <= 0) {
1627 /* If this connection isn't simply closed to be retried, AND nothing was
1628 read from the HTTP server (that counts), this can't be right so we
1629 return an error here */
1630 failf(data, fmt: "Empty reply from server");
1631 /* Mark it as closed to avoid the "left intact" message */
1632 streamclose(conn, "Empty reply from server");
1633 return CURLE_GOT_NOTHING;
1634 }
1635
1636 return CURLE_OK;
1637}
1638
1639/*
1640 * Determine if we should use HTTP 1.1 (OR BETTER) for this request. Reasons
1641 * to avoid it include:
1642 *
1643 * - if the user specifically requested HTTP 1.0
1644 * - if the server we are connected to only supports 1.0
1645 * - if any server previously contacted to handle this request only supports
1646 * 1.0.
1647 */
1648bool Curl_use_http_1_1plus(const struct Curl_easy *data,
1649 const struct connectdata *conn)
1650{
1651 if((data->state.httpversion == 10) || (conn->httpversion == 10))
1652 return FALSE;
1653 if((data->state.httpwant == CURL_HTTP_VERSION_1_0) &&
1654 (conn->httpversion <= 10))
1655 return FALSE;
1656 return ((data->state.httpwant == CURL_HTTP_VERSION_NONE) ||
1657 (data->state.httpwant >= CURL_HTTP_VERSION_1_1));
1658}
1659
1660#ifndef USE_HYPER
1661static const char *get_http_string(const struct Curl_easy *data,
1662 const struct connectdata *conn)
1663{
1664 if(Curl_conn_is_http3(data, conn, FIRSTSOCKET))
1665 return "3";
1666 if(Curl_conn_is_http2(data, conn, FIRSTSOCKET))
1667 return "2";
1668 if(Curl_use_http_1_1plus(data, conn))
1669 return "1.1";
1670
1671 return "1.0";
1672}
1673#endif
1674
1675/* check and possibly add an Expect: header */
1676static CURLcode expect100(struct Curl_easy *data,
1677 struct connectdata *conn,
1678 struct dynbuf *req)
1679{
1680 CURLcode result = CURLE_OK;
1681 data->state.expect100header = FALSE; /* default to false unless it is set
1682 to TRUE below */
1683 if(!data->state.disableexpect && Curl_use_http_1_1plus(data, conn) &&
1684 (conn->httpversion < 20)) {
1685 /* if not doing HTTP 1.0 or version 2, or disabled explicitly, we add an
1686 Expect: 100-continue to the headers which actually speeds up post
1687 operations (as there is one packet coming back from the web server) */
1688 const char *ptr = Curl_checkheaders(data, STRCONST("Expect"));
1689 if(ptr) {
1690 data->state.expect100header =
1691 Curl_compareheader(headerline: ptr, STRCONST("Expect:"), STRCONST("100-continue"));
1692 }
1693 else {
1694 result = Curl_dyn_addn(s: req, STRCONST("Expect: 100-continue\r\n"));
1695 if(!result)
1696 data->state.expect100header = TRUE;
1697 }
1698 }
1699
1700 return result;
1701}
1702
1703enum proxy_use {
1704 HEADER_SERVER, /* direct to server */
1705 HEADER_PROXY, /* regular request to proxy */
1706 HEADER_CONNECT /* sending CONNECT to a proxy */
1707};
1708
1709/* used to compile the provided trailers into one buffer
1710 will return an error code if one of the headers is
1711 not formatted correctly */
1712CURLcode Curl_http_compile_trailers(struct curl_slist *trailers,
1713 struct dynbuf *b,
1714 struct Curl_easy *handle)
1715{
1716 char *ptr = NULL;
1717 CURLcode result = CURLE_OK;
1718 const char *endofline_native = NULL;
1719 const char *endofline_network = NULL;
1720
1721 if(
1722#ifdef CURL_DO_LINEEND_CONV
1723 (handle->state.prefer_ascii) ||
1724#endif
1725 (handle->set.crlf)) {
1726 /* \n will become \r\n later on */
1727 endofline_native = "\n";
1728 endofline_network = "\x0a";
1729 }
1730 else {
1731 endofline_native = "\r\n";
1732 endofline_network = "\x0d\x0a";
1733 }
1734
1735 while(trailers) {
1736 /* only add correctly formatted trailers */
1737 ptr = strchr(s: trailers->data, c: ':');
1738 if(ptr && *(ptr + 1) == ' ') {
1739 result = Curl_dyn_add(s: b, str: trailers->data);
1740 if(result)
1741 return result;
1742 result = Curl_dyn_add(s: b, str: endofline_native);
1743 if(result)
1744 return result;
1745 }
1746 else
1747 infof(handle, "Malformatted trailing header, skipping trailer");
1748 trailers = trailers->next;
1749 }
1750 result = Curl_dyn_add(s: b, str: endofline_network);
1751 return result;
1752}
1753
1754static bool hd_name_eq(const char *n1, size_t n1len,
1755 const char *n2, size_t n2len)
1756{
1757 if(n1len == n2len) {
1758 return strncasecompare(n1, n2, n1len);
1759 }
1760 return FALSE;
1761}
1762
1763CURLcode Curl_dynhds_add_custom(struct Curl_easy *data,
1764 bool is_connect,
1765 struct dynhds *hds)
1766{
1767 struct connectdata *conn = data->conn;
1768 char *ptr;
1769 struct curl_slist *h[2];
1770 struct curl_slist *headers;
1771 int numlists = 1; /* by default */
1772 int i;
1773
1774#ifndef CURL_DISABLE_PROXY
1775 enum proxy_use proxy;
1776
1777 if(is_connect)
1778 proxy = HEADER_CONNECT;
1779 else
1780 proxy = conn->bits.httpproxy && !conn->bits.tunnel_proxy?
1781 HEADER_PROXY:HEADER_SERVER;
1782
1783 switch(proxy) {
1784 case HEADER_SERVER:
1785 h[0] = data->set.headers;
1786 break;
1787 case HEADER_PROXY:
1788 h[0] = data->set.headers;
1789 if(data->set.sep_headers) {
1790 h[1] = data->set.proxyheaders;
1791 numlists++;
1792 }
1793 break;
1794 case HEADER_CONNECT:
1795 if(data->set.sep_headers)
1796 h[0] = data->set.proxyheaders;
1797 else
1798 h[0] = data->set.headers;
1799 break;
1800 }
1801#else
1802 (void)is_connect;
1803 h[0] = data->set.headers;
1804#endif
1805
1806 /* loop through one or two lists */
1807 for(i = 0; i < numlists; i++) {
1808 for(headers = h[i]; headers; headers = headers->next) {
1809 const char *name, *value;
1810 size_t namelen, valuelen;
1811
1812 /* There are 2 quirks in place for custom headers:
1813 * 1. setting only 'name:' to suppress a header from being sent
1814 * 2. setting only 'name;' to send an empty (illegal) header
1815 */
1816 ptr = strchr(s: headers->data, c: ':');
1817 if(ptr) {
1818 name = headers->data;
1819 namelen = ptr - headers->data;
1820 ptr++; /* pass the colon */
1821 while(*ptr && ISSPACE(*ptr))
1822 ptr++;
1823 if(*ptr) {
1824 value = ptr;
1825 valuelen = strlen(s: value);
1826 }
1827 else {
1828 /* quirk #1, suppress this header */
1829 continue;
1830 }
1831 }
1832 else {
1833 ptr = strchr(s: headers->data, c: ';');
1834
1835 if(!ptr) {
1836 /* neither : nor ; in provided header value. We seem
1837 * to ignore this silently */
1838 continue;
1839 }
1840
1841 name = headers->data;
1842 namelen = ptr - headers->data;
1843 ptr++; /* pass the semicolon */
1844 while(*ptr && ISSPACE(*ptr))
1845 ptr++;
1846 if(!*ptr) {
1847 /* quirk #2, send an empty header */
1848 value = "";
1849 valuelen = 0;
1850 }
1851 else {
1852 /* this may be used for something else in the future,
1853 * ignore this for now */
1854 continue;
1855 }
1856 }
1857
1858 DEBUGASSERT(name && value);
1859 if(data->state.aptr.host &&
1860 /* a Host: header was sent already, don't pass on any custom Host:
1861 header as that will produce *two* in the same request! */
1862 hd_name_eq(n1: name, n1len: namelen, STRCONST("Host:")))
1863 ;
1864 else if(data->state.httpreq == HTTPREQ_POST_FORM &&
1865 /* this header (extended by formdata.c) is sent later */
1866 hd_name_eq(n1: name, n1len: namelen, STRCONST("Content-Type:")))
1867 ;
1868 else if(data->state.httpreq == HTTPREQ_POST_MIME &&
1869 /* this header is sent later */
1870 hd_name_eq(n1: name, n1len: namelen, STRCONST("Content-Type:")))
1871 ;
1872 else if(conn->bits.authneg &&
1873 /* while doing auth neg, don't allow the custom length since
1874 we will force length zero then */
1875 hd_name_eq(n1: name, n1len: namelen, STRCONST("Content-Length:")))
1876 ;
1877 else if(data->state.aptr.te &&
1878 /* when asking for Transfer-Encoding, don't pass on a custom
1879 Connection: */
1880 hd_name_eq(n1: name, n1len: namelen, STRCONST("Connection:")))
1881 ;
1882 else if((conn->httpversion >= 20) &&
1883 hd_name_eq(n1: name, n1len: namelen, STRCONST("Transfer-Encoding:")))
1884 /* HTTP/2 doesn't support chunked requests */
1885 ;
1886 else if((hd_name_eq(n1: name, n1len: namelen, STRCONST("Authorization:")) ||
1887 hd_name_eq(n1: name, n1len: namelen, STRCONST("Cookie:"))) &&
1888 /* be careful of sending this potentially sensitive header to
1889 other hosts */
1890 !Curl_auth_allowed_to_host(data))
1891 ;
1892 else {
1893 CURLcode result;
1894
1895 result = Curl_dynhds_add(dynhds: hds, name, namelen, value, valuelen);
1896 if(result)
1897 return result;
1898 }
1899 }
1900 }
1901
1902 return CURLE_OK;
1903}
1904
1905CURLcode Curl_add_custom_headers(struct Curl_easy *data,
1906 bool is_connect,
1907#ifndef USE_HYPER
1908 struct dynbuf *req
1909#else
1910 void *req
1911#endif
1912 )
1913{
1914 struct connectdata *conn = data->conn;
1915 char *ptr;
1916 struct curl_slist *h[2];
1917 struct curl_slist *headers;
1918 int numlists = 1; /* by default */
1919 int i;
1920
1921#ifndef CURL_DISABLE_PROXY
1922 enum proxy_use proxy;
1923
1924 if(is_connect)
1925 proxy = HEADER_CONNECT;
1926 else
1927 proxy = conn->bits.httpproxy && !conn->bits.tunnel_proxy?
1928 HEADER_PROXY:HEADER_SERVER;
1929
1930 switch(proxy) {
1931 case HEADER_SERVER:
1932 h[0] = data->set.headers;
1933 break;
1934 case HEADER_PROXY:
1935 h[0] = data->set.headers;
1936 if(data->set.sep_headers) {
1937 h[1] = data->set.proxyheaders;
1938 numlists++;
1939 }
1940 break;
1941 case HEADER_CONNECT:
1942 if(data->set.sep_headers)
1943 h[0] = data->set.proxyheaders;
1944 else
1945 h[0] = data->set.headers;
1946 break;
1947 }
1948#else
1949 (void)is_connect;
1950 h[0] = data->set.headers;
1951#endif
1952
1953 /* loop through one or two lists */
1954 for(i = 0; i < numlists; i++) {
1955 headers = h[i];
1956
1957 while(headers) {
1958 char *semicolonp = NULL;
1959 ptr = strchr(s: headers->data, c: ':');
1960 if(!ptr) {
1961 char *optr;
1962 /* no colon, semicolon? */
1963 ptr = strchr(s: headers->data, c: ';');
1964 if(ptr) {
1965 optr = ptr;
1966 ptr++; /* pass the semicolon */
1967 while(*ptr && ISSPACE(*ptr))
1968 ptr++;
1969
1970 if(*ptr) {
1971 /* this may be used for something else in the future */
1972 optr = NULL;
1973 }
1974 else {
1975 if(*(--ptr) == ';') {
1976 /* copy the source */
1977 semicolonp = strdup(headers->data);
1978 if(!semicolonp) {
1979#ifndef USE_HYPER
1980 Curl_dyn_free(s: req);
1981#endif
1982 return CURLE_OUT_OF_MEMORY;
1983 }
1984 /* put a colon where the semicolon is */
1985 semicolonp[ptr - headers->data] = ':';
1986 /* point at the colon */
1987 optr = &semicolonp [ptr - headers->data];
1988 }
1989 }
1990 ptr = optr;
1991 }
1992 }
1993 if(ptr && (ptr != headers->data)) {
1994 /* we require a colon for this to be a true header */
1995
1996 ptr++; /* pass the colon */
1997 while(*ptr && ISSPACE(*ptr))
1998 ptr++;
1999
2000 if(*ptr || semicolonp) {
2001 /* only send this if the contents was non-blank or done special */
2002 CURLcode result = CURLE_OK;
2003 char *compare = semicolonp ? semicolonp : headers->data;
2004
2005 if(data->state.aptr.host &&
2006 /* a Host: header was sent already, don't pass on any custom Host:
2007 header as that will produce *two* in the same request! */
2008 checkprefix("Host:", compare))
2009 ;
2010 else if(data->state.httpreq == HTTPREQ_POST_FORM &&
2011 /* this header (extended by formdata.c) is sent later */
2012 checkprefix("Content-Type:", compare))
2013 ;
2014 else if(data->state.httpreq == HTTPREQ_POST_MIME &&
2015 /* this header is sent later */
2016 checkprefix("Content-Type:", compare))
2017 ;
2018 else if(conn->bits.authneg &&
2019 /* while doing auth neg, don't allow the custom length since
2020 we will force length zero then */
2021 checkprefix("Content-Length:", compare))
2022 ;
2023 else if(data->state.aptr.te &&
2024 /* when asking for Transfer-Encoding, don't pass on a custom
2025 Connection: */
2026 checkprefix("Connection:", compare))
2027 ;
2028 else if((conn->httpversion >= 20) &&
2029 checkprefix("Transfer-Encoding:", compare))
2030 /* HTTP/2 doesn't support chunked requests */
2031 ;
2032 else if((checkprefix("Authorization:", compare) ||
2033 checkprefix("Cookie:", compare)) &&
2034 /* be careful of sending this potentially sensitive header to
2035 other hosts */
2036 !Curl_auth_allowed_to_host(data))
2037 ;
2038 else {
2039#ifdef USE_HYPER
2040 result = Curl_hyper_header(data, req, compare);
2041#else
2042 result = Curl_dyn_addf(s: req, fmt: "%s\r\n", compare);
2043#endif
2044 }
2045 if(semicolonp)
2046 free(semicolonp);
2047 if(result)
2048 return result;
2049 }
2050 }
2051 headers = headers->next;
2052 }
2053 }
2054
2055 return CURLE_OK;
2056}
2057
2058#ifndef CURL_DISABLE_PARSEDATE
2059CURLcode Curl_add_timecondition(struct Curl_easy *data,
2060#ifndef USE_HYPER
2061 struct dynbuf *req
2062#else
2063 void *req
2064#endif
2065 )
2066{
2067 const struct tm *tm;
2068 struct tm keeptime;
2069 CURLcode result;
2070 char datestr[80];
2071 const char *condp;
2072 size_t len;
2073
2074 if(data->set.timecondition == CURL_TIMECOND_NONE)
2075 /* no condition was asked for */
2076 return CURLE_OK;
2077
2078 result = Curl_gmtime(intime: data->set.timevalue, store: &keeptime);
2079 if(result) {
2080 failf(data, fmt: "Invalid TIMEVALUE");
2081 return result;
2082 }
2083 tm = &keeptime;
2084
2085 switch(data->set.timecondition) {
2086 default:
2087 return CURLE_BAD_FUNCTION_ARGUMENT;
2088
2089 case CURL_TIMECOND_IFMODSINCE:
2090 condp = "If-Modified-Since";
2091 len = 17;
2092 break;
2093 case CURL_TIMECOND_IFUNMODSINCE:
2094 condp = "If-Unmodified-Since";
2095 len = 19;
2096 break;
2097 case CURL_TIMECOND_LASTMOD:
2098 condp = "Last-Modified";
2099 len = 13;
2100 break;
2101 }
2102
2103 if(Curl_checkheaders(data, thisheader: condp, thislen: len)) {
2104 /* A custom header was specified; it will be sent instead. */
2105 return CURLE_OK;
2106 }
2107
2108 /* The If-Modified-Since header family should have their times set in
2109 * GMT as RFC2616 defines: "All HTTP date/time stamps MUST be
2110 * represented in Greenwich Mean Time (GMT), without exception. For the
2111 * purposes of HTTP, GMT is exactly equal to UTC (Coordinated Universal
2112 * Time)." (see page 20 of RFC2616).
2113 */
2114
2115 /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */
2116 msnprintf(buffer: datestr, maxlength: sizeof(datestr),
2117 format: "%s: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n",
2118 condp,
2119 Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
2120 tm->tm_mday,
2121 Curl_month[tm->tm_mon],
2122 tm->tm_year + 1900,
2123 tm->tm_hour,
2124 tm->tm_min,
2125 tm->tm_sec);
2126
2127#ifndef USE_HYPER
2128 result = Curl_dyn_add(s: req, str: datestr);
2129#else
2130 result = Curl_hyper_header(data, req, datestr);
2131#endif
2132
2133 return result;
2134}
2135#else
2136/* disabled */
2137CURLcode Curl_add_timecondition(struct Curl_easy *data,
2138 struct dynbuf *req)
2139{
2140 (void)data;
2141 (void)req;
2142 return CURLE_OK;
2143}
2144#endif
2145
2146void Curl_http_method(struct Curl_easy *data, struct connectdata *conn,
2147 const char **method, Curl_HttpReq *reqp)
2148{
2149 Curl_HttpReq httpreq = (Curl_HttpReq)data->state.httpreq;
2150 const char *request;
2151 if((conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_FTP)) &&
2152 data->state.upload)
2153 httpreq = HTTPREQ_PUT;
2154
2155 /* Now set the 'request' pointer to the proper request string */
2156 if(data->set.str[STRING_CUSTOMREQUEST])
2157 request = data->set.str[STRING_CUSTOMREQUEST];
2158 else {
2159 if(data->req.no_body)
2160 request = "HEAD";
2161 else {
2162 DEBUGASSERT((httpreq >= HTTPREQ_GET) && (httpreq <= HTTPREQ_HEAD));
2163 switch(httpreq) {
2164 case HTTPREQ_POST:
2165 case HTTPREQ_POST_FORM:
2166 case HTTPREQ_POST_MIME:
2167 request = "POST";
2168 break;
2169 case HTTPREQ_PUT:
2170 request = "PUT";
2171 break;
2172 default: /* this should never happen */
2173 case HTTPREQ_GET:
2174 request = "GET";
2175 break;
2176 case HTTPREQ_HEAD:
2177 request = "HEAD";
2178 break;
2179 }
2180 }
2181 }
2182 *method = request;
2183 *reqp = httpreq;
2184}
2185
2186CURLcode Curl_http_useragent(struct Curl_easy *data)
2187{
2188 /* The User-Agent string might have been allocated in url.c already, because
2189 it might have been used in the proxy connect, but if we have got a header
2190 with the user-agent string specified, we erase the previously made string
2191 here. */
2192 if(Curl_checkheaders(data, STRCONST("User-Agent"))) {
2193 free(data->state.aptr.uagent);
2194 data->state.aptr.uagent = NULL;
2195 }
2196 return CURLE_OK;
2197}
2198
2199
2200CURLcode Curl_http_host(struct Curl_easy *data, struct connectdata *conn)
2201{
2202 const char *ptr;
2203 struct dynamically_allocated_data *aptr = &data->state.aptr;
2204 if(!data->state.this_is_a_follow) {
2205 /* Free to avoid leaking memory on multiple requests */
2206 free(data->state.first_host);
2207
2208 data->state.first_host = strdup(conn->host.name);
2209 if(!data->state.first_host)
2210 return CURLE_OUT_OF_MEMORY;
2211
2212 data->state.first_remote_port = conn->remote_port;
2213 data->state.first_remote_protocol = conn->handler->protocol;
2214 }
2215 Curl_safefree(aptr->host);
2216
2217 ptr = Curl_checkheaders(data, STRCONST("Host"));
2218 if(ptr && (!data->state.this_is_a_follow ||
2219 strcasecompare(data->state.first_host, conn->host.name))) {
2220#if !defined(CURL_DISABLE_COOKIES)
2221 /* If we have a given custom Host: header, we extract the host name in
2222 order to possibly use it for cookie reasons later on. We only allow the
2223 custom Host: header if this is NOT a redirect, as setting Host: in the
2224 redirected request is being out on thin ice. Except if the host name
2225 is the same as the first one! */
2226 char *cookiehost = Curl_copy_header_value(header: ptr);
2227 if(!cookiehost)
2228 return CURLE_OUT_OF_MEMORY;
2229 if(!*cookiehost)
2230 /* ignore empty data */
2231 free(cookiehost);
2232 else {
2233 /* If the host begins with '[', we start searching for the port after
2234 the bracket has been closed */
2235 if(*cookiehost == '[') {
2236 char *closingbracket;
2237 /* since the 'cookiehost' is an allocated memory area that will be
2238 freed later we cannot simply increment the pointer */
2239 memmove(dest: cookiehost, src: cookiehost + 1, n: strlen(s: cookiehost) - 1);
2240 closingbracket = strchr(s: cookiehost, c: ']');
2241 if(closingbracket)
2242 *closingbracket = 0;
2243 }
2244 else {
2245 int startsearch = 0;
2246 char *colon = strchr(s: cookiehost + startsearch, c: ':');
2247 if(colon)
2248 *colon = 0; /* The host must not include an embedded port number */
2249 }
2250 Curl_safefree(aptr->cookiehost);
2251 aptr->cookiehost = cookiehost;
2252 }
2253#endif
2254
2255 if(strcmp(s1: "Host:", s2: ptr)) {
2256 aptr->host = aprintf(format: "Host:%s\r\n", &ptr[5]);
2257 if(!aptr->host)
2258 return CURLE_OUT_OF_MEMORY;
2259 }
2260 }
2261 else {
2262 /* When building Host: headers, we must put the host name within
2263 [brackets] if the host name is a plain IPv6-address. RFC2732-style. */
2264 const char *host = conn->host.name;
2265
2266 if(((conn->given->protocol&(CURLPROTO_HTTPS|CURLPROTO_WSS)) &&
2267 (conn->remote_port == PORT_HTTPS)) ||
2268 ((conn->given->protocol&(CURLPROTO_HTTP|CURLPROTO_WS)) &&
2269 (conn->remote_port == PORT_HTTP)) )
2270 /* if(HTTPS on port 443) OR (HTTP on port 80) then don't include
2271 the port number in the host string */
2272 aptr->host = aprintf(format: "Host: %s%s%s\r\n", conn->bits.ipv6_ip?"[":"",
2273 host, conn->bits.ipv6_ip?"]":"");
2274 else
2275 aptr->host = aprintf(format: "Host: %s%s%s:%d\r\n", conn->bits.ipv6_ip?"[":"",
2276 host, conn->bits.ipv6_ip?"]":"",
2277 conn->remote_port);
2278
2279 if(!aptr->host)
2280 /* without Host: we can't make a nice request */
2281 return CURLE_OUT_OF_MEMORY;
2282 }
2283 return CURLE_OK;
2284}
2285
2286/*
2287 * Append the request-target to the HTTP request
2288 */
2289CURLcode Curl_http_target(struct Curl_easy *data,
2290 struct connectdata *conn,
2291 struct dynbuf *r)
2292{
2293 CURLcode result = CURLE_OK;
2294 const char *path = data->state.up.path;
2295 const char *query = data->state.up.query;
2296
2297 if(data->set.str[STRING_TARGET]) {
2298 path = data->set.str[STRING_TARGET];
2299 query = NULL;
2300 }
2301
2302#ifndef CURL_DISABLE_PROXY
2303 if(conn->bits.httpproxy && !conn->bits.tunnel_proxy) {
2304 /* Using a proxy but does not tunnel through it */
2305
2306 /* The path sent to the proxy is in fact the entire URL. But if the remote
2307 host is a IDN-name, we must make sure that the request we produce only
2308 uses the encoded host name! */
2309
2310 /* and no fragment part */
2311 CURLUcode uc;
2312 char *url;
2313 CURLU *h = curl_url_dup(in: data->state.uh);
2314 if(!h)
2315 return CURLE_OUT_OF_MEMORY;
2316
2317 if(conn->host.dispname != conn->host.name) {
2318 uc = curl_url_set(handle: h, what: CURLUPART_HOST, part: conn->host.name, flags: 0);
2319 if(uc) {
2320 curl_url_cleanup(handle: h);
2321 return CURLE_OUT_OF_MEMORY;
2322 }
2323 }
2324 uc = curl_url_set(handle: h, what: CURLUPART_FRAGMENT, NULL, flags: 0);
2325 if(uc) {
2326 curl_url_cleanup(handle: h);
2327 return CURLE_OUT_OF_MEMORY;
2328 }
2329
2330 if(strcasecompare("http", data->state.up.scheme)) {
2331 /* when getting HTTP, we don't want the userinfo the URL */
2332 uc = curl_url_set(handle: h, what: CURLUPART_USER, NULL, flags: 0);
2333 if(uc) {
2334 curl_url_cleanup(handle: h);
2335 return CURLE_OUT_OF_MEMORY;
2336 }
2337 uc = curl_url_set(handle: h, what: CURLUPART_PASSWORD, NULL, flags: 0);
2338 if(uc) {
2339 curl_url_cleanup(handle: h);
2340 return CURLE_OUT_OF_MEMORY;
2341 }
2342 }
2343 /* Extract the URL to use in the request. Store in STRING_TEMP_URL for
2344 clean-up reasons if the function returns before the free() further
2345 down. */
2346 uc = curl_url_get(handle: h, what: CURLUPART_URL, part: &url, CURLU_NO_DEFAULT_PORT);
2347 if(uc) {
2348 curl_url_cleanup(handle: h);
2349 return CURLE_OUT_OF_MEMORY;
2350 }
2351
2352 curl_url_cleanup(handle: h);
2353
2354 /* target or url */
2355 result = Curl_dyn_add(s: r, str: data->set.str[STRING_TARGET]?
2356 data->set.str[STRING_TARGET]:url);
2357 free(url);
2358 if(result)
2359 return (result);
2360
2361 if(strcasecompare("ftp", data->state.up.scheme)) {
2362 if(data->set.proxy_transfer_mode) {
2363 /* when doing ftp, append ;type=<a|i> if not present */
2364 char *type = strstr(haystack: path, needle: ";type=");
2365 if(type && type[6] && type[7] == 0) {
2366 switch(Curl_raw_toupper(in: type[6])) {
2367 case 'A':
2368 case 'D':
2369 case 'I':
2370 break;
2371 default:
2372 type = NULL;
2373 }
2374 }
2375 if(!type) {
2376 result = Curl_dyn_addf(s: r, fmt: ";type=%c",
2377 data->state.prefer_ascii ? 'a' : 'i');
2378 if(result)
2379 return result;
2380 }
2381 }
2382 }
2383 }
2384
2385 else
2386#else
2387 (void)conn; /* not used in disabled-proxy builds */
2388#endif
2389 {
2390 result = Curl_dyn_add(s: r, str: path);
2391 if(result)
2392 return result;
2393 if(query)
2394 result = Curl_dyn_addf(s: r, fmt: "?%s", query);
2395 }
2396
2397 return result;
2398}
2399
2400CURLcode Curl_http_body(struct Curl_easy *data, struct connectdata *conn,
2401 Curl_HttpReq httpreq, const char **tep)
2402{
2403 CURLcode result = CURLE_OK;
2404 const char *ptr;
2405 struct HTTP *http = data->req.p.http;
2406 http->postsize = 0;
2407
2408 switch(httpreq) {
2409 case HTTPREQ_POST_MIME:
2410 data->state.mimepost = &data->set.mimepost;
2411 break;
2412#ifndef CURL_DISABLE_FORM_API
2413 case HTTPREQ_POST_FORM:
2414 /* Convert the form structure into a mime structure, then keep
2415 the conversion */
2416 if(!data->state.formp) {
2417 data->state.formp = calloc(sizeof(curl_mimepart), 1);
2418 if(!data->state.formp)
2419 return CURLE_OUT_OF_MEMORY;
2420 Curl_mime_cleanpart(part: data->state.formp);
2421 result = Curl_getformdata(data, data->state.formp, post: data->set.httppost,
2422 fread_func: data->state.fread_func);
2423 if(result)
2424 return result;
2425 data->state.mimepost = data->state.formp;
2426 }
2427 break;
2428#endif
2429 default:
2430 data->state.mimepost = NULL;
2431 }
2432
2433#ifndef CURL_DISABLE_MIME
2434 if(data->state.mimepost) {
2435 const char *cthdr = Curl_checkheaders(data, STRCONST("Content-Type"));
2436
2437 /* Read and seek body only. */
2438 data->state.mimepost->flags |= MIME_BODY_ONLY;
2439
2440 /* Prepare the mime structure headers & set content type. */
2441
2442 if(cthdr)
2443 for(cthdr += 13; *cthdr == ' '; cthdr++)
2444 ;
2445 else if(data->state.mimepost->kind == MIMEKIND_MULTIPART)
2446 cthdr = "multipart/form-data";
2447
2448 curl_mime_headers(part: data->state.mimepost, headers: data->set.headers, take_ownership: 0);
2449 result = Curl_mime_prepare_headers(data, part: data->state.mimepost, contenttype: cthdr,
2450 NULL, strategy: MIMESTRATEGY_FORM);
2451 curl_mime_headers(part: data->state.mimepost, NULL, take_ownership: 0);
2452 if(!result)
2453 result = Curl_mime_rewind(part: data->state.mimepost);
2454 if(result)
2455 return result;
2456 http->postsize = Curl_mime_size(part: data->state.mimepost);
2457 }
2458#endif
2459
2460 ptr = Curl_checkheaders(data, STRCONST("Transfer-Encoding"));
2461 if(ptr) {
2462 /* Some kind of TE is requested, check if 'chunked' is chosen */
2463 data->req.upload_chunky =
2464 Curl_compareheader(headerline: ptr,
2465 STRCONST("Transfer-Encoding:"), STRCONST("chunked"));
2466 }
2467 else {
2468 if((conn->handler->protocol & PROTO_FAMILY_HTTP) &&
2469 (((httpreq == HTTPREQ_POST_MIME || httpreq == HTTPREQ_POST_FORM) &&
2470 http->postsize < 0) ||
2471 ((data->state.upload || httpreq == HTTPREQ_POST) &&
2472 data->state.infilesize == -1))) {
2473 if(conn->bits.authneg)
2474 /* don't enable chunked during auth neg */
2475 ;
2476 else if(Curl_use_http_1_1plus(data, conn)) {
2477 if(conn->httpversion < 20)
2478 /* HTTP, upload, unknown file size and not HTTP 1.0 */
2479 data->req.upload_chunky = TRUE;
2480 }
2481 else {
2482 failf(data, fmt: "Chunky upload is not supported by HTTP 1.0");
2483 return CURLE_UPLOAD_FAILED;
2484 }
2485 }
2486 else {
2487 /* else, no chunky upload */
2488 data->req.upload_chunky = FALSE;
2489 }
2490
2491 if(data->req.upload_chunky)
2492 *tep = "Transfer-Encoding: chunked\r\n";
2493 }
2494 return result;
2495}
2496
2497CURLcode Curl_http_bodysend(struct Curl_easy *data, struct connectdata *conn,
2498 struct dynbuf *r, Curl_HttpReq httpreq)
2499{
2500#ifndef USE_HYPER
2501 /* Hyper always handles the body separately */
2502 curl_off_t included_body = 0;
2503#else
2504 /* from this point down, this function should not be used */
2505#define Curl_buffer_send(a,b,c,d,e,f) CURLE_OK
2506#endif
2507 CURLcode result = CURLE_OK;
2508 struct HTTP *http = data->req.p.http;
2509 const char *ptr;
2510
2511 /* If 'authdone' is FALSE, we must not set the write socket index to the
2512 Curl_transfer() call below, as we're not ready to actually upload any
2513 data yet. */
2514
2515 switch(httpreq) {
2516
2517 case HTTPREQ_PUT: /* Let's PUT the data to the server! */
2518
2519 if(conn->bits.authneg)
2520 http->postsize = 0;
2521 else
2522 http->postsize = data->state.infilesize;
2523
2524 if((http->postsize != -1) && !data->req.upload_chunky &&
2525 (conn->bits.authneg ||
2526 !Curl_checkheaders(data, STRCONST("Content-Length")))) {
2527 /* only add Content-Length if not uploading chunked */
2528 result = Curl_dyn_addf(s: r, fmt: "Content-Length: %" CURL_FORMAT_CURL_OFF_T
2529 "\r\n", http->postsize);
2530 if(result)
2531 return result;
2532 }
2533
2534 /* For really small puts we don't use Expect: headers at all, and for
2535 the somewhat bigger ones we allow the app to disable it. Just make
2536 sure that the expect100header is always set to the preferred value
2537 here. */
2538 ptr = Curl_checkheaders(data, STRCONST("Expect"));
2539 if(ptr) {
2540 data->state.expect100header =
2541 Curl_compareheader(headerline: ptr, STRCONST("Expect:"), STRCONST("100-continue"));
2542 }
2543 else if(http->postsize > EXPECT_100_THRESHOLD || http->postsize < 0) {
2544 result = expect100(data, conn, req: r);
2545 if(result)
2546 return result;
2547 }
2548
2549 /* end of headers */
2550 result = Curl_dyn_addn(s: r, STRCONST("\r\n"));
2551 if(result)
2552 return result;
2553
2554 /* set the upload size to the progress meter */
2555 Curl_pgrsSetUploadSize(data, size: http->postsize);
2556
2557 /* this sends the buffer and frees all the buffer resources */
2558 result = Curl_buffer_send(in: r, data, http: data->req.p.http,
2559 bytes_written: &data->info.request_size, included_body_bytes: 0,
2560 FIRSTSOCKET);
2561 if(result)
2562 failf(data, fmt: "Failed sending PUT request");
2563 else
2564 /* prepare for transfer */
2565 Curl_setup_transfer(data, FIRSTSOCKET, size: -1, TRUE,
2566 writesockindex: http->postsize?FIRSTSOCKET:-1);
2567 if(result)
2568 return result;
2569 break;
2570
2571 case HTTPREQ_POST_FORM:
2572 case HTTPREQ_POST_MIME:
2573 /* This is form posting using mime data. */
2574 if(conn->bits.authneg) {
2575 /* nothing to post! */
2576 result = Curl_dyn_addn(s: r, STRCONST("Content-Length: 0\r\n\r\n"));
2577 if(result)
2578 return result;
2579
2580 result = Curl_buffer_send(in: r, data, http: data->req.p.http,
2581 bytes_written: &data->info.request_size, included_body_bytes: 0,
2582 FIRSTSOCKET);
2583 if(result)
2584 failf(data, fmt: "Failed sending POST request");
2585 else
2586 /* setup variables for the upcoming transfer */
2587 Curl_setup_transfer(data, FIRSTSOCKET, size: -1, TRUE, writesockindex: -1);
2588 break;
2589 }
2590
2591 data->state.infilesize = http->postsize;
2592
2593 /* We only set Content-Length and allow a custom Content-Length if
2594 we don't upload data chunked, as RFC2616 forbids us to set both
2595 kinds of headers (Transfer-Encoding: chunked and Content-Length) */
2596 if(http->postsize != -1 && !data->req.upload_chunky &&
2597 (!Curl_checkheaders(data, STRCONST("Content-Length")))) {
2598 /* we allow replacing this header if not during auth negotiation,
2599 although it isn't very wise to actually set your own */
2600 result = Curl_dyn_addf(s: r,
2601 fmt: "Content-Length: %" CURL_FORMAT_CURL_OFF_T
2602 "\r\n", http->postsize);
2603 if(result)
2604 return result;
2605 }
2606
2607#ifndef CURL_DISABLE_MIME
2608 /* Output mime-generated headers. */
2609 {
2610 struct curl_slist *hdr;
2611
2612 for(hdr = data->state.mimepost->curlheaders; hdr; hdr = hdr->next) {
2613 result = Curl_dyn_addf(s: r, fmt: "%s\r\n", hdr->data);
2614 if(result)
2615 return result;
2616 }
2617 }
2618#endif
2619
2620 /* For really small posts we don't use Expect: headers at all, and for
2621 the somewhat bigger ones we allow the app to disable it. Just make
2622 sure that the expect100header is always set to the preferred value
2623 here. */
2624 ptr = Curl_checkheaders(data, STRCONST("Expect"));
2625 if(ptr) {
2626 data->state.expect100header =
2627 Curl_compareheader(headerline: ptr, STRCONST("Expect:"), STRCONST("100-continue"));
2628 }
2629 else if(http->postsize > EXPECT_100_THRESHOLD || http->postsize < 0) {
2630 result = expect100(data, conn, req: r);
2631 if(result)
2632 return result;
2633 }
2634 else
2635 data->state.expect100header = FALSE;
2636
2637 /* make the request end in a true CRLF */
2638 result = Curl_dyn_addn(s: r, STRCONST("\r\n"));
2639 if(result)
2640 return result;
2641
2642 /* set the upload size to the progress meter */
2643 Curl_pgrsSetUploadSize(data, size: http->postsize);
2644
2645 /* Read from mime structure. */
2646 data->state.fread_func = (curl_read_callback) Curl_mime_read;
2647 data->state.in = (void *) data->state.mimepost;
2648 http->sending = HTTPSEND_BODY;
2649
2650 /* this sends the buffer and frees all the buffer resources */
2651 result = Curl_buffer_send(in: r, data, http: data->req.p.http,
2652 bytes_written: &data->info.request_size, included_body_bytes: 0,
2653 FIRSTSOCKET);
2654 if(result)
2655 failf(data, fmt: "Failed sending POST request");
2656 else
2657 /* prepare for transfer */
2658 Curl_setup_transfer(data, FIRSTSOCKET, size: -1, TRUE,
2659 writesockindex: http->postsize?FIRSTSOCKET:-1);
2660 if(result)
2661 return result;
2662
2663 break;
2664
2665 case HTTPREQ_POST:
2666 /* this is the simple POST, using x-www-form-urlencoded style */
2667
2668 if(conn->bits.authneg)
2669 http->postsize = 0;
2670 else
2671 /* the size of the post body */
2672 http->postsize = data->state.infilesize;
2673
2674 /* We only set Content-Length and allow a custom Content-Length if
2675 we don't upload data chunked, as RFC2616 forbids us to set both
2676 kinds of headers (Transfer-Encoding: chunked and Content-Length) */
2677 if((http->postsize != -1) && !data->req.upload_chunky &&
2678 (conn->bits.authneg ||
2679 !Curl_checkheaders(data, STRCONST("Content-Length")))) {
2680 /* we allow replacing this header if not during auth negotiation,
2681 although it isn't very wise to actually set your own */
2682 result = Curl_dyn_addf(s: r, fmt: "Content-Length: %" CURL_FORMAT_CURL_OFF_T
2683 "\r\n", http->postsize);
2684 if(result)
2685 return result;
2686 }
2687
2688 if(!Curl_checkheaders(data, STRCONST("Content-Type"))) {
2689 result = Curl_dyn_addn(s: r, STRCONST("Content-Type: application/"
2690 "x-www-form-urlencoded\r\n"));
2691 if(result)
2692 return result;
2693 }
2694
2695 /* For really small posts we don't use Expect: headers at all, and for
2696 the somewhat bigger ones we allow the app to disable it. Just make
2697 sure that the expect100header is always set to the preferred value
2698 here. */
2699 ptr = Curl_checkheaders(data, STRCONST("Expect"));
2700 if(ptr) {
2701 data->state.expect100header =
2702 Curl_compareheader(headerline: ptr, STRCONST("Expect:"), STRCONST("100-continue"));
2703 }
2704 else if(http->postsize > EXPECT_100_THRESHOLD || http->postsize < 0) {
2705 result = expect100(data, conn, req: r);
2706 if(result)
2707 return result;
2708 }
2709 else
2710 data->state.expect100header = FALSE;
2711
2712#ifndef USE_HYPER
2713 /* With Hyper the body is always passed on separately */
2714 if(data->set.postfields) {
2715 if(!data->state.expect100header &&
2716 (http->postsize < MAX_INITIAL_POST_SIZE)) {
2717 /* if we don't use expect: 100 AND
2718 postsize is less than MAX_INITIAL_POST_SIZE
2719
2720 then append the post data to the HTTP request header. This limit
2721 is no magic limit but only set to prevent really huge POSTs to
2722 get the data duplicated with malloc() and family. */
2723
2724 /* end of headers! */
2725 result = Curl_dyn_addn(s: r, STRCONST("\r\n"));
2726 if(result)
2727 return result;
2728
2729 if(!data->req.upload_chunky) {
2730 /* We're not sending it 'chunked', append it to the request
2731 already now to reduce the number of send() calls */
2732 result = Curl_dyn_addn(s: r, mem: data->set.postfields,
2733 len: (size_t)http->postsize);
2734 included_body = http->postsize;
2735 }
2736 else {
2737 if(http->postsize) {
2738 char chunk[16];
2739 /* Append the POST data chunky-style */
2740 msnprintf(buffer: chunk, maxlength: sizeof(chunk), format: "%x\r\n", (int)http->postsize);
2741 result = Curl_dyn_add(s: r, str: chunk);
2742 if(!result) {
2743 included_body = http->postsize + strlen(s: chunk);
2744 result = Curl_dyn_addn(s: r, mem: data->set.postfields,
2745 len: (size_t)http->postsize);
2746 if(!result)
2747 result = Curl_dyn_addn(s: r, STRCONST("\r\n"));
2748 included_body += 2;
2749 }
2750 }
2751 if(!result) {
2752 result = Curl_dyn_addn(s: r, STRCONST("\x30\x0d\x0a\x0d\x0a"));
2753 /* 0 CR LF CR LF */
2754 included_body += 5;
2755 }
2756 }
2757 if(result)
2758 return result;
2759 /* Make sure the progress information is accurate */
2760 Curl_pgrsSetUploadSize(data, size: http->postsize);
2761 }
2762 else {
2763 /* A huge POST coming up, do data separate from the request */
2764 http->postdata = data->set.postfields;
2765 http->sending = HTTPSEND_BODY;
2766 http->backup.data = data;
2767 data->state.fread_func = (curl_read_callback)readmoredata;
2768 data->state.in = (void *)http;
2769
2770 /* set the upload size to the progress meter */
2771 Curl_pgrsSetUploadSize(data, size: http->postsize);
2772
2773 /* end of headers! */
2774 result = Curl_dyn_addn(s: r, STRCONST("\r\n"));
2775 if(result)
2776 return result;
2777 }
2778 }
2779 else
2780#endif
2781 {
2782 /* end of headers! */
2783 result = Curl_dyn_addn(s: r, STRCONST("\r\n"));
2784 if(result)
2785 return result;
2786
2787 if(data->req.upload_chunky && conn->bits.authneg) {
2788 /* Chunky upload is selected and we're negotiating auth still, send
2789 end-of-data only */
2790 result = Curl_dyn_addn(s: r, mem: (char *)STRCONST("\x30\x0d\x0a\x0d\x0a"));
2791 /* 0 CR LF CR LF */
2792 if(result)
2793 return result;
2794 }
2795
2796 else if(data->state.infilesize) {
2797 /* set the upload size to the progress meter */
2798 Curl_pgrsSetUploadSize(data, size: http->postsize?http->postsize:-1);
2799
2800 /* set the pointer to mark that we will send the post body using the
2801 read callback, but only if we're not in authenticate negotiation */
2802 if(!conn->bits.authneg)
2803 http->postdata = (char *)&http->postdata;
2804 }
2805 }
2806 /* issue the request */
2807 result = Curl_buffer_send(in: r, data, http: data->req.p.http,
2808 bytes_written: &data->info.request_size, included_body_bytes: included_body,
2809 FIRSTSOCKET);
2810
2811 if(result)
2812 failf(data, fmt: "Failed sending HTTP POST request");
2813 else
2814 Curl_setup_transfer(data, FIRSTSOCKET, size: -1, TRUE,
2815 writesockindex: http->postdata?FIRSTSOCKET:-1);
2816 break;
2817
2818 default:
2819 result = Curl_dyn_addn(s: r, STRCONST("\r\n"));
2820 if(result)
2821 return result;
2822
2823 /* issue the request */
2824 result = Curl_buffer_send(in: r, data, http: data->req.p.http,
2825 bytes_written: &data->info.request_size, included_body_bytes: 0,
2826 FIRSTSOCKET);
2827 if(result)
2828 failf(data, fmt: "Failed sending HTTP request");
2829#ifdef USE_WEBSOCKETS
2830 else if((conn->handler->protocol & (CURLPROTO_WS|CURLPROTO_WSS)) &&
2831 !(data->set.connect_only))
2832 /* Set up the transfer for two-way since without CONNECT_ONLY set, this
2833 request probably wants to send data too post upgrade */
2834 Curl_setup_transfer(data, FIRSTSOCKET, -1, TRUE, FIRSTSOCKET);
2835#endif
2836 else
2837 /* HTTP GET/HEAD download: */
2838 Curl_setup_transfer(data, FIRSTSOCKET, size: -1, TRUE, writesockindex: -1);
2839 }
2840
2841 return result;
2842}
2843
2844#if !defined(CURL_DISABLE_COOKIES)
2845
2846CURLcode Curl_http_cookies(struct Curl_easy *data,
2847 struct connectdata *conn,
2848 struct dynbuf *r)
2849{
2850 CURLcode result = CURLE_OK;
2851 char *addcookies = NULL;
2852 bool linecap = FALSE;
2853 if(data->set.str[STRING_COOKIE] &&
2854 !Curl_checkheaders(data, STRCONST("Cookie")))
2855 addcookies = data->set.str[STRING_COOKIE];
2856
2857 if(data->cookies || addcookies) {
2858 struct Cookie *co = NULL; /* no cookies from start */
2859 int count = 0;
2860
2861 if(data->cookies && data->state.cookie_engine) {
2862 const char *host = data->state.aptr.cookiehost ?
2863 data->state.aptr.cookiehost : conn->host.name;
2864 const bool secure_context =
2865 conn->handler->protocol&(CURLPROTO_HTTPS|CURLPROTO_WSS) ||
2866 strcasecompare("localhost", host) ||
2867 !strcmp(s1: host, s2: "127.0.0.1") ||
2868 !strcmp(s1: host, s2: "::1") ? TRUE : FALSE;
2869 Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE);
2870 co = Curl_cookie_getlist(data, c: data->cookies, host, path: data->state.up.path,
2871 secure: secure_context);
2872 Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
2873 }
2874 if(co) {
2875 struct Cookie *store = co;
2876 size_t clen = 8; /* hold the size of the generated Cookie: header */
2877 /* now loop through all cookies that matched */
2878 while(co) {
2879 if(co->value) {
2880 size_t add;
2881 if(!count) {
2882 result = Curl_dyn_addn(s: r, STRCONST("Cookie: "));
2883 if(result)
2884 break;
2885 }
2886 add = strlen(s: co->name) + strlen(s: co->value) + 1;
2887 if(clen + add >= MAX_COOKIE_HEADER_LEN) {
2888 infof(data, "Restricted outgoing cookies due to header size, "
2889 "'%s' not sent", co->name);
2890 linecap = TRUE;
2891 break;
2892 }
2893 result = Curl_dyn_addf(s: r, fmt: "%s%s=%s", count?"; ":"",
2894 co->name, co->value);
2895 if(result)
2896 break;
2897 clen += add + (count ? 2 : 0);
2898 count++;
2899 }
2900 co = co->next; /* next cookie please */
2901 }
2902 Curl_cookie_freelist(cookies: store);
2903 }
2904 if(addcookies && !result && !linecap) {
2905 if(!count)
2906 result = Curl_dyn_addn(s: r, STRCONST("Cookie: "));
2907 if(!result) {
2908 result = Curl_dyn_addf(s: r, fmt: "%s%s", count?"; ":"", addcookies);
2909 count++;
2910 }
2911 }
2912 if(count && !result)
2913 result = Curl_dyn_addn(s: r, STRCONST("\r\n"));
2914
2915 if(result)
2916 return result;
2917 }
2918 return result;
2919}
2920#endif
2921
2922CURLcode Curl_http_range(struct Curl_easy *data,
2923 Curl_HttpReq httpreq)
2924{
2925 if(data->state.use_range) {
2926 /*
2927 * A range is selected. We use different headers whether we're downloading
2928 * or uploading and we always let customized headers override our internal
2929 * ones if any such are specified.
2930 */
2931 if(((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD)) &&
2932 !Curl_checkheaders(data, STRCONST("Range"))) {
2933 /* if a line like this was already allocated, free the previous one */
2934 free(data->state.aptr.rangeline);
2935 data->state.aptr.rangeline = aprintf(format: "Range: bytes=%s\r\n",
2936 data->state.range);
2937 }
2938 else if((httpreq == HTTPREQ_POST || httpreq == HTTPREQ_PUT) &&
2939 !Curl_checkheaders(data, STRCONST("Content-Range"))) {
2940
2941 /* if a line like this was already allocated, free the previous one */
2942 free(data->state.aptr.rangeline);
2943
2944 if(data->set.set_resume_from < 0) {
2945 /* Upload resume was asked for, but we don't know the size of the
2946 remote part so we tell the server (and act accordingly) that we
2947 upload the whole file (again) */
2948 data->state.aptr.rangeline =
2949 aprintf(format: "Content-Range: bytes 0-%" CURL_FORMAT_CURL_OFF_T
2950 "/%" CURL_FORMAT_CURL_OFF_T "\r\n",
2951 data->state.infilesize - 1, data->state.infilesize);
2952
2953 }
2954 else if(data->state.resume_from) {
2955 /* This is because "resume" was selected */
2956 curl_off_t total_expected_size =
2957 data->state.resume_from + data->state.infilesize;
2958 data->state.aptr.rangeline =
2959 aprintf(format: "Content-Range: bytes %s%" CURL_FORMAT_CURL_OFF_T
2960 "/%" CURL_FORMAT_CURL_OFF_T "\r\n",
2961 data->state.range, total_expected_size-1,
2962 total_expected_size);
2963 }
2964 else {
2965 /* Range was selected and then we just pass the incoming range and
2966 append total size */
2967 data->state.aptr.rangeline =
2968 aprintf(format: "Content-Range: bytes %s/%" CURL_FORMAT_CURL_OFF_T "\r\n",
2969 data->state.range, data->state.infilesize);
2970 }
2971 if(!data->state.aptr.rangeline)
2972 return CURLE_OUT_OF_MEMORY;
2973 }
2974 }
2975 return CURLE_OK;
2976}
2977
2978CURLcode Curl_http_resume(struct Curl_easy *data,
2979 struct connectdata *conn,
2980 Curl_HttpReq httpreq)
2981{
2982 if((HTTPREQ_POST == httpreq || HTTPREQ_PUT == httpreq) &&
2983 data->state.resume_from) {
2984 /**********************************************************************
2985 * Resuming upload in HTTP means that we PUT or POST and that we have
2986 * got a resume_from value set. The resume value has already created
2987 * a Range: header that will be passed along. We need to "fast forward"
2988 * the file the given number of bytes and decrease the assume upload
2989 * file size before we continue this venture in the dark lands of HTTP.
2990 * Resuming mime/form posting at an offset > 0 has no sense and is ignored.
2991 *********************************************************************/
2992
2993 if(data->state.resume_from < 0) {
2994 /*
2995 * This is meant to get the size of the present remote-file by itself.
2996 * We don't support this now. Bail out!
2997 */
2998 data->state.resume_from = 0;
2999 }
3000
3001 if(data->state.resume_from && !data->state.followlocation) {
3002 /* only act on the first request */
3003
3004 /* Now, let's read off the proper amount of bytes from the
3005 input. */
3006 int seekerr = CURL_SEEKFUNC_CANTSEEK;
3007 if(conn->seek_func) {
3008 Curl_set_in_callback(data, true);
3009 seekerr = conn->seek_func(conn->seek_client, data->state.resume_from,
3010 SEEK_SET);
3011 Curl_set_in_callback(data, false);
3012 }
3013
3014 if(seekerr != CURL_SEEKFUNC_OK) {
3015 curl_off_t passed = 0;
3016
3017 if(seekerr != CURL_SEEKFUNC_CANTSEEK) {
3018 failf(data, fmt: "Could not seek stream");
3019 return CURLE_READ_ERROR;
3020 }
3021 /* when seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */
3022 do {
3023 size_t readthisamountnow =
3024 (data->state.resume_from - passed > data->set.buffer_size) ?
3025 (size_t)data->set.buffer_size :
3026 curlx_sotouz(sonum: data->state.resume_from - passed);
3027
3028 size_t actuallyread =
3029 data->state.fread_func(data->state.buffer, 1, readthisamountnow,
3030 data->state.in);
3031
3032 passed += actuallyread;
3033 if((actuallyread == 0) || (actuallyread > readthisamountnow)) {
3034 /* this checks for greater-than only to make sure that the
3035 CURL_READFUNC_ABORT return code still aborts */
3036 failf(data, fmt: "Could only read %" CURL_FORMAT_CURL_OFF_T
3037 " bytes from the input", passed);
3038 return CURLE_READ_ERROR;
3039 }
3040 } while(passed < data->state.resume_from);
3041 }
3042
3043 /* now, decrease the size of the read */
3044 if(data->state.infilesize>0) {
3045 data->state.infilesize -= data->state.resume_from;
3046
3047 if(data->state.infilesize <= 0) {
3048 failf(data, fmt: "File already completely uploaded");
3049 return CURLE_PARTIAL_FILE;
3050 }
3051 }
3052 /* we've passed, proceed as normal */
3053 }
3054 }
3055 return CURLE_OK;
3056}
3057
3058CURLcode Curl_http_firstwrite(struct Curl_easy *data,
3059 struct connectdata *conn,
3060 bool *done)
3061{
3062 struct SingleRequest *k = &data->req;
3063
3064 if(data->req.newurl) {
3065 if(conn->bits.close) {
3066 /* Abort after the headers if "follow Location" is set
3067 and we're set to close anyway. */
3068 k->keepon &= ~KEEP_RECV;
3069 *done = TRUE;
3070 return CURLE_OK;
3071 }
3072 /* We have a new url to load, but since we want to be able to reuse this
3073 connection properly, we read the full response in "ignore more" */
3074 k->ignorebody = TRUE;
3075 infof(data, "Ignoring the response-body");
3076 }
3077 if(data->state.resume_from && !k->content_range &&
3078 (data->state.httpreq == HTTPREQ_GET) &&
3079 !k->ignorebody) {
3080
3081 if(k->size == data->state.resume_from) {
3082 /* The resume point is at the end of file, consider this fine even if it
3083 doesn't allow resume from here. */
3084 infof(data, "The entire document is already downloaded");
3085 streamclose(conn, "already downloaded");
3086 /* Abort download */
3087 k->keepon &= ~KEEP_RECV;
3088 *done = TRUE;
3089 return CURLE_OK;
3090 }
3091
3092 /* we wanted to resume a download, although the server doesn't seem to
3093 * support this and we did this with a GET (if it wasn't a GET we did a
3094 * POST or PUT resume) */
3095 failf(data, fmt: "HTTP server doesn't seem to support "
3096 "byte ranges. Cannot resume.");
3097 return CURLE_RANGE_ERROR;
3098 }
3099
3100 if(data->set.timecondition && !data->state.range) {
3101 /* A time condition has been set AND no ranges have been requested. This
3102 seems to be what chapter 13.3.4 of RFC 2616 defines to be the correct
3103 action for an HTTP/1.1 client */
3104
3105 if(!Curl_meets_timecondition(data, timeofdoc: k->timeofdoc)) {
3106 *done = TRUE;
3107 /* We're simulating an HTTP 304 from server so we return
3108 what should have been returned from the server */
3109 data->info.httpcode = 304;
3110 infof(data, "Simulate an HTTP 304 response");
3111 /* we abort the transfer before it is completed == we ruin the
3112 reuse ability. Close the connection */
3113 streamclose(conn, "Simulated 304 handling");
3114 return CURLE_OK;
3115 }
3116 } /* we have a time condition */
3117
3118 return CURLE_OK;
3119}
3120
3121#ifdef HAVE_LIBZ
3122CURLcode Curl_transferencode(struct Curl_easy *data)
3123{
3124 if(!Curl_checkheaders(data, STRCONST("TE")) &&
3125 data->set.http_transfer_encoding) {
3126 /* When we are to insert a TE: header in the request, we must also insert
3127 TE in a Connection: header, so we need to merge the custom provided
3128 Connection: header and prevent the original to get sent. Note that if
3129 the user has inserted his/her own TE: header we don't do this magic
3130 but then assume that the user will handle it all! */
3131 char *cptr = Curl_checkheaders(data, STRCONST("Connection"));
3132#define TE_HEADER "TE: gzip\r\n"
3133
3134 Curl_safefree(data->state.aptr.te);
3135
3136 if(cptr) {
3137 cptr = Curl_copy_header_value(header: cptr);
3138 if(!cptr)
3139 return CURLE_OUT_OF_MEMORY;
3140 }
3141
3142 /* Create the (updated) Connection: header */
3143 data->state.aptr.te = aprintf(format: "Connection: %s%sTE\r\n" TE_HEADER,
3144 cptr ? cptr : "", (cptr && *cptr) ? ", ":"");
3145
3146 free(cptr);
3147 if(!data->state.aptr.te)
3148 return CURLE_OUT_OF_MEMORY;
3149 }
3150 return CURLE_OK;
3151}
3152#endif
3153
3154#ifndef USE_HYPER
3155/*
3156 * Curl_http() gets called from the generic multi_do() function when an HTTP
3157 * request is to be performed. This creates and sends a properly constructed
3158 * HTTP request.
3159 */
3160CURLcode Curl_http(struct Curl_easy *data, bool *done)
3161{
3162 struct connectdata *conn = data->conn;
3163 CURLcode result = CURLE_OK;
3164 struct HTTP *http;
3165 Curl_HttpReq httpreq;
3166 const char *te = ""; /* transfer-encoding */
3167 const char *request;
3168 const char *httpstring;
3169 struct dynbuf req;
3170 char *altused = NULL;
3171 const char *p_accept; /* Accept: string */
3172
3173 /* Always consider the DO phase done after this function call, even if there
3174 may be parts of the request that are not yet sent, since we can deal with
3175 the rest of the request in the PERFORM phase. */
3176 *done = TRUE;
3177
3178 switch(conn->alpn) {
3179 case CURL_HTTP_VERSION_3:
3180 DEBUGASSERT(Curl_conn_is_http3(data, conn, FIRSTSOCKET));
3181 break;
3182 case CURL_HTTP_VERSION_2:
3183#ifndef CURL_DISABLE_PROXY
3184 if(!Curl_conn_is_http2(data, conn, FIRSTSOCKET) &&
3185 conn->bits.proxy && !conn->bits.tunnel_proxy
3186 ) {
3187 result = Curl_http2_switch(data, conn, FIRSTSOCKET);
3188 if(result)
3189 return result;
3190 }
3191 else
3192#endif
3193 DEBUGASSERT(Curl_conn_is_http2(data, conn, FIRSTSOCKET));
3194 break;
3195 case CURL_HTTP_VERSION_1_1:
3196 /* continue with HTTP/1.1 when explicitly requested */
3197 break;
3198 default:
3199 /* Check if user wants to use HTTP/2 with clear TCP */
3200 if(Curl_http2_may_switch(data, conn, FIRSTSOCKET)) {
3201 DEBUGF(infof(data, "HTTP/2 over clean TCP"));
3202 result = Curl_http2_switch(data, conn, FIRSTSOCKET);
3203 if(result)
3204 return result;
3205 }
3206 break;
3207 }
3208
3209 http = data->req.p.http;
3210 DEBUGASSERT(http);
3211
3212 result = Curl_http_host(data, conn);
3213 if(result)
3214 return result;
3215
3216 result = Curl_http_useragent(data);
3217 if(result)
3218 return result;
3219
3220 Curl_http_method(data, conn, method: &request, reqp: &httpreq);
3221
3222 /* setup the authentication headers */
3223 {
3224 char *pq = NULL;
3225 if(data->state.up.query) {
3226 pq = aprintf(format: "%s?%s", data->state.up.path, data->state.up.query);
3227 if(!pq)
3228 return CURLE_OUT_OF_MEMORY;
3229 }
3230 result = Curl_http_output_auth(data, conn, request, httpreq,
3231 path: (pq ? pq : data->state.up.path), FALSE);
3232 free(pq);
3233 if(result)
3234 return result;
3235 }
3236
3237 Curl_safefree(data->state.aptr.ref);
3238 if(data->state.referer && !Curl_checkheaders(data, STRCONST("Referer"))) {
3239 data->state.aptr.ref = aprintf(format: "Referer: %s\r\n", data->state.referer);
3240 if(!data->state.aptr.ref)
3241 return CURLE_OUT_OF_MEMORY;
3242 }
3243
3244 if(!Curl_checkheaders(data, STRCONST("Accept-Encoding")) &&
3245 data->set.str[STRING_ENCODING]) {
3246 Curl_safefree(data->state.aptr.accept_encoding);
3247 data->state.aptr.accept_encoding =
3248 aprintf(format: "Accept-Encoding: %s\r\n", data->set.str[STRING_ENCODING]);
3249 if(!data->state.aptr.accept_encoding)
3250 return CURLE_OUT_OF_MEMORY;
3251 }
3252 else
3253 Curl_safefree(data->state.aptr.accept_encoding);
3254
3255#ifdef HAVE_LIBZ
3256 /* we only consider transfer-encoding magic if libz support is built-in */
3257 result = Curl_transferencode(data);
3258 if(result)
3259 return result;
3260#endif
3261
3262 result = Curl_http_body(data, conn, httpreq, tep: &te);
3263 if(result)
3264 return result;
3265
3266 p_accept = Curl_checkheaders(data,
3267 STRCONST("Accept"))?NULL:"Accept: */*\r\n";
3268
3269 result = Curl_http_resume(data, conn, httpreq);
3270 if(result)
3271 return result;
3272
3273 result = Curl_http_range(data, httpreq);
3274 if(result)
3275 return result;
3276
3277 httpstring = get_http_string(data, conn);
3278
3279 /* initialize a dynamic send-buffer */
3280 Curl_dyn_init(s: &req, DYN_HTTP_REQUEST);
3281
3282 /* make sure the header buffer is reset - if there are leftovers from a
3283 previous transfer */
3284 Curl_dyn_reset(s: &data->state.headerb);
3285
3286 /* add the main request stuff */
3287 /* GET/HEAD/POST/PUT */
3288 result = Curl_dyn_addf(s: &req, fmt: "%s ", request);
3289 if(!result)
3290 result = Curl_http_target(data, conn, r: &req);
3291 if(result) {
3292 Curl_dyn_free(s: &req);
3293 return result;
3294 }
3295
3296#ifndef CURL_DISABLE_ALTSVC
3297 if(conn->bits.altused && !Curl_checkheaders(data, STRCONST("Alt-Used"))) {
3298 altused = aprintf(format: "Alt-Used: %s:%d\r\n",
3299 conn->conn_to_host.name, conn->conn_to_port);
3300 if(!altused) {
3301 Curl_dyn_free(s: &req);
3302 return CURLE_OUT_OF_MEMORY;
3303 }
3304 }
3305#endif
3306 result =
3307 Curl_dyn_addf(s: &req,
3308 fmt: " HTTP/%s\r\n" /* HTTP version */
3309 "%s" /* host */
3310 "%s" /* proxyuserpwd */
3311 "%s" /* userpwd */
3312 "%s" /* range */
3313 "%s" /* user agent */
3314 "%s" /* accept */
3315 "%s" /* TE: */
3316 "%s" /* accept-encoding */
3317 "%s" /* referer */
3318 "%s" /* Proxy-Connection */
3319 "%s" /* transfer-encoding */
3320 "%s",/* Alt-Used */
3321
3322 httpstring,
3323 (data->state.aptr.host?data->state.aptr.host:""),
3324 data->state.aptr.proxyuserpwd?
3325 data->state.aptr.proxyuserpwd:"",
3326 data->state.aptr.userpwd?data->state.aptr.userpwd:"",
3327 (data->state.use_range && data->state.aptr.rangeline)?
3328 data->state.aptr.rangeline:"",
3329 (data->set.str[STRING_USERAGENT] &&
3330 *data->set.str[STRING_USERAGENT] &&
3331 data->state.aptr.uagent)?
3332 data->state.aptr.uagent:"",
3333 p_accept?p_accept:"",
3334 data->state.aptr.te?data->state.aptr.te:"",
3335 (data->set.str[STRING_ENCODING] &&
3336 *data->set.str[STRING_ENCODING] &&
3337 data->state.aptr.accept_encoding)?
3338 data->state.aptr.accept_encoding:"",
3339 (data->state.referer && data->state.aptr.ref)?
3340 data->state.aptr.ref:"" /* Referer: <data> */,
3341#ifndef CURL_DISABLE_PROXY
3342 (conn->bits.httpproxy &&
3343 !conn->bits.tunnel_proxy &&
3344 !Curl_checkheaders(data, STRCONST("Proxy-Connection")) &&
3345 !Curl_checkProxyheaders(data,
3346 conn,
3347 STRCONST("Proxy-Connection")))?
3348 "Proxy-Connection: Keep-Alive\r\n":"",
3349#else
3350 "",
3351#endif
3352 te,
3353 altused ? altused : ""
3354 );
3355
3356 /* clear userpwd and proxyuserpwd to avoid reusing old credentials
3357 * from reused connections */
3358 Curl_safefree(data->state.aptr.userpwd);
3359 Curl_safefree(data->state.aptr.proxyuserpwd);
3360 free(altused);
3361
3362 if(result) {
3363 Curl_dyn_free(s: &req);
3364 return result;
3365 }
3366
3367 if(!(conn->handler->flags&PROTOPT_SSL) &&
3368 conn->httpversion < 20 &&
3369 (data->state.httpwant == CURL_HTTP_VERSION_2)) {
3370 /* append HTTP2 upgrade magic stuff to the HTTP request if it isn't done
3371 over SSL */
3372 result = Curl_http2_request_upgrade(&req, data);
3373 if(result) {
3374 Curl_dyn_free(s: &req);
3375 return result;
3376 }
3377 }
3378
3379 result = Curl_http_cookies(data, conn, r: &req);
3380#ifdef USE_WEBSOCKETS
3381 if(!result && conn->handler->protocol&(CURLPROTO_WS|CURLPROTO_WSS))
3382 result = Curl_ws_request(data, &req);
3383#endif
3384 if(!result)
3385 result = Curl_add_timecondition(data, req: &req);
3386 if(!result)
3387 result = Curl_add_custom_headers(data, FALSE, req: &req);
3388
3389 if(!result) {
3390 http->postdata = NULL; /* nothing to post at this point */
3391 if((httpreq == HTTPREQ_GET) ||
3392 (httpreq == HTTPREQ_HEAD))
3393 Curl_pgrsSetUploadSize(data, size: 0); /* nothing */
3394
3395 /* bodysend takes ownership of the 'req' memory on success */
3396 result = Curl_http_bodysend(data, conn, r: &req, httpreq);
3397 }
3398 if(result) {
3399 Curl_dyn_free(s: &req);
3400 return result;
3401 }
3402
3403 if((http->postsize > -1) &&
3404 (http->postsize <= data->req.writebytecount) &&
3405 (http->sending != HTTPSEND_REQUEST))
3406 data->req.upload_done = TRUE;
3407
3408 if(data->req.writebytecount) {
3409 /* if a request-body has been sent off, we make sure this progress is noted
3410 properly */
3411 Curl_pgrsSetUploadCounter(data, size: data->req.writebytecount);
3412 if(Curl_pgrsUpdate(data))
3413 result = CURLE_ABORTED_BY_CALLBACK;
3414
3415 if(!http->postsize) {
3416 /* already sent the entire request body, mark the "upload" as
3417 complete */
3418 infof(data, "upload completely sent off: %" CURL_FORMAT_CURL_OFF_T
3419 " out of %" CURL_FORMAT_CURL_OFF_T " bytes",
3420 data->req.writebytecount, http->postsize);
3421 data->req.upload_done = TRUE;
3422 data->req.keepon &= ~KEEP_SEND; /* we're done writing */
3423 data->req.exp100 = EXP100_SEND_DATA; /* already sent */
3424 Curl_expire_done(data, id: EXPIRE_100_TIMEOUT);
3425 }
3426 }
3427
3428 if(data->req.upload_done)
3429 Curl_conn_ev_data_done_send(data);
3430
3431 if((conn->httpversion >= 20) && data->req.upload_chunky)
3432 /* upload_chunky was set above to set up the request in a chunky fashion,
3433 but is disabled here again to avoid that the chunked encoded version is
3434 actually used when sending the request body over h2 */
3435 data->req.upload_chunky = FALSE;
3436 return result;
3437}
3438
3439#endif /* USE_HYPER */
3440
3441typedef enum {
3442 STATUS_UNKNOWN, /* not enough data to tell yet */
3443 STATUS_DONE, /* a status line was read */
3444 STATUS_BAD /* not a status line */
3445} statusline;
3446
3447
3448/* Check a string for a prefix. Check no more than 'len' bytes */
3449static bool checkprefixmax(const char *prefix, const char *buffer, size_t len)
3450{
3451 size_t ch = CURLMIN(strlen(prefix), len);
3452 return curl_strnequal(s1: prefix, s2: buffer, n: ch);
3453}
3454
3455/*
3456 * checkhttpprefix()
3457 *
3458 * Returns TRUE if member of the list matches prefix of string
3459 */
3460static statusline
3461checkhttpprefix(struct Curl_easy *data,
3462 const char *s, size_t len)
3463{
3464 struct curl_slist *head = data->set.http200aliases;
3465 statusline rc = STATUS_BAD;
3466 statusline onmatch = len >= 5? STATUS_DONE : STATUS_UNKNOWN;
3467
3468 while(head) {
3469 if(checkprefixmax(prefix: head->data, buffer: s, len)) {
3470 rc = onmatch;
3471 break;
3472 }
3473 head = head->next;
3474 }
3475
3476 if((rc != STATUS_DONE) && (checkprefixmax(prefix: "HTTP/", buffer: s, len)))
3477 rc = onmatch;
3478
3479 return rc;
3480}
3481
3482#ifndef CURL_DISABLE_RTSP
3483static statusline
3484checkrtspprefix(struct Curl_easy *data,
3485 const char *s, size_t len)
3486{
3487 statusline result = STATUS_BAD;
3488 statusline onmatch = len >= 5? STATUS_DONE : STATUS_UNKNOWN;
3489 (void)data; /* unused */
3490 if(checkprefixmax("RTSP/", s, len))
3491 result = onmatch;
3492
3493 return result;
3494}
3495#endif /* CURL_DISABLE_RTSP */
3496
3497static statusline
3498checkprotoprefix(struct Curl_easy *data, struct connectdata *conn,
3499 const char *s, size_t len)
3500{
3501#ifndef CURL_DISABLE_RTSP
3502 if(conn->handler->protocol & CURLPROTO_RTSP)
3503 return checkrtspprefix(data, s, len);
3504#else
3505 (void)conn;
3506#endif /* CURL_DISABLE_RTSP */
3507
3508 return checkhttpprefix(data, s, len);
3509}
3510
3511/*
3512 * Curl_http_header() parses a single response header.
3513 */
3514CURLcode Curl_http_header(struct Curl_easy *data, struct connectdata *conn,
3515 char *headp)
3516{
3517 CURLcode result;
3518 struct SingleRequest *k = &data->req;
3519 /* Check for Content-Length: header lines to get size */
3520 if(!k->http_bodyless &&
3521 !data->set.ignorecl && checkprefix("Content-Length:", headp)) {
3522 curl_off_t contentlength;
3523 CURLofft offt = curlx_strtoofft(str: headp + strlen(s: "Content-Length:"),
3524 NULL, base: 10, num: &contentlength);
3525
3526 if(offt == CURL_OFFT_OK) {
3527 k->size = contentlength;
3528 k->maxdownload = k->size;
3529 }
3530 else if(offt == CURL_OFFT_FLOW) {
3531 /* out of range */
3532 if(data->set.max_filesize) {
3533 failf(data, fmt: "Maximum file size exceeded");
3534 return CURLE_FILESIZE_EXCEEDED;
3535 }
3536 streamclose(conn, "overflow content-length");
3537 infof(data, "Overflow Content-Length: value");
3538 }
3539 else {
3540 /* negative or just rubbish - bad HTTP */
3541 failf(data, fmt: "Invalid Content-Length: value");
3542 return CURLE_WEIRD_SERVER_REPLY;
3543 }
3544 }
3545 /* check for Content-Type: header lines to get the MIME-type */
3546 else if(checkprefix("Content-Type:", headp)) {
3547 char *contenttype = Curl_copy_header_value(header: headp);
3548 if(!contenttype)
3549 return CURLE_OUT_OF_MEMORY;
3550 if(!*contenttype)
3551 /* ignore empty data */
3552 free(contenttype);
3553 else {
3554 Curl_safefree(data->info.contenttype);
3555 data->info.contenttype = contenttype;
3556 }
3557 }
3558#ifndef CURL_DISABLE_PROXY
3559 else if((conn->httpversion == 10) &&
3560 conn->bits.httpproxy &&
3561 Curl_compareheader(headerline: headp,
3562 STRCONST("Proxy-Connection:"),
3563 STRCONST("keep-alive"))) {
3564 /*
3565 * When an HTTP/1.0 reply comes when using a proxy, the
3566 * 'Proxy-Connection: keep-alive' line tells us the
3567 * connection will be kept alive for our pleasure.
3568 * Default action for 1.0 is to close.
3569 */
3570 connkeep(conn, "Proxy-Connection keep-alive"); /* don't close */
3571 infof(data, "HTTP/1.0 proxy connection set to keep alive");
3572 }
3573 else if((conn->httpversion == 11) &&
3574 conn->bits.httpproxy &&
3575 Curl_compareheader(headerline: headp,
3576 STRCONST("Proxy-Connection:"),
3577 STRCONST("close"))) {
3578 /*
3579 * We get an HTTP/1.1 response from a proxy and it says it'll
3580 * close down after this transfer.
3581 */
3582 connclose(conn, "Proxy-Connection: asked to close after done");
3583 infof(data, "HTTP/1.1 proxy connection set close");
3584 }
3585#endif
3586 else if((conn->httpversion == 10) &&
3587 Curl_compareheader(headerline: headp,
3588 STRCONST("Connection:"),
3589 STRCONST("keep-alive"))) {
3590 /*
3591 * An HTTP/1.0 reply with the 'Connection: keep-alive' line
3592 * tells us the connection will be kept alive for our
3593 * pleasure. Default action for 1.0 is to close.
3594 *
3595 * [RFC2068, section 19.7.1] */
3596 connkeep(conn, "Connection keep-alive");
3597 infof(data, "HTTP/1.0 connection set to keep alive");
3598 }
3599 else if(Curl_compareheader(headerline: headp,
3600 STRCONST("Connection:"), STRCONST("close"))) {
3601 /*
3602 * [RFC 2616, section 8.1.2.1]
3603 * "Connection: close" is HTTP/1.1 language and means that
3604 * the connection will close when this request has been
3605 * served.
3606 */
3607 streamclose(conn, "Connection: close used");
3608 }
3609 else if(!k->http_bodyless && checkprefix("Transfer-Encoding:", headp)) {
3610 /* One or more encodings. We check for chunked and/or a compression
3611 algorithm. */
3612 /*
3613 * [RFC 2616, section 3.6.1] A 'chunked' transfer encoding
3614 * means that the server will send a series of "chunks". Each
3615 * chunk starts with line with info (including size of the
3616 * coming block) (terminated with CRLF), then a block of data
3617 * with the previously mentioned size. There can be any amount
3618 * of chunks, and a chunk-data set to zero signals the
3619 * end-of-chunks. */
3620
3621 result = Curl_build_unencoding_stack(data,
3622 enclist: headp + strlen(s: "Transfer-Encoding:"),
3623 TRUE);
3624 if(result)
3625 return result;
3626 if(!k->chunk && data->set.http_transfer_encoding) {
3627 /* if this isn't chunked, only close can signal the end of this transfer
3628 as Content-Length is said not to be trusted for transfer-encoding! */
3629 connclose(conn, "HTTP/1.1 transfer-encoding without chunks");
3630 k->ignore_cl = TRUE;
3631 }
3632 }
3633 else if(!k->http_bodyless && checkprefix("Content-Encoding:", headp) &&
3634 data->set.str[STRING_ENCODING]) {
3635 /*
3636 * Process Content-Encoding. Look for the values: identity,
3637 * gzip, deflate, compress, x-gzip and x-compress. x-gzip and
3638 * x-compress are the same as gzip and compress. (Sec 3.5 RFC
3639 * 2616). zlib cannot handle compress. However, errors are
3640 * handled further down when the response body is processed
3641 */
3642 result = Curl_build_unencoding_stack(data,
3643 enclist: headp + strlen(s: "Content-Encoding:"),
3644 FALSE);
3645 if(result)
3646 return result;
3647 }
3648 else if(checkprefix("Retry-After:", headp)) {
3649 /* Retry-After = HTTP-date / delay-seconds */
3650 curl_off_t retry_after = 0; /* zero for unknown or "now" */
3651 /* Try it as a decimal number, if it works it is not a date */
3652 (void)curlx_strtoofft(str: headp + strlen(s: "Retry-After:"),
3653 NULL, base: 10, num: &retry_after);
3654 if(!retry_after) {
3655 time_t date = Curl_getdate_capped(p: headp + strlen(s: "Retry-After:"));
3656 if(-1 != date)
3657 /* convert date to number of seconds into the future */
3658 retry_after = date - time(NULL);
3659 }
3660 data->info.retry_after = retry_after; /* store it */
3661 }
3662 else if(!k->http_bodyless && checkprefix("Content-Range:", headp)) {
3663 /* Content-Range: bytes [num]-
3664 Content-Range: bytes: [num]-
3665 Content-Range: [num]-
3666 Content-Range: [asterisk]/[total]
3667
3668 The second format was added since Sun's webserver
3669 JavaWebServer/1.1.1 obviously sends the header this way!
3670 The third added since some servers use that!
3671 The fourth means the requested range was unsatisfied.
3672 */
3673
3674 char *ptr = headp + strlen(s: "Content-Range:");
3675
3676 /* Move forward until first digit or asterisk */
3677 while(*ptr && !ISDIGIT(*ptr) && *ptr != '*')
3678 ptr++;
3679
3680 /* if it truly stopped on a digit */
3681 if(ISDIGIT(*ptr)) {
3682 if(!curlx_strtoofft(str: ptr, NULL, base: 10, num: &k->offset)) {
3683 if(data->state.resume_from == k->offset)
3684 /* we asked for a resume and we got it */
3685 k->content_range = TRUE;
3686 }
3687 }
3688 else
3689 data->state.resume_from = 0; /* get everything */
3690 }
3691#if !defined(CURL_DISABLE_COOKIES)
3692 else if(data->cookies && data->state.cookie_engine &&
3693 checkprefix("Set-Cookie:", headp)) {
3694 /* If there is a custom-set Host: name, use it here, or else use real peer
3695 host name. */
3696 const char *host = data->state.aptr.cookiehost?
3697 data->state.aptr.cookiehost:conn->host.name;
3698 const bool secure_context =
3699 conn->handler->protocol&(CURLPROTO_HTTPS|CURLPROTO_WSS) ||
3700 strcasecompare("localhost", host) ||
3701 !strcmp(s1: host, s2: "127.0.0.1") ||
3702 !strcmp(s1: host, s2: "::1") ? TRUE : FALSE;
3703
3704 Curl_share_lock(data, CURL_LOCK_DATA_COOKIE,
3705 CURL_LOCK_ACCESS_SINGLE);
3706 Curl_cookie_add(data, c: data->cookies, TRUE, FALSE,
3707 lineptr: headp + strlen(s: "Set-Cookie:"), domain: host,
3708 path: data->state.up.path, secure: secure_context);
3709 Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
3710 }
3711#endif
3712 else if(!k->http_bodyless && checkprefix("Last-Modified:", headp) &&
3713 (data->set.timecondition || data->set.get_filetime) ) {
3714 k->timeofdoc = Curl_getdate_capped(p: headp + strlen(s: "Last-Modified:"));
3715 if(data->set.get_filetime)
3716 data->info.filetime = k->timeofdoc;
3717 }
3718 else if((checkprefix("WWW-Authenticate:", headp) &&
3719 (401 == k->httpcode)) ||
3720 (checkprefix("Proxy-authenticate:", headp) &&
3721 (407 == k->httpcode))) {
3722
3723 bool proxy = (k->httpcode == 407) ? TRUE : FALSE;
3724 char *auth = Curl_copy_header_value(header: headp);
3725 if(!auth)
3726 return CURLE_OUT_OF_MEMORY;
3727
3728 result = Curl_http_input_auth(data, proxy, auth);
3729
3730 free(auth);
3731
3732 if(result)
3733 return result;
3734 }
3735#ifdef USE_SPNEGO
3736 else if(checkprefix("Persistent-Auth:", headp)) {
3737 struct negotiatedata *negdata = &conn->negotiate;
3738 struct auth *authp = &data->state.authhost;
3739 if(authp->picked == CURLAUTH_NEGOTIATE) {
3740 char *persistentauth = Curl_copy_header_value(headp);
3741 if(!persistentauth)
3742 return CURLE_OUT_OF_MEMORY;
3743 negdata->noauthpersist = checkprefix("false", persistentauth)?
3744 TRUE:FALSE;
3745 negdata->havenoauthpersist = TRUE;
3746 infof(data, "Negotiate: noauthpersist -> %d, header part: %s",
3747 negdata->noauthpersist, persistentauth);
3748 free(persistentauth);
3749 }
3750 }
3751#endif
3752 else if((k->httpcode >= 300 && k->httpcode < 400) &&
3753 checkprefix("Location:", headp) &&
3754 !data->req.location) {
3755 /* this is the URL that the server advises us to use instead */
3756 char *location = Curl_copy_header_value(header: headp);
3757 if(!location)
3758 return CURLE_OUT_OF_MEMORY;
3759 if(!*location)
3760 /* ignore empty data */
3761 free(location);
3762 else {
3763 data->req.location = location;
3764
3765 if(data->set.http_follow_location) {
3766 DEBUGASSERT(!data->req.newurl);
3767 data->req.newurl = strdup(data->req.location); /* clone */
3768 if(!data->req.newurl)
3769 return CURLE_OUT_OF_MEMORY;
3770
3771 /* some cases of POST and PUT etc needs to rewind the data
3772 stream at this point */
3773 result = http_perhapsrewind(data, conn);
3774 if(result)
3775 return result;
3776
3777 /* mark the next request as a followed location: */
3778 data->state.this_is_a_follow = TRUE;
3779 }
3780 }
3781 }
3782
3783#ifndef CURL_DISABLE_HSTS
3784 /* If enabled, the header is incoming and this is over HTTPS */
3785 else if(data->hsts && checkprefix("Strict-Transport-Security:", headp) &&
3786 ((conn->handler->flags & PROTOPT_SSL) ||
3787#ifdef CURLDEBUG
3788 /* allow debug builds to circumvent the HTTPS restriction */
3789 getenv("CURL_HSTS_HTTP")
3790#else
3791 0
3792#endif
3793 )) {
3794 CURLcode check =
3795 Curl_hsts_parse(h: data->hsts, hostname: conn->host.name,
3796 sts: headp + strlen(s: "Strict-Transport-Security:"));
3797 if(check)
3798 infof(data, "Illegal STS header skipped");
3799#ifdef DEBUGBUILD
3800 else
3801 infof(data, "Parsed STS header fine (%zu entries)",
3802 data->hsts->list.size);
3803#endif
3804 }
3805#endif
3806#ifndef CURL_DISABLE_ALTSVC
3807 /* If enabled, the header is incoming and this is over HTTPS */
3808 else if(data->asi && checkprefix("Alt-Svc:", headp) &&
3809 ((conn->handler->flags & PROTOPT_SSL) ||
3810#ifdef CURLDEBUG
3811 /* allow debug builds to circumvent the HTTPS restriction */
3812 getenv("CURL_ALTSVC_HTTP")
3813#else
3814 0
3815#endif
3816 )) {
3817 /* the ALPN of the current request */
3818 enum alpnid id = (conn->httpversion == 30)? ALPN_h3 :
3819 (conn->httpversion == 20) ? ALPN_h2 : ALPN_h1;
3820 result = Curl_altsvc_parse(data, altsvc: data->asi,
3821 value: headp + strlen(s: "Alt-Svc:"),
3822 srcalpn: id, srchost: conn->host.name,
3823 srcport: curlx_uitous(uinum: (unsigned int)conn->remote_port));
3824 if(result)
3825 return result;
3826 }
3827#endif
3828 else if(conn->handler->protocol & CURLPROTO_RTSP) {
3829 result = Curl_rtsp_parseheader(data, headp);
3830 if(result)
3831 return result;
3832 }
3833 return CURLE_OK;
3834}
3835
3836/*
3837 * Called after the first HTTP response line (the status line) has been
3838 * received and parsed.
3839 */
3840
3841CURLcode Curl_http_statusline(struct Curl_easy *data,
3842 struct connectdata *conn)
3843{
3844 struct SingleRequest *k = &data->req;
3845 data->info.httpcode = k->httpcode;
3846
3847 data->info.httpversion = conn->httpversion;
3848 if(!data->state.httpversion ||
3849 data->state.httpversion > conn->httpversion)
3850 /* store the lowest server version we encounter */
3851 data->state.httpversion = conn->httpversion;
3852
3853 /*
3854 * This code executes as part of processing the header. As a
3855 * result, it's not totally clear how to interpret the
3856 * response code yet as that depends on what other headers may
3857 * be present. 401 and 407 may be errors, but may be OK
3858 * depending on how authentication is working. Other codes
3859 * are definitely errors, so give up here.
3860 */
3861 if(data->state.resume_from && data->state.httpreq == HTTPREQ_GET &&
3862 k->httpcode == 416) {
3863 /* "Requested Range Not Satisfiable", just proceed and
3864 pretend this is no error */
3865 k->ignorebody = TRUE; /* Avoid appending error msg to good data. */
3866 }
3867
3868 if(conn->httpversion == 10) {
3869 /* Default action for HTTP/1.0 must be to close, unless
3870 we get one of those fancy headers that tell us the
3871 server keeps it open for us! */
3872 infof(data, "HTTP 1.0, assume close after body");
3873 connclose(conn, "HTTP/1.0 close after body");
3874 }
3875 else if(conn->httpversion == 20 ||
3876 (k->upgr101 == UPGR101_H2 && k->httpcode == 101)) {
3877 DEBUGF(infof(data, "HTTP/2 found, allow multiplexing"));
3878 /* HTTP/2 cannot avoid multiplexing since it is a core functionality
3879 of the protocol */
3880 conn->bundle->multiuse = BUNDLE_MULTIPLEX;
3881 }
3882 else if(conn->httpversion >= 11 &&
3883 !conn->bits.close) {
3884 /* If HTTP version is >= 1.1 and connection is persistent */
3885 DEBUGF(infof(data,
3886 "HTTP 1.1 or later with persistent connection"));
3887 }
3888
3889 k->http_bodyless = k->httpcode >= 100 && k->httpcode < 200;
3890 switch(k->httpcode) {
3891 case 304:
3892 /* (quote from RFC2616, section 10.3.5): The 304 response
3893 * MUST NOT contain a message-body, and thus is always
3894 * terminated by the first empty line after the header
3895 * fields. */
3896 if(data->set.timecondition)
3897 data->info.timecond = TRUE;
3898 /* FALLTHROUGH */
3899 case 204:
3900 /* (quote from RFC2616, section 10.2.5): The server has
3901 * fulfilled the request but does not need to return an
3902 * entity-body ... The 204 response MUST NOT include a
3903 * message-body, and thus is always terminated by the first
3904 * empty line after the header fields. */
3905 k->size = 0;
3906 k->maxdownload = 0;
3907 k->http_bodyless = TRUE;
3908 break;
3909 default:
3910 break;
3911 }
3912 return CURLE_OK;
3913}
3914
3915/* Content-Length must be ignored if any Transfer-Encoding is present in the
3916 response. Refer to RFC 7230 section 3.3.3 and RFC2616 section 4.4. This is
3917 figured out here after all headers have been received but before the final
3918 call to the user's header callback, so that a valid content length can be
3919 retrieved by the user in the final call. */
3920CURLcode Curl_http_size(struct Curl_easy *data)
3921{
3922 struct SingleRequest *k = &data->req;
3923 if(data->req.ignore_cl || k->chunk) {
3924 k->size = k->maxdownload = -1;
3925 }
3926 else if(k->size != -1) {
3927 if(data->set.max_filesize &&
3928 k->size > data->set.max_filesize) {
3929 failf(data, fmt: "Maximum file size exceeded");
3930 return CURLE_FILESIZE_EXCEEDED;
3931 }
3932 Curl_pgrsSetDownloadSize(data, size: k->size);
3933 k->maxdownload = k->size;
3934 }
3935 return CURLE_OK;
3936}
3937
3938static CURLcode verify_header(struct Curl_easy *data)
3939{
3940 struct SingleRequest *k = &data->req;
3941 const char *header = Curl_dyn_ptr(s: &data->state.headerb);
3942 size_t hlen = Curl_dyn_len(s: &data->state.headerb);
3943 char *ptr = memchr(s: header, c: 0x00, n: hlen);
3944 if(ptr) {
3945 /* this is bad, bail out */
3946 failf(data, fmt: "Nul byte in header");
3947 return CURLE_WEIRD_SERVER_REPLY;
3948 }
3949 if(k->headerline < 2)
3950 /* the first "header" is the status-line and it has no colon */
3951 return CURLE_OK;
3952 if(((header[0] == ' ') || (header[0] == '\t')) && k->headerline > 2)
3953 /* line folding, can't happen on line 2 */
3954 ;
3955 else {
3956 ptr = memchr(s: header, c: ':', n: hlen);
3957 if(!ptr) {
3958 /* this is bad, bail out */
3959 failf(data, fmt: "Header without colon");
3960 return CURLE_WEIRD_SERVER_REPLY;
3961 }
3962 }
3963 return CURLE_OK;
3964}
3965
3966CURLcode Curl_bump_headersize(struct Curl_easy *data,
3967 size_t delta,
3968 bool connect_only)
3969{
3970 size_t bad = 0;
3971 unsigned int max = MAX_HTTP_RESP_HEADER_SIZE;
3972 if(delta < MAX_HTTP_RESP_HEADER_SIZE) {
3973 data->info.header_size += (unsigned int)delta;
3974 data->req.allheadercount += (unsigned int)delta;
3975 if(!connect_only)
3976 data->req.headerbytecount += (unsigned int)delta;
3977 if(data->req.allheadercount > max)
3978 bad = data->req.allheadercount;
3979 else if(data->info.header_size > (max * 20)) {
3980 bad = data->info.header_size;
3981 max *= 20;
3982 }
3983 }
3984 else
3985 bad = data->req.allheadercount + delta;
3986 if(bad) {
3987 failf(data, fmt: "Too large response headers: %zu > %u", bad, max);
3988 return CURLE_RECV_ERROR;
3989 }
3990 return CURLE_OK;
3991}
3992
3993
3994/*
3995 * Read any HTTP header lines from the server and pass them to the client app.
3996 */
3997CURLcode Curl_http_readwrite_headers(struct Curl_easy *data,
3998 struct connectdata *conn,
3999 ssize_t *nread,
4000 bool *stop_reading)
4001{
4002 CURLcode result;
4003 struct SingleRequest *k = &data->req;
4004 ssize_t onread = *nread;
4005 char *ostr = k->str;
4006 char *headp;
4007 char *str_start;
4008 char *end_ptr;
4009
4010 /* header line within buffer loop */
4011 do {
4012 size_t rest_length;
4013 size_t full_length;
4014 int writetype;
4015
4016 /* str_start is start of line within buf */
4017 str_start = k->str;
4018
4019 /* data is in network encoding so use 0x0a instead of '\n' */
4020 end_ptr = memchr(s: str_start, c: 0x0a, n: *nread);
4021
4022 if(!end_ptr) {
4023 /* Not a complete header line within buffer, append the data to
4024 the end of the headerbuff. */
4025 result = Curl_dyn_addn(s: &data->state.headerb, mem: str_start, len: *nread);
4026 if(result)
4027 return result;
4028
4029 if(!k->headerline) {
4030 /* check if this looks like a protocol header */
4031 statusline st =
4032 checkprotoprefix(data, conn,
4033 s: Curl_dyn_ptr(s: &data->state.headerb),
4034 len: Curl_dyn_len(s: &data->state.headerb));
4035
4036 if(st == STATUS_BAD) {
4037 /* this is not the beginning of a protocol first header line */
4038 k->header = FALSE;
4039 k->badheader = HEADER_ALLBAD;
4040 streamclose(conn, "bad HTTP: No end-of-message indicator");
4041 if(!data->set.http09_allowed) {
4042 failf(data, fmt: "Received HTTP/0.9 when not allowed");
4043 return CURLE_UNSUPPORTED_PROTOCOL;
4044 }
4045 break;
4046 }
4047 }
4048
4049 break; /* read more and try again */
4050 }
4051
4052 /* decrease the size of the remaining (supposed) header line */
4053 rest_length = (end_ptr - k->str) + 1;
4054 *nread -= (ssize_t)rest_length;
4055
4056 k->str = end_ptr + 1; /* move past new line */
4057
4058 full_length = k->str - str_start;
4059
4060 result = Curl_dyn_addn(s: &data->state.headerb, mem: str_start, len: full_length);
4061 if(result)
4062 return result;
4063
4064 /****
4065 * We now have a FULL header line in 'headerb'.
4066 *****/
4067
4068 if(!k->headerline) {
4069 /* the first read header */
4070 statusline st = checkprotoprefix(data, conn,
4071 s: Curl_dyn_ptr(s: &data->state.headerb),
4072 len: Curl_dyn_len(s: &data->state.headerb));
4073 if(st == STATUS_BAD) {
4074 streamclose(conn, "bad HTTP: No end-of-message indicator");
4075 /* this is not the beginning of a protocol first header line */
4076 if(!data->set.http09_allowed) {
4077 failf(data, fmt: "Received HTTP/0.9 when not allowed");
4078 return CURLE_UNSUPPORTED_PROTOCOL;
4079 }
4080 k->header = FALSE;
4081 if(*nread)
4082 /* since there's more, this is a partial bad header */
4083 k->badheader = HEADER_PARTHEADER;
4084 else {
4085 /* this was all we read so it's all a bad header */
4086 k->badheader = HEADER_ALLBAD;
4087 *nread = onread;
4088 k->str = ostr;
4089 return CURLE_OK;
4090 }
4091 break;
4092 }
4093 }
4094
4095 /* headers are in network encoding so use 0x0a and 0x0d instead of '\n'
4096 and '\r' */
4097 headp = Curl_dyn_ptr(s: &data->state.headerb);
4098 if((0x0a == *headp) || (0x0d == *headp)) {
4099 size_t headerlen;
4100 /* Zero-length header line means end of headers! */
4101
4102 if('\r' == *headp)
4103 headp++; /* pass the \r byte */
4104 if('\n' == *headp)
4105 headp++; /* pass the \n byte */
4106
4107 if(100 <= k->httpcode && 199 >= k->httpcode) {
4108 /* "A user agent MAY ignore unexpected 1xx status responses." */
4109 switch(k->httpcode) {
4110 case 100:
4111 /*
4112 * We have made an HTTP PUT or POST and this is 1.1-lingo
4113 * that tells us that the server is OK with this and ready
4114 * to receive the data.
4115 * However, we'll get more headers now so we must get
4116 * back into the header-parsing state!
4117 */
4118 k->header = TRUE;
4119 k->headerline = 0; /* restart the header line counter */
4120
4121 /* if we did wait for this do enable write now! */
4122 if(k->exp100 > EXP100_SEND_DATA) {
4123 k->exp100 = EXP100_SEND_DATA;
4124 k->keepon |= KEEP_SEND;
4125 Curl_expire_done(data, id: EXPIRE_100_TIMEOUT);
4126 }
4127 break;
4128 case 101:
4129 /* Switching Protocols */
4130 if(k->upgr101 == UPGR101_H2) {
4131 /* Switching to HTTP/2 */
4132 DEBUGASSERT(conn->httpversion < 20);
4133 infof(data, "Received 101, Switching to HTTP/2");
4134 k->upgr101 = UPGR101_RECEIVED;
4135
4136 /* we'll get more headers (HTTP/2 response) */
4137 k->header = TRUE;
4138 k->headerline = 0; /* restart the header line counter */
4139
4140 /* switch to http2 now. The bytes after response headers
4141 are also processed here, otherwise they are lost. */
4142 result = Curl_http2_upgrade(data, conn, FIRSTSOCKET,
4143 k->str, *nread);
4144 if(result)
4145 return result;
4146 *nread = 0;
4147 }
4148#ifdef USE_WEBSOCKETS
4149 else if(k->upgr101 == UPGR101_WS) {
4150 /* verify the response */
4151 result = Curl_ws_accept(data, k->str, *nread);
4152 if(result)
4153 return result;
4154 k->header = FALSE; /* no more header to parse! */
4155 if(data->set.connect_only) {
4156 k->keepon &= ~KEEP_RECV; /* read no more content */
4157 *nread = 0;
4158 }
4159 }
4160#endif
4161 else {
4162 /* Not switching to another protocol */
4163 k->header = FALSE; /* no more header to parse! */
4164 }
4165 break;
4166 default:
4167 /* the status code 1xx indicates a provisional response, so
4168 we'll get another set of headers */
4169 k->header = TRUE;
4170 k->headerline = 0; /* restart the header line counter */
4171 break;
4172 }
4173 }
4174 else {
4175 if(k->upgr101 == UPGR101_H2) {
4176 /* A requested upgrade was denied, poke the multi handle to possibly
4177 allow a pending pipewait to continue */
4178 Curl_multi_connchanged(multi: data->multi);
4179 }
4180 k->header = FALSE; /* no more header to parse! */
4181
4182 if((k->size == -1) && !k->chunk && !conn->bits.close &&
4183 (conn->httpversion == 11) &&
4184 !(conn->handler->protocol & CURLPROTO_RTSP) &&
4185 data->state.httpreq != HTTPREQ_HEAD) {
4186 /* On HTTP 1.1, when connection is not to get closed, but no
4187 Content-Length nor Transfer-Encoding chunked have been
4188 received, according to RFC2616 section 4.4 point 5, we
4189 assume that the server will close the connection to
4190 signal the end of the document. */
4191 infof(data, "no chunk, no close, no size. Assume close to "
4192 "signal end");
4193 streamclose(conn, "HTTP: No end-of-message indicator");
4194 }
4195 }
4196
4197 if(!k->header) {
4198 result = Curl_http_size(data);
4199 if(result)
4200 return result;
4201 }
4202
4203 /* At this point we have some idea about the fate of the connection.
4204 If we are closing the connection it may result auth failure. */
4205#if defined(USE_NTLM)
4206 if(conn->bits.close &&
4207 (((data->req.httpcode == 401) &&
4208 (conn->http_ntlm_state == NTLMSTATE_TYPE2)) ||
4209 ((data->req.httpcode == 407) &&
4210 (conn->proxy_ntlm_state == NTLMSTATE_TYPE2)))) {
4211 infof(data, "Connection closure while negotiating auth (HTTP 1.0?)");
4212 data->state.authproblem = TRUE;
4213 }
4214#endif
4215#if defined(USE_SPNEGO)
4216 if(conn->bits.close &&
4217 (((data->req.httpcode == 401) &&
4218 (conn->http_negotiate_state == GSS_AUTHRECV)) ||
4219 ((data->req.httpcode == 407) &&
4220 (conn->proxy_negotiate_state == GSS_AUTHRECV)))) {
4221 infof(data, "Connection closure while negotiating auth (HTTP 1.0?)");
4222 data->state.authproblem = TRUE;
4223 }
4224 if((conn->http_negotiate_state == GSS_AUTHDONE) &&
4225 (data->req.httpcode != 401)) {
4226 conn->http_negotiate_state = GSS_AUTHSUCC;
4227 }
4228 if((conn->proxy_negotiate_state == GSS_AUTHDONE) &&
4229 (data->req.httpcode != 407)) {
4230 conn->proxy_negotiate_state = GSS_AUTHSUCC;
4231 }
4232#endif
4233
4234 /* now, only output this if the header AND body are requested:
4235 */
4236 writetype = CLIENTWRITE_HEADER |
4237 ((k->httpcode/100 == 1) ? CLIENTWRITE_1XX : 0);
4238
4239 headerlen = Curl_dyn_len(s: &data->state.headerb);
4240 result = Curl_client_write(data, type: writetype,
4241 ptr: Curl_dyn_ptr(s: &data->state.headerb),
4242 len: headerlen);
4243 if(result)
4244 return result;
4245
4246 result = Curl_bump_headersize(data, delta: headerlen, FALSE);
4247 if(result)
4248 return result;
4249
4250 /*
4251 * When all the headers have been parsed, see if we should give
4252 * up and return an error.
4253 */
4254 if(http_should_fail(data)) {
4255 failf(data, fmt: "The requested URL returned error: %d",
4256 k->httpcode);
4257 return CURLE_HTTP_RETURNED_ERROR;
4258 }
4259
4260#ifdef USE_WEBSOCKETS
4261 /* All non-101 HTTP status codes are bad when wanting to upgrade to
4262 websockets */
4263 if(data->req.upgr101 == UPGR101_WS) {
4264 failf(data, "Refused WebSockets upgrade: %d", k->httpcode);
4265 return CURLE_HTTP_RETURNED_ERROR;
4266 }
4267#endif
4268
4269
4270 data->req.deductheadercount =
4271 (100 <= k->httpcode && 199 >= k->httpcode)?data->req.headerbytecount:0;
4272
4273 /* Curl_http_auth_act() checks what authentication methods
4274 * that are available and decides which one (if any) to
4275 * use. It will set 'newurl' if an auth method was picked. */
4276 result = Curl_http_auth_act(data);
4277
4278 if(result)
4279 return result;
4280
4281 if(k->httpcode >= 300) {
4282 if((!conn->bits.authneg) && !conn->bits.close &&
4283 !data->state.rewindbeforesend) {
4284 /*
4285 * General treatment of errors when about to send data. Including :
4286 * "417 Expectation Failed", while waiting for 100-continue.
4287 *
4288 * The check for close above is done simply because of something
4289 * else has already deemed the connection to get closed then
4290 * something else should've considered the big picture and we
4291 * avoid this check.
4292 *
4293 * rewindbeforesend indicates that something has told libcurl to
4294 * continue sending even if it gets discarded
4295 */
4296
4297 switch(data->state.httpreq) {
4298 case HTTPREQ_PUT:
4299 case HTTPREQ_POST:
4300 case HTTPREQ_POST_FORM:
4301 case HTTPREQ_POST_MIME:
4302 /* We got an error response. If this happened before the whole
4303 * request body has been sent we stop sending and mark the
4304 * connection for closure after we've read the entire response.
4305 */
4306 Curl_expire_done(data, id: EXPIRE_100_TIMEOUT);
4307 if(!k->upload_done) {
4308 if((k->httpcode == 417) && data->state.expect100header) {
4309 /* 417 Expectation Failed - try again without the Expect
4310 header */
4311 if(!k->writebytecount &&
4312 k->exp100 == EXP100_AWAITING_CONTINUE) {
4313 infof(data, "Got HTTP failure 417 while waiting for a 100");
4314 }
4315 else {
4316 infof(data, "Got HTTP failure 417 while sending data");
4317 streamclose(conn,
4318 "Stop sending data before everything sent");
4319 result = http_perhapsrewind(data, conn);
4320 if(result)
4321 return result;
4322 }
4323 data->state.disableexpect = TRUE;
4324 DEBUGASSERT(!data->req.newurl);
4325 data->req.newurl = strdup(data->state.url);
4326 Curl_done_sending(data, k);
4327 }
4328 else if(data->set.http_keep_sending_on_error) {
4329 infof(data, "HTTP error before end of send, keep sending");
4330 if(k->exp100 > EXP100_SEND_DATA) {
4331 k->exp100 = EXP100_SEND_DATA;
4332 k->keepon |= KEEP_SEND;
4333 }
4334 }
4335 else {
4336 infof(data, "HTTP error before end of send, stop sending");
4337 streamclose(conn, "Stop sending data before everything sent");
4338 result = Curl_done_sending(data, k);
4339 if(result)
4340 return result;
4341 k->upload_done = TRUE;
4342 if(data->state.expect100header)
4343 k->exp100 = EXP100_FAILED;
4344 }
4345 }
4346 break;
4347
4348 default: /* default label present to avoid compiler warnings */
4349 break;
4350 }
4351 }
4352
4353 if(data->state.rewindbeforesend &&
4354 (conn->writesockfd != CURL_SOCKET_BAD)) {
4355 /* We rewind before next send, continue sending now */
4356 infof(data, "Keep sending data to get tossed away");
4357 k->keepon |= KEEP_SEND;
4358 }
4359 }
4360
4361 if(!k->header) {
4362 /*
4363 * really end-of-headers.
4364 *
4365 * If we requested a "no body", this is a good time to get
4366 * out and return home.
4367 */
4368 if(data->req.no_body)
4369 *stop_reading = TRUE;
4370#ifndef CURL_DISABLE_RTSP
4371 else if((conn->handler->protocol & CURLPROTO_RTSP) &&
4372 (data->set.rtspreq == RTSPREQ_DESCRIBE) &&
4373 (k->size <= -1))
4374 /* Respect section 4.4 of rfc2326: If the Content-Length header is
4375 absent, a length 0 must be assumed. It will prevent libcurl from
4376 hanging on DESCRIBE request that got refused for whatever
4377 reason */
4378 *stop_reading = TRUE;
4379#endif
4380
4381 /* If max download size is *zero* (nothing) we already have
4382 nothing and can safely return ok now! But for HTTP/2, we'd
4383 like to call http2_handle_stream_close to properly close a
4384 stream. In order to do this, we keep reading until we
4385 close the stream. */
4386 if(0 == k->maxdownload
4387 && !Curl_conn_is_http2(data, conn, FIRSTSOCKET)
4388 && !Curl_conn_is_http3(data, conn, FIRSTSOCKET))
4389 *stop_reading = TRUE;
4390
4391 if(*stop_reading) {
4392 /* we make sure that this socket isn't read more now */
4393 k->keepon &= ~KEEP_RECV;
4394 }
4395
4396 Curl_debug(data, type: CURLINFO_HEADER_IN, ptr: str_start, size: headerlen);
4397 break; /* exit header line loop */
4398 }
4399
4400 /* We continue reading headers, reset the line-based header */
4401 Curl_dyn_reset(s: &data->state.headerb);
4402 continue;
4403 }
4404
4405 /*
4406 * Checks for special headers coming up.
4407 */
4408
4409 writetype = CLIENTWRITE_HEADER;
4410 if(!k->headerline++) {
4411 /* This is the first header, it MUST be the error code line
4412 or else we consider this to be the body right away! */
4413 bool fine_statusline = FALSE;
4414 if(conn->handler->protocol & PROTO_FAMILY_HTTP) {
4415 /*
4416 * https://datatracker.ietf.org/doc/html/rfc7230#section-3.1.2
4417 *
4418 * The response code is always a three-digit number in HTTP as the spec
4419 * says. We allow any three-digit number here, but we cannot make
4420 * guarantees on future behaviors since it isn't within the protocol.
4421 */
4422 int httpversion = 0;
4423 char *p = headp;
4424
4425 while(*p && ISBLANK(*p))
4426 p++;
4427 if(!strncmp(s1: p, s2: "HTTP/", n: 5)) {
4428 p += 5;
4429 switch(*p) {
4430 case '1':
4431 p++;
4432 if((p[0] == '.') && (p[1] == '0' || p[1] == '1')) {
4433 if(ISBLANK(p[2])) {
4434 httpversion = 10 + (p[1] - '0');
4435 p += 3;
4436 if(ISDIGIT(p[0]) && ISDIGIT(p[1]) && ISDIGIT(p[2])) {
4437 k->httpcode = (p[0] - '0') * 100 + (p[1] - '0') * 10 +
4438 (p[2] - '0');
4439 p += 3;
4440 if(ISSPACE(*p))
4441 fine_statusline = TRUE;
4442 }
4443 }
4444 }
4445 if(!fine_statusline) {
4446 failf(data, fmt: "Unsupported HTTP/1 subversion in response");
4447 return CURLE_UNSUPPORTED_PROTOCOL;
4448 }
4449 break;
4450 case '2':
4451 case '3':
4452 if(!ISBLANK(p[1]))
4453 break;
4454 httpversion = (*p - '0') * 10;
4455 p += 2;
4456 if(ISDIGIT(p[0]) && ISDIGIT(p[1]) && ISDIGIT(p[2])) {
4457 k->httpcode = (p[0] - '0') * 100 + (p[1] - '0') * 10 +
4458 (p[2] - '0');
4459 p += 3;
4460 if(!ISSPACE(*p))
4461 break;
4462 fine_statusline = TRUE;
4463 }
4464 break;
4465 default: /* unsupported */
4466 failf(data, fmt: "Unsupported HTTP version in response");
4467 return CURLE_UNSUPPORTED_PROTOCOL;
4468 }
4469 }
4470
4471 if(fine_statusline) {
4472 if(k->httpcode < 100) {
4473 failf(data, fmt: "Unsupported response code in HTTP response");
4474 return CURLE_UNSUPPORTED_PROTOCOL;
4475 }
4476 switch(httpversion) {
4477 case 10:
4478 case 11:
4479#ifdef USE_HTTP2
4480 case 20:
4481#endif
4482#ifdef ENABLE_QUIC
4483 case 30:
4484#endif
4485 conn->httpversion = (unsigned char)httpversion;
4486 break;
4487 default:
4488 failf(data, fmt: "Unsupported HTTP version (%u.%d) in response",
4489 httpversion/10, httpversion%10);
4490 return CURLE_UNSUPPORTED_PROTOCOL;
4491 }
4492
4493 if(k->upgr101 == UPGR101_RECEIVED) {
4494 /* supposedly upgraded to http2 now */
4495 if(conn->httpversion != 20)
4496 infof(data, "Lying server, not serving HTTP/2");
4497 }
4498 if(conn->httpversion < 20) {
4499 conn->bundle->multiuse = BUNDLE_NO_MULTIUSE;
4500 }
4501 }
4502 else {
4503 /* If user has set option HTTP200ALIASES,
4504 compare header line against list of aliases
4505 */
4506 statusline check =
4507 checkhttpprefix(data,
4508 s: Curl_dyn_ptr(s: &data->state.headerb),
4509 len: Curl_dyn_len(s: &data->state.headerb));
4510 if(check == STATUS_DONE) {
4511 fine_statusline = TRUE;
4512 k->httpcode = 200;
4513 conn->httpversion = 10;
4514 }
4515 }
4516 }
4517 else if(conn->handler->protocol & CURLPROTO_RTSP) {
4518 char *p = headp;
4519 while(*p && ISBLANK(*p))
4520 p++;
4521 if(!strncmp(s1: p, s2: "RTSP/", n: 5)) {
4522 p += 5;
4523 if(ISDIGIT(*p)) {
4524 p++;
4525 if((p[0] == '.') && ISDIGIT(p[1])) {
4526 if(ISBLANK(p[2])) {
4527 p += 3;
4528 if(ISDIGIT(p[0]) && ISDIGIT(p[1]) && ISDIGIT(p[2])) {
4529 k->httpcode = (p[0] - '0') * 100 + (p[1] - '0') * 10 +
4530 (p[2] - '0');
4531 p += 3;
4532 if(ISSPACE(*p)) {
4533 fine_statusline = TRUE;
4534 conn->httpversion = 11; /* RTSP acts like HTTP 1.1 */
4535 }
4536 }
4537 }
4538 }
4539 }
4540 if(!fine_statusline)
4541 return CURLE_WEIRD_SERVER_REPLY;
4542 }
4543 }
4544
4545 if(fine_statusline) {
4546 result = Curl_http_statusline(data, conn);
4547 if(result)
4548 return result;
4549 writetype |= CLIENTWRITE_STATUS;
4550 }
4551 else {
4552 k->header = FALSE; /* this is not a header line */
4553 break;
4554 }
4555 }
4556
4557 result = verify_header(data);
4558 if(result)
4559 return result;
4560
4561 result = Curl_http_header(data, conn, headp);
4562 if(result)
4563 return result;
4564
4565 /*
4566 * End of header-checks. Write them to the client.
4567 */
4568 if(k->httpcode/100 == 1)
4569 writetype |= CLIENTWRITE_1XX;
4570
4571 Curl_debug(data, type: CURLINFO_HEADER_IN, ptr: headp,
4572 size: Curl_dyn_len(s: &data->state.headerb));
4573
4574 result = Curl_client_write(data, type: writetype, ptr: headp,
4575 len: Curl_dyn_len(s: &data->state.headerb));
4576 if(result)
4577 return result;
4578
4579 result = Curl_bump_headersize(data, delta: Curl_dyn_len(s: &data->state.headerb),
4580 FALSE);
4581 if(result)
4582 return result;
4583
4584 Curl_dyn_reset(s: &data->state.headerb);
4585 }
4586 while(*k->str); /* header line within buffer */
4587
4588 /* We might have reached the end of the header part here, but
4589 there might be a non-header part left in the end of the read
4590 buffer. */
4591
4592 return CURLE_OK;
4593}
4594
4595
4596/* Decode HTTP status code string. */
4597CURLcode Curl_http_decode_status(int *pstatus, const char *s, size_t len)
4598{
4599 CURLcode result = CURLE_BAD_FUNCTION_ARGUMENT;
4600 int status = 0;
4601 int i;
4602
4603 if(len != 3)
4604 goto out;
4605
4606 for(i = 0; i < 3; ++i) {
4607 char c = s[i];
4608
4609 if(c < '0' || c > '9')
4610 goto out;
4611
4612 status *= 10;
4613 status += c - '0';
4614 }
4615 result = CURLE_OK;
4616out:
4617 *pstatus = result? -1 : status;
4618 return result;
4619}
4620
4621/* simple implementation of strndup(), which isn't portable */
4622static char *my_strndup(const char *ptr, size_t len)
4623{
4624 char *copy = malloc(len + 1);
4625 if(!copy)
4626 return NULL;
4627 memcpy(dest: copy, src: ptr, n: len);
4628 copy[len] = '\0';
4629 return copy;
4630}
4631
4632CURLcode Curl_http_req_make(struct httpreq **preq,
4633 const char *method, size_t m_len,
4634 const char *scheme, size_t s_len,
4635 const char *authority, size_t a_len,
4636 const char *path, size_t p_len)
4637{
4638 struct httpreq *req;
4639 CURLcode result = CURLE_OUT_OF_MEMORY;
4640
4641 DEBUGASSERT(method);
4642 if(m_len + 1 >= sizeof(req->method))
4643 return CURLE_BAD_FUNCTION_ARGUMENT;
4644
4645 req = calloc(1, sizeof(*req));
4646 if(!req)
4647 goto out;
4648 memcpy(dest: req->method, src: method, n: m_len);
4649 if(scheme) {
4650 req->scheme = my_strndup(ptr: scheme, len: s_len);
4651 if(!req->scheme)
4652 goto out;
4653 }
4654 if(authority) {
4655 req->authority = my_strndup(ptr: authority, len: a_len);
4656 if(!req->authority)
4657 goto out;
4658 }
4659 if(path) {
4660 req->path = my_strndup(ptr: path, len: p_len);
4661 if(!req->path)
4662 goto out;
4663 }
4664 Curl_dynhds_init(dynhds: &req->headers, max_entries: 0, DYN_HTTP_REQUEST);
4665 Curl_dynhds_init(dynhds: &req->trailers, max_entries: 0, DYN_HTTP_REQUEST);
4666 result = CURLE_OK;
4667
4668out:
4669 if(result && req)
4670 Curl_http_req_free(req);
4671 *preq = result? NULL : req;
4672 return result;
4673}
4674
4675static CURLcode req_assign_url_authority(struct httpreq *req, CURLU *url)
4676{
4677 char *user, *pass, *host, *port;
4678 struct dynbuf buf;
4679 CURLUcode uc;
4680 CURLcode result = CURLE_URL_MALFORMAT;
4681
4682 user = pass = host = port = NULL;
4683 Curl_dyn_init(s: &buf, DYN_HTTP_REQUEST);
4684
4685 uc = curl_url_get(handle: url, what: CURLUPART_HOST, part: &host, flags: 0);
4686 if(uc && uc != CURLUE_NO_HOST)
4687 goto out;
4688 if(!host) {
4689 req->authority = NULL;
4690 result = CURLE_OK;
4691 goto out;
4692 }
4693
4694 uc = curl_url_get(handle: url, what: CURLUPART_PORT, part: &port, CURLU_NO_DEFAULT_PORT);
4695 if(uc && uc != CURLUE_NO_PORT)
4696 goto out;
4697 uc = curl_url_get(handle: url, what: CURLUPART_USER, part: &user, flags: 0);
4698 if(uc && uc != CURLUE_NO_USER)
4699 goto out;
4700 if(user) {
4701 uc = curl_url_get(handle: url, what: CURLUPART_PASSWORD, part: &pass, flags: 0);
4702 if(uc && uc != CURLUE_NO_PASSWORD)
4703 goto out;
4704 }
4705
4706 if(user) {
4707 result = Curl_dyn_add(s: &buf, str: user);
4708 if(result)
4709 goto out;
4710 if(pass) {
4711 result = Curl_dyn_addf(s: &buf, fmt: ":%s", pass);
4712 if(result)
4713 goto out;
4714 }
4715 result = Curl_dyn_add(s: &buf, str: "@");
4716 if(result)
4717 goto out;
4718 }
4719 result = Curl_dyn_add(s: &buf, str: host);
4720 if(result)
4721 goto out;
4722 if(port) {
4723 result = Curl_dyn_addf(s: &buf, fmt: ":%s", port);
4724 if(result)
4725 goto out;
4726 }
4727 req->authority = strdup(Curl_dyn_ptr(&buf));
4728 if(!req->authority)
4729 goto out;
4730 result = CURLE_OK;
4731
4732out:
4733 free(user);
4734 free(pass);
4735 free(host);
4736 free(port);
4737 Curl_dyn_free(s: &buf);
4738 return result;
4739}
4740
4741static CURLcode req_assign_url_path(struct httpreq *req, CURLU *url)
4742{
4743 char *path, *query;
4744 struct dynbuf buf;
4745 CURLUcode uc;
4746 CURLcode result = CURLE_URL_MALFORMAT;
4747
4748 path = query = NULL;
4749 Curl_dyn_init(s: &buf, DYN_HTTP_REQUEST);
4750
4751 uc = curl_url_get(handle: url, what: CURLUPART_PATH, part: &path, CURLU_PATH_AS_IS);
4752 if(uc)
4753 goto out;
4754 uc = curl_url_get(handle: url, what: CURLUPART_QUERY, part: &query, flags: 0);
4755 if(uc && uc != CURLUE_NO_QUERY)
4756 goto out;
4757
4758 if(!path && !query) {
4759 req->path = NULL;
4760 }
4761 else if(path && !query) {
4762 req->path = path;
4763 path = NULL;
4764 }
4765 else {
4766 if(path) {
4767 result = Curl_dyn_add(s: &buf, str: path);
4768 if(result)
4769 goto out;
4770 }
4771 if(query) {
4772 result = Curl_dyn_addf(s: &buf, fmt: "?%s", query);
4773 if(result)
4774 goto out;
4775 }
4776 req->path = strdup(Curl_dyn_ptr(&buf));
4777 if(!req->path)
4778 goto out;
4779 }
4780 result = CURLE_OK;
4781
4782out:
4783 free(path);
4784 free(query);
4785 Curl_dyn_free(s: &buf);
4786 return result;
4787}
4788
4789CURLcode Curl_http_req_make2(struct httpreq **preq,
4790 const char *method, size_t m_len,
4791 CURLU *url, const char *scheme_default)
4792{
4793 struct httpreq *req;
4794 CURLcode result = CURLE_OUT_OF_MEMORY;
4795 CURLUcode uc;
4796
4797 DEBUGASSERT(method);
4798 if(m_len + 1 >= sizeof(req->method))
4799 return CURLE_BAD_FUNCTION_ARGUMENT;
4800
4801 req = calloc(1, sizeof(*req));
4802 if(!req)
4803 goto out;
4804 memcpy(dest: req->method, src: method, n: m_len);
4805
4806 uc = curl_url_get(handle: url, what: CURLUPART_SCHEME, part: &req->scheme, flags: 0);
4807 if(uc && uc != CURLUE_NO_SCHEME)
4808 goto out;
4809 if(!req->scheme && scheme_default) {
4810 req->scheme = strdup(scheme_default);
4811 if(!req->scheme)
4812 goto out;
4813 }
4814
4815 result = req_assign_url_authority(req, url);
4816 if(result)
4817 goto out;
4818 result = req_assign_url_path(req, url);
4819 if(result)
4820 goto out;
4821
4822 Curl_dynhds_init(dynhds: &req->headers, max_entries: 0, DYN_HTTP_REQUEST);
4823 Curl_dynhds_init(dynhds: &req->trailers, max_entries: 0, DYN_HTTP_REQUEST);
4824 result = CURLE_OK;
4825
4826out:
4827 if(result && req)
4828 Curl_http_req_free(req);
4829 *preq = result? NULL : req;
4830 return result;
4831}
4832
4833void Curl_http_req_free(struct httpreq *req)
4834{
4835 if(req) {
4836 free(req->scheme);
4837 free(req->authority);
4838 free(req->path);
4839 Curl_dynhds_free(dynhds: &req->headers);
4840 Curl_dynhds_free(dynhds: &req->trailers);
4841 free(req);
4842 }
4843}
4844
4845struct name_const {
4846 const char *name;
4847 size_t namelen;
4848};
4849
4850static struct name_const H2_NON_FIELD[] = {
4851 { STRCONST("Host") },
4852 { STRCONST("Upgrade") },
4853 { STRCONST("Connection") },
4854 { STRCONST("Keep-Alive") },
4855 { STRCONST("Proxy-Connection") },
4856 { STRCONST("Transfer-Encoding") },
4857};
4858
4859static bool h2_non_field(const char *name, size_t namelen)
4860{
4861 size_t i;
4862 for(i = 0; i < sizeof(H2_NON_FIELD)/sizeof(H2_NON_FIELD[0]); ++i) {
4863 if(namelen < H2_NON_FIELD[i].namelen)
4864 return FALSE;
4865 if(namelen == H2_NON_FIELD[i].namelen &&
4866 strcasecompare(H2_NON_FIELD[i].name, name))
4867 return TRUE;
4868 }
4869 return FALSE;
4870}
4871
4872CURLcode Curl_http_req_to_h2(struct dynhds *h2_headers,
4873 struct httpreq *req, struct Curl_easy *data)
4874{
4875 const char *scheme = NULL, *authority = NULL;
4876 struct dynhds_entry *e;
4877 size_t i;
4878 CURLcode result;
4879
4880 DEBUGASSERT(req);
4881 DEBUGASSERT(h2_headers);
4882
4883 if(req->scheme) {
4884 scheme = req->scheme;
4885 }
4886 else if(strcmp(s1: "CONNECT", s2: req->method)) {
4887 scheme = Curl_checkheaders(data, STRCONST(HTTP_PSEUDO_SCHEME));
4888 if(scheme) {
4889 scheme += sizeof(HTTP_PSEUDO_SCHEME);
4890 while(*scheme && ISBLANK(*scheme))
4891 scheme++;
4892 infof(data, "set pseudo header %s to %s", HTTP_PSEUDO_SCHEME, scheme);
4893 }
4894 else {
4895 scheme = (data->conn && data->conn->handler->flags & PROTOPT_SSL)?
4896 "https" : "http";
4897 }
4898 }
4899
4900 if(req->authority) {
4901 authority = req->authority;
4902 }
4903 else {
4904 e = Curl_dynhds_get(dynhds: &req->headers, STRCONST("Host"));
4905 if(e)
4906 authority = e->value;
4907 }
4908
4909 Curl_dynhds_reset(dynhds: h2_headers);
4910 Curl_dynhds_set_opts(dynhds: h2_headers, DYNHDS_OPT_LOWERCASE);
4911 result = Curl_dynhds_add(dynhds: h2_headers, STRCONST(HTTP_PSEUDO_METHOD),
4912 value: req->method, valuelen: strlen(s: req->method));
4913 if(!result && scheme) {
4914 result = Curl_dynhds_add(dynhds: h2_headers, STRCONST(HTTP_PSEUDO_SCHEME),
4915 value: scheme, valuelen: strlen(s: scheme));
4916 }
4917 if(!result && authority) {
4918 result = Curl_dynhds_add(dynhds: h2_headers, STRCONST(HTTP_PSEUDO_AUTHORITY),
4919 value: authority, valuelen: strlen(s: authority));
4920 }
4921 if(!result && req->path) {
4922 result = Curl_dynhds_add(dynhds: h2_headers, STRCONST(HTTP_PSEUDO_PATH),
4923 value: req->path, valuelen: strlen(s: req->path));
4924 }
4925 for(i = 0; !result && i < Curl_dynhds_count(dynhds: &req->headers); ++i) {
4926 e = Curl_dynhds_getn(dynhds: &req->headers, n: i);
4927 if(!h2_non_field(name: e->name, namelen: e->namelen)) {
4928 result = Curl_dynhds_add(dynhds: h2_headers, name: e->name, namelen: e->namelen,
4929 value: e->value, valuelen: e->valuelen);
4930 }
4931 }
4932
4933 return result;
4934}
4935
4936CURLcode Curl_http_resp_make(struct http_resp **presp,
4937 int status,
4938 const char *description)
4939{
4940 struct http_resp *resp;
4941 CURLcode result = CURLE_OUT_OF_MEMORY;
4942
4943 resp = calloc(1, sizeof(*resp));
4944 if(!resp)
4945 goto out;
4946
4947 resp->status = status;
4948 if(description) {
4949 resp->description = strdup(description);
4950 if(!resp->description)
4951 goto out;
4952 }
4953 Curl_dynhds_init(dynhds: &resp->headers, max_entries: 0, DYN_HTTP_REQUEST);
4954 Curl_dynhds_init(dynhds: &resp->trailers, max_entries: 0, DYN_HTTP_REQUEST);
4955 result = CURLE_OK;
4956
4957out:
4958 if(result && resp)
4959 Curl_http_resp_free(resp);
4960 *presp = result? NULL : resp;
4961 return result;
4962}
4963
4964void Curl_http_resp_free(struct http_resp *resp)
4965{
4966 if(resp) {
4967 free(resp->description);
4968 Curl_dynhds_free(dynhds: &resp->headers);
4969 Curl_dynhds_free(dynhds: &resp->trailers);
4970 if(resp->prev)
4971 Curl_http_resp_free(resp: resp->prev);
4972 free(resp);
4973 }
4974}
4975
4976#endif /* CURL_DISABLE_HTTP */
4977