]> granicus.if.org Git - curl/commitdiff
curl_easy_recv: Improve documentation and example program
authorMichael Kaufmann <mail@michael-kaufmann.ch>
Sun, 18 Dec 2016 11:51:48 +0000 (12:51 +0100)
committerMichael Kaufmann <mail@michael-kaufmann.ch>
Sun, 18 Dec 2016 11:56:23 +0000 (12:56 +0100)
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

docs/examples/sendrecv.c
docs/libcurl/curl_easy_recv.3
docs/libcurl/curl_easy_send.3
docs/libcurl/opts/CURLOPT_CONNECT_ONLY.3
tests/data/test556
tests/libtest/lib556.c

index 41e283cdce92c127be6e5b0d1d2ca8b1a884ae73..662323487da2d639ef71de0a777e9f7ddcfc8aef 100644 (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));
+    if(res != CURLE_OK) {
+      printf("Error: %s\n", curl_easy_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);
+    /* Extract the socket from the curl handle - we'll need it for waiting. */
+    res = curl_easy_getinfo(curl, CURLINFO_ACTIVESOCKET, &sockfd);
 
-    if(CURLE_OK != res) {
+    if(res != CURLE_OK) {
       printf("Error: %s\n", curl_easy_strerror(res));
       return 1;
     }
 
-    sockfd = (curl_socket_t)sockextr;
+    printf("Sending request.\n");
 
-    /* wait for the socket to become ready for sending */
-    if(!wait_on_socket(sockfd, 0, 60000L)) {
-      printf("Error: timeout.\n");
-      return 1;
-    }
+    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;
 
-    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(res == CURLE_AGAIN && !wait_on_socket(sockfd, 0, 60000L)) {
+          printf("Error: timeout.\n");
+          return 1;
+        }
+      } while(res == CURLE_AGAIN);
 
-    if(CURLE_OK != res) {
-      printf("Error: %s\n", curl_easy_strerror(res));
-      return 1;
-    }
-    puts("Reading response.");
+      if(res != CURLE_OK) {
+        printf("Error: %s\n", curl_easy_strerror(res));
+        return 1;
+      }
 
-    /* read the response */
-    for(;;) {
-      char buf[1024];
+      printf("Sent %" CURL_FORMAT_CURL_OFF_T " bytes.\n",
+        (curl_off_t)nsent);
+
+    } while(nsent_total < request_len);
 
-      wait_on_socket(sockfd, 1, 60000L);
-      res = curl_easy_recv(curl, buf, 1024, &iolen);
+    printf("Reading response.\n");
 
-      if(CURLE_OK != res)
+    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);
+
+        if(res == CURLE_AGAIN && !wait_on_socket(sockfd, 1, 60000L)) {
+          printf("Error: timeout.\n");
+          return 1;
+        }
+      } while(res == CURLE_AGAIN);
+
+      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 */
index 3560cc511a89f7866e8b3b28dec7bcbeedb8888e..7a812dd004c384541277ef01340f2f5723b909fe 100644 (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"
index 2a744398508092ff5006fe1245f85d35ecc9e4eb..47976f3913d061bcddf197835d39e239f049004b 100644 (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"
index 61289e8d2437bccd5d0259dc173f2d8784faeb2c..973c2aedc99b4a562aa42b2cf2a38fef4a9b1232 100644 (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) "
index 55c7c91c3315ef0b7f51c151a2578851bf2739f5..549b9a4a2cbf6fbdfa60c1815ff4fcdfccc42f30 100644 (file)
@@ -8,7 +8,7 @@ HTTP GET
 
 <reply>
 <data>
-HTTP/1.1 200 OK\r
+HTTP/1.1 200 OK swsclose\r
 Server: test-server/fake\r
 Last-Modified: Tue, 13 Jun 2000 12:10:00 GMT\r
 Content-Length: 6\r
index a26101940bdcebdcf78a1aa51d9fe5643884d9e2..f06c5299c5172112cdd63a5b4074b83003dc8872 100644 (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: