Commit 91ff9380 authored by Dan Fandrich's avatar Dan Fandrich
Browse files

Improved the logic the decides whether to use HTTP 1.1 features or not in a

request.

Detect cases where an upload must be sent chunked and the server supports
only HTTP 1.0 and return CURLE_UPLOAD_FAILED.
parent 3acd1146
Loading
Loading
Loading
Loading
+13 −0
Original line number Original line Diff line number Diff line
@@ -6,6 +6,19 @@


                                  Changelog
                                  Changelog


Daniel Fandrich (5 Sep 2008)
- Improved the logic the decides whether to use HTTP 1.1 features or not in a
  request.  Setting a specific version with CURLOPT_HTTP_VERSION overrides
  all other checks, but otherwise, a 1.0 request will be made if the server
  is known to support only 1.0 because it previously responded so and the
  connection was kept alive, or a response to a previous request on this handle
  came back as 1.0. The latter could take place in cases like redirection or
  authentication where several requests have to be made before the operation
  is complete.

- Detect cases where an upload must be sent chunked and the server supports
  only HTTP 1.0 and return CURLE_UPLOAD_FAILED.

Daniel Stenberg (5 Sep 2008)
Daniel Stenberg (5 Sep 2008)
- Martin Drasar provided the CURLOPT_POSTREDIR patch. It renames
- Martin Drasar provided the CURLOPT_POSTREDIR patch. It renames
  CURLOPT_POST301 (but adds a define for backwards compatibility for you who
  CURLOPT_POST301 (but adds a define for backwards compatibility for you who
+1 −0
Original line number Original line Diff line number Diff line
@@ -12,6 +12,7 @@ This release includes the following changes:
 o pkg-config can now show supported_protocols and supported_features
 o pkg-config can now show supported_protocols and supported_features
 o Added CURLOPT_CERTINFO and CURLINFO_CERTINFO
 o Added CURLOPT_CERTINFO and CURLINFO_CERTINFO
 o Added CURLOPT_POSTREDIR
 o Added CURLOPT_POSTREDIR
 o Better detect HTTP 1.0 servers and don't do HTTP 1.1 requests on them


This release includes the following bugfixes:
This release includes the following bugfixes:


+0 −6
Original line number Original line Diff line number Diff line
@@ -4,12 +4,6 @@ To be addressed before 7.19.1 (planned release: October/November 2008)
162 - Craig Perras' note "http upload: how to stop on error"
162 - Craig Perras' note "http upload: how to stop on error"
      http://curl.haxx.se/mail/archive-2008-08/0138.html
      http://curl.haxx.se/mail/archive-2008-08/0138.html


163 - Detecting illegal attempts at chunked transfers on HTTP 1.0
      (tests 1069, 1072, 1073)
      http://curl.haxx.se/mail/archive-2008-08/0435.html

164 - Automatic downgrading to HTTP 1.0 (tests 1071 through 1074)

165 - "Problem with CURLOPT_RESUME_FROM and CURLOPT_APPEND" by Daniele Pinau,
165 - "Problem with CURLOPT_RESUME_FROM and CURLOPT_APPEND" by Daniele Pinau,
      recipe: http://curl.haxx.se/mail/lib-2008-08/0439.html
      recipe: http://curl.haxx.se/mail/lib-2008-08/0439.html


+31 −11
Original line number Original line Diff line number Diff line
@@ -1950,15 +1950,30 @@ CURLcode Curl_http_done(struct connectdata *conn,
  return CURLE_OK;
  return CURLE_OK;
}
}



/* Determine if we should use HTTP 1.1 for this request. Reasons to avoid it
are if the user specifically requested HTTP 1.0, if the server we are
connected to only supports 1.0, or if any server previously contacted to
handle this request only supports 1.0. */
static bool use_http_1_1(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))));
}

/* check and possibly add an Expect: header */
/* check and possibly add an Expect: header */
static CURLcode expect100(struct SessionHandle *data,
static CURLcode expect100(struct SessionHandle *data,
                          struct connectdata *conn,
                          send_buffer *req_buffer)
                          send_buffer *req_buffer)
{
{
  CURLcode result = CURLE_OK;
  CURLcode result = CURLE_OK;
  data->state.expect100header = FALSE; /* default to false unless it is set
  data->state.expect100header = FALSE; /* default to false unless it is set
                                          to TRUE below */
                                          to TRUE below */
  if((data->set.httpversion != CURL_HTTP_VERSION_1_0) &&
  if(use_http_1_1(data, conn) && !checkheaders(data, "Expect:")) {
     !checkheaders(data, "Expect:")) {
    /* if not doing HTTP 1.0 or disabled explicitly, we add a Expect:
    /* if not doing HTTP 1.0 or disabled explicitly, we add a Expect:
       100-continue to the headers which actually speeds up post
       100-continue to the headers which actually speeds up post
       operations (as there is one packet coming back from the web
       operations (as there is one packet coming back from the web
@@ -2139,10 +2154,14 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
  else {
  else {
    if((conn->protocol&PROT_HTTP) &&
    if((conn->protocol&PROT_HTTP) &&
        data->set.upload &&
        data->set.upload &&
        (data->set.infilesize == -1) &&
        (data->set.infilesize == -1)) {
        (data->set.httpversion != CURL_HTTP_VERSION_1_0)) {
      if (use_http_1_1(data, conn)) {
        /* HTTP, upload, unknown file size and not HTTP 1.0 */
        /* HTTP, upload, unknown file size and not HTTP 1.0 */
        data->req.upload_chunky = TRUE;
        data->req.upload_chunky = TRUE;
      } else {
        failf(data, "Chunky upload is not supported by HTTP 1.0");
        return CURLE_UPLOAD_FAILED;
      }
    }
    }
    else {
    else {
      /* else, no chunky upload */
      /* else, no chunky upload */
@@ -2410,8 +2429,9 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
    }
    }
  }
  }


  /* Use 1.1 unless the use specificly asked for 1.0 */
  /* Use 1.1 unless the user specifically asked for 1.0 or the server only
  httpstring= data->set.httpversion==CURL_HTTP_VERSION_1_0?"1.0":"1.1";
     supports 1.0 */
  httpstring= use_http_1_1(data, conn)?"1.1":"1.0";


  /* initialize a dynamic send-buffer */
  /* initialize a dynamic send-buffer */
  req_buffer = add_buffer_init();
  req_buffer = add_buffer_init();
@@ -2635,7 +2655,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
        return result;
        return result;
    }
    }


    result = expect100(data, req_buffer);
    result = expect100(data, conn, req_buffer);
    if(result)
    if(result)
      return result;
      return result;


@@ -2707,7 +2727,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
        return result;
        return result;
    }
    }


    result = expect100(data, req_buffer);
    result = expect100(data, conn, req_buffer);
    if(result)
    if(result)
      return result;
      return result;


@@ -2772,7 +2792,7 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
           sure that the expect100header is always set to the preferred value
           sure that the expect100header is always set to the preferred value
           here. */
           here. */
        if(postsize > TINY_INITIAL_POST_SIZE) {
        if(postsize > TINY_INITIAL_POST_SIZE) {
          result = expect100(data, req_buffer);
          result = expect100(data, conn, req_buffer);
          if(result)
          if(result)
            return result;
            return result;
        }
        }
+16 −11
Original line number Original line Diff line number Diff line
@@ -832,7 +832,7 @@ static CURLcode readwrite_headers(struct SessionHandle *data,
	k->header = FALSE; /* no more header to parse! */
	k->header = FALSE; /* no more header to parse! */


	if((k->size == -1) && !k->chunk && !conn->bits.close &&
	if((k->size == -1) && !k->chunk && !conn->bits.close &&
	   (k->httpversion >= 11) ) {
	   (conn->httpversion >= 11) ) {
	  /* On HTTP 1.1, when connection is not to get closed, but no
	  /* On HTTP 1.1, when connection is not to get closed, but no
	     Content-Length nor Content-Encoding chunked have been
	     Content-Length nor Content-Encoding chunked have been
	     received, according to RFC2616 section 4.4 point 5, we
	     received, according to RFC2616 section 4.4 point 5, we
@@ -1006,17 +1006,17 @@ static CURLcode readwrite_headers(struct SessionHandle *data,
      nc = sscanf(HEADER1,
      nc = sscanf(HEADER1,
		  " HTTP/%d.%d %3d",
		  " HTTP/%d.%d %3d",
		  &httpversion_major,
		  &httpversion_major,
		  &k->httpversion,
		  &conn->httpversion,
		  &k->httpcode);
		  &k->httpcode);
      if(nc==3) {
      if(nc==3) {
	k->httpversion += 10 * httpversion_major;
	conn->httpversion += 10 * httpversion_major;
      }
      }
      else {
      else {
	/* this is the real world, not a Nirvana
	/* this is the real world, not a Nirvana
	   NCSA 1.5.x returns this crap when asked for HTTP/1.1
	   NCSA 1.5.x returns this crap when asked for HTTP/1.1
	*/
	*/
	nc=sscanf(HEADER1, " HTTP %3d", &k->httpcode);
	nc=sscanf(HEADER1, " HTTP %3d", &k->httpcode);
	k->httpversion = 10;
	conn->httpversion = 10;


	/* If user has set option HTTP200ALIASES,
	/* If user has set option HTTP200ALIASES,
	   compare header line against list of aliases
	   compare header line against list of aliases
@@ -1025,14 +1025,18 @@ static CURLcode readwrite_headers(struct SessionHandle *data,
	  if(checkhttpprefix(data, k->p)) {
	  if(checkhttpprefix(data, k->p)) {
	    nc = 1;
	    nc = 1;
	    k->httpcode = 200;
	    k->httpcode = 200;
	    k->httpversion = 10;
	    conn->httpversion = 10;
	  }
	  }
	}
	}
      }
      }


      if(nc) {
      if(nc) {
	data->info.httpcode = k->httpcode;
	data->info.httpcode = k->httpcode;
	data->info.httpversion = k->httpversion;
	data->info.httpversion = conn->httpversion;
        if (!data->state.httpversion ||
            data->state.httpversion > conn->httpversion)
          /* store the lowest server version we encounter */
          data->state.httpversion = conn->httpversion;


	/*
	/*
	 * This code executes as part of processing the header.  As a
	 * This code executes as part of processing the header.  As a
@@ -1060,14 +1064,14 @@ static CURLcode readwrite_headers(struct SessionHandle *data,
	  }
	  }
	}
	}


	if(k->httpversion == 10) {
	if(conn->httpversion == 10) {
	  /* Default action for HTTP/1.0 must be to close, unless
	  /* Default action for HTTP/1.0 must be to close, unless
	     we get one of those fancy headers that tell us the
	     we get one of those fancy headers that tell us the
	     server keeps it open for us! */
	     server keeps it open for us! */
	  infof(data, "HTTP 1.0, assume close after body\n");
	  infof(data, "HTTP 1.0, assume close after body\n");
	  conn->bits.close = TRUE;
	  conn->bits.close = TRUE;
	}
	}
	else if(k->httpversion >= 11 &&
	else if(conn->httpversion >= 11 &&
		!conn->bits.close) {
		!conn->bits.close) {
	  /* If HTTP version is >= 1.1 and connection is persistent
	  /* If HTTP version is >= 1.1 and connection is persistent
	     server supports pipelining. */
	     server supports pipelining. */
@@ -1161,7 +1165,7 @@ static CURLcode readwrite_headers(struct SessionHandle *data,
	data->info.contenttype = contenttype;
	data->info.contenttype = contenttype;
      }
      }
    }
    }
    else if((k->httpversion == 10) &&
    else if((conn->httpversion == 10) &&
	    conn->bits.httpproxy &&
	    conn->bits.httpproxy &&
	    Curl_compareheader(k->p,
	    Curl_compareheader(k->p,
			       "Proxy-Connection:", "keep-alive")) {
			       "Proxy-Connection:", "keep-alive")) {
@@ -1174,7 +1178,7 @@ static CURLcode readwrite_headers(struct SessionHandle *data,
      conn->bits.close = FALSE; /* don't close when done */
      conn->bits.close = FALSE; /* don't close when done */
      infof(data, "HTTP/1.0 proxy connection set to keep alive!\n");
      infof(data, "HTTP/1.0 proxy connection set to keep alive!\n");
    }
    }
    else if((k->httpversion == 11) &&
    else if((conn->httpversion == 11) &&
	    conn->bits.httpproxy &&
	    conn->bits.httpproxy &&
	    Curl_compareheader(k->p,
	    Curl_compareheader(k->p,
			       "Proxy-Connection:", "close")) {
			       "Proxy-Connection:", "close")) {
@@ -1185,7 +1189,7 @@ static CURLcode readwrite_headers(struct SessionHandle *data,
      conn->bits.close = TRUE; /* close when done */
      conn->bits.close = TRUE; /* close when done */
      infof(data, "HTTP/1.1 proxy connection set close!\n");
      infof(data, "HTTP/1.1 proxy connection set close!\n");
    }
    }
    else if((k->httpversion == 10) &&
    else if((conn->httpversion == 10) &&
	    Curl_compareheader(k->p, "Connection:", "keep-alive")) {
	    Curl_compareheader(k->p, "Connection:", "keep-alive")) {
      /*
      /*
       * A HTTP/1.0 reply with the 'Connection: keep-alive' line
       * A HTTP/1.0 reply with the 'Connection: keep-alive' line
@@ -1886,6 +1890,7 @@ CURLcode Curl_pretransfer(struct SessionHandle *data)
  data->set.followlocation=0; /* reset the location-follow counter */
  data->set.followlocation=0; /* reset the location-follow counter */
  data->state.this_is_a_follow = FALSE; /* reset this */
  data->state.this_is_a_follow = FALSE; /* reset this */
  data->state.errorbuf = FALSE; /* no error has occurred */
  data->state.errorbuf = FALSE; /* no error has occurred */
  data->state.httpversion = 0; /* don't assume any particular server version */


  data->state.authproblem = FALSE;
  data->state.authproblem = FALSE;
  data->state.authhost.want = data->set.httpauth;
  data->state.authhost.want = data->set.httpauth;
Loading