- When libcurl was doing a HTTP POST and the server would respond with
"Connection: close" and actually close the connection after the response-body, libcurl could still have outstanding data to send and it would not properly notice this and stop sending. This caused weirdness and sad faces. http://curl.haxx.se/bug/view.cgi?id=2080222 Note that there are still reasons to consider libcurl's behavior when getting a >= 400 response code while sending data, as Craig Perras' note "http upload: how to stop on error" specifies: http://curl.haxx.se/mail/archive-2008-08/0138.html
This commit is contained in:
Родитель
bae4e12302
Коммит
c67a99ff27
12
CHANGES
12
CHANGES
|
@ -6,6 +6,18 @@
|
||||||
|
|
||||||
Changelog
|
Changelog
|
||||||
|
|
||||||
|
Daniel Stenberg (29 Aug 2008)
|
||||||
|
- When libcurl was doing a HTTP POST and the server would respond with
|
||||||
|
"Connection: close" and actually close the connection after the
|
||||||
|
response-body, libcurl could still have outstanding data to send and it
|
||||||
|
would not properly notice this and stop sending. This caused weirdness and
|
||||||
|
sad faces. http://curl.haxx.se/bug/view.cgi?id=2080222
|
||||||
|
|
||||||
|
Note that there are still reasons to consider libcurl's behavior when
|
||||||
|
getting a >= 400 response code while sending data, as Craig Perras' note
|
||||||
|
"http upload: how to stop on error" specifies:
|
||||||
|
http://curl.haxx.se/mail/archive-2008-08/0138.html
|
||||||
|
|
||||||
Daniel Stenberg (28 Aug 2008)
|
Daniel Stenberg (28 Aug 2008)
|
||||||
- Dengminwen reported that libcurl would lock a (cookie) share twice (without
|
- Dengminwen reported that libcurl would lock a (cookie) share twice (without
|
||||||
an unlock in between) for a certain case and that in fact works when using
|
an unlock in between) for a certain case and that in fact works when using
|
||||||
|
|
|
@ -60,6 +60,7 @@ This release includes the following bugfixes:
|
||||||
o proxy closing connect during CONNECT with auth with the multi interface
|
o proxy closing connect during CONNECT with auth with the multi interface
|
||||||
o CURLOPT_UPLOAD sets HTTP method back to GET or HEAD when passed in a 0
|
o CURLOPT_UPLOAD sets HTTP method back to GET or HEAD when passed in a 0
|
||||||
o shared cookies could get locked twice
|
o shared cookies could get locked twice
|
||||||
|
o deal with closed connection while doing POST/PUT
|
||||||
|
|
||||||
This release includes the following known bugs:
|
This release includes the following known bugs:
|
||||||
|
|
||||||
|
|
|
@ -15,8 +15,6 @@ To be addressed before 7.19.1 (planned release: October 2008)
|
||||||
http://curl.haxx.se/mail/archive-2008-08/0075.html
|
http://curl.haxx.se/mail/archive-2008-08/0075.html
|
||||||
|
|
||||||
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 Most possibly this is
|
http://curl.haxx.se/mail/archive-2008-08/0138.html
|
||||||
also closely related to the bug "race condition while POSTing to
|
|
||||||
HTTP/1.0 servers" => http://curl.haxx.se/bug/view.cgi?id=2080222
|
|
||||||
|
|
||||||
163 -
|
163 -
|
||||||
|
|
|
@ -656,6 +656,15 @@ static CURLcode readwrite_data(struct SessionHandle *data,
|
||||||
|
|
||||||
} while(data_pending(conn));
|
} while(data_pending(conn));
|
||||||
|
|
||||||
|
if(((k->keepon & (KEEP_READ|KEEP_WRITE)) == KEEP_WRITE) &&
|
||||||
|
conn->bits.close ) {
|
||||||
|
/* When we've read the entire thing and the close bit is set, the server may
|
||||||
|
now close the connection. If there's now any kind of sending going on from
|
||||||
|
our side, we need to stop that immediately. */
|
||||||
|
infof(data, "we are done reading and this is set to close, stop send\n");
|
||||||
|
k->keepon &= ~KEEP_WRITE; /* no writing anymore either */
|
||||||
|
}
|
||||||
|
|
||||||
return CURLE_OK;
|
return CURLE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -113,12 +113,14 @@ PASVBADIP
|
||||||
- makes PASV send back an illegal IP in its 227 response
|
- makes PASV send back an illegal IP in its 227 response
|
||||||
|
|
||||||
For HTTP/HTTPS:
|
For HTTP/HTTPS:
|
||||||
auth_required - if this is set and a POST/PUT is made without auth, the
|
auth_required if this is set and a POST/PUT is made without auth, the
|
||||||
server will NOT wait for the full request body to get sent
|
server will NOT wait for the full request body to get sent
|
||||||
idle - do nothing after receiving the request, just "sit idle"
|
idle do nothing after receiving the request, just "sit idle"
|
||||||
stream - continuously send data to the client, never-ending
|
stream continuously send data to the client, never-ending
|
||||||
pipe: [num] - tell the server to expect this many HTTP requests before
|
pipe: [num] tell the server to expect this many HTTP requests before
|
||||||
sending back anything, to allow pipelining tests
|
sending back anything, to allow pipelining tests
|
||||||
|
skip: [num] instructs the server to ignore reading this many bytes from a PUT
|
||||||
|
or POST request
|
||||||
</servercmd>
|
</servercmd>
|
||||||
</reply>
|
</reply>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
<testcase>
|
||||||
|
<info>
|
||||||
|
<keywords>
|
||||||
|
HTTP
|
||||||
|
HTTP POST
|
||||||
|
</keywords>
|
||||||
|
</info>
|
||||||
|
#
|
||||||
|
# Server-side
|
||||||
|
<reply>
|
||||||
|
<data>
|
||||||
|
HTTP/1.1 403 Go away and swsclose
|
||||||
|
Server: test-server/fake
|
||||||
|
Content-Type: text/html
|
||||||
|
Content-Length: 55
|
||||||
|
Connection: close
|
||||||
|
|
||||||
|
you are not supposed to be allowed to send things here
|
||||||
|
</data>
|
||||||
|
<servercmd>
|
||||||
|
skip: 2300
|
||||||
|
</servercmd>
|
||||||
|
</reply>
|
||||||
|
|
||||||
|
#
|
||||||
|
# Client-side
|
||||||
|
<client>
|
||||||
|
<server>
|
||||||
|
http
|
||||||
|
</server>
|
||||||
|
<name>
|
||||||
|
HTTP POST with server sending error before (all) data is received
|
||||||
|
</name>
|
||||||
|
<command>
|
||||||
|
-d @log/input1070 http://%HOSTIP:%HTTPPORT/1070
|
||||||
|
</command>
|
||||||
|
<file name="log/input1070">
|
||||||
|
This creates the named file with this content before the test case is run,
|
||||||
|
which is useful if the test case needs a file to act on. We create this file
|
||||||
|
rather large (larger than your typical TCP packet) so that not all of it can nor
|
||||||
|
will be sent in one go as that is kind of the point of this test!
|
||||||
|
|
||||||
|
Here's 2000 x 'O':
|
||||||
|
OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO
|
||||||
|
</file>
|
||||||
|
</client>
|
||||||
|
|
||||||
|
#
|
||||||
|
# Verify data after the test has been "shot"
|
||||||
|
<verify>
|
||||||
|
<strip>
|
||||||
|
^User-Agent:.*
|
||||||
|
</strip>
|
||||||
|
<protocol nonewline="yes">
|
||||||
|
POST /1070 HTTP/1.1
|
||||||
|
Host: %HOSTIP:%HTTPPORT
|
||||||
|
Accept: */*
|
||||||
|
Content-Length: 2313
|
||||||
|
Content-Type: application/x-www-form-urlencoded
|
||||||
|
Expect: 100-continue
|
||||||
|
|
||||||
|
This creates
|
||||||
|
</protocol>
|
||||||
|
</verify>
|
||||||
|
</testcase>
|
|
@ -99,6 +99,10 @@ struct httprequest {
|
||||||
bool ntlm; /* Authorization ntlm header found */
|
bool ntlm; /* Authorization ntlm header found */
|
||||||
int pipe; /* if non-zero, expect this many requests to do a "piped"
|
int pipe; /* if non-zero, expect this many requests to do a "piped"
|
||||||
request/response */
|
request/response */
|
||||||
|
int skip; /* if non-zero, the server is instructed to not read this
|
||||||
|
many bytes from a PUT/POST request. Ie the client sends N
|
||||||
|
bytes said in Content-Length, but the server only reads N
|
||||||
|
- skip bytes. */
|
||||||
int rcmd; /* doing a special command, see defines above */
|
int rcmd; /* doing a special command, see defines above */
|
||||||
int prot_version; /* HTTP version * 10 */
|
int prot_version; /* HTTP version * 10 */
|
||||||
bool pipelining; /* true if request is pipelined */
|
bool pipelining; /* true if request is pipelined */
|
||||||
|
@ -303,6 +307,13 @@ int ProcessRequest(struct httprequest *req)
|
||||||
req->pipe = num-1; /* decrease by one since we don't count the
|
req->pipe = num-1; /* decrease by one since we don't count the
|
||||||
first request in this number */
|
first request in this number */
|
||||||
}
|
}
|
||||||
|
else if(1 == sscanf(cmd, "skip: %d", &num)) {
|
||||||
|
logmsg("instructed to skip this number of bytes %d", num);
|
||||||
|
req->skip = num;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
logmsg("funny instruction found: %s", cmd);
|
||||||
|
}
|
||||||
free(cmd);
|
free(cmd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -351,7 +362,7 @@ int ProcessRequest(struct httprequest *req)
|
||||||
headers, for the pipelining case mostly */
|
headers, for the pipelining case mostly */
|
||||||
req->checkindex += (end - line) + strlen(END_OF_HEADERS);
|
req->checkindex += (end - line) + strlen(END_OF_HEADERS);
|
||||||
|
|
||||||
/* **** Persistency ****
|
/* **** Persistence ****
|
||||||
*
|
*
|
||||||
* If the request is a HTTP/1.0 one, we close the connection unconditionally
|
* If the request is a HTTP/1.0 one, we close the connection unconditionally
|
||||||
* when we're done.
|
* when we're done.
|
||||||
|
@ -363,14 +374,17 @@ int ProcessRequest(struct httprequest *req)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
do {
|
do {
|
||||||
if(!req->cl && curlx_strnequal("Content-Length:", line, 15)) {
|
if((req->cl<=0) && curlx_strnequal("Content-Length:", line, 15)) {
|
||||||
/* If we don't ignore content-length, we read it and we read the whole
|
/* If we don't ignore content-length, we read it and we read the whole
|
||||||
request including the body before we return. If we've been told to
|
request including the body before we return. If we've been told to
|
||||||
ignore the content-length, we will return as soon as all headers
|
ignore the content-length, we will return as soon as all headers
|
||||||
have been received */
|
have been received */
|
||||||
req->cl = strtol(line+15, &line, 10);
|
size_t cl = strtol(line+15, &line, 10);
|
||||||
|
req->cl = cl - req->skip;
|
||||||
|
|
||||||
logmsg("Found Content-Length: %d in the request", req->cl);
|
logmsg("Found Content-Length: %d in the request", cl);
|
||||||
|
if(req->skip)
|
||||||
|
logmsg("... but will abort after %d bytes", req->cl);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
else if(curlx_strnequal("Transfer-Encoding: chunked", line,
|
else if(curlx_strnequal("Transfer-Encoding: chunked", line,
|
||||||
|
@ -457,7 +471,7 @@ int ProcessRequest(struct httprequest *req)
|
||||||
if(req->auth_req && !req->auth)
|
if(req->auth_req && !req->auth)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
if(req->cl) {
|
if(req->cl > 0) {
|
||||||
if(req->cl <= req->offset - (end - req->reqbuf) - strlen(END_OF_HEADERS))
|
if(req->cl <= req->offset - (end - req->reqbuf) - strlen(END_OF_HEADERS))
|
||||||
return 1; /* done */
|
return 1; /* done */
|
||||||
else
|
else
|
||||||
|
@ -552,6 +566,7 @@ static int get_request(curl_socket_t sock, struct httprequest *req)
|
||||||
req->digest = FALSE;
|
req->digest = FALSE;
|
||||||
req->ntlm = FALSE;
|
req->ntlm = FALSE;
|
||||||
req->pipe = 0;
|
req->pipe = 0;
|
||||||
|
req->skip = 0;
|
||||||
req->rcmd = RCMD_NORMALREQ;
|
req->rcmd = RCMD_NORMALREQ;
|
||||||
req->prot_version = 0;
|
req->prot_version = 0;
|
||||||
req->pipelining = FALSE;
|
req->pipelining = FALSE;
|
||||||
|
@ -564,8 +579,15 @@ static int get_request(curl_socket_t sock, struct httprequest *req)
|
||||||
got = pipereq_length;
|
got = pipereq_length;
|
||||||
pipereq_length = 0;
|
pipereq_length = 0;
|
||||||
}
|
}
|
||||||
else
|
else {
|
||||||
got = sread(sock, reqbuf + req->offset, REQBUFSIZ-1 - req->offset);
|
if(req->skip)
|
||||||
|
/* we are instructed to not read the entire thing, so we make sure to only
|
||||||
|
read what we're supposed to and NOT read the enire thing the client
|
||||||
|
wants to send! */
|
||||||
|
got = sread(sock, reqbuf + req->offset, req->cl);
|
||||||
|
else
|
||||||
|
got = sread(sock, reqbuf + req->offset, REQBUFSIZ-1 - req->offset);
|
||||||
|
}
|
||||||
if (got <= 0) {
|
if (got <= 0) {
|
||||||
if (got < 0) {
|
if (got < 0) {
|
||||||
logmsg("recv() returned error: %d", SOCKERRNO);
|
logmsg("recv() returned error: %d", SOCKERRNO);
|
||||||
|
|
Загрузка…
Ссылка в новой задаче