Home | History | Annotate | Download | only in lib
      1 /***************************************************************************
      2  *                                  _   _ ____  _
      3  *  Project                     ___| | | |  _ \| |
      4  *                             / __| | | | |_) | |
      5  *                            | (__| |_| |  _ <| |___
      6  *                             \___|\___/|_| \_\_____|
      7  *
      8  * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel (at) 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.haxx.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  ***************************************************************************/
     22 
     23 #include "curl_setup.h"
     24 
     25 #if !defined(CURL_DISABLE_PROXY) && !defined(CURL_DISABLE_HTTP)
     26 
     27 #include "urldata.h"
     28 #include <curl/curl.h>
     29 #include "http_proxy.h"
     30 #include "sendf.h"
     31 #include "http.h"
     32 #include "url.h"
     33 #include "select.h"
     34 #include "progress.h"
     35 #include "non-ascii.h"
     36 #include "connect.h"
     37 #include "curlx.h"
     38 #include "vtls/vtls.h"
     39 
     40 /* The last 3 #include files should be in this order */
     41 #include "curl_printf.h"
     42 #include "curl_memory.h"
     43 #include "memdebug.h"
     44 
     45 /*
     46  * Perform SSL initialization for HTTPS proxy.  Sets
     47  * proxy_ssl_connected connection bit when complete.  Can be
     48  * called multiple times.
     49  */
     50 static CURLcode https_proxy_connect(struct connectdata *conn, int sockindex)
     51 {
     52 #ifdef USE_SSL
     53   CURLcode result = CURLE_OK;
     54   DEBUGASSERT(conn->http_proxy.proxytype == CURLPROXY_HTTPS);
     55   if(!conn->bits.proxy_ssl_connected[sockindex]) {
     56     /* perform SSL initialization for this socket */
     57     result =
     58       Curl_ssl_connect_nonblocking(conn, sockindex,
     59                                    &conn->bits.proxy_ssl_connected[sockindex]);
     60     if(result)
     61       conn->bits.close = TRUE; /* a failed connection is marked for closure to
     62                                   prevent (bad) re-use or similar */
     63   }
     64   return result;
     65 #else
     66   (void) conn;
     67   (void) sockindex;
     68   return CURLE_NOT_BUILT_IN;
     69 #endif
     70 }
     71 
     72 CURLcode Curl_proxy_connect(struct connectdata *conn, int sockindex)
     73 {
     74   if(conn->http_proxy.proxytype == CURLPROXY_HTTPS) {
     75     const CURLcode result = https_proxy_connect(conn, sockindex);
     76     if(result)
     77       return result;
     78     if(!conn->bits.proxy_ssl_connected[sockindex])
     79       return result; /* wait for HTTPS proxy SSL initialization to complete */
     80   }
     81 
     82   if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
     83 #ifndef CURL_DISABLE_PROXY
     84     /* for [protocol] tunneled through HTTP proxy */
     85     struct HTTP http_proxy;
     86     void *prot_save;
     87     const char *hostname;
     88     int remote_port;
     89     CURLcode result;
     90 
     91     /* BLOCKING */
     92     /* We want "seamless" operations through HTTP proxy tunnel */
     93 
     94     /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the
     95      * member conn->proto.http; we want [protocol] through HTTP and we have
     96      * to change the member temporarily for connecting to the HTTP
     97      * proxy. After Curl_proxyCONNECT we have to set back the member to the
     98      * original pointer
     99      *
    100      * This function might be called several times in the multi interface case
    101      * if the proxy's CONNTECT response is not instant.
    102      */
    103     prot_save = conn->data->req.protop;
    104     memset(&http_proxy, 0, sizeof(http_proxy));
    105     conn->data->req.protop = &http_proxy;
    106     connkeep(conn, "HTTP proxy CONNECT");
    107     if(sockindex == SECONDARYSOCKET)
    108       hostname = conn->secondaryhostname;
    109     else if(conn->bits.conn_to_host)
    110       hostname = conn->conn_to_host.name;
    111     else
    112       hostname = conn->host.name;
    113 
    114     if(sockindex == SECONDARYSOCKET)
    115       remote_port = conn->secondary_port;
    116     else if(conn->bits.conn_to_port)
    117       remote_port = conn->conn_to_port;
    118     else
    119       remote_port = conn->remote_port;
    120     result = Curl_proxyCONNECT(conn, sockindex, hostname,
    121                                remote_port, FALSE);
    122     conn->data->req.protop = prot_save;
    123     if(CURLE_OK != result)
    124       return result;
    125     Curl_safefree(conn->allocptr.proxyuserpwd);
    126 #else
    127     return CURLE_NOT_BUILT_IN;
    128 #endif
    129   }
    130   /* no HTTP tunnel proxy, just return */
    131   return CURLE_OK;
    132 }
    133 
    134 /*
    135  * Curl_proxyCONNECT() requires that we're connected to a HTTP proxy. This
    136  * function will issue the necessary commands to get a seamless tunnel through
    137  * this proxy. After that, the socket can be used just as a normal socket.
    138  *
    139  * 'blocking' set to TRUE means that this function will do the entire CONNECT
    140  * + response in a blocking fashion. Should be avoided!
    141  */
    142 
    143 CURLcode Curl_proxyCONNECT(struct connectdata *conn,
    144                            int sockindex,
    145                            const char *hostname,
    146                            int remote_port,
    147                            bool blocking)
    148 {
    149   int subversion=0;
    150   struct Curl_easy *data=conn->data;
    151   struct SingleRequest *k = &data->req;
    152   CURLcode result;
    153   curl_socket_t tunnelsocket = conn->sock[sockindex];
    154   curl_off_t cl=0;
    155   bool closeConnection = FALSE;
    156   bool chunked_encoding = FALSE;
    157   time_t check;
    158 
    159 #define SELECT_OK      0
    160 #define SELECT_ERROR   1
    161 #define SELECT_TIMEOUT 2
    162   int error = SELECT_OK;
    163 
    164   if(conn->tunnel_state[sockindex] == TUNNEL_COMPLETE)
    165     return CURLE_OK; /* CONNECT is already completed */
    166 
    167   conn->bits.proxy_connect_closed = FALSE;
    168 
    169   do {
    170     if(TUNNEL_INIT == conn->tunnel_state[sockindex]) {
    171       /* BEGIN CONNECT PHASE */
    172       char *host_port;
    173       Curl_send_buffer *req_buffer;
    174 
    175       infof(data, "Establish HTTP proxy tunnel to %s:%hu\n",
    176             hostname, remote_port);
    177 
    178         /* This only happens if we've looped here due to authentication
    179            reasons, and we don't really use the newly cloned URL here
    180            then. Just free() it. */
    181       free(data->req.newurl);
    182       data->req.newurl = NULL;
    183 
    184       /* initialize a dynamic send-buffer */
    185       req_buffer = Curl_add_buffer_init();
    186 
    187       if(!req_buffer)
    188         return CURLE_OUT_OF_MEMORY;
    189 
    190       host_port = aprintf("%s:%hu", hostname, remote_port);
    191       if(!host_port) {
    192         Curl_add_buffer_free(req_buffer);
    193         return CURLE_OUT_OF_MEMORY;
    194       }
    195 
    196       /* Setup the proxy-authorization header, if any */
    197       result = Curl_http_output_auth(conn, "CONNECT", host_port, TRUE);
    198 
    199       free(host_port);
    200 
    201       if(!result) {
    202         char *host=(char *)"";
    203         const char *proxyconn="";
    204         const char *useragent="";
    205         const char *http = (conn->http_proxy.proxytype == CURLPROXY_HTTP_1_0) ?
    206           "1.0" : "1.1";
    207         bool ipv6_ip = conn->bits.ipv6_ip;
    208         char *hostheader;
    209 
    210         /* the hostname may be different */
    211         if(hostname != conn->host.name)
    212           ipv6_ip = (strchr(hostname, ':') != NULL);
    213         hostheader= /* host:port with IPv6 support */
    214           aprintf("%s%s%s:%hu", ipv6_ip?"[":"", hostname, ipv6_ip?"]":"",
    215                   remote_port);
    216         if(!hostheader) {
    217           Curl_add_buffer_free(req_buffer);
    218           return CURLE_OUT_OF_MEMORY;
    219         }
    220 
    221         if(!Curl_checkProxyheaders(conn, "Host:")) {
    222           host = aprintf("Host: %s\r\n", hostheader);
    223           if(!host) {
    224             free(hostheader);
    225             Curl_add_buffer_free(req_buffer);
    226             return CURLE_OUT_OF_MEMORY;
    227           }
    228         }
    229         if(!Curl_checkProxyheaders(conn, "Proxy-Connection:"))
    230           proxyconn = "Proxy-Connection: Keep-Alive\r\n";
    231 
    232         if(!Curl_checkProxyheaders(conn, "User-Agent:") &&
    233            data->set.str[STRING_USERAGENT])
    234           useragent = conn->allocptr.uagent;
    235 
    236         result =
    237           Curl_add_bufferf(req_buffer,
    238                            "CONNECT %s HTTP/%s\r\n"
    239                            "%s"  /* Host: */
    240                            "%s"  /* Proxy-Authorization */
    241                            "%s"  /* User-Agent */
    242                            "%s", /* Proxy-Connection */
    243                            hostheader,
    244                            http,
    245                            host,
    246                            conn->allocptr.proxyuserpwd?
    247                            conn->allocptr.proxyuserpwd:"",
    248                            useragent,
    249                            proxyconn);
    250 
    251         if(host && *host)
    252           free(host);
    253         free(hostheader);
    254 
    255         if(!result)
    256           result = Curl_add_custom_headers(conn, TRUE, req_buffer);
    257 
    258         if(!result)
    259           /* CRLF terminate the request */
    260           result = Curl_add_bufferf(req_buffer, "\r\n");
    261 
    262         if(!result) {
    263           /* Send the connect request to the proxy */
    264           /* BLOCKING */
    265           result =
    266             Curl_add_buffer_send(req_buffer, conn,
    267                                  &data->info.request_size, 0, sockindex);
    268         }
    269         req_buffer = NULL;
    270         if(result)
    271           failf(data, "Failed sending CONNECT to proxy");
    272       }
    273 
    274       Curl_add_buffer_free(req_buffer);
    275       if(result)
    276         return result;
    277 
    278       conn->tunnel_state[sockindex] = TUNNEL_CONNECT;
    279     } /* END CONNECT PHASE */
    280 
    281     check = Curl_timeleft(data, NULL, TRUE);
    282     if(check <= 0) {
    283       failf(data, "Proxy CONNECT aborted due to timeout");
    284       return CURLE_RECV_ERROR;
    285     }
    286 
    287     if(!blocking) {
    288       if(0 == SOCKET_READABLE(tunnelsocket, 0))
    289         /* return so we'll be called again polling-style */
    290         return CURLE_OK;
    291       else {
    292         DEBUGF(infof(data,
    293                "Read response immediately from proxy CONNECT\n"));
    294       }
    295     }
    296 
    297     /* at this point, the tunnel_connecting phase is over. */
    298 
    299     { /* READING RESPONSE PHASE */
    300       size_t nread;   /* total size read */
    301       int perline; /* count bytes per line */
    302       int keepon=TRUE;
    303       ssize_t gotbytes;
    304       char *ptr;
    305       char *line_start;
    306 
    307       ptr=data->state.buffer;
    308       line_start = ptr;
    309 
    310       nread=0;
    311       perline=0;
    312 
    313       while((nread<BUFSIZE) && (keepon && !error)) {
    314 
    315         check = Curl_timeleft(data, NULL, TRUE);
    316         if(check <= 0) {
    317           failf(data, "Proxy CONNECT aborted due to timeout");
    318           error = SELECT_TIMEOUT; /* already too little time */
    319           break;
    320         }
    321 
    322         /* loop every second at least, less if the timeout is near */
    323         switch (SOCKET_READABLE(tunnelsocket, check<1000L?check:1000)) {
    324         case -1: /* select() error, stop reading */
    325           error = SELECT_ERROR;
    326           failf(data, "Proxy CONNECT aborted due to select/poll error");
    327           break;
    328         case 0: /* timeout */
    329           break;
    330         default:
    331           DEBUGASSERT(ptr+BUFSIZE-nread <= data->state.buffer+BUFSIZE+1);
    332           result = Curl_read(conn, tunnelsocket, ptr, BUFSIZE-nread,
    333                              &gotbytes);
    334           if(result==CURLE_AGAIN)
    335             continue; /* go loop yourself */
    336           else if(result)
    337             keepon = FALSE;
    338           else if(gotbytes <= 0) {
    339             keepon = FALSE;
    340             if(data->set.proxyauth && data->state.authproxy.avail) {
    341               /* proxy auth was requested and there was proxy auth available,
    342                  then deem this as "mere" proxy disconnect */
    343               conn->bits.proxy_connect_closed = TRUE;
    344               infof(data, "Proxy CONNECT connection closed\n");
    345             }
    346             else {
    347               error = SELECT_ERROR;
    348               failf(data, "Proxy CONNECT aborted");
    349             }
    350           }
    351           else {
    352             /*
    353              * We got a whole chunk of data, which can be anything from one
    354              * byte to a set of lines and possibly just a piece of the last
    355              * line.
    356              */
    357             int i;
    358 
    359             nread += gotbytes;
    360 
    361             if(keepon > TRUE) {
    362               /* This means we are currently ignoring a response-body */
    363 
    364               nread = 0; /* make next read start over in the read buffer */
    365               ptr=data->state.buffer;
    366               if(cl) {
    367                 /* A Content-Length based body: simply count down the counter
    368                    and make sure to break out of the loop when we're done! */
    369                 cl -= gotbytes;
    370                 if(cl<=0) {
    371                   keepon = FALSE;
    372                   break;
    373                 }
    374               }
    375               else {
    376                 /* chunked-encoded body, so we need to do the chunked dance
    377                    properly to know when the end of the body is reached */
    378                 CHUNKcode r;
    379                 ssize_t tookcareof=0;
    380 
    381                 /* now parse the chunked piece of data so that we can
    382                    properly tell when the stream ends */
    383                 r = Curl_httpchunk_read(conn, ptr, gotbytes, &tookcareof);
    384                 if(r == CHUNKE_STOP) {
    385                   /* we're done reading chunks! */
    386                   infof(data, "chunk reading DONE\n");
    387                   keepon = FALSE;
    388                   /* we did the full CONNECT treatment, go COMPLETE */
    389                   conn->tunnel_state[sockindex] = TUNNEL_COMPLETE;
    390                 }
    391                 else
    392                   infof(data, "Read %zd bytes of chunk, continue\n",
    393                         tookcareof);
    394               }
    395             }
    396             else
    397               for(i = 0; i < gotbytes; ptr++, i++) {
    398                 perline++; /* amount of bytes in this line so far */
    399                 if(*ptr == 0x0a) {
    400                   char letter;
    401                   int writetype;
    402 
    403                   /* convert from the network encoding */
    404                   result = Curl_convert_from_network(data, line_start,
    405                                                      perline);
    406                   /* Curl_convert_from_network calls failf if unsuccessful */
    407                   if(result)
    408                     return result;
    409 
    410                   /* output debug if that is requested */
    411                   if(data->set.verbose)
    412                     Curl_debug(data, CURLINFO_HEADER_IN,
    413                                line_start, (size_t)perline, conn);
    414 
    415                   /* send the header to the callback */
    416                   writetype = CLIENTWRITE_HEADER;
    417                   if(data->set.include_header)
    418                     writetype |= CLIENTWRITE_BODY;
    419 
    420                   result = Curl_client_write(conn, writetype, line_start,
    421                                              perline);
    422 
    423                   data->info.header_size += (long)perline;
    424                   data->req.headerbytecount += (long)perline;
    425 
    426                   if(result)
    427                     return result;
    428 
    429                   /* Newlines are CRLF, so the CR is ignored as the line isn't
    430                      really terminated until the LF comes. Treat a following CR
    431                      as end-of-headers as well.*/
    432 
    433                   if(('\r' == line_start[0]) ||
    434                      ('\n' == line_start[0])) {
    435                     /* end of response-headers from the proxy */
    436                     nread = 0; /* make next read start over in the read
    437                                   buffer */
    438                     ptr=data->state.buffer;
    439                     if((407 == k->httpcode) && !data->state.authproblem) {
    440                       /* If we get a 407 response code with content length
    441                          when we have no auth problem, we must ignore the
    442                          whole response-body */
    443                       keepon = 2;
    444 
    445                       if(cl) {
    446                         infof(data, "Ignore %" CURL_FORMAT_CURL_OFF_T
    447                               " bytes of response-body\n", cl);
    448 
    449                         /* remove the remaining chunk of what we already
    450                            read */
    451                         cl -= (gotbytes - i);
    452 
    453                         if(cl<=0)
    454                           /* if the whole thing was already read, we are done!
    455                            */
    456                           keepon=FALSE;
    457                       }
    458                       else if(chunked_encoding) {
    459                         CHUNKcode r;
    460                         /* We set ignorebody true here since the chunked
    461                            decoder function will acknowledge that. Pay
    462                            attention so that this is cleared again when this
    463                            function returns! */
    464                         k->ignorebody = TRUE;
    465                         infof(data, "%zd bytes of chunk left\n", gotbytes-i);
    466 
    467                         if(line_start[1] == '\n') {
    468                           /* this can only be a LF if the letter at index 0
    469                              was a CR */
    470                           line_start++;
    471                           i++;
    472                         }
    473 
    474                         /* now parse the chunked piece of data so that we can
    475                            properly tell when the stream ends */
    476                         r = Curl_httpchunk_read(conn, line_start+1,
    477                                                   gotbytes -i, &gotbytes);
    478                         if(r == CHUNKE_STOP) {
    479                           /* we're done reading chunks! */
    480                           infof(data, "chunk reading DONE\n");
    481                           keepon = FALSE;
    482                           /* we did the full CONNECT treatment, go to
    483                              COMPLETE */
    484                           conn->tunnel_state[sockindex] = TUNNEL_COMPLETE;
    485                         }
    486                         else
    487                           infof(data, "Read %zd bytes of chunk, continue\n",
    488                                 gotbytes);
    489                       }
    490                       else {
    491                         /* without content-length or chunked encoding, we
    492                            can't keep the connection alive since the close is
    493                            the end signal so we bail out at once instead */
    494                         keepon=FALSE;
    495                       }
    496                     }
    497                     else {
    498                       keepon = FALSE;
    499                       if(200 == data->info.httpproxycode) {
    500                         if(gotbytes - (i+1))
    501                           failf(data, "Proxy CONNECT followed by %zd bytes "
    502                                 "of opaque data. Data ignored (known bug #39)",
    503                                 gotbytes - (i+1));
    504                       }
    505                     }
    506                     /* we did the full CONNECT treatment, go to COMPLETE */
    507                     conn->tunnel_state[sockindex] = TUNNEL_COMPLETE;
    508                     break; /* breaks out of for-loop, not switch() */
    509                   }
    510 
    511                   /* keep a backup of the position we are about to blank */
    512                   letter = line_start[perline];
    513                   line_start[perline]=0; /* zero terminate the buffer */
    514                   if((checkprefix("WWW-Authenticate:", line_start) &&
    515                       (401 == k->httpcode)) ||
    516                      (checkprefix("Proxy-authenticate:", line_start) &&
    517                       (407 == k->httpcode))) {
    518 
    519                     bool proxy = (k->httpcode == 407) ? TRUE : FALSE;
    520                     char *auth = Curl_copy_header_value(line_start);
    521                     if(!auth)
    522                       return CURLE_OUT_OF_MEMORY;
    523 
    524                     result = Curl_http_input_auth(conn, proxy, auth);
    525 
    526                     free(auth);
    527 
    528                     if(result)
    529                       return result;
    530                   }
    531                   else if(checkprefix("Content-Length:", line_start)) {
    532                     cl = curlx_strtoofft(line_start +
    533                                          strlen("Content-Length:"), NULL, 10);
    534                   }
    535                   else if(Curl_compareheader(line_start,
    536                                              "Connection:", "close"))
    537                     closeConnection = TRUE;
    538                   else if(Curl_compareheader(line_start,
    539                                              "Transfer-Encoding:",
    540                                              "chunked")) {
    541                     infof(data, "CONNECT responded chunked\n");
    542                     chunked_encoding = TRUE;
    543                     /* init our chunky engine */
    544                     Curl_httpchunk_init(conn);
    545                   }
    546                   else if(Curl_compareheader(line_start,
    547                                              "Proxy-Connection:", "close"))
    548                     closeConnection = TRUE;
    549                   else if(2 == sscanf(line_start, "HTTP/1.%d %d",
    550                                       &subversion,
    551                                       &k->httpcode)) {
    552                     /* store the HTTP code from the proxy */
    553                     data->info.httpproxycode = k->httpcode;
    554                   }
    555                   /* put back the letter we blanked out before */
    556                   line_start[perline]= letter;
    557 
    558                   perline=0; /* line starts over here */
    559                   line_start = ptr+1; /* this skips the zero byte we wrote */
    560                 }
    561               }
    562           }
    563           break;
    564         } /* switch */
    565         if(Curl_pgrsUpdate(conn))
    566           return CURLE_ABORTED_BY_CALLBACK;
    567       } /* while there's buffer left and loop is requested */
    568 
    569       if(error)
    570         return CURLE_RECV_ERROR;
    571 
    572       if(data->info.httpproxycode != 200) {
    573         /* Deal with the possibly already received authenticate
    574            headers. 'newurl' is set to a new URL if we must loop. */
    575         result = Curl_http_auth_act(conn);
    576         if(result)
    577           return result;
    578 
    579         if(conn->bits.close)
    580           /* the connection has been marked for closure, most likely in the
    581              Curl_http_auth_act() function and thus we can kill it at once
    582              below
    583           */
    584           closeConnection = TRUE;
    585       }
    586 
    587       if(closeConnection && data->req.newurl) {
    588         /* Connection closed by server. Don't use it anymore */
    589         Curl_closesocket(conn, conn->sock[sockindex]);
    590         conn->sock[sockindex] = CURL_SOCKET_BAD;
    591         break;
    592       }
    593     } /* END READING RESPONSE PHASE */
    594 
    595     /* If we are supposed to continue and request a new URL, which basically
    596      * means the HTTP authentication is still going on so if the tunnel
    597      * is complete we start over in INIT state */
    598     if(data->req.newurl &&
    599        (TUNNEL_COMPLETE == conn->tunnel_state[sockindex])) {
    600       conn->tunnel_state[sockindex] = TUNNEL_INIT;
    601       infof(data, "TUNNEL_STATE switched to: %d\n",
    602             conn->tunnel_state[sockindex]);
    603     }
    604 
    605   } while(data->req.newurl);
    606 
    607   if(200 != data->req.httpcode) {
    608     if(closeConnection && data->req.newurl) {
    609       conn->bits.proxy_connect_closed = TRUE;
    610       infof(data, "Connect me again please\n");
    611     }
    612     else {
    613       free(data->req.newurl);
    614       data->req.newurl = NULL;
    615       /* failure, close this connection to avoid re-use */
    616       streamclose(conn, "proxy CONNECT failure");
    617       Curl_closesocket(conn, conn->sock[sockindex]);
    618       conn->sock[sockindex] = CURL_SOCKET_BAD;
    619     }
    620 
    621     /* to back to init state */
    622     conn->tunnel_state[sockindex] = TUNNEL_INIT;
    623 
    624     if(conn->bits.proxy_connect_closed)
    625       /* this is not an error, just part of the connection negotiation */
    626       return CURLE_OK;
    627     else {
    628       failf(data, "Received HTTP code %d from proxy after CONNECT",
    629             data->req.httpcode);
    630       return CURLE_RECV_ERROR;
    631     }
    632   }
    633 
    634   conn->tunnel_state[sockindex] = TUNNEL_COMPLETE;
    635 
    636   /* If a proxy-authorization header was used for the proxy, then we should
    637      make sure that it isn't accidentally used for the document request
    638      after we've connected. So let's free and clear it here. */
    639   Curl_safefree(conn->allocptr.proxyuserpwd);
    640   conn->allocptr.proxyuserpwd = NULL;
    641 
    642   data->state.authproxy.done = TRUE;
    643 
    644   infof (data, "Proxy replied OK to CONNECT request\n");
    645   data->req.ignorebody = FALSE; /* put it (back) to non-ignore state */
    646   conn->bits.rewindaftersend = FALSE; /* make sure this isn't set for the
    647                                          document request  */
    648   return CURLE_OK;
    649 }
    650 #endif /* CURL_DISABLE_PROXY */
    651