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; CURLcode res;
/* Minimalistic http request */ /* Minimalistic http request */
const char *request = "GET / HTTP/1.0\r\nHost: example.com\r\n\r\n"; const char *request = "GET / HTTP/1.0\r\nHost: example.com\r\n\r\n";
curl_socket_t sockfd; /* socket */ size_t request_len = strlen(request);
long sockextr; curl_socket_t sockfd;
size_t iolen; size_t nsent_total = 0;
curl_off_t nread;
/* A general note of caution here: if you're using curl_easy_recv() or /* 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 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); curl_easy_setopt(curl, CURLOPT_CONNECT_ONLY, 1L);
res = curl_easy_perform(curl); res = curl_easy_perform(curl);
if(CURLE_OK != res) { if(res != CURLE_OK) {
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) {
printf("Error: %s\n", curl_easy_strerror(res)); printf("Error: %s\n", curl_easy_strerror(res));
return 1; 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(res != CURLE_OK) {
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) {
printf("Error: %s\n", curl_easy_strerror(res)); printf("Error: %s\n", curl_easy_strerror(res));
return 1; 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(;;) { for(;;) {
/* Warning: This example program may loop indefinitely (see above). */
char buf[1024]; char buf[1024];
size_t nread;
do {
nread = 0;
res = curl_easy_recv(curl, buf, sizeof(buf), &nread);
wait_on_socket(sockfd, 1, 60000L); if(res == CURLE_AGAIN && !wait_on_socket(sockfd, 1, 60000L)) {
res = curl_easy_recv(curl, buf, 1024, &iolen); 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; 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 */ /* 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 \fIcurl_easy_recv(3)\fP does not work on connections that were created without
this option. this option.
You must ensure that the socket has data to read before calling The call will return \fBCURLE_AGAIN\fP if there is no data to read - the
\fIcurl_easy_recv(3)\fP, otherwise the call will return \fBCURLE_AGAIN\fP - socket is used in non-blocking mode internally. When \fBCURLE_AGAIN\fP is
the socket is used in non-blocking mode internally. Use returned, use your operating system facilities like \fIselect(2)\fP to wait
\fIcurl_easy_getinfo(3)\fP with \fICURLINFO_ACTIVESOCKET(3)\fP to obtain the for data. The socket may be obtained using \fIcurl_easy_getinfo(3)\fP with
socket; use your operating system facilities like \fIselect(2)\fP to check if \fICURLINFO_ACTIVESOCKET(3)\fP.
it has any data you can read.
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 .SH AVAILABILITY
Added in 7.18.2. Added in 7.18.2.
.SH RETURN VALUE .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. On failure, returns the appropriate error code.
If there is no data to read, the function returns \fBCURLE_AGAIN\fP. Use your The function may return \fBCURLE_AGAIN\fP. In this case, use your operating
operating system facilities to wait until the data is ready, and retry. 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 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 .SH EXAMPLE
See \fBsendrecv.c\fP in \fBdocs/examples\fP directory for usage example. See \fBsendrecv.c\fP in \fBdocs/examples\fP directory for usage example.
.SH "SEE ALSO" .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. 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 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 \fIcurl_easy_send(3)\fP will not work on connections that were created without
this option. this option.
You must ensure that the socket is writable before calling The call will return \fBCURLE_AGAIN\fP if it's not possible to send data right
\fIcurl_easy_send(3)\fP, otherwise the call will return \fBCURLE_AGAIN\fP - now - the socket is used in non-blocking mode internally. When
the socket is used in non-blocking mode internally. Use \fBCURLE_AGAIN\fP is returned, use your operating system facilities like
\fIcurl_easy_getinfo(3)\fP with \fICURLINFO_ACTIVESOCKET(3)\fP to obtain the \fIselect(2)\fP to wait until the socket is writable. The socket may be
socket; use your operating system facilities like \fIselect(2)\fP to check if obtained using \fIcurl_easy_getinfo(3)\fP with \fICURLINFO_ACTIVESOCKET(3)\fP.
it can be written to.
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 .SH AVAILABILITY
Added in 7.18.2. Added in 7.18.2.
.SH RETURN VALUE .SH RETURN VALUE
@ -59,8 +63,11 @@ wanted to send.
On failure, returns the appropriate error code. 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 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 .SH EXAMPLE
See \fBsendrecv.c\fP in \fBdocs/examples\fP directory for usage example. See \fBsendrecv.c\fP in \fBdocs/examples\fP directory for usage example.
.SH "SEE ALSO" .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. Returns CURLE_OK if the option is supported, and CURLE_UNKNOWN_OPTION if not.
.SH "SEE ALSO" .SH "SEE ALSO"
.BR CURLOPT_VERBOSE "(3), " CURLOPT_HTTPPROXYTUNNEL "(3), " .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> <reply>
<data> <data>
HTTP/1.1 200 OK HTTP/1.1 200 OK swsclose
Server: test-server/fake Server: test-server/fake
Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT
Content-Length: 6 Content-Length: 6

View File

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