efl_net_dialer_http: keep fd directly.
authorGustavo Sverzut Barbieri <barbieri@profusion.mobi>
Tue, 30 Aug 2016 02:35:31 +0000 (23:35 -0300)
committerGustavo Sverzut Barbieri <barbieri@profusion.mobi>
Tue, 30 Aug 2016 03:36:00 +0000 (00:36 -0300)
provide curl with CURLOPT_OPENSOCKETFUNCTION and keep the fd in our
private data.

This is required because on _efl_net_dialer_http_efl_io_writer_write()
we may have no fdhandler.

It happened to me while implementing the WebSocket that uses a
bi-directional communication on top of HTTP and the server sent the
whole message, CURL reads:

   recvfrom(7, "...", 16384, 0, NULL, NULL) = 86
   recvfrom(7, "", 16384, 0, NULL, NULL) = 0

After the empty (second) recvfrom(), CURL will remove the fdhandler:

    DBG:ecore_con lib/ecore_con/efl_net_dialer_http.c:482 _efl_net_dialer_http_curlm_socket_manage() dialer=0x4000000040000005 fdhandler=(nil), fd=7, curl_easy=0x5561846ca8d0, flags=0x4

However I should be able to write to this socket, in my case I need to
reply to a PING request with a PONG.

src/lib/ecore_con/ecore_con_url_curl.h
src/lib/ecore_con/efl_net_dialer_http.c

index ff9ad17..98d4a1e 100644 (file)
@@ -40,6 +40,22 @@ typedef int curl_socket_t;
 #define curl_socket_typedef
 #endif /* curl_socket_typedef */
 
+typedef enum  {
+  CURLSOCKTYPE_IPCXN,  /* socket created for a specific IP connection */
+  CURLSOCKTYPE_ACCEPT, /* socket created by accept() call */
+  CURLSOCKTYPE_LAST    /* never use */
+} curlsocktype;
+
+struct curl_sockaddr {
+  int family;
+  int socktype;
+  int protocol;
+  unsigned int addrlen; /* addrlen was a socklen_t type before 7.18.0 but it
+                           turned really ugly and painful on the systems that
+                           lack this type */
+  struct sockaddr addr;
+};
+
 #define CURL_POLL_NONE   0
 #define CURL_POLL_IN     1
 #define CURL_POLL_OUT    2
@@ -243,8 +259,12 @@ typedef enum
    CINIT(INFILESIZE_LARGE, OFF_T, 115),
    CINIT(POSTFIELDSIZE_LARGE, OFF_T, 120),
    CINIT(COOKIELIST, OBJECTPOINT, 135),
+   CINIT(OPENSOCKETFUNCTION, FUNCTIONPOINT, 163),
+   CINIT(OPENSOCKETDATA, OBJECTPOINT, 164),
    CINIT(USERNAME, OBJECTPOINT, 173),
    CINIT(PASSWORD, OBJECTPOINT, 174),
+   CINIT(CLOSESOCKETFUNCTION, FUNCTIONPOINT, 208),
+   CINIT(CLOSESOCKETDATA, OBJECTPOINT, 209),
    CINIT(XFERINFOFUNCTION, FUNCTIONPOINT, 219),
 #define CURLOPT_XFERINFODATA CURLOPT_PROGRESSDATA
 } CURLoption;
index 68477ac..06c5743 100644 (file)
@@ -200,6 +200,7 @@ typedef struct
       Efl_Net_Http_Authentication_Method method;
       Eina_Bool restricted;
    } authentication;
+   int fd;
    Eina_Error error;
    Efl_Net_Http_Version version;
    Efl_Net_Dialer_Http_Primary_Mode primary_mode;
@@ -928,6 +929,37 @@ _efl_net_dialer_http_receive_header(const char *buffer, size_t count, size_t nit
    return len;
 }
 
+static curl_socket_t
+_efl_net_dialer_http_socket_open(void *data, curlsocktype purpose EINA_UNUSED, struct curl_sockaddr *addr)
+{
+   Eo *o = data;
+   Efl_Net_Dialer_Http_Data *pd = efl_data_scope_get(o, MY_CLASS);
+
+   pd->fd = socket(addr->family, addr->socktype, addr->protocol);
+   if (pd->fd < 0)
+     ERR("could not create curl socket family=%d, type=%d, protocol=%d",
+         addr->family, addr->socktype, addr->protocol);
+   else
+     DBG("socket(%d, %d, %d) = %d",
+         addr->family, addr->socktype, addr->protocol, pd->fd);
+
+   return pd->fd;
+}
+
+static void
+_efl_net_dialer_http_socket_close(void *data, curl_socket_t fd)
+{
+   Eo *o = data;
+   Efl_Net_Dialer_Http_Data *pd = efl_data_scope_get(o, MY_CLASS);
+
+   EINA_SAFETY_ON_TRUE_RETURN(pd->fd != fd);
+
+   DBG("close(%d)", fd);
+
+   close(fd);
+   pd->fd = -1;
+}
+
 EOLIAN static Efl_Object *
 _efl_net_dialer_http_efl_object_constructor(Eo *o, Efl_Net_Dialer_Http_Data *pd)
 {
@@ -961,6 +993,12 @@ _efl_net_dialer_http_efl_object_constructor(Eo *o, Efl_Net_Dialer_Http_Data *pd)
    curl_easy_setopt(pd->easy, CURLOPT_READFUNCTION, _efl_net_dialer_http_send_data);
    curl_easy_setopt(pd->easy, CURLOPT_READDATA, o);
 
+   curl_easy_setopt(pd->easy, CURLOPT_OPENSOCKETFUNCTION, _efl_net_dialer_http_socket_open);
+   curl_easy_setopt(pd->easy, CURLOPT_OPENSOCKETDATA, o);
+
+   curl_easy_setopt(pd->easy, CURLOPT_CLOSESOCKETFUNCTION, _efl_net_dialer_http_socket_close);
+   curl_easy_setopt(pd->easy, CURLOPT_CLOSESOCKETDATA, o);
+
    curl_easy_setopt(pd->easy, CURLOPT_NOPROGRESS, 0L);
 
    curl_easy_setopt(pd->easy, CURLOPT_VERBOSE, (long)(eina_log_domain_level_check(_ecore_con_log_dom, EINA_LOG_LEVEL_DBG)));
@@ -1256,14 +1294,13 @@ _efl_net_dialer_http_efl_io_writer_write(Eo *o, Efl_Net_Dialer_Http_Data *pd, Ei
 
    pd->error = 0;
    rm = curl_multi_socket_action(pd->cm->multi,
-                                 ecore_main_fd_handler_fd_get(pd->fdhandler),
+                                 pd->fd,
                                  CURL_CSELECT_OUT, &pd->cm->running);
    if (rm != CURLM_OK)
      {
         err = _curlcode_to_eina_error(rm);
-        ERR("dialer=%p could not trigger socket=%d action: %s",
-            o, ecore_main_fd_handler_fd_get(pd->fdhandler),
-            eina_error_msg_get(err));
+        ERR("dialer=%p could not trigger socket=%d (fdhandler=%p) action: %s",
+            o, pd->fd, pd->fdhandler, eina_error_msg_get(err));
         goto error;
      }
    _efl_net_dialer_http_curlm_check(pd->cm);
@@ -1315,6 +1352,7 @@ _efl_net_dialer_http_efl_io_closer_close(Eo *o, Efl_Net_Dialer_Http_Data *pd)
 
    if (pd->cm)
      {
+        DBG("close dialer=%p, cm=%p, easy=%p", o, pd->cm, pd->easy);
         _efl_net_dialer_http_curlm_remove(pd->cm, o, pd->easy);
         pd->cm = NULL;
      }