Skip to content
http.c 108 KiB
Newer Older
          } while(passed < data->state.resume_from);

      /* 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(((httpreq == HTTPREQ_GET) || (httpreq == HTTPREQ_HEAD)) &&
      /* 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",
            !Curl_checkheaders(data, "Content-Range:")) {
      /* if a line like this was already allocated, free the previous one */
      if(conn->allocptr.rangeline)
        free(conn->allocptr.rangeline);

      if(data->set.set_resume_from < 0) {
        /* Upload resume was asked for, but we don't know the size of the
           remote part so we tell the server (and act accordingly) that we
           upload the whole file (again) */
        conn->allocptr.rangeline =
          aprintf("Content-Range: bytes 0-%" FORMAT_OFF_T
                  "/%" FORMAT_OFF_T "\r\n",
                  data->set.infilesize - 1, data->set.infilesize);

      }
      else if(data->state.resume_from) {
        /* 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
                    "/%" FORMAT_OFF_T "\r\n",
                    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 user specifically asked for 1.0 or the server only
     supports 1.0 */
  httpstring= use_http_1_1(data, conn)?"1.1":"1.0";
  result = Curl_add_bufferf(req_buffer, "%s ", request);
    result = Curl_add_bufferf(req_buffer, "ftp://%s:%s@%s",
                              conn->user, conn->passwd,
                              ppath + sizeof("ftp://") - 1);
    result = Curl_add_buffer(req_buffer, ppath, strlen(ppath));
                "%s" /* ftp typecode (;type=x) */
                " HTTP/%s\r\n" /* HTTP version */
                "%s" /* proxyuserpwd */
                "%s" /* userpwd */
                "%s" /* range */
                "%s" /* user agent */
                "%s" /* host */
                "%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 */
                conn->allocptr.te?conn->allocptr.te:"",
                (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 &&
                 !Curl_checkheaders(data, "Proxy-Connection:"))?
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,
                               (bool)(conn->handler->protocol&CURLPROTO_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 = Curl_add_bufferf(req_buffer, "Cookie: ");
                               "%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 = Curl_add_bufferf(req_buffer, "Cookie: ");
        result = Curl_add_bufferf(req_buffer, "%s%s",
                             count?"; ":"",
                             addcookies);
        count++;
      }
    }
    if(count && (CURLE_OK == result))
      result = Curl_add_buffer(req_buffer, "\r\n", 2);
Daniel Stenberg's avatar
Daniel Stenberg committed

    result = Curl_add_timecondition(data, req_buffer);
Daniel Stenberg's avatar
Daniel Stenberg committed

  result = Curl_add_custom_headers(conn, req_buffer);
  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 = Curl_add_bufferf(req_buffer, "Content-Length: 0\r\n\r\n");
      result = Curl_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 */
        Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE, &http->readbytecount,
                            -1, NULL);
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

    /* Get the currently set callback function pointer and store that in the
       form struct since we might want the actual user-provided callback later
       on. The conn->fread_func pointer itself will be changed for the
       multipart case to the function that returns a multipart formatted
       stream. */
    http->form.fread_func = conn->fread_func;

    /* Set the read function to read from the generated form data */
    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 */
                           "Content-Length: %" FORMAT_OFF_T "\r\n",
    result = expect100(data, conn, req_buffer);
      /* 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 = Curl_add_buffer(req_buffer, contentType, linelength);
    /* make the request end in a true CRLF */
    result = Curl_add_buffer(req_buffer, "\r\n", 2);
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 = Curl_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 */
      Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE,
                          &http->readbytecount, FIRSTSOCKET,
                          &http->writebytecount);

    if(result) {
      Curl_formclean(&http->sendit); /* free that whole lot */
      return result;
    }

    /* convert the form data */
    result = Curl_convert_form(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 */
                           "Content-Length: %" FORMAT_OFF_T "\r\n",
Daniel Stenberg's avatar
Daniel Stenberg committed

    result = expect100(data, conn, req_buffer);
    result = Curl_add_buffer(req_buffer, "\r\n", 2); /* end of headers */
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 = Curl_add_buffer_send(req_buffer, conn,
                                  &data->info.request_size, 0, FIRSTSOCKET);
    if(result)
      failf(data, "Failed sending PUT request");
    else
      /* prepare for transfer */
      Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE,
                          &http->readbytecount, postsize?FIRSTSOCKET:-1,
                          postsize?&http->writebytecount:NULL);
  case HTTPREQ_POST:
    /* this is the simple POST, using x-www-form-urlencoded style */

    if(conn->bits.authneg)
      postsize = 0;
      /* 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):-1);
    }
    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(conn->bits.authneg || !Curl_checkheaders(data, "Content-Length:")) {
        /* we allow replacing this header if not during auth negotiation,
           although it isn't very wise to actually set your own */
                             "Content-Length: %" FORMAT_OFF_T"\r\n",
                             postsize);
        if(result)
          return result;
      }
    }
    if(!Curl_checkheaders(data, "Content-Type:")) {
      result = Curl_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. */
    ptr = Curl_checkheaders(data, "Expect:");
    if(ptr) {
      data->state.expect100header =
        Curl_compareheader(ptr, "Expect:", "100-continue");
    }
    else if(postsize > TINY_INITIAL_POST_SIZE || postsize < 0) {
      result = expect100(data, conn, 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 = Curl_add_buffer(req_buffer, "\r\n", 2); /* end of headers! */
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 = Curl_add_buffer(req_buffer, data->set.postfields,
                              (size_t)postsize);
          included_body = postsize;
        }
        else {
          /* Append the POST data chunky-style */
          result = Curl_add_bufferf(req_buffer, "%x\r\n", (int)postsize);
            result = Curl_add_buffer(req_buffer, data->set.postfields,
                                "\x0d\x0a\x30\x0d\x0a\x0d\x0a", 7);
          /* CR  LF   0  CR  LF  CR  LF */
          included_body = postsize + 7;
        }
        if(result)
          return result;
        /* Make sure the progress information is accurate */
        Curl_pgrsSetUploadSize(data, postsize);
      }
      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 = Curl_add_buffer(req_buffer, "\r\n", 2); /* end of headers! */
      result = Curl_add_buffer(req_buffer, "\r\n", 2); /* end of headers! */
Daniel Stenberg's avatar
Daniel Stenberg committed

      if(data->req.upload_chunky && conn->bits.authneg) {
        /* Chunky upload is selected and we're negotiating auth still, send
           end-of-data only */
                            "\x0d\x0a\x30\x0d\x0a\x0d\x0a", 7);
        /* CR  LF   0  CR  LF  CR  LF */
        if(result)
          return result;
      }

      else 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;
    result = Curl_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
      Curl_setup_transfer(conn, FIRSTSOCKET, -1, TRUE,
                          &http->readbytecount, http->postdata?FIRSTSOCKET:-1,
                          http->postdata?&http->writebytecount:NULL);
    result = Curl_add_buffer(req_buffer, "\r\n", 2);
    result = Curl_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: */
      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;

    if(http->writebytecount >= postsize) {
      /* already sent the entire request body, mark the "upload" as
         complete */
      infof(data, "upload completely sent off: %" FORMAT_OFF_T "out of "
            "%" FORMAT_OFF_T " bytes\n",
            http->writebytecount, postsize);
      data->req.upload_done = TRUE;
      data->req.keepon &= ~KEEP_SEND; /* we're done writing */
      data->req.exp100 = EXP100_SEND_DATA; /* already sent */
    }
Daniel Stenberg's avatar
Daniel Stenberg committed
}

/*
 * checkhttpprefix()
 *
 * Returns TRUE if member of the list matches prefix of string
 */
static bool
checkhttpprefix(struct SessionHandle *data,
                const char *s)
{
  struct curl_slist *head = data->set.http200aliases;
  bool rc = FALSE;
#ifdef CURL_DOES_CONVERSIONS
  /* convert from the network encoding using a scratch area */
    failf (data, "Failed to allocate memory for conversion!");
    return FALSE; /* can't return CURLE_OUT_OF_MEMORY so return FALSE */
  }
  if(CURLE_OK != Curl_convert_from_network(data, scratch, strlen(s)+1)) {
    /* Curl_convert_from_network calls failf if unsuccessful */
    free(scratch);
    return FALSE; /* can't return CURLE_foobar so return FALSE */
  }
  s = scratch;
#endif /* CURL_DOES_CONVERSIONS */

  while(head) {
    if(checkprefix(head->data, s)) {
      rc = TRUE;
      break;
    }
    head = head->next;
  }

  if((rc != TRUE) && (checkprefix("HTTP/", s)))
    rc = TRUE;

#ifdef CURL_DOES_CONVERSIONS
  free(scratch);
#endif /* CURL_DOES_CONVERSIONS */
  return rc;
}

#ifndef CURL_DISABLE_RTSP
static bool
checkrtspprefix(struct SessionHandle *data,
                const char *s)
{

#ifdef CURL_DOES_CONVERSIONS
  /* convert from the network encoding using a scratch area */
  char *scratch = strdup(s);
  if(NULL == scratch) {
    failf (data, "Failed to allocate memory for conversion!");
    return FALSE; /* can't return CURLE_OUT_OF_MEMORY so return FALSE */
  }
  if(CURLE_OK != Curl_convert_from_network(data, scratch, strlen(s)+1)) {
    /* Curl_convert_from_network calls failf if unsuccessful */
    free(scratch);
    return FALSE; /* can't return CURLE_foobar so return FALSE */
  }
  s = scratch;
#else
  (void)data; /* unused */
#endif /* CURL_DOES_CONVERSIONS */
  if(checkprefix("RTSP/", s))
    return TRUE;
  else
    return FALSE;
}
#endif /* CURL_DISABLE_RTSP */

static bool
checkprotoprefix(struct SessionHandle *data, struct connectdata *conn,
                 const char *s)
{
#ifndef CURL_DISABLE_RTSP
  if(conn->handler->protocol & CURLPROTO_RTSP)
Yang Tse's avatar
 
Yang Tse committed
#else
  (void)conn;
#endif /* CURL_DISABLE_RTSP */

  return checkhttpprefix(data, s);
}

/*
 * header_append() copies a chunk of data to the end of the already received
 * header. We make sure that the full string fit in the allocated header
 * buffer, or else we enlarge it.
 */
static CURLcode header_append(struct SessionHandle *data,
                              struct SingleRequest *k,
                              size_t length)
{
  if(k->hbuflen + length >= data->state.headersize) {
    /* We enlarge the header buffer as it is too small */
    char *newbuff;
    size_t hbufp_index;
    size_t newsize;

    if(k->hbuflen + length > CURL_MAX_HTTP_HEADER) {
      /* The reason to have a max limit for this is to avoid the risk of a bad
         server feeding libcurl with a never-ending header that will cause
         reallocs infinitely */
      failf (data, "Avoided giant realloc for header (max is %d)!",
             CURL_MAX_HTTP_HEADER);
      return CURLE_OUT_OF_MEMORY;
    }

    newsize=CURLMAX((k->hbuflen+ length)*3/2, data->state.headersize*2);
    hbufp_index = k->hbufp - data->state.headerbuff;
    newbuff = realloc(data->state.headerbuff, newsize);
    if(!newbuff) {
      failf (data, "Failed to alloc memory for big header!");
      return CURLE_OUT_OF_MEMORY;
    }
    data->state.headersize=newsize;
    data->state.headerbuff = newbuff;
    k->hbufp = data->state.headerbuff + hbufp_index;
  }
  memcpy(k->hbufp, k->str_start, length);
  k->hbufp += length;
  k->hbuflen += length;
  *k->hbufp = 0;

  return CURLE_OK;
}


/*
 * Read any HTTP header lines from the server and pass them to the client app.
 */
CURLcode Curl_http_readwrite_headers(struct SessionHandle *data,
                                       struct connectdata *conn,
                                       ssize_t *nread,
                                       bool *stop_reading)

  /* header line within buffer loop */
  do {
    size_t rest_length;
    size_t full_length;
    int writetype;

    /* str_start is start of line within buf */
    k->str_start = k->str;

    /* data is in network encoding so use 0x0a instead of '\n' */
    k->end_ptr = memchr(k->str_start, 0x0a, *nread);

    if(!k->end_ptr) {
      /* Not a complete header line within buffer, append the data to
         the end of the headerbuff. */
      result = header_append(data, k, *nread);
      if(result)
        return result;

      if(!k->headerline && (k->hbuflen>5)) {
        /* make a first check that this looks like a protocol header */
        if(!checkprotoprefix(data, conn, data->state.headerbuff)) {
          /* this is not the beginning of a protocol first header line */
          k->header = FALSE;
          k->badheader = HEADER_ALLBAD;
          break;
        }
      }

      break; /* read more and try again */
    }

    /* decrease the size of the remaining (supposed) header line */
    rest_length = (k->end_ptr - k->str)+1;
    *nread -= (ssize_t)rest_length;

    k->str = k->end_ptr + 1; /* move past new line */

    full_length = k->str - k->str_start;

    result = header_append(data, k, full_length);
    if(result)
      return result;

    k->end_ptr = k->hbufp;
    k->p = data->state.headerbuff;

    /****
     * We now have a FULL header line that p points to
     *****/

    if(!k->headerline) {
      /* the first read header */
      if((k->hbuflen>5) &&
         !checkprotoprefix(data, conn, data->state.headerbuff)) {
        /* this is not the beginning of a protocol first header line */
        k->header = FALSE;
        if(*nread)
          /* since there's more, this is a partial bad header */
          k->badheader = HEADER_PARTHEADER;
        else {
          /* this was all we read so it's all a bad header */
          k->badheader = HEADER_ALLBAD;
          *nread = (ssize_t)rest_length;
        }
        break;
      }
    }

    /* headers are in network encoding so
       use 0x0a and 0x0d instead of '\n' and '\r' */
    if((0x0a == *k->p) || (0x0d == *k->p)) {
      size_t headerlen;
      /* Zero-length header line means end of headers! */

#ifdef CURL_DOES_CONVERSIONS
      if(0x0d == *k->p) {
        *k->p = '\r'; /* replace with CR in host encoding */
        k->p++;       /* pass the CR byte */
      }
      if(0x0a == *k->p) {
        *k->p = '\n'; /* replace with LF in host encoding */
        k->p++;       /* pass the LF byte */
      }
#else
      if('\r' == *k->p)
        k->p++; /* pass the \r byte */
      if('\n' == *k->p)
        k->p++; /* pass the \n byte */
#endif /* CURL_DOES_CONVERSIONS */

      if(100 <= k->httpcode && 199 >= k->httpcode) {
        /*
         * We have made a HTTP PUT or POST and this is 1.1-lingo
         * that tells us that the server is OK with this and ready
         * to receive the data.
         * However, we'll get more headers now so we must get
         * back into the header-parsing state!
         */
        k->header = TRUE;
        k->headerline = 0; /* restart the header line counter */

        /* if we did wait for this do enable write now! */
        if(k->exp100) {
          k->exp100 = EXP100_SEND_DATA;
          k->keepon |= KEEP_SEND;
        }
      }
      else {
        k->header = FALSE; /* no more header to parse! */

        if((k->size == -1) && !k->chunk && !conn->bits.close &&
           (conn->httpversion >= 11) &&
           !(conn->handler->protocol & CURLPROTO_RTSP)) {
          /* On HTTP 1.1, when connection is not to get closed, but no
             Content-Length nor Content-Encoding chunked have been
             received, according to RFC2616 section 4.4 point 5, we
             assume that the server will close the connection to
             signal the end of the document. */
          infof(data, "no chunk, no close, no size. Assume close to "
                "signal end\n");
          conn->bits.close = TRUE;
        }
      }

      /*
       * When all the headers have been parsed, see if we should give
       * up and return an error.
       */
      if(http_should_fail(conn)) {
        failf (data, "The requested URL returned error: %d",
               k->httpcode);
        return CURLE_HTTP_RETURNED_ERROR;
      }

      /* now, only output this if the header AND body are requested:
       */
      writetype = CLIENTWRITE_HEADER;
      if(data->set.include_header)
        writetype |= CLIENTWRITE_BODY;

      headerlen = k->p - data->state.headerbuff;

      result = Curl_client_write(conn, writetype,
                                 data->state.headerbuff,
                                 headerlen);
      if(result)
        return result;

      data->info.header_size += (long)headerlen;
      data->req.headerbytecount += (long)headerlen;

      data->req.deductheadercount =
        (100 <= k->httpcode && 199 >= k->httpcode)?data->req.headerbytecount:0;

      if(!*stop_reading) {
        /* Curl_http_auth_act() checks what authentication methods
         * that are available and decides which one (if any) to
         * use. It will set 'newurl' if an auth method was picked. */
        result = Curl_http_auth_act(conn);

        if(result)
          return result;

        if(k->httpcode >= 300) {
          if((!conn->bits.authneg) && !conn->bits.close &&
             !conn->bits.rewindaftersend) {
            /*
             * General treatment of errors when about to send data. Including :
             * "417 Expectation Failed", while waiting for 100-continue.
             *
             * The check for close above is done simply because of something
             * else has already deemed the connection to get closed then
             * something else should've considered the big picture and we
             * avoid this check.
             *
             * rewindaftersend indicates that something has told libcurl to
             * continue sending even if it gets discarded
             */

            switch(data->set.httpreq) {
            case HTTPREQ_PUT:
            case HTTPREQ_POST:
            case HTTPREQ_POST_FORM:
              /* We got an error response. If this happened before the whole
               * request body has been sent we stop sending and mark the
               * connection for closure after we've read the entire response.
               */
              if(!k->upload_done) {
                infof(data, "HTTP error before end of send, stop sending\n");
                conn->bits.close = TRUE; /* close after this */
                k->upload_done = TRUE;
                k->keepon &= ~KEEP_SEND; /* don't send */
                if(data->state.expect100header)
                  k->exp100 = EXP100_FAILED;
              }
              break;

            default: /* default label present to avoid compiler warnings */
              break;
            }
          }
        }

        if(conn->bits.rewindaftersend) {
          /* We rewind after a complete send, so thus we continue
             sending now */
          infof(data, "Keep sending data to get tossed away!\n");
          k->keepon |= KEEP_SEND;
        }
      }

      if(!k->header) {
        /*
         * really end-of-headers.
         *
         * If we requested a "no body", this is a good time to get
         * out and return home.
         */
        if(data->set.opt_no_body)
          *stop_reading = TRUE;
        else {
          /* If we know the expected size of this document, we set the
             maximum download size to the size of the expected
             document or else, we won't know when to stop reading!

             Note that we set the download maximum even if we read a
             "Connection: close" header, to make sure that
             "Content-Length: 0" still prevents us from attempting to
             read the (missing) response-body.
          */
          /* According to RFC2616 section 4.4, we MUST ignore
             Content-Length: headers if we are now receiving data
             using chunked Transfer-Encoding.
          */
          if(k->chunk)
            k->maxdownload = k->size = -1;
        }
        if(-1 != k->size) {
          /* We do this operation even if no_body is true, since this
             data might be retrieved later with curl_easy_getinfo()
             and its CURLINFO_CONTENT_LENGTH_DOWNLOAD option. */

          Curl_pgrsSetDownloadSize(data, k->size);
          k->maxdownload = k->size;
        }
        /* If max download size is *zero* (nothing) we already
           have nothing and can safely return ok now! */
        if(0 == k->maxdownload)
          *stop_reading = TRUE;

        if(*stop_reading) {
          /* we make sure that this socket isn't read more now */
          k->keepon &= ~KEEP_RECV;
        }

        if(data->set.verbose)
          Curl_debug(data, CURLINFO_HEADER_IN,
                     k->str_start, headerlen, conn);
        break;          /* exit header line loop */
      }

      /* We continue reading headers, so reset the line-based
         header parsing variables hbufp && hbuflen */
      k->hbufp = data->state.headerbuff;
      k->hbuflen = 0;
      continue;
    }

    /*
     * Checks for special headers coming up.
     */

    if(!k->headerline++) {
      /* This is the first header, it MUST be the error code line
         or else we consider this to be the body right away! */
      int httpversion_major;
Yang Tse's avatar
Yang Tse committed
      int nc = 0;
#ifdef CURL_DOES_CONVERSIONS
#define HEADER1 scratch
#define SCRATCHSIZE 21
      CURLcode res;
      char scratch[SCRATCHSIZE+1]; /* "HTTP/major.minor 123" */
      /* We can't really convert this yet because we
         don't know if it's the 1st header line or the body.
         So we do a partial conversion into a scratch area,
         leaving the data at k->p as-is.
      */
      strncpy(&scratch[0], k->p, SCRATCHSIZE);
      scratch[SCRATCHSIZE] = 0; /* null terminate */
      res = Curl_convert_from_network(data,
                                      &scratch[0],
                                      SCRATCHSIZE);
        /* Curl_convert_from_network calls failf if unsuccessful */
        return res;
#else
#define HEADER1 k->p /* no conversion needed, just use k->p */
#endif /* CURL_DOES_CONVERSIONS */

      if(conn->handler->protocol & CURLPROTO_HTTP) {
                    " HTTP/%d.%d %3d",
                    &httpversion_major,