summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGustavo Sverzut Barbieri <barbieri@profusion.mobi>2016-08-05 00:05:01 -0300
committerGustavo Sverzut Barbieri <barbieri@profusion.mobi>2016-08-05 00:21:40 -0300
commit35bab5c64a6c928121202697221ecebf4b606659 (patch)
treee90001c5e61c8693e5ec6b24a7691153fad39b78
parentfd8326fc52c8ef45eb8c7a5cdc51f7b1135dcfd2 (diff)
downloadefl-devs/barbieri/ecore-con-eoify.tar.gz
WIP: add efl_net eo API.devs/barbieri/ecore-con-eoify
<< THIS IS A WORK IN PROGRESS, DO NOT MERGE >> The API is composed of the following class structure: - Efl.Net.Socket: common connection, able to send/receive data - Efl.Net.Socket.UDP - Efl.Net.Dialer.UDP - Efl.Net.Socket.TCP - Efl.Net.Dialer.TCP - Efl.Net.Socket.Unix - Efl.Net.Dialer.Unix - Efl.Net.Dialer (mixin): creates a connection to a server (client) - Efl.Net.Server: creates a server to listen for connections - Efl.Net.Server.UDP - Efl.Net.Server.TCP - Efl.Net.Server.Unix
-rw-r--r--src/Makefile_Ecore_Con.am22
-rw-r--r--src/examples/ecore/efl_net_server_tcp.c277
-rw-r--r--src/lib/ecore_con/Ecore_Con.h30
-rw-r--r--src/lib/ecore_con/Ecore_Con_Eo.h16
-rw-r--r--src/lib/ecore_con/ecore_con-eoify-analisys.md486
-rw-r--r--src/lib/ecore_con/ecore_con.c470
-rw-r--r--src/lib/ecore_con/ecore_con_private.h38
-rw-r--r--src/lib/ecore_con/efl_net_dialer.eo39
-rw-r--r--src/lib/ecore_con/efl_net_dialer_tcp.eo3
-rw-r--r--src/lib/ecore_con/efl_net_dialer_udp.eo3
-rw-r--r--src/lib/ecore_con/efl_net_dialer_unix.eo5
-rw-r--r--src/lib/ecore_con/efl_net_server.eo114
-rw-r--r--src/lib/ecore_con/efl_net_server_tcp.eo3
-rw-r--r--src/lib/ecore_con/efl_net_server_udp.eo3
-rw-r--r--src/lib/ecore_con/efl_net_server_unix.eo5
-rw-r--r--src/lib/ecore_con/efl_net_socket.eo286
-rw-r--r--src/lib/ecore_con/efl_net_socket_tcp.eo3
-rw-r--r--src/lib/ecore_con/efl_net_socket_udp.eo11
-rw-r--r--src/lib/ecore_con/efl_net_socket_unix.eo24
-rw-r--r--src/lib/ecore_con/efl_net_types.eot19
20 files changed, 1855 insertions, 2 deletions
diff --git a/src/Makefile_Ecore_Con.am b/src/Makefile_Ecore_Con.am
index a478dc2298..3d5f19c391 100644
--- a/src/Makefile_Ecore_Con.am
+++ b/src/Makefile_Ecore_Con.am
@@ -9,10 +9,27 @@ ecore_con_eolian_files = \
lib/ecore_con/ecore_con_eet_base.eo \
lib/ecore_con/ecore_con_eet_server_obj.eo \
lib/ecore_con/ecore_con_eet_client_obj.eo \
- lib/ecore_con/efl_network_url.eo
+ lib/ecore_con/efl_network_url.eo \
+ lib/ecore_con/efl_net_dialer.eo \
+ lib/ecore_con/efl_net_dialer_tcp.eo \
+ lib/ecore_con/efl_net_dialer_udp.eo \
+ lib/ecore_con/efl_net_dialer_unix.eo \
+ lib/ecore_con/efl_net_server.eo \
+ lib/ecore_con/efl_net_server_tcp.eo \
+ lib/ecore_con/efl_net_server_udp.eo \
+ lib/ecore_con/efl_net_server_unix.eo \
+ lib/ecore_con/efl_net_socket.eo \
+ lib/ecore_con/efl_net_socket_tcp.eo \
+ lib/ecore_con/efl_net_socket_udp.eo \
+ lib/ecore_con/efl_net_socket_unix.eo
+
+ecore_con_eolian_type_files = \
+ lib/ecore_con/efl_net_types.eot
+
ecore_con_eolian_c = $(ecore_con_eolian_files:%.eo=%.eo.c)
ecore_con_eolian_h = $(ecore_con_eolian_files:%.eo=%.eo.h) \
+ $(ecore_con_eolian_type_files:%.eot=%.eot.h) \
$(ecore_con_eolian_files:%.eo=%.eo.legacy.h)
BUILT_SOURCES += \
@@ -21,7 +38,8 @@ BUILT_SOURCES += \
ecoreconeolianfilesdir = $(datadir)/eolian/include/ecore-@VMAJ@
ecoreconeolianfiles_DATA = \
- $(ecore_con_eolian_files)
+ $(ecore_con_eolian_files) \
+ $(ecore_con_eolian_type_files)
EXTRA_DIST2 += \
${ecoreconeolianfiles_DATA}
diff --git a/src/examples/ecore/efl_net_server_tcp.c b/src/examples/ecore/efl_net_server_tcp.c
new file mode 100644
index 0000000000..828bd5dfd0
--- /dev/null
+++ b/src/examples/ecore/efl_net_server_tcp.c
@@ -0,0 +1,277 @@
+#include <Ecore_Getopt.h>
+#include <Ecore_Con.h>
+#include <Eina.h>
+#include <Ecore.h>
+
+static Eina_List *clients = NULL;
+
+static void
+_client_del(Efl_Net_Socket *client)
+{
+ clients = eina_list_remove(clients, client);
+ eo_del(client);
+}
+
+static void
+_cb_client_received(void *data EINA_UNUSED, const Eo_Event *event)
+{
+ Efl_Net_Socket *client = event->object;
+ Efl_Net_Socket_Receive_Event *ev = event->info;
+ Eina_Error error;
+
+ printf("received from %p, %s->%s: %zd[%.*s]\n", client,
+ efl_net_socket_address_local_get(client),
+ efl_net_socket_address_remote_get(client),
+ ev->size, (int)ev->size, (const char *)ev->data);
+
+ error = efl_net_socket_send(client, ev->data, ev->size);
+ if (error) {
+ fprintf(stderr, "ERROR: could not send data to client %p, %s->%s: %s\n",
+ client, efl_net_socket_address_local_get(client),
+ efl_net_socket_address_remote_get(client),
+ eina_error_msg_get(error));
+ _client_del(client);
+ }
+}
+
+static void
+_cb_client_sent(void *data EINA_UNUSED, const Eo_Event *event)
+{
+ Efl_Net_Socket *client = event->object;
+ Efl_Net_Socket_Sent_Event *ev = event->info;
+
+ if (ev->error) {
+ fprintf(stderr, "ERROR: could not send data to client %p, %s->%s: %s, did %zd bytes\n",
+ client, efl_net_socket_address_local_get(client),
+ efl_net_socket_address_remote_get(client),
+ eina_error_msg_get(error), ev->size);
+ _client_del(client);
+ return;
+ }
+
+ printf("sent from %p, %s->%s: %zd\n", client,
+ efl_net_socket_address_local_get(client),
+ efl_net_socket_address_remote_get(client),
+ ev->size);
+}
+
+static void
+_cb_client_drained(void *data EINA_UNUSED, const Eo_Event *event)
+{
+ Efl_Net_Socket *client = event->object;
+
+ printf("drained on %p, %s->%s\n", client,
+ efl_net_socket_address_local_get(client),
+ efl_net_socket_address_remote_get(client));
+}
+
+static void
+_cb_client_closed(void *data EINA_UNUSED, const Eo_Event *event)
+{
+ Efl_Net_Socket *client = event->object;
+
+ printf("closed on %p, %s->%s\n", client,
+ efl_net_socket_address_local_get(client),
+ efl_net_socket_address_remote_get(client));
+}
+
+static void
+_cb_client_timedout(void *data EINA_UNUSED, const Eo_Event *event)
+{
+ Efl_Net_Socket *client = event->object;
+
+ printf("timedout on %p, %s->%s\n", client,
+ efl_net_socket_address_local_get(client),
+ efl_net_socket_address_remote_get(client));
+}
+
+static void
+_cb_client_error(void *data EINA_UNUSED, const Eo_Event *event)
+{
+ Efl_Net_Socket *client = event->object;
+ Eina_Error error = *(const Eina_Error *)client->info;
+
+ fprintf(stderr, "ERROR: %p, %s->%s: %s\n", client,
+ efl_net_socket_address_local_get(client),
+ efl_net_socket_address_remote_get(client),
+ eina_error_msg_get(error));
+ _client_del(client);
+}
+
+static void
+_cb_client_add(void *data EINA_UNUSED, const Eo_Event *event)
+{
+ Efl_Net_Socket *client = event->info;
+
+ printf("client,add: %p, %s->%s\n", client,
+ efl_net_socket_address_local_get(client),
+ efl_net_socket_address_remote_get(client));
+
+ // TODO: change to eo_event_callback_array_add()
+ eo_event_callback_add(client, EFL_NET_SOCKET_EVENT_RECEIVED,
+ _cb_client_received, NULL);
+ eo_event_callback_add(client, EFL_NET_SOCKET_EVENT_SENT,
+ _cb_client_sent, NULL);
+ eo_event_callback_add(client, EFL_NET_SOCKET_EVENT_DRAINED,
+ _cb_client_drained, NULL);
+ eo_event_callback_add(client, EFL_NET_SOCKET_EVENT_CLOSED,
+ _cb_client_closed, NULL);
+ eo_event_callback_add(client, EFL_NET_SOCKET_EVENT_TIMEDOUT,
+ _cb_client_timedout, NULL);
+ eo_event_callback_add(client, EFL_NET_SOCKET_EVENT_ERROR,
+ _cb_client_error, NULL);
+
+ clients = eina_list_append(clients, client);
+}
+
+static void
+_cb_client_rejected(void *data EINA_UNUSED, const Eo_Event *event)
+{
+ const char *address = event->info;
+
+ printf("client,rejected: %s\n", address);
+}
+
+static void
+_cb_error(void *data EINA_UNUSED, const Eo_Event *event)
+{
+ Eina_Error error = *(const Eina_Error *)event->info;
+
+ fprintf(stderr, "ERROR: server error: %s\n", eina_error_msg_get(error));
+ ecore_main_loop_quit();
+}
+
+static Eina_Bool
+_cb_stdin(void *data EINA_UNUSED, Ecore_Fd_Handler *fdh)
+{
+ ssize_t r;
+ size_t len = 0;
+ char *buf = NULL;
+ Eina_List *itr, *itr_next;
+ Efl_Net_Socket *client;
+
+ r = getline(&buf, &len, stdin);
+ if (r < 0) {
+ perror("ERROR: could not read from stdin");
+ ecore_main_loop_quit();
+ return;
+ }
+
+ EINA_LIST_FOREACH_SAFE(clients, itr, itr_next, client) {
+ Eina_Error error = efl_net_socket_send(client, buf, len);
+ if (error) {
+ fprintf(stderr,
+ "ERROR: could not send data to client %p, %s->%s: %s\n",
+ client, efl_net_socket_address_local_get(client),
+ efl_net_socket_address_remote_get(client),
+ eina_error_msg_get(error));
+ _client_del(client);
+ }
+ }
+
+ free(buf);
+}
+
+static const Ecore_Getopt options = {
+ "efl_net_server_tcp",
+ NULL /* default usage line */,
+ "1" /* version */,
+ "(C) 2016 Enlightenment Project",
+ "BSD 2-Clause",
+ "Example of Efl_Net_Server_TCP usage.\n"
+ EINA_TRUE /* strict parsing */,
+ {
+ ECORE_GETOPT_STORE_UINT('f', "flags", "Efl.Net.Socket.Flags to use"),
+ ECORE_GETOPT_STORE_DOUBLE('t', "client-timeout", "Timeout in seconds for newly accepted clients."),
+ ECORE_GETOPT_STORE_UINT('l', "client-limit", "Maximum number of clients."),
+ ECORE_GETOPT_STORE_BOOL('r', "reject-excess-clients", "Automatically reject clients going after client-limit."),
+
+ /* standard block to provide version, copyright, license and help */
+ ECORE_GETOPT_VERSION('V', "version"),
+ ECORE_GETOPT_COPYRIGHT('C', "copyright"),
+ ECORE_GETOPT_LICENSE('L', "license"),
+ ECORE_GETOPT_HELP('h', "help"),
+
+ ECORE_GETOPT_STORE_METAVAR_STR(0, NULL, "IP:PORT to listen.", "ADDRESS"),
+
+ ECORE_GETOPT_SENTINEL
+ }
+};
+
+int
+main(int argc, char *argv[])
+{
+ unsigned int flags = EFL_NET_SOCKET_FLAGS_DEFAULTS;
+ double timeout = 2.0;
+ unsigned int client_limit = 2;
+ Eina_Bool reject_excess_clients = EINA_FALSE;
+ Eina_Bool quit_option = EINA_FALSE;
+ const char *addr = "0.0.0.0:8000";
+ Ecore_Getopt_Value values[] = {
+ ECORE_GETOPT_VALUE_UINT(flags),
+ ECORE_GETOPT_VALUE_DOUBLE(timeout),
+ ECORE_GETOPT_VALUE_UINT(client_limit),
+ ECORE_GETOPT_VALUE_BOOL(reject_excess_clients),
+ ECORE_GETOPT_VALUE_BOOL(quit_option),
+ ECORE_GETOPT_VALUE_BOOL(quit_option),
+ ECORE_GETOPT_VALUE_BOOL(quit_option),
+ ECORE_GETOPT_VALUE_BOOL(quit_option),
+ ECORE_GETOPT_VALUE_STR(addr),
+ ECORE_GETOPT_VALUE_NONE
+ }
+ Efl_Net_Server_Tcp *srv;
+ Ecore_Fd_Handler *fdh;
+ int args, retval = EXIT_SUCCESS;
+
+ eina_init();
+ ecore_init();
+ ecore_con_init();
+
+ args = ecore_getopt_parse(&options, values, argc, argv);
+ if (args < 0)
+ {
+ fputs("ERROR: Could not parse command line options.\n", stderr);
+ retval = EXIT_FAILURE;
+ goto end;
+ }
+
+ if (quit_option) goto end;
+
+ args = ecore_getopt_parse_positional(&options, values, argc, argv, args);
+ if (args < 0)
+ {
+ fputs("ERROR: Could not parse positional arguments.\n", stderr);
+ retval = EXIT_FAILURE;
+ goto end;
+ }
+
+ srv = eo_add(EFL_NET_SERVER_TCP, NULL,
+ efl_net_server_address_set(eo_self, addr),
+ efl_net_server_flags_set(eo_self, flags),
+ efl_net_server_client_limit_set(eo_self, client_limit,
+ reject_excess_clients),
+ efl_net_server_client_timeout_set(eo_self, timeout));
+
+ // TODO: change to eo_event_callback_array_add()
+ eo_event_callback_add(srv, EFL_NET_SERVER_EVENT_CLIENT_ADD,
+ _cb_client_add, NULL);
+ eo_event_callback_add(srv, EFL_NET_SERVER_EVENT_CLIENT_REJECTED,
+ _cb_client_rejected, NULL);
+ eo_event_callback_add(srv, EFL_NET_SERVER_EVENT_ERROR,
+ _cb_error, NULL);
+
+ fdh = ecore_main_fd_handler_add(STDIN_FILENO, ECORE_FD_READ,
+ _cb_stdin, NULL, NULL, NULL);
+
+ ecore_main_loop_begin();
+
+ eo_unref(srv);
+ ecore_main_fd_handler_del(fdh);
+
+ end:
+ ecore_con_shutdown();
+ ecore_shutdown();
+ eina_shutdown();
+
+ return retval;
+}
diff --git a/src/lib/ecore_con/Ecore_Con.h b/src/lib/ecore_con/Ecore_Con.h
index d298ccf892..b815ba9eb7 100644
--- a/src/lib/ecore_con/Ecore_Con.h
+++ b/src/lib/ecore_con/Ecore_Con.h
@@ -653,6 +653,36 @@ EAPI extern int ECORE_CON_EVENT_URL_PROGRESS;
*/
/**
+ * @addtogroup Ecore_Con_Errors_Group Ecore_Con_Lib_Group
+ * @ingroup Ecore_Con_Group
+ *
+ * @{
+ */
+#ifdef EFL_BETA_API_SUPPORT
+/**
+ * No space left on device (errno: ENOSPC).
+ * @since 1.19
+ */
+EAPI extern Eina_Error EFL_NET_ERROR_NO_SPACE;
+
+/**
+ * Transport endpoint is not connected (errno: ENOTCONN).
+ * @since 1.19
+ */
+EAPI extern Eina_Error EFL_NET_ERROR_NOT_CONNECTED;
+
+/**
+ * Operation Canceled (errno: ECANCELED).
+ * @since 1.19
+ */
+EAPI extern Eina_Error EFL_NET_ERROR_CANCELED;
+
+#endif
+/**
+ * @}
+ */
+
+/**
* @addtogroup Ecore_Con_Events_Group Ecore_Con_Lib_Group
* @ingroup Ecore_Con_Group
*
diff --git a/src/lib/ecore_con/Ecore_Con_Eo.h b/src/lib/ecore_con/Ecore_Con_Eo.h
index cb2903b5f1..321a303772 100644
--- a/src/lib/ecore_con/Ecore_Con_Eo.h
+++ b/src/lib/ecore_con/Ecore_Con_Eo.h
@@ -1,5 +1,21 @@
+/* TODO: remove efl_network_* once efl_net_* are ready */
#include "efl_network.eo.h"
#include "efl_network_server.eo.h"
#include "efl_network_connector.eo.h"
#include "efl_network_client.eo.h"
#include "efl_network_url.eo.h"
+
+#include "efl_net_types.eot.h"
+
+#include "efl_net_dialer.eo.h"
+#include "efl_net_dialer_tcp.eo.h"
+#include "efl_net_dialer_udp.eo.h"
+#include "efl_net_dialer_unix.eo.h"
+#include "efl_net_server.eo.h"
+#include "efl_net_server_tcp.eo.h"
+#include "efl_net_server_udp.eo.h"
+#include "efl_net_server_unix.eo.h"
+#include "efl_net_socket.eo.h"
+#include "efl_net_socket_tcp.eo.h"
+#include "efl_net_socket_udp.eo.h"
+#include "efl_net_socket_unix.eo.h"
diff --git a/src/lib/ecore_con/ecore_con-eoify-analisys.md b/src/lib/ecore_con/ecore_con-eoify-analisys.md
new file mode 100644
index 0000000000..27909646dd
--- /dev/null
+++ b/src/lib/ecore_con/ecore_con-eoify-analisys.md
@@ -0,0 +1,486 @@
+# Ecore_Con
+
+This module is used via `Ecore_Con_Server`, for both server and
+connect-to-server roles, which can be confusing at first sight.
+
+There is an `Ecore_Con_Client` handle, but users do not create them
+manually, rather receive them via an `Ecore_Event`
+`ECORE_CON_EVENT_CLIENT_ADD`. This handle is to be used by server-side
+to identify connected clients and send them data.
+
+The **connect-to-server** role (usually called "connection", "socket" or
+"client") is done via an `Ecore_Con_Server` created with
+`ecore_con_server_connect()`.
+
+The server role is done via an `Ecore_Con_Server` created with
+`ecore_con_server_add()`.
+
+Current `ecore_con` usage (except `ecore_con_url`, within EFL):
+https://gist.github.com/barbieri/ed77c1e829a6e684dd736a771d6732d1
+
+## Ecore_Con_Server (as server role)
+
+Methods (omitted `ecore_con_server_` prefix):
+
+ - `add(type, name, port, data): Ecore_Con_Server` *[[constructor]]*
+ - `ssl_cert_add(string): bool` *[[Add a PEM certificate file]]*
+ - `ssl_privkey_add(string): bool` *[[Add a PEM private key file]]*
+ - `ssl_crl_add(string): bool` *[[add PEM CRL file]]*
+ - `ssl_cafile_add(string): bool` *[[add PEM CA file]]*
+
+Properties:
+
+ - `data: void_ptr` *[[user data]]*
+ - `connected: bool` *[[if connected or not]]*
+ - `port: int` *[[the port serving the requests]]*
+ - `uptime: double` *[[time in seconds since it was started]]*
+ - `client_limit: int` *[[number of concurrent clients to allow]]*
+ - `reject_excess_clients: bool` *[[if true, automatically disconnects extra clients]]*
+
+Events (omitted `ECORE_CON_EVENT_` prefix):
+
+ - `CLIENT_ADD: Ecore_Con_Client`
+ - `CLIENT_DEL: Ecore_Con_Client`
+
+# Ecore_Con_Client (used only by the server role)
+
+Methods:
+
+ - `send(buffer): int` *[[server writes data to client]]*
+ - `flush(): void` *[[block until all pending data is written]]*
+ - `del(): void` *[[server close connection to client and free client handle]]*
+ - `upgrade(type): bool` *[[upgrade to SSL using STARTTLS]]*
+
+Properties:
+
+ - `fd: int` *[[get the internal filedescriptor]]*
+ - `data [RW]: void_ptr` *[[user data associated with a client]]*
+ - `ip: string` *[[IP address of this client]]*
+ - `port: int` *[[port of connected client]]*
+ - `uptime: double` *[[time in seconds since it was connected]]*
+ - `connected: bool` *[[if connected]]*
+ - `timeout [RW]: double` *[[duration of idleness to keep the client alive]]*
+
+Events (omitted `ECORE_CON_EVENT_CLIENT_` prefix and `Ecore_Con_Client` handle):
+
+ - `DATA: buffer` *[[reports incoming data]]*
+ - `WRITE: int` *[[reports data sent]]*
+ - `UPGRADE: void` *[[reports completed handshake]]*
+ - `ERROR: string` *[[reports error]]*
+
+
+# Ecore_Con_Server (as connect-to-server role)
+
+Methods:
+
+ - `connect(type, name, port, data):` *[[constructor]]*
+ - `del():` *[[destructor]]*
+ - `send(buffer): int` *[[write data to server]]*
+ - `flush(): void` *[[block until all pending data is written]]*
+ - `upgrade(type): bool` *[[upgrade to SSL using STARTTLS]]*
+
+Methods (Did I get all of these right?):
+
+ - `ssl_cert_add(string): bool` *[[Add a PEM certificate file]]*
+ - `ssl_privkey_add(string): bool` *[[Add a PEM private key file]]*
+ - `ssl_crl_add(string): bool` *[[add PEM CRL file]]*
+ - `ssl_cafile_add(string): bool` *[[add PEM CA file]]*
+ - `ssl_server_upgrade(type): void` *[[start TLS]]*
+ - `ssl_server_verify(): void` *[[enable (once) the verification of loaded certificates]]*
+ - `ssl_server_verify_basic(): void` *[[enable hostname-based verification]]*
+ - `ssl_server_verify_name_set(string): void` *[[change the name to use for the server]]*
+ - `ssl_server_verify_name_get(): string` *[[get the name to use for the server]]*
+
+Properties (omitted ecore_con_server_ prefix):
+
+ - `fd: int` *[[get the internal filedescriptor]]*
+ - `ip: string` *[[IP address of the connected server]]*
+ - `data: void_ptr` *[[user data]]*
+ - `uptime: double` *[[time in seconds since it was connected]]*
+ - `connected: bool` *[[if connected or not]]*
+ - `timeout [RW]: double` *[[duration of idleness to keep the server alive]]*
+
+Events (omitted `ECORE_CON_EVENT_SERVER_` prefix and `Ecore_Con_Server` handle):
+
+ - `ADD:` *[[server was connected]]*
+ - `DEL:` *[[server is disconnected]]*
+ - `DATA: buffer` *[[server sent some data and it's ready to be read]]*
+ - `WRITE: int` *[[reports data sent]]*
+ - `ERROR: string` *[[reports error]]*
+ - `UPGRADE: void` *[[reports completed handshake]]*
+
+# Node.js
+
+Node.js is based on a single thread and main loop with asynchronous
+events.
+
+## net.Server
+
+This is analogous to `Ecore_Con_Server` as returned by the
+`ecore_con_server_add()`. It will listen for connections and each new
+accepted is a `net.Socket ` (see below), which is the equivalent of
+`Ecore_Con_Client` as passed in `ECORE_CON_EVENT_CLIENT_ADD` event.
+
+A server is created with `net.createServer([options], [cb])`
+
+Doc: https://nodejs.org/api/net.html#net_class_net_server
+
+Methods:
+
+ - `address(): object` *[[bound address with port, family and address]]*
+ - `close(): void` *[[stop the server servicing new conns, keeps existing]]*
+ - `getConnections(cb(err, count)):` *[[query count of existing conns]]*
+ - `listen(options, [cb]):` *[[start listening to conns]]*
+
+Properties:
+
+ - `listening: bool` *[[whenever server is listening]]*
+ - `maxConnections: int` *[[maximum concurrent conns to accept]]*
+
+Events:
+
+ - `close: void` *[[emitted when closed and the last connection is gone]]*
+ - `connection: client net.Socket` *[[new connection/client]]*
+ - `error: Error` *[[when an error occurs]]*
+ - `listening: void` *[[server.listen() was called]]*
+
+## net.Socket
+
+This is analogous to either `Ecore_Con_Client` as passed in
+`ECORE_CON_EVENT_CLIENT_ADD` or `Ecore_Con_Server` as returned by
+`ecore_con_server_connect()`. `Ecore_Con_Client` represents a client
+connected to a server, while the `Ecore_Con_Server` represents a
+connection to the server. Node.JS, as most APIs out there, simply
+handle this as an ongoing connection which offers common properties
+such as local and remote addresses, read and write methods and so on.
+
+A connection is passed to `net.Server::connection` event or created
+with net.connect() or net.createConnection().
+
+Doc: https://nodejs.org/api/net.html#net_class_net_socket
+
+Methods:
+
+ - `constructor([{fd, readable, writeable}], [allowHalfOpen])`
+ - `write(data, encoding): bool` *[[sends data, True means all data was sent]]*
+ - `address(): object` *[[bound address with port, family and address]]*
+ - `connect(options, listener):` *[[connect to a server]]*
+ - `destroy([error]):` *[[ensures no more i/o]]*
+ - `end([data] [,encoding]):` *[[do write(data, encoding), then send FIN]]*
+ - `pause():` *[[pauses data reading, does not emit 'data' event]]*
+ - `resume():` *[[resumes data reading, allows emit 'data' event]]*
+ - `setEncoding(string):` *[[sets an encoding to convert raw bytes to string]]*
+ - `setKeepAlive(bool, initialDelay):` *[[change keep alive behavior]]*
+ - `setNoDelay(bool):` *[[send data immediately on each write()]]*
+ - `setTimeout(int):` *[[set socket timeout milliseconds, 0 disables]]*
+
+Properties:
+
+ - `bufferSize: int` *[[write buffer to keep]]*
+ - `bytesRead: int` *[[received bytes]]*
+ - `bytesWritten: int` *[[sent bytes]]*
+ - `connecting: bool` *[[if connect() is still on going]]*
+ - `destroyed: bool` *[[conn was destroyed]]*
+ - `localAddress: string` *[[local IP or path]]*
+ - `localPort: int` *[[local port number]]*
+ - `remoteAddress: string` *[[remote IP or path]]*
+ - `remoteFamily: string` [remote family, IPv4 or IPv6]]*
+ - `remotePort: int` *[[remote port number]]*
+
+Events:
+
+ - `close: had_error bool` *[[emitted once the socket is fully closed]]*
+ - `connect: void` *[[emitted once the connection is fully stablished]]*
+ - `data: buffer` *[[data is received]]*
+ - `drain: void` *[[emitted when write buffer becomes empty]]*
+ - `end: void` *[[emitted when the other end sends a FIN packet]]*
+ - `error: Error` *[[when an error occurs, followed by 'close' event.]]*
+ - `lookup: err, address, family, host` *[[after name is resolved, before connecting]]*
+ - `timeout: void` *[[after inactivity timeout]]*
+
+
+# Qt
+
+Qt is based on single thread and main loop with asynchronous events.
+
+## QTcpServer
+
+This is analogous to `Ecore_Con_Server` as returned by
+`ecore_con_server_add()`. It will listen for connecting and each new
+accepted is a `QTcpSocket` (derivate of `QAbstractSocket`, see below,
+which is the equivalent of `Ecore_Con_Client` as passed in
+`ECORE_CON_EVENT_CLIENT_ADD` event.
+
+A server is created with `QTcpServer` object. Clients (`QTcpSocket`)
+are child objects of `QTcpServer` and are deleted if the server is
+destroyed.
+
+Doc: http://doc.qt.io/qt-5/qtcpserver.html
+
+Methods:
+
+ - `close(): void` *[[stop listening for connections]]*
+ - `listen(address, port): bool`
+ - `nextPendingConnection(): QTcpSocket` *[[returns the next ready client as QTcpSocket]]*
+ - `pauseAccepting(): void`
+ - `resumeAccepting(): void`
+ - `waitForNewConnection(time)` *[[blocks main thread]]*
+
+Properties:
+
+ - `isListening(): bool`
+ - `errorString(): string` *[[string of serverError()]]*
+ - `serverError(): SocketError`
+ - `serverAddress(): QHostAddress`
+ - `serverPort(): int`
+ - `maxPendingConnections(): int`
+ - `setMaxPendingConnections(int): void`
+ - `hasPendingConnections(): bool` *[[if can call nextPendingConnection()]]*
+ - `proxy(): QNetworkProxy`
+ - `setProxy(QNetworkProxy): void`
+
+Events:
+
+ - `acceptError: SocketError` *[[if accept triggers an error]]*
+ - `newConnection: void` *[[need to call nextPendingConnection()]]*
+
+## QAbstractSocket
+
+This is analogous to either `Ecore_Con_Client` as passed in
+`ECORE_CON_EVENT_CLIENT_ADD` or `Ecore_Con_Server` as returned by
+`ecore_con_server_connect()`. `Ecore_Con_Client` represents a client
+connected to a server, while the `Ecore_Con_Server` represents a
+connection to the server. Qt, as most APIs out there, simply handle
+this as an ongoing connection which offers common properties such as
+local and remote addresses, read and write methods and so on.
+
+A connection is returned by `QTcpServer::nextPendingConnection()` or
+created with `QTcpSocket()`, `QSslSocket()`, `QUdpSocket()`,
+`QLocalSocket()`...
+
+Connections are implemented by specific classes such as `QTcpSocket`,
+`QSslSocket`, `QUdpSocket` and `QLocalSocket`.
+
+Doc: http://doc.qt.io/qt-5/qabstractsocket.html
+
+Methods:
+
+ - `connectToHost(address, port, mode):` *[[connect to server]]*
+ - `disconnectFromHost()` *[[schedule for close once all data is sent]]*
+ - `abort(): void` *[[ensures no more i/o]]*
+ - `flush(): bool` *[[flushes as much data to socket without block]]*
+ - `close(): bool` *[[emits 'aboutToClose', set mode=NotOpen, closes the socket]]*
+ - `waitForBytesWritten(time): bool` *[[block caller thread for write()]]*
+ - `waitForReadyRead(time): bool` *[[block caller thread for read()]]*
+ - `waitForDisconnect(time): bool`*[[block caller thread for disconnect]]*
+ - `waitForConnect(time): bool` *[[block caller thread for connect]]*
+ - `read(char *, int64 max): int64`
+ - `readAll(): buffer`
+ - `readLine(): buffer`
+ - `write(char *, int64 max): int64`
+ - `getChar(char *): bool`
+ - `putChar(char): bool`
+ - `peek(char *, int64 max): int64`
+ - `reset()`
+
+Properties:
+
+ - `localAddress(): QHostAddress`
+ - `localPort(): int`
+ - `peerAddress(): QHostAddress`
+ - `peerPort(): int`
+ - `readBufferSize() int64`
+ - `setReadBufferSize(int64)`
+ - `setSocketOption(option, QVariant value)`
+ - `socketOption(option): QVariant`
+ - `setProxy(QNetworkProxy)`
+ - `proxy()`
+ - `socketType(): SocketType`
+ - `state(): SocketState`
+ - `error(): SocketError`
+ - `isOpen(): bool`
+ - `isReadable(): bool`
+ - `isWritable(): bool`
+ - `isSequential(): bool` *[[ie: TCP, streams]]*
+ - `isTextModeEnabled(): bool`
+ - `setTextModeEnabled(bool):`
+ - `canReadLine(): bool` *[[if there is a full line to be read]]*
+ - `atEnd(): bool` *[[to be used in loop constructs with read()]]*
+ - `bytesAvailable(): int64` *[[number of bytes waiting to be read]]*
+ - `bytesToWrite(): int64` *[[bytes queued for write]]*
+ - `pos(): int64`
+ - `canReadLine(): bool`
+
+Events:
+
+ - `connected: void`
+ - `disconnected: void`
+ - `error: SocketError`
+ - `hostFound: void` *[[after name is resolved, before connecting]]*
+ - `proxyAuthenticationRequired: proxy, authenticator`
+ - `stateChanged: SocketState`
+ - `aboutToClose: void` *[[once close() is called]]*
+ - `bytesWritten: int64`
+ - `readChannelFinished`
+ - `readyRead`
+
+# Go (language)
+
+Go is based on *synchronous* and *blocking* primitives, using multiple
+co-routines (goroutines) to implement concurrent tasks.
+
+## net.Listener
+
+This is the server role and is analogous to `Ecore_Con_Server` as
+returned by `ecore_con_server_add()`.
+
+Listeners are implemented implemented by specific classes such as
+`TCPListener`, `UDPListener` and `UnixListener`.
+
+Doc: https://golang.org/pkg/net/#Listener
+
+Methods:
+
+ - `Listen(network, address): Listener, error` *[[creates a listener]]*
+ - `Accept(): Conn, error` *[[waits for and returns a new connection]]*
+ - `Close(): error` *[[closes the listener]]*
+
+Properties:
+
+ - `Addr(): Addr` *[[returns the listener local address]]*
+
+
+## net.Dialer
+
+This is the **client-to-server** role and is analogous to
+`Ecore_Con_Server` as returned by `ecore_con_server_connect()`.
+
+Doc: https://golang.org/pkg/net/#Dialer
+
+Methods:
+
+ - `Dial(network, address): Conn, error` *[[connect to the remote server]]*
+ - `Cancel(): chan` *[[close connection if chan is closed]]*
+
+Properties:
+
+ - `Timeout: time` *[[maximum time to wait before conn times out]]*
+ - `Deadline: time` *[[absolute point in time to fail]]*
+ - `LocalAddr: Addr` *[[local address, if nil, assign automatic]]*
+ - `DualStack: bool` *[[ipv4 and ipv6]]*
+ - `FallbackDelay: time` *[[if DualStack, time to change to fallback conn]]*
+ - `KeepAlive: time` *[[duration of idleness to keep the connection alive]]*
+
+## net.Addr
+
+Doc: https://golang.org/pkg/net/#Addr
+
+Properties:
+
+ - `Network(): string` *[[network name]]*
+ - `String(): string` *[[string representation of the address]]*
+
+
+## net.TCPAddr / net.UDPAddr
+
+Doc: https://golang.org/pkg/net/#TCPAddr
+
+Properties:
+
+ - `IP:` (4/16 byte address)
+ - `Port: int`
+ - `Zone: string` *[[IPv6 zone]]*
+
+## net.Conn
+
+This is analogous to either `Ecore_Con_Client` as passed in
+`ECORE_CON_EVENT_CLIENT_ADD` or `Ecore_Con_Server` as returned by
+`ecore_con_server_connect()`. `Ecore_Con_Client` represents a client
+connected to a server, while the `Ecore_Con_Server` represents a
+connection to the server. Go, as most APIs out there, simply handle
+this as an ongoing connection which offers common properties such as
+local and remote addresses, read and write methods and so on.
+
+A connection is returned by `net.Listener::Accept()` or created with
+`net.Dialer`, such as `net.Dial(network, address)`.
+
+Unlike EFL, go primitives are all *synchronous* and people use
+goroutines to start a co-routine (pseudo thread) to do the processing.
+
+Connections are implemented by specific classes such as `IPConn`,
+`TCPConn`, `UDPConn` and `UnixConn`.
+
+Doc: https://golang.org/pkg/net/#Conn
+
+Methods:
+
+ - `Read(buffer): int, error` *[[read to buffer, returns read amount]]*
+ - `Write(buffer): int, error` *[[write buffer, returns written ammount]]*
+ - `Close(): error` *[[closes the connection]]*
+
+Properties:
+
+ - `LocalAddr(): Addr` *[[returns the local address]]*
+ - `RemoteAddr(): Addr` *[[returns the remote address]]*
+ - `SetDeadline(time): error` *[[set I/O deadlines]]*
+ - `SetReadDeadline(time): error` *[[set Input deadline]]*
+ - `SetWriteDeadline(time): error` *[[set Output deadline]]*
+
+
+## net.IPConn (net.Conn)
+
+Base class for `TCPConn` and `UDPConn`.
+
+Doc: https://golang.org/pkg/net/#IPConn
+
+Extra methods:
+
+ - `ReadFrom(buffer): int, Addr, error` *[[PacketConn variant of Read()]]*
+ - `ReadFromIP(buffer): int, IPAddr, error` *[[PacketConn variant of Read()]]*
+ - `ReadMsgIP(buffer, out_of_band_buffer): ...` *[[PacketConn variant]]*
+ - `WriteTo(buffer, Addr): int, error` *[[PacketConn variant]]*
+ - `WriteToIP(buffer, IPAddr): int, error` *[[PacketConn variant]]*
+ - `WriteMsgIP(buffer, out_of_band_buffer, IPAddr): `*[[PacketConn variant]]*
+ - `SetReadBuffer(int):` *[[bytes to use to receive data]]*
+ - `SetWriteBuffer(int):` *[[bytes to use to send data]]*
+
+
+## Soletta
+
+Soletta (https://github.com/solettaproject/soletta) is based on single
+thread and main loop with asynchronous events.
+
+It doesn't expose middleground classes to do socket, just the very
+basic POSIX-like API and high level via each module (MQTT,
+HTTP...). Nonetheless it focus on small and efficient APIs, thus
+instead of multiple setters and getters, most parameters are specified
+to the constructor as a structure with optional members (can be zero).
+
+The API to be listed here is not even a network one, but rather a
+streaming API used by its UART module. It's focused on efficiency and
+to save on memory it uses blobs (memory + size with parent and
+references) to avoid copies in the streaming API.
+
+Doc: http://solettaproject.github.io/docs/c-api/group__UART.html
+
+Methods:
+
+ - `open(config): handle` *[[constructor]]*
+ - `feed(blob): int` *[[]]*
+
+Events:
+
+ - `data: binbuf, &int` *[[reports data is available in binbuf, user reports how much was consumed]]*
+ - `feed_done: blob, error` *[[notifies a blob was fully sent or failed]]*
+
+As one can see, by feeding immutable blobs, the API does not keep an
+internal binbuf of pending memory, instead it will keep an array of
+pending blobs and the offset of the first pending blob (for partial
+sends). This maps well to iovec APIs in Linux.
+
+To avoid going over-memory on continuous send, `-ENOSPC` is returned
+if it goes over pre-defined buffer size in config.
+
+The constructor specifies maximum read bytes (to be allocated using a
+binbuf, 0 is unlimited) and write size (to be allowed in pending blobs
+list).
diff --git a/src/lib/ecore_con/ecore_con.c b/src/lib/ecore_con/ecore_con.c
index 29fe7311bb..1b749edaa2 100644
--- a/src/lib/ecore_con/ecore_con.c
+++ b/src/lib/ecore_con/ecore_con.c
@@ -175,6 +175,10 @@ EAPI int ECORE_CON_EVENT_CLIENT_ERROR = 0;
EAPI int ECORE_CON_EVENT_SERVER_ERROR = 0;
EAPI int ECORE_CON_EVENT_PROXY_BIND = 0;
+EAPI Eina_Error EFL_NET_ERROR_NO_SPACE = 0;
+EAPI Eina_Error EFL_NET_ERROR_NOT_CONNECTED = 0;
+EAPI Eina_Error EFL_NET_ERROR_CANCELED = 0;
+
static Eina_List *servers = NULL;
static int _ecore_con_init_count = 0;
static int _ecore_con_event_count = 0;
@@ -219,6 +223,10 @@ ecore_con_init(void)
eina_magic_string_set(ECORE_MAGIC_CON_CLIENT, "Ecore_Con_Client");
eina_magic_string_set(ECORE_MAGIC_CON_URL, "Ecore_Con_Url");
+ EFL_NET_ERROR_NO_SPACE = eina_error_msg_static_register("No space left on device");
+ EFL_NET_ERROR_NOT_CONNECTED = eina_error_msg_static_register("Transport endpoint is not connected");
+ EFL_NET_ERROR_CANCELED = eina_error_msg_static_register("Operation Canceled");
+
/* TODO Remember return value, if it fails, use gethostbyname() */
ecore_con_socks_init();
ecore_con_ssl_init();
@@ -2984,3 +2992,465 @@ _ecore_con_lookup_done(void *data,
#include "efl_network_client.eo.c"
#include "efl_network_server.eo.c"
#include "efl_network_connector.eo.c"
+
+EOLIAN static void
+_efl_net_socket_adopt(Eo *obj, Efl_Net_Socket_Data *skt, int fd)
+{
+ EO_CONSTRUCTOR_CHECK_RETURN(obj);
+ skt->fd = fd;
+}
+
+EOLIAN static Eina_Error
+_efl_net_socket_send(Eo *obj, Efl_Net_Socket_Data *skt, const uint8_t *data, size_t size)
+{
+ EINA_SAFETY_ON_TRUE_RETURN_VAL(skt->fd < 0, EFL_NET_ERROR_NOT_CONNECTED);
+ EINA_SAFETY_ON_FALSE_RETURN_VAL(eo_finalized_get(obj), EFL_NET_ERROR_NOT_CONNECTED);
+
+ if (skt->buffer.send_size)
+ {
+ size_t used = eina_binbuf_length_get(skt->buffer.send);
+
+ if (skt->buffer.send_size < size + used)
+ {
+ DBG("using a limited send buffer=%zd, already used=%zd, size=%zd, required=%zd",
+ skt->buffer.send_size, used, size,
+ size + used - skt->buffer.send_size);
+ return EFL_NET_ERROR_NO_SPACE;
+ }
+ }
+
+ if (!eina_binbuf_append_length(skt->buffer.send, data, size))
+ {
+ DBG("could not queue data to be sent, buffer usage=%zd, size=%zd",
+ eina_binbuf_length_get(skt->buffer.send), size);
+ return EFL_NET_ERROR_NO_SPACE;
+ }
+
+ // TODO: monitor ECORE_FD_WRITE and send data
+
+ return 0;
+}
+
+EOLIAN static Eina_Bool
+_efl_net_socket_flush(Eo *obj EINA_UNUSED, Efl_Net_Socket_Data *skt)
+{
+ EINA_SAFETY_ON_TRUE_RETURN_VAL(skt->fd < 0, EINA_FALSE);
+
+ // TODO: send data
+
+ return eina_binbuf_length_get(skt->buffer.send) == 0;
+}
+
+EOLIAN static void
+_efl_net_socket_close(Eo *obj, Efl_Net_Socket_Data *skt)
+{
+ EINA_SAFETY_ON_TRUE_RETURN(skt->fd < 0);
+
+ close(efl_net_socket_steal_fd(obj));
+
+ eo_event_callback_call(obj, EFL_NET_SOCKET_EVENT_CLOSED, NULL);
+}
+
+EOLIAN static int
+_efl_net_socket_steal_fd(Eo *obj EINA_UNUSED, Efl_Net_Socket_Data *skt)
+{
+ int fd = skt->fd;
+
+ EINA_SAFETY_ON_TRUE_RETURN_VAL(skt->fd < 0, -1);
+ skt->fd = -1;
+
+ if (skt->buffer.send)
+ {
+ eina_binbuf_free(skt->buffer.send);
+ skt->buffer.send = NULL;
+ }
+
+ if (skt->buffer.receive)
+ {
+ eina_binbuf_free(skt->buffer.receive);
+ skt->buffer.receive = NULL;
+ }
+
+ eina_stringshare_replace(&skt->address.local, NULL);
+ eina_stringshare_replace(&skt->address.remote, NULL);
+
+ if (skt->timeout.timer)
+ {
+ eo_del(skt->timeout.timer);
+ skt->timeout.timer = NULL;
+ }
+
+ if (skt->fd_handler)
+ {
+ eo_del(skt->fd_handler);
+ skt->fd_handler = NULL;
+ }
+
+ return fd;
+}
+
+EOLIAN static Eina_Stringshare *
+_efl_net_socket_address_local_get(Eo *obj EINA_UNUSED, Efl_Net_Socket_Data *skt)
+{
+ return skt->address.local;
+}
+
+EOLIAN static Eina_Stringshare *
+_efl_net_socket_address_remote_get(Eo *obj EINA_UNUSED, Efl_Net_Socket_Data *skt)
+{
+ return skt->address.remote;
+}
+
+EOLIAN static Efl_Net_Socket_Flags
+_efl_net_socket_flags_get(Eo *obj EINA_UNUSED, Efl_Net_Socket_Data *skt)
+{
+ return skt->flags;
+}
+
+EOLIAN static void
+_efl_net_socket_flags_set(Eo *obj EINA_UNUSED, Efl_Net_Socket_Data *skt, Efl_Net_Socket_Flags flags)
+{
+ if (skt->flags == flags) return;
+
+ skt->flags = flags;
+ if (skt->fd < 0) return;
+
+ /* if we're adopting a socket, its flags were already set */
+ if (!eo_finalized_get(obj)) return;
+
+ // TODO: setsockopt()
+}
+
+EOLIAN static Eina_Bool
+_efl_net_socket_connected_get(Eo *obj EINA_UNUSED, Efl_Net_Socket_Data *skt)
+{
+ // TODO: efl_net_dialer to override and check if it's still connecting (resolving names, etc)
+ return skt->fd >= 0;
+}
+
+EOLIAN static size_t
+_efl_net_socket_send_buffer_usage_get(Eo *obj EINA_UNUSED, Efl_Net_Socket_Data *skt)
+{
+ return eina_binbuf_length_get(skt->buffer.send);
+}
+
+EOLIAN static size_t
+_efl_net_socket_send_buffer_size_get(Eo *obj EINA_UNUSED, Efl_Net_Socket_Data *skt)
+{
+ return skt->buffer.send_size;
+}
+
+static Eina_Bool
+ _efl_net_socket_common_buffer_resize(Eina_Binbuf **p_buf, size_t bytes)
+{
+ if (*p_buf && bytes)
+ {
+ size_t len = eina_binbuf_length_get(*p_buf);
+ if (len > bytes)
+ {
+ unsigned char *old = eina_binbuf_string_steal(*p_buf);
+ unsigned char *mem;
+
+ WRN("shrinking buffer to %zd below data size %zd, crop!",
+ bytes, len);
+
+ mem = realloc(old, bytes + 1);
+ EINA_SAFETY_ON_NULL_GOTO(mem, realloc_failed);
+
+ mem[bytes] = 0;
+ old = mem;
+
+ eina_binbuf_free(*p_buf);
+ *p_buf = eina_binbuf_manage_new(mem, bytes, EINA_FALSE);
+ EINA_SAFETY_ON_NULL_GOTO(*p_buf, realloc_failed);
+
+ realloc_failed:
+ free(old);
+ return EINA_FALSE;
+ }
+ }
+
+ if (!*p_buf)
+ {
+ if (bytes == 0)
+ {
+ *p_buf = eina_binbuf_new();
+ EINA_SAFETY_ON_NULL_RETURN_VAL(*p_buf, EINA_FALSE);
+ return EINA_TRUE;
+ }
+ else
+ {
+ unsigned char *mem = malloc(bytes + 1);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(mem, EINA_FALSE);
+ *p_buf = eina_binbuf_manage_new(mem, bytes, EINA_FALSE);
+ EINA_SAFETY_ON_NULL_GOTO(*p_buf, binbuf_failed);
+ eina_binbuf_reset(*p_buf); /* reset len to zero */
+ return EINA_TRUE;
+
+ binbuf_failed:
+ free(mem);
+ return EINA_FALSE;
+ }
+ }
+
+ return EINA_TRUE;
+}
+
+EOLIAN static void
+_efl_net_socket_send_buffer_size_set(Eo *obj EINA_UNUSED, Efl_Net_Socket_Data *skt, size_t bytes)
+{
+ if (skt->buffer.send_size == bytes) return;
+
+ if (!_efl_net_socket_common_buffer_resize(&skt->buffer.send, bytes))
+ {
+ ERR("could not resize send buffer from %zd to %zd",
+ skt->buffer.send_size, bytes);
+ return;
+ }
+
+ skt->buffer.send_size = bytes;
+}
+
+EOLIAN static size_t
+_efl_net_socket_receive_buffer_size_get(Eo *obj EINA_UNUSED, Efl_Net_Socket_Data *skt)
+{
+ return skt->buffer.receive_size;
+}
+
+EOLIAN static void
+_efl_net_socket_receive_buffer_size_set(Eo *obj EINA_UNUSED, Efl_Net_Socket_Data *skt, size_t bytes)
+{
+ if (skt->buffer.receive_size == bytes) return;
+
+ if (!_efl_net_socket_common_buffer_resize(&skt->buffer.receive, bytes))
+ {
+ ERR("could not resize receive buffer from %zd to %zd",
+ skt->buffer.receive_size, bytes);
+ return;
+ }
+
+ skt->buffer.receive_size = bytes;
+}
+
+EOLIAN static double
+_efl_net_socket_timeout_get(Eo *obj EINA_UNUSED, Efl_Net_Socket_Data *skt)
+{
+ return skt->timeout.seconds;
+}
+
+static void
+ _efl_net_socket_timeout_expired(void *data, const Eo_Event *event EINA_UNUSED)
+{
+ Efl_Net_Socket *obj = data;
+
+ eo_event_callback_call(obj, EFL_NET_SOCKET_EVENT_TIMEDOUT, NULL);
+ efl_net_socket_close(obj);
+}
+
+EOLIAN static void
+_efl_net_socket_timeout_set(Eo *obj, Efl_Net_Socket_Data *skt, double seconds)
+{
+ skt->timeout.seconds = seconds;
+
+ if (skt->timeout.timer)
+ {
+ if (seconds > 0)
+ efl_loop_timer_interval_set(skt->timeout.timer, seconds);
+ else
+ {
+ eo_del(skt->timeout.timer);
+ skt->timeout.timer = NULL;
+ }
+ }
+ else if (seconds > 0)
+ {
+ skt->timeout.timer = eo_add(EFL_LOOP_TIMER_CLASS, obj,
+ efl_loop_timer_interval_set(eo_self, seconds),
+ eo_event_callback_add(eo_self, EFL_LOOP_TIMER_EVENT_TICK, _efl_net_socket_timeout_expired, obj));
+ }
+}
+
+EOLIAN static Eo_Base *
+_efl_net_socket_eo_base_constructor(Eo *obj, Efl_Net_Socket_Data *skt)
+{
+ skt->fd = -1;
+ skt->flags = EFL_NET_SOCKET_FLAGS_DEFAULTS;
+
+ return eo_constructor(eo_super(obj, EFL_NET_SOCKET_CLASS));
+}
+
+EOLIAN static void
+_efl_net_socket_eo_base_destructor(Eo *obj, Efl_Net_Socket_Data *skt)
+{
+ if (skt->fd) efl_net_socket_close(obj);
+
+ eo_destructor(eo_super(obj, EFL_NET_SOCKET_CLASS));
+}
+
+static void
+_efl_net_socket_on_error(void *data, const Eo_Event *event)
+{
+ Efl_Net_Socket *obj = data;
+
+ // TODO: event, find out error to report
+}
+
+
+static void
+_efl_net_socket_on_read(void *data, const Eo_Event *event)
+{
+ Efl_Net_Socket *obj = data;
+
+ // TODO: event
+
+ // TODO: it seems _check_fd_event_catcher_del is wrong, ++ instead of --.
+ // TODO: deleting the event handler won't remove the FD_READ flag!
+}
+
+EOLIAN static Eo *
+_efl_net_socket_eo_base_finalize(Eo *obj, Efl_Net_Socket_Data *skt)
+{
+ EINA_SAFETY_ON_TRUE_RETURN_VAL(skt->fd < 0, NULL);
+
+ if (!skt->buffer.send) {
+ if (!_efl_net_socket_common_buffer_resize(&skt->buffer.send, skt->buffer.send_size)) {
+ ERR("could not init send buffer");
+ return NULL;
+ }
+ }
+
+ if (!skt->buffer.receive) {
+ if (!_efl_net_socket_common_buffer_resize(&skt->buffer.receive, skt->buffer.receive_size)) {
+ ERR("could not init receive buffer");
+ return NULL;
+ }
+ }
+
+ // TODO: getsockname() and getpeername() if skt->address.local/remote are unset?
+
+ /* timeout defaults to zero, so if set timer will be created */
+
+ if (!skt->fd_handler)
+ {
+ skt->fd_handler = eo_add(EFL_LOOP_FD_CLASS, obj,
+ eo_event_callback_add(eo_self, EFL_LOOP_FD_EVENT_ERROR, _efl_net_socket_on_error, obj),
+ eo_event_callback_add(eo_self, EFL_LOOP_FD_EVENT_READ, _efl_net_socket_on_read, obj));
+ }
+
+ return eo_finalize(eo_super(obj, EFL_NET_SOCKET_CLASS));
+}
+
+EOLIAN static void
+_efl_net_server_adopt(Eo *obj, Efl_Net_Server_Data *svr, int fd)
+{
+ EO_CONSTRUCTOR_CHECK_RETURN(obj);
+ svr->fd = fd;
+}
+
+EOLIAN static void
+_efl_net_server_address_set(Eo *obj, Efl_Net_Server_Data *svr, const char *address)
+{
+ EO_CONSTRUCTOR_CHECK_RETURN(obj);
+ eina_stringshare_replace(&svr->address, address);
+}
+
+EOLIAN static Eina_Stringshare *
+_efl_net_server_address_get(Eo *obj EINA_UNUSED, Efl_Net_Server_Data *svr)
+{
+ return svr->address;
+}
+
+EOLIAN static Efl_Net_Socket_Flags
+_efl_net_server_flags_get(Eo *obj EINA_UNUSED, Efl_Net_Server_Data *svr)
+{
+ return svr->flags;
+}
+
+EOLIAN static void
+_efl_net_server_flags_set(Eo *obj EINA_UNUSED, Efl_Net_Server_Data *svr, Efl_Net_Socket_Flags flags)
+{
+ if (svr->flags == flags) return;
+
+ svr->flags = flags;
+ if (svr->fd < 0) return;
+
+ /* if we're adopting a socket, its flags were already set */
+ if (!eo_finalized_get(obj)) return;
+
+ // TODO: setsockopt()
+}
+
+EOLIAN static void
+_efl_net_server_clients_limit_set(Eo *obj EINA_UNUSED, Efl_Net_Server_Data *svr, unsigned int limit, Eina_Bool reject_excess)
+{
+ svr->clients.limit = limit;
+ svr->clients.reject_excess = reject_excess;
+}
+
+EOLIAN static void
+_efl_net_server_clients_limit_get(Eo *obj EINA_UNUSED, Efl_Net_Server_Data *svr, unsigned int *limit, Eina_Bool *reject_excess)
+{
+ if (limit) *limit = svr->clients.limit;
+ if (reject_excess) *reject_excess = svr->clients.reject_excess;
+}
+
+EOLIAN static double
+_efl_net_server_clients_timeout_get(Eo *obj EINA_UNUSED, Efl_Net_Server_Data *svr)
+{
+ return svr->clients.timeout;
+}
+
+EOLIAN static void
+_efl_net_server_clients_timeout_set(Eo *obj EINA_UNUSED, Efl_Net_Server_Data *svr, double seconds)
+{
+ svr->clients.timeout = seconds;
+}
+
+EOLIAN static Eo_Base *
+_efl_net_server_eo_base_constructor(Eo *obj, Efl_Net_Server_Data *svr)
+{
+ svr->fd = -1;
+ svr->flags = EFL_NET_SOCKET_FLAGS_DEFAULTS;
+
+ return eo_constructor(eo_super(obj, EFL_NET_SERVER_CLASS));
+}
+
+EOLIAN static void
+_efl_net_server_eo_base_destructor(Eo *obj, Efl_Net_Server_Data *svr)
+{
+ close(svr->fd);
+ svr->fd = -1;
+
+ eina_stringshare_replace(&svr->address, NULL);
+
+ if (svr->fd_handler)
+ {
+ eo_del(svr->fd_handler);
+ svr->fd_handler = NULL;
+ }
+
+ eo_destructor(eo_super(obj, EFL_NET_SERVER_CLASS));
+}
+
+EOLIAN static Eo *
+_efl_net_server_eo_base_finalize(Eo *obj, Efl_Net_Server_Data *svr)
+{
+ EINA_SAFETY_ON_TRUE_RETURN_VAL(svr->fd < 0, NULL);
+
+ // TODO: listen/bind, register fd handler
+
+ return eo_finalize(eo_super(obj, EFL_NET_SERVER_CLASS));
+}
+
+#include "efl_net_dialer.eo.c"
+#include "efl_net_dialer_tcp.eo.c"
+#include "efl_net_dialer_udp.eo.c"
+#include "efl_net_dialer_unix.eo.c"
+#include "efl_net_server.eo.c"
+#include "efl_net_server_tcp.eo.c"
+#include "efl_net_server_udp.eo.c"
+#include "efl_net_server_unix.eo.c"
+#include "efl_net_socket.eo.c"
+#include "efl_net_socket_tcp.eo.c"
+#include "efl_net_socket_udp.eo.c"
+#include "efl_net_socket_unix.eo.c"
diff --git a/src/lib/ecore_con/ecore_con_private.h b/src/lib/ecore_con/ecore_con_private.h
index 331e4926ba..a6718da832 100644
--- a/src/lib/ecore_con/ecore_con_private.h
+++ b/src/lib/ecore_con/ecore_con_private.h
@@ -207,6 +207,44 @@ struct _Efl_Network_Server_Data
typedef struct _Efl_Network_Server_Data Efl_Network_Server_Data;
+typedef struct _Efl_Net_Socket_Data {
+ struct {
+ Eina_Binbuf *send; // TODO: if Eina_Blob, change to list + offset within the first element + send_usage accounting
+ Eina_Binbuf *receive;
+ size_t receive_size;
+ size_t send_size;
+ } buffer;
+ struct {
+ Eina_Stringshare *local;
+ Eina_Stringshare *remote;
+ } address;
+ struct {
+ Efl_Loop_Timer *timer;
+ double seconds;
+ } timeout;
+ Efl_Loop_Fd *fd_handler;
+ int fd;
+ Efl_Net_Socket_Flags flags;
+} Efl_Net_Socket_Data;
+
+typedef struct _Efl_Net_Server_Data {
+ Eina_Stringshare *address;
+ Efl_Loop_Fd *fd_handler;
+ int fd;
+ Efl_Net_Socket_Flags flags;
+ struct {
+ double timeout;
+ unsigned int limit;
+ unsigned int count;
+ Eina_Bool reject_excess;
+ } clients;
+} Efl_Net_Server_Data;
+
+typedef struct _Efl_Net_Dialer_Data {
+ Eina_Stringshare *address_connected;
+} Efl_Net_Dialer_Data;
+
+
struct _Ecore_Con_Info
{
unsigned int size;
diff --git a/src/lib/ecore_con/efl_net_dialer.eo b/src/lib/ecore_con/efl_net_dialer.eo
new file mode 100644
index 0000000000..5d789d9d47
--- /dev/null
+++ b/src/lib/ecore_con/efl_net_dialer.eo
@@ -0,0 +1,39 @@
+mixin Efl.Net.Dialer (Eo.Interface) {
+ [[The Dialer creates a client socket that establishes a network
+ connection to a remote server.
+
+ Use specific dialer for each connection type, such as
+ @Efl.Net.Dialer.UDP, @Efl.Net.Dialer.TCP or
+ @Efl.Net.Dialer.Unix.
+
+ @since 1.19
+ ]]
+
+ events {
+ resolved: string; [[The connecting address was resolved.
+
+ This event is dispatched if a name
+ resolution was needed, after the name was
+ resolved and before the connection is
+ established.
+
+ It is the one to be reported in
+ @Efl.Net.Socket.address_remote. The
+ original address is reported in
+ @.address_connected.
+ ]]
+ }
+
+ methods {
+ @property address_connected {
+ [[The address used to create and connect this socket. It
+ is the unresolved address, see the property address_remote
+ for the actual resolved address]]
+ get @virtual_pure {
+ }
+ values {
+ address: string;
+ }
+ }
+ }
+}
diff --git a/src/lib/ecore_con/efl_net_dialer_tcp.eo b/src/lib/ecore_con/efl_net_dialer_tcp.eo
new file mode 100644
index 0000000000..344f108f7e
--- /dev/null
+++ b/src/lib/ecore_con/efl_net_dialer_tcp.eo
@@ -0,0 +1,3 @@
+class Efl.Net.Dialer.TCP (Efl.Net.Socket.TCP, Efl.Net.Dialer) {
+ data: null;
+}
diff --git a/src/lib/ecore_con/efl_net_dialer_udp.eo b/src/lib/ecore_con/efl_net_dialer_udp.eo
new file mode 100644
index 0000000000..18036f995f
--- /dev/null
+++ b/src/lib/ecore_con/efl_net_dialer_udp.eo
@@ -0,0 +1,3 @@
+class Efl.Net.Dialer.UDP (Efl.Net.Socket.UDP, Efl.Net.Dialer) {
+ data: null;
+}
diff --git a/src/lib/ecore_con/efl_net_dialer_unix.eo b/src/lib/ecore_con/efl_net_dialer_unix.eo
new file mode 100644
index 0000000000..9b0cb57155
--- /dev/null
+++ b/src/lib/ecore_con/efl_net_dialer_unix.eo
@@ -0,0 +1,5 @@
+class Efl.Net.Dialer.Unix (Efl.Net.Socket.Unix, Efl.Net.Dialer) {
+ data: null;
+
+ /* TODO: abstract, user or system socket */
+}
diff --git a/src/lib/ecore_con/efl_net_server.eo b/src/lib/ecore_con/efl_net_server.eo
new file mode 100644
index 0000000000..ca932f8707
--- /dev/null
+++ b/src/lib/ecore_con/efl_net_server.eo
@@ -0,0 +1,114 @@
+import efl_net_types;
+
+class Efl.Net.Server (Efl.Loop_User) {
+ [[The Server will wait for incoming clients and establish their
+ connections, to be exposed as @Efl.Net.Socket reported by
+ \@ref EFL_NET_SERVER_CLIENT_ADD event.
+
+ To be notified on client disconnection, listen to Eo event "del"
+ on each connection.
+
+ @since 1.19
+ ]]
+
+ events {
+ client,add @hot: Efl.Net.Socket; [[New client was accepted]]
+ client,rejected: string; [[Notifies a client that was rejected.
+
+ See @.clients_limit property.
+ ]]
+ error: Eina.Error; [[Some error happened and the server needs to be stopped.
+
+ The error may be in either listen(2),
+ bind(2), accept(2) or socket becoming
+ invalid.
+
+ TODO: auto del the object?
+ ]]
+ }
+
+ methods {
+ adopt {
+ [[Constructor-only method that is used to initialize the new object owning a pre-existent file descriptor.
+
+ Usually servers are created from specific classes such
+ as @Efl.Net.Server.TCP, @Efl.Net.Server.UDP or
+ @Efl.Net.Server.Unix.
+
+ This method is useful if you receive the filedescriptor
+ from elsewhere, such as systemd or inetd socket
+ activation.
+ ]]
+ params {
+ @in fd: int; [[The Filedescriptor to adopt]]
+ }
+ }
+
+ @property address {
+ [[The local IP or unix-local address used to create and listen.
+
+ This will be used with bind(2) syscall. If an IP
+ connection, should specify the port after ":", such as
+ "0.0.0.0:80".
+ ]]
+ values {
+ address: string;
+ }
+ }
+
+ @property flags {
+ [[Bitwise OR of flags to used on sockets.
+
+ These flags are set on the server internal socket as
+ well as each newly created connection.
+ ]]
+ values {
+ flags: Efl.Net.Socket_Flags;
+ }
+ }
+
+ @property clients_limit {
+ [[Number of concurrent clients accepted by this server.
+
+ If reject_excess is set to true, then the
+ connection will be accepted and immediately closed.
+
+ If reject_excess is set to false (default), then
+ accept(2) won't be called and clients will be queued at
+ the kernel side, usually up to 4096 pending clients.
+
+ Whenever changed, this property will only apply to new
+ connections, that is, if the current connection count
+ alredy exceeds the limit, no connections will be
+ closed.
+ ]]
+ values {
+ limit: uint;
+ reject_excess: bool @optional;
+ }
+ }
+
+ @property clients_timeout {
+ [[Timeout (in seconds) to be set on each new client.
+
+ This property affects only new clients and is not
+ applicable to the server's internal file descriptor used
+ to bind(2) and accept(2) new connections.
+ ]]
+ values {
+ timeout: double;
+ }
+ }
+ }
+
+ implements {
+ Eo.Base.constructor;
+ Eo.Base.destructor;
+ Eo.Base.finalize;
+ // TODO: needed for Loop_User? Eo.Base.parent.set;
+ }
+
+ constructors {
+ .adopt;
+ }
+}
diff --git a/src/lib/ecore_con/efl_net_server_tcp.eo b/src/lib/ecore_con/efl_net_server_tcp.eo
new file mode 100644
index 0000000000..3efb72f19f
--- /dev/null
+++ b/src/lib/ecore_con/efl_net_server_tcp.eo
@@ -0,0 +1,3 @@
+class Efl.Net.Server.TCP (Efl.Net.Server) {
+ data: null;
+}
diff --git a/src/lib/ecore_con/efl_net_server_udp.eo b/src/lib/ecore_con/efl_net_server_udp.eo
new file mode 100644
index 0000000000..7bf0ee4245
--- /dev/null
+++ b/src/lib/ecore_con/efl_net_server_udp.eo
@@ -0,0 +1,3 @@
+class Efl.Net.Server.UDP (Efl.Net.Server) {
+ data: null;
+}
diff --git a/src/lib/ecore_con/efl_net_server_unix.eo b/src/lib/ecore_con/efl_net_server_unix.eo
new file mode 100644
index 0000000000..bd15be5a40
--- /dev/null
+++ b/src/lib/ecore_con/efl_net_server_unix.eo
@@ -0,0 +1,5 @@
+class Efl.Net.Server.Unix (Efl.Net.Server) {
+ data: null;
+
+ /* TODO: abstract, user or system socket */
+}
diff --git a/src/lib/ecore_con/efl_net_socket.eo b/src/lib/ecore_con/efl_net_socket.eo
new file mode 100644
index 0000000000..c1643cb46c
--- /dev/null
+++ b/src/lib/ecore_con/efl_net_socket.eo
@@ -0,0 +1,286 @@
+import efl_net_types;
+
+// TODO: was using Eina.Binbuf, but asked to change to simpler types
+struct Efl.Net.Socket.Receive_Event {
+ data: const(uint8) *;
+ size: size;
+}
+
+struct Efl.Net.Socket.Sent_Event {
+ //blob: Eina.Blob; [[blob with data given to @.send]]
+ size: size; [[size that was sent, if no error, matches blob's size, otherwise states partial operation.]]
+ error: Eina.Error; [[if operation failed, notifies the error that happened]]
+}
+
+class Efl.Net.Socket (Efl.Loop_User) {
+ [[Abstract class representing a network connection socket.
+
+ A base socket is an already established connection and can send
+ data (see @.send method), as well as notify about incoming data
+ (see \@ref EFL_NET_SOCKET_EVENT_RECEIVED event).
+
+ When the connection is closed, the socket will be automatically
+ deleted and the Eo event "del" is to be used.
+
+ @since 1.19
+ ]]
+
+ events {
+ //received @hot: Eina.Binbuf; [[Data is available to read,
+ received @hot: Efl.Net.Socket.Receive_Event; [[
+
+ TODO: Only when using Eina.Binbuf:
+
+ Data is available to read,
+ if consumed use eina_binbuf_remove().
+
+ To implement input flow control
+ used a fixed size receive buffer
+ (@.receive_buffer_size). When
+ the buffer size is fully filled,
+ the socket will stop reading
+ more data.
+ ]]
+ sent: Efl.Net.Socket.Sent_Event; [[Data specified by blob was sent.]]
+
+ drained; [[All pending data was sent.
+
+ This event is to implement flow control. To
+ avoid saturating the socket, listen this event
+ and only send more data once it's emitted.
+
+ Alternatively one can specify
+ @.send_buffer_size and check for @.send return
+ code, if it's "no space left", queue locally
+ the data, stop producing more and wait for
+ \@ref EFL_NET_SOCKET_EVENT_SENT
+ event in order to resume it.
+ ]]
+
+ closed; [[Socket was closed]]
+ timedout; [[Socket was inactive and timed out]] /* maybe just use error event for that? */
+
+ error: Eina.Error; [[An error occurred and the socket will be closed.
+
+ The error may be in either read, send or
+ socket becoming invalid (remote peer
+ closed the connection). If it's on send,
+ then it will be informed also on
+ \@ref EFL_NET_SOCKET_EVENT_SENT
+ event.
+
+ TODO: auto del the object?
+ ]]
+ }
+
+ methods {
+ adopt {
+ [[Constructor-only method that is used to initialize the new object owning a pre-existent file descriptor.
+
+ Usually sockets are created from a specific dialer, such
+ as @Efl.Net.Dialer.TCP, @Efl.Net.Dialer.UDP or
+ @Efl.Net.Dialer.Unix, or internally from a server such
+ as @Efl.Net.Server.TCP, @Efl.Net.Server.UDP or
+ @Efl.Net.Server.Unix.
+
+ This method is useful if you receive the filedescriptor
+ from elsewhere.
+ ]]
+ params {
+ @in fd: int; [[The Filedescriptor to adopt]]
+ }
+ }
+
+ send {
+ [[Queue data to be sent to remote.
+
+ The API is asynchronous, thus data won't be immediately
+ sent to the kernel, instead it will be queued locally
+ and when the kernel can do a non-blocking operation,
+ then it will be dispatched.
+
+ The maximum amount of bytes to be sent is defined with
+ @.send_buffer_size, with current usage reported as
+ @.send_buffer_usage.
+
+ If @.send_buffer_usage and the new blob size exceeds
+ @.send_buffer_size, then an error (ENOSPC XXX TODO EINA_ERROR...)
+ is returned immediately, no references are taken to the
+ blob and no \@ref EFL_NET_SOCKET_EVENT_SENT
+ event will be dispatched for this blob.
+
+ If no error is returned, then a reference is kept to the
+ blob and once it's fully sent, the
+ \@ref EFL_NET_SOCKET_EVENT_SENT
+ event will be dispatched with size parameter matching
+ the blob's size and error will be zero. If some error
+ occurs during the send syscall operation, the
+ \@ref EFL_NET_SOCKET_EVENT_SENT
+ event will be dispatched with size parameter less then
+ blob's size and error will be non-zero. In all cases,
+ after the \@ref EFL_NET_SOCKET_EVENT_SENT event is
+ dispatched, the reference to the blob will be released.
+
+ If the object is deleted with pending blobs, the
+ \@ref EFL_NET_SOCKET_EVENT_SENT
+ event will be dispatched with an error notifying
+ cancellation (ECANCELED XXX TODO EINA_ERROR...)
+
+ When all pending blobs were fully sent, then
+ \@ref EFL_NET_SOCKET_EVENT_DRAINED
+ event is dispatched.
+ ]]
+ params {
+ //@in data: Eina.Blob; [[data to queue for sending]]
+ @in data: const(uint8) * @nonull;
+ @in size: size;
+ }
+
+ return: Eina.Error (0); [[0 on success,
+ ENOSPC XXX TODO EINA_ERROR if no space left]]
+ }
+
+ flush {
+ [[Try to send as much as data without blocking.
+
+ If all data was sent, then
+ \@ref EFL_NET_SOCKET_EVENT_DRAINED
+ event will be dispatched and true is returned.
+
+ If the kernel can't sent more data and the operation
+ would block waiting, then false is returned.
+
+ If really all data must be sent, the user should busy
+ wait based on @.send_buffer_usage and call @.flush in a
+ loop.
+ ]]
+ return: bool (false); [[true if all data was sent]]
+ }
+
+ close {
+ [[Closes the socket, discarding all read data and pending data to be sent.
+
+ If pending data was discarded, then
+ \@ref EFL_NET_SOCKET_EVENT_SENT
+ will be called with \@ref EFL_NET_ERROR_CANCELED.
+
+ If all data must be delivered prior to close, do it from
+ \@ref EFL_NET_SOCKET_EVENT_DRAINED
+ event callback.
+ ]]
+ }
+
+ steal_fd {
+ [[Steal the file descriptor and make this connection shallow.
+
+ A shallow socket can't do any real operation and must be
+ deleted.
+
+ XXX TODO: auto delete?
+ ]]
+ return: int (-1); [[the internal file descriptor]]
+ }
+
+ @property address_local {
+ [[The local IP or unix-local address.
+
+ This is analogous to getsockname(), it must return the
+ local address and port for IP connetions, the path for
+ unix socket.
+ ]]
+ get {
+ }
+ values {
+ address: string;
+ }
+ }
+
+ @property address_remote {
+ [[The remote IP or unix-local address.
+
+ This is Analogous to getpeername(), it must return the
+ remote (peer) address and port for IP connections, the
+ path for unix socket.
+
+ For IP addresses, the returned value is the final IP
+ address, so it's already resolved.
+ ]]
+ get {
+ }
+ values {
+ address: string;
+ }
+ }
+
+ @property flags {
+ [[Bitwise OR of flags to used on this socket.]]
+ values {
+ flags: Efl.Net.Socket_Flags;
+ }
+ }
+
+ @property connected {
+ [[If the socket is still connected.]]
+ get {
+ }
+ values {
+ connected: bool;
+ }
+ }
+
+ @property send_buffer_usage {
+ [[How many bytes are queued to be sent.
+
+ If @.send_buffer_size >= 0, then this must be <=
+ @.send_buffer_size.
+ ]]
+ get {
+ }
+ values {
+ bytes: size;
+ }
+ }
+
+ @property send_buffer_size {
+ [[Amount of bytes to use when sending data.
+ 0 is unlimited,
+ > 0 is an upper limit of queued bytes before
+ @.send returns ENOSPC]]
+ values {
+ bytes: size;
+ }
+ }
+
+ @property receive_buffer_size {
+ [[Amount of bytes to use when receiving data.
+ 0 is unlimited, > 0 is fixed size.]]
+ values {
+ bytes: size;
+ }
+ }
+
+ @property timeout {
+ [[Timeout (in seconds) to close the connection.
+
+ If nothing is sent or received until this amount of
+ seconds is elapsed, then the connection will be
+ automatically closed and the socket object will be
+ deleted.
+ ]]
+ values {
+ timeout: double;
+ }
+ }
+ }
+
+ implements {
+ Eo.Base.constructor;
+ Eo.Base.destructor;
+ Eo.Base.finalize;
+ // TODO: needed for Loop_User? Eo.Base.parent.set;
+ }
+
+ constructors {
+ .adopt;
+ }
+}
diff --git a/src/lib/ecore_con/efl_net_socket_tcp.eo b/src/lib/ecore_con/efl_net_socket_tcp.eo
new file mode 100644
index 0000000000..6c86600b8f
--- /dev/null
+++ b/src/lib/ecore_con/efl_net_socket_tcp.eo
@@ -0,0 +1,3 @@
+class Efl.Net.Socket.TCP (Efl.Net.Socket) {
+ data: null;
+}
diff --git a/src/lib/ecore_con/efl_net_socket_udp.eo b/src/lib/ecore_con/efl_net_socket_udp.eo
new file mode 100644
index 0000000000..810881dc98
--- /dev/null
+++ b/src/lib/ecore_con/efl_net_socket_udp.eo
@@ -0,0 +1,11 @@
+class Efl.Net.Socket.UDP (Efl.Net.Socket) {
+ data: null;
+
+ events {
+ /* TODO: received,packet received,msg */
+ }
+
+ methods {
+ /* TODO: sendto() and sendmsg() */
+ }
+}
diff --git a/src/lib/ecore_con/efl_net_socket_unix.eo b/src/lib/ecore_con/efl_net_socket_unix.eo
new file mode 100644
index 0000000000..ac3ae54da6
--- /dev/null
+++ b/src/lib/ecore_con/efl_net_socket_unix.eo
@@ -0,0 +1,24 @@
+class Efl.Net.Socket.Unix (Efl.Net.Socket) {
+ data: null;
+
+ events {
+ /* TODO: received,fd: int fd */
+ }
+
+ methods {
+ /* TODO: pass_fd(int fd) */
+
+ /* TODO:
+ @property credentials {
+ get {
+ }
+
+ values {
+ pid: uint64; [[The process identifier of remote peer]]
+ uid: uint64; [[The user identifier of remote peer]]
+ gid: uint64; [[The group identifier of the remote peer]]
+ }
+ }
+ */
+ }
+}
diff --git a/src/lib/ecore_con/efl_net_types.eot b/src/lib/ecore_con/efl_net_types.eot
new file mode 100644
index 0000000000..f8b4c9d1f5
--- /dev/null
+++ b/src/lib/ecore_con/efl_net_types.eot
@@ -0,0 +1,19 @@
+import eina_types;
+
+enum Efl.Net.Socket_Flags {
+ none = 0,
+ defaults = (Efl.Net.Socket_Flags.close_on_exec | Efl.Net.Socket_Flags.non_block),
+ close_on_exec = (1 << 0), [[set SOCK_CLOEXEC to mark socket to be closed when the process (or a child) call exec().]]
+ non_block = (1 << 1), [[set SOCK_NONBLOCK, send/receive may return EAGAIN.]]
+ keep_alive = (1 << 2), [[set SO_KEEPALIVE on connection oriented sockets.]]
+ reuse_address = (1 << 3), [[set SO_REUSEADDR ]]
+ reuse_port = (1 << 4), [[set SO_REUSEPORT (since Linux 3.9)]]
+ broadcast = (1 << 5), [[set SO_BROADCAST]]
+ multicast = (1 << 6), [[configure multicast. Uses IPPROTO_IP with IP_ADD_MEMBERSHIP]]
+ tcp_nodelay = (1 << 7), [[configure TCP connections with TCP_NODELAY]]
+ tcp_cork = (1 << 8), [[configure TCP connections with TCP_CORK to avoid sending partial frames.]]
+}
+
+var ERROR_NOT_CONNECTED: Eina.Error; // TODO: seems to not generate anything
+var ERROR_NO_SPACE: Eina.Error; // TODO: seems to not generate anything
+var ERROR_CANCELED: Eina.Error; // TODO: seems to not generate anything