cURL / Mailing Lists / curl-library / Single Mail

curl-library

Re: Re[4]: Working with curl connections as with sockets.

From: tetetest tetetest <tetetest_at_rambler.ru>
Date: Mon, 05 May 2008 15:07:35 +0400

* Daniel Stenberg <daniel_at_haxx.se> [Sun, 4 May 2008 23:18:29 +0200
(CEST)]:

> I disagree. First, send() is separate from select() so mimicing send()
> does not imply select().
>
> Then, select(), poll(), epoll() and similar functions should all still
> be possible to use and thus it would be pointless for us to offer a
> function with the exact same funtion and purpose.
>
> curl_easy_send() and curl_easy_recv() would be provided because they
> cannot by done otherwise.

Ok, I see the point. Agreed.

> >> So, supporting an EAGAIN-style return code will make our functions
> >> more likely to work as drop-in replacements in existing
applications.
> > In essence this means that we add at least one more CURLE_* error
> > code.

> Yes, I can't think of any better way.

Ok, I will do it - this is easy. :)

> > Besides, Curl_write() should be modified to take into account this
> > error: currently it returns CURLE_SEND_ERROR if the socket is
> > not ready.
> Nope it doesn't. It returns 0 bytes written and CURLE_OK.

Ah, I see now... You are right.

> But yes, of course the underlying code would need to do right.

I have checked the usage of Curl_write(), and have come to conclusion
that if we modify it "to do right", the existing code will break.
Currently, libcurl does not handle EAGAIN at all - it simply retries to
send the data until it all goes out or a failure occurs. By the way,
this means that on slow lines, libcurl can take up lots of CPU time
continuously calling send() in a cycle (am I right? Or did I miss some
waits on the socket?).

Handling EAGAIN from Curl_write() will require modifications of the
"core" libcurl code (e.g., lib/transfer.c around line 1537). Not that I
am afraid of hacking the sources, but isn't it too much for such a
simple feature?

So for the time being I simply check that Curl_write() returns CURLE_OK
with zero bytes sent, and assume that it's the symptom of EAGAIN.

Please find attached the patch (5 existing files modified), 2 man pages
(going to docs/libcurl), and one example of usage (going to
docs/examples).

Your comments are welcome.

--
tetetest tetetest.




/*****************************************************************************
 * _ _ ____ _
 * Project ___| | | | _ \| |
 * / __| | | | |_) | |
 * | (__| |_| | _ <| |___
 * \___|\___/|_| \_\_____|
 *
 * An example of curl_easy_send() and curl_easy_recv() usage.
 *
 * $Id$
 */

#include <stdio.h>
#include <string.h>
#include <curl/curl.h>

/* Auxiliary function that waits on the socket. */
int wait_on_socket(int sockfd, int for_recv, long timeout_ms)
{
  // ok, we know what to check.
  // do it.
  struct timeval tv;
  long seconds, usecs;
  fd_set infd, outfd, errfd;
  int res;

  tv.tv_sec = timeout_ms / 1000;
  tv.tv_usec= (timeout_ms % 1000) * 1000;

  FD_ZERO(&infd);
  FD_ZERO(&outfd);
  FD_ZERO(&errfd);
  
  FD_SET(sockfd, &errfd); /* always check for error */

  if(for_recv)
  {
    FD_SET(sockfd, &infd);
  }
  else
  {
    FD_SET(sockfd, &outfd);
  }

  /* select() returns the number of signalled sockets or -1 */
  res = select(sockfd + 1, &infd, &outfd, &errfd, &tv);
  return res;
}

int main(void)
{
  CURL *curl;
  CURLcode res;
  /* Minimalistic http request */
  char *request = "GET / HTTP/1.0\r\nHost: curl.haxx.se\r\n\r\n";
  int sockfd; /* socket */
  size_t iolen;

  curl = curl_easy_init();
  if(curl) {
    curl_easy_setopt(curl, CURLOPT_URL, "curl.haxx.se");
    /* Do not do the transfer - only connect to host */
    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 */
    res = curl_easy_getinfo(curl, CURLINFO_LASTSOCKET, &sockfd);
    
    if(CURLE_OK != res)
    {
      printf("Error: %s\n", strerror(res));
      return 1;
    }
    
    /* 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)
    {
      printf("Error: %s\n", strerror(res));
      return 1;
    }
    puts("Reading response.");
    
    /* read the response */
    for(;;)
    {
      char buf[1024];

      wait_on_socket(sockfd, 1, 60000L);
      res = curl_easy_recv(curl, buf, 1024, &iolen);

      if(CURLE_OK != res)
        break;

      printf("Received %u bytes.\n", iolen);
    }

    /* always cleanup */
    curl_easy_cleanup(curl);
  }
  return 0;
}

Received on 2008-05-05