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