davemds pushed a commit to branch master.

http://git.enlightenment.org/bindings/python/python-efl.git/commit/?id=eaf31100891e631e5d9dd0a713003b6d70178650

commit eaf31100891e631e5d9dd0a713003b6d70178650
Author: Dave Andreoli <d...@gurumeditation.it>
Date:   Mon Nov 16 20:45:07 2015 +0100

    A new ecore module: ecore_con
    
    It's now super easy to perform various network task in a full efl fashion.
    Implemented "Lookup" for dns query and "Url" to perform http requests.
    "Server" will come soon.
    
    Comes with quite complete docs, examples and unit tests
---
 CODING                                 |   1 +
 doc/ecore/class-lookup.rst             |   6 +
 doc/ecore/class-url.rst                |   9 +
 doc/ecore/ecore.rst                    |  24 +-
 doc/ecore/module-ecore_con.rst         |   5 +
 efl/ecore/efl.ecore_events.pxi         |   8 +
 efl/ecore_con/efl.ecore_con.pxd        | 179 +++++++
 efl/ecore_con/efl.ecore_con.pyx        | 315 +++++++++++++
 efl/ecore_con/efl.ecore_con_lookup.pxi |  88 ++++
 efl/ecore_con/efl.ecore_con_url.pxi    | 827 +++++++++++++++++++++++++++++++++
 examples/ecore/con/lookup.py           |  18 +
 examples/ecore/con/url.py              |  31 ++
 include/efl.ecore.pxd                  |   3 +
 setup.py                               |  18 +-
 tests/ecore/test_11_con.py             | 156 +++++++
 15 files changed, 1680 insertions(+), 8 deletions(-)

diff --git a/CODING b/CODING
index f793701..ec9e28e 100644
--- a/CODING
+++ b/CODING
@@ -32,6 +32,7 @@ Documentation cheatsheet
   :func:`elm_list_go`  (for functions)
   :attr:`homogeneous`  (for properties)
   :ref:`Elm_List_Mode` (for enums)
+  :data:`ELM_LIST_LIMIT` (for enum values)
 
   :func:`efl.evas.Object.delete`  (for items not in current scope)
   :func:`~efl.evas.Object.delete` (will show it short, just "delete")
diff --git a/doc/ecore/class-lookup.rst b/doc/ecore/class-lookup.rst
new file mode 100644
index 0000000..a98e4e9
--- /dev/null
+++ b/doc/ecore/class-lookup.rst
@@ -0,0 +1,6 @@
+.. currentmodule:: efl.ecore_con
+
+:class:`efl.ecore_con.Lookup` Class
+===================================
+
+.. autoclass:: efl.ecore_con.Lookup
diff --git a/doc/ecore/class-url.rst b/doc/ecore/class-url.rst
new file mode 100644
index 0000000..c3f735d
--- /dev/null
+++ b/doc/ecore/class-url.rst
@@ -0,0 +1,9 @@
+.. currentmodule:: efl.ecore_con
+
+:class:`efl.ecore_con.Url` Class
+===================================
+
+.. autoclass:: efl.ecore_con.Url
+.. autoclass:: efl.ecore_con.EventUrlComplete
+.. autoclass:: efl.ecore_con.EventUrlProgress
+.. autoclass:: efl.ecore_con.EventUrlData
diff --git a/doc/ecore/ecore.rst b/doc/ecore/ecore.rst
index bf261ce..cfb36c1 100644
--- a/doc/ecore/ecore.rst
+++ b/doc/ecore/ecore.rst
@@ -73,6 +73,16 @@ error. Any valid file descriptor can be used with this API, 
regardless of if
 was gotten with an OS specific API or from ecore.
 see :py:class:`FdHandler<efl.ecore.FdHandler>`
 
+
+File monitor
+------------
+
+Using the :py:class:`FileMonitor<efl.ecore.FileMonitor>` class you can monitor
+a directory for changes, a single calback will be called when events occur.
+Events will be generatd everytime a file or directory (that live in the
+give path) is created/deleted/modified.
+
+
 File download
 -------------
 
@@ -82,13 +92,14 @@ used to inform the user while progress occurs and when the 
download has
 finished.
 
 
-File monitor
-------------
+Ecore Con
+---------
 
-Using the :py:class:`FileMonitor<efl.ecore.FileMonitor>` class you can monitor
-a directory for changes, a single calback will be called when events occur.
-Events will be generatd everytime a file or directory (that live in the
-give path) is created/deleted/modified.
+The ecore_con module provide various utilities to perform different network
+related tasks. Everything provided in a fully async way. Most notable are the
+:class:`efl.ecore_con.Lookup` class to perform DNS requests, the
+:class:`efl.ecore_con.Url` class to perform HTTP requests and the
+:class:`efl.ecore_con.Server` class to implement your own server.
 
 
 
@@ -99,6 +110,7 @@ API Reference
    :titlesonly:
 
    module-ecore
+   module-ecore_con
 
 
 Inheritance diagram
diff --git a/doc/ecore/module-ecore_con.rst b/doc/ecore/module-ecore_con.rst
new file mode 100644
index 0000000..c756d90
--- /dev/null
+++ b/doc/ecore/module-ecore_con.rst
@@ -0,0 +1,5 @@
+
+.. automodule:: efl.ecore_con
+   :exclude-members: Url, EventUrlComplete, EventUrlProgress, EventUrlData,
+                     Lookup, ConEventFilter
+
diff --git a/efl/ecore/efl.ecore_events.pxi b/efl/ecore/efl.ecore_events.pxi
index 880cc7e..d3f739d 100644
--- a/efl/ecore/efl.ecore_events.pxi
+++ b/efl/ecore/efl.ecore_events.pxi
@@ -29,6 +29,11 @@ cdef object _event_mapping_register(int type, cls):
 cdef object _event_mapping_unregister(int type):
     _event_type_mapping.pop(type)
 
+cdef object _event_mapping_get(int type):
+    if not type in _event_type_mapping:
+        raise ValueError("event type '%d' not registered." % type)
+    return _event_type_mapping.get(type)
+
 
 cdef Eina_Bool event_handler_cb(void *data, int type, void *event) with gil:
     cdef EventHandler handler
@@ -65,6 +70,9 @@ cdef class Event(object):
     cdef int _set_obj(self, void *obj) except 0:
         raise NotImplementedError("Event._set_obj() not implemented.")
 
+    cdef object _get_obj(self):
+        raise NotImplementedError("Event._get_obj() not implemented.")
+
 
 cdef class EventHandler(object):
     def __init__(self, int type, func, *args, **kargs):
diff --git a/efl/ecore_con/efl.ecore_con.pxd b/efl/ecore_con/efl.ecore_con.pxd
new file mode 100644
index 0000000..04ec4a6
--- /dev/null
+++ b/efl/ecore_con/efl.ecore_con.pxd
@@ -0,0 +1,179 @@
+# Copyright (C) 2007-2015 various contributors (see AUTHORS)
+#
+# This file is part of Python-EFL.
+#
+# Python-EFL is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# Python-EFL is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this Python-EFL.  If not, see <http://www.gnu.org/licenses/>.
+
+
+from efl.eina cimport *
+from efl.c_eo cimport Eo as cEo
+from efl.eo cimport Eo, object_from_instance
+from efl.ecore cimport Ecore_Event_Handler, Event
+from efl.utils.conversions cimport _ctouni, eina_list_strings_to_python_list
+
+
+
+cdef extern from "Ecore_Con.h":
+
+    # where is this defined in real ?
+    cdef struct sockaddr:
+        pass
+
+    # defines
+    int ECORE_CON_EVENT_URL_COMPLETE
+    int ECORE_CON_EVENT_URL_PROGRESS
+    int ECORE_CON_EVENT_URL_DATA
+
+    # typedefs
+    ctypedef cEo Ecore_Con_Url
+
+    ctypedef struct Ecore_Con_Event_Url_Progress_SubParam:
+        double total
+        double now
+
+    ctypedef struct Ecore_Con_Event_Url_Progress:
+        Ecore_Con_Url *url_con
+        Ecore_Con_Event_Url_Progress_SubParam down
+        Ecore_Con_Event_Url_Progress_SubParam up
+
+    ctypedef struct Ecore_Con_Event_Url_Data:
+        Ecore_Con_Url *url_con
+        int size
+        unsigned char *data
+
+    ctypedef struct Ecore_Con_Event_Url_Complete:
+        Ecore_Con_Url *url_con
+        int status
+
+    ctypedef void (*Ecore_Con_Dns_Cb)(const char *canonname, const char *ip,
+                                      sockaddr *addr, int addrlen, void *data)
+
+    # enums
+    cpdef enum Ecore_Con_Type:
+        ECORE_CON_LOCAL_USER
+        ECORE_CON_LOCAL_SYSTEM
+        ECORE_CON_LOCAL_ABSTRACT
+        ECORE_CON_REMOTE_TCP
+        ECORE_CON_REMOTE_MCAST
+        ECORE_CON_REMOTE_UDP
+        ECORE_CON_REMOTE_BROADCAST
+        ECORE_CON_REMOTE_NODELAY
+        ECORE_CON_REMOTE_CORK
+        ECORE_CON_USE_SSL2
+        ECORE_CON_USE_SSL3
+        ECORE_CON_USE_TLS
+        ECORE_CON_USE_MIXED
+        ECORE_CON_LOAD_CERT
+        ECORE_CON_NO_PROXY
+        ECORE_CON_SOCKET_ACTIVATE
+    ctypedef enum Ecore_Con_Type:
+        pass
+
+    cpdef enum Ecore_Con_Url_Time:
+        ECORE_CON_URL_TIME_NONE
+        ECORE_CON_URL_TIME_IFMODSINCE
+        ECORE_CON_URL_TIME_IFUNMODSINCE
+    ctypedef enum Ecore_Con_Url_Time:
+        pass
+
+    cpdef enum Ecore_Con_Url_Http_Version:
+        ECORE_CON_URL_HTTP_VERSION_1_0
+        ECORE_CON_URL_HTTP_VERSION_1_1
+    ctypedef enum Ecore_Con_Url_Http_Version:
+        pass
+
+    # functions
+    int               ecore_con_init()
+    int               ecore_con_shutdown()
+#     Ecore_Con_Server *ecore_con_server_connect(Ecore_Con_Type type, const 
char *name, int port, const void *data)
+
+    int               ecore_con_url_init()
+    int               ecore_con_url_shutdown()
+    void              ecore_con_url_pipeline_set(Eina_Bool enable)
+    Eina_Bool         ecore_con_url_pipeline_get()
+    Eina_Bool         ecore_con_lookup(const char *name, Ecore_Con_Dns_Cb 
done_cb, const void *data)
+    
+    Ecore_Con_Url    *ecore_con_url_new(const char *url)
+    void              ecore_con_url_free(Ecore_Con_Url *url_obj)
+    Ecore_Con_Url *   ecore_con_url_custom_new(const char *url, const char 
*custom_request)
+    void              ecore_con_url_verbose_set(Ecore_Con_Url *url_con, 
Eina_Bool verbose)
+    Eina_Bool         ecore_con_url_http_version_set(Ecore_Con_Url *url_con, 
Ecore_Con_Url_Http_Version version)
+    void              ecore_con_url_timeout_set(Ecore_Con_Url *url_con, double 
timeout)
+    int               ecore_con_url_status_code_get(Ecore_Con_Url *url_con)
+    Eina_Bool         ecore_con_url_get(Ecore_Con_Url *url_con)
+    Eina_Bool         ecore_con_url_head(Ecore_Con_Url *url_con)
+    Eina_Bool         ecore_con_url_post(Ecore_Con_Url *url_con, const void 
*data, long length, const char *content_type)
+    Eina_Bool         ecore_con_url_ftp_upload(Ecore_Con_Url *url_con, const 
char *filename, const char *user, const char *passwd, const char *upload_dir)
+    void              ecore_con_url_ftp_use_epsv_set(Ecore_Con_Url *url_con, 
Eina_Bool use_epsv)
+
+    Eina_Bool         ecore_con_url_url_set(Ecore_Con_Url *obj, const char 
*url)
+    const char       *ecore_con_url_url_get(const Ecore_Con_Url *obj)
+    void              ecore_con_url_fd_set(Ecore_Con_Url *url_con, int fd)
+
+    void              ecore_con_url_additional_header_add(Ecore_Con_Url 
*url_con, const char *key, const char *value)
+    void              ecore_con_url_additional_headers_clear(Ecore_Con_Url 
*url_con)
+    const Eina_List  *ecore_con_url_response_headers_get(Ecore_Con_Url 
*url_con)
+    int               ecore_con_url_received_bytes_get(Ecore_Con_Url *url_con)
+    Eina_Bool         ecore_con_url_httpauth_set(Ecore_Con_Url *url_con, const 
char *username, const char *password, Eina_Bool safe)
+    void              ecore_con_url_time(Ecore_Con_Url *url_con, 
Ecore_Con_Url_Time time_condition, double timestamp)
+
+    void              ecore_con_url_cookies_init(Ecore_Con_Url *url_con)
+    void              ecore_con_url_cookies_clear(Ecore_Con_Url *url_con)
+    void              ecore_con_url_cookies_session_clear(Ecore_Con_Url 
*url_con)
+    void              
ecore_con_url_cookies_ignore_old_session_set(Ecore_Con_Url *url_con, Eina_Bool 
ignore)
+    void              ecore_con_url_cookies_file_add(Ecore_Con_Url *url_con, 
const char *file_name)
+    Eina_Bool         ecore_con_url_cookies_jar_file_set(Ecore_Con_Url 
*url_con, const char *cookiejar_file)
+    void              ecore_con_url_cookies_jar_write(Ecore_Con_Url *url_con)
+
+    void              ecore_con_url_ssl_verify_peer_set(Ecore_Con_Url 
*url_con, Eina_Bool verify)
+    int               ecore_con_url_ssl_ca_set(Ecore_Con_Url *url_con, const 
char *ca_path)
+
+    Eina_Bool         ecore_con_url_proxy_set(Ecore_Con_Url *url_con, const 
char *proxy)
+    Eina_Bool         ecore_con_url_proxy_username_set(Ecore_Con_Url *url_con, 
const char *username)
+    Eina_Bool         ecore_con_url_proxy_password_set(Ecore_Con_Url *url_con, 
const char *password)
+
+
+cdef class Url(Eo):
+    pass
+
+cdef class Lookup(object):
+    cdef object done_cb
+    cdef tuple args
+    cdef dict kargs
+
+cdef class EventUrlComplete(Event):
+    cdef readonly Url url
+    cdef readonly int status
+
+
+cdef class EventUrlProgress(Event):
+    cdef readonly Url url
+    cdef readonly double down_total
+    cdef readonly double down_now
+    cdef readonly double up_total
+    cdef readonly double up_now
+
+
+cdef class EventUrlData(Event):
+    cdef readonly Url url
+    cdef readonly int size
+    cdef readonly bytes data # TODO :/
+
+
+cdef class ConEventFilter(object):
+    cdef dict callbacks
+    cdef dict handlers
+    cdef callback_add(self, int ev_type, Eo obj, object func, tuple args, dict 
kargs)
+    cdef callback_del(self, int ev_type, Eo obj, object func, tuple args, dict 
kargs)
+    cdef callback_del_full(self, Eo obj)
diff --git a/efl/ecore_con/efl.ecore_con.pyx b/efl/ecore_con/efl.ecore_con.pyx
new file mode 100644
index 0000000..2d2f094
--- /dev/null
+++ b/efl/ecore_con/efl.ecore_con.pyx
@@ -0,0 +1,315 @@
+# Copyright (C) 2007-2015 various contributors (see AUTHORS)
+#
+# This file is part of Python-EFL.
+#
+# Python-EFL is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# Python-EFL is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this Python-EFL.  If not, see <http://www.gnu.org/licenses/>.
+
+"""
+
+:mod:`efl.ecore_con` Module
+###########################
+
+The ecore_con module provide various utilities to perform different network
+related tasks. Everything in a full async ecore way. Most notable are the
+:class:`Lookup` class to perform DNS requests, the :class:`Url` class to
+perform HTTP requests and the :class:`Server` class to implement your own
+server.
+
+Don't forget about the :class:`efl.ecore.FileDownload` class if what you need
+is just to fetch some data to file.
+
+
+Classes
+=======
+
+.. toctree::
+
+   class-lookup.rst
+   class-url.rst
+
+
+
+Enumerations
+============
+
+.. _Ecore_Con_Type:
+
+Ecore Con Type
+--------------
+
+Types for an ecore_con client/server object.
+
+A correct way to set this type is with an ECORE_CON_$TYPE, optionally OR'ed
+with an ECORE_CON_$USE if encryption is desired, and LOAD_CERT if the
+previously loaded certificate should be used.
+
+.. data:: ECORE_CON_LOCAL_USER
+
+    Socket in ~/.ecore.
+
+.. data:: ECORE_CON_LOCAL_SYSTEM
+
+   Socket in /tmp.
+
+.. data:: ECORE_CON_LOCAL_ABSTRACT
+
+    Abstract socket.
+
+.. data:: ECORE_CON_REMOTE_TCP
+
+    Remote server using TCP.
+
+
+.. data:: ECORE_CON_REMOTE_MCAST
+
+    Remote multicast server.
+
+
+.. data:: ECORE_CON_REMOTE_UDP
+
+    Remote server using UDP.
+
+.. data:: ECORE_CON_REMOTE_BROADCAST
+
+    Remote broadcast using UDP.
+
+.. data:: ECORE_CON_REMOTE_NODELAY
+
+    Remote connection sending packets immediately.
+
+.. data:: ECORE_CON_REMOTE_CORK
+
+    Remote connection sending data in large chunks. (only on linux)
+
+.. data:: ECORE_CON_USE_SSL2
+
+    Use SSL2: UNSUPPORTED.
+
+.. data:: ECORE_CON_USE_SSL3
+
+    Use SSL3: UNSUPPORTED.
+
+.. data:: ECORE_CON_USE_TLS
+
+    Use TLS.
+
+.. data:: ECORE_CON_USE_MIXED
+
+    Use both TLS and SSL3.
+
+.. data:: ECORE_CON_LOAD_CERT
+
+    Attempt to use the loaded certificate.
+
+.. data:: ECORE_CON_NO_PROXY
+
+    Disable all types of proxy on the server.
+
+
+.. _Ecore_Con_Url_Time:
+
+Ecore Con Url Time
+------------------
+
+The type of condition to use when making an HTTP request dependent on time, so
+that headers such as "If-Modified-Since" are used.
+
+.. data:: ECORE_CON_URL_TIME_NONE
+
+    Do not place time restrictions on the HTTP requests. 
+
+.. data:: ECORE_CON_URL_TIME_IFMODSINCE
+
+   Add the "If-Modified-Since" HTTP header, so that the request is performed by
+   the server only if the target has been modified since the time value passed
+   to it in the request.
+
+.. data:: ECORE_CON_URL_TIME_IFUNMODSINCE
+
+    Add the "If-Unmodified-Since" HTTP header, so that the request is performed
+    by the server only if the target has NOT been modified since the time value
+    passed to it in the request.
+
+
+.. _Ecore_Con_Url_Http_Version:
+
+Ecore Con Url Http Version
+--------------------------
+
+The http version to use.
+
+.. data:: ECORE_CON_URL_HTTP_VERSION_1_0
+
+    HTTP version 1.0.
+
+.. data:: ECORE_CON_URL_HTTP_VERSION_1_1
+
+   HTTP version 1.1 (default)
+
+
+Module level functions
+======================
+
+"""
+
+from libc.stdint cimport uintptr_t
+from cpython cimport PyUnicode_AsUTF8String, Py_INCREF, Py_DECREF
+
+import traceback
+import atexit
+
+
+from efl.ecore cimport _event_mapping_register, _event_mapping_get, \
+    ecore_event_handler_add, ecore_event_handler_del
+
+
+cdef int _con_events_registered = 0
+
+
+def init():
+    """Initialize the Ecore Con library
+
+    .. note::
+        You never need to call this function, it is automatically called when
+        the module is imported.
+
+    .. versionadded:: 1.17
+
+    """
+
+    ecore_con_init()
+    ecore_con_url_init()
+
+    global _con_events_registered
+    if _con_events_registered == 0:
+        _event_mapping_register(ECORE_CON_EVENT_URL_COMPLETE, EventUrlComplete)
+        _event_mapping_register(ECORE_CON_EVENT_URL_PROGRESS, EventUrlProgress)
+        _event_mapping_register(ECORE_CON_EVENT_URL_DATA, EventUrlData)
+        _con_events_registered = 1
+
+
+def shutdown():
+    """Shuts down the Ecore Con library.
+
+    .. note::
+        You never need to call this function, it is automatically called 
atexit.
+
+    .. versionadded:: 1.17
+
+    """
+    ecore_con_url_shutdown()
+    ecore_con_shutdown()
+
+
+cdef Eina_Bool _con_event_filter_cb(void *data, int ev_type, void *ev) with 
gil:
+    cdef:
+        ConEventFilter filter = <ConEventFilter>data
+        object event_cls
+        Event py_event
+        list cbs
+        Eo obj
+        object func
+        tuple args
+        dict kargs
+
+    # create correct "EventAbc" python object, using the global mapping
+    event_cls = _event_mapping_get(ev_type)
+    if event_cls:
+        py_event = event_cls()
+        py_event._set_obj(ev)
+
+        # do we have callbacks for this object/event ?
+        try:
+            obj = py_event._get_obj()
+            cbs = filter.callbacks.get(ev_type).get(obj)
+        except:
+            cbs = None
+
+        if cbs:
+            cbs = cbs[:] # copy, so we can change filter.callbacks
+            for func, args, kargs in cbs:
+                try:
+                    func(py_event, *args, **kargs)
+                except Exception:
+                    traceback.print_exc()
+
+    return 1 # always return true, no matter what
+
+cdef class ConEventFilter(object):
+
+    """
+    self.callbacks = {
+        EV_TYPE: {
+            objX: [(cb,args,kargs), ... ]
+            objY: [(cb,args,kargs), ... ]
+        },
+        ...
+    }
+    """
+
+    def __cinit__(self):
+        self.callbacks = {}
+        self.handlers = {}
+
+    cdef callback_add(self, int ev_type, Eo obj, object func, tuple args, dict 
kargs):
+        # store the function in the callbacks dict
+        if not ev_type in self.callbacks:
+            self.callbacks[ev_type] = {}
+        if not obj in self.callbacks[ev_type]:
+            self.callbacks[ev_type][obj] = []
+        self.callbacks[ev_type][obj].append((func,args,kargs))
+
+        # connect a single ecore signal, one per event_type
+        cdef Ecore_Event_Handler* ee
+        if not ev_type in self.handlers:
+            ee = ecore_event_handler_add(ev_type, _con_event_filter_cb,
+                                         <void *>self)
+            self.handlers[ev_type] = <uintptr_t><void *>ee
+
+    cdef callback_del(self, int ev_type, Eo obj, object func, tuple args, dict 
kargs):
+        try:
+            self.callbacks[ev_type][obj].remove((func, args, kargs))
+        except ValueError:
+            raise ValueError(
+                "callback is not registered: %s, args=%s, kargs=%s" %
+                (func, args, kargs))
+
+        # can delete the ecore handler?
+        if self.callbacks.get(ev_type) and 
self.callbacks.get(ev_type).get(obj):
+            return
+        if ev_type in self.handlers:
+            handler = self.handlers.pop(ev_type)
+            ecore_event_handler_del(<Ecore_Event_Handler *><uintptr_t>handler)
+
+    cdef callback_del_full(self, Eo obj):
+        for ev_type in self.callbacks:
+            if obj in self.callbacks[ev_type]:
+                # remove all the cbs for the obj
+                self.callbacks[ev_type].pop(obj, None)
+
+                # can delete the ecore handler?
+                if len(self.callbacks[ev_type]) < 1 and ev_type in 
self.handlers:
+                    handler = self.handlers.pop(ev_type)
+                    ecore_event_handler_del(<Ecore_Event_Handler 
*><uintptr_t>handler)
+
+# name suggestions are welcome for this unusual "singleton" instance
+cdef ConEventFilter GEF = ConEventFilter()
+
+
+include "efl.ecore_con_lookup.pxi"
+include "efl.ecore_con_url.pxi"
+
+
+init()
+atexit.register(shutdown)
diff --git a/efl/ecore_con/efl.ecore_con_lookup.pxi 
b/efl/ecore_con/efl.ecore_con_lookup.pxi
new file mode 100644
index 0000000..e002916
--- /dev/null
+++ b/efl/ecore_con/efl.ecore_con_lookup.pxi
@@ -0,0 +1,88 @@
+# Copyright (C) 2007-2015 various contributors (see AUTHORS)
+#
+# This file is part of Python-EFL.
+#
+# Python-EFL is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# Python-EFL is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this Python-EFL.  If not, see <http://www.gnu.org/licenses/>.
+
+
+cdef void _con_dns_lookup_cb(const char *canonname, const char *ip, sockaddr 
*sockaddr, int addrlen, void *data) with gil:
+    cdef Lookup self = <Lookup>data
+
+    try:
+        # TODO read sockaddr and replace the placeholder None with something
+        #      more usefull from the sockaddr struct.
+        self.done_cb(_ctouni(canonname), _ctouni(ip), None, *self.args, 
**self.kargs)
+    except Exception:
+        traceback.print_exc()
+
+    Py_DECREF(self)
+
+
+cdef class Lookup(object):
+    def __init__(self, name, done_cb, *args, **kargs):
+        """Lookup()
+
+        A simple class to perform asynchronous DNS lookups.
+
+        :param string name: The hostname to query
+        :param callable done_cb: The function to call when done
+        :param \*args: Any other arguments will be passed back in ``done_cb``
+        :param \**kargs: Any other keywords arguments will be passed back in 
``done_cb``
+
+        .. versionadded:: 1.17
+
+        Just create an instance and give a callback function to be called
+        when the operation is complete.
+
+        This class performs a DNS lookup on the hostname specified by
+        `name`, then calls `done_cb` with the result and the data given as
+        parameter. The result will be given to the done_cb as follows:
+
+        **expected `done_cb` signature**::
+
+            func(canonname, ip, sockaddr)
+
+        where:
+            * **canonname** (string) is the canonical domain name
+            * **ip** (string) is the recolved ip address
+            * **sockaddr** (None) is a placeholder for future expansion
+
+
+
+        **Usage example**::
+
+            import ecore_con
+
+            def done_cb(canonname, ip, sockaddr):
+                print(canonname)
+                print(ip)
+
+            ecore_con.Lookup('example.com', done_cb)
+
+        
+        """
+
+        if not callable(done_cb):
+            raise TypeError("Parameter 'done_cb' must be callable")
+
+        self.done_cb = done_cb
+        self.args = args
+        self.kargs = kargs
+
+        if isinstance(name, unicode): name = PyUnicode_AsUTF8String(name)
+        ecore_con_lookup(<const char *>name if name is not None else NULL,
+                         _con_dns_lookup_cb, <void*>self)
+
+        Py_INCREF(self)
+
diff --git a/efl/ecore_con/efl.ecore_con_url.pxi 
b/efl/ecore_con/efl.ecore_con_url.pxi
new file mode 100644
index 0000000..a48a6f8
--- /dev/null
+++ b/efl/ecore_con/efl.ecore_con_url.pxi
@@ -0,0 +1,827 @@
+# Copyright (C) 2007-2015 various contributors (see AUTHORS)
+#
+# This file is part of Python-EFL.
+#
+# Python-EFL is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 3 of the License, or (at your option) any later version.
+#
+# Python-EFL is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public License
+# along with this Python-EFL.  If not, see <http://www.gnu.org/licenses/>.
+
+# from efl.ecore cimport EventHandler
+# from efl.utils.conversions cimport _ctouni as _charp_to_str
+
+
+
+cdef class EventUrlProgress(Event):
+    """Represents Ecore_Con_Event_Url_Progress event from C-api.
+
+    This event notifies the progress of the current operation.
+
+    attributes:
+        * url (:class:`Url`): the object that generate the event
+        * down_total(double): total size of the downloading data (in bytes)
+        * down_now(double): current size of the downloading data (in bytes) 
+        * up_total(double): total size of the uploading data (in bytes)
+        * up_now(double): current size of the uploading data (in bytes) 
+
+    """
+    cdef int _set_obj(self, void *ev) except 0:
+        cdef Ecore_Con_Event_Url_Progress *event
+        event = <Ecore_Con_Event_Url_Progress*>ev
+
+        self.url = object_from_instance(event.url_con)
+        self.down_total = event.down.total
+        self.down_now = event.down.now
+        self.up_total = event.up.total
+        self.up_now = event.up.now
+        return 1
+
+    cdef object _get_obj(self):
+        return self.url
+
+cdef class EventUrlComplete(Event):
+    """Represents Ecore_Con_Event_Url_Complete event from C-api.
+
+    This event notifies the operation is completed.
+
+    attributes:
+        * url (:class:`Url`): the object that generate the event
+        * status(int): HTTP status code of the operation (200, 404, 401, etc.)
+
+    """
+    cdef int _set_obj(self, void *ev) except 0:
+        cdef Ecore_Con_Event_Url_Complete *event
+        event = <Ecore_Con_Event_Url_Complete*>ev
+
+        self.url = object_from_instance(event.url_con)
+        self.status = event.status
+        return 1
+
+    cdef object _get_obj(self):
+        return self.url
+
+cdef class EventUrlData(Event):
+    """Represents Ecore_Con_Event_Url_Data event from C-api.
+
+    This event hold the data while the are received.
+
+    .. note::
+        The data attribute is a raw series of bytes, map to ``str`` in python2
+        and ``bytes`` in python3.
+
+    attributes:
+        * url (:class:`Url`): the object that generate the event
+        * size(int): the size of the current received data (in bytes)
+        * data(bytes): the data received on this event
+
+    """
+    cdef int _set_obj(self, void *ev) except 0:
+        cdef Ecore_Con_Event_Url_Data *event
+        event = <Ecore_Con_Event_Url_Data*>ev
+
+        self.url = object_from_instance(event.url_con)
+        self.size = event.size
+        self.data = event.data[:event.size] #raw string copy
+        return 1
+    
+    cdef object _get_obj(self):
+        return self.url
+
+
+cdef class Url(Eo):
+    """
+
+    Utility class to make it easy to perform http requests (POST, GET, etc).
+
+    .. versionadded:: 1.17
+
+    Brief usage:
+        1. Create an :class:`Url` object with ecore_con.Url('myurl')
+        2. Register object callbacks using :func:`on_complete_event_add`,
+           :func:`on_progress_event_add` and :func:`on_data_event_add` to
+           receive the response, e.g. for HTTP/FTP downloads.
+        3. Perform the operation with :func:`get`, :func:`head` and 
:func:`post`
+
+    If it's necessary use the :attr:`url` property. to change the object url. 
+
+    .. note::
+        It is good practice to reuse :class:`Url` objects wherever possible,
+        but bear in mind that each one can only perform one operation at a
+        time. You need to wait for the complete event before re-using or
+        destroying the object.
+
+    .. warning::
+        It is **really important** to call the :func:`delete()` method as soon
+        as you have finished with your object, as it automatically remove all
+        the registered events for you, that will otherwise continue to use
+        resources.
+
+    Basic usage examples::
+
+        # HTTP GET
+        u = ecore.Url("http://www.google.com";)
+        u.get()
+
+        # HTTP POST
+        u = ecore.Url('https://httpbin.org/post')
+        u.post(b'my data to post', 'text/txt')
+
+        # FTP download
+        u = ecore.Url("ftp://ftp.example.com/pub/myfile";)
+        u.get()
+
+        # FTP upload as ftp://ftp.example.com/file
+        u = ecore.Url("ftp://ftp.example.com";)
+        u.ftp_upload("/tmp/file", "user", "pass", None)
+
+        # FTP upload as ftp://ftp.example.com/dir/file
+        u = ecore.Url("ftp://ftp.example.com";)
+        u.ftp_upload("/tmp/file", "user", "pass", "dir")
+
+    To actually make something usefull with your request you will need to
+    connect the :class:`EventUrlComplete`, :class:`EventUrlProgress` and
+    :class:`EventUrlData` events using the :func:`on_complete_event_add` and
+    friends functions.
+
+    A more complete example::
+
+        from efl import ecore
+
+        def on_data(event):
+            print("data: " + str(event.data[:80]))
+            # do something here with the received data
+
+        def on_progress(event):
+            # print(event)
+            print("received %d on a total of %d bytes" % (
+                   event.down_now, event.down_total))
+
+        def on_complete(event):
+            # print(event)
+            print("http result: %d" % event.status)
+            print("Total received bytes: %d" % event.url.received_bytes)
+
+            u.delete() # don't forget to delete !!
+
+        u = ecore.Url('http://www.google.com', verbose=False)
+        u.on_data_event_add(on_data)
+        u.on_progress_event_add(on_progress)
+        u.on_complete_event_add(on_complete)
+        u.get()
+
+        ecore.main_loop_begin()
+
+    If you need  to save the received data to a file use the :attr:`fd`
+    property, as::
+
+        fd = open('/tmp/tmpMxBtta', 'w')
+        u = ecore.Url('http://example.com', fd=fd.fileno())
+        u.get()
+
+    .. seealso::
+        If you just need to download a file please consider using the
+        simpler :class:`efl.ecore.FileDownload` class instead.
+
+    .. seealso::
+        The ecore module level functions :func:`url_pipeline_set` and
+        :func:`url_pipeline_get` to enable HTTP 1.1 pipelining.
+
+    """
+    def __init__(self, url, custom_request=None, **kargs):
+        """Url(...)
+
+        :param url: URL that will receive requests.
+        :type url: string
+        :param custom_request: Custom request (e.g. GET, POST, HEAD, PUT, HEAD,
+                               SUBSCRIBE and other obscure HTTP requests)
+        :type custom_request: string
+        :param \**kwargs: All the remaining keyword arguments are interpreted
+                          as properties of the instance
+
+        .. versionadded:: 1.17
+
+        """
+        if isinstance(url, unicode): url = PyUnicode_AsUTF8String(url)
+        if custom_request is None:
+            self._set_obj(ecore_con_url_new(
+                <const char *>url if url is not None else NULL))
+        else:
+            if isinstance(custom_request, unicode):
+                custom_request = PyUnicode_AsUTF8String(custom_request)
+            self._set_obj(ecore_con_url_custom_new(
+                <const char *>url if url is not None else NULL,
+                <const char *>custom_request if custom_request is not None 
else NULL))
+    
+        self._set_properties_from_keyword_args(kargs)
+
+    def __repr__(self):
+        return "%s(url=%s)" % (self.__class__.__name__, self.url)
+
+    def delete(self):
+        """Delete the :class:`Url` object and free all used resources.
+
+        .. note::
+            This is **really important** to call as soon as you have finished
+            with your object, as it automatically remove all the registered
+            events. That will otherwise continue to use resources.
+
+        """
+        GEF.callback_del_full(self)
+        ecore_con_url_free(self.obj)
+
+    property fd:
+        """Set up a file to have response data written into.
+
+        This attr can be used to easily setup a file where the downloaded data
+        will be saved.
+
+        Note that :class:`EventUrlData` events will not be emitted if a file
+        has been set to receive the response data.
+
+        .. seealso::
+            If you just need to download a file please consider using the
+            simpler :class:`efl.ecore.FileDownload` class instead.
+
+        :type: int (**writeonly**)
+
+        """
+        def __set__(self, int fd):
+            ecore_con_url_fd_set(self.obj, fd)
+
+    def get(self):
+        """Send a GET request.
+
+        The request is performed immediately, but you need to setup event
+        handlers with :func:`on_complete_event_add` or
+        :func:`on_complete_event_add` to get more information about its result.
+
+        :return: ``True`` on success, ``False`` on error.
+        
+        """
+        return bool(ecore_con_url_get(self.obj))
+
+    def head(self):
+        """Send a HEAD request.
+
+        The request is performed immediately, but you need to setup event
+        handlers with :func:`on_complete_event_add` or
+        :func:`on_complete_event_add` to get more information about its result.
+
+        :return: ``True`` on success, ``False`` on error.
+        
+        """
+        return bool(ecore_con_url_head(self.obj))
+
+    def post(self, bytes data, content_type):
+        """Send a post request.
+        
+        The request is performed immediately, but you need to setup event
+        handlers with :func:`on_complete_event_add` or
+        :func:`on_complete_event_add` to get more information about its result.
+
+        :param data: Payload (data sent on the request). Can be ``None``.
+        :type data: bytes
+        :param content_type: Content type of the payload (e.g. `text/xml`).
+                             Can be ``None``.
+        :type content_type: string
+
+        :return: ``True`` on success, ``False`` on error.
+
+        """
+        if isinstance(content_type, unicode):
+            content_type = PyUnicode_AsUTF8String(content_type)
+        return bool(ecore_con_url_post(self.obj,
+            <const void*><const char *>data if data is not None else NULL,
+            len(data),
+            <const char *>content_type if content_type is not None else NULL))
+
+    def time(self, Ecore_Con_Url_Time time_condition, double timestamp):
+        """Whether HTTP requests should be conditional, dependent on
+        modification time.
+
+        This function may set the header `If-Modified-Since` or
+        `If-Unmodified-Since`, depending on the value of time_condition, with
+        the value timestamp.
+
+        :param time_condition: Condition to use for HTTP requests.
+        :type time_condition: :ref:`Ecore_Con_Url_Time`
+        :param timestamp: Time since 1 Jan 1970 to use in the condition.
+        :type timestamp: double
+
+        """
+        ecore_con_url_time(self.obj, time_condition, timestamp)
+
+    def ftp_upload(self, filename, user, passwd, upload_dir):
+        """Upload a file to an ftp site.
+
+        :param string filename: The path to the file to send
+        :param string user: The username to log in with 
+        :param string passwd: The password to log in with 
+        :param string upload_dir: The directory to which the file will upload
+
+        :return: ``True`` on success, ``False`` otherwise.
+        :rtype: bool
+
+        """
+        if isinstance(filename, unicode): filename = 
PyUnicode_AsUTF8String(filename)
+        if isinstance(user, unicode): user = PyUnicode_AsUTF8String(user)
+        if isinstance(passwd, unicode): passwd = PyUnicode_AsUTF8String(passwd)
+        if isinstance(upload_dir, unicode): upload_dir = 
PyUnicode_AsUTF8String(upload_dir)
+        return bool(ecore_con_url_ftp_upload(self.obj,
+                <const char *>filename if filename is not None else NULL,
+                <const char *>user if user is not None else NULL,
+                <const char *>passwd if passwd is not None else NULL,
+                <const char *>upload_dir if upload_dir is not None else NULL))
+
+    property ftp_use_epsv:
+        """Enable or disable EPSV extension.
+
+        :type: bool (**writeonly**)
+
+        """
+        def __set__(self, bint use_epsv):
+            ecore_con_url_ftp_use_epsv_set(self.obj, use_epsv)
+
+    def cookies_init(self):
+        """Enable the cookie engine for subsequent HTTP requests. 
+
+        After this function is called, cookies set by the server in HTTP
+        responses will be parsed and stored, as well as sent back to the server
+        in new HTTP requests.
+
+        """
+        ecore_con_url_cookies_init(self.obj)
+
+    def cookies_clear(self):
+        """Clear currently loaded cookies.
+        
+        The cleared cookies are removed and will not be sent in subsequent HTTP
+        requests, nor will they be written to the cookiejar file set via
+        :attr:`cookies_jar_file`.
+
+        .. note::
+            This function will initialize the cookie engine if it has not been
+            initialized yet. The cookie files set by
+            :func:`cookies_file_add` aren't loaded immediately, just
+            when the request is started. Thus, if you ask to clear the cookies,
+            but has a file already set by that function, the cookies will then
+            be loaded and you will have old cookies set. In order to don't have
+            any old cookie set, you need to don't call
+            :func:`cookies_file_add` ever on the :class:`Url` class, and
+            call this function to clear any cookie set by a previous request on
+            this handler.
+
+        """
+        ecore_con_url_cookies_clear(self.obj)
+
+    def cookies_session_clear(self):
+        """Clear currently loaded session cookies.
+        
+        Session cookies are cookies with no expire date set, which usually
+        means they are removed after the current session is closed.
+
+        The cleared cookies are removed and will not be sent in subsequent HTTP
+        requests, nor will they be written to the cookiejar file set via
+        :attr:`cookies_jar_file`.
+
+        .. note::
+            This function will initialize the cookie engine if it has not been
+            initialized yet. The cookie files set by
+            :func:`cookies_file_add` aren't loaded immediately, just
+            when the request is started. Thus, if you ask to clear the session
+            cookies, but has a file already set by that function, the session
+            cookies will then be loaded and you will have old cookies set. In
+            order to don't have any old session cookie set, you need to don't
+            call :func:`cookies_file_add` ever on the :class:`Url` class, and
+            call this function to clear any session cookie set by a previous
+            request on this handler. An easier way to don't use old session
+            cookies is by using the function
+            :attr:`cookies_ignore_old_session`.
+
+        """
+        ecore_con_url_cookies_session_clear(self.obj)
+
+    def cookies_file_add(self, file_name):
+        """Add a file to the list of files from which to load cookies.
+
+        Files must contain cookies defined according to two possible formats:
+            * HTTP-style header ("Set-Cookie: ...").
+            * Netscape/Mozilla cookie data format.
+
+        Cookies will only be read from this file. If you want to save cookies
+        to a file, use :attr:`cookies_jar_file`. Also notice that
+        this function supports the both types of cookie file cited above, while
+        :attr:`cookies_jar_file` will save only in the Netscape/Mozilla's
+        format.
+
+        Please notice that the file will not be read immediately, but rather
+        added to a list of files that will be loaded and parsed at a later
+        time.
+
+        .. note::
+            This function will initialize the cookie engine if it has not been
+            initialized yet.
+
+        :param string file_name: Name of the file that will be added to the 
list.
+
+        """
+        if isinstance(file_name, unicode):
+            file_name = PyUnicode_AsUTF8String(file_name)
+        ecore_con_url_cookies_file_add(self.obj,
+                    <const char *>file_name if file_name is not None else NULL)
+
+    property cookies_jar_file:
+        """The name of the file to which all current cookies will be written
+        when either cookies are flushed or Ecore_Con is shut down.
+
+        Cookies are written following Netscape/Mozilla's data format, also
+        known as cookie-jar.
+
+        Cookies will only be saved to this file. If you need to read cookies
+        from a file, use ecore_con_url_cookies_file_add() instead.
+
+        .. note::
+            This function will initialize the cookie engine if it has not been
+            initialized yet.
+
+        .. seealso:: :func:`cookies_jar_write`
+
+        :type: string (**writeonly**)
+
+        """
+        def __set__(self, cookiejar_file):
+            if isinstance(cookiejar_file, unicode):
+                cookiejar_file = PyUnicode_AsUTF8String(cookiejar_file)
+            ecore_con_url_cookies_jar_file_set(self.obj,
+                <const char *>cookiejar_file if cookiejar_file is not None 
else NULL)
+            if isinstance(cookiejar_file, unicode):
+                cookiejar_file = PyUnicode_AsUTF8String(cookiejar_file)
+            ecore_con_url_cookies_jar_file_set(self.obj,
+                <const char *>cookiejar_file if cookiejar_file is not None 
else NULL)
+
+    def cookies_jar_write(self):
+        """Write all current cookies to the cookie jar immediately.
+        
+        A cookie-jar file must have been previously set by
+        :attr:`cookies_jar_file`, otherwise nothing will be done.
+
+        .. note::
+            This function will initialize the cookie engine if it has not been
+            initialized yet.
+
+        .. seealso:: :attr:`cookies_jar_file`
+
+        """
+        ecore_con_url_cookies_jar_write(self.obj)
+    
+    property cookies_ignore_old_session:
+        """Control whether session cookies from previous sessions shall be 
loaded. 
+
+        Session cookies are cookies with no expire date set, which usually
+        means they are removed after the current session is closed.
+
+        By default, when Ecore_Con_Url loads cookies from a file, all cookies
+        are loaded, including session cookies, which, most of the time, were
+        supposed to be loaded and valid only for that session.
+
+        If ignore is set to ``True``, when Ecore_Con_Url loads cookies from
+        the files passed to :func:`cookies_file_add`, session cookies
+        will not be loaded.
+
+        :type: bool (**writeonly**)
+
+        """
+        def __set__(self, bint ignore):
+            ecore_con_url_cookies_ignore_old_session_set(self.obj, ignore)
+
+    property ssl_verify_peer:
+        """Toggle libcurl's verify peer's certificate option. 
+
+        If this is ``True``, libcurl will verify the authenticity of the
+        peer's certificate, otherwise it will not. Default behavior of libcurl
+        is to check peer's certificate.
+
+        :type: bool (**writeonly**)
+
+        """
+        def __set__(self, bint verify):
+            ecore_con_url_ssl_verify_peer_set(self.obj, verify)
+
+    property ssl_ca:
+        """Set a custom CA to trust for SSL/TLS connections. 
+
+        Specify the path of a file (in PEM format) containing one or more CA
+        certificate(s) to use for the validation of the server certificate.
+
+        This can also disable CA validation if set to ``None``.
+        However, the server certificate still needs to be valid for the
+        connection to succeed (i.e., the certificate must concern the server
+        the connection is made to).
+
+        :type: string (**writeonly**)
+
+        """
+        def __set__(self, ca_path):
+            if isinstance(ca_path, unicode):
+                ca_path = PyUnicode_AsUTF8String(ca_path)
+            ecore_con_url_ssl_ca_set(self.obj,
+                <const char *>ca_path if ca_path is not None else NULL)
+
+    property proxy:
+        """Set the HTTP proxy to use.
+
+        The parameter is the host name or dotted IP address. To specify port
+        number in this string, append :[port] to the end of the host name. The
+        proxy string may be prefixed with [protocol]:// since any such prefix
+        will be ignored. The proxy's port number may optionally be specified
+        with the separate option. If not specified, libcurl will default to
+        using port 1080 for proxies.
+
+        Set this to ``None`` to disable the usage of proxy.
+
+        :type: string (**writeonly**)
+
+        """
+        def __set__(self, proxy):
+            if isinstance(proxy, unicode): proxy = 
PyUnicode_AsUTF8String(proxy)
+            ecore_con_url_proxy_set(self.obj,
+                            <const char *>proxy if proxy is not None else NULL)
+
+    property proxy_username:
+        """Username to use for proxy.
+
+        If socks protocol is used for proxy, protocol should be socks5 and
+        above.
+        
+        :type: string (**writeonly**)
+
+        """
+        def __set__(self, user):
+            if isinstance(user, unicode): user = PyUnicode_AsUTF8String(user)
+            ecore_con_url_proxy_username_set(self.obj,
+                            <const char *>user if user is not None else NULL)
+
+    property proxy_password:
+        """Password to use for proxy. 
+
+        If socks protocol is used for proxy, protocol should be socks5 and
+        above.
+
+        :type: string (**writeonly**)
+
+        """
+        def __set__(self, passwd):
+            if isinstance(passwd, unicode): passwd = 
PyUnicode_AsUTF8String(passwd)
+            ecore_con_url_proxy_username_set(self.obj,
+                            <const char *>passwd if passwd is not None else 
NULL)
+
+    property timeout:
+        """transfer timeout in seconds.
+
+        The maximum time in seconds that you allow the :class:`Url` class
+        transfer operation to take. Normally, name lookups can take a
+        considerable time and limiting operations to less than a few minutes
+        risk aborting perfectly normal operations.
+
+        :type: double (**writeonly**)
+
+        """
+        def __set__(self, double timeout):
+            ecore_con_url_timeout_set(self.obj, timeout)
+
+    property http_version:
+        """The HTTP version used for the request.
+
+        Can be :data:`ECORE_CON_URL_HTTP_VERSION_1_0` or
+        :data:`ECORE_CON_URL_HTTP_VERSION_1_1`
+        
+        :type: :ref:`Ecore_Con_Url_Http_Version` (**writeonly**)
+
+        """
+        def __set__(self, Ecore_Con_Url_Http_Version version):
+            ecore_con_url_http_version_set(self.obj, version)
+
+    property status_code:
+        """The returned HTTP STATUS code. 
+
+        This is used to, at any time, try to return the status code for a
+        transmission.
+
+        :type: int  (**readonly**)
+
+        """
+        def __get__(self):
+            return ecore_con_url_status_code_get(self.obj)
+
+    property url:
+        """Controls the URL to send the request to.
+
+        :type: string
+
+        """
+        def __get__(self):
+            return _ctouni(ecore_con_url_url_get(self.obj))
+
+        def __set__(self, url):
+            if isinstance(url, unicode): url = PyUnicode_AsUTF8String(url)
+            ecore_con_url_url_set(self.obj, <const char *>url if url is not 
None else NULL)
+
+    property verbose:
+        """Toggle libcurl's verbose output. 
+
+        If set to ``True``, libcurl will output a lot of verbose
+        information about its operations, which is useful for debugging. The
+        verbose information will be sent to stderr.
+
+        :type: bool (**writeonly**)
+
+        """
+        def __set__(self, bint verbose):
+            ecore_con_url_verbose_set(self.obj, verbose)
+
+    def additional_header_add(self, key, value):
+        """Add an additional header to the request connection object. 
+
+        Add an additional header (User-Agent, Content-Type, etc.) to the
+        request connection object. This addition will be valid for only one
+        :func:`get` or :func:`post` call.
+
+        :param string key: Header key 
+        :param string value: Header value
+
+        Some functions like :func:`time` also add headers to the request. 
+        
+        """
+        if isinstance(key, unicode): key = PyUnicode_AsUTF8String(key)
+        if isinstance(value, unicode): value = PyUnicode_AsUTF8String(value)
+        ecore_con_url_additional_header_add(self.obj,
+                           <const char *>key if key is not None else NULL,
+                           <const char *>value if value is not None else NULL)
+
+    def additional_headers_clear(self):
+        """Clean additional headers.
+        
+        Clean additional headers associated with a connection object
+        (previously added with :func:additional_header_add`).
+
+        """
+        ecore_con_url_additional_headers_clear(self.obj)
+
+    property response_headers:
+        """The headers from last request sent. 
+
+        Retrieve a list containing the response headers. This function should
+        be used after an :class:`EventUrlComplete` event (headers should
+        normally be ready at that time).
+
+        :type: list of strings (**readonly**)
+
+        """
+        def __get__(self):
+            return eina_list_strings_to_python_list(
+                        ecore_con_url_response_headers_get(self.obj))
+
+    property received_bytes:
+        """The number of bytes received. 
+
+        Retrieve the number of bytes received on the last request of the
+        :class:`Url` object.
+
+        :type: int (**readonly**)
+
+        """
+        def __get__(self):
+            return ecore_con_url_received_bytes_get(self.obj)
+
+    def httpauth_set(self, username, password, bint safe):
+        """Set to use http auth, with given username and password
+        
+        :param string username: Username to use in authentication 
+        :param string password: Password to use in authentication
+        :param bool safe: Whether to use "safer" methods (eg, NOT http basic 
auth)
+
+        :return: ``True`` on success, ``False`` on error.
+        :rtype: bool
+
+        .. warning:: Require libcurl >= 7.19.1 to work, otherwise will
+                     always return ``False``.
+        
+        """
+        if isinstance(username, unicode):
+            username = PyUnicode_AsUTF8String(username)
+        if isinstance(password, unicode):
+            password = PyUnicode_AsUTF8String(password)
+        return bool(ecore_con_url_httpauth_set(self.obj,
+                    <const char *>username if username is not None else NULL,
+                    <const char *>password if password is not None else NULL,
+                    safe))
+
+    def on_complete_event_add(self, func, *args, **kargs):
+        """Adds event listener to know when the Url operation is completed.
+
+        The given function will be called with the following signature::
+
+            func(event, *args, **kargs)
+
+        The ``event`` parameter is an :class:`EventUrlComplete` instance.
+
+        :see: :func:`on_complete_event_del`
+
+        """
+        GEF.callback_add(ECORE_CON_EVENT_URL_COMPLETE, self, func, args, kargs)
+
+    def on_complete_event_del(self, func, *args, **kargs):
+        """Removes an event listener previously registered 
+
+        Parameters must match exactly the ones given in the
+        :func:`on_complete_event_add` call
+
+        :raise ValueError: if parameters don't match an already
+                           registered callback.
+        """
+        GEF.callback_del(ECORE_CON_EVENT_URL_COMPLETE, self, func, args, kargs)
+        
+    def on_progress_event_add(self, func, *args, **kargs):
+        """Adds event listener to know the operation status progress.
+
+        The given function will be called with the following signature::
+
+            func(event, *args, **kargs)
+
+        The ``event`` parameter is an :class:`EventUrlProgress` instance.
+
+        :see: :func:`on_progress_event_del`
+
+        """
+        GEF.callback_add(ECORE_CON_EVENT_URL_PROGRESS, self, func, args, kargs)
+
+    def on_progress_event_del(self, func, *args, **kargs):
+        """Removes an event listener previously registered 
+
+        Parameters must match exactly the ones given in the
+        :func:`on_progress_event_add` call
+
+        :raise ValueError: if parameters don't match an already
+                           registered callback.
+        """
+        GEF.callback_del(ECORE_CON_EVENT_URL_PROGRESS, self, func, args, kargs)
+        
+    def on_data_event_add(self, func, *args, **kargs):
+        """Adds event listener to collect the data while they are received.
+
+        The given function will be called with the following signature::
+
+            func(event, *args, **kargs)
+
+        The ``event`` parameter is an :class:`EventUrlData` instance.
+
+        :see: :func:`on_data_event_del`
+
+        """
+        GEF.callback_add(ECORE_CON_EVENT_URL_DATA, self, func, args, kargs)
+
+    def on_data_event_del(self, func, *args, **kargs):
+        """Removes an event listener previously registered 
+
+        Parameters must match exactly the ones given in the
+        :func:`on_data_event_add` call
+
+        :raise ValueError: if parameters don't match an already
+                           registered callback.
+        """
+        GEF.callback_del(ECORE_CON_EVENT_URL_DATA, self, func, args, kargs)
+
+
+def url_pipeline_set(bint enable):
+    """Enable or disable HTTP 1.1 pipelining.
+
+    Pipelining allows to send one request after another one, without having to
+    wait for the reply of the first request. The respective replies are
+    received in the order that the requests were sent.
+
+    .. warning:: Enabling this feature will be valid for all requests done
+                 using ecore_con_url.
+
+    .. versionadded:: 1.17
+
+    """
+    ecore_con_url_pipeline_set(enable)
+
+def url_pipeline_get():
+    """Is HTTP 1.1 pipelining enable ? 
+
+    :return: ``True`` if enable
+
+    .. versionadded:: 1.17
+
+    """
+    return bool(ecore_con_url_pipeline_get())
+
diff --git a/examples/ecore/con/lookup.py b/examples/ecore/con/lookup.py
new file mode 100755
index 0000000..db71402
--- /dev/null
+++ b/examples/ecore/con/lookup.py
@@ -0,0 +1,18 @@
+#!/usr/bin/env python
+# encoding: utf-8
+
+
+from efl import ecore
+from efl import ecore_con
+
+
+def on_complete(canonname, ip):
+    print('Lookup completed')
+    print('Canonname: %s' % canonname)
+    print('Ip: %s' % ip)
+
+    ecore.main_loop_quit()
+
+ecore_con.Lookup('example.com', on_complete)
+
+ecore.main_loop_begin()
diff --git a/examples/ecore/con/url.py b/examples/ecore/con/url.py
new file mode 100755
index 0000000..2757e6d
--- /dev/null
+++ b/examples/ecore/con/url.py
@@ -0,0 +1,31 @@
+#!/usr/bin/env python
+# encoding: utf-8
+
+
+from efl import ecore
+from efl import ecore_con
+
+def on_data(event):
+    print("data: " + str(event.data))
+    # do something here with the received data
+
+def on_progress(event):
+    # print(event)
+    print("received %d on a total of %d bytes" % (
+           event.down_now, event.down_total))
+
+def on_complete(event):
+    # print(event)
+    print("http result: %d" % event.status)
+    print("Total received bytes: %d" % event.url.received_bytes)
+
+    u.delete() # don't forget to delete !!
+    ecore.main_loop_quit()
+
+u = ecore_con.Url('http://www.example.com', verbose=False)
+u.on_data_event_add(on_data)
+u.on_progress_event_add(on_progress)
+u.on_complete_event_add(on_complete)
+u.get()
+
+ecore.main_loop_begin()
diff --git a/include/efl.ecore.pxd b/include/efl.ecore.pxd
index 3ceece5..bc3d385 100644
--- a/include/efl.ecore.pxd
+++ b/include/efl.ecore.pxd
@@ -371,6 +371,7 @@ cdef class EventHandler(object):
 
 cdef class Event(object):
     cdef int _set_obj(self, void *obj) except 0
+    cdef object _get_obj(self)
 
 
 cdef class EventSignalUser(Event):
@@ -444,5 +445,7 @@ cdef class FileMonitor:
 
     cdef object _exec_monitor(self, Ecore_File_Event event, const char *path)
 
+
 cdef object _event_mapping_register(int type, cls)
 cdef object _event_mapping_unregister(int type)
+cdef object _event_mapping_get(int type)
diff --git a/setup.py b/setup.py
index 5a3c6d2..36a50ee 100755
--- a/setup.py
+++ b/setup.py
@@ -110,8 +110,9 @@ class CleanGenerated(Command):
         pass
 
     def run(self):
-        for lib in ("eo", "evas", "ecore", "ecore_x", "edje", "emotion",
-                    "elementary", "ethumb", "dbus_mainloop", "utils"):
+        for lib in ("eo", "evas", "ecore", "ecore_x", "ecore_con", "edje",
+                    "emotion", "elementary", "ethumb", "dbus_mainloop",
+                    "utils"):
             lib_path = os.path.join(script_path, "efl", lib)
             for root, dirs, files in os.walk(lib_path):
                 for f in files:
@@ -307,6 +308,19 @@ if set(("build", "build_ext", "install", "bdist", 
"sdist")) & set(sys.argv):
                                           eina_libs + evas_libs)
     ext_modules.append(ecore_ext)
 
+    # === Ecore Con ===
+    ecore_con_cflags, ecore_con_libs = pkg_config('EcoreCon', 'ecore-con',
+                                                  EFL_MIN_VER)
+    ecore_con_ext = Extension("ecore_con",
+                              ["efl/ecore_con/efl.ecore_con" + module_suffix],
+                              include_dirs=['include/'],
+                              extra_compile_args=list(set(ecore_cflags +
+                                                          ecore_file_cflags +
+                                                          ecore_con_cflags)),
+                              extra_link_args=ecore_libs + ecore_file_libs +
+                                              ecore_con_libs + eina_libs)
+    ext_modules.append(ecore_con_ext)
+
     # === Ecore X ===
     try:
         ecore_input_cflags, ecore_input_libs = pkg_config('EcoreInput',
diff --git a/tests/ecore/test_11_con.py b/tests/ecore/test_11_con.py
new file mode 100644
index 0000000..51eb634
--- /dev/null
+++ b/tests/ecore/test_11_con.py
@@ -0,0 +1,156 @@
+#!/usr/bin/env python
+
+import unittest
+import os
+import tempfile
+
+from efl import ecore, ecore_con
+
+
+class TestCon(unittest.TestCase):
+
+    def testLookup(self):
+        def _dns_complete(canonname, ip, sockaddr, arg1, my_karg):
+            self.assertEqual(canonname, 'google-public-dns-a.google.com.')
+            self.assertEqual(ip, '8.8.8.8')
+            self.assertEqual(arg1, "arg1")
+            self.assertEqual(my_karg, 1234)
+            ecore.main_loop_quit()
+
+        ecore_con.Lookup('google-public-dns-a.google.com',
+                          _dns_complete, "arg1", my_karg=1234)
+        ecore.main_loop_begin()
+
+    def testUrl(self):
+        self.complete_counter = 0
+        self.progress_counter = 0
+        self.exit_counter = 3 # we expect 3 complete cb calls
+        self.test_url = 'http://www.example.com'
+        self.received_data = []
+
+        def _on_complete(event, add=1):
+            self.assertIsInstance(event, ecore_con.EventUrlComplete)
+            self.assertEqual(event.status, 200) # assume net is ok
+            self.assertEqual(event.url.url, self.test_url)
+
+            self.complete_counter += add
+            self.exit_counter -= 1
+            if self.exit_counter == 0:
+                ecore.main_loop_quit()
+
+        def _on_progress(event, param1, two, one):
+            self.assertIsInstance(event, ecore_con.EventUrlProgress)
+            self.assertEqual(param1, 'param1')
+            self.assertEqual(one, 1)
+            self.assertEqual(two, 2)
+            self.progress_counter += 1
+
+        def _on_data(event):
+            self.assertIsInstance(event, ecore_con.EventUrlData)
+            self.received_data.append(event.data)
+
+        u = ecore_con.Url(self.test_url)
+        self.assertIsInstance(u, ecore_con.Url)
+
+        u.on_complete_event_add(_on_complete, add=100)
+        u.on_complete_event_add(_on_complete, add=10)
+        u.on_complete_event_del(_on_complete, add=100) #test event_del
+        u.on_complete_event_add(_on_complete, add=1)
+        u.on_complete_event_add(_on_complete, add=5)
+
+        u.on_progress_event_add(_on_progress, 'param1', one=1, two=2)
+        u.on_data_event_add(_on_data)
+
+        self.assertTrue(u.get()) #perform the GET request
+
+        ecore.main_loop_begin()
+
+        self.assertEqual(u.status_code, 200) # assume net is ok
+        self.assertEqual(self.complete_counter, 16)
+        self.assertTrue(self.progress_counter > 0)
+
+        data = b''.join(self.received_data)
+        self.assertEqual(len(data), u.received_bytes)
+
+        u.delete()
+
+    def testUrlDelete(self):
+
+        self.test_url1 = 'http://www.example.com'
+        self.test_url2 = 'http://www.google.com'
+        self.complete_counter = 0
+
+        def _on_complete1(event):
+            self.assertIsInstance(event, ecore_con.EventUrlComplete)
+            self.assertEqual(event.url.url, self.test_url1)
+            self.complete_counter += 1
+
+        def _on_complete2(event):
+            self.assertIsInstance(event, ecore_con.EventUrlComplete)
+            self.assertEqual(event.url.url, self.test_url2)
+            self.complete_counter += 10
+            
+        u1 = ecore_con.Url(self.test_url1)
+        u1.on_complete_event_add(_on_complete1)
+
+        u2 = ecore_con.Url(self.test_url2)
+        u2.on_complete_event_add(_on_complete2)
+
+        u3 = ecore_con.Url(self.test_url1)
+        u3.on_complete_event_add(_on_complete1)
+        u3.get() 
+        u3.delete() # the previous get will not run
+
+        self.assertTrue(u1.get()) #perform the GET request
+        self.assertTrue(u2.get()) #perform the GET request
+
+        ecore.Timer(2.5, lambda: ecore.main_loop_quit())
+        ecore.main_loop_begin()
+
+        self.assertEqual(u1.status_code, 200) # assume net is ok
+        self.assertEqual(u2.status_code, 200) # assume net is ok
+        self.assertEqual(self.complete_counter, 11)
+
+        u1.delete()
+        u2.delete()
+
+    def testUrlToFile(self):
+        self.test_url = 'http://www.example.com'
+
+        def _on_complete(event):
+            ecore.main_loop_quit()
+
+        fd, path = tempfile.mkstemp()
+        u = ecore_con.Url(self.test_url, fd=fd)
+        u.on_complete_event_add(_on_complete)
+        u.get()
+
+        ecore.main_loop_begin()
+        self.assertEqual(u.status_code, 200) # assume net is ok
+        self.assertEqual(os.path.getsize(path), u.received_bytes)
+        os.unlink(path)
+
+        u.delete()
+
+    # def testFtp(self):
+        # pass #TODO
+
+    def testPost(self):
+        self.test_url = 'https://httpbin.org/post'
+        self.data_to_post = b'my data to post'
+
+        def _on_complete(event):
+            ecore.main_loop_quit()
+
+        u = ecore_con.Url(self.test_url)
+        u.on_complete_event_add(_on_complete)
+        # u.post(self.data_to_post, "multipart/form-data")
+        u.post(self.data_to_post, "text/txt")
+        
+        ecore.main_loop_begin()
+        self.assertEqual(u.status_code, 200) # assume net is ok
+
+        u.delete()
+
+if __name__ == '__main__':
+    unittest.main(verbosity=2)

-- 


Reply via email to