curl_easy_recv: Improve documentation and example program

Follow-up to 82245ea: Fix the example program sendrecv.c (handle
CURLE_AGAIN, handle incomplete send). Improve the documentation
for curl_easy_recv() and curl_easy_send().

Reviewed-by: Frank Meier
Assisted-by: Jay Satiro

See https://github.com/curl/curl/pull/1134
This commit is contained in:
Michael Kaufmann 2016-12-18 12:51:48 +01:00
parent 82245eaa56
commit afff64dbcd
6 changed files with 98 additions and 59 deletions

View File

@ -62,10 +62,9 @@ int main(void)
CURLcode res;
/* Minimalistic http request */
const char *request = "GET / HTTP/1.0\r\nHost: example.com\r\n\r\n";
curl_socket_t sockfd; /* socket */
long sockextr;
size_t iolen;
curl_off_t nread;
size_t request_len = strlen(request);
curl_socket_t sockfd;
size_t nsent_total = 0;
/* A general note of caution here: if you're using curl_easy_recv() or
curl_easy_send() to implement HTTP or _any_ other protocol libcurl
@ -82,54 +81,76 @@ int main(void)
curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 1L);
res = curl_easy_perform(curl);
if(CURLE_OK != res) {
printf("Error: %s\n", strerror(res));
return 1;
}
/* Extract the socket from the curl handle - we'll need it for waiting.
* Note that this API takes a pointer to a 'long' while we use
* curl_socket_t for sockets otherwise.
*/
res = curl_easy_getinfo(curl, CURLINFO_LASTSOCKET, &sockextr);
if(CURLE_OK != res) {
if(res != CURLE_OK) {
printf("Error: %s\n", curl_easy_strerror(res));
return 1;
}
sockfd = (curl_socket_t)sockextr;
/* Extract the socket from the curl handle - we'll need it for waiting. */
res = curl_easy_getinfo(curl, CURLINFO_ACTIVESOCKET, &sockfd);
/* wait for the socket to become ready for sending */
if(!wait_on_socket(sockfd, 0, 60000L)) {
printf("Error: timeout.\n");
return 1;
}
puts("Sending request.");
/* Send the request. Real applications should check the iolen
* to see if all the request has been sent */
res = curl_easy_send(curl, request, strlen(request), &iolen);
if(CURLE_OK != res) {
if(res != CURLE_OK) {
printf("Error: %s\n", curl_easy_strerror(res));
return 1;
}
puts("Reading response.");
/* read the response */
printf("Sending request.\n");
do {
/* Warning: This example program may loop indefinitely.
* A production-quality program must define a timeout and exit this loop
* as soon as the timeout has expired. */
size_t nsent;
do {
nsent = 0;
res = curl_easy_send(curl, request + nsent_total,
request_len - nsent_total, &nsent);
nsent_total += nsent;
if(res == CURLE_AGAIN && !wait_on_socket(sockfd, 0, 60000L)) {
printf("Error: timeout.\n");
return 1;
}
} while(res == CURLE_AGAIN);
if(res != CURLE_OK) {
printf("Error: %s\n", curl_easy_strerror(res));
return 1;
}
printf("Sent %" CURL_FORMAT_CURL_OFF_T " bytes.\n",
(curl_off_t)nsent);
} while(nsent_total < request_len);
printf("Reading response.\n");
for(;;) {
/* Warning: This example program may loop indefinitely (see above). */
char buf[1024];
size_t nread;
do {
nread = 0;
res = curl_easy_recv(curl, buf, sizeof(buf), &nread);
wait_on_socket(sockfd, 1, 60000L);
res = curl_easy_recv(curl, buf, 1024, &iolen);
if(res == CURLE_AGAIN && !wait_on_socket(sockfd, 1, 60000L)) {
printf("Error: timeout.\n");
return 1;
}
} while(res == CURLE_AGAIN);
if(CURLE_OK != res)
if(res != CURLE_OK) {
printf("Error: %s\n", curl_easy_strerror(res));
break;
}
nread = (curl_off_t)iolen;
if(nread == 0) {
/* end of the response */
break;
}
printf("Received %" CURL_FORMAT_CURL_OFF_T " bytes.\n", nread);
printf("Received %" CURL_FORMAT_CURL_OFF_T " bytes.\n",
(curl_off_t)nread);
}
/* always cleanup */

View File

@ -46,12 +46,21 @@ calling \fIcurl_easy_perform(3)\fP or \fIcurl_multi_perform(3)\fP. Note that
\fIcurl_easy_recv(3)\fP does not work on connections that were created without
this option.
You must ensure that the socket has data to read before calling
\fIcurl_easy_recv(3)\fP, otherwise the call will return \fBCURLE_AGAIN\fP -
the socket is used in non-blocking mode internally. Use
\fIcurl_easy_getinfo(3)\fP with \fICURLINFO_ACTIVESOCKET(3)\fP to obtain the
socket; use your operating system facilities like \fIselect(2)\fP to check if
it has any data you can read.
The call will return \fBCURLE_AGAIN\fP if there is no data to read - the
socket is used in non-blocking mode internally. When \fBCURLE_AGAIN\fP is
returned, use your operating system facilities like \fIselect(2)\fP to wait
for data. The socket may be obtained using \fIcurl_easy_getinfo(3)\fP with
\fICURLINFO_ACTIVESOCKET(3)\fP.
Wait on the socket only if \fIcurl_easy_recv(3)\fP returns \fBCURLE_AGAIN\fP.
The reason for this is libcurl or the SSL library may internally cache some
data, therefore you should call \fIcurl_easy_recv(3)\fP until all data is
read which would include any cached data.
Furthermore if you wait on the socket and it tells you there is data to read,
\fIcurl_easy_recv(3)\fP may return \fBCURLE_AGAIN\fP if the only data that was
read was for internal SSL processing, and no other data is available.
.SH AVAILABILITY
Added in 7.18.2.
.SH RETURN VALUE
@ -60,13 +69,13 @@ On success, returns \fBCURLE_OK\fP, stores the received data into
On failure, returns the appropriate error code.
If there is no data to read, the function returns \fBCURLE_AGAIN\fP. Use your
operating system facilities to wait until the data is ready, and retry.
The function may return \fBCURLE_AGAIN\fP. In this case, use your operating
system facilities to wait until data can be read, and retry.
Reading exactly 0 bytes would indicate a closed connection.
Reading exactly 0 bytes indicates a closed connection.
If there's no socket available to use from the previous transfer, this function
returns CURLE_UNSUPPORTED_PROTOCOL.
returns \fBCURLE_UNSUPPORTED_PROTOCOL\fP.
.SH EXAMPLE
See \fBsendrecv.c\fP in \fBdocs/examples\fP directory for usage example.
.SH "SEE ALSO"

View File

@ -40,16 +40,20 @@ connection set-up.
The variable \fBn\fP points to will receive the number of sent bytes.
To establish the connection, set \fICURLOPT_CONNECT_ONLY(3)\fP option before
calling \fIcurl_easy_perform(3)\fP or \fIcurl_multi_perform()\fP. Note that
calling \fIcurl_easy_perform(3)\fP or \fIcurl_multi_perform(3)\fP. Note that
\fIcurl_easy_send(3)\fP will not work on connections that were created without
this option.
You must ensure that the socket is writable before calling
\fIcurl_easy_send(3)\fP, otherwise the call will return \fBCURLE_AGAIN\fP -
the socket is used in non-blocking mode internally. Use
\fIcurl_easy_getinfo(3)\fP with \fICURLINFO_ACTIVESOCKET(3)\fP to obtain the
socket; use your operating system facilities like \fIselect(2)\fP to check if
it can be written to.
The call will return \fBCURLE_AGAIN\fP if it's not possible to send data right
now - the socket is used in non-blocking mode internally. When
\fBCURLE_AGAIN\fP is returned, use your operating system facilities like
\fIselect(2)\fP to wait until the socket is writable. The socket may be
obtained using \fIcurl_easy_getinfo(3)\fP with \fICURLINFO_ACTIVESOCKET(3)\fP.
Furthermore if you wait on the socket and it tells you it's writable,
\fIcurl_easy_send(3)\fP may return \fBCURLE_AGAIN\fP if the only data that was
sent was for internal SSL processing, and no other data could be sent.
.SH AVAILABILITY
Added in 7.18.2.
.SH RETURN VALUE
@ -59,8 +63,11 @@ wanted to send.
On failure, returns the appropriate error code.
This function may return \fBCURLE_AGAIN\fP. In this case, use your operating
system facilities to wait until the socket is writable, and retry.
If there's no socket available to use from the previous transfer, this function
returns CURLE_UNSUPPORTED_PROTOCOL.
returns \fBCURLE_UNSUPPORTED_PROTOCOL\fP.
.SH EXAMPLE
See \fBsendrecv.c\fP in \fBdocs/examples\fP directory for usage example.
.SH "SEE ALSO"

View File

@ -49,3 +49,4 @@ Added in 7.15.2
Returns CURLE_OK if the option is supported, and CURLE_UNKNOWN_OPTION if not.
.SH "SEE ALSO"
.BR CURLOPT_VERBOSE "(3), " CURLOPT_HTTPPROXYTUNNEL "(3), "
.BR curl_easy_recv "(3), " curl_easy_send "(3) "

View File

@ -8,7 +8,7 @@ HTTP GET
<reply>
<data>
HTTP/1.1 200 OK
HTTP/1.1 200 OK swsclose
Server: test-server/fake
Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT
Content-Length: 6

View File

@ -77,11 +77,10 @@ int test(char *URL)
if(!res) {
/* we assume that sending always work */
size_t total=0;
do {
/* busy-read like crazy */
res = curl_easy_recv(curl, buf, 1024, &iolen);
res = curl_easy_recv(curl, buf, sizeof(buf), &iolen);
#ifdef TPF
sleep(1); /* avoid ctl-10 dump */
@ -92,10 +91,12 @@ int test(char *URL)
if(!write(STDOUT_FILENO, buf, iolen))
break;
}
total += iolen;
} while(((res == CURLE_OK) || (res == CURLE_AGAIN)) && (total < 129));
} while((res == CURLE_OK && iolen != 0) || (res == CURLE_AGAIN));
}
if(res != CURLE_OK || iolen != 0)
return TEST_ERR_FAILURE;
}
test_cleanup: