Skip to content
http.c 88.6 KiB
Newer Older
  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;
  Curl_safefree(conn->allocptr.ref);
  if(data->change.referer && !checkheaders(data, "Referer:"))
    conn->allocptr.ref = aprintf("Referer: %s\r\n", data->change.referer);
  if(data->set.str[STRING_COOKIE] && !checkheaders(data, "Cookie:"))
    addcookies = data->set.str[STRING_COOKIE];
Daniel Stenberg's avatar
Daniel Stenberg committed

  if(!checkheaders(data, "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;
  }

  ptr = checkheaders(data, "Transfer-Encoding:");
  if(ptr) {
    /* Some kind of TE is requested, check if 'chunked' is chosen */
      Curl_compareheader(ptr, "Transfer-Encoding:", "chunked");
    if((conn->protocol&PROT_HTTP) &&
        data->set.upload &&
        (data->set.infilesize == -1) &&
        (data->set.httpversion != CURL_HTTP_VERSION_1_0)) {
      /* HTTP, upload, unknown file size and not HTTP 1.0 */
      data->req.upload_chunky = TRUE;
    }
    else {
      /* else, no chunky upload */
      data->req.upload_chunky = FALSE;
    }

    if(data->req.upload_chunky)
  ptr = checkheaders(data, "Host:");
  if(ptr && (!data->state.this_is_a_follow ||
             curl_strequal(data->state.first_host, conn->host.name))) {
    /* 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 *start = ptr+strlen("Host:");
      start++;
    ptr = start; /* start host-scanning here */

    /* scan through the string to find the end (space or colon) */
    while(*ptr && !ISSPACE(*ptr) && !(':'==*ptr))
      size_t len=ptr-start;
      Curl_safefree(conn->allocptr.cookiehost);
      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;
    }
    /* 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?"]":"");
      conn->allocptr.host = aprintf("Host: %s%s%s:%d\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

  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
           user has entered a user name or password that contain the host name
           string. */
        size_t currlen = strlen(conn->host.dispname);
        size_t newlen = strlen(conn->host.name);
        size_t urllen = strlen(url);
        newurl = malloc(urllen + newlen - currlen + 1);
        if(newurl) {
          /* copy the part before the host name */
          memcpy(newurl, url, ptr - url);
          /* append the new host name instead of the old */
          memcpy(newurl + (ptr - url), conn->host.name, newlen);
          /* append the piece after the host name */
          memcpy(newurl + newlen + (ptr - url),
                 ptr + currlen, /* copy the trailing zero byte too */
                 urllen - (ptr-url) - currlen + 1);
          if(data->change.url_alloc)
            free(data->change.url);
          data->change.url = newurl;
          data->change.url_alloc = TRUE;
        }
        else
          return CURLE_OUT_OF_MEMORY;
    if (data->set.proxy_transfer_mode) {
      /* when doing ftp, append ;type=<a|i> if not present */
      if(checkprefix("ftp://", ppath) || checkprefix("ftps://", ppath)) {
        char *p = strstr(ppath, ";type=");
        if(p && p[6] && p[7] == 0) {
          switch (toupper((int)((unsigned char)p[6]))) {
          case 'A':
          case 'D':
          case 'I':
            break;
          default:
            p = NULL;
          }
        if(!p)
          snprintf(ftp_typecode, sizeof(ftp_typecode), ";type=%c",
                   data->set.prefer_ascii ? 'a' : 'i');
    /* 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,
                               checkheaders(data, "Content-Type:"),
                               &http->postsize);
     if(CURLE_OK != result) {
       /* Curl_getFormData() doesn't use failf() */
       failf(data, "failed creating formpost data");
       return result;
     }
  }


  http->p_pragma =
    (!checkheaders(data, "Pragma:") &&
     (conn->bits.httpproxy && !conn->bits.tunnel_proxy) )?
    "Pragma: no-cache\r\n":NULL;
Daniel Stenberg's avatar
Daniel Stenberg committed

  http->p_accept = checkheaders(data, "Accept:")?NULL:"Accept: */*\r\n";
Daniel Stenberg's avatar
Daniel Stenberg committed

  if(( (HTTPREQ_POST == httpreq) ||
       (HTTPREQ_POST_FORM == httpreq) ||
       (HTTPREQ_PUT == httpreq) ) &&
    /**********************************************************************
     * 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.
     *********************************************************************/
      /*
       * This is meant to get the size of the present remote-file by itself.
       * We don't support this now. Bail out!
       */
    if(data->state.resume_from && !data->state.this_is_a_follow) {
      /* do we still game? */

      /* Now, let's read off the proper amount of bytes from the
         input. */
      if(conn->seek_func) {
        curl_off_t readthisamountnow = data->state.resume_from;

        if(conn->seek_func(conn->seek_client,
          return CURLE_READ_ERROR;
        }
          size_t readthisamountnow = (size_t)(data->state.resume_from - passed);
          size_t actuallyread;

          if(readthisamountnow > BUFSIZE)
            readthisamountnow = BUFSIZE;

          actuallyread = data->set.fread_func(data->state.buffer, 1,
                                              (size_t)readthisamountnow,
                                              data->set.in);

          passed += actuallyread;
          if(actuallyread != readthisamountnow) {
            failf(data, "Could only read %" FORMAT_OFF_T
                  " bytes from the input",
                  passed);
            return CURLE_READ_ERROR;
          }
        } while(passed != data->state.resume_from); /* loop until done */
      }

      /* now, decrease the size of the read */
        data->set.infilesize -= data->state.resume_from;
          failf(data, "File already completely uploaded");
          return CURLE_PARTIAL_FILE;
        }
      }
      /* we've passed, proceed as normal */
    }
  }
    /*
     * A range is selected. We use different headers whether we're downloading
     * or uploading and we always let customized headers override our internal
     * ones if any such are specified.
     */
      /* if a line like this was already allocated, free the previous one */
      if(conn->allocptr.rangeline)
        free(conn->allocptr.rangeline);
      conn->allocptr.rangeline = aprintf("Range: bytes=%s\r\n",
      /* if a line like this was already allocated, free the previous one */
      if(conn->allocptr.rangeline)
        free(conn->allocptr.rangeline);

        /* This is because "resume" was selected */
        curl_off_t total_expected_size=
          data->state.resume_from + data->set.infilesize;
        conn->allocptr.rangeline =
            aprintf("Content-Range: bytes %s%" FORMAT_OFF_T
                    data->state.range, total_expected_size-1,
                    total_expected_size);
        /* Range was selected and then we just pass the incoming range and
        conn->allocptr.rangeline =
            aprintf("Content-Range: bytes %s/%" FORMAT_OFF_T "\r\n",
                    data->state.range, data->set.infilesize);
      if(!conn->allocptr.rangeline)
        return CURLE_OUT_OF_MEMORY;
  /* Use 1.1 unless the use specificly asked for 1.0 */
  httpstring= data->set.httpversion==CURL_HTTP_VERSION_1_0?"1.0":"1.1";
  /* initialize a dynamic send-buffer */
  req_buffer = add_buffer_init();
  /* add the main request stuff */
  result =
    add_bufferf(req_buffer,
                "%s " /* GET/HEAD/POST/PUT */
                "%s%s HTTP/%s\r\n" /* path + HTTP version */
                "%s" /* proxyuserpwd */
                "%s" /* userpwd */
                "%s" /* range */
                "%s" /* user agent */
                "%s" /* host */
                "%s" /* pragma */
                "%s" /* accept */
                "%s" /* accept-encoding */
                "%s" /* referer */
                "%s" /* Proxy-Connection */
                "%s",/* transfer-encoding */
                conn->allocptr.proxyuserpwd?
                conn->allocptr.userpwd?conn->allocptr.userpwd:"",
                (data->state.use_range && conn->allocptr.rangeline)?
                (data->set.str[STRING_USERAGENT] &&
                 *data->set.str[STRING_USERAGENT] && conn->allocptr.uagent)?
                (conn->allocptr.host?conn->allocptr.host:""), /* Host: host */
                http->p_pragma?http->p_pragma:"",
                http->p_accept?http->p_accept:"",
                (data->set.str[STRING_ENCODING] &&
                 *data->set.str[STRING_ENCODING] &&
                 conn->allocptr.accept_encoding)?
                conn->allocptr.accept_encoding:"",
                (data->change.referer && conn->allocptr.ref)?
                conn->allocptr.ref:"" /* Referer: <data> */,
                (conn->bits.httpproxy &&
                 !conn->bits.tunnel_proxy &&
                 !checkheaders(data, "Proxy-Connection:"))?
                "Proxy-Connection: Keep-Alive\r\n":"",
                te
      );
Daniel Stenberg's avatar
Daniel Stenberg committed

  /*
   * Free userpwd now --- cannot reuse this for Negotiate and possibly NTLM
   * with basic and digest, it will be freed anyway by the next request
   */
  Curl_safefree (conn->allocptr.userpwd);
  conn->allocptr.userpwd = NULL;
  if(data->cookies || addcookies) {
    struct Cookie *co=NULL; /* no cookies from start */
    int count=0;

    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,
                               data->state.path,
                               (bool)(conn->protocol&PROT_HTTPS?TRUE:FALSE));
      Curl_share_unlock(data, CURL_LOCK_DATA_COOKIE);
    }
    if(co) {
      struct Cookie *store=co;
      /* now loop through all cookies that matched */
      while(co) {
        if(co->value) {
          if(0 == count) {
            result = add_bufferf(req_buffer, "Cookie: ");
          result = add_bufferf(req_buffer,
                               "%s%s=%s", count?"; ":"",
                               co->name, co->value);
          if(result)
            break;
        co = co->next; /* next cookie please */
      Curl_cookie_freelist(store, FALSE); /* free the cookie list */
Daniel Stenberg's avatar
Daniel Stenberg committed
    }
    if(addcookies && (CURLE_OK == result)) {
      if(!count)
        result = add_bufferf(req_buffer, "Cookie: ");
      if(CURLE_OK == result) {
        result = add_bufferf(req_buffer, "%s%s",
                             count?"; ":"",
                             addcookies);
        count++;
      }
    }
    if(count && (CURLE_OK == result))
      result = add_buffer(req_buffer, "\r\n", 2);

    if(result)
      return result;
  }
Daniel Stenberg's avatar
Daniel Stenberg committed

  if(data->set.timecondition) {
    struct tm *tm;
Daniel Stenberg's avatar
Daniel Stenberg committed

    /* 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).
     */
Daniel Stenberg's avatar
Daniel Stenberg committed
#ifdef HAVE_GMTIME_R
    /* thread-safe version */
    struct tm keeptime;
    tm = (struct tm *)gmtime_r(&data->set.timevalue, &keeptime);
Daniel Stenberg's avatar
Daniel Stenberg committed
#else
Daniel Stenberg's avatar
Daniel Stenberg committed
#endif
Daniel Stenberg's avatar
Daniel Stenberg committed

    /* 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 = add_bufferf(req_buffer,
                           "If-Modified-Since: %s\r\n", buf);
      break;
    case CURL_TIMECOND_IFUNMODSINCE:
      result = add_bufferf(req_buffer,
                           "If-Unmodified-Since: %s\r\n", buf);
      break;
    case CURL_TIMECOND_LASTMOD:
      result = add_bufferf(req_buffer,
                           "Last-Modified: %s\r\n", buf);
      break;
Daniel Stenberg's avatar
Daniel Stenberg committed
    }
Daniel Stenberg's avatar
Daniel Stenberg committed

  result = add_custom_headers(conn, req_buffer);
  if(result)
    return result;
  http->postdata = NULL;  /* nothing to post at this point */
  Curl_pgrsSetUploadSize(data, 0); /* upload size is 0 atm */
  /* If 'authdone' is FALSE, we must not set the write socket index to the
     Curl_transfer() call below, as we're not ready to actually upload any
     data yet. */
  case HTTPREQ_POST_FORM:
    if(!http->sendit || conn->bits.authneg) {
      /* nothing to post! */
      result = add_bufferf(req_buffer, "Content-Length: 0\r\n\r\n");
      if(result)
        return result;
      result = add_buffer_send(req_buffer, conn,
                               &data->info.request_size, 0, FIRSTSOCKET);
      if(result)
        failf(data, "Failed sending POST request");
      else
        /* setup variables for the upcoming transfer */
        result = Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE,
                                     &http->readbytecount,
                                     -1, NULL);
      break;
    }
Daniel Stenberg's avatar
Daniel Stenberg committed

    if(Curl_FormInit(&http->form, http->sendit)) {
      failf(data, "Internal HTTP POST error!");
      return CURLE_HTTP_POST_ERROR;
    }
Daniel Stenberg's avatar
Daniel Stenberg committed

    /* set the read function to read from the generated form data */
    http->form.fread_func = conn->fread_func; /* get the previously set callback
                                                 function pointer */
    conn->fread_func = (curl_read_callback)Curl_FormReader;
    conn->fread_in = &http->form;
    if(!data->req.upload_chunky) {
      /* only add Content-Length if not uploading chunked */
      result = add_bufferf(req_buffer,
                           "Content-Length: %" FORMAT_OFF_T "\r\n",
                           http->postsize);
    result = expect100(data, req_buffer);
    if(result)
      return result;
      /* Get Content-Type: line from Curl_formpostheader.
       */
      char *contentType;
      size_t linelength=0;
      contentType = Curl_formpostheader((void *)&http->form,
                                        &linelength);
      if(!contentType) {
        failf(data, "Could not get Content-Type header line!");
        return CURLE_HTTP_POST_ERROR;
      result = add_buffer(req_buffer, contentType, linelength);
    /* make the request end in a true CRLF */
    result = add_buffer(req_buffer, "\r\n", 2);
    if(result)
      return result;
Daniel Stenberg's avatar
Daniel Stenberg committed

    /* set upload size to the progress meter */
    Curl_pgrsSetUploadSize(data, http->postsize);
    /* fire away the whole request to the server */
    result = add_buffer_send(req_buffer, conn,
                             &data->info.request_size, 0, FIRSTSOCKET);
    if(result)
      failf(data, "Failed sending POST request");
    else
      /* setup variables for the upcoming transfer */
      result = Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE,
                                   &http->readbytecount,
                                   FIRSTSOCKET,
                                   &http->writebytecount);

    if(result) {
      Curl_formclean(&http->sendit); /* free that whole lot */
      return result;
    }
#ifdef CURL_DOES_CONVERSIONS
/* time to convert the form data... */
    result = Curl_formconvert(data, http->sendit);
    if(result) {
      Curl_formclean(&http->sendit); /* free that whole lot */
      return result;
    }
Daniel Stenberg's avatar
Daniel Stenberg committed

  case HTTPREQ_PUT: /* Let's PUT the data to the server! */
    if(conn->bits.authneg)
      postsize = 0;
    else
      postsize = data->set.infilesize;
    if((postsize != -1) && !data->req.upload_chunky) {
      /* only add Content-Length if not uploading chunked */
      result = add_bufferf(req_buffer,
                           "Content-Length: %" FORMAT_OFF_T "\r\n",
                           postsize );
Daniel Stenberg's avatar
Daniel Stenberg committed

    result = expect100(data, req_buffer);
    if(result)
      return result;
    result = add_buffer(req_buffer, "\r\n", 2); /* end of headers */
    if(result)
      return result;
Daniel Stenberg's avatar
Daniel Stenberg committed

    /* set the upload size to the progress meter */
    Curl_pgrsSetUploadSize(data, postsize);
    /* this sends the buffer and frees all the buffer resources */
    result = add_buffer_send(req_buffer, conn,
                             &data->info.request_size, 0, FIRSTSOCKET);
    if(result)
      failf(data, "Failed sending PUT request");
    else
      /* prepare for transfer */
      result = Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE,
                                   &http->readbytecount,
                                   postsize?FIRSTSOCKET:-1,
                                   postsize?&http->writebytecount:NULL);
    if(result)
      return result;
    break;
  case HTTPREQ_POST:
    /* this is the simple POST, using x-www-form-urlencoded style */

    if(conn->bits.authneg)
      postsize = 0;
    else
      /* figure out the size of the postfields */
      postsize = (data->set.postfieldsize != -1)?
        data->set.postfieldsize:
        (data->set.postfields? (curl_off_t)strlen(data->set.postfields):0);

        if(!data->req.upload_chunky) {
          /* We only set Content-Length and allow a custom Content-Length if
             we don't upload data chunked, as RFC2616 forbids us to set both
             kinds of headers (Transfer-Encoding: chunked and Content-Length) */

          if(!checkheaders(data, "Content-Length:")) {
            /* we allow replacing this header, although it isn't very wise to
               actually set your own */
            result = add_bufferf(req_buffer,
                                 "Content-Length: %" FORMAT_OFF_T"\r\n",
                                 postsize);
            if(result)
              return result;
          }
        }

        if(!checkheaders(data, "Content-Type:")) {
          result = add_bufferf(req_buffer,
                               "Content-Type: application/x-www-form-urlencoded\r\n");
        /* For really small posts we don't use Expect: headers at all, and for
           the somewhat bigger ones we allow the app to disable it. Just make
           sure that the expect100header is always set to the preferred value
           here. */
        if(postsize > TINY_INITIAL_POST_SIZE) {
          result = expect100(data, req_buffer);
          if(result)
            return result;
        }
        else
          data->state.expect100header = FALSE;
          if(!data->state.expect100header &&
             (postsize < MAX_INITIAL_POST_SIZE))  {
            /* if we don't use expect: 100  AND
               postsize is less than MAX_INITIAL_POST_SIZE
               then append the post data to the HTTP request header. This limit
               is no magic limit but only set to prevent really huge POSTs to
               get the data duplicated with malloc() and family. */
            result = add_buffer(req_buffer, "\r\n", 2); /* end of headers! */
            if(result)
              return result;
Daniel Stenberg's avatar
Daniel Stenberg committed

            if(!data->req.upload_chunky) {
              /* We're not sending it 'chunked', append it to the request
                 already now to reduce the number if send() calls */
              result = add_buffer(req_buffer, data->set.postfields,
              included_body = postsize;
            }
            else {
              /* Append the POST data chunky-style */
              result = add_bufferf(req_buffer, "%x\r\n", (int)postsize);
              if(CURLE_OK == result)
                result = add_buffer(req_buffer, data->set.postfields,
                                    (size_t)postsize);
              if(CURLE_OK == result)
                result = add_buffer(req_buffer,
                                    "\x0d\x0a\x30\x0d\x0a\x0d\x0a", 7);
              /* CR  LF   0  CR  LF  CR  LF */
              included_body = postsize + 7;
            }
            if(result)
              return result;
          else {
            /* A huge POST coming up, do data separate from the request */
            http->postsize = postsize;
            http->postdata = data->set.postfields;
            conn->fread_func = (curl_read_callback)readmoredata;
            conn->fread_in = (void *)conn;
            /* set the upload size to the progress meter */
            Curl_pgrsSetUploadSize(data, http->postsize);
Daniel Stenberg's avatar
Daniel Stenberg committed

            result = add_buffer(req_buffer, "\r\n", 2); /* end of headers! */
            if(result)
              return result;
          }
        }
        else {
          result = add_buffer(req_buffer, "\r\n", 2); /* end of headers! */
          if(result)
            return result;
Daniel Stenberg's avatar
Daniel Stenberg committed

          if(data->set.postfieldsize) {
            /* set the upload size to the progress meter */
            Curl_pgrsSetUploadSize(data, postsize?postsize:-1);
            /* set the pointer to mark that we will send the post body using the
               read callback, but only if we're not in authenticate
               negotiation  */
            if(!conn->bits.authneg) {
              http->postdata = (char *)&http->postdata;
              http->postsize = postsize;
            }
        /* issue the request */
        result = add_buffer_send(req_buffer, conn, &data->info.request_size,
                                 (size_t)included_body, FIRSTSOCKET);
        if(result)
          failf(data, "Failed sending HTTP POST request");
        else
          result =
            Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE,
                                &http->readbytecount,
                                http->postdata?FIRSTSOCKET:-1,
                                http->postdata?&http->writebytecount:NULL);
        break;
  default:
    result = add_buffer(req_buffer, "\r\n", 2);
    if(result)
      return result;
    /* issue the request */
    result = add_buffer_send(req_buffer, conn,
                             &data->info.request_size, 0, FIRSTSOCKET);
Daniel Stenberg's avatar
Daniel Stenberg committed
    if(result)
      failf(data, "Failed sending HTTP request");
    else
      /* HTTP GET/HEAD download: */
      result = Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE,
                                   &http->readbytecount,
                                   http->postdata?FIRSTSOCKET:-1,
                                   http->postdata?&http->writebytecount:NULL);
Daniel Stenberg's avatar
Daniel Stenberg committed

  if(http->writebytecount) {
    /* if a request-body has been sent off, we make sure this progress is noted
       properly */
    Curl_pgrsSetUploadCounter(data, http->writebytecount);
    if(Curl_pgrsUpdate(conn))
      result = CURLE_ABORTED_BY_CALLBACK;
  }

  return result;
Daniel Stenberg's avatar
Daniel Stenberg committed
}