1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2003, Daniel Stenberg, <daniel@haxx.se>, et al.
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 http://curl.haxx.se/docs/copyright.html.
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.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
22 ***************************************************************************/
26 #ifndef CURL_DISABLE_HTTP
27 /* -- WIN32 approved -- */
33 #include <sys/types.h>
38 #if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
43 #ifdef HAVE_SYS_SOCKET_H
44 #include <sys/socket.h>
46 #ifdef HAVE_NETINET_IN_H
47 #include <netinet/in.h>
52 #ifdef TIME_WITH_SYS_TIME
57 #include <sys/resource.h>
62 #ifdef HAVE_ARPA_INET_H
63 #include <arpa/inet.h>
68 #include <sys/ioctl.h>
71 #ifdef HAVE_SYS_PARAM_H
72 #include <sys/param.h>
75 #ifdef HAVE_SYS_SELECT_H
76 #include <sys/select.h>
83 #include <curl/curl.h>
93 #define _MPRINTF_REPLACE /* use our functions only */
94 #include <curl/mprintf.h>
96 /* The last #include file should be: */
101 /* fread() emulation to provide POST and/or request data */
102 static int readmoredata(char *buffer,
107 struct connectdata *conn = (struct connectdata *)userp;
108 struct HTTP *http = conn->proto.http;
109 int fullsize = size * nitems;
111 if(0 == http->postsize)
112 /* nothing to return */
115 /* make sure that a HTTP request is never sent away chunked! */
116 conn->bits.forbidchunk= (http->sending == HTTPSEND_REQUEST)?TRUE:FALSE;
118 if(http->postsize <= fullsize) {
119 memcpy(buffer, http->postdata, http->postsize);
120 fullsize = http->postsize;
122 if(http->backup.postsize) {
123 /* move backup data into focus and continue on that */
124 http->postdata = http->backup.postdata;
125 http->postsize = http->backup.postsize;
126 conn->fread = http->backup.fread;
127 conn->fread_in = http->backup.fread_in;
129 http->sending++; /* move one step up */
131 http->backup.postsize=0;
139 memcpy(buffer, http->postdata, fullsize);
140 http->postdata += fullsize;
141 http->postsize -= fullsize;
146 /* ------------------------------------------------------------------------- */
148 * The add_buffer series of functions are used to build one large memory chunk
149 * from repeated function invokes. Used so that the entire HTTP request can
158 typedef struct send_buffer send_buffer;
161 add_buffer(send_buffer *in, const void *inptr, size_t size);
164 * add_buffer_init() returns a fine buffer struct
167 send_buffer *add_buffer_init(void)
170 blonk=(send_buffer *)malloc(sizeof(send_buffer));
172 memset(blonk, 0, sizeof(send_buffer));
175 return NULL; /* failed, go home */
179 * add_buffer_send() sends a buffer and frees all associated memory.
182 CURLcode add_buffer_send(send_buffer *in,
184 struct connectdata *conn,
185 long *bytes_written) /* add the number of sent
186 bytes to this counter */
192 struct HTTP *http = conn->proto.http;
194 /* The looping below is required since we use non-blocking sockets, but due
195 to the circumstances we will just loop and try again and again etc */
198 size = in->size_used;
200 res = Curl_write(conn, sockfd, ptr, size, &amount);
202 if(CURLE_OK == res) {
204 if(conn->data->set.verbose)
205 /* this data _may_ contain binary stuff */
206 Curl_debug(conn->data, CURLINFO_HEADER_OUT, ptr, amount);
208 *bytes_written += amount;
211 /* The whole request could not be sent in one system call. We must queue
212 it up and send it later when we get the chance. We must not loop here
213 and wait until it might work again. */
218 /* backup the currently set pointers */
219 http->backup.fread = conn->fread;
220 http->backup.fread_in = conn->fread_in;
221 http->backup.postdata = http->postdata;
222 http->backup.postsize = http->postsize;
224 /* set the new pointers for the request-sending */
225 conn->fread = (curl_read_callback)readmoredata;
226 conn->fread_in = (void *)conn;
227 http->postdata = ptr;
228 http->postsize = size;
230 http->send_buffer = in;
231 http->sending = HTTPSEND_REQUEST;
235 http->sending = HTTPSEND_BODY;
236 /* the full buffer was sent, clean up and return */
247 * add_bufferf() builds a buffer from the formatted input
250 CURLcode add_bufferf(send_buffer *in, const char *fmt, ...)
252 CURLcode result = CURLE_OUT_OF_MEMORY;
256 s = vaprintf(fmt, ap); /* this allocs a new string to append */
260 result = add_buffer(in, s, strlen(s));
267 * add_buffer() appends a memory chunk to the existing one
270 CURLcode add_buffer(send_buffer *in, const void *inptr, size_t size)
276 ((in->size_used + size) > (in->size_max - 1))) {
277 new_size = (in->size_used+size)*2;
279 /* we have a buffer, enlarge the existing one */
280 new_rb = (char *)realloc(in->buffer, new_size);
282 /* create a new buffer */
283 new_rb = (char *)malloc(new_size);
286 return CURLE_OUT_OF_MEMORY;
289 in->size_max = new_size;
291 memcpy(&in->buffer[in->size_used], inptr, size);
293 in->size_used += size;
298 /* end of the add_buffer functions */
299 /* ------------------------------------------------------------------------- */
302 * Curl_compareheader()
304 * Returns TRUE if 'headerline' contains the 'header' with given 'content'.
305 * Pass headers WITH the colon.
308 Curl_compareheader(char *headerline, /* line to check */
309 const char *header, /* header keyword _with_ colon */
310 const char *content) /* content string to find */
312 /* RFC2616, section 4.2 says: "Each header field consists of a name followed
313 * by a colon (":") and the field value. Field names are case-insensitive.
314 * The field value MAY be preceded by any amount of LWS, though a single SP
317 size_t hlen = strlen(header);
323 if(!strnequal(headerline, header, hlen))
324 return FALSE; /* doesn't start with header */
326 /* pass the header */
327 start = &headerline[hlen];
329 /* pass all white spaces */
330 while(*start && isspace((int)*start))
333 /* find the end of the header line */
334 end = strchr(start, '\r'); /* lines end with CRLF */
336 /* in case there's a non-standard compliant line here */
337 end = strchr(start, '\n');
340 /* hm, there's no line ending here, use the zero byte! */
341 end = strchr(start, '\0');
344 len = end-start; /* length of the content part of the input line */
345 clen = strlen(content); /* length of the word to find */
347 /* find the content string in the rest of the line */
348 for(;len>=clen;len--, start++) {
349 if(strnequal(start, content, clen))
350 return TRUE; /* match! */
353 return FALSE; /* no match */
357 * This function checks the linked list of custom HTTP headers for a particular
360 static char *checkheaders(struct SessionHandle *data, const char *thisheader)
362 struct curl_slist *head;
363 size_t thislen = strlen(thisheader);
365 for(head = data->set.headers; head; head=head->next) {
366 if(strnequal(head->data, thisheader, thislen))
373 * ConnectHTTPProxyTunnel() requires that we're connected to a HTTP proxy. This
374 * function will issue the necessary commands to get a seamless tunnel through
375 * this proxy. After that, the socket can be used just as a normal socket.
378 CURLcode Curl_ConnectHTTPProxyTunnel(struct connectdata *conn,
380 char *hostname, int remote_port)
384 struct SessionHandle *data=conn->data;
388 int nread; /* total size read */
389 int perline; /* count bytes per line */
393 int timeout = 3600; /* default timeout in seconds */
394 struct timeval interval;
400 #define SELECT_ERROR 1
401 #define SELECT_TIMEOUT 2
402 int error = SELECT_OK;
404 infof(data, "Establish HTTP proxy tunnel to %s:%d\n", hostname, remote_port);
406 /* OK, now send the connect request to the proxy */
408 Curl_sendf(tunnelsocket, conn,
409 "CONNECT %s:%d HTTP/1.0\015\012"
413 hostname, remote_port,
414 (conn->bits.proxy_user_passwd)?conn->allocptr.proxyuserpwd:"",
415 (data->set.useragent?conn->allocptr.uagent:"")
418 failf(data, "Failed sending CONNECT to proxy");
422 /* Now, read the full reply we get from the proxy */
425 if(data->set.timeout) {
426 /* if timeout is requested, find out how much remaining time we have */
427 timeout = data->set.timeout - /* timeout time */
428 Curl_tvdiff(Curl_tvnow(), conn->now)/1000; /* spent time */
430 failf(data, "Transfer aborted due to timeout");
431 return -SELECT_TIMEOUT; /* already too little time */
435 FD_ZERO (&readfd); /* clear it */
436 FD_SET (tunnelsocket, &readfd); /* read socket */
438 /* get this in a backup variable to be able to restore it on each lap in the
442 ptr=data->state.buffer;
449 while((nread<BUFSIZE) && (keepon && !error)) {
450 readfd = rkeepfd; /* set every lap */
451 interval.tv_sec = timeout;
452 interval.tv_usec = 0;
454 switch (select (tunnelsocket+1, &readfd, NULL, NULL, &interval)) {
455 case -1: /* select() error, stop reading */
456 error = SELECT_ERROR;
457 failf(data, "Transfer aborted due to select() error");
459 case 0: /* timeout */
460 error = SELECT_TIMEOUT;
461 failf(data, "Transfer aborted due to timeout");
465 * This code previously didn't use the kerberos sec_read() code
466 * to read, but when we use Curl_read() it may do so. Do confirm
467 * that this is still ok and then remove this comment!
469 res= Curl_read(conn, tunnelsocket, ptr, BUFSIZE-nread,
473 continue; /* go loop yourself */
476 else if(gotbytes <= 0) {
478 error = SELECT_ERROR;
479 failf(data, "Connection aborted");
482 /* we got a whole chunk of data, which can be anything from one
483 * byte to a set of lines and possibly just a piece of the last
488 for(i = 0; i < gotbytes; ptr++, i++) {
489 perline++; /* amount of bytes in this line so far */
491 /* a newline is CRLF in ftp-talk, so the CR is ignored as
492 the line isn't really terminated until the LF comes */
494 if('\r' == line_start[0]) {
497 break; /* breaks out of loop, not switch */
500 /* output debug output if that is requested */
501 if(data->set.verbose)
502 Curl_debug(data, CURLINFO_HEADER_IN, line_start, perline);
504 if(2 == sscanf(line_start, "HTTP/1.%d %d",
510 perline=0; /* line starts over here */
517 } /* while there's buffer left and loop is requested */
520 return CURLE_RECV_ERROR;
522 if(200 != httperror) {
524 /* Added Nov 6 1998 */
525 failf(data, "Proxy requires authorization!");
527 failf(data, "Received error code %d from proxy", httperror);
528 return CURLE_RECV_ERROR;
531 infof (data, "Proxy replied to CONNECT request\n");
536 * HTTP stuff to do at connect-time.
538 CURLcode Curl_http_connect(struct connectdata *conn)
540 struct SessionHandle *data;
545 /* If we are not using a proxy and we want a secure connection,
546 * perform SSL initialization & connection now.
547 * If using a proxy with https, then we must tell the proxy to CONNECT
548 * us to the host we want to talk to. Only after the connect
549 * has occured, can we start talking SSL
552 if(conn->bits.httpproxy &&
553 ((conn->protocol & PROT_HTTPS) || data->set.tunnel_thru_httpproxy)) {
555 /* either HTTPS over proxy, OR explicitly asked for a tunnel */
556 result = Curl_ConnectHTTPProxyTunnel(conn, conn->firstsocket,
557 conn->hostname, conn->remote_port);
558 if(CURLE_OK != result)
562 if(conn->protocol & PROT_HTTPS) {
563 /* now, perform the SSL initialization for this socket */
564 result = Curl_SSLConnect(conn);
569 if(conn->bits.user_passwd && !data->state.this_is_a_follow) {
570 /* Authorization: is requested, this is not a followed location, get the
571 original host name */
572 if (data->state.auth_host)
573 /* Free to avoid leaking memory on multiple requests*/
574 free(data->state.auth_host);
576 data->state.auth_host = strdup(conn->hostname);
582 CURLcode Curl_http_done(struct connectdata *conn)
584 struct SessionHandle *data;
588 http=conn->proto.http;
590 /* set the proper values (possibly modified on POST) */
591 conn->fread = data->set.fread; /* restore */
592 conn->fread_in = data->set.in; /* restore */
594 if(http->send_buffer) {
595 send_buffer *buff = http->send_buffer;
601 if(HTTPREQ_POST_FORM == data->set.httpreq) {
602 conn->bytecount = http->readbytecount + http->writebytecount;
604 Curl_formclean(http->sendit); /* Now free that whole lot */
606 else if(HTTPREQ_PUT == data->set.httpreq)
607 conn->bytecount = http->readbytecount + http->writebytecount;
609 if(0 == (http->readbytecount + conn->headerbytecount)) {
610 /* nothing was read from the HTTP server, this can't be right
611 so we return an error here */
612 failf(data, "Empty reply from server");
613 return CURLE_GOT_NOTHING;
619 CURLcode Curl_http(struct connectdata *conn)
621 struct SessionHandle *data=conn->data;
622 char *buf = data->state.buffer; /* this is a short cut to the buffer */
623 CURLcode result=CURLE_OK;
625 struct Cookie *co=NULL; /* no cookies from start */
626 char *ppath = conn->ppath; /* three previous function arguments */
627 char *host = conn->name;
628 const char *te = ""; /* tranfer-encoding */
631 if(!conn->proto.http) {
632 /* Only allocate this struct if we don't already have it! */
634 http = (struct HTTP *)malloc(sizeof(struct HTTP));
636 return CURLE_OUT_OF_MEMORY;
637 memset(http, 0, sizeof(struct HTTP));
638 conn->proto.http = http;
641 http = conn->proto.http;
643 /* We default to persistant connections */
644 conn->bits.close = FALSE;
646 if ( (conn->protocol&(PROT_HTTP|PROT_FTP)) &&
648 data->set.httpreq = HTTPREQ_PUT;
651 /* The User-Agent string has been built in url.c already, because it might
652 have been used in the proxy connect, but if we have got a header with
653 the user-agent string specified, we erase the previously made string
655 if(checkheaders(data, "User-Agent:") && conn->allocptr.uagent) {
656 free(conn->allocptr.uagent);
657 conn->allocptr.uagent=NULL;
660 if((conn->bits.user_passwd) && !checkheaders(data, "Authorization:")) {
663 /* To prevent the user+password to get sent to other than the original
664 host due to a location-follow, we do some weirdo checks here */
665 if(!data->state.this_is_a_follow ||
666 !data->state.auth_host ||
667 curl_strequal(data->state.auth_host, conn->hostname) ||
668 data->set.http_disable_hostname_check_before_authentication) {
669 sprintf(data->state.buffer, "%s:%s",
670 data->state.user, data->state.passwd);
671 if(Curl_base64_encode(data->state.buffer, strlen(data->state.buffer),
672 &authorization) >= 0) {
673 if(conn->allocptr.userpwd)
674 free(conn->allocptr.userpwd);
675 conn->allocptr.userpwd = aprintf( "Authorization: Basic %s\015\012",
681 if((data->change.referer) && !checkheaders(data, "Referer:")) {
682 if(conn->allocptr.ref)
683 free(conn->allocptr.ref);
684 conn->allocptr.ref = aprintf("Referer: %s\015\012", data->change.referer);
686 if(data->set.cookie && !checkheaders(data, "Cookie:")) {
687 if(conn->allocptr.cookie)
688 free(conn->allocptr.cookie);
689 conn->allocptr.cookie = aprintf("Cookie: %s\015\012", data->set.cookie);
692 if(!conn->bits.upload_chunky && (data->set.httpreq != HTTPREQ_GET)) {
693 /* not a chunky transfer but data is to be sent */
694 char *ptr = checkheaders(data, "Transfer-Encoding:");
696 /* Some kind of TE is requested, check if 'chunked' is chosen */
697 if(Curl_compareheader(ptr, "Transfer-Encoding:", "chunked"))
698 /* we have been told explicitly to upload chunky so deal with it! */
699 conn->bits.upload_chunky = TRUE;
703 if(conn->bits.upload_chunky) {
704 /* RFC2616 section 4.4:
705 Messages MUST NOT include both a Content-Length header field and a
706 non-identity transfer-coding. If the message does include a non-
707 identity transfer-coding, the Content-Length MUST be ignored. */
709 if(!checkheaders(data, "Transfer-Encoding:")) {
710 te = "Transfer-Encoding: chunked\r\n";
713 /* The "Transfer-Encoding:" header was already added. */
718 ptr = checkheaders(data, "Host:");
720 /* If we have a given custom Host: header, we extract the host name
721 in order to possibly use it for cookie reasons later on. */
722 char *start = ptr+strlen("Host:");
724 while(*start && isspace((int)*start ))
726 ptr = start; /* start host-scanning here */
728 /* scan through the string to find the end (space or colon) */
729 while(*ptr && !isspace((int)*ptr) && !(':'==*ptr))
734 conn->allocptr.cookiehost = malloc(len+1);
735 if(!conn->allocptr.cookiehost)
736 return CURLE_OUT_OF_MEMORY;
737 memcpy(conn->allocptr.cookiehost, start, len);
738 conn->allocptr.cookiehost[len]=0;
742 /* if ptr_host is already set, it is almost OK since we only re-use
743 connections to the very same host and port, but when we use a HTTP
744 proxy we have a persistant connect and yet we must change the Host:
747 if(conn->allocptr.host)
748 free(conn->allocptr.host);
750 /* When building Host: headers, we must put the host name within
751 [brackets] if the host name is a plain IPv6-address. RFC2732-style. */
753 if(((conn->protocol&PROT_HTTPS) && (conn->remote_port == PORT_HTTPS)) ||
754 (!(conn->protocol&PROT_HTTPS) && (conn->remote_port == PORT_HTTP)) )
755 /* If (HTTPS on port 443) OR (non-HTTPS on port 80) then don't include
756 the port number in the host string */
757 conn->allocptr.host = aprintf("Host: %s%s%s\r\n",
758 conn->bits.ipv6_ip?"[":"",
760 conn->bits.ipv6_ip?"]":"");
762 conn->allocptr.host = aprintf("Host: %s%s%s:%d\r\n",
763 conn->bits.ipv6_ip?"[":"",
765 conn->bits.ipv6_ip?"]":"",
770 co = Curl_cookie_getlist(data->cookies,
771 conn->allocptr.cookiehost?
772 conn->allocptr.cookiehost:host, ppath,
773 (bool)(conn->protocol&PROT_HTTPS?TRUE:FALSE));
776 if (data->change.proxy && *data->change.proxy &&
777 !data->set.tunnel_thru_httpproxy &&
778 !(conn->protocol&PROT_HTTPS)) {
779 /* The path sent to the proxy is in fact the entire URL */
780 ppath = data->change.url;
782 if(HTTPREQ_POST_FORM == data->set.httpreq) {
783 /* we must build the whole darned post sequence first, so that we have
784 a size of the whole shebang before we start to send it */
785 result = Curl_getFormData(&http->sendit, data->set.httppost,
787 if(CURLE_OK != result) {
788 /* Curl_getFormData() doesn't use failf() */
789 failf(data, "failed creating formpost data");
795 if(!checkheaders(data, "Pragma:"))
796 http->p_pragma = "Pragma: no-cache\r\n";
798 if(!checkheaders(data, "Accept:"))
799 http->p_accept = "Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*\r\n";
801 if(( (HTTPREQ_POST == data->set.httpreq) ||
802 (HTTPREQ_POST_FORM == data->set.httpreq) ||
803 (HTTPREQ_PUT == data->set.httpreq) ) &&
805 /**********************************************************************
806 * Resuming upload in HTTP means that we PUT or POST and that we have
807 * got a resume_from value set. The resume value has already created
808 * a Range: header that will be passed along. We need to "fast forward"
809 * the file the given number of bytes and decrease the assume upload
810 * file size before we continue this venture in the dark lands of HTTP.
811 *********************************************************************/
813 if(conn->resume_from < 0 ) {
815 * This is meant to get the size of the present remote-file by itself.
816 * We don't support this now. Bail out!
818 conn->resume_from = 0;
821 if(conn->resume_from) {
822 /* do we still game? */
825 /* Now, let's read off the proper amount of bytes from the
826 input. If we knew it was a proper file we could've just
827 fseek()ed but we only have a stream here */
829 int readthisamountnow = (conn->resume_from - passed);
832 if(readthisamountnow > BUFSIZE)
833 readthisamountnow = BUFSIZE;
836 data->set.fread(data->state.buffer, 1, readthisamountnow,
839 passed += actuallyread;
840 if(actuallyread != readthisamountnow) {
841 failf(data, "Could only read %d bytes from the input",
843 return CURLE_READ_ERROR;
845 } while(passed != conn->resume_from); /* loop until done */
847 /* now, decrease the size of the read */
848 if(data->set.infilesize>0) {
849 data->set.infilesize -= conn->resume_from;
851 if(data->set.infilesize <= 0) {
852 failf(data, "File already completely uploaded");
853 return CURLE_PARTIAL_FILE;
856 /* we've passed, proceed as normal */
859 if(conn->bits.use_range) {
861 * A range is selected. We use different headers whether we're downloading
862 * or uploading and we always let customized headers override our internal
863 * ones if any such are specified.
865 if((data->set.httpreq == HTTPREQ_GET) &&
866 !checkheaders(data, "Range:")) {
867 /* if a line like this was already allocated, free the previous one */
868 if(conn->allocptr.rangeline)
869 free(conn->allocptr.rangeline);
870 conn->allocptr.rangeline = aprintf("Range: bytes=%s\r\n", conn->range);
872 else if((data->set.httpreq != HTTPREQ_GET) &&
873 !checkheaders(data, "Content-Range:")) {
875 if(conn->resume_from) {
876 /* This is because "resume" was selected */
877 long total_expected_size= conn->resume_from + data->set.infilesize;
878 conn->allocptr.rangeline = aprintf("Content-Range: bytes %s%ld/%ld\r\n",
879 conn->range, total_expected_size-1,
880 total_expected_size);
883 /* Range was selected and then we just pass the incoming range and
885 conn->allocptr.rangeline = aprintf("Content-Range: bytes %s/%d\r\n",
886 conn->range, data->set.infilesize);
892 /* Use 1.1 unless the use specificly asked for 1.0 */
893 const char *httpstring=
894 data->set.httpversion==CURL_HTTP_VERSION_1_0?"1.0":"1.1";
896 send_buffer *req_buffer;
897 struct curl_slist *headers=data->set.headers;
899 /* initialize a dynamic send-buffer */
900 req_buffer = add_buffer_init();
902 /* add the main request stuff */
903 add_bufferf(req_buffer,
904 "%s " /* GET/HEAD/POST/PUT */
905 "%s HTTP/%s\r\n" /* path */
906 "%s" /* proxyuserpwd */
909 "%s" /* user agent */
914 "%s" /* accept-encoding */
916 "%s",/* transfer-encoding */
918 data->set.customrequest?data->set.customrequest:
919 (data->set.no_body?"HEAD":
920 ((HTTPREQ_POST == data->set.httpreq) ||
921 (HTTPREQ_POST_FORM == data->set.httpreq))?"POST":
922 (HTTPREQ_PUT == data->set.httpreq)?"PUT":"GET"),
924 (conn->bits.proxy_user_passwd &&
925 conn->allocptr.proxyuserpwd)?conn->allocptr.proxyuserpwd:"",
926 (conn->bits.user_passwd && conn->allocptr.userpwd)?
927 conn->allocptr.userpwd:"",
928 (conn->bits.use_range && conn->allocptr.rangeline)?
929 conn->allocptr.rangeline:"",
930 (data->set.useragent && *data->set.useragent && conn->allocptr.uagent)?
931 conn->allocptr.uagent:"",
932 (conn->allocptr.cookie?conn->allocptr.cookie:""), /* Cookie: <data> */
933 (conn->allocptr.host?conn->allocptr.host:""), /* Host: host */
934 http->p_pragma?http->p_pragma:"",
935 http->p_accept?http->p_accept:"",
936 (data->set.encoding && *data->set.encoding && conn->allocptr.accept_encoding)?
937 conn->allocptr.accept_encoding:"", /* 08/28/02 jhrg */
938 (data->change.referer && conn->allocptr.ref)?conn->allocptr.ref:"" /* Referer: <data> <CRLF> */,
944 struct Cookie *store=co;
945 /* now loop through all cookies that matched */
947 if(co->value && strlen(co->value)) {
949 add_bufferf(req_buffer, "Cookie: ");
951 add_bufferf(req_buffer,
952 "%s%s=%s", count?"; ":"", co->name, co->value);
955 co = co->next; /* next cookie please */
958 add_buffer(req_buffer, "\r\n", 2);
960 Curl_cookie_freelist(store); /* free the cookie list */
964 if(data->set.timecondition) {
967 /* Phil Karn (Fri, 13 Apr 2001) pointed out that the If-Modified-Since
968 * header family should have their times set in GMT as RFC2616 defines:
969 * "All HTTP date/time stamps MUST be represented in Greenwich Mean Time
970 * (GMT), without exception. For the purposes of HTTP, GMT is exactly
971 * equal to UTC (Coordinated Universal Time)." (see page 20 of RFC2616).
975 /* thread-safe version */
977 thistime = (struct tm *)gmtime_r(&data->set.timevalue, &keeptime);
979 thistime = gmtime(&data->set.timevalue);
981 if(NULL == thistime) {
982 failf(data, "localtime() failed!");
983 return CURLE_OUT_OF_MEMORY;
987 /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */
988 strftime(buf, BUFSIZE-1, "%a, %d %b %Y %H:%M:%S GMT", thistime);
990 /* TODO: Right, we *could* write a replacement here */
991 strcpy(buf, "no strftime() support");
993 switch(data->set.timecondition) {
994 case CURL_TIMECOND_IFMODSINCE:
996 add_bufferf(req_buffer,
997 "If-Modified-Since: %s\r\n", buf);
999 case CURL_TIMECOND_IFUNMODSINCE:
1000 add_bufferf(req_buffer,
1001 "If-Unmodified-Since: %s\r\n", buf);
1003 case CURL_TIMECOND_LASTMOD:
1004 add_bufferf(req_buffer,
1005 "Last-Modified: %s\r\n", buf);
1011 char *ptr = strchr(headers->data, ':');
1013 /* we require a colon for this to be a true header */
1015 ptr++; /* pass the colon */
1016 while(*ptr && isspace((int)*ptr))
1020 /* only send this if the contents was non-blank */
1022 add_bufferf(req_buffer, "%s\r\n", headers->data);
1025 headers = headers->next;
1028 http->postdata = NULL; /* nothing to post at this point */
1029 Curl_pgrsSetUploadSize(data, 0); /* upload size is 0 atm */
1031 switch(data->set.httpreq) {
1033 case HTTPREQ_POST_FORM:
1034 if(Curl_FormInit(&http->form, http->sendit)) {
1035 failf(data, "Internal HTTP POST error!");
1036 return CURLE_HTTP_POST_ERROR;
1039 /* set the read function to read from the generated form data */
1040 conn->fread = (curl_read_callback)Curl_FormReader;
1041 conn->fread_in = &http->form;
1043 http->sending = HTTPSEND_BODY;
1045 if(!conn->bits.upload_chunky)
1046 /* only add Content-Length if not uploading chunked */
1047 add_bufferf(req_buffer,
1048 "Content-Length: %d\r\n", http->postsize);
1050 if(!checkheaders(data, "Expect:")) {
1051 /* if not disabled explicitly we add a Expect: 100-continue
1052 to the headers which actually speeds up post operations (as
1053 there is one packet coming back from the web server) */
1054 add_bufferf(req_buffer,
1055 "Expect: 100-continue\r\n");
1056 data->set.expect100header = TRUE;
1059 if(!checkheaders(data, "Content-Type:")) {
1060 /* Get Content-Type: line from Curl_FormReadOneLine, which happens
1061 to always be the first line. We can know this for sure since
1062 we always build the formpost linked list the same way!
1064 The Content-Type header line also contains the MIME boundary
1065 string etc why disabling this header is likely to not make things
1066 work, but we support it anyway.
1068 char contentType[256];
1070 linelength = Curl_FormReadOneLine (contentType,
1071 sizeof(contentType),
1073 (FILE *)&http->form);
1074 if(linelength == -1) {
1075 failf(data, "Could not get Content-Type header line!");
1076 return CURLE_HTTP_POST_ERROR;
1078 add_buffer(req_buffer, contentType, linelength);
1081 /* make the request end in a true CRLF */
1082 add_buffer(req_buffer, "\r\n", 2);
1084 /* set upload size to the progress meter */
1085 Curl_pgrsSetUploadSize(data, http->postsize);
1087 /* fire away the whole request to the server */
1088 result = add_buffer_send(req_buffer, conn->firstsocket, conn,
1089 &data->info.request_size);
1091 failf(data, "Failed sending POST request");
1093 /* setup variables for the upcoming transfer */
1094 result = Curl_Transfer(conn, conn->firstsocket, -1, TRUE,
1095 &http->readbytecount,
1097 &http->writebytecount);
1099 Curl_formclean(http->sendit); /* free that whole lot */
1104 case HTTPREQ_PUT: /* Let's PUT the data to the server! */
1106 if((data->set.infilesize>0) && !conn->bits.upload_chunky)
1107 /* only add Content-Length if not uploading chunked */
1108 add_bufferf(req_buffer,
1109 "Content-Length: %d\r\n", /* file size */
1110 data->set.infilesize );
1112 add_bufferf(req_buffer, "\r\n");
1114 /* set the upload size to the progress meter */
1115 Curl_pgrsSetUploadSize(data, data->set.infilesize);
1117 /* this sends the buffer and frees all the buffer resources */
1118 result = add_buffer_send(req_buffer, conn->firstsocket, conn,
1119 &data->info.request_size);
1121 failf(data, "Failed sending POST request");
1123 /* prepare for transfer */
1124 result = Curl_Transfer(conn, conn->firstsocket, -1, TRUE,
1125 &http->readbytecount,
1127 &http->writebytecount);
1133 /* this is the simple POST, using x-www-form-urlencoded style */
1135 if(!conn->bits.upload_chunky) {
1136 /* We only set Content-Length and allow a custom Content-Length if
1137 we don't upload data chunked, as RFC2616 forbids us to set both
1138 kinds of headers (Transfer-Encoding: chunked and Content-Length) */
1140 if(!checkheaders(data, "Content-Length:"))
1141 /* we allow replacing this header, although it isn't very wise to
1142 actually set your own */
1143 add_bufferf(req_buffer,
1144 "Content-Length: %d\r\n",
1145 data->set.postfieldsize?
1146 data->set.postfieldsize:
1147 (data->set.postfields?strlen(data->set.postfields):0) );
1150 if(!checkheaders(data, "Content-Type:"))
1151 add_bufferf(req_buffer,
1152 "Content-Type: application/x-www-form-urlencoded\r\n");
1154 add_buffer(req_buffer, "\r\n", 2);
1156 /* and here we setup the pointers to the actual data */
1157 if(data->set.postfields) {
1158 if(data->set.postfieldsize)
1159 http->postsize = data->set.postfieldsize;
1161 http->postsize = strlen(data->set.postfields);
1162 http->postdata = data->set.postfields;
1164 http->sending = HTTPSEND_BODY;
1166 conn->fread = (curl_read_callback)readmoredata;
1167 conn->fread_in = (void *)conn;
1169 /* set the upload size to the progress meter */
1170 Curl_pgrsSetUploadSize(data, http->postsize);
1173 /* set the upload size to the progress meter */
1174 Curl_pgrsSetUploadSize(data, data->set.infilesize);
1176 /* issue the request, headers-only */
1177 result = add_buffer_send(req_buffer, conn->firstsocket, conn,
1178 &data->info.request_size);
1181 failf(data, "Failed sending HTTP POST request");
1184 Curl_Transfer(conn, conn->firstsocket, -1, TRUE,
1185 &http->readbytecount,
1187 &http->writebytecount);
1191 add_buffer(req_buffer, "\r\n", 2);
1193 /* issue the request */
1194 result = add_buffer_send(req_buffer, conn->firstsocket, conn,
1195 &data->info.request_size);
1198 failf(data, "Failed sending HTTP request");
1200 /* HTTP GET/HEAD download: */
1201 result = Curl_Transfer(conn, conn->firstsocket, -1, TRUE,
1202 &http->readbytecount,
1203 http->postdata?conn->firstsocket:-1,
1204 http->postdata?&http->writebytecount:NULL);
1208 } while (0); /* this is just a left-over from the multiple document download