📄 http.c
字号:
res= Curl_read(conn, tunnelsocket, ptr, BUFSIZE-nread, &gotbytes); if(res< 0) /* EWOULDBLOCK */ continue; /* go loop yourself */ else if(res) keepon = FALSE; else if(gotbytes <= 0) { keepon = FALSE; error = SELECT_ERROR; failf(data, "Proxy CONNECT aborted"); } else { /* * We got a whole chunk of data, which can be anything from one byte * to a set of lines and possibly just a piece of the last line. * * TODO: To make this code work less error-prone, we need to make * sure that we read and create full lines before we compare them, * as there is really nothing that stops the proxy from delivering * the response lines in multiple parts, each part consisting of * only a little piece of the line(s). */ int i; nread += gotbytes; for(i = 0; i < gotbytes; ptr++, i++) { perline++; /* amount of bytes in this line so far */ if(*ptr=='\n') { char letter; /* Newlines are CRLF, so the CR is ignored as the line isn't really terminated until the LF comes */ if('\r' == line_start[0]) { /* end of response-headers from the proxy */ keepon=FALSE; break; /* breaks out of for-loop, not switch() */ } /* output debug output if that is requested */ if(data->set.verbose) Curl_debug(data, CURLINFO_HEADER_IN, line_start, perline); /* keep a backup of the position we are about to blank */ letter = line_start[perline]; line_start[perline]=0; /* zero terminate the buffer */ if((checkprefix("WWW-Authenticate:", line_start) && (401 == httpcode)) || (checkprefix("Proxy-authenticate:", line_start) && (407 == httpcode))) { result = Curl_http_auth(conn, httpcode, line_start); if(result) return result; } else if(2 == sscanf(line_start, "HTTP/1.%d %d", &subversion, &httpcode)) { /* store the HTTP code */ data->info.httpproxycode = httpcode; } /* put back the letter we blanked out before */ line_start[perline]= letter; perline=0; /* line starts over here */ line_start = ptr+1; /* this skips the zero byte we wrote */ } } } break; } /* switch */ } /* while there's buffer left and loop is requested */ if(error) return CURLE_RECV_ERROR; /* Deal with the possibly already received authenticate headers. 'newurl' is set to a new URL if we must loop. */ Curl_http_auth_act(conn); } while(conn->newurl); if(200 != httpcode) { failf(data, "Received HTTP code %d from proxy after CONNECT", httpcode); return CURLE_RECV_ERROR; } /* If a proxy-authorization header was used for the proxy, then we should make sure that it isn't accidentally used for the document request after we've connected. So let's free and clear it here. */ Curl_safefree(conn->allocptr.proxyuserpwd); conn->allocptr.proxyuserpwd = NULL; Curl_http_auth_stage(data, 401); /* move on to the host auth */ infof (data, "Proxy replied OK to CONNECT request\n"); return CURLE_OK;}/* * HTTP stuff to do at connect-time. */CURLcode Curl_http_connect(struct connectdata *conn){ struct SessionHandle *data; CURLcode result; data=conn->data; /* If we are not using a proxy and we want a secure connection, * perform SSL initialization & connection now. * If using a proxy with https, then we must tell the proxy to CONNECT * us to the host we want to talk to. Only after the connect * has occured, can we start talking SSL */ if(conn->bits.httpproxy && ((conn->protocol & PROT_HTTPS) || data->set.tunnel_thru_httpproxy)) { /* either HTTPS over proxy, OR explicitly asked for a tunnel */ result = Curl_ConnectHTTPProxyTunnel(conn, conn->firstsocket, conn->hostname, conn->remote_port); if(CURLE_OK != result) return result; } if(conn->protocol & PROT_HTTPS) { /* now, perform the SSL initialization for this socket */ result = Curl_SSLConnect(conn); if(result) return result; } if(conn->bits.user_passwd && !data->state.this_is_a_follow) { /* Authorization: is requested, this is not a followed location, get the original host name */ if (data->state.auth_host) /* Free to avoid leaking memory on multiple requests*/ free(data->state.auth_host); data->state.auth_host = strdup(conn->hostname); } return CURLE_OK;}CURLcode Curl_http_done(struct connectdata *conn){ struct SessionHandle *data; struct HTTP *http; data=conn->data; http=conn->proto.http; /* set the proper values (possibly modified on POST) */ conn->fread = data->set.fread; /* restore */ conn->fread_in = data->set.in; /* restore */ if (http == NULL) return CURLE_OK; if(http->send_buffer) { send_buffer *buff = http->send_buffer; free(buff->buffer); free(buff); http->send_buffer = NULL; /* cleaer the pointer */ } if(HTTPREQ_POST_FORM == data->set.httpreq) { conn->bytecount = http->readbytecount + http->writebytecount; Curl_formclean(http->sendit); /* Now free that whole lot */ } else if(HTTPREQ_PUT == data->set.httpreq) conn->bytecount = http->readbytecount + http->writebytecount; if(0 == (http->readbytecount + conn->headerbytecount)) { /* nothing was read from the HTTP server, this can't be right so we return an error here */ failf(data, "Empty reply from server"); return CURLE_GOT_NOTHING; } return CURLE_OK;}void Curl_http_auth_stage(struct SessionHandle *data, int stage){ if(stage == 401) data->state.authwant = data->set.httpauth; else if(stage == 407) data->state.authwant = data->set.proxyauth; else return; /* bad input stage */ data->state.authstage = stage; data->state.authavail = CURLAUTH_NONE;}CURLcode Curl_http(struct connectdata *conn){ struct SessionHandle *data=conn->data; char *buf = data->state.buffer; /* this is a short cut to the buffer */ CURLcode result=CURLE_OK; struct HTTP *http; struct Cookie *co=NULL; /* no cookies from start */ char *ppath = conn->ppath; /* three previous function arguments */ char *host = conn->name; const char *te = ""; /* tranfer-encoding */ char *ptr; char *request; bool authdone=TRUE; /* if the authentication phase is done */ Curl_HttpReq httpreq; /* type of HTTP request */ if(!conn->proto.http) { /* Only allocate this struct if we don't already have it! */ http = (struct HTTP *)malloc(sizeof(struct HTTP)); if(!http) return CURLE_OUT_OF_MEMORY; memset(http, 0, sizeof(struct HTTP)); conn->proto.http = http; } else http = conn->proto.http; /* We default to persistant connections */ conn->bits.close = FALSE; if ( (conn->protocol&(PROT_HTTP|PROT_FTP)) && data->set.upload) { data->set.httpreq = HTTPREQ_PUT; } request = data->set.customrequest? data->set.customrequest: (data->set.no_body?(char *)"HEAD": ((HTTPREQ_POST == data->set.httpreq) || (HTTPREQ_POST_FORM == data->set.httpreq))?(char *)"POST": (HTTPREQ_PUT == data->set.httpreq)?(char *)"PUT":(char *)"GET"); /* The User-Agent string has been built in url.c already, because it might have been used in the proxy connect, but if we have got a header with the user-agent string specified, we erase the previously made string here. */ if(checkheaders(data, "User-Agent:") && conn->allocptr.uagent) { free(conn->allocptr.uagent); conn->allocptr.uagent=NULL; } /* setup the authentication headers */ result = http_auth_headers(conn, request, ppath, &authdone); if(result) return result; Curl_safefree(conn->allocptr.ref); if(data->change.referer && !checkheaders(data, "Referer:")) conn->allocptr.ref = aprintf("Referer: %s\015\012", data->change.referer); else conn->allocptr.ref = NULL; Curl_safefree(conn->allocptr.cookie); if(data->set.cookie && !checkheaders(data, "Cookie:")) conn->allocptr.cookie = aprintf("Cookie: %s\015\012", data->set.cookie); else conn->allocptr.cookie = NULL; if(!conn->bits.upload_chunky && (data->set.httpreq != HTTPREQ_GET)) { /* not a chunky transfer but data is to be sent */ ptr = checkheaders(data, "Transfer-Encoding:"); if(ptr) { /* Some kind of TE is requested, check if 'chunked' is chosen */ if(Curl_compareheader(ptr, "Transfer-Encoding:", "chunked")) /* we have been told explicitly to upload chunky so deal with it! */ conn->bits.upload_chunky = TRUE; } } if(conn->bits.upload_chunky) { /* RFC2616 section 4.4: Messages MUST NOT include both a Content-Length header field and a non-identity transfer-coding. If the message does include a non- identity transfer-coding, the Content-Length MUST be ignored. */ if(!checkheaders(data, "Transfer-Encoding:")) { te = "Transfer-Encoding: chunked\r\n"; } else { /* The "Transfer-Encoding:" header was already added. */ te = ""; } } ptr = checkheaders(data, "Host:"); if(ptr) { /* If we have a given custom Host: header, we extract the host name in order to possibly use it for cookie reasons later on. */ char *start = ptr+strlen("Host:"); while(*start && isspace((int)*start )) start++; ptr = start; /* start host-scanning here */ /* scan through the string to find the end (space or colon) */ while(*ptr && !isspace((int)*ptr) && !(':'==*ptr)) ptr++; if(ptr != start) { int len=ptr-start; conn->allocptr.cookiehost = malloc(len+1); if(!conn->allocptr.cookiehost) return CURLE_OUT_OF_MEMORY; memcpy(conn->allocptr.cookiehost, start, len); conn->allocptr.cookiehost[len]=0; } } else { /* if ptr_host is already set, it is almost OK since we only re-use connections to the very same host and port, but when we use a HTTP proxy we have a persistant connect and yet we must change the Host: header! */ if(conn->allocptr.host) free(conn->allocptr.host); /* When building Host: headers, we must put the host name within [brackets] if the host name is a plain IPv6-address. RFC2732-style. */ if(((conn->protocol&PROT_HTTPS) && (conn->remote_port == PORT_HTTPS)) || (!(conn->protocol&PROT_HTTPS) && (conn->remote_port == PORT_HTTP)) ) /* If (HTTPS on port 443) OR (non-HTTPS on port 80) then don't include the port number in the host string */ conn->allocptr.host = aprintf("Host: %s%s%s\r\n", conn->bits.ipv6_ip?"[":"", host, conn->bits.ipv6_ip?"]":""); else conn->allocptr.host = aprintf("Host: %s%s%s:%d\r\n", conn->bits.ipv6_ip?"[":"", host, conn->bits.ipv6_ip?"]":"", conn->remote_port); if(!conn->allocptr.host) /* without Host: we can't make a nice request */ return CURLE_OUT_OF_MEMORY; } if(data->cookies) { Curl_share_lock(data, CURL_LOCK_DATA_COOKIE, CURL_LOCK_ACCESS_SINGLE); co = Curl_cookie_getlist(data->cookies, conn->allocptr.cookiehost? conn->allocptr.cookiehost:host, ppath, (bool)(conn->protocol&PROT_HTTPS?TRUE:FALSE)); Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE); } if (conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy && !(conn->protocol&PROT_HTTPS)) { /* The path sent to the proxy is in fact the entire URL */ ppath = data->change.url; } if(HTTPREQ_POST_FORM == data->set.httpreq) { /* we must build the whole darned post sequence first, so that we have a size of the whole shebang before we start to send it */ result = Curl_getFormData(&http->sendit, data->set.httppost, &http->postsize); if(CURLE_OK != result) { /* Curl_getFormData() doesn't use failf() */ failf(data, "failed creating formpost data"); return result; } } if(!checkheaders(data, "Pragma:")) http->p_pragma = "Pragma: no-cache\r\n"; if(!checkheaders(data, "Accept:")) http->p_accept = "Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, */*\r\n"; if(( (HTTPREQ_POST == data->set.httpreq) || (HTTPREQ_POST_FORM == data->set.httpreq) || (HTTPREQ_PUT == data->set.httpreq) ) && conn->resume_from) { /********************************************************************** * Resuming upload in HTTP means that we PUT or POST and that we have * got a resume_from value set. The resume value has already created * a Range: header that will be passed along. We need to "fast forward" * the file the given number of bytes and decrease the assume upload * file size before we continue this venture in the dark lands of HTTP. *********************************************************************/ if(conn->resume_from < 0 ) { /* * This is meant to get the size of the present remote-file by itself. * We don't support this now. Bail out! */ conn->resume_from = 0; } if(conn->resume_from) { /* do we still game? */ int passed=0; /* Now, let's read off the proper amount of bytes from the input. If we knew it was a proper file we could've just fseek()ed but we only have a stream here */ do { int readthisamountnow = (conn->resume_from - passed); int actuallyread;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -