summaryrefslogtreecommitdiff
path: root/src/lib/ecore_con
diff options
context:
space:
mode:
authorGustavo Sverzut Barbieri <barbieri@profusion.mobi>2017-03-22 04:29:16 -0300
committerGustavo Sverzut Barbieri <barbieri@profusion.mobi>2017-03-29 12:44:19 -0300
commitfa0e2865a1a53cd05298b8368fa9abdb7c83bff8 (patch)
treeec445601f6df26f124851b56dcde2b6a870f2684 /src/lib/ecore_con
parent17444557deab4f1ecb62e0fd03a7090a3a4bcbfd (diff)
implement efl_net_{socket,dialer,server}_windows
This is the local socket for windows, analogous to AF_UNIX. `Efl_Net_Socket_Windows` is the base class doing `ReadFile()` and `WriteFile()` using overlapped I/O, as well as the close procedure (`FlushFileBuffers()`, `DisconnectNamedPipe()` and `CloseHandle()`). These are done on top of an existing HANDLE that is set by `Efl_Net_Dialer_Windows` (from `CreateFile()`) or `Efl_Net_Server_Windows` (from `CreateNamedPipe()`). The overlapped I/O will return immediately, either with operation completed or `ERROR_IO_PENDING`, which means the kernel will execute that asynchronously and will later `SetEvent(overlapped.hEvent)` which is an event we wait on our main loop. That `overlapped` handle must exist during the call lifetime, thus cannot be bound to `pd`, as we may call `CancelIo()` but there is no guarantee the memory won't be touched, in that case we keep the overlapped around, but without an associated object. Windows provides no notification "can read without blocking" or non-blocking calls that returns partial data. The way to go is to use these overlapped I/O, with an initial `ReadFile()` to an internal buffer, once that operation finishes, we callback the user to says there is something to read (`efl_io_reader_can_read_set()`) and wait until `efl_io_reader_read()` is called to consume the available data, then `ReadFile()` is called again to read more data to the same internal buffer. Likewise, there is no "can write without blocking" or non-blocking calls that sends only partial data. The way to go is to get user bytes in `efl_io_writer_write()` and copy them in an internal buffer, then call `WriteFile()` on that and inform the user nothing else can be written until that operation completes (`efl_io_writer_can_write_set()`). This is cumbersome since we say we "sent" stuff when we actually didn't, it's still in our internal buffer (`pd->send.bytes`), but nonetheless the kernel and the other peer may be adding even more buffers, in this case we need to do a best effort to get it delivery. A particular case is troublesome: `write() -> close()`, this may result in `WriteFile()` pending, in this case we wait using `GetOverlappedResult()`, *this is nasty and may block*, but it's the only way I see to cope with such common use case. Other operations, like ongoing `ReadFile()` or `ConnectNamedPipe()` will be canceled using `CancelIo()`. Q: Why no I/O Completion Port (IOCP) was used? Why no CreateThreadpoolIo()? These perform much better! A: These will call back from secondary threads, but in EFL we must report back to the user in order to process incoming data or get more data to send. That is, we serialize everything to the main thread, making it impossible to use the benefits of IOCP and similar such as CreateThreadpoolIo(). Since we'd need to wakeup the main thread anyways, using `OVERLAPPED.hEvent` with `ecore_main_win32_handler_add()` does the job as we expect. Thanks to Vincent Torri (vtorri) for his help getting this code done with an example on how to do the NamedPipe handling on Windows.
Diffstat (limited to 'src/lib/ecore_con')
-rw-r--r--src/lib/ecore_con/Ecore_Con_Eo.h3
-rw-r--r--src/lib/ecore_con/ecore_con_legacy.c8
-rw-r--r--src/lib/ecore_con/ecore_con_private.h24
-rw-r--r--src/lib/ecore_con/efl_net_dialer_windows.c164
-rw-r--r--src/lib/ecore_con/efl_net_dialer_windows.eo20
-rw-r--r--src/lib/ecore_con/efl_net_server_windows.c508
-rw-r--r--src/lib/ecore_con/efl_net_server_windows.eo36
-rw-r--r--src/lib/ecore_con/efl_net_socket_windows.c929
-rw-r--r--src/lib/ecore_con/efl_net_socket_windows.eo26
9 files changed, 1717 insertions, 1 deletions
diff --git a/src/lib/ecore_con/Ecore_Con_Eo.h b/src/lib/ecore_con/Ecore_Con_Eo.h
index 03ade071de..9825a2b2a6 100644
--- a/src/lib/ecore_con/Ecore_Con_Eo.h
+++ b/src/lib/ecore_con/Ecore_Con_Eo.h
@@ -18,6 +18,9 @@
18#include "efl_net_server_tcp.eo.h" 18#include "efl_net_server_tcp.eo.h"
19 19
20#ifdef _WIN32 20#ifdef _WIN32
21#include "efl_net_socket_windows.eo.h"
22#include "efl_net_dialer_windows.eo.h"
23#include "efl_net_server_windows.eo.h"
21#else 24#else
22#include "efl_net_socket_unix.eo.h" 25#include "efl_net_socket_unix.eo.h"
23#include "efl_net_dialer_unix.eo.h" 26#include "efl_net_dialer_unix.eo.h"
diff --git a/src/lib/ecore_con/ecore_con_legacy.c b/src/lib/ecore_con/ecore_con_legacy.c
index 47c436b5f4..8cbb5967e6 100644
--- a/src/lib/ecore_con/ecore_con_legacy.c
+++ b/src/lib/ecore_con/ecore_con_legacy.c
@@ -1662,6 +1662,8 @@ ecore_con_server_add(Ecore_Con_Type compl_type,
1662 case ECORE_CON_LOCAL_ABSTRACT: 1662 case ECORE_CON_LOCAL_ABSTRACT:
1663#ifdef EFL_NET_SERVER_UNIX_CLASS 1663#ifdef EFL_NET_SERVER_UNIX_CLASS
1664 cls = EFL_NET_SERVER_UNIX_CLASS; 1664 cls = EFL_NET_SERVER_UNIX_CLASS;
1665#elif defined(EFL_NET_SERVER_WINDOWS_CLASS)
1666 cls = EFL_NET_SERVER_WINDOWS_CLASS;
1665#else 1667#else
1666 ERR("Your platform doesn't support Efl_Net_Server-compatible local communication"); 1668 ERR("Your platform doesn't support Efl_Net_Server-compatible local communication");
1667 // TODO: maybe write to a file and use TCP 1669 // TODO: maybe write to a file and use TCP
@@ -1845,7 +1847,9 @@ _ecore_con_server_dialer_set(Ecore_Con_Server *svr, Eo *dialer)
1845 (type == ECORE_CON_LOCAL_SYSTEM)) 1847 (type == ECORE_CON_LOCAL_SYSTEM))
1846 { 1848 {
1847 char *path = ecore_con_local_path_new(type == ECORE_CON_LOCAL_SYSTEM, svr->name, svr->port); 1849 char *path = ecore_con_local_path_new(type == ECORE_CON_LOCAL_SYSTEM, svr->name, svr->port);
1850#ifdef EFL_NET_DIALER_UNIX_CLASS
1848 struct stat st; 1851 struct stat st;
1852#endif
1849 1853
1850 if (!path) 1854 if (!path)
1851 { 1855 {
@@ -1858,6 +1862,7 @@ _ecore_con_server_dialer_set(Ecore_Con_Server *svr, Eo *dialer)
1858 free(path); 1862 free(path);
1859 } 1863 }
1860 1864
1865#ifdef EFL_NET_DIALER_UNIX_CLASS
1861 if ((stat(address, &st) != 0) 1866 if ((stat(address, &st) != 0)
1862#ifdef S_ISSOCK 1867#ifdef S_ISSOCK
1863 || (!S_ISSOCK(st.st_mode)) 1868 || (!S_ISSOCK(st.st_mode))
@@ -1867,6 +1872,7 @@ _ecore_con_server_dialer_set(Ecore_Con_Server *svr, Eo *dialer)
1867 DBG("%s is not a socket", address); 1872 DBG("%s is not a socket", address);
1868 return EINA_FALSE; 1873 return EINA_FALSE;
1869 } 1874 }
1875#endif
1870 } 1876 }
1871 1877
1872 if ((svr->type & ECORE_CON_NO_PROXY) == ECORE_CON_NO_PROXY) 1878 if ((svr->type & ECORE_CON_NO_PROXY) == ECORE_CON_NO_PROXY)
@@ -2095,6 +2101,8 @@ ecore_con_server_connect(Ecore_Con_Type compl_type,
2095 case ECORE_CON_LOCAL_ABSTRACT: 2101 case ECORE_CON_LOCAL_ABSTRACT:
2096#ifdef EFL_NET_DIALER_UNIX_CLASS 2102#ifdef EFL_NET_DIALER_UNIX_CLASS
2097 cls = EFL_NET_DIALER_UNIX_CLASS; 2103 cls = EFL_NET_DIALER_UNIX_CLASS;
2104#elif defined(EFL_NET_DIALER_WINDOWS_CLASS)
2105 cls = EFL_NET_DIALER_WINDOWS_CLASS;
2098#else 2106#else
2099 ERR("Your platform doesn't support Efl_Net_Dialer-compatible local communication"); 2107 ERR("Your platform doesn't support Efl_Net_Dialer-compatible local communication");
2100 // TODO: maybe write to a file and use TCP 2108 // TODO: maybe write to a file and use TCP
diff --git a/src/lib/ecore_con/ecore_con_private.h b/src/lib/ecore_con/ecore_con_private.h
index 42f6dedef7..b1842fc2e9 100644
--- a/src/lib/ecore_con/ecore_con_private.h
+++ b/src/lib/ecore_con/ecore_con_private.h
@@ -170,7 +170,29 @@ void _ecore_con_local_mkpath(const char *path, mode_t mode);
170void _efl_net_server_udp_client_init(Eo *client, SOCKET fd, const struct sockaddr *addr, socklen_t addrlen, const char *str); 170void _efl_net_server_udp_client_init(Eo *client, SOCKET fd, const struct sockaddr *addr, socklen_t addrlen, const char *str);
171void _efl_net_server_udp_client_feed(Eo *client, Eina_Rw_Slice slice); 171void _efl_net_server_udp_client_feed(Eo *client, Eina_Rw_Slice slice);
172 172
173#ifndef _WIN32 173#ifdef EFL_NET_SOCKET_WINDOWS_CLASS
174#define PIPE_NS "\\\\.\\pipe\\"
175char *_efl_net_windows_error_msg_get(DWORD win32err);
176Eina_Error _efl_net_socket_windows_init(Eo *o, HANDLE h);
177Eina_Error _efl_net_socket_windows_io_start(Eo *o);
178HANDLE _efl_net_socket_windows_handle_get(const Eo *o);
179
180typedef struct _Efl_Net_Socket_Windows_Operation Efl_Net_Socket_Windows_Operation;
181typedef Eina_Error (*Efl_Net_Socket_Windows_Operation_Success_Cb)(void *data, Eo *sock, DWORD used_size);
182typedef Eina_Error (*Efl_Net_Socket_Windows_Operation_Failure_Cb)(void *data, Eo *sock, DWORD win32err);
183
184Efl_Net_Socket_Windows_Operation *_efl_net_socket_windows_operation_new(Eo *sock, Efl_Net_Socket_Windows_Operation_Success_Cb success_cb, Efl_Net_Socket_Windows_Operation_Failure_Cb failure_cb, const void *data);
185Eina_Error _efl_net_socket_windows_operation_failed(Efl_Net_Socket_Windows_Operation *op, DWORD win32err);
186Eina_Error _efl_net_socket_windows_operation_succeeded(Efl_Net_Socket_Windows_Operation *op, DWORD used_size);
187
188static inline OVERLAPPED *
189_efl_net_socket_windows_operation_overlapped_get(Efl_Net_Socket_Windows_Operation *op)
190{
191 return (OVERLAPPED *)op;
192}
193#endif
194
195#ifdef EFL_NET_SOCKET_UNIX_CLASS
174Eina_Bool efl_net_unix_fmt(char *buf, size_t buflen, SOCKET fd, const struct sockaddr_un *addr, socklen_t addrlen); 196Eina_Bool efl_net_unix_fmt(char *buf, size_t buflen, SOCKET fd, const struct sockaddr_un *addr, socklen_t addrlen);
175#endif 197#endif
176Eina_Bool efl_net_ip_port_parse(const char *address, struct sockaddr_storage *storage); 198Eina_Bool efl_net_ip_port_parse(const char *address, struct sockaddr_storage *storage);
diff --git a/src/lib/ecore_con/efl_net_dialer_windows.c b/src/lib/ecore_con/efl_net_dialer_windows.c
new file mode 100644
index 0000000000..451c7e95fe
--- /dev/null
+++ b/src/lib/ecore_con/efl_net_dialer_windows.c
@@ -0,0 +1,164 @@
1#define EFL_NET_SOCKET_WINDOWS_PROTECTED 1
2#define EFL_IO_READER_PROTECTED 1
3#define EFL_IO_WRITER_PROTECTED 1
4#define EFL_IO_CLOSER_PROTECTED 1
5#define EFL_NET_DIALER_PROTECTED 1
6#define EFL_NET_SOCKET_PROTECTED 1
7
8#ifdef HAVE_CONFIG_H
9# include <config.h>
10#endif
11
12#include "Ecore.h"
13#include "Ecore_Con.h"
14#include "ecore_con_private.h"
15
16#define MY_CLASS EFL_NET_DIALER_WINDOWS_CLASS
17
18typedef struct _Efl_Net_Dialer_Windows_Data
19{
20 Eina_Stringshare *address_dial;
21 double timeout_dial;
22 Eina_Bool connected;
23} Efl_Net_Dialer_Windows_Data;
24
25EOLIAN static void
26_efl_net_dialer_windows_efl_object_destructor(Eo *o, Efl_Net_Dialer_Windows_Data *pd)
27{
28 efl_destructor(efl_super(o, MY_CLASS));
29
30 eina_stringshare_replace(&pd->address_dial, NULL);
31}
32
33EOLIAN static Eina_Error
34_efl_net_dialer_windows_efl_net_dialer_dial(Eo *o, Efl_Net_Dialer_Windows_Data *pd, const char *address)
35{
36 Eina_Error err;
37 HANDLE h;
38 char cstr[256], sstr[256];
39 ULONG cpid = 0, spid = 0;
40
41 EINA_SAFETY_ON_NULL_RETURN_VAL(address, EINVAL);
42 EINA_SAFETY_ON_TRUE_RETURN_VAL(strchr(address, '/') != NULL, EINVAL);
43 EINA_SAFETY_ON_TRUE_RETURN_VAL(strchr(address, '\\') != NULL, EINVAL);
44 EINA_SAFETY_ON_TRUE_RETURN_VAL(strlen(PIPE_NS) + strlen(address) >= 256, EINVAL);
45 EINA_SAFETY_ON_TRUE_RETURN_VAL(efl_net_dialer_connected_get(o), EISCONN);
46
47 efl_net_dialer_address_dial_set(o, address);
48
49 h = CreateFile(pd->address_dial,
50 FILE_READ_ATTRIBUTES | FILE_READ_DATA |
51 FILE_WRITE_ATTRIBUTES | FILE_WRITE_DATA,
52 FILE_SHARE_READ | FILE_SHARE_WRITE,
53 NULL, OPEN_EXISTING,
54 FILE_FLAG_OVERLAPPED, NULL);
55 if (h == INVALID_HANDLE_VALUE)
56 {
57 DWORD win32err = GetLastError();
58 switch (win32err)
59 {
60 case ERROR_ACCESS_DENIED: return EACCES;
61 case ERROR_INVALID_PARAMETER: return EINVAL;
62 case ERROR_SHARING_VIOLATION: return EINVAL;
63 case ERROR_FILE_EXISTS: return EEXIST;
64 case ERROR_FILE_NOT_FOUND: return ENOENT;
65 case ERROR_PIPE_BUSY: return EBUSY;
66 case ERROR_INVALID_NAME: return EADDRNOTAVAIL;
67 default:
68 {
69 char *msg = evil_format_message(win32err);
70 ERR("CreateFile(%s): Unexpected win32err=%lu (%s)", pd->address_dial, win32err, msg);
71 free(msg);
72 return EINVAL;
73 }
74 }
75 }
76
77 err = _efl_net_socket_windows_init(o, h);
78 if (err)
79 {
80 CloseHandle(h);
81 return err;
82 }
83
84#if _WIN32_WINNT >= 0x0600
85 if (!GetNamedPipeClientProcessId(_efl_net_socket_windows_handle_get(o), &cpid))
86 {
87 char *msg = evil_last_error_get();
88 WRN("server=%p (%s) could not GetNamedPipeClientProcessId(o): %s", o, address, msg);
89 free(msg);
90 }
91 if (!GetNamedPipeServerProcessId(_efl_net_socket_windows_handle_get(o), &spid))
92 {
93 char *msg = evil_last_error_get();
94 WRN("server=%p (%s) could not GetNamedPipeServerProcessId(o): %s", o, address, msg);
95 free(msg);
96 }
97#endif
98
99 if (cpid)
100 snprintf(cstr, sizeof(cstr), "%s:%lu", address, cpid);
101 else
102 eina_strlcpy(cstr, address, sizeof(cstr));
103
104 if (spid)
105 snprintf(sstr, sizeof(sstr), "%s:%lu", address, spid);
106 else
107 eina_strlcpy(sstr, address, sizeof(sstr));
108
109 efl_net_socket_address_remote_set(o, sstr);
110 efl_net_socket_address_local_set(o, cstr);
111 efl_event_callback_call(o, EFL_NET_DIALER_EVENT_RESOLVED, NULL);
112 efl_net_dialer_connected_set(o, EINA_TRUE);
113
114 return _efl_net_socket_windows_io_start(o);
115}
116
117EOLIAN static void
118_efl_net_dialer_windows_efl_net_dialer_address_dial_set(Eo *o EINA_UNUSED, Efl_Net_Dialer_Windows_Data *pd, const char *address)
119{
120 const char *tmp = eina_stringshare_printf(PIPE_NS "%s", address);
121 eina_stringshare_del(pd->address_dial);
122 pd->address_dial = tmp;
123}
124
125EOLIAN static const char *
126_efl_net_dialer_windows_efl_net_dialer_address_dial_get(Eo *o EINA_UNUSED, Efl_Net_Dialer_Windows_Data *pd)
127{
128 return pd->address_dial + strlen(PIPE_NS);
129}
130
131EOLIAN static void
132_efl_net_dialer_windows_efl_net_dialer_connected_set(Eo *o, Efl_Net_Dialer_Windows_Data *pd, Eina_Bool connected)
133{
134 if (pd->connected == connected) return;
135 pd->connected = connected;
136 if (connected) efl_event_callback_call(o, EFL_NET_DIALER_EVENT_CONNECTED, NULL);
137}
138
139EOLIAN static Eina_Bool
140_efl_net_dialer_windows_efl_net_dialer_connected_get(Eo *o EINA_UNUSED, Efl_Net_Dialer_Windows_Data *pd)
141{
142 return pd->connected;
143}
144
145EOLIAN static void
146_efl_net_dialer_windows_efl_net_dialer_timeout_dial_set(Eo *o EINA_UNUSED, Efl_Net_Dialer_Windows_Data *pd, double seconds)
147{
148 pd->timeout_dial = seconds;
149}
150
151EOLIAN static double
152_efl_net_dialer_windows_efl_net_dialer_timeout_dial_get(Eo *o EINA_UNUSED, Efl_Net_Dialer_Windows_Data *pd)
153{
154 return pd->timeout_dial;
155}
156
157EOLIAN static Eina_Error
158_efl_net_dialer_windows_efl_io_closer_close(Eo *o, Efl_Net_Dialer_Windows_Data *pd EINA_UNUSED)
159{
160 efl_net_dialer_connected_set(o, EINA_FALSE);
161 return efl_io_closer_close(efl_super(o, MY_CLASS));
162}
163
164#include "efl_net_dialer_windows.eo.c"
diff --git a/src/lib/ecore_con/efl_net_dialer_windows.eo b/src/lib/ecore_con/efl_net_dialer_windows.eo
new file mode 100644
index 0000000000..50a38b89b5
--- /dev/null
+++ b/src/lib/ecore_con/efl_net_dialer_windows.eo
@@ -0,0 +1,20 @@
1class Efl.Net.Dialer.Windows (Efl.Net.Socket.Windows, Efl.Net.Dialer) {
2 [[Connects to a Windows NamedPipe server.
3
4 The dial address will have "\\\\.\\pipe\\" prepended as required by
5 Windows CreateNamedPipe().
6
7 \@note Proxies are meaningless, thus are not implemented.
8
9 @since 1.19
10 ]]
11
12 implements {
13 Efl.Object.destructor;
14 Efl.Net.Dialer.dial; [[address parameter will have "\\\\.\\pipe\\" prepended]]
15 Efl.Net.Dialer.address_dial { get; set; }
16 Efl.Net.Dialer.connected { get; set; }
17 Efl.Net.Dialer.timeout_dial { get; set; }
18 Efl.Io.Closer.close;
19 }
20}
diff --git a/src/lib/ecore_con/efl_net_server_windows.c b/src/lib/ecore_con/efl_net_server_windows.c
new file mode 100644
index 0000000000..832782ca83
--- /dev/null
+++ b/src/lib/ecore_con/efl_net_server_windows.c
@@ -0,0 +1,508 @@
1#define EFL_NET_SERVER_WINDOWS_PROTECTED 1
2#define EFL_NET_SERVER_PROTECTED 1
3#define EFL_NET_SOCKET_PROTECTED 1
4
5#ifdef HAVE_CONFIG_H
6# include <config.h>
7#endif
8
9#include "Ecore.h"
10#include "Ecore_Con.h"
11#include "ecore_con_private.h"
12
13/*
14 * See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365601(v=vs.85).aspx
15 * Named Pipe Server Using Completion Routines
16 *
17 * See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365603(v=vs.85).aspx
18 * Named Pipe Server Using Overlapped I/O
19 *
20 * Each instance (PIPEINST) is an Efl_Net_Socket_Windows. Instead of
21 * pre-creating all possible instances and having all of them to
22 * accept connections (ConnectNamedPipe()), we create a single one
23 * (Efl_Net_Server_Windows_Data::next_client), once it's connected we
24 * announce the client and if it's used, a new "next_client" is
25 * started, otherwise if the announced client is not used, then it's
26 * disconnected and reused with a new ConnectNamedPipe().
27 */
28
29#define MY_CLASS EFL_NET_SERVER_WINDOWS_CLASS
30
31typedef struct _Efl_Net_Server_Windows_Data
32{
33 Eo *next_client;
34 Eina_List *pending_clients;
35
36 Eina_Stringshare *address; /* includes prefix: \\.\pipe\, returned without it */
37 Efl_Future *pending_announcer_job;
38 unsigned int clients_count;
39 unsigned int clients_limit;
40 Eina_Bool clients_reject_excess;
41 Eina_Bool serving;
42 Eina_Bool first;
43 Eina_Bool allow_remote;
44} Efl_Net_Server_Windows_Data;
45
46static Eina_Error _efl_net_server_windows_client_listen(Eo *o, Efl_Net_Server_Windows_Data *pd);
47static Eina_Error _efl_net_server_windows_client_new(Eo *o, Efl_Net_Server_Windows_Data *pd);
48
49static Eina_Error
50_efl_net_server_windows_client_listen_success(void *data, Eo *client, DWORD used_size EINA_UNUSED)
51{
52 Eo *o = data;
53 Efl_Net_Server_Windows_Data *pd = efl_data_scope_get(o, MY_CLASS);
54 char cstr[256], sstr[256];
55 ULONG cpid = 0, spid = 0;
56 const char *addr = efl_net_server_address_get(o);
57
58 EINA_SAFETY_ON_NULL_RETURN_VAL(pd, EINVAL);
59
60#if _WIN32_WINNT >= 0x0600
61 if (!GetNamedPipeClientProcessId(_efl_net_socket_windows_handle_get(client), &cpid))
62 {
63 char *msg = evil_last_error_get();
64 WRN("server=%p (%s) could not GetNamedPipeClientProcessId(client): %s", o, addr, msg);
65 free(msg);
66 }
67 if (!GetNamedPipeServerProcessId(_efl_net_socket_windows_handle_get(client), &spid))
68 {
69 char *msg = evil_last_error_get();
70 WRN("server=%p (%s) could not GetNamedPipeServerProcessId(client): %s", o, addr, msg);
71 free(msg);
72 }
73#endif
74
75 if (cpid)
76 snprintf(cstr, sizeof(cstr), "%s:%lu", addr, cpid);
77 else
78 eina_strlcpy(cstr, addr, sizeof(cstr));
79
80 if (spid)
81 snprintf(sstr, sizeof(sstr), "%s:%lu", addr, spid);
82 else
83 eina_strlcpy(sstr, addr, sizeof(sstr));
84
85 DBG("server=%p received incoming connection at %s<->%s", o, sstr, cstr);
86
87 efl_ref(o); /* will trigger events, which call user which may delete us */
88
89 if ((pd->clients_limit > 0) && (pd->clients_count >= pd->clients_limit))
90 {
91 if (!pd->clients_reject_excess)
92 {
93 /* keep queueing, but do not call user */
94
95 pd->pending_clients = eina_list_append(pd->pending_clients, client);
96 if (pd->next_client == client)
97 pd->next_client = NULL;
98
99 efl_net_socket_address_local_set(client, sstr);
100 efl_net_socket_address_remote_set(client, cstr);
101
102 DBG("server=%p queued client %p", o, client);
103 }
104 else
105 {
106 DBG("server=%p rejecting client %p", o, client);
107 efl_event_callback_call(o, EFL_NET_SERVER_EVENT_CLIENT_REJECTED, cstr);
108
109 if (pd->next_client != client)
110 efl_del(client);
111 else
112 {
113 HANDLE h = _efl_net_socket_windows_handle_get(client);
114 DisconnectNamedPipe(h);
115
116 /* reuse existing pipe for a new connection */
117 _efl_net_server_windows_client_listen(o, pd);
118 }
119 }
120 }
121 else
122 {
123 efl_net_socket_address_local_set(client, sstr);
124 efl_net_socket_address_remote_set(client, cstr);
125
126 DBG("server=%p announcing client %p", o, client);
127 if (pd->next_client == client)
128 pd->next_client = NULL;
129 efl_net_server_client_announce(o, client);
130 }
131
132 if (!pd->next_client)
133 _efl_net_server_windows_client_new(o, pd);
134
135 efl_unref(o);
136 return 0;
137}
138
139static Eina_Error
140_efl_net_server_windows_client_listen_failure(void *data, Eo *client EINA_UNUSED, DWORD win32err)
141{
142 Eo *o = data;
143 Eina_Error err = EINVAL;
144
145 if (eina_log_domain_level_check(_ecore_con_log_dom, EINA_LOG_LEVEL_DBG))
146 {
147 char *msg = _efl_net_windows_error_msg_get(win32err);
148 DBG("server=%p failed to accept connection at %s: error=%s",
149 o, efl_net_server_address_get(o), msg);
150 free(msg);
151 }
152
153 switch (win32err)
154 {
155 case ERROR_PIPE_CONNECTED: err = 0; break;
156 case ERROR_NO_DATA: err = ENOTCONN; break;
157 case ERROR_OPERATION_ABORTED: err = ECANCELED; break;
158 }
159
160 if (err)
161 efl_event_callback_call(o, EFL_NET_SERVER_EVENT_ERROR, &err);
162
163 // TODO: create a new one on failure?
164
165 return err;
166}
167
168static Eina_Error
169_efl_net_server_windows_client_listen(Eo *o, Efl_Net_Server_Windows_Data *pd)
170{
171 Efl_Net_Socket_Windows_Operation *op;
172 HANDLE h;
173 OVERLAPPED *ovl;
174 DWORD win32err;
175 BOOL r;
176
177 op = _efl_net_socket_windows_operation_new(pd->next_client,
178 _efl_net_server_windows_client_listen_success,
179 _efl_net_server_windows_client_listen_failure,
180 o);
181
182 EINA_SAFETY_ON_NULL_RETURN_VAL(op, EINVAL);
183
184 h = _efl_net_socket_windows_handle_get(pd->next_client);
185 ovl = _efl_net_socket_windows_operation_overlapped_get(op);
186
187 r = ConnectNamedPipe(h, ovl);
188 win32err = GetLastError();
189 if (eina_log_domain_level_check(_ecore_con_log_dom, EINA_LOG_LEVEL_DBG))
190 {
191 char *msg = _efl_net_windows_error_msg_get(win32err);
192 DBG("server=%p (%s) socket=%p ConnectNamedPipe(%p, %p)=%s",
193 o, efl_net_server_address_get(o),
194 pd->next_client, h, ovl, msg);
195 free(msg);
196 }
197
198 if (!r)
199 {
200 if (win32err == ERROR_IO_PENDING)
201 return 0;
202 else
203 {
204 return _efl_net_socket_windows_operation_failed(op, win32err);
205 }
206 }
207
208 return _efl_net_socket_windows_operation_succeeded(op, 0);
209}
210
211static Eina_Error
212_efl_net_server_windows_client_new(Eo *o, Efl_Net_Server_Windows_Data *pd)
213{
214 Eina_Error err;
215 HANDLE h;
216
217 h = CreateNamedPipe(pd->address,
218 PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED |
219 (pd->first ? FILE_FLAG_FIRST_PIPE_INSTANCE : 0),
220 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE |
221 (pd->allow_remote ? PIPE_ACCEPT_REMOTE_CLIENTS : PIPE_REJECT_REMOTE_CLIENTS),
222 (pd->clients_limit > 0 ? pd->clients_limit : PIPE_UNLIMITED_INSTANCES),
223 4096, 4096, INFINITE, NULL);
224 if (h == INVALID_HANDLE_VALUE)
225 {
226 DWORD win32err = GetLastError();
227 switch (win32err)
228 {
229 case ERROR_ACCESS_DENIED: return EACCES;
230 case ERROR_INVALID_PARAMETER: return EINVAL;
231 default:
232 {
233 char *msg = evil_format_message(win32err);
234 ERR("Unexpected win32err=%lu (%s)", win32err, msg);
235 return EINVAL;
236 }
237 }
238 }
239
240 pd->next_client = efl_add(EFL_NET_SOCKET_WINDOWS_CLASS, o,
241 efl_io_closer_close_on_destructor_set(efl_added, EINA_TRUE));
242 if (!pd->next_client)
243 {
244 err = ENOMEM;
245 goto error_socket;
246 }
247
248 err = _efl_net_socket_windows_init(pd->next_client, h);
249 if (err) goto error_init;
250
251 pd->first = EINA_FALSE;
252
253 err = _efl_net_server_windows_client_listen(o, pd);
254 if (err) return err;
255
256 efl_net_server_serving_set(o, EINA_TRUE);
257 return 0;
258
259 error_init:
260 efl_del(pd->next_client);
261 error_socket:
262 CloseHandle(h);
263 return err;
264}
265
266EOLIAN static void
267_efl_net_server_windows_allow_remote_set(Eo *o EINA_UNUSED, Efl_Net_Server_Windows_Data *pd, Eina_Bool allow_remote)
268{
269 pd->allow_remote = allow_remote;
270}
271
272EOLIAN static Eina_Bool
273_efl_net_server_windows_allow_remote_get(Eo *o EINA_UNUSED, Efl_Net_Server_Windows_Data *pd)
274{
275 return pd->allow_remote;
276}
277
278EOLIAN static Eo *
279_efl_net_server_windows_efl_object_constructor(Eo *o, Efl_Net_Server_Windows_Data *pd)
280{
281 pd->first = EINA_TRUE;
282
283 return efl_constructor(efl_super(o, MY_CLASS));
284}
285
286EOLIAN static void
287_efl_net_server_windows_efl_object_destructor(Eo *o, Efl_Net_Server_Windows_Data *pd)
288{
289 if (pd->next_client)
290 {
291 efl_del(pd->next_client);
292 pd->next_client = NULL;
293 }
294
295 while (pd->pending_clients)
296 {
297 Eo *client = pd->pending_clients->data;
298 pd->pending_clients = eina_list_remove_list(pd->pending_clients, pd->pending_clients);
299 efl_del(client);
300 }
301
302 efl_destructor(efl_super(o, MY_CLASS));
303}
304
305EOLIAN static void
306_efl_net_server_windows_efl_net_server_address_set(Eo *o EINA_UNUSED, Efl_Net_Server_Windows_Data *pd, const char *address)
307{
308 const char *tmp = eina_stringshare_printf(PIPE_NS "%s", address);
309 eina_stringshare_del(pd->address);
310 pd->address = tmp;
311}
312
313EOLIAN static const char *
314_efl_net_server_windows_efl_net_server_address_get(Eo *o EINA_UNUSED, Efl_Net_Server_Windows_Data *pd)
315{
316 return pd->address + strlen(PIPE_NS);
317}
318
319static void
320_efl_net_server_windows_pending_announce_job(void *data, const Efl_Event *ev EINA_UNUSED)
321{
322 Eo *o = data;
323 Efl_Net_Server_Windows_Data *pd = efl_data_scope_get(o, MY_CLASS);
324 Eo *client;
325
326 pd->pending_announcer_job = NULL;
327
328 if (!pd->pending_clients) return;
329 if ((pd->clients_limit > 0) && (pd->clients_limit <= pd->clients_count)) return;
330
331 client = pd->pending_clients->data;
332 pd->pending_clients = eina_list_remove_list(pd->pending_clients, pd->pending_clients);
333
334 efl_net_server_client_announce(o, client);
335}
336
337static void
338_efl_net_server_windows_pending_announce_job_schedule(Eo *o, Efl_Net_Server_Windows_Data *pd)
339{
340 Eo *loop;
341
342 if (pd->pending_announcer_job) return;
343 if (!pd->pending_clients) return;
344 if ((pd->clients_limit > 0) && (pd->clients_limit <= pd->clients_count)) return;
345
346 loop = efl_loop_get(o);
347 if (!loop) return;
348 efl_future_use(&pd->pending_announcer_job, efl_loop_job(loop, o));
349 efl_future_then(pd->pending_announcer_job, _efl_net_server_windows_pending_announce_job, NULL, NULL, o);
350 efl_future_link(o, pd->pending_announcer_job);
351}
352
353EOLIAN static void
354_efl_net_server_windows_efl_net_server_clients_count_set(Eo *o, Efl_Net_Server_Windows_Data *pd, unsigned int count)
355{
356 pd->clients_count = count;
357
358 /* a job to avoid blowing the stack with recursion,
359 * do each announcement from main loop
360 */
361 _efl_net_server_windows_pending_announce_job_schedule(o, pd);
362}
363
364EOLIAN static unsigned int
365_efl_net_server_windows_efl_net_server_clients_count_get(Eo *o EINA_UNUSED, Efl_Net_Server_Windows_Data *pd)
366{
367 return pd->clients_count;
368}
369
370EOLIAN static void
371_efl_net_server_windows_efl_net_server_clients_limit_set(Eo *o EINA_UNUSED, Efl_Net_Server_Windows_Data *pd, unsigned int limit, Eina_Bool reject_excess)
372{
373 pd->clients_limit = limit;
374 pd->clients_reject_excess = reject_excess;
375
376 if ((limit > 0) && (reject_excess))
377 {
378 while (pd->pending_clients)
379 {
380 Eina_List *last = eina_list_last(pd->pending_clients);
381 Eo *client = eina_list_data_get(last);
382 efl_del(client);
383 pd->pending_clients = eina_list_remove_list(pd->pending_clients, last);
384 }
385 }
386
387 _efl_net_server_windows_pending_announce_job_schedule(o, pd);
388}
389
390EOLIAN static void
391_efl_net_server_windows_efl_net_server_clients_limit_get(Eo *o EINA_UNUSED, Efl_Net_Server_Windows_Data *pd, unsigned int *limit, Eina_Bool *reject_excess)
392{
393 if (limit) *limit = pd->clients_limit;
394 if (reject_excess) *reject_excess = pd->clients_reject_excess;
395}
396
397EOLIAN static void
398_efl_net_server_windows_efl_net_server_serving_set(Eo *o EINA_UNUSED, Efl_Net_Server_Windows_Data *pd, Eina_Bool serving)
399{
400 if (pd->serving == serving) return;
401 pd->serving = serving;
402 if (serving)
403 efl_event_callback_call(o, EFL_NET_SERVER_EVENT_SERVING, NULL);
404}
405
406EOLIAN static Eina_Bool
407_efl_net_server_windows_efl_net_server_serving_get(Eo *o EINA_UNUSED, Efl_Net_Server_Windows_Data *pd)
408{
409 return pd->serving;
410}
411
412EOLIAN static Eina_Error
413_efl_net_server_windows_efl_net_server_serve(Eo *o, Efl_Net_Server_Windows_Data *pd, const char *address)
414{
415 EINA_SAFETY_ON_NULL_RETURN_VAL(address, EINVAL);
416 EINA_SAFETY_ON_TRUE_RETURN_VAL(strchr(address, '/') != NULL, EINVAL);
417 EINA_SAFETY_ON_TRUE_RETURN_VAL(strchr(address, '\\') != NULL, EINVAL);
418 EINA_SAFETY_ON_TRUE_RETURN_VAL(strlen(PIPE_NS) + strlen(address) >= 256, EINVAL);
419 EINA_SAFETY_ON_TRUE_RETURN_VAL(pd->serving, EALREADY);
420
421 efl_net_server_address_set(o, address);
422
423 return _efl_net_server_windows_client_new(o, pd);
424}
425
426static void
427_efl_net_server_windows_client_event_closed(void *data, const Efl_Event *event)
428{
429 Eo *server = data;
430 Eo *client = event->object;
431
432 efl_event_callback_del(client, EFL_IO_CLOSER_EVENT_CLOSED, _efl_net_server_windows_client_event_closed, server);
433 if (efl_parent_get(client) == server)
434 efl_parent_set(client, NULL);
435
436 efl_net_server_clients_count_set(server, efl_net_server_clients_count_get(server) - 1);
437}
438
439EOLIAN static Eina_Bool
440_efl_net_server_windows_efl_net_server_client_announce(Eo *o, Efl_Net_Server_Windows_Data *pd, Efl_Net_Socket *client)
441{
442 Eina_Error err;
443
444 EINA_SAFETY_ON_NULL_RETURN_VAL(client, EINA_FALSE);
445 EINA_SAFETY_ON_FALSE_GOTO(efl_isa(client, EFL_NET_SOCKET_INTERFACE), wrong_type);
446 EINA_SAFETY_ON_FALSE_GOTO(efl_parent_get(client) == o, wrong_parent);
447 efl_event_callback_call(o, EFL_NET_SERVER_EVENT_CLIENT_ADD, client);
448
449 if (efl_parent_get(client) != o)
450 {
451 DBG("client %s was reparented! Ignoring it...",
452 efl_net_socket_address_remote_get(client));
453 return EINA_TRUE;
454 }
455
456 if (efl_ref_get(client) == 1) /* users must take a reference themselves */
457 {
458 DBG("client %s was not handled, closing it...",
459 efl_net_socket_address_remote_get(client));
460 if (pd->next_client)
461 efl_del(client);
462 else
463 {
464 HANDLE h = _efl_net_socket_windows_handle_get(client);
465 DisconnectNamedPipe(h);
466
467 /* reuse existing pipe for a new connection */
468 pd->next_client = client;
469 _efl_net_server_windows_client_listen(o, pd);
470 }
471 return EINA_FALSE;
472 }
473 else if (efl_io_closer_closed_get(client))
474 {
475 DBG("client %s was closed from 'client,add', delete it...",
476 efl_net_socket_address_remote_get(client));
477 efl_del(client);
478 return EINA_FALSE;
479 }
480
481 efl_net_server_clients_count_set(o, efl_net_server_clients_count_get(o) + 1);
482 efl_event_callback_add(client, EFL_IO_CLOSER_EVENT_CLOSED, _efl_net_server_windows_client_event_closed, o);
483
484 err = _efl_net_socket_windows_io_start(client);
485 if (err)
486 {
487 WRN("server=%p client=%p failed to start I/O: %s", o, client, eina_error_msg_get(err));
488 if (!efl_io_closer_closed_get(client))
489 efl_io_closer_close(client);
490 }
491
492 return EINA_TRUE;
493
494 wrong_type:
495 ERR("%p client %p (%s) doesn't implement Efl.Net.Socket interface, deleting it.", o, client, efl_class_name_get(efl_class_get(client)));
496 efl_io_closer_close(client);
497 efl_del(client);
498 return EINA_FALSE;
499
500 wrong_parent:
501 ERR("%p client %p (%s) parent=%p is not our child, deleting it.", o, client, efl_class_name_get(efl_class_get(client)), efl_parent_get(client));
502 efl_io_closer_close(client);
503 efl_del(client);
504 return EINA_FALSE;
505
506}
507
508#include "efl_net_server_windows.eo.c"
diff --git a/src/lib/ecore_con/efl_net_server_windows.eo b/src/lib/ecore_con/efl_net_server_windows.eo
new file mode 100644
index 0000000000..31137efc39
--- /dev/null
+++ b/src/lib/ecore_con/efl_net_server_windows.eo
@@ -0,0 +1,36 @@
1class Efl.Net.Server.Windows (Efl.Loop_User, Efl.Net.Server) {
2 [[A Windows NamedPipe server.
3
4 The @Efl.Net.Server.serve method will call CreateNamedPipe()
5 directly, thus path will be accessed and created in that
6 method. If the created socket must be subject to some special
7 mode or user, change before executing that method.
8
9 @since 1.19
10 ]]
11
12 methods {
13 @property allow_remote {
14 [[If server allows remote (different machine) clients.
15
16 If this property is $true, then it will allow clients to
17 connect from remote machines. If $false (default), then
18 just local clients are allowed.
19 ]]
20 values {
21 allow_remote: bool; [[If $true, server will allow remote machines to connect.]]
22 }
23 }
24 }
25
26 implements {
27 Efl.Object.constructor;
28 Efl.Object.destructor;
29 Efl.Net.Server.address { get; set; }
30 Efl.Net.Server.clients_count { get; set; }
31 Efl.Net.Server.clients_limit { get; set; }
32 Efl.Net.Server.serving { get; set; }
33 Efl.Net.Server.serve; [[address parameter will have "\\\\.\\pipe\\" prepended]]
34 Efl.Net.Server.client_announce;
35 }
36}
diff --git a/src/lib/ecore_con/efl_net_socket_windows.c b/src/lib/ecore_con/efl_net_socket_windows.c
new file mode 100644
index 0000000000..02c4b05c90
--- /dev/null
+++ b/src/lib/ecore_con/efl_net_socket_windows.c
@@ -0,0 +1,929 @@
1#define EFL_NET_SOCKET_WINDOWS_PROTECTED 1
2#define EFL_IO_READER_PROTECTED 1
3#define EFL_IO_WRITER_PROTECTED 1
4#define EFL_IO_CLOSER_PROTECTED 1
5#define EFL_NET_SOCKET_PROTECTED 1
6
7#ifdef HAVE_CONFIG_H
8# include <config.h>
9#endif
10
11#include "Ecore.h"
12#include "Ecore_Con.h"
13#include "ecore_con_private.h"
14
15#define MY_CLASS EFL_NET_SOCKET_WINDOWS_CLASS
16
17#define BUFFER_SIZE (4 * 4096)
18
19typedef struct _Efl_Net_Socket_Windows_Data
20{
21 Eina_Stringshare *address_local;
22 Eina_Stringshare *address_remote;
23 Eina_List *pending_ops;
24 struct {
25 union {
26 uint8_t *bytes;
27 void *mem;
28 };
29 DWORD len;
30 DWORD used;
31 DWORD base;
32 Efl_Net_Socket_Windows_Operation *pending;
33 } recv;
34 struct {
35 union {
36 uint8_t *bytes;
37 void *mem;
38 };
39 DWORD len;
40 DWORD used;
41 Efl_Net_Socket_Windows_Operation *pending;
42 } send;
43 HANDLE handle;
44 Eina_Bool can_read;
45 Eina_Bool eos;
46 Eina_Bool pending_eos;
47 Eina_Bool can_write;
48 Eina_Bool io_started;
49 Eina_Bool close_on_exec;
50 Eina_Bool close_on_destructor;
51} Efl_Net_Socket_Windows_Data;
52
53struct _Efl_Net_Socket_Windows_Operation
54{
55 OVERLAPPED base;
56 Efl_Net_Socket_Windows_Operation_Success_Cb success_cb;
57 Efl_Net_Socket_Windows_Operation_Failure_Cb failure_cb;
58 const void *data;
59 Ecore_Win32_Handler *event_handler;
60 Eo *o;
61 Eina_Bool deleting;
62};
63
64/*
65 * differences to evil_format_message():
66 * - shorter string
67 * - no newline
68 * - fallback to error code if format fails
69 */
70char *
71_efl_net_windows_error_msg_get(DWORD win32err)
72{
73 LPTSTR msg;
74 char *str;
75 char *disp;
76 int len, reqlen;
77
78 if (!FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
79 NULL,
80 win32err,
81 0, /* Default language */
82 (LPTSTR)&msg,
83 0,
84 NULL))
85 goto fallback;
86
87#ifdef UNICODE
88 str = evil_wchar_to_char(msg);
89#else
90 str = msg;
91#endif /* UNICODE */
92
93 len = strlen(str);
94 if ((len > 0) && (str[len - 1] == '\n'))
95 str[--len] = '\0';
96 if ((len > 0) && (str[len - 1] == '\r'))
97 str[--len] = '\0';
98 if ((len > 0) && (str[len - 1] == '.'))
99 str[--len] = '\0';
100
101 reqlen = snprintf("", 0, "%lu(%s)", win32err, str);
102 if (reqlen < 1) goto error;
103 disp = malloc(reqlen + 1);
104 if (!disp) goto error;
105 snprintf(disp, reqlen + 1, "%lu(%s)", win32err, str);
106
107#ifdef UNICODE
108 free(str);
109#endif /* UNICODE */
110 LocalFree(msg);
111
112 return disp;
113
114 error:
115#ifdef UNICODE
116 free(str);
117#endif /* UNICODE */
118 LocalFree(msg);
119 fallback:
120 {
121 char buf[64];
122 snprintf(buf, sizeof(buf), "%ld", win32err);
123 return strdup(buf);
124 }
125}
126
127static void
128_efl_net_socket_windows_handle_close(HANDLE h)
129{
130 if (!FlushFileBuffers(h))
131 {
132 DWORD win32err = GetLastError();
133 if (win32err != ERROR_PIPE_NOT_CONNECTED)
134 {
135 char *msg = _efl_net_windows_error_msg_get(GetLastError());
136 WRN("HANDLE=%p could not flush buffers: %s", h, msg);
137 free(msg);
138 }
139 }
140 if (!DisconnectNamedPipe(h))
141 {
142 DWORD win32err = GetLastError();
143 if ((win32err != ERROR_NOT_SUPPORTED) && /* dialer socket don't support it */
144 (win32err != ERROR_PIPE_NOT_CONNECTED))
145 {
146 char *msg = _efl_net_windows_error_msg_get(win32err);
147 WRN("HANDLE=%p could not disconnect: %s", h, msg);
148 free(msg);
149 }
150 }
151 CloseHandle(h);
152 DBG("HANDLE=%p closed", h);
153}
154
155static Eina_Bool _efl_net_socket_windows_operation_event(void *, Ecore_Win32_Handler *wh);
156
157Efl_Net_Socket_Windows_Operation *
158_efl_net_socket_windows_operation_new(Eo *o, Efl_Net_Socket_Windows_Operation_Success_Cb success_cb, Efl_Net_Socket_Windows_Operation_Failure_Cb failure_cb, const void *data)
159{
160 Efl_Net_Socket_Windows_Operation *op;
161 Efl_Net_Socket_Windows_Data *pd = efl_data_scope_get(o, EFL_NET_SOCKET_WINDOWS_CLASS);
162
163 EINA_SAFETY_ON_NULL_RETURN_VAL(pd, NULL);
164 EINA_SAFETY_ON_NULL_RETURN_VAL(success_cb, NULL);
165 EINA_SAFETY_ON_NULL_RETURN_VAL(failure_cb, NULL);
166
167 op = calloc(1, sizeof(*op));
168 EINA_SAFETY_ON_NULL_RETURN_VAL(op, NULL);
169
170 op->base.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
171 if (!op->base.hEvent)
172 {
173 char *msg = _efl_net_windows_error_msg_get(GetLastError());
174 ERR("socket=%p success_cb=%p failure_cb=%p data=%p: error=%s",
175 op->o, op->success_cb, op->failure_cb, op->data, msg);
176 free(msg);
177 goto error_event;
178 }
179
180 op->event_handler = ecore_main_win32_handler_add(op->base.hEvent, _efl_net_socket_windows_operation_event, op);
181 if (!op->event_handler)
182 {
183 ERR("socket=%p success_cb=%p failure_cb=%p data=%p: could not create event handler",
184 op->o, op->success_cb, op->failure_cb, op->data);
185 goto error_handler;
186 }
187
188 op->success_cb = success_cb;
189 op->failure_cb = failure_cb;
190 op->data = data;
191 op->o = o;
192 pd->pending_ops = eina_list_append(pd->pending_ops, op);
193
194 DBG("op=%p (socket=%p) success_cb=%p failure_cb=%p data=%p",
195 op, op->o, op->success_cb, op->failure_cb, op->data);
196
197 return op;
198
199 error_handler:
200 CloseHandle(op->base.hEvent);
201 error_event:
202 free(op);
203 return NULL;
204}
205
206static Eina_Error
207_efl_net_socket_windows_operation_done(Efl_Net_Socket_Windows_Operation *op, DWORD win32err, DWORD used_size)
208{
209 Efl_Net_Socket_Windows_Data *pd;
210 Eina_Error err = 0;
211
212 if (eina_log_domain_level_check(_ecore_con_log_dom, EINA_LOG_LEVEL_DBG))
213 {
214 char *msg = _efl_net_windows_error_msg_get(win32err);
215 DBG("op=%p (socket=%p) success_cb=%p failure_cb=%p data=%p error=%s used_size=%lu",
216 op, op->o, op->success_cb, op->failure_cb, op->data, msg, used_size);
217 free(msg);
218 }
219
220 op->deleting = EINA_TRUE;
221
222 efl_ref(op->o);
223 pd = efl_data_scope_get(op->o, EFL_NET_SOCKET_WINDOWS_CLASS);
224 if (pd)
225 pd->pending_ops = eina_list_remove(pd->pending_ops, op);
226
227 if (win32err)
228 err = op->failure_cb((void *)op->data, op->o, win32err);
229 else
230 op->success_cb((void *)op->data, op->o, used_size);
231
232 if (op->event_handler)
233 {
234 if (WaitForSingleObject(pd->handle, 0) != WAIT_OBJECT_0)
235 {
236 DWORD used_size = 0;
237 if (GetOverlappedResult(pd->handle, &op->base, &used_size, FALSE))
238 {
239 DBG("op=%p (socket=%p) success_cb=%p failure_cb=%p data=%p GetOverlappedResult(%p, %p, &size=%lu, FALSE)",
240 op, op->o, op->success_cb, op->failure_cb, op->data, pd->handle, &op->base, used_size);
241 }
242 else
243 {
244 win32err = GetLastError();
245 if (eina_log_domain_level_check(_ecore_con_log_dom, EINA_LOG_LEVEL_DBG))
246 {
247 char *msg = _efl_net_windows_error_msg_get(win32err);
248 DBG("op=%p (socket=%p) success_cb=%p failure_cb=%p data=%p GetOverlappedResult(%p, %p, &size=%lu, TRUE)=%s",
249 op, op->o, op->success_cb, op->failure_cb, op->data, pd->handle, &op->base, used_size, msg);
250 free(msg);
251 }
252
253 if (win32err == ERROR_IO_INCOMPLETE)
254 {
255 DBG("op=%p (socket=%p) success_cb=%p failure_cb=%p data=%p: still pending I/O...",
256 op, op->o, op->success_cb, op->failure_cb, op->data);
257 efl_unref(op->o);
258 op->o = NULL;
259 return 0;
260 }
261 }
262 }
263
264 ecore_main_win32_handler_del(op->event_handler);
265 }
266
267#ifndef ERROR_HANDLES_CLOSED
268#define ERROR_HANDLES_CLOSED 676
269#endif
270 if ((win32err == ERROR_HANDLES_CLOSED) && !efl_io_closer_closed_get(op->o))
271 efl_io_closer_close(op->o);
272 efl_unref(op->o);
273
274 CloseHandle(op->base.hEvent);
275 free(op);
276 return err;
277}
278
279Eina_Error
280_efl_net_socket_windows_operation_failed(Efl_Net_Socket_Windows_Operation *op, DWORD win32err)
281{
282 EINA_SAFETY_ON_NULL_RETURN_VAL(op, EINVAL);
283 EINA_SAFETY_ON_TRUE_RETURN_VAL(op->deleting, EINVAL);
284
285 return _efl_net_socket_windows_operation_done(op, win32err, 0);
286}
287
288Eina_Error
289_efl_net_socket_windows_operation_succeeded(Efl_Net_Socket_Windows_Operation *op, DWORD used_size)
290{
291 EINA_SAFETY_ON_NULL_RETURN_VAL(op, EINVAL);
292 EINA_SAFETY_ON_TRUE_RETURN_VAL(op->deleting, EINVAL);
293
294 return _efl_net_socket_windows_operation_done(op, 0, used_size);
295}
296
297static Eina_Bool
298_efl_net_socket_windows_operation_event(void *data, Ecore_Win32_Handler *wh EINA_UNUSED)
299{
300 Efl_Net_Socket_Windows_Operation *op = data;
301 HANDLE h = _efl_net_socket_windows_handle_get(op->o);
302 DWORD used_size = 0;
303
304 if ((op->deleting) || (h == INVALID_HANDLE_VALUE))
305 {
306 DBG("op=%p was deleted and pending I/O completed!", op);
307 CloseHandle(op->base.hEvent);
308 free(op);
309 return ECORE_CALLBACK_CANCEL;
310 }
311
312 op->event_handler = NULL;
313
314 if (GetOverlappedResult(h, &op->base, &used_size, FALSE))
315 {
316 DBG("op=%p (socket=%p) success_cb=%p failure_cb=%p data=%p GetOverlappedResult(%p, %p, &size=%lu, FALSE)",
317 op, op->o, op->success_cb, op->failure_cb, op->data, h, &op->base, used_size);
318 _efl_net_socket_windows_operation_succeeded(op, used_size);
319 }
320 else
321 {
322 DWORD win32err = GetLastError();
323 if (eina_log_domain_level_check(_ecore_con_log_dom, EINA_LOG_LEVEL_DBG))
324 {
325 char *msg = _efl_net_windows_error_msg_get(win32err);
326 DBG("op=%p (socket=%p) success_cb=%p failure_cb=%p data=%p GetOverlappedResult(%p, %p, &size=%lu, FALSE)=%s",
327 op, op->o, op->success_cb, op->failure_cb, op->data, h, &op->base, used_size, msg);
328 free(msg);
329 }
330
331 _efl_net_socket_windows_operation_failed(op, win32err);
332 }
333
334 return ECORE_CALLBACK_CANCEL;
335}
336
337Eina_Error
338_efl_net_socket_windows_init(Eo *o, HANDLE h)
339{
340 Efl_Net_Socket_Windows_Data *pd = efl_data_scope_get(o, MY_CLASS);
341
342 EINA_SAFETY_ON_TRUE_RETURN_VAL(h == INVALID_HANDLE_VALUE, EINVAL);
343 EINA_SAFETY_ON_NULL_RETURN_VAL(pd, EINVAL);
344 EINA_SAFETY_ON_TRUE_RETURN_VAL(pd->handle != INVALID_HANDLE_VALUE, EALREADY);
345
346 pd->handle = h;
347
348 DBG("socket=%p adopted handle=%p", o, h);
349 return 0;
350}
351
352static Eina_Error _efl_net_socket_windows_recv(Eo *o, Efl_Net_Socket_Windows_Data *pd);
353
354static Eina_Error
355_efl_net_socket_windows_recv_success(void *data EINA_UNUSED, Eo *o, DWORD used_size)
356{
357 Efl_Net_Socket_Windows_Data *pd = efl_data_scope_get(o, MY_CLASS);
358
359 EINA_SAFETY_ON_NULL_RETURN_VAL(pd, EINVAL);
360
361 pd->recv.pending = NULL;
362 EINA_SAFETY_ON_TRUE_RETURN_VAL(pd->recv.used + used_size > pd->recv.len, EINVAL);
363
364 pd->recv.used += used_size;
365
366 /* calls back the user, may read()/write()/close() */
367 efl_io_reader_can_read_set(o, pd->recv.used > 0);
368
369 if (pd->handle == INVALID_HANDLE_VALUE) return 0;
370 if (pd->recv.used == pd->recv.len) return 0;
371 if (pd->recv.pending) return 0;
372
373 return _efl_net_socket_windows_recv(o, pd);
374}
375
376static Eina_Error
377_efl_net_socket_windows_recv_failure(void *data EINA_UNUSED, Eo *o, DWORD win32err)
378{
379 Efl_Net_Socket_Windows_Data *pd = efl_data_scope_get(o, MY_CLASS);
380
381 if (eina_log_domain_level_check(_ecore_con_log_dom, EINA_LOG_LEVEL_DBG))
382 {
383 char *msg = _efl_net_windows_error_msg_get(win32err);
384 DBG("socket=%p recv error=%s", o, msg);
385 free(msg);
386 }
387
388 pd->recv.pending = NULL;
389
390 if (pd->recv.used > 0)
391 pd->pending_eos = EINA_TRUE; /* eos when buffer drains */
392 else
393 {
394 efl_io_writer_can_write_set(o, EINA_FALSE);
395 efl_io_reader_can_read_set(o, EINA_FALSE);
396 efl_io_reader_eos_set(o, EINA_TRUE);
397 }
398
399 switch (win32err)
400 {
401 case ERROR_INVALID_USER_BUFFER: return EFAULT;
402 case ERROR_NOT_ENOUGH_MEMORY: return ENOMEM;
403 case ERROR_OPERATION_ABORTED: return ECANCELED;
404 case ERROR_BROKEN_PIPE: return EPIPE;
405 default: return EIO;
406 }
407}
408
409static Eina_Error
410_efl_net_socket_windows_recv(Eo *o, Efl_Net_Socket_Windows_Data *pd)
411{
412 Efl_Net_Socket_Windows_Operation *op;
413 OVERLAPPED *ovl;
414 DWORD used_size = 0, win32err;
415 BOOL r;
416
417 EINA_SAFETY_ON_TRUE_RETURN_VAL(pd->recv.pending != NULL, EINPROGRESS);
418
419 if (pd->handle == INVALID_HANDLE_VALUE) return EBADF;
420 if (pd->recv.len == 0) return ENOMEM;
421
422 if (pd->recv.base > 0)
423 {
424 DWORD todo = pd->recv.used - pd->recv.base;
425 if (todo > 0)
426 memmove(pd->recv.bytes, pd->recv.bytes + pd->recv.base, todo);
427 pd->recv.used -= pd->recv.base;
428 pd->recv.base = 0;
429 }
430
431 if (pd->recv.used == pd->recv.len) return ENOSPC;
432
433 op = _efl_net_socket_windows_operation_new(o,
434 _efl_net_socket_windows_recv_success,
435 _efl_net_socket_windows_recv_failure,
436 NULL);
437 EINA_SAFETY_ON_NULL_RETURN_VAL(op, EINVAL);
438 ovl = _efl_net_socket_windows_operation_overlapped_get(op);
439 r = ReadFile(pd->handle,
440 pd->recv.bytes + pd->recv.used,
441 pd->recv.len - pd->recv.used,
442 &used_size, ovl);
443 win32err = GetLastError();
444 if (eina_log_domain_level_check(_ecore_con_log_dom, EINA_LOG_LEVEL_DBG))
445 {
446 char *msg = _efl_net_windows_error_msg_get(win32err);
447 DBG("socket=%p ReadFile(%p, %p, %lu, &size=%lu, %p)=%s",
448 o, pd->handle, pd->recv.bytes + pd->recv.used,
449 pd->recv.len - pd->recv.used, used_size, ovl, msg);
450 free(msg);
451 }
452
453 if (!r)
454 {
455 if (win32err == ERROR_IO_PENDING)
456 {
457 pd->recv.pending = op;
458 return 0;
459 }
460 else
461 {
462 return _efl_net_socket_windows_operation_failed(op, win32err);
463 }
464 }
465
466 return _efl_net_socket_windows_operation_succeeded(op, used_size);
467}
468
469static Eina_Error _efl_net_socket_windows_send(Eo *o, Efl_Net_Socket_Windows_Data *pd);
470
471static Eina_Error
472_efl_net_socket_windows_send_success(void *data EINA_UNUSED, Eo *o, DWORD used_size)
473{
474 Efl_Net_Socket_Windows_Data *pd = efl_data_scope_get(o, MY_CLASS);
475
476 EINA_SAFETY_ON_NULL_RETURN_VAL(pd, EINVAL);
477
478 pd->send.pending = NULL;
479 EINA_SAFETY_ON_TRUE_RETURN_VAL(pd->send.used < used_size, EINVAL);
480
481 if (used_size > 0)
482 {
483 DWORD todo = pd->send.used - used_size;
484 if (todo > 0)
485 memmove(pd->send.bytes, pd->send.bytes + used_size, todo);
486 pd->send.used -= used_size;
487 }
488
489 /* calls back the user, may read()/write()/close() */
490 /* only can_write if we're fully done with previous request! */
491 efl_io_writer_can_write_set(o, pd->send.used == 0);
492
493 if (pd->handle == INVALID_HANDLE_VALUE) return 0;
494 if (pd->send.used == 0) return 0;
495 if (pd->send.pending) return 0;
496
497 return _efl_net_socket_windows_send(o, pd);
498}
499
500static Eina_Error
501_efl_net_socket_windows_send_failure(void *data EINA_UNUSED, Eo *o, DWORD win32err)
502{
503 Efl_Net_Socket_Windows_Data *pd = efl_data_scope_get(o, MY_CLASS);
504
505 EINA_SAFETY_ON_NULL_RETURN_VAL(pd, EINVAL);
506
507 if (eina_log_domain_level_check(_ecore_con_log_dom, EINA_LOG_LEVEL_DBG))
508 {
509 char *msg = _efl_net_windows_error_msg_get(win32err);
510 DBG("socket=%p send error=%s", o, msg);
511 free(msg);
512 }
513
514 pd->send.pending = NULL;
515
516 efl_io_writer_can_write_set(o, EINA_FALSE);
517
518 if (pd->recv.used > 0)
519 pd->pending_eos = EINA_TRUE; /* eos when buffer drains */
520 else
521 {
522 efl_io_reader_can_read_set(o, EINA_FALSE);
523 efl_io_reader_eos_set(o, EINA_TRUE);
524 }
525
526 switch (win32err)
527 {
528 case ERROR_INVALID_USER_BUFFER: return EFAULT;
529 case ERROR_NOT_ENOUGH_MEMORY: return ENOMEM;
530 case ERROR_OPERATION_ABORTED: return ECANCELED;
531 case ERROR_BROKEN_PIPE: return EPIPE;
532 default: return EIO;
533 }
534}
535
536static Eina_Error
537_efl_net_socket_windows_send(Eo *o, Efl_Net_Socket_Windows_Data *pd)
538{
539 Efl_Net_Socket_Windows_Operation *op;
540 OVERLAPPED *ovl;
541 DWORD used_size = 0, win32err;
542 BOOL r;
543
544 EINA_SAFETY_ON_TRUE_RETURN_VAL(pd->send.pending != NULL, EINPROGRESS);
545
546 if (pd->handle == INVALID_HANDLE_VALUE) return EBADF;
547 if (pd->send.used == 0) return 0;
548
549 op = _efl_net_socket_windows_operation_new(o,
550 _efl_net_socket_windows_send_success,
551 _efl_net_socket_windows_send_failure,
552 NULL);
553 EINA_SAFETY_ON_NULL_RETURN_VAL(op, EINVAL);
554
555 ovl = _efl_net_socket_windows_operation_overlapped_get(op);
556
557 r = WriteFile(pd->handle,
558 pd->send.bytes,
559 pd->send.used,
560 &used_size, ovl);
561 win32err = GetLastError();
562 if (eina_log_domain_level_check(_ecore_con_log_dom, EINA_LOG_LEVEL_DBG))
563 {
564 char *msg = _efl_net_windows_error_msg_get(win32err);
565 DBG("socket=%p WriteFile(%p, %p, %lu, &size=%lu, %p)=%s",
566 o, pd->handle, pd->send.bytes, pd->send.used, used_size, ovl, msg);
567 free(msg);
568 }
569
570 if (!r)
571 {
572 if (win32err == ERROR_IO_PENDING)
573 {
574 pd->send.pending = op;
575 return 0;
576 }
577 else
578 {
579 return _efl_net_socket_windows_operation_failed(op, win32err);
580 }
581 }
582
583 return _efl_net_socket_windows_operation_succeeded(op, used_size);
584}
585
586Eina_Error
587_efl_net_socket_windows_io_start(Eo *o)
588{
589 Efl_Net_Socket_Windows_Data *pd = efl_data_scope_get(o, MY_CLASS);
590 Eina_Error err;
591
592 EINA_SAFETY_ON_NULL_RETURN_VAL(pd, EINVAL);
593 EINA_SAFETY_ON_TRUE_RETURN_VAL(pd->io_started, EALREADY);
594
595 if (!pd->recv.mem)
596 {
597 pd->recv.mem = malloc(BUFFER_SIZE);
598 EINA_SAFETY_ON_NULL_RETURN_VAL(pd->recv.mem, ENOMEM);
599 pd->recv.len = BUFFER_SIZE;
600 pd->recv.used = 0;
601 }
602
603 if (!pd->send.mem)
604 {
605 pd->send.mem = malloc(BUFFER_SIZE);
606 EINA_SAFETY_ON_NULL_RETURN_VAL(pd->send.mem, ENOMEM);
607 pd->send.len = BUFFER_SIZE;
608 pd->send.used = 0;
609 }
610
611 DBG("socket=%p starting I/O...", o);
612 err = _efl_net_socket_windows_recv(o, pd);
613 if (err) return err;
614
615 efl_io_writer_can_write_set(o, EINA_TRUE);
616 pd->io_started = EINA_TRUE;
617 return 0;
618}
619
620HANDLE
621_efl_net_socket_windows_handle_get(const Eo *o)
622{
623 Efl_Net_Socket_Windows_Data *pd = efl_data_scope_get(o, MY_CLASS);
624 EINA_SAFETY_ON_NULL_RETURN_VAL(pd, INVALID_HANDLE_VALUE);
625 return pd->handle;
626}
627
628EOLIAN static Eo *
629_efl_net_socket_windows_efl_object_constructor(Eo *o, Efl_Net_Socket_Windows_Data *pd)
630{
631 pd->handle = INVALID_HANDLE_VALUE;
632
633 return efl_constructor(efl_super(o, MY_CLASS));
634}
635
636EOLIAN static void
637_efl_net_socket_windows_efl_object_destructor(Eo *o, Efl_Net_Socket_Windows_Data *pd)
638{
639 if (efl_io_closer_close_on_destructor_get(o) &&
640 (!efl_io_closer_closed_get(o)))
641 {
642 efl_event_freeze(o);
643 efl_io_closer_close(o);
644 efl_event_thaw(o);
645 }
646
647 efl_destructor(efl_super(o, MY_CLASS));
648
649 eina_stringshare_replace(&pd->address_local, NULL);
650 eina_stringshare_replace(&pd->address_remote, NULL);
651
652 free(pd->recv.mem);
653 pd->recv.mem = NULL;
654 pd->recv.len = 0;
655 pd->recv.used = 0;
656 pd->recv.base = 0;
657
658 free(pd->send.mem);
659 pd->send.mem = NULL;
660 pd->send.len = 0;
661 pd->send.used = 0;
662}
663
664EOLIAN static Eina_Error
665_efl_net_socket_windows_efl_io_closer_close(Eo *o, Efl_Net_Socket_Windows_Data *pd)
666{
667 HANDLE h;
668
669 EINA_SAFETY_ON_TRUE_RETURN_VAL(efl_io_closer_closed_get(o), EBADF);
670
671 DBG("socket=%p closing handle=%p", o, pd->handle);
672
673 if (pd->pending_ops)
674 {
675 if (pd->send.pending)
676 {
677 Efl_Net_Socket_Windows_Operation *op = pd->send.pending;
678 DWORD used_size = 0;
679 DBG("op=%p (socket=%p) success_cb=%p failure_cb=%p data=%p: wait %lu bytes pending write...",
680 op, op->o, op->success_cb, op->failure_cb, op->data, pd->send.used);
681 if (GetOverlappedResult(pd->handle, &op->base, &used_size, TRUE))
682 {
683 DBG("op=%p (socket=%p) success_cb=%p failure_cb=%p data=%p GetOverlappedResult(%p, %p, &size=%lu, TRUE)",
684 op, op->o, op->success_cb, op->failure_cb, op->data, pd->handle, &op->base, used_size);
685 }
686 else
687 {
688 DWORD win32err = GetLastError();
689 if (eina_log_domain_level_check(_ecore_con_log_dom, EINA_LOG_LEVEL_DBG))
690 {
691 char *msg = _efl_net_windows_error_msg_get(win32err);
692 DBG("op=%p (socket=%p) success_cb=%p failure_cb=%p data=%p GetOverlappedResult(%p, %p, &size=%lu, TRUE)=%s",
693 op, op->o, op->success_cb, op->failure_cb, op->data, pd->handle, &op->base, used_size, msg);
694 free(msg);
695 }
696 }
697 }
698 if (!FlushFileBuffers(pd->handle))
699 {
700 char *msg = _efl_net_windows_error_msg_get(GetLastError());
701 WRN("HANDLE=%p could not flush buffers: %s", pd->handle, msg);
702 free(msg);
703 }
704 if (CancelIo(pd->handle))
705 DBG("socket=%p CancelIo(%p)", o, pd->handle);
706 else if (eina_log_domain_level_check(_ecore_con_log_dom, EINA_LOG_LEVEL_DBG))
707 {
708 char *msg = _efl_net_windows_error_msg_get(GetLastError());
709 WRN("socket=%p CancelIo(%p)=%s", o, pd->handle, msg);
710 free(msg);
711 }
712 }
713
714 while (pd->pending_ops)
715 _efl_net_socket_windows_operation_failed(pd->pending_ops->data, ERROR_OPERATION_ABORTED);
716
717 efl_io_writer_can_write_set(o, EINA_FALSE);
718 efl_io_reader_can_read_set(o, EINA_FALSE);
719 efl_io_reader_eos_set(o, EINA_TRUE);
720
721 h = InterlockedExchangePointer(&pd->handle, INVALID_HANDLE_VALUE);
722 if (h != INVALID_HANDLE_VALUE)
723 _efl_net_socket_windows_handle_close(h);
724
725 efl_event_callback_call(o, EFL_IO_CLOSER_EVENT_CLOSED, NULL);
726
727 return 0;
728}
729
730EOLIAN static Eina_Bool
731_efl_net_socket_windows_efl_io_closer_closed_get(Eo *o EINA_UNUSED, Efl_Net_Socket_Windows_Data *pd)
732{
733 return pd->handle == INVALID_HANDLE_VALUE;
734}
735
736EOLIAN static Eina_Bool
737_efl_net_socket_windows_efl_io_closer_close_on_exec_set(Eo *o EINA_UNUSED, Efl_Net_Socket_Windows_Data *pd, Eina_Bool close_on_exec)
738{
739 DBG("close on exec is not supported on windows");
740 pd->close_on_exec = close_on_exec;
741 return EINA_FALSE;
742}
743
744EOLIAN static Eina_Bool
745_efl_net_socket_windows_efl_io_closer_close_on_exec_get(Eo *o EINA_UNUSED, Efl_Net_Socket_Windows_Data *pd)
746{
747 return pd->close_on_exec;
748}
749
750EOLIAN static void
751_efl_net_socket_windows_efl_io_closer_close_on_destructor_set(Eo *o EINA_UNUSED, Efl_Net_Socket_Windows_Data *pd, Eina_Bool close_on_destructor)
752{
753 pd->close_on_destructor = close_on_destructor;
754}
755
756EOLIAN static Eina_Bool
757_efl_net_socket_windows_efl_io_closer_close_on_destructor_get(Eo *o EINA_UNUSED, Efl_Net_Socket_Windows_Data *pd)
758{
759 return pd->close_on_destructor;
760}
761
762EOLIAN static Eina_Error
763_efl_net_socket_windows_efl_io_reader_read(Eo *o, Efl_Net_Socket_Windows_Data *pd, Eina_Rw_Slice *rw_slice)
764{
765 Eina_Slice ro_slice;
766 DWORD remaining;
767
768 EINA_SAFETY_ON_NULL_RETURN_VAL(rw_slice, EINVAL);
769 EINA_SAFETY_ON_TRUE_RETURN_VAL(pd->recv.base > pd->recv.used, EINVAL);
770
771 ro_slice.len = pd->recv.used - pd->recv.base;
772 if (ro_slice.len == 0)
773 {
774 rw_slice->len = 0;
775 if (pd->pending_eos)
776 {
777 efl_io_reader_eos_set(o, EINA_TRUE);
778 return 0;
779 }
780 return EAGAIN;
781 }
782 ro_slice.bytes = pd->recv.bytes + pd->recv.base;
783
784 *rw_slice = eina_rw_slice_copy(*rw_slice, ro_slice);
785
786 pd->recv.base += rw_slice->len;
787 remaining = pd->recv.used - pd->recv.base;
788
789 efl_io_reader_can_read_set(o, remaining > 0);
790
791 if ((pd->pending_eos) && (remaining == 0))
792 {
793 efl_io_reader_eos_set(o, EINA_TRUE);
794 return 0;
795 }
796
797 if (!pd->recv.pending)
798 {
799 DBG("recv more from socket=%p", o);
800 _efl_net_socket_windows_recv(o, pd);
801 return 0;
802 }
803
804 return 0;
805}
806
807EOLIAN static void
808_efl_net_socket_windows_efl_io_reader_can_read_set(Eo *o, Efl_Net_Socket_Windows_Data *pd, Eina_Bool can_read)
809{
810 EINA_SAFETY_ON_TRUE_RETURN(efl_io_closer_closed_get(o) && can_read);
811 if (pd->can_read == can_read) return;
812 pd->can_read = can_read;
813 efl_event_callback_call(o, EFL_IO_READER_EVENT_CAN_READ_CHANGED, NULL);
814}
815
816EOLIAN static Eina_Bool
817_efl_net_socket_windows_efl_io_reader_can_read_get(Eo *o EINA_UNUSED, Efl_Net_Socket_Windows_Data *pd)
818{
819 return pd->can_read;
820}
821
822EOLIAN static void
823_efl_net_socket_windows_efl_io_reader_eos_set(Eo *o, Efl_Net_Socket_Windows_Data *pd, Eina_Bool is_eos)
824{
825 if (pd->eos == is_eos) return;
826 pd->eos = is_eos;
827 if (is_eos)
828 efl_event_callback_call(o, EFL_IO_READER_EVENT_EOS, NULL);
829}
830
831EOLIAN static Eina_Bool
832_efl_net_socket_windows_efl_io_reader_eos_get(Eo *o EINA_UNUSED, Efl_Net_Socket_Windows_Data *pd)
833{
834 return pd->eos;
835}
836
837EOLIAN static Eina_Error
838_efl_net_socket_windows_efl_io_writer_write(Eo *o, Efl_Net_Socket_Windows_Data *pd, Eina_Slice *slice, Eina_Slice *remaining)
839{
840 Eina_Error err = EINVAL;
841 DWORD available, todo;
842
843 EINA_SAFETY_ON_NULL_RETURN_VAL(slice, EINVAL);
844 EINA_SAFETY_ON_NULL_GOTO(slice->mem, error);
845 EINA_SAFETY_ON_TRUE_GOTO(efl_io_closer_closed_get(o), error);
846 err = ENOMEM;
847 EINA_SAFETY_ON_NULL_GOTO(pd->send.mem, error);
848
849 /* note: do not queue more data if one is already pending,
850 * otherwise we over-commit a lot and on write() -> close(), it
851 * would take us more than simply waiting on the pending send to
852 * complete.
853 */
854 if (pd->send.pending)
855 {
856 efl_io_writer_can_write_set(o, EINA_FALSE);
857 err = EAGAIN;
858 goto error;
859 }
860 err = EINVAL;
861 EINA_SAFETY_ON_TRUE_GOTO(pd->send.used != 0, error);
862
863 available = pd->send.len - pd->send.used;
864 if (slice->len < available)
865 todo = slice->len;
866 else
867 todo = available;
868
869 memcpy(pd->send.bytes + pd->send.used, slice->mem, todo);
870 if (remaining)
871 {
872 remaining->len = slice->len - todo;
873 remaining->bytes = slice->bytes + todo;
874 }
875 slice->len = todo;
876 pd->send.used += todo;
877
878 efl_io_writer_can_write_set(o, EINA_FALSE);
879
880 DBG("send %lu bytes more to socket=%p", pd->send.used, o);
881 return _efl_net_socket_windows_send(o, pd);
882
883 error:
884 if (remaining) *remaining = *slice;
885 slice->len = 0;
886 slice->mem = NULL;
887 return err;
888}
889
890EOLIAN static void
891_efl_net_socket_windows_efl_io_writer_can_write_set(Eo *o, Efl_Net_Socket_Windows_Data *pd, Eina_Bool can_write)
892{
893 EINA_SAFETY_ON_TRUE_RETURN(efl_io_closer_closed_get(o) && can_write);
894 if (pd->can_write == can_write) return;
895 pd->can_write = can_write;
896 efl_event_callback_call(o, EFL_IO_WRITER_EVENT_CAN_WRITE_CHANGED, NULL);
897}
898
899EOLIAN static Eina_Bool
900_efl_net_socket_windows_efl_io_writer_can_write_get(Eo *o EINA_UNUSED, Efl_Net_Socket_Windows_Data *pd)
901{
902 return pd->can_write;
903}
904
905EOLIAN static void
906_efl_net_socket_windows_efl_net_socket_address_local_set(Eo *o EINA_UNUSED, Efl_Net_Socket_Windows_Data *pd, const char *address)
907{
908 eina_stringshare_replace(&pd->address_local, address);
909}
910
911EOLIAN static const char *
912_efl_net_socket_windows_efl_net_socket_address_local_get(Eo *o EINA_UNUSED, Efl_Net_Socket_Windows_Data *pd)
913{
914 return pd->address_local;
915}
916
917EOLIAN static void
918_efl_net_socket_windows_efl_net_socket_address_remote_set(Eo *o EINA_UNUSED, Efl_Net_Socket_Windows_Data *pd, const char *address)
919{
920 eina_stringshare_replace(&pd->address_remote, address);
921}
922
923EOLIAN static const char *
924_efl_net_socket_windows_efl_net_socket_address_remote_get(Eo *o EINA_UNUSED, Efl_Net_Socket_Windows_Data *pd)
925{
926 return pd->address_remote;
927}
928
929#include "efl_net_socket_windows.eo.c"
diff --git a/src/lib/ecore_con/efl_net_socket_windows.eo b/src/lib/ecore_con/efl_net_socket_windows.eo
new file mode 100644
index 0000000000..e53d10df54
--- /dev/null
+++ b/src/lib/ecore_con/efl_net_socket_windows.eo
@@ -0,0 +1,26 @@
1class Efl.Net.Socket.Windows (Efl.Loop_User, Efl.Net.Socket) {
2 [[A base Windows NamedPipe socket.
3
4 This is the common class and takes an existing file HANDLE,
5 usually created by an dialer (CreatFile()) or server
6 (CreateNamedPipe()).
7
8 @since 1.19
9 ]]
10
11 implements {
12 Efl.Object.constructor;
13 Efl.Object.destructor;
14 Efl.Io.Closer.close;
15 Efl.Io.Closer.closed { get; }
16 Efl.Io.Closer.close_on_exec { get; set; }
17 Efl.Io.Closer.close_on_destructor { get; set; }
18 Efl.Io.Reader.read;
19 Efl.Io.Reader.can_read { get; set; }
20 Efl.Io.Reader.eos { get; set; }
21 Efl.Io.Writer.write;
22 Efl.Io.Writer.can_write { get; set; }
23 Efl.Net.Socket.address_local { get; set; }
24 Efl.Net.Socket.address_remote { get; set; }
25 }
26}