Skip to content
http.c 116 KiB
Newer Older
    else
      http->postsize = 0;

    return fullsize;
  }

  memcpy(buffer, http->postdata, fullsize);
  http->postdata += fullsize;
  http->postsize -= fullsize;

  return fullsize;
}

/* ------------------------------------------------------------------------- */
 * Curl_add_buffer_init() sets up and returns a fine buffer struct
Curl_send_buffer *Curl_add_buffer_init(void)
  return calloc(1, sizeof(Curl_send_buffer));
 * Curl_add_buffer_send() sends a header buffer and frees all associated
 * memory.  Body data may be appended to the header data if desired.
Daniel Stenberg's avatar
Daniel Stenberg committed
 *
 * Returns CURLcode
CURLcode Curl_add_buffer_send(Curl_send_buffer *in,
                              struct connectdata *conn,

                               /* add the number of sent bytes to this
                                  counter */
                              long *bytes_written,

                               /* how much of the buffer contains body data */
                              size_t included_body_bytes,
                              int socketindex)
  struct HTTP *http = conn->data->req.protop;
  DEBUGASSERT(socketindex <= SECONDARYSOCKET);
  /* The looping below is required since we use non-blocking sockets, but due
     to the circumstances we will just loop and try again and again etc */

  ptr = in->buffer;
  size = in->size_used;

  headersize = size - included_body_bytes; /* the initial part that isn't body
                                              is header */

  DEBUGASSERT(size > included_body_bytes);
  res = Curl_convert_to_network(conn->data, ptr, headersize);
  /* Curl_convert_to_network calls failf if unsuccessful */
  if(res) {
    /* conversion failed, free memory and return to the caller */
    if(in->buffer)
      free(in->buffer);
    free(in);
    return res;
  if(conn->handler->flags & PROTOPT_SSL) {
    /* We never send more than CURL_MAX_WRITE_SIZE bytes in one single chunk
       when we speak HTTPS, as if only a fraction of it is sent now, this data
       needs to fit into the normal read-callback buffer later on and that
       buffer is using this size.
    */

    sendsize= (size > CURL_MAX_WRITE_SIZE)?CURL_MAX_WRITE_SIZE:size;

    /* OpenSSL is very picky and we must send the SAME buffer pointer to the
       library when we attempt to re-send this buffer. Sending the same data
       is not enough, we must use the exact same address. For this reason, we
       must copy the data to the uploadbuffer first, since that is the buffer
       we will be using if this send is retried later.
    */
    memcpy(conn->data->state.uploadbuffer, ptr, sendsize);
    ptr = conn->data->state.uploadbuffer;
  }
  else
    sendsize = size;
  res = Curl_write(conn, sockfd, ptr, sendsize, &amount);
    /*
     * Note that we may not send the entire chunk at once, and we have a set
     * number of data bytes at the end of the big buffer (out of which we may
     * only send away a part).
     */
    /* how much of the header that was sent */
    size_t headlen = (size_t)amount>headersize?headersize:(size_t)amount;
    size_t bodylen = amount - headlen;
      /* this data _may_ contain binary stuff */
      Curl_debug(conn->data, CURLINFO_HEADER_OUT, ptr, headlen, conn);
        /* there was body data sent beyond the initial header part, pass that
           on to the debug callback too */
        Curl_debug(conn->data, CURLINFO_DATA_OUT,
      /* since we sent a piece of the body here, up the byte counter for it
         accordingly */
    /* 'amount' can never be a very large value here so typecasting it so a
       signed 31 bit value should not cause problems even if ssize_t is
       64bit */
    *bytes_written += (long)amount;
    if(http) {
      if((size_t)amount != size) {
        /* The whole request could not be sent in one system call. We must
           queue it up and send it later when we get the chance. We must not
           loop here and wait until it might work again. */
        ptr = in->buffer + amount;
        /* backup the currently set pointers */
        http->backup.fread_func = conn->fread_func;
        http->backup.fread_in = conn->fread_in;
        http->backup.postdata = http->postdata;
        http->backup.postsize = http->postsize;
        /* set the new pointers for the request-sending */
        conn->fread_func = (curl_read_callback)readmoredata;
        conn->fread_in = (void *)conn;
        http->postdata = ptr;
        http->postsize = (curl_off_t)size;
        http->send_buffer = in;
        http->sending = HTTPSEND_REQUEST;
        return CURLE_OK;
      }
      http->sending = HTTPSEND_BODY;
      /* the full buffer was sent, clean up and return */
    }
    else {
      if((size_t)amount != size)
        /* We have no continue-send mechanism now, fail. This can only happen
           when this function is used from the CONNECT sending function. We
           currently (stupidly) assume that the whole request is always sent
           away in the first single chunk.

           This needs FIXing.
        */
        return CURLE_SEND_ERROR;
Daniel Stenberg's avatar
Daniel Stenberg committed
 * add_bufferf() add the formatted input to the buffer.
CURLcode Curl_add_bufferf(Curl_send_buffer *in, const char *fmt, ...)
  s = vaprintf(fmt, ap); /* this allocs a new string to append */
    CURLcode result = Curl_add_buffer(in, s, strlen(s));
    return result;
  /* If we failed, we cleanup the whole buffer and return error */
  if(in->buffer)
    free(in->buffer);
  free(in);
  return CURLE_OUT_OF_MEMORY;
Daniel Stenberg's avatar
Daniel Stenberg committed
 * add_buffer() appends a memory chunk to the existing buffer
CURLcode Curl_add_buffer(Curl_send_buffer *in, const void *inptr, size_t size)
  if(~size < in->size_used) {
    /* If resulting used size of send buffer would wrap size_t, cleanup
       the whole buffer and return error. Otherwise the required buffer
       size will fit into a single allocatable memory chunk */
    Curl_safefree(in->buffer);
    free(in);
    return CURLE_OUT_OF_MEMORY;
  }

  if(!in->buffer ||
     ((in->size_used + size) > (in->size_max - 1))) {

    /* If current buffer size isn't enough to hold the result, use a
       buffer size that doubles the required size. If this new size
       would wrap size_t, then just use the largest possible one */

    if((size > (size_t)-1/2) || (in->size_used > (size_t)-1/2) ||
       (~(size*2) < (in->size_used*2)))
      new_size = (size_t)-1;
    else
      new_size = (in->size_used+size)*2;

    if(in->buffer)
      /* we have a buffer, enlarge the existing one */
      new_rb = realloc(in->buffer, new_size);
      new_rb = malloc(new_size);
    if(!new_rb) {
      /* If we failed, we cleanup the whole buffer and return error */
      Curl_safefree(in->buffer);
      free(in);
  memcpy(&in->buffer[in->size_used], inptr, size);

  return CURLE_OK;
}

/* end of the add_buffer functions */
/* ------------------------------------------------------------------------- */
/*
 * Curl_compareheader()
 *
 * Returns TRUE if 'headerline' contains the 'header' with given 'content'.
 * Pass headers WITH the colon.
 */
bool
Curl_compareheader(const char *headerline, /* line to check */
                   const char *header,  /* header keyword _with_ colon */
                   const char *content) /* content string to find */
{
  /* RFC2616, section 4.2 says: "Each header field consists of a name followed
   * by a colon (":") and the field value. Field names are case-insensitive.
   * The field value MAY be preceded by any amount of LWS, though a single SP
   * is preferred." */

  size_t hlen = strlen(header);
  size_t clen;
  size_t len;
  const char *start;
  const char *end;
  if(!Curl_raw_nequal(headerline, header, hlen))
    return FALSE; /* doesn't start with header */

  /* pass the header */
  start = &headerline[hlen];

  /* pass all white spaces */
    start++;

  /* find the end of the header line */
  end = strchr(start, '\r'); /* lines end with CRLF */
  if(!end) {
    /* in case there's a non-standard compliant line here */
    end = strchr(start, '\n');

    if(!end)
      /* hm, there's no line ending here, use the zero byte! */
      end = strchr(start, '\0');
  }

  len = end-start; /* length of the content part of the input line */
  clen = strlen(content); /* length of the word to find */

  /* find the content string in the rest of the line */
  for(;len>=clen;len--, start++) {
    if(Curl_raw_nequal(start, content, clen))
Daniel Stenberg's avatar
Daniel Stenberg committed
 * Curl_http_connect() performs HTTP stuff to do at connect-time, called from
 * the generic Curl_connect().
CURLcode Curl_http_connect(struct connectdata *conn, bool *done)
Daniel Stenberg's avatar
Daniel Stenberg committed
{
  /* We default to persistent connections. We set this already in this connect
     function to make the re-use checks properly be able to check this bit. */
  connkeep(conn, "HTTP default");
  /* the CONNECT procedure might not have been completed */
  result = Curl_proxy_connect(conn);
  if(result)
    return result;

  if(conn->tunnel_state[FIRSTSOCKET] == TUNNEL_CONNECT)
    /* nothing else to do except wait right now - we're not done here. */
    return CURLE_OK;

  if(conn->given->flags & PROTOPT_SSL) {
    result = https_connecting(conn, done);
    if(result)
      return result;
/* this returns the socket to wait for in the DO and DOING state for the multi
   interface and then we're always _sending_ a request and thus we wait for
   the single socket to become writable only */
static int http_getsock_do(struct connectdata *conn,
                           curl_socket_t *socks,
                           int numsocks)
{
  /* write mode */
  (void)numsocks; /* unused, we trust it to be at least 1 */
  socks[0] = conn->sock[FIRSTSOCKET];
  return GETSOCK_WRITESOCK(0);
}

#ifdef USE_SSL
static CURLcode https_connecting(struct connectdata *conn, bool *done)
  DEBUGASSERT((conn) && (conn->handler->flags & PROTOPT_SSL));

  /* perform SSL initialization for this socket */
  result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, done);
Yang Tse's avatar
Yang Tse committed
  if(result)
    connclose(conn, "Failed HTTPS connection");

Yang Tse's avatar
Yang Tse committed
  return result;
#if defined(USE_SSLEAY) || defined(USE_GNUTLS) || defined(USE_SCHANNEL) || \
    defined(USE_DARWINSSL) || defined(USE_POLARSSL) || defined(USE_NSS)
/* This function is for OpenSSL, GnuTLS, darwinssl, schannel and polarssl only.
   It should be made to query the generic SSL layer instead. */
static int https_getsock(struct connectdata *conn,
  if(conn->handler->flags & PROTOPT_SSL) {
    struct ssl_connect_data *connssl = &conn->ssl[FIRSTSOCKET];
    if(connssl->connecting_state == ssl_connect_2_writing) {
      socks[0] = conn->sock[FIRSTSOCKET];
      return GETSOCK_WRITESOCK(0);
    else if(connssl->connecting_state == ssl_connect_2_reading) {
      socks[0] = conn->sock[FIRSTSOCKET];
      return GETSOCK_READSOCK(0);
static int https_getsock(struct connectdata *conn,
                         curl_socket_t *socks,
                         int numsocks)
{
  (void)conn;
  (void)socks;
  (void)numsocks;
  return GETSOCK_BLANK;
}
#endif /* USE_SSLEAY || USE_GNUTLS || USE_SCHANNEL */
Daniel Stenberg's avatar
Daniel Stenberg committed
/*
 * Curl_http_done() gets called from Curl_done() after a single HTTP request
 * has been performed.
 */

CURLcode Curl_http_done(struct connectdata *conn,
  struct SessionHandle *data = conn->data;
  struct HTTP *http =data->req.protop;
  if(data->state.proxyneg.state == GSS_AUTHSENT ||
      data->state.negotiate.state == GSS_AUTHSENT)
    Curl_cleanup_negotiate(data);
#endif

  /* set the proper values (possibly modified on POST) */
  conn->fread_func = data->set.fread_func; /* restore */
  conn->fread_in = data->set.in; /* restore */
  conn->seek_func = data->set.seek_func; /* restore */
  conn->seek_client = data->set.seek_client; /* restore */
    Curl_send_buffer *buff = http->send_buffer;
    http->send_buffer = NULL; /* clear the pointer */
  if(HTTPREQ_POST_FORM == data->set.httpreq) {
    data->req.bytecount = http->readbytecount + http->writebytecount;
    Curl_formclean(&http->sendit); /* Now free that whole lot */
      /* a file being uploaded was left opened, close it! */
      fclose(http->form.fp);
Daniel Stenberg's avatar
Daniel Stenberg committed
  }
  else if(HTTPREQ_PUT == data->set.httpreq)
    data->req.bytecount = http->readbytecount + http->writebytecount;
Daniel Stenberg's avatar
Daniel Stenberg committed
  if(!premature && /* this check is pointless when DONE is called before the
                      entire operation is complete */
     !conn->bits.retry &&
       data->req.headerbytecount -
       data->req.deductheadercount)) <= 0) {
    /* If this connection isn't simply closed to be retried, AND nothing was
       read from the HTTP server (that counts), this can't be right so we
       return an error here */
    failf(data, "Empty reply from server");
/*
 * Determine if we should use HTTP 1.1 (OR BETTER) for this request. Reasons
 * to avoid it include:
 *
 * - if the user specifically requested HTTP 1.0
 * - if the server we are connected to only supports 1.0
 * - if any server previously contacted to handle this request only supports
 * 1.0.
 */
static bool use_http_1_1plus(const struct SessionHandle *data,
                             const struct connectdata *conn)
  return ((data->set.httpversion >= CURL_HTTP_VERSION_1_1) ||
         ((data->set.httpversion != CURL_HTTP_VERSION_1_0) &&
          ((conn->httpversion == 11) ||
           ((conn->httpversion != 10) &&
            (data->state.httpversion != 10))))) ? TRUE : FALSE;
/* check and possibly add an Expect: header */
static CURLcode expect100(struct SessionHandle *data,
  data->state.expect100header = FALSE; /* default to false unless it is set
                                          to TRUE below */
  if(use_http_1_1plus(data, conn)) {
    /* if not doing HTTP 1.0 or disabled explicitly, we add a Expect:
       100-continue to the headers which actually speeds up post operations
       (as there is one packet coming back from the web server) */
    ptr = Curl_checkheaders(conn, "Expect:");
      data->state.expect100header =
        Curl_compareheader(ptr, "Expect:", "100-continue");
    }
    else {
      result = Curl_add_bufferf(req_buffer,
                         "Expect: 100-continue\r\n");
      if(result == CURLE_OK)
        data->state.expect100header = TRUE;
    }
enum proxy_use {
  HEADER_SERVER,  /* direct to server */
  HEADER_PROXY,   /* regular request to proxy */
  HEADER_CONNECT  /* sending CONNECT to a proxy */
};

CURLcode Curl_add_custom_headers(struct connectdata *conn,
                                 Curl_send_buffer *req_buffer)
  struct curl_slist *h[2];
  struct curl_slist *headers;
  int numlists=1; /* by default */
  struct SessionHandle *data = conn->data;
  int i;
  enum proxy_use proxy;

  if(is_connect)
    proxy = HEADER_CONNECT;
  else
    proxy = conn->bits.httpproxy && !conn->bits.tunnel_proxy?
      HEADER_PROXY:HEADER_SERVER;

  switch(proxy) {
  case HEADER_SERVER:
    h[0] = data->set.headers;
    break;
  case HEADER_PROXY:
    h[0] = data->set.headers;
    if(data->set.sep_headers) {
      h[1] = data->set.proxyheaders;
      numlists++;
    break;
  case HEADER_CONNECT:
    if(data->set.sep_headers)
      h[0] = data->set.proxyheaders;
    else
      h[0] = data->set.headers;
    break;
  }

  /* loop through one or two lists */
  for(i=0; i < numlists; i++) {
    headers = h[i];

    while(headers) {
      ptr = strchr(headers->data, ':');
        /* we require a colon for this to be a true header */
        while(*ptr && ISSPACE(*ptr))
          ptr++;

        if(*ptr) {
          /* only send this if the contents was non-blank */

          if(conn->allocptr.host &&
             /* a Host: header was sent already, don't pass on any custom Host:
                header as that will produce *two* in the same request! */
             checkprefix("Host:", headers->data))
            ;
          else if(data->set.httpreq == HTTPREQ_POST_FORM &&
                  /* this header (extended by formdata.c) is sent later */
                  checkprefix("Content-Type:", headers->data))
            ;
          else if(conn->bits.authneg &&
                  /* while doing auth neg, don't allow the custom length since
                     we will force length zero then */
                  checkprefix("Content-Length", headers->data))
            ;
          else if(conn->allocptr.te &&
                  /* when asking for Transfer-Encoding, don't pass on a custom
                     Connection: */
                  checkprefix("Connection", headers->data))
            ;
          else {
            CURLcode result = Curl_add_bufferf(req_buffer, "%s\r\n",
                                               headers->data);
      else {
        ptr = strchr(headers->data, ';');
        if(ptr) {

          ptr++; /* pass the semicolon */
          while(*ptr && ISSPACE(*ptr))
            ptr++;

          if(*ptr) {
            /* this may be used for something else in the future */
          }
          else {
            if(*(--ptr) == ';') {
              CURLcode result;

              /* send no-value custom header if terminated by semicolon */
              *ptr = ':';
              result = Curl_add_bufferf(req_buffer, "%s\r\n",
                                        headers->data);
              if(result)
                return result;
            }
          }
        }
      }
      headers = headers->next;
  return CURLE_OK;
CURLcode Curl_add_timecondition(struct SessionHandle *data,
                                Curl_send_buffer *req_buffer)
{
  const struct tm *tm;
  struct tm keeptime;
  CURLcode result = Curl_gmtime(data->set.timevalue, &keeptime);
    failf(data, "Invalid TIMEVALUE");
    return result;
  }
  tm = &keeptime;

  /* The If-Modified-Since header family should have their times set in
   * GMT as RFC2616 defines: "All HTTP date/time stamps MUST be
   * represented in Greenwich Mean Time (GMT), without exception. For the
   * purposes of HTTP, GMT is exactly equal to UTC (Coordinated Universal
   * Time)." (see page 20 of RFC2616).
   */

  /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */
  snprintf(buf, BUFSIZE-1,
           "%s, %02d %s %4d %02d:%02d:%02d GMT",
           Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
           tm->tm_mday,
           Curl_month[tm->tm_mon],
           tm->tm_year + 1900,
           tm->tm_hour,
           tm->tm_min,
           tm->tm_sec);

  switch(data->set.timecondition) {
  case CURL_TIMECOND_IFMODSINCE:
  default:
    result = Curl_add_bufferf(req_buffer,
                              "If-Modified-Since: %s\r\n", buf);
    break;
  case CURL_TIMECOND_IFUNMODSINCE:
    result = Curl_add_bufferf(req_buffer,
                              "If-Unmodified-Since: %s\r\n", buf);
    break;
  case CURL_TIMECOND_LASTMOD:
    result = Curl_add_bufferf(req_buffer,
                              "Last-Modified: %s\r\n", buf);
    break;
  }

  return result;
}

Daniel Stenberg's avatar
Daniel Stenberg committed
/*
 * Curl_http() gets called from the generic Curl_do() function when a HTTP
Daniel Stenberg's avatar
Daniel Stenberg committed
 * request is to be performed. This creates and sends a properly constructed
Daniel Stenberg's avatar
Daniel Stenberg committed
 * HTTP request.
 */
CURLcode Curl_http(struct connectdata *conn, bool *done)
  struct SessionHandle *data=conn->data;
  CURLcode result=CURLE_OK;
  struct HTTP *http;
  const char *ppath = data->state.path;
  char ftp_typecode[sizeof("/;type=?")] = "";
  const char *host = conn->host.name;
Gisle Vanem's avatar
Gisle Vanem committed
  const char *te = ""; /* transfer-encoding */
  const char *ptr;
  Curl_HttpReq httpreq = data->set.httpreq;
Yang Tse's avatar
Yang Tse committed
  curl_off_t included_body = 0;
Yang Tse's avatar
Yang Tse committed
  curl_off_t postsize = 0; /* curl_off_t to handle large file sizes */
  /* Always consider the DO phase done after this function call, even if there
     may be parts of the request that is not yet sent, since we can deal with
     the rest of the request in the PERFORM phase. */
  *done = TRUE;

  if(conn->httpversion < 20) { /* unless the connection is re-used and already
                                  http2 */
    switch (conn->negnpn) {
      result = Curl_http2_init(conn);
      if(result)
        return result;

      result = Curl_http2_setup(conn);
      if(result)
        return result;

      result = Curl_http2_switched(conn);
      if(result)
        return result;
      break;
    case NPN_HTTP1_1:
      /* continue with HTTP/1.1 when explicitly requested */
      break;
    default:
      /* and as fallback */
      break;
    /* prepare for a http2 request */
    result = Curl_http2_setup(conn);
    if(result)
      return result;
  if(!data->state.this_is_a_follow) {
    /* this is not a followed location, get the original host name */
    if(data->state.first_host)
      /* Free to avoid leaking memory on multiple requests*/
      free(data->state.first_host);

    data->state.first_host = strdup(conn->host.name);
    if(!data->state.first_host)
      return CURLE_OUT_OF_MEMORY;
  }
  http->writebytecount = http->readbytecount = 0;
  if((conn->handler->protocol&(PROTO_FAMILY_HTTP|CURLPROTO_FTP)) &&
Daniel Stenberg's avatar
Daniel Stenberg committed
  }
  /* Now set the 'request' pointer to the proper request string */
  if(data->set.str[STRING_CUSTOMREQUEST])
    request = data->set.str[STRING_CUSTOMREQUEST];
      DEBUGASSERT((httpreq > HTTPREQ_NONE) && (httpreq < HTTPREQ_LAST));
      switch(httpreq) {
      case HTTPREQ_POST:
      case HTTPREQ_POST_FORM:
      default: /* this should never happen */
  /* The User-Agent string might have been allocated 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(Curl_checkheaders(conn, "User-Agent:") && conn->allocptr.uagent) {
    free(conn->allocptr.uagent);
    conn->allocptr.uagent=NULL;
  /* setup the authentication headers */
  result = Curl_http_output_auth(conn, request, ppath, FALSE);
  if((data->state.authhost.multi || data->state.authproxy.multi) &&
     (httpreq != HTTPREQ_GET) &&
     (httpreq != HTTPREQ_HEAD)) {
    /* Auth is required and we are not authenticated yet. Make a PUT or POST
       with content-length zero as a "probe". */
    conn->bits.authneg = TRUE;
    conn->bits.authneg = FALSE;
  if(data->change.referer && !Curl_checkheaders(conn, "Referer:")) {
    conn->allocptr.ref = aprintf("Referer: %s\r\n", data->change.referer);
    if(!conn->allocptr.ref)
      return CURLE_OUT_OF_MEMORY;
  }
  if(data->set.str[STRING_COOKIE] && !Curl_checkheaders(conn, "Cookie:"))
    addcookies = data->set.str[STRING_COOKIE];
Daniel Stenberg's avatar
Daniel Stenberg committed

  if(!Curl_checkheaders(conn, "Accept-Encoding:") &&
    Curl_safefree(conn->allocptr.accept_encoding);
    conn->allocptr.accept_encoding =
      aprintf("Accept-Encoding: %s\r\n", data->set.str[STRING_ENCODING]);
    if(!conn->allocptr.accept_encoding)
      return CURLE_OUT_OF_MEMORY;
  }

#ifdef HAVE_LIBZ
  /* we only consider transfer-encoding magic if libz support is built-in */

  if(!Curl_checkheaders(conn, "TE:") &&
     data->set.http_transfer_encoding) {
    /* When we are to insert a TE: header in the request, we must also insert
       TE in a Connection: header, so we need to merge the custom provided
       Connection: header and prevent the original to get sent. Note that if
       the user has inserted his/hers own TE: header we don't do this magic
       but then assume that the user will handle it all! */
    char *cptr = Curl_checkheaders(conn, "Connection:");
#define TE_HEADER "TE: gzip\r\n"
    Curl_safefree(conn->allocptr.te);

    /* Create the (updated) Connection: header */
    conn->allocptr.te = cptr? aprintf("%s, TE\r\n" TE_HEADER, cptr):
      strdup("Connection: TE\r\n" TE_HEADER);
    if(!conn->allocptr.te)
      return CURLE_OUT_OF_MEMORY;
  }
  if(conn->httpversion == 20)
    /* In HTTP2 forbids Transfer-Encoding: chunked */
    ptr = NULL;
    ptr = Curl_checkheaders(conn, "Transfer-Encoding:");
    if(ptr) {
      /* Some kind of TE is requested, check if 'chunked' is chosen */
      data->req.upload_chunky =
        Curl_compareheader(ptr, "Transfer-Encoding:", "chunked");
    }
    else {
      if((conn->handler->protocol&PROTO_FAMILY_HTTP) &&
         data->set.upload &&
         (data->state.infilesize == -1)) {
        if(conn->bits.authneg)
          /* don't enable chunked during auth neg */
          ;
        else if(use_http_1_1plus(data, conn)) {
          /* HTTP, upload, unknown file size and not HTTP 1.0 */
          data->req.upload_chunky = TRUE;
        }
        else {
          failf(data, "Chunky upload is not supported by HTTP 1.0");
          return CURLE_UPLOAD_FAILED;
        }
        /* else, no chunky upload */
        data->req.upload_chunky = FALSE;
      if(data->req.upload_chunky)
        te = "Transfer-Encoding: chunked\r\n";
    }
  ptr = Curl_checkheaders(conn, "Host:");
  if(ptr && (!data->state.this_is_a_follow ||
             Curl_raw_equal(data->state.first_host, conn->host.name))) {
#if !defined(CURL_DISABLE_COOKIES)
    /* If we have a given custom Host: header, we extract the host name in
       order to possibly use it for cookie reasons later on. We only allow the
       custom Host: header if this is NOT a redirect, as setting Host: in the
       redirected request is being out on thin ice. Except if the host name
       is the same as the first one! */
    char *cookiehost = Curl_copy_header_value(ptr);
      return CURLE_OUT_OF_MEMORY;
      /* ignore empty data */
      /* If the host begins with '[', we start searching for the port after
         the bracket has been closed */
      int startsearch = 0;
      if(*cookiehost == '[') {
        char *closingbracket;
        /* since the 'cookiehost' is an allocated memory area that will be
           freed later we cannot simply increment the pointer */
        memmove(cookiehost, cookiehost + 1, strlen(cookiehost) - 1);
        closingbracket = strchr(cookiehost, ']');
        if(closingbracket)
          *closingbracket = 0;
      }
      else {
        char *colon = strchr(cookiehost + startsearch, ':');
        if(colon)
          *colon = 0; /* The host must not include an embedded port number */
      }
      Curl_safefree(conn->allocptr.cookiehost);
      conn->allocptr.cookiehost = cookiehost;
    /* 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->given->protocol&CURLPROTO_HTTPS) &&
        (conn->remote_port == PORT_HTTPS)) ||
       ((conn->given->protocol&CURLPROTO_HTTP) &&
        (conn->remote_port == PORT_HTTP)) )
      /* if(HTTPS on port 443) OR (HTTP 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?"]":"");
Yang Tse's avatar
 
Yang Tse committed
      conn->allocptr.host = aprintf("Host: %s%s%s:%hu\r\n",
                                    conn->bits.ipv6_ip?"[":"",
                                    host,
                                    conn->bits.ipv6_ip?"]":"",

    if(!conn->allocptr.host)
      /* without Host: we can't make a nice request */
      return CURLE_OUT_OF_MEMORY;
Daniel Stenberg's avatar
Daniel Stenberg committed

#ifndef CURL_DISABLE_PROXY
  if(conn->bits.httpproxy && !conn->bits.tunnel_proxy)  {
    /* Using a proxy but does not tunnel through it */

    /* The path sent to the proxy is in fact the entire URL. But if the remote
       host is a IDN-name, we must make sure that the request we produce only
       uses the encoded host name! */
    if(conn->host.dispname != conn->host.name) {
      char *url = data->change.url;
      ptr = strstr(url, conn->host.dispname);
      if(ptr) {
        /* This is where the display name starts in the URL, now replace this
           part with the encoded name. TODO: This method of replacing the host
           name is rather crude as I believe there's a slight risk that the