summaryrefslogtreecommitdiff
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-27 16:15:06 -0300
commit5bd011ceda998970ad4eaa49f052c6300fcce8f4 (patch)
tree70d66e6d3d742403c3497821cc3b0281a955cdc8
parent3bab78beed4c769a8b3c198c88e52389da2806cb (diff)
downloadefl-5bd011ceda998970ad4eaa49f052c6300fcce8f4.tar.gz
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.
-rw-r--r--src/Makefile_Ecore_Con.am9
-rw-r--r--src/examples/ecore/Makefile.am6
-rw-r--r--src/examples/ecore/efl_io_copier_example.c72
-rw-r--r--src/examples/ecore/efl_net_dialer_simple_example.c5
-rw-r--r--src/examples/ecore/efl_net_dialer_windows_example.c218
-rw-r--r--src/examples/ecore/efl_net_server_example.c12
-rw-r--r--src/examples/ecore/efl_net_server_simple_example.c12
-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
16 files changed, 2038 insertions, 14 deletions
diff --git a/src/Makefile_Ecore_Con.am b/src/Makefile_Ecore_Con.am
index b22c3f6c0d..cfae980ed4 100644
--- a/src/Makefile_Ecore_Con.am
+++ b/src/Makefile_Ecore_Con.am
@@ -33,6 +33,10 @@ ecore_con_eolian_files = \
lib/ecore_con/ecore_con_eet_client_obj.eo
if HAVE_WINDOWS
+ecore_con_eolian_files += \
+ lib/ecore_con/efl_net_socket_windows.eo \
+ lib/ecore_con/efl_net_dialer_windows.eo \
+ lib/ecore_con/efl_net_server_windows.eo
else
ecore_con_eolian_files += \
lib/ecore_con/efl_net_socket_unix.eo \
@@ -145,7 +149,10 @@ lib/ecore_con/efl_net_ssl_ctx-gnutls.c \
lib/ecore_con/efl_net_ssl_ctx-none.c
if HAVE_WINDOWS
-#lib_ecore_con_libecore_con_la_SOURCES += lib/ecore_con/ecore_con_local_win32.c
+lib_ecore_con_libecore_con_la_SOURCES += \
+lib/ecore_con/efl_net_socket_windows.c \
+lib/ecore_con/efl_net_dialer_windows.c \
+lib/ecore_con/efl_net_server_windows.c
else
lib_ecore_con_libecore_con_la_SOURCES += \
lib/ecore_con/efl_net_socket_unix.c \
diff --git a/src/examples/ecore/Makefile.am b/src/examples/ecore/Makefile.am
index c90e0c6f2b..e10dfc7827 100644
--- a/src/examples/ecore/Makefile.am
+++ b/src/examples/ecore/Makefile.am
@@ -353,7 +353,11 @@ efl_net_dialer_udp_example_LDADD = $(ECORE_CON_COMMON_LDADD)
efl_net_dialer_simple_example_SOURCES = efl_net_dialer_simple_example.c
efl_net_dialer_simple_example_LDADD = $(ECORE_CON_COMMON_LDADD)
-if ! HAVE_WINDOWS
+if HAVE_WINDOWS
+EXTRA_PROGRAMS += efl_net_dialer_windows_example
+efl_net_dialer_windows_example_SOURCES = efl_net_dialer_windows_example.c
+efl_net_dialer_windows_example_LDADD = $(ECORE_CON_COMMON_LDADD)
+else
EXTRA_PROGRAMS += efl_net_dialer_unix_example
efl_net_dialer_unix_example_SOURCES = efl_net_dialer_unix_example.c
efl_net_dialer_unix_example_LDADD = $(ECORE_CON_COMMON_LDADD)
diff --git a/src/examples/ecore/efl_io_copier_example.c b/src/examples/ecore/efl_io_copier_example.c
index 5e5cbebc66..1abb486985 100644
--- a/src/examples/ecore/efl_io_copier_example.c
+++ b/src/examples/ecore/efl_io_copier_example.c
@@ -330,7 +330,10 @@ static const Ecore_Getopt options = {
"http://address to do a GET request\n"
"ws://address or wss:// to do WebSocket request (must send some data once connected)\n"
"udp://IP:PORT to bind using UDP and an IPv4 (A.B.C.D:PORT) or IPv6 ([A:B:C:D::E]:PORT).\n"
-#ifndef _WIN32
+#ifdef EFL_NET_DIALER_WINDOWS_CLASS
+ "windows://path to connect to an Windows NamedPipe server. It will have '\\\\.pipe\\' prepended.\n"
+#endif
+#ifdef EFL_NET_DIALER_UNIX_CLASS
"unix://path to connect to an AF_UNIX server. For Linux one can create abstract sockets with unix://abstract:name.\n"
#endif
"ssl://IP:PORT to connect using TCP+SSL and an IPv4 (A.B.C.D:PORT) or IPv6 ([A:B:C:D::E]:PORT).\n"
@@ -346,7 +349,10 @@ static const Ecore_Getopt options = {
"http://address to do a PUT request\n"
"ws://address or wss:// to do WebSocket request\n"
"udp://IP:PORT to connect using UDP and an IPv4 (A.B.C.D:PORT) or IPv6 ([A:B:C:D::E]:PORT).\n"
-#ifndef _WIN32
+#ifdef EFL_NET_DIALER_WINDOWS_CLASS
+ "windows://path to connect to an Windows NamedPipe server. It will have '\\\\.pipe\\' prepended.\n"
+#endif
+#ifdef EFL_NET_SERVER_UNIX_CLASS
"unix://path to connect to an AF_UNIX server. For Linux one can create abstract sockets with unix://abstract:name.\n"
#endif
"ssl://IP:PORT to connect using TCP+SSL and an IPv4 (A.B.C.D:PORT) or IPv6 ([A:B:C:D::E]:PORT).\n"
@@ -542,7 +548,7 @@ main(int argc, char **argv)
goto end_input;
}
}
-#ifndef _WIN32
+#ifdef EFL_NET_DIALER_UNIX_CLASS
else if (strncmp(input_fname, "unix://", strlen("unix://")) == 0)
{
/*
@@ -571,6 +577,35 @@ main(int argc, char **argv)
}
}
#endif
+#ifdef EFL_NET_DIALER_WINDOWS_CLASS
+ else if (strncmp(input_fname, "windows://", strlen("windows://")) == 0)
+ {
+ /*
+ * Since Efl.Net.Socket implements the required interfaces,
+ * they can be used here as well.
+ */
+ const char *address = input_fname + strlen("windows://");
+ Eina_Error err;
+ input = efl_add(EFL_NET_DIALER_WINDOWS_CLASS, ecore_main_loop_get(),
+ efl_event_callback_array_add(efl_added, input_cbs(), NULL), /* optional */
+ efl_event_callback_array_add(efl_added, dialer_cbs(), NULL) /* optional */
+ );
+ if (!input)
+ {
+ fprintf(stderr, "ERROR: could not create Windows NamedPipe Dialer.\n");
+ retval = EXIT_FAILURE;
+ goto end;
+ }
+
+ err = efl_net_dialer_dial(input, address);
+ if (err)
+ {
+ fprintf(stderr, "ERROR: could not Windows NamedPipe dial %s: %s\n",
+ address, eina_error_msg_get(err));
+ goto end_input;
+ }
+ }
+#endif
else if (strncmp(input_fname, "ssl://", strlen("ssl://")) == 0)
{
/*
@@ -787,7 +822,7 @@ main(int argc, char **argv)
goto end_output;
}
}
-#ifndef _WIN32
+#ifdef EFL_NET_DIALER_UNIX_CLASS
else if (strncmp(output_fname, "unix://", strlen("unix://")) == 0)
{
/*
@@ -816,6 +851,35 @@ main(int argc, char **argv)
}
}
#endif
+#ifdef EFL_NET_DIALER_WINDOWS_CLASS
+ else if (strncmp(output_fname, "windows://", strlen("windows://")) == 0)
+ {
+ /*
+ * Since Efl.Net.Socket implements the required interfaces,
+ * they can be used here as well.
+ */
+ const char *address = output_fname + strlen("windows://");
+ Eina_Error err;
+ output = efl_add(EFL_NET_DIALER_WINDOWS_CLASS, ecore_main_loop_get(),
+ efl_event_callback_array_add(efl_added, output_cbs(), NULL), /* optional */
+ efl_event_callback_array_add(efl_added, dialer_cbs(), NULL) /* optional */
+ );
+ if (!output)
+ {
+ fprintf(stderr, "ERROR: could not create Windows NamedPipe Dialer.\n");
+ retval = EXIT_FAILURE;
+ goto end_input;
+ }
+
+ err = efl_net_dialer_dial(output, address);
+ if (err)
+ {
+ fprintf(stderr, "ERROR: could not Windows NamedPipe dial %s: %s\n",
+ address, eina_error_msg_get(err));
+ goto end_output;
+ }
+ }
+#endif
else if (strncmp(output_fname, "ssl://", strlen("ssl://")) == 0)
{
/*
diff --git a/src/examples/ecore/efl_net_dialer_simple_example.c b/src/examples/ecore/efl_net_dialer_simple_example.c
index bda865c20e..4a1fde5689 100644
--- a/src/examples/ecore/efl_net_dialer_simple_example.c
+++ b/src/examples/ecore/efl_net_dialer_simple_example.c
@@ -346,9 +346,12 @@ main(int argc, char **argv)
if (strcmp(protocol, "tcp") == 0) cls = EFL_NET_DIALER_TCP_CLASS;
else if (strcmp(protocol, "udp") == 0) cls = EFL_NET_DIALER_UDP_CLASS;
else if (strcmp(protocol, "ssl") == 0) cls = EFL_NET_DIALER_SSL_CLASS;
-#ifndef _WIN32
+#ifdef EFL_NET_DIALER_UNIX_CLASS
else if (strcmp(protocol, "unix") == 0) cls = EFL_NET_DIALER_UNIX_CLASS;
#endif
+#ifdef EFL_NET_DIALER_WINDOWS_CLASS
+ else if (strcmp(protocol, "windows") == 0) cls = EFL_NET_DIALER_WINDOWS_CLASS;
+#endif
else
{
fprintf(stderr, "ERROR: unsupported protocol: %s\n", protocol);
diff --git a/src/examples/ecore/efl_net_dialer_windows_example.c b/src/examples/ecore/efl_net_dialer_windows_example.c
new file mode 100644
index 0000000000..88c23d0d63
--- /dev/null
+++ b/src/examples/ecore/efl_net_dialer_windows_example.c
@@ -0,0 +1,218 @@
+#define EFL_BETA_API_SUPPORT 1
+#define EFL_EO_API_SUPPORT 1
+#include <Ecore.h>
+#include <Ecore_Con.h>
+#include <Ecore_Getopt.h>
+#include <fcntl.h>
+#include <ctype.h>
+
+static int retval = EXIT_SUCCESS;
+static Eina_Bool do_read = EINA_FALSE;
+
+static void
+_connected(void *data EINA_UNUSED, const Efl_Event *event)
+{
+ fprintf(stderr,
+ "INFO: connected to '%s' (%s)\n"
+ "INFO: - local address=%s\n"
+ "INFO: - read-after-write=%u\n",
+ efl_net_dialer_address_dial_get(event->object),
+ efl_net_socket_address_remote_get(event->object),
+ efl_net_socket_address_local_get(event->object),
+ do_read);
+}
+
+static void
+_eos(void *data EINA_UNUSED, const Efl_Event *event EINA_UNUSED)
+{
+ fprintf(stderr, "INFO: end of stream. \n");
+ ecore_main_loop_quit();
+}
+
+static void
+_can_read(void *data EINA_UNUSED, const Efl_Event *event)
+{
+ char buf[4];
+ Eina_Error err;
+ Eina_Bool can_read = efl_io_reader_can_read_get(event->object);
+
+ /* NOTE: this message may appear with can read=0 BEFORE
+ * "read '...'" because efl_io_reader_read() will change the status
+ * of can_read to FALSE prior to return so we can print it!
+ */
+ fprintf(stderr, "INFO: can read=%d\n", can_read);
+ if (!can_read) return;
+ if (!do_read) return;
+
+ do
+ {
+ Eina_Rw_Slice rw_slice = EINA_SLICE_ARRAY(buf);
+
+ err = efl_io_reader_read(event->object, &rw_slice);
+ if (err)
+ {
+ if (err == EAGAIN) return;
+ fprintf(stderr, "ERROR: could not read: %s\n", eina_error_msg_get(err));
+ retval = EXIT_FAILURE;
+ ecore_main_loop_quit();
+ return;
+ }
+
+ fprintf(stderr, "INFO: read '" EINA_SLICE_STR_FMT "'\n", EINA_SLICE_STR_PRINT(rw_slice));
+ }
+ while (efl_io_reader_can_read_get(event->object));
+}
+
+static void
+_can_write(void *data EINA_UNUSED, const Efl_Event *event)
+{
+ static Eina_Slice slice = EINA_SLICE_STR_LITERAL("Hello World!");
+ Eina_Slice to_write;
+ Eina_Error err;
+ Eina_Bool can_write = efl_io_writer_can_write_get(event->object);
+
+ /* NOTE: this message may appear with can write=0 BEFORE
+ * "wrote '...'" because efl_io_writer_write() will change the status
+ * of can_write to FALSE prior to return so we can print it!
+ */
+ fprintf(stderr, "INFO: can write=%d (wanted bytes=%zd)\n", can_write, slice.len);
+ if (!can_write) return;
+ if (slice.len == 0) return;
+
+ to_write = slice;
+ err = efl_io_writer_write(event->object, &to_write, &slice);
+ if (err)
+ {
+ fprintf(stderr, "ERROR: could not write: %s\n", eina_error_msg_get(err));
+ retval = EXIT_FAILURE;
+ ecore_main_loop_quit();
+ return;
+ }
+
+ fprintf(stderr, "INFO: wrote '" EINA_SLICE_STR_FMT "', still pending=%zd bytes\n", EINA_SLICE_STR_PRINT(to_write), slice.len);
+
+ if ((!do_read) && (slice.len == 0))
+ {
+ retval = EXIT_SUCCESS;
+ ecore_main_loop_quit();
+ return;
+ }
+}
+
+static void
+_resolved(void *data EINA_UNUSED, const Efl_Event *event)
+{
+ fprintf(stderr, "INFO: resolved %s => %s\n",
+ efl_net_dialer_address_dial_get(event->object),
+ efl_net_socket_address_remote_get(event->object));
+}
+
+static void
+_error(void *data EINA_UNUSED, const Efl_Event *event)
+{
+ const Eina_Error *perr = event->info;
+ fprintf(stderr, "INFO: error: %d '%s'\n", *perr, eina_error_msg_get(*perr));
+ retval = EXIT_FAILURE;
+}
+
+EFL_CALLBACKS_ARRAY_DEFINE(dialer_cbs,
+ { EFL_NET_DIALER_EVENT_CONNECTED, _connected },
+ { EFL_NET_DIALER_EVENT_RESOLVED, _resolved },
+ { EFL_NET_DIALER_EVENT_ERROR, _error },
+ { EFL_IO_READER_EVENT_EOS, _eos },
+ { EFL_IO_READER_EVENT_CAN_READ_CHANGED, _can_read },
+ { EFL_IO_WRITER_EVENT_CAN_WRITE_CHANGED, _can_write }
+ );
+
+static const Ecore_Getopt options = {
+ "efl_net_dialer_windows_example", /* program name */
+ NULL, /* usage line */
+ "1", /* version */
+ "(C) 2017 Enlightenment Project", /* copyright */
+ "BSD 2-Clause", /* license */
+ /* long description, may be multiline and contain \n */
+ "Example of Efl_Net_Dialer_Windows usage, sending a message and receiving a reply\n",
+ EINA_FALSE,
+ {
+ ECORE_GETOPT_STORE_TRUE('r', "read-after-write", "Do a read after writes are done."),
+ 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,
+ "The address (URL) to dial", "address"),
+ ECORE_GETOPT_SENTINEL
+ }
+};
+
+int
+main(int argc, char **argv)
+{
+ char *address = NULL;
+ Eina_Bool quit_option = EINA_FALSE;
+ Ecore_Getopt_Value values[] = {
+ ECORE_GETOPT_VALUE_BOOL(do_read),
+
+ /* standard block to provide version, copyright, license and help */
+ ECORE_GETOPT_VALUE_BOOL(quit_option), /* -V/--version quits */
+ ECORE_GETOPT_VALUE_BOOL(quit_option), /* -C/--copyright quits */
+ ECORE_GETOPT_VALUE_BOOL(quit_option), /* -L/--license quits */
+ ECORE_GETOPT_VALUE_BOOL(quit_option), /* -h/--help quits */
+
+ /* positional argument */
+ ECORE_GETOPT_VALUE_STR(address),
+
+ ECORE_GETOPT_VALUE_NONE /* sentinel */
+ };
+ int args;
+ Eo *dialer, *loop;
+ Eina_Error err;
+
+ 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;
+
+ loop = ecore_main_loop_get();
+
+ 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;
+ }
+
+ dialer = efl_add(EFL_NET_DIALER_WINDOWS_CLASS, loop,
+ efl_name_set(efl_added, "dialer"),
+ efl_event_callback_array_add(efl_added, dialer_cbs(), NULL));
+
+ err = efl_net_dialer_dial(dialer, address);
+ if (err != 0)
+ {
+ fprintf(stderr, "ERROR: could not dial '%s': %s\n",
+ address, eina_error_msg_get(err));
+ goto no_mainloop;
+ }
+
+ ecore_main_loop_begin();
+
+ fprintf(stderr, "INFO: main loop finished.\n");
+
+ no_mainloop:
+ efl_del(dialer);
+
+ end:
+ ecore_con_shutdown();
+ ecore_shutdown();
+
+ return retval;
+}
diff --git a/src/examples/ecore/efl_net_server_example.c b/src/examples/ecore/efl_net_server_example.c
index 3a9598e2cb..c3b19ec3fc 100644
--- a/src/examples/ecore/efl_net_server_example.c
+++ b/src/examples/ecore/efl_net_server_example.c
@@ -461,7 +461,10 @@ static const char * protocols[] = {
"tcp",
"udp",
"ssl",
-#ifndef _WIN32
+#ifdef EFL_NET_SERVER_WINDOWS_CLASS
+ "windows",
+#endif
+#ifdef EFL_NET_SERVER_UNIX_CLASS
"unix",
#endif
NULL
@@ -622,7 +625,10 @@ main(int argc, char **argv)
if (strcmp(protocol, "tcp") == 0) cls = EFL_NET_SERVER_TCP_CLASS;
else if (strcmp(protocol, "udp") == 0) cls = EFL_NET_SERVER_UDP_CLASS;
else if (strcmp(protocol, "ssl") == 0) cls = EFL_NET_SERVER_SSL_CLASS;
-#ifndef _WIN32
+#ifdef EFL_NET_SERVER_WINDOWS_CLASS
+ else if (strcmp(protocol, "windows") == 0) cls = EFL_NET_SERVER_WINDOWS_CLASS;
+#endif
+#ifdef EFL_NET_SERVER_UNIX_CLASS
else if (strcmp(protocol, "unix") == 0) cls = EFL_NET_SERVER_UNIX_CLASS;
#endif
else
@@ -703,7 +709,7 @@ main(int argc, char **argv)
efl_net_server_ssl_reuse_port_set(server, EINA_TRUE); /* optional, but nice for testing... not secure unless you know what you're doing */
if (socket_activated) efl_net_server_ssl_socket_activate(server, address);
}
-#ifndef _WIN32
+#ifdef EFL_NET_SERVER_UNIX_CLASS
else if (cls == EFL_NET_SERVER_UNIX_CLASS)
{
efl_net_server_unix_unlink_before_bind_set(server, EINA_TRUE); /* makes testing easier */
diff --git a/src/examples/ecore/efl_net_server_simple_example.c b/src/examples/ecore/efl_net_server_simple_example.c
index 548dd92ccc..ddce4c86cd 100644
--- a/src/examples/ecore/efl_net_server_simple_example.c
+++ b/src/examples/ecore/efl_net_server_simple_example.c
@@ -265,7 +265,10 @@ static const char * protocols[] = {
"tcp",
"udp",
"ssl",
-#ifndef _WIN32
+#ifdef EFL_NET_SERVER_WINDOWS_CLASS
+ "windows",
+#endif
+#ifdef EFL_NET_SERVER_UNIX_CLASS
"unix",
#endif
NULL
@@ -426,7 +429,10 @@ main(int argc, char **argv)
if (strcmp(protocol, "tcp") == 0) cls = EFL_NET_SERVER_TCP_CLASS;
else if (strcmp(protocol, "udp") == 0) cls = EFL_NET_SERVER_UDP_CLASS;
else if (strcmp(protocol, "ssl") == 0) cls = EFL_NET_SERVER_SSL_CLASS;
-#ifndef _WIN32
+#ifdef EFL_NET_SERVER_WINDOWS_CLASS
+ else if (strcmp(protocol, "windows") == 0) cls = EFL_NET_SERVER_WINDOWS_CLASS;
+#endif
+#ifdef EFL_NET_SERVER_UNIX_CLASS
else if (strcmp(protocol, "unix") == 0) cls = EFL_NET_SERVER_UNIX_CLASS;
#endif
else
@@ -511,7 +517,7 @@ main(int argc, char **argv)
efl_net_server_ssl_reuse_port_set(server, EINA_TRUE); /* optional, but nice for testing... not secure unless you know what you're doing */
if (socket_activated) efl_net_server_ssl_socket_activate(server, address);
}
-#ifndef _WIN32
+#ifdef EFL_NET_SERVER_UNIX_CLASS
else if (cls == EFL_NET_SERVER_UNIX_CLASS)
{
efl_net_server_unix_unlink_before_bind_set(server, EINA_TRUE); /* makes testing easier */
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 @@
#include "efl_net_server_tcp.eo.h"
#ifdef _WIN32
+#include "efl_net_socket_windows.eo.h"
+#include "efl_net_dialer_windows.eo.h"
+#include "efl_net_server_windows.eo.h"
#else
#include "efl_net_socket_unix.eo.h"
#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,
case ECORE_CON_LOCAL_ABSTRACT:
#ifdef EFL_NET_SERVER_UNIX_CLASS
cls = EFL_NET_SERVER_UNIX_CLASS;
+#elif defined(EFL_NET_SERVER_WINDOWS_CLASS)
+ cls = EFL_NET_SERVER_WINDOWS_CLASS;
#else
ERR("Your platform doesn't support Efl_Net_Server-compatible local communication");
// TODO: maybe write to a file and use TCP
@@ -1845,7 +1847,9 @@ _ecore_con_server_dialer_set(Ecore_Con_Server *svr, Eo *dialer)
(type == ECORE_CON_LOCAL_SYSTEM))
{
char *path = ecore_con_local_path_new(type == ECORE_CON_LOCAL_SYSTEM, svr->name, svr->port);
+#ifdef EFL_NET_DIALER_UNIX_CLASS
struct stat st;
+#endif
if (!path)
{
@@ -1858,6 +1862,7 @@ _ecore_con_server_dialer_set(Ecore_Con_Server *svr, Eo *dialer)
free(path);
}
+#ifdef EFL_NET_DIALER_UNIX_CLASS
if ((stat(address, &st) != 0)
#ifdef S_ISSOCK
|| (!S_ISSOCK(st.st_mode))
@@ -1867,6 +1872,7 @@ _ecore_con_server_dialer_set(Ecore_Con_Server *svr, Eo *dialer)
DBG("%s is not a socket", address);
return EINA_FALSE;
}
+#endif
}
if ((svr->type & ECORE_CON_NO_PROXY) == ECORE_CON_NO_PROXY)
@@ -2095,6 +2101,8 @@ ecore_con_server_connect(Ecore_Con_Type compl_type,
case ECORE_CON_LOCAL_ABSTRACT:
#ifdef EFL_NET_DIALER_UNIX_CLASS
cls = EFL_NET_DIALER_UNIX_CLASS;
+#elif defined(EFL_NET_DIALER_WINDOWS_CLASS)
+ cls = EFL_NET_DIALER_WINDOWS_CLASS;
#else
ERR("Your platform doesn't support Efl_Net_Dialer-compatible local communication");
// 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);
void _efl_net_server_udp_client_init(Eo *client, SOCKET fd, const struct sockaddr *addr, socklen_t addrlen, const char *str);
void _efl_net_server_udp_client_feed(Eo *client, Eina_Rw_Slice slice);
-#ifndef _WIN32
+#ifdef EFL_NET_SOCKET_WINDOWS_CLASS
+#define PIPE_NS "\\\\.\\pipe\\"
+char *_efl_net_windows_error_msg_get(DWORD win32err);
+Eina_Error _efl_net_socket_windows_init(Eo *o, HANDLE h);
+Eina_Error _efl_net_socket_windows_io_start(Eo *o);
+HANDLE _efl_net_socket_windows_handle_get(const Eo *o);
+
+typedef struct _Efl_Net_Socket_Windows_Operation Efl_Net_Socket_Windows_Operation;
+typedef Eina_Error (*Efl_Net_Socket_Windows_Operation_Success_Cb)(void *data, Eo *sock, DWORD used_size);
+typedef Eina_Error (*Efl_Net_Socket_Windows_Operation_Failure_Cb)(void *data, Eo *sock, DWORD win32err);
+
+Efl_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);
+Eina_Error _efl_net_socket_windows_operation_failed(Efl_Net_Socket_Windows_Operation *op, DWORD win32err);
+Eina_Error _efl_net_socket_windows_operation_succeeded(Efl_Net_Socket_Windows_Operation *op, DWORD used_size);
+
+static inline OVERLAPPED *
+_efl_net_socket_windows_operation_overlapped_get(Efl_Net_Socket_Windows_Operation *op)
+{
+ return (OVERLAPPED *)op;
+}
+#endif
+
+#ifdef EFL_NET_SOCKET_UNIX_CLASS
Eina_Bool efl_net_unix_fmt(char *buf, size_t buflen, SOCKET fd, const struct sockaddr_un *addr, socklen_t addrlen);
#endif
Eina_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 @@
+#define EFL_NET_SOCKET_WINDOWS_PROTECTED 1
+#define EFL_IO_READER_PROTECTED 1
+#define EFL_IO_WRITER_PROTECTED 1
+#define EFL_IO_CLOSER_PROTECTED 1
+#define EFL_NET_DIALER_PROTECTED 1
+#define EFL_NET_SOCKET_PROTECTED 1
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "Ecore.h"
+#include "Ecore_Con.h"
+#include "ecore_con_private.h"
+
+#define MY_CLASS EFL_NET_DIALER_WINDOWS_CLASS
+
+typedef struct _Efl_Net_Dialer_Windows_Data
+{
+ Eina_Stringshare *address_dial;
+ double timeout_dial;
+ Eina_Bool connected;
+} Efl_Net_Dialer_Windows_Data;
+
+EOLIAN static void
+_efl_net_dialer_windows_efl_object_destructor(Eo *o, Efl_Net_Dialer_Windows_Data *pd)
+{
+ efl_destructor(efl_super(o, MY_CLASS));
+
+ eina_stringshare_replace(&pd->address_dial, NULL);
+}
+
+EOLIAN static Eina_Error
+_efl_net_dialer_windows_efl_net_dialer_dial(Eo *o, Efl_Net_Dialer_Windows_Data *pd, const char *address)
+{
+ Eina_Error err;
+ HANDLE h;
+ char cstr[256], sstr[256];
+ ULONG cpid = 0, spid = 0;
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(address, EINVAL);
+ EINA_SAFETY_ON_TRUE_RETURN_VAL(strchr(address, '/') != NULL, EINVAL);
+ EINA_SAFETY_ON_TRUE_RETURN_VAL(strchr(address, '\\') != NULL, EINVAL);
+ EINA_SAFETY_ON_TRUE_RETURN_VAL(strlen(PIPE_NS) + strlen(address) >= 256, EINVAL);
+ EINA_SAFETY_ON_TRUE_RETURN_VAL(efl_net_dialer_connected_get(o), EISCONN);
+
+ efl_net_dialer_address_dial_set(o, address);
+
+ h = CreateFile(pd->address_dial,
+ FILE_READ_ATTRIBUTES | FILE_READ_DATA |
+ FILE_WRITE_ATTRIBUTES | FILE_WRITE_DATA,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, OPEN_EXISTING,
+ FILE_FLAG_OVERLAPPED, NULL);
+ if (h == INVALID_HANDLE_VALUE)
+ {
+ DWORD win32err = GetLastError();
+ switch (win32err)
+ {
+ case ERROR_ACCESS_DENIED: return EACCES;
+ case ERROR_INVALID_PARAMETER: return EINVAL;
+ case ERROR_SHARING_VIOLATION: return EINVAL;
+ case ERROR_FILE_EXISTS: return EEXIST;
+ case ERROR_FILE_NOT_FOUND: return ENOENT;
+ case ERROR_PIPE_BUSY: return EBUSY;
+ case ERROR_INVALID_NAME: return EADDRNOTAVAIL;
+ default:
+ {
+ char *msg = evil_format_message(win32err);
+ ERR("CreateFile(%s): Unexpected win32err=%lu (%s)", pd->address_dial, win32err, msg);
+ free(msg);
+ return EINVAL;
+ }
+ }
+ }
+
+ err = _efl_net_socket_windows_init(o, h);
+ if (err)
+ {
+ CloseHandle(h);
+ return err;
+ }
+
+#if _WIN32_WINNT >= 0x0600
+ if (!GetNamedPipeClientProcessId(_efl_net_socket_windows_handle_get(o), &cpid))
+ {
+ char *msg = evil_last_error_get();
+ WRN("server=%p (%s) could not GetNamedPipeClientProcessId(o): %s", o, address, msg);
+ free(msg);
+ }
+ if (!GetNamedPipeServerProcessId(_efl_net_socket_windows_handle_get(o), &spid))
+ {
+ char *msg = evil_last_error_get();
+ WRN("server=%p (%s) could not GetNamedPipeServerProcessId(o): %s", o, address, msg);
+ free(msg);
+ }
+#endif
+
+ if (cpid)
+ snprintf(cstr, sizeof(cstr), "%s:%lu", address, cpid);
+ else
+ eina_strlcpy(cstr, address, sizeof(cstr));
+
+ if (spid)
+ snprintf(sstr, sizeof(sstr), "%s:%lu", address, spid);
+ else
+ eina_strlcpy(sstr, address, sizeof(sstr));
+
+ efl_net_socket_address_remote_set(o, sstr);
+ efl_net_socket_address_local_set(o, cstr);
+ efl_event_callback_call(o, EFL_NET_DIALER_EVENT_RESOLVED, NULL);
+ efl_net_dialer_connected_set(o, EINA_TRUE);
+
+ return _efl_net_socket_windows_io_start(o);
+}
+
+EOLIAN static void
+_efl_net_dialer_windows_efl_net_dialer_address_dial_set(Eo *o EINA_UNUSED, Efl_Net_Dialer_Windows_Data *pd, const char *address)
+{
+ const char *tmp = eina_stringshare_printf(PIPE_NS "%s", address);
+ eina_stringshare_del(pd->address_dial);
+ pd->address_dial = tmp;
+}
+
+EOLIAN static const char *
+_efl_net_dialer_windows_efl_net_dialer_address_dial_get(Eo *o EINA_UNUSED, Efl_Net_Dialer_Windows_Data *pd)
+{
+ return pd->address_dial + strlen(PIPE_NS);
+}
+
+EOLIAN static void
+_efl_net_dialer_windows_efl_net_dialer_connected_set(Eo *o, Efl_Net_Dialer_Windows_Data *pd, Eina_Bool connected)
+{
+ if (pd->connected == connected) return;
+ pd->connected = connected;
+ if (connected) efl_event_callback_call(o, EFL_NET_DIALER_EVENT_CONNECTED, NULL);
+}
+
+EOLIAN static Eina_Bool
+_efl_net_dialer_windows_efl_net_dialer_connected_get(Eo *o EINA_UNUSED, Efl_Net_Dialer_Windows_Data *pd)
+{
+ return pd->connected;
+}
+
+EOLIAN static void
+_efl_net_dialer_windows_efl_net_dialer_timeout_dial_set(Eo *o EINA_UNUSED, Efl_Net_Dialer_Windows_Data *pd, double seconds)
+{
+ pd->timeout_dial = seconds;
+}
+
+EOLIAN static double
+_efl_net_dialer_windows_efl_net_dialer_timeout_dial_get(Eo *o EINA_UNUSED, Efl_Net_Dialer_Windows_Data *pd)
+{
+ return pd->timeout_dial;
+}
+
+EOLIAN static Eina_Error
+_efl_net_dialer_windows_efl_io_closer_close(Eo *o, Efl_Net_Dialer_Windows_Data *pd EINA_UNUSED)
+{
+ efl_net_dialer_connected_set(o, EINA_FALSE);
+ return efl_io_closer_close(efl_super(o, MY_CLASS));
+}
+
+#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 @@
+class Efl.Net.Dialer.Windows (Efl.Net.Socket.Windows, Efl.Net.Dialer) {
+ [[Connects to a Windows NamedPipe server.
+
+ The dial address will have "\\\\.\\pipe\\" prepended as required by
+ Windows CreateNamedPipe().
+
+ \@note Proxies are meaningless, thus are not implemented.
+
+ @since 1.19
+ ]]
+
+ implements {
+ Efl.Object.destructor;
+ Efl.Net.Dialer.dial; [[address parameter will have "\\\\.\\pipe\\" prepended]]
+ Efl.Net.Dialer.address_dial { get; set; }
+ Efl.Net.Dialer.connected { get; set; }
+ Efl.Net.Dialer.timeout_dial { get; set; }
+ Efl.Io.Closer.close;
+ }
+}
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 @@
+#define EFL_NET_SERVER_WINDOWS_PROTECTED 1
+#define EFL_NET_SERVER_PROTECTED 1
+#define EFL_NET_SOCKET_PROTECTED 1
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "Ecore.h"
+#include "Ecore_Con.h"
+#include "ecore_con_private.h"
+
+/*
+ * See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365601(v=vs.85).aspx
+ * Named Pipe Server Using Completion Routines
+ *
+ * See https://msdn.microsoft.com/en-us/library/windows/desktop/aa365603(v=vs.85).aspx
+ * Named Pipe Server Using Overlapped I/O
+ *
+ * Each instance (PIPEINST) is an Efl_Net_Socket_Windows. Instead of
+ * pre-creating all possible instances and having all of them to
+ * accept connections (ConnectNamedPipe()), we create a single one
+ * (Efl_Net_Server_Windows_Data::next_client), once it's connected we
+ * announce the client and if it's used, a new "next_client" is
+ * started, otherwise if the announced client is not used, then it's
+ * disconnected and reused with a new ConnectNamedPipe().
+ */
+
+#define MY_CLASS EFL_NET_SERVER_WINDOWS_CLASS
+
+typedef struct _Efl_Net_Server_Windows_Data
+{
+ Eo *next_client;
+ Eina_List *pending_clients;
+
+ Eina_Stringshare *address; /* includes prefix: \\.\pipe\, returned without it */
+ Efl_Future *pending_announcer_job;
+ unsigned int clients_count;
+ unsigned int clients_limit;
+ Eina_Bool clients_reject_excess;
+ Eina_Bool serving;
+ Eina_Bool first;
+ Eina_Bool allow_remote;
+} Efl_Net_Server_Windows_Data;
+
+static Eina_Error _efl_net_server_windows_client_listen(Eo *o, Efl_Net_Server_Windows_Data *pd);
+static Eina_Error _efl_net_server_windows_client_new(Eo *o, Efl_Net_Server_Windows_Data *pd);
+
+static Eina_Error
+_efl_net_server_windows_client_listen_success(void *data, Eo *client, DWORD used_size EINA_UNUSED)
+{
+ Eo *o = data;
+ Efl_Net_Server_Windows_Data *pd = efl_data_scope_get(o, MY_CLASS);
+ char cstr[256], sstr[256];
+ ULONG cpid = 0, spid = 0;
+ const char *addr = efl_net_server_address_get(o);
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(pd, EINVAL);
+
+#if _WIN32_WINNT >= 0x0600
+ if (!GetNamedPipeClientProcessId(_efl_net_socket_windows_handle_get(client), &cpid))
+ {
+ char *msg = evil_last_error_get();
+ WRN("server=%p (%s) could not GetNamedPipeClientProcessId(client): %s", o, addr, msg);
+ free(msg);
+ }
+ if (!GetNamedPipeServerProcessId(_efl_net_socket_windows_handle_get(client), &spid))
+ {
+ char *msg = evil_last_error_get();
+ WRN("server=%p (%s) could not GetNamedPipeServerProcessId(client): %s", o, addr, msg);
+ free(msg);
+ }
+#endif
+
+ if (cpid)
+ snprintf(cstr, sizeof(cstr), "%s:%lu", addr, cpid);
+ else
+ eina_strlcpy(cstr, addr, sizeof(cstr));
+
+ if (spid)
+ snprintf(sstr, sizeof(sstr), "%s:%lu", addr, spid);
+ else
+ eina_strlcpy(sstr, addr, sizeof(sstr));
+
+ DBG("server=%p received incoming connection at %s<->%s", o, sstr, cstr);
+
+ efl_ref(o); /* will trigger events, which call user which may delete us */
+
+ if ((pd->clients_limit > 0) && (pd->clients_count >= pd->clients_limit))
+ {
+ if (!pd->clients_reject_excess)
+ {
+ /* keep queueing, but do not call user */
+
+ pd->pending_clients = eina_list_append(pd->pending_clients, client);
+ if (pd->next_client == client)
+ pd->next_client = NULL;
+
+ efl_net_socket_address_local_set(client, sstr);
+ efl_net_socket_address_remote_set(client, cstr);
+
+ DBG("server=%p queued client %p", o, client);
+ }
+ else
+ {
+ DBG("server=%p rejecting client %p", o, client);
+ efl_event_callback_call(o, EFL_NET_SERVER_EVENT_CLIENT_REJECTED, cstr);
+
+ if (pd->next_client != client)
+ efl_del(client);
+ else
+ {
+ HANDLE h = _efl_net_socket_windows_handle_get(client);
+ DisconnectNamedPipe(h);
+
+ /* reuse existing pipe for a new connection */
+ _efl_net_server_windows_client_listen(o, pd);
+ }
+ }
+ }
+ else
+ {
+ efl_net_socket_address_local_set(client, sstr);
+ efl_net_socket_address_remote_set(client, cstr);
+
+ DBG("server=%p announcing client %p", o, client);
+ if (pd->next_client == client)
+ pd->next_client = NULL;
+ efl_net_server_client_announce(o, client);
+ }
+
+ if (!pd->next_client)
+ _efl_net_server_windows_client_new(o, pd);
+
+ efl_unref(o);
+ return 0;
+}
+
+static Eina_Error
+_efl_net_server_windows_client_listen_failure(void *data, Eo *client EINA_UNUSED, DWORD win32err)
+{
+ Eo *o = data;
+ Eina_Error err = EINVAL;
+
+ if (eina_log_domain_level_check(_ecore_con_log_dom, EINA_LOG_LEVEL_DBG))
+ {
+ char *msg = _efl_net_windows_error_msg_get(win32err);
+ DBG("server=%p failed to accept connection at %s: error=%s",
+ o, efl_net_server_address_get(o), msg);
+ free(msg);
+ }
+
+ switch (win32err)
+ {
+ case ERROR_PIPE_CONNECTED: err = 0; break;
+ case ERROR_NO_DATA: err = ENOTCONN; break;
+ case ERROR_OPERATION_ABORTED: err = ECANCELED; break;
+ }
+
+ if (err)
+ efl_event_callback_call(o, EFL_NET_SERVER_EVENT_ERROR, &err);
+
+ // TODO: create a new one on failure?
+
+ return err;
+}
+
+static Eina_Error
+_efl_net_server_windows_client_listen(Eo *o, Efl_Net_Server_Windows_Data *pd)
+{
+ Efl_Net_Socket_Windows_Operation *op;
+ HANDLE h;
+ OVERLAPPED *ovl;
+ DWORD win32err;
+ BOOL r;
+
+ op = _efl_net_socket_windows_operation_new(pd->next_client,
+ _efl_net_server_windows_client_listen_success,
+ _efl_net_server_windows_client_listen_failure,
+ o);
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(op, EINVAL);
+
+ h = _efl_net_socket_windows_handle_get(pd->next_client);
+ ovl = _efl_net_socket_windows_operation_overlapped_get(op);
+
+ r = ConnectNamedPipe(h, ovl);
+ win32err = GetLastError();
+ if (eina_log_domain_level_check(_ecore_con_log_dom, EINA_LOG_LEVEL_DBG))
+ {
+ char *msg = _efl_net_windows_error_msg_get(win32err);
+ DBG("server=%p (%s) socket=%p ConnectNamedPipe(%p, %p)=%s",
+ o, efl_net_server_address_get(o),
+ pd->next_client, h, ovl, msg);
+ free(msg);
+ }
+
+ if (!r)
+ {
+ if (win32err == ERROR_IO_PENDING)
+ return 0;
+ else
+ {
+ return _efl_net_socket_windows_operation_failed(op, win32err);
+ }
+ }
+
+ return _efl_net_socket_windows_operation_succeeded(op, 0);
+}
+
+static Eina_Error
+_efl_net_server_windows_client_new(Eo *o, Efl_Net_Server_Windows_Data *pd)
+{
+ Eina_Error err;
+ HANDLE h;
+
+ h = CreateNamedPipe(pd->address,
+ PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED |
+ (pd->first ? FILE_FLAG_FIRST_PIPE_INSTANCE : 0),
+ PIPE_TYPE_BYTE | PIPE_READMODE_BYTE |
+ (pd->allow_remote ? PIPE_ACCEPT_REMOTE_CLIENTS : PIPE_REJECT_REMOTE_CLIENTS),
+ (pd->clients_limit > 0 ? pd->clients_limit : PIPE_UNLIMITED_INSTANCES),
+ 4096, 4096, INFINITE, NULL);
+ if (h == INVALID_HANDLE_VALUE)
+ {
+ DWORD win32err = GetLastError();
+ switch (win32err)
+ {
+ case ERROR_ACCESS_DENIED: return EACCES;
+ case ERROR_INVALID_PARAMETER: return EINVAL;
+ default:
+ {
+ char *msg = evil_format_message(win32err);
+ ERR("Unexpected win32err=%lu (%s)", win32err, msg);
+ return EINVAL;
+ }
+ }
+ }
+
+ pd->next_client = efl_add(EFL_NET_SOCKET_WINDOWS_CLASS, o,
+ efl_io_closer_close_on_destructor_set(efl_added, EINA_TRUE));
+ if (!pd->next_client)
+ {
+ err = ENOMEM;
+ goto error_socket;
+ }
+
+ err = _efl_net_socket_windows_init(pd->next_client, h);
+ if (err) goto error_init;
+
+ pd->first = EINA_FALSE;
+
+ err = _efl_net_server_windows_client_listen(o, pd);
+ if (err) return err;
+
+ efl_net_server_serving_set(o, EINA_TRUE);
+ return 0;
+
+ error_init:
+ efl_del(pd->next_client);
+ error_socket:
+ CloseHandle(h);
+ return err;
+}
+
+EOLIAN static void
+_efl_net_server_windows_allow_remote_set(Eo *o EINA_UNUSED, Efl_Net_Server_Windows_Data *pd, Eina_Bool allow_remote)
+{
+ pd->allow_remote = allow_remote;
+}
+
+EOLIAN static Eina_Bool
+_efl_net_server_windows_allow_remote_get(Eo *o EINA_UNUSED, Efl_Net_Server_Windows_Data *pd)
+{
+ return pd->allow_remote;
+}
+
+EOLIAN static Eo *
+_efl_net_server_windows_efl_object_constructor(Eo *o, Efl_Net_Server_Windows_Data *pd)
+{
+ pd->first = EINA_TRUE;
+
+ return efl_constructor(efl_super(o, MY_CLASS));
+}
+
+EOLIAN static void
+_efl_net_server_windows_efl_object_destructor(Eo *o, Efl_Net_Server_Windows_Data *pd)
+{
+ if (pd->next_client)
+ {
+ efl_del(pd->next_client);
+ pd->next_client = NULL;
+ }
+
+ while (pd->pending_clients)
+ {
+ Eo *client = pd->pending_clients->data;
+ pd->pending_clients = eina_list_remove_list(pd->pending_clients, pd->pending_clients);
+ efl_del(client);
+ }
+
+ efl_destructor(efl_super(o, MY_CLASS));
+}
+
+EOLIAN static void
+_efl_net_server_windows_efl_net_server_address_set(Eo *o EINA_UNUSED, Efl_Net_Server_Windows_Data *pd, const char *address)
+{
+ const char *tmp = eina_stringshare_printf(PIPE_NS "%s", address);
+ eina_stringshare_del(pd->address);
+ pd->address = tmp;
+}
+
+EOLIAN static const char *
+_efl_net_server_windows_efl_net_server_address_get(Eo *o EINA_UNUSED, Efl_Net_Server_Windows_Data *pd)
+{
+ return pd->address + strlen(PIPE_NS);
+}
+
+static void
+_efl_net_server_windows_pending_announce_job(void *data, const Efl_Event *ev EINA_UNUSED)
+{
+ Eo *o = data;
+ Efl_Net_Server_Windows_Data *pd = efl_data_scope_get(o, MY_CLASS);
+ Eo *client;
+
+ pd->pending_announcer_job = NULL;
+
+ if (!pd->pending_clients) return;
+ if ((pd->clients_limit > 0) && (pd->clients_limit <= pd->clients_count)) return;
+
+ client = pd->pending_clients->data;
+ pd->pending_clients = eina_list_remove_list(pd->pending_clients, pd->pending_clients);
+
+ efl_net_server_client_announce(o, client);
+}
+
+static void
+_efl_net_server_windows_pending_announce_job_schedule(Eo *o, Efl_Net_Server_Windows_Data *pd)
+{
+ Eo *loop;
+
+ if (pd->pending_announcer_job) return;
+ if (!pd->pending_clients) return;
+ if ((pd->clients_limit > 0) && (pd->clients_limit <= pd->clients_count)) return;
+
+ loop = efl_loop_get(o);
+ if (!loop) return;
+ efl_future_use(&pd->pending_announcer_job, efl_loop_job(loop, o));
+ efl_future_then(pd->pending_announcer_job, _efl_net_server_windows_pending_announce_job, NULL, NULL, o);
+ efl_future_link(o, pd->pending_announcer_job);
+}
+
+EOLIAN static void
+_efl_net_server_windows_efl_net_server_clients_count_set(Eo *o, Efl_Net_Server_Windows_Data *pd, unsigned int count)
+{
+ pd->clients_count = count;
+
+ /* a job to avoid blowing the stack with recursion,
+ * do each announcement from main loop
+ */
+ _efl_net_server_windows_pending_announce_job_schedule(o, pd);
+}
+
+EOLIAN static unsigned int
+_efl_net_server_windows_efl_net_server_clients_count_get(Eo *o EINA_UNUSED, Efl_Net_Server_Windows_Data *pd)
+{
+ return pd->clients_count;
+}
+
+EOLIAN static void
+_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)
+{
+ pd->clients_limit = limit;
+ pd->clients_reject_excess = reject_excess;
+
+ if ((limit > 0) && (reject_excess))
+ {
+ while (pd->pending_clients)
+ {
+ Eina_List *last = eina_list_last(pd->pending_clients);
+ Eo *client = eina_list_data_get(last);
+ efl_del(client);
+ pd->pending_clients = eina_list_remove_list(pd->pending_clients, last);
+ }
+ }
+
+ _efl_net_server_windows_pending_announce_job_schedule(o, pd);
+}
+
+EOLIAN static void
+_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)
+{
+ if (limit) *limit = pd->clients_limit;
+ if (reject_excess) *reject_excess = pd->clients_reject_excess;
+}
+
+EOLIAN static void
+_efl_net_server_windows_efl_net_server_serving_set(Eo *o EINA_UNUSED, Efl_Net_Server_Windows_Data *pd, Eina_Bool serving)
+{
+ if (pd->serving == serving) return;
+ pd->serving = serving;
+ if (serving)
+ efl_event_callback_call(o, EFL_NET_SERVER_EVENT_SERVING, NULL);
+}
+
+EOLIAN static Eina_Bool
+_efl_net_server_windows_efl_net_server_serving_get(Eo *o EINA_UNUSED, Efl_Net_Server_Windows_Data *pd)
+{
+ return pd->serving;
+}
+
+EOLIAN static Eina_Error
+_efl_net_server_windows_efl_net_server_serve(Eo *o, Efl_Net_Server_Windows_Data *pd, const char *address)
+{
+ EINA_SAFETY_ON_NULL_RETURN_VAL(address, EINVAL);
+ EINA_SAFETY_ON_TRUE_RETURN_VAL(strchr(address, '/') != NULL, EINVAL);
+ EINA_SAFETY_ON_TRUE_RETURN_VAL(strchr(address, '\\') != NULL, EINVAL);
+ EINA_SAFETY_ON_TRUE_RETURN_VAL(strlen(PIPE_NS) + strlen(address) >= 256, EINVAL);
+ EINA_SAFETY_ON_TRUE_RETURN_VAL(pd->serving, EALREADY);
+
+ efl_net_server_address_set(o, address);
+
+ return _efl_net_server_windows_client_new(o, pd);
+}
+
+static void
+_efl_net_server_windows_client_event_closed(void *data, const Efl_Event *event)
+{
+ Eo *server = data;
+ Eo *client = event->object;
+
+ efl_event_callback_del(client, EFL_IO_CLOSER_EVENT_CLOSED, _efl_net_server_windows_client_event_closed, server);
+ if (efl_parent_get(client) == server)
+ efl_parent_set(client, NULL);
+
+ efl_net_server_clients_count_set(server, efl_net_server_clients_count_get(server) - 1);
+}
+
+EOLIAN static Eina_Bool
+_efl_net_server_windows_efl_net_server_client_announce(Eo *o, Efl_Net_Server_Windows_Data *pd, Efl_Net_Socket *client)
+{
+ Eina_Error err;
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(client, EINA_FALSE);
+ EINA_SAFETY_ON_FALSE_GOTO(efl_isa(client, EFL_NET_SOCKET_INTERFACE), wrong_type);
+ EINA_SAFETY_ON_FALSE_GOTO(efl_parent_get(client) == o, wrong_parent);
+ efl_event_callback_call(o, EFL_NET_SERVER_EVENT_CLIENT_ADD, client);
+
+ if (efl_parent_get(client) != o)
+ {
+ DBG("client %s was reparented! Ignoring it...",
+ efl_net_socket_address_remote_get(client));
+ return EINA_TRUE;
+ }
+
+ if (efl_ref_get(client) == 1) /* users must take a reference themselves */
+ {
+ DBG("client %s was not handled, closing it...",
+ efl_net_socket_address_remote_get(client));
+ if (pd->next_client)
+ efl_del(client);
+ else
+ {
+ HANDLE h = _efl_net_socket_windows_handle_get(client);
+ DisconnectNamedPipe(h);
+
+ /* reuse existing pipe for a new connection */
+ pd->next_client = client;
+ _efl_net_server_windows_client_listen(o, pd);
+ }
+ return EINA_FALSE;
+ }
+ else if (efl_io_closer_closed_get(client))
+ {
+ DBG("client %s was closed from 'client,add', delete it...",
+ efl_net_socket_address_remote_get(client));
+ efl_del(client);
+ return EINA_FALSE;
+ }
+
+ efl_net_server_clients_count_set(o, efl_net_server_clients_count_get(o) + 1);
+ efl_event_callback_add(client, EFL_IO_CLOSER_EVENT_CLOSED, _efl_net_server_windows_client_event_closed, o);
+
+ err = _efl_net_socket_windows_io_start(client);
+ if (err)
+ {
+ WRN("server=%p client=%p failed to start I/O: %s", o, client, eina_error_msg_get(err));
+ if (!efl_io_closer_closed_get(client))
+ efl_io_closer_close(client);
+ }
+
+ return EINA_TRUE;
+
+ wrong_type:
+ ERR("%p client %p (%s) doesn't implement Efl.Net.Socket interface, deleting it.", o, client, efl_class_name_get(efl_class_get(client)));
+ efl_io_closer_close(client);
+ efl_del(client);
+ return EINA_FALSE;
+
+ wrong_parent:
+ 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));
+ efl_io_closer_close(client);
+ efl_del(client);
+ return EINA_FALSE;
+
+}
+
+#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 @@
+class Efl.Net.Server.Windows (Efl.Loop_User, Efl.Net.Server) {
+ [[A Windows NamedPipe server.
+
+ The @Efl.Net.Server.serve method will call CreateNamedPipe()
+ directly, thus path will be accessed and created in that
+ method. If the created socket must be subject to some special
+ mode or user, change before executing that method.
+
+ @since 1.19
+ ]]
+
+ methods {
+ @property allow_remote {
+ [[If server allows remote (different machine) clients.
+
+ If this property is $true, then it will allow clients to
+ connect from remote machines. If $false (default), then
+ just local clients are allowed.
+ ]]
+ values {
+ allow_remote: bool; [[If $true, server will allow remote machines to connect.]]
+ }
+ }
+ }
+
+ implements {
+ Efl.Object.constructor;
+ Efl.Object.destructor;
+ Efl.Net.Server.address { get; set; }
+ Efl.Net.Server.clients_count { get; set; }
+ Efl.Net.Server.clients_limit { get; set; }
+ Efl.Net.Server.serving { get; set; }
+ Efl.Net.Server.serve; [[address parameter will have "\\\\.\\pipe\\" prepended]]
+ Efl.Net.Server.client_announce;
+ }
+}
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 @@
+#define EFL_NET_SOCKET_WINDOWS_PROTECTED 1
+#define EFL_IO_READER_PROTECTED 1
+#define EFL_IO_WRITER_PROTECTED 1
+#define EFL_IO_CLOSER_PROTECTED 1
+#define EFL_NET_SOCKET_PROTECTED 1
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include "Ecore.h"
+#include "Ecore_Con.h"
+#include "ecore_con_private.h"
+
+#define MY_CLASS EFL_NET_SOCKET_WINDOWS_CLASS
+
+#define BUFFER_SIZE (4 * 4096)
+
+typedef struct _Efl_Net_Socket_Windows_Data
+{
+ Eina_Stringshare *address_local;
+ Eina_Stringshare *address_remote;
+ Eina_List *pending_ops;
+ struct {
+ union {
+ uint8_t *bytes;
+ void *mem;
+ };
+ DWORD len;
+ DWORD used;
+ DWORD base;
+ Efl_Net_Socket_Windows_Operation *pending;
+ } recv;
+ struct {
+ union {
+ uint8_t *bytes;
+ void *mem;
+ };
+ DWORD len;
+ DWORD used;
+ Efl_Net_Socket_Windows_Operation *pending;
+ } send;
+ HANDLE handle;
+ Eina_Bool can_read;
+ Eina_Bool eos;
+ Eina_Bool pending_eos;
+ Eina_Bool can_write;
+ Eina_Bool io_started;
+ Eina_Bool close_on_exec;
+ Eina_Bool close_on_destructor;
+} Efl_Net_Socket_Windows_Data;
+
+struct _Efl_Net_Socket_Windows_Operation
+{
+ OVERLAPPED base;
+ Efl_Net_Socket_Windows_Operation_Success_Cb success_cb;
+ Efl_Net_Socket_Windows_Operation_Failure_Cb failure_cb;
+ const void *data;
+ Ecore_Win32_Handler *event_handler;
+ Eo *o;
+ Eina_Bool deleting;
+};
+
+/*
+ * differences to evil_format_message():
+ * - shorter string
+ * - no newline
+ * - fallback to error code if format fails
+ */
+char *
+_efl_net_windows_error_msg_get(DWORD win32err)
+{
+ LPTSTR msg;
+ char *str;
+ char *disp;
+ int len, reqlen;
+
+ if (!FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ win32err,
+ 0, /* Default language */
+ (LPTSTR)&msg,
+ 0,
+ NULL))
+ goto fallback;
+
+#ifdef UNICODE
+ str = evil_wchar_to_char(msg);
+#else
+ str = msg;
+#endif /* UNICODE */
+
+ len = strlen(str);
+ if ((len > 0) && (str[len - 1] == '\n'))
+ str[--len] = '\0';
+ if ((len > 0) && (str[len - 1] == '\r'))
+ str[--len] = '\0';
+ if ((len > 0) && (str[len - 1] == '.'))
+ str[--len] = '\0';
+
+ reqlen = snprintf("", 0, "%lu(%s)", win32err, str);
+ if (reqlen < 1) goto error;
+ disp = malloc(reqlen + 1);
+ if (!disp) goto error;
+ snprintf(disp, reqlen + 1, "%lu(%s)", win32err, str);
+
+#ifdef UNICODE
+ free(str);
+#endif /* UNICODE */
+ LocalFree(msg);
+
+ return disp;
+
+ error:
+#ifdef UNICODE
+ free(str);
+#endif /* UNICODE */
+ LocalFree(msg);
+ fallback:
+ {
+ char buf[64];
+ snprintf(buf, sizeof(buf), "%ld", win32err);
+ return strdup(buf);
+ }
+}
+
+static void
+_efl_net_socket_windows_handle_close(HANDLE h)
+{
+ if (!FlushFileBuffers(h))
+ {
+ DWORD win32err = GetLastError();
+ if (win32err != ERROR_PIPE_NOT_CONNECTED)
+ {
+ char *msg = _efl_net_windows_error_msg_get(GetLastError());
+ WRN("HANDLE=%p could not flush buffers: %s", h, msg);
+ free(msg);
+ }
+ }
+ if (!DisconnectNamedPipe(h))
+ {
+ DWORD win32err = GetLastError();
+ if ((win32err != ERROR_NOT_SUPPORTED) && /* dialer socket don't support it */
+ (win32err != ERROR_PIPE_NOT_CONNECTED))
+ {
+ char *msg = _efl_net_windows_error_msg_get(win32err);
+ WRN("HANDLE=%p could not disconnect: %s", h, msg);
+ free(msg);
+ }
+ }
+ CloseHandle(h);
+ DBG("HANDLE=%p closed", h);
+}
+
+static Eina_Bool _efl_net_socket_windows_operation_event(void *, Ecore_Win32_Handler *wh);
+
+Efl_Net_Socket_Windows_Operation *
+_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)
+{
+ Efl_Net_Socket_Windows_Operation *op;
+ Efl_Net_Socket_Windows_Data *pd = efl_data_scope_get(o, EFL_NET_SOCKET_WINDOWS_CLASS);
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(pd, NULL);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(success_cb, NULL);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(failure_cb, NULL);
+
+ op = calloc(1, sizeof(*op));
+ EINA_SAFETY_ON_NULL_RETURN_VAL(op, NULL);
+
+ op->base.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (!op->base.hEvent)
+ {
+ char *msg = _efl_net_windows_error_msg_get(GetLastError());
+ ERR("socket=%p success_cb=%p failure_cb=%p data=%p: error=%s",
+ op->o, op->success_cb, op->failure_cb, op->data, msg);
+ free(msg);
+ goto error_event;
+ }
+
+ op->event_handler = ecore_main_win32_handler_add(op->base.hEvent, _efl_net_socket_windows_operation_event, op);
+ if (!op->event_handler)
+ {
+ ERR("socket=%p success_cb=%p failure_cb=%p data=%p: could not create event handler",
+ op->o, op->success_cb, op->failure_cb, op->data);
+ goto error_handler;
+ }
+
+ op->success_cb = success_cb;
+ op->failure_cb = failure_cb;
+ op->data = data;
+ op->o = o;
+ pd->pending_ops = eina_list_append(pd->pending_ops, op);
+
+ DBG("op=%p (socket=%p) success_cb=%p failure_cb=%p data=%p",
+ op, op->o, op->success_cb, op->failure_cb, op->data);
+
+ return op;
+
+ error_handler:
+ CloseHandle(op->base.hEvent);
+ error_event:
+ free(op);
+ return NULL;
+}
+
+static Eina_Error
+_efl_net_socket_windows_operation_done(Efl_Net_Socket_Windows_Operation *op, DWORD win32err, DWORD used_size)
+{
+ Efl_Net_Socket_Windows_Data *pd;
+ Eina_Error err = 0;
+
+ if (eina_log_domain_level_check(_ecore_con_log_dom, EINA_LOG_LEVEL_DBG))
+ {
+ char *msg = _efl_net_windows_error_msg_get(win32err);
+ DBG("op=%p (socket=%p) success_cb=%p failure_cb=%p data=%p error=%s used_size=%lu",
+ op, op->o, op->success_cb, op->failure_cb, op->data, msg, used_size);
+ free(msg);
+ }
+
+ op->deleting = EINA_TRUE;
+
+ efl_ref(op->o);
+ pd = efl_data_scope_get(op->o, EFL_NET_SOCKET_WINDOWS_CLASS);
+ if (pd)
+ pd->pending_ops = eina_list_remove(pd->pending_ops, op);
+
+ if (win32err)
+ err = op->failure_cb((void *)op->data, op->o, win32err);
+ else
+ op->success_cb((void *)op->data, op->o, used_size);
+
+ if (op->event_handler)
+ {
+ if (WaitForSingleObject(pd->handle, 0) != WAIT_OBJECT_0)
+ {
+ DWORD used_size = 0;
+ if (GetOverlappedResult(pd->handle, &op->base, &used_size, FALSE))
+ {
+ DBG("op=%p (socket=%p) success_cb=%p failure_cb=%p data=%p GetOverlappedResult(%p, %p, &size=%lu, FALSE)",
+ op, op->o, op->success_cb, op->failure_cb, op->data, pd->handle, &op->base, used_size);
+ }
+ else
+ {
+ win32err = GetLastError();
+ if (eina_log_domain_level_check(_ecore_con_log_dom, EINA_LOG_LEVEL_DBG))
+ {
+ char *msg = _efl_net_windows_error_msg_get(win32err);
+ DBG("op=%p (socket=%p) success_cb=%p failure_cb=%p data=%p GetOverlappedResult(%p, %p, &size=%lu, TRUE)=%s",
+ op, op->o, op->success_cb, op->failure_cb, op->data, pd->handle, &op->base, used_size, msg);
+ free(msg);
+ }
+
+ if (win32err == ERROR_IO_INCOMPLETE)
+ {
+ DBG("op=%p (socket=%p) success_cb=%p failure_cb=%p data=%p: still pending I/O...",
+ op, op->o, op->success_cb, op->failure_cb, op->data);
+ efl_unref(op->o);
+ op->o = NULL;
+ return 0;
+ }
+ }
+ }
+
+ ecore_main_win32_handler_del(op->event_handler);
+ }
+
+#ifndef ERROR_HANDLES_CLOSED
+#define ERROR_HANDLES_CLOSED 676
+#endif
+ if ((win32err == ERROR_HANDLES_CLOSED) && !efl_io_closer_closed_get(op->o))
+ efl_io_closer_close(op->o);
+ efl_unref(op->o);
+
+ CloseHandle(op->base.hEvent);
+ free(op);
+ return err;
+}
+
+Eina_Error
+_efl_net_socket_windows_operation_failed(Efl_Net_Socket_Windows_Operation *op, DWORD win32err)
+{
+ EINA_SAFETY_ON_NULL_RETURN_VAL(op, EINVAL);
+ EINA_SAFETY_ON_TRUE_RETURN_VAL(op->deleting, EINVAL);
+
+ return _efl_net_socket_windows_operation_done(op, win32err, 0);
+}
+
+Eina_Error
+_efl_net_socket_windows_operation_succeeded(Efl_Net_Socket_Windows_Operation *op, DWORD used_size)
+{
+ EINA_SAFETY_ON_NULL_RETURN_VAL(op, EINVAL);
+ EINA_SAFETY_ON_TRUE_RETURN_VAL(op->deleting, EINVAL);
+
+ return _efl_net_socket_windows_operation_done(op, 0, used_size);
+}
+
+static Eina_Bool
+_efl_net_socket_windows_operation_event(void *data, Ecore_Win32_Handler *wh EINA_UNUSED)
+{
+ Efl_Net_Socket_Windows_Operation *op = data;
+ HANDLE h = _efl_net_socket_windows_handle_get(op->o);
+ DWORD used_size = 0;
+
+ if ((op->deleting) || (h == INVALID_HANDLE_VALUE))
+ {
+ DBG("op=%p was deleted and pending I/O completed!", op);
+ CloseHandle(op->base.hEvent);
+ free(op);
+ return ECORE_CALLBACK_CANCEL;
+ }
+
+ op->event_handler = NULL;
+
+ if (GetOverlappedResult(h, &op->base, &used_size, FALSE))
+ {
+ DBG("op=%p (socket=%p) success_cb=%p failure_cb=%p data=%p GetOverlappedResult(%p, %p, &size=%lu, FALSE)",
+ op, op->o, op->success_cb, op->failure_cb, op->data, h, &op->base, used_size);
+ _efl_net_socket_windows_operation_succeeded(op, used_size);
+ }
+ else
+ {
+ DWORD win32err = GetLastError();
+ if (eina_log_domain_level_check(_ecore_con_log_dom, EINA_LOG_LEVEL_DBG))
+ {
+ char *msg = _efl_net_windows_error_msg_get(win32err);
+ DBG("op=%p (socket=%p) success_cb=%p failure_cb=%p data=%p GetOverlappedResult(%p, %p, &size=%lu, FALSE)=%s",
+ op, op->o, op->success_cb, op->failure_cb, op->data, h, &op->base, used_size, msg);
+ free(msg);
+ }
+
+ _efl_net_socket_windows_operation_failed(op, win32err);
+ }
+
+ return ECORE_CALLBACK_CANCEL;
+}
+
+Eina_Error
+_efl_net_socket_windows_init(Eo *o, HANDLE h)
+{
+ Efl_Net_Socket_Windows_Data *pd = efl_data_scope_get(o, MY_CLASS);
+
+ EINA_SAFETY_ON_TRUE_RETURN_VAL(h == INVALID_HANDLE_VALUE, EINVAL);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(pd, EINVAL);
+ EINA_SAFETY_ON_TRUE_RETURN_VAL(pd->handle != INVALID_HANDLE_VALUE, EALREADY);
+
+ pd->handle = h;
+
+ DBG("socket=%p adopted handle=%p", o, h);
+ return 0;
+}
+
+static Eina_Error _efl_net_socket_windows_recv(Eo *o, Efl_Net_Socket_Windows_Data *pd);
+
+static Eina_Error
+_efl_net_socket_windows_recv_success(void *data EINA_UNUSED, Eo *o, DWORD used_size)
+{
+ Efl_Net_Socket_Windows_Data *pd = efl_data_scope_get(o, MY_CLASS);
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(pd, EINVAL);
+
+ pd->recv.pending = NULL;
+ EINA_SAFETY_ON_TRUE_RETURN_VAL(pd->recv.used + used_size > pd->recv.len, EINVAL);
+
+ pd->recv.used += used_size;
+
+ /* calls back the user, may read()/write()/close() */
+ efl_io_reader_can_read_set(o, pd->recv.used > 0);
+
+ if (pd->handle == INVALID_HANDLE_VALUE) return 0;
+ if (pd->recv.used == pd->recv.len) return 0;
+ if (pd->recv.pending) return 0;
+
+ return _efl_net_socket_windows_recv(o, pd);
+}
+
+static Eina_Error
+_efl_net_socket_windows_recv_failure(void *data EINA_UNUSED, Eo *o, DWORD win32err)
+{
+ Efl_Net_Socket_Windows_Data *pd = efl_data_scope_get(o, MY_CLASS);
+
+ if (eina_log_domain_level_check(_ecore_con_log_dom, EINA_LOG_LEVEL_DBG))
+ {
+ char *msg = _efl_net_windows_error_msg_get(win32err);
+ DBG("socket=%p recv error=%s", o, msg);
+ free(msg);
+ }
+
+ pd->recv.pending = NULL;
+
+ if (pd->recv.used > 0)
+ pd->pending_eos = EINA_TRUE; /* eos when buffer drains */
+ else
+ {
+ efl_io_writer_can_write_set(o, EINA_FALSE);
+ efl_io_reader_can_read_set(o, EINA_FALSE);
+ efl_io_reader_eos_set(o, EINA_TRUE);
+ }
+
+ switch (win32err)
+ {
+ case ERROR_INVALID_USER_BUFFER: return EFAULT;
+ case ERROR_NOT_ENOUGH_MEMORY: return ENOMEM;
+ case ERROR_OPERATION_ABORTED: return ECANCELED;
+ case ERROR_BROKEN_PIPE: return EPIPE;
+ default: return EIO;
+ }
+}
+
+static Eina_Error
+_efl_net_socket_windows_recv(Eo *o, Efl_Net_Socket_Windows_Data *pd)
+{
+ Efl_Net_Socket_Windows_Operation *op;
+ OVERLAPPED *ovl;
+ DWORD used_size = 0, win32err;
+ BOOL r;
+
+ EINA_SAFETY_ON_TRUE_RETURN_VAL(pd->recv.pending != NULL, EINPROGRESS);
+
+ if (pd->handle == INVALID_HANDLE_VALUE) return EBADF;
+ if (pd->recv.len == 0) return ENOMEM;
+
+ if (pd->recv.base > 0)
+ {
+ DWORD todo = pd->recv.used - pd->recv.base;
+ if (todo > 0)
+ memmove(pd->recv.bytes, pd->recv.bytes + pd->recv.base, todo);
+ pd->recv.used -= pd->recv.base;
+ pd->recv.base = 0;
+ }
+
+ if (pd->recv.used == pd->recv.len) return ENOSPC;
+
+ op = _efl_net_socket_windows_operation_new(o,
+ _efl_net_socket_windows_recv_success,
+ _efl_net_socket_windows_recv_failure,
+ NULL);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(op, EINVAL);
+ ovl = _efl_net_socket_windows_operation_overlapped_get(op);
+ r = ReadFile(pd->handle,
+ pd->recv.bytes + pd->recv.used,
+ pd->recv.len - pd->recv.used,
+ &used_size, ovl);
+ win32err = GetLastError();
+ if (eina_log_domain_level_check(_ecore_con_log_dom, EINA_LOG_LEVEL_DBG))
+ {
+ char *msg = _efl_net_windows_error_msg_get(win32err);
+ DBG("socket=%p ReadFile(%p, %p, %lu, &size=%lu, %p)=%s",
+ o, pd->handle, pd->recv.bytes + pd->recv.used,
+ pd->recv.len - pd->recv.used, used_size, ovl, msg);
+ free(msg);
+ }
+
+ if (!r)
+ {
+ if (win32err == ERROR_IO_PENDING)
+ {
+ pd->recv.pending = op;
+ return 0;
+ }
+ else
+ {
+ return _efl_net_socket_windows_operation_failed(op, win32err);
+ }
+ }
+
+ return _efl_net_socket_windows_operation_succeeded(op, used_size);
+}
+
+static Eina_Error _efl_net_socket_windows_send(Eo *o, Efl_Net_Socket_Windows_Data *pd);
+
+static Eina_Error
+_efl_net_socket_windows_send_success(void *data EINA_UNUSED, Eo *o, DWORD used_size)
+{
+ Efl_Net_Socket_Windows_Data *pd = efl_data_scope_get(o, MY_CLASS);
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(pd, EINVAL);
+
+ pd->send.pending = NULL;
+ EINA_SAFETY_ON_TRUE_RETURN_VAL(pd->send.used < used_size, EINVAL);
+
+ if (used_size > 0)
+ {
+ DWORD todo = pd->send.used - used_size;
+ if (todo > 0)
+ memmove(pd->send.bytes, pd->send.bytes + used_size, todo);
+ pd->send.used -= used_size;
+ }
+
+ /* calls back the user, may read()/write()/close() */
+ /* only can_write if we're fully done with previous request! */
+ efl_io_writer_can_write_set(o, pd->send.used == 0);
+
+ if (pd->handle == INVALID_HANDLE_VALUE) return 0;
+ if (pd->send.used == 0) return 0;
+ if (pd->send.pending) return 0;
+
+ return _efl_net_socket_windows_send(o, pd);
+}
+
+static Eina_Error
+_efl_net_socket_windows_send_failure(void *data EINA_UNUSED, Eo *o, DWORD win32err)
+{
+ Efl_Net_Socket_Windows_Data *pd = efl_data_scope_get(o, MY_CLASS);
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(pd, EINVAL);
+
+ if (eina_log_domain_level_check(_ecore_con_log_dom, EINA_LOG_LEVEL_DBG))
+ {
+ char *msg = _efl_net_windows_error_msg_get(win32err);
+ DBG("socket=%p send error=%s", o, msg);
+ free(msg);
+ }
+
+ pd->send.pending = NULL;
+
+ efl_io_writer_can_write_set(o, EINA_FALSE);
+
+ if (pd->recv.used > 0)
+ pd->pending_eos = EINA_TRUE; /* eos when buffer drains */
+ else
+ {
+ efl_io_reader_can_read_set(o, EINA_FALSE);
+ efl_io_reader_eos_set(o, EINA_TRUE);
+ }
+
+ switch (win32err)
+ {
+ case ERROR_INVALID_USER_BUFFER: return EFAULT;
+ case ERROR_NOT_ENOUGH_MEMORY: return ENOMEM;
+ case ERROR_OPERATION_ABORTED: return ECANCELED;
+ case ERROR_BROKEN_PIPE: return EPIPE;
+ default: return EIO;
+ }
+}
+
+static Eina_Error
+_efl_net_socket_windows_send(Eo *o, Efl_Net_Socket_Windows_Data *pd)
+{
+ Efl_Net_Socket_Windows_Operation *op;
+ OVERLAPPED *ovl;
+ DWORD used_size = 0, win32err;
+ BOOL r;
+
+ EINA_SAFETY_ON_TRUE_RETURN_VAL(pd->send.pending != NULL, EINPROGRESS);
+
+ if (pd->handle == INVALID_HANDLE_VALUE) return EBADF;
+ if (pd->send.used == 0) return 0;
+
+ op = _efl_net_socket_windows_operation_new(o,
+ _efl_net_socket_windows_send_success,
+ _efl_net_socket_windows_send_failure,
+ NULL);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(op, EINVAL);
+
+ ovl = _efl_net_socket_windows_operation_overlapped_get(op);
+
+ r = WriteFile(pd->handle,
+ pd->send.bytes,
+ pd->send.used,
+ &used_size, ovl);
+ win32err = GetLastError();
+ if (eina_log_domain_level_check(_ecore_con_log_dom, EINA_LOG_LEVEL_DBG))
+ {
+ char *msg = _efl_net_windows_error_msg_get(win32err);
+ DBG("socket=%p WriteFile(%p, %p, %lu, &size=%lu, %p)=%s",
+ o, pd->handle, pd->send.bytes, pd->send.used, used_size, ovl, msg);
+ free(msg);
+ }
+
+ if (!r)
+ {
+ if (win32err == ERROR_IO_PENDING)
+ {
+ pd->send.pending = op;
+ return 0;
+ }
+ else
+ {
+ return _efl_net_socket_windows_operation_failed(op, win32err);
+ }
+ }
+
+ return _efl_net_socket_windows_operation_succeeded(op, used_size);
+}
+
+Eina_Error
+_efl_net_socket_windows_io_start(Eo *o)
+{
+ Efl_Net_Socket_Windows_Data *pd = efl_data_scope_get(o, MY_CLASS);
+ Eina_Error err;
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(pd, EINVAL);
+ EINA_SAFETY_ON_TRUE_RETURN_VAL(pd->io_started, EALREADY);
+
+ if (!pd->recv.mem)
+ {
+ pd->recv.mem = malloc(BUFFER_SIZE);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(pd->recv.mem, ENOMEM);
+ pd->recv.len = BUFFER_SIZE;
+ pd->recv.used = 0;
+ }
+
+ if (!pd->send.mem)
+ {
+ pd->send.mem = malloc(BUFFER_SIZE);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(pd->send.mem, ENOMEM);
+ pd->send.len = BUFFER_SIZE;
+ pd->send.used = 0;
+ }
+
+ DBG("socket=%p starting I/O...", o);
+ err = _efl_net_socket_windows_recv(o, pd);
+ if (err) return err;
+
+ efl_io_writer_can_write_set(o, EINA_TRUE);
+ pd->io_started = EINA_TRUE;
+ return 0;
+}
+
+HANDLE
+_efl_net_socket_windows_handle_get(const Eo *o)
+{
+ Efl_Net_Socket_Windows_Data *pd = efl_data_scope_get(o, MY_CLASS);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(pd, INVALID_HANDLE_VALUE);
+ return pd->handle;
+}
+
+EOLIAN static Eo *
+_efl_net_socket_windows_efl_object_constructor(Eo *o, Efl_Net_Socket_Windows_Data *pd)
+{
+ pd->handle = INVALID_HANDLE_VALUE;
+
+ return efl_constructor(efl_super(o, MY_CLASS));
+}
+
+EOLIAN static void
+_efl_net_socket_windows_efl_object_destructor(Eo *o, Efl_Net_Socket_Windows_Data *pd)
+{
+ if (efl_io_closer_close_on_destructor_get(o) &&
+ (!efl_io_closer_closed_get(o)))
+ {
+ efl_event_freeze(o);
+ efl_io_closer_close(o);
+ efl_event_thaw(o);
+ }
+
+ efl_destructor(efl_super(o, MY_CLASS));
+
+ eina_stringshare_replace(&pd->address_local, NULL);
+ eina_stringshare_replace(&pd->address_remote, NULL);
+
+ free(pd->recv.mem);
+ pd->recv.mem = NULL;
+ pd->recv.len = 0;
+ pd->recv.used = 0;
+ pd->recv.base = 0;
+
+ free(pd->send.mem);
+ pd->send.mem = NULL;
+ pd->send.len = 0;
+ pd->send.used = 0;
+}
+
+EOLIAN static Eina_Error
+_efl_net_socket_windows_efl_io_closer_close(Eo *o, Efl_Net_Socket_Windows_Data *pd)
+{
+ HANDLE h;
+
+ EINA_SAFETY_ON_TRUE_RETURN_VAL(efl_io_closer_closed_get(o), EBADF);
+
+ DBG("socket=%p closing handle=%p", o, pd->handle);
+
+ if (pd->pending_ops)
+ {
+ if (pd->send.pending)
+ {
+ Efl_Net_Socket_Windows_Operation *op = pd->send.pending;
+ DWORD used_size = 0;
+ DBG("op=%p (socket=%p) success_cb=%p failure_cb=%p data=%p: wait %lu bytes pending write...",
+ op, op->o, op->success_cb, op->failure_cb, op->data, pd->send.used);
+ if (GetOverlappedResult(pd->handle, &op->base, &used_size, TRUE))
+ {
+ DBG("op=%p (socket=%p) success_cb=%p failure_cb=%p data=%p GetOverlappedResult(%p, %p, &size=%lu, TRUE)",
+ op, op->o, op->success_cb, op->failure_cb, op->data, pd->handle, &op->base, used_size);
+ }
+ else
+ {
+ DWORD win32err = GetLastError();
+ if (eina_log_domain_level_check(_ecore_con_log_dom, EINA_LOG_LEVEL_DBG))
+ {
+ char *msg = _efl_net_windows_error_msg_get(win32err);
+ DBG("op=%p (socket=%p) success_cb=%p failure_cb=%p data=%p GetOverlappedResult(%p, %p, &size=%lu, TRUE)=%s",
+ op, op->o, op->success_cb, op->failure_cb, op->data, pd->handle, &op->base, used_size, msg);
+ free(msg);
+ }
+ }
+ }
+ if (!FlushFileBuffers(pd->handle))
+ {
+ char *msg = _efl_net_windows_error_msg_get(GetLastError());
+ WRN("HANDLE=%p could not flush buffers: %s", pd->handle, msg);
+ free(msg);
+ }
+ if (CancelIo(pd->handle))
+ DBG("socket=%p CancelIo(%p)", o, pd->handle);
+ else if (eina_log_domain_level_check(_ecore_con_log_dom, EINA_LOG_LEVEL_DBG))
+ {
+ char *msg = _efl_net_windows_error_msg_get(GetLastError());
+ WRN("socket=%p CancelIo(%p)=%s", o, pd->handle, msg);
+ free(msg);
+ }
+ }
+
+ while (pd->pending_ops)
+ _efl_net_socket_windows_operation_failed(pd->pending_ops->data, ERROR_OPERATION_ABORTED);
+
+ efl_io_writer_can_write_set(o, EINA_FALSE);
+ efl_io_reader_can_read_set(o, EINA_FALSE);
+ efl_io_reader_eos_set(o, EINA_TRUE);
+
+ h = InterlockedExchangePointer(&pd->handle, INVALID_HANDLE_VALUE);
+ if (h != INVALID_HANDLE_VALUE)
+ _efl_net_socket_windows_handle_close(h);
+
+ efl_event_callback_call(o, EFL_IO_CLOSER_EVENT_CLOSED, NULL);
+
+ return 0;
+}
+
+EOLIAN static Eina_Bool
+_efl_net_socket_windows_efl_io_closer_closed_get(Eo *o EINA_UNUSED, Efl_Net_Socket_Windows_Data *pd)
+{
+ return pd->handle == INVALID_HANDLE_VALUE;
+}
+
+EOLIAN static Eina_Bool
+_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)
+{
+ DBG("close on exec is not supported on windows");
+ pd->close_on_exec = close_on_exec;
+ return EINA_FALSE;
+}
+
+EOLIAN static Eina_Bool
+_efl_net_socket_windows_efl_io_closer_close_on_exec_get(Eo *o EINA_UNUSED, Efl_Net_Socket_Windows_Data *pd)
+{
+ return pd->close_on_exec;
+}
+
+EOLIAN static void
+_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)
+{
+ pd->close_on_destructor = close_on_destructor;
+}
+
+EOLIAN static Eina_Bool
+_efl_net_socket_windows_efl_io_closer_close_on_destructor_get(Eo *o EINA_UNUSED, Efl_Net_Socket_Windows_Data *pd)
+{
+ return pd->close_on_destructor;
+}
+
+EOLIAN static Eina_Error
+_efl_net_socket_windows_efl_io_reader_read(Eo *o, Efl_Net_Socket_Windows_Data *pd, Eina_Rw_Slice *rw_slice)
+{
+ Eina_Slice ro_slice;
+ DWORD remaining;
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(rw_slice, EINVAL);
+ EINA_SAFETY_ON_TRUE_RETURN_VAL(pd->recv.base > pd->recv.used, EINVAL);
+
+ ro_slice.len = pd->recv.used - pd->recv.base;
+ if (ro_slice.len == 0)
+ {
+ rw_slice->len = 0;
+ if (pd->pending_eos)
+ {
+ efl_io_reader_eos_set(o, EINA_TRUE);
+ return 0;
+ }
+ return EAGAIN;
+ }
+ ro_slice.bytes = pd->recv.bytes + pd->recv.base;
+
+ *rw_slice = eina_rw_slice_copy(*rw_slice, ro_slice);
+
+ pd->recv.base += rw_slice->len;
+ remaining = pd->recv.used - pd->recv.base;
+
+ efl_io_reader_can_read_set(o, remaining > 0);
+
+ if ((pd->pending_eos) && (remaining == 0))
+ {
+ efl_io_reader_eos_set(o, EINA_TRUE);
+ return 0;
+ }
+
+ if (!pd->recv.pending)
+ {
+ DBG("recv more from socket=%p", o);
+ _efl_net_socket_windows_recv(o, pd);
+ return 0;
+ }
+
+ return 0;
+}
+
+EOLIAN static void
+_efl_net_socket_windows_efl_io_reader_can_read_set(Eo *o, Efl_Net_Socket_Windows_Data *pd, Eina_Bool can_read)
+{
+ EINA_SAFETY_ON_TRUE_RETURN(efl_io_closer_closed_get(o) && can_read);
+ if (pd->can_read == can_read) return;
+ pd->can_read = can_read;
+ efl_event_callback_call(o, EFL_IO_READER_EVENT_CAN_READ_CHANGED, NULL);
+}
+
+EOLIAN static Eina_Bool
+_efl_net_socket_windows_efl_io_reader_can_read_get(Eo *o EINA_UNUSED, Efl_Net_Socket_Windows_Data *pd)
+{
+ return pd->can_read;
+}
+
+EOLIAN static void
+_efl_net_socket_windows_efl_io_reader_eos_set(Eo *o, Efl_Net_Socket_Windows_Data *pd, Eina_Bool is_eos)
+{
+ if (pd->eos == is_eos) return;
+ pd->eos = is_eos;
+ if (is_eos)
+ efl_event_callback_call(o, EFL_IO_READER_EVENT_EOS, NULL);
+}
+
+EOLIAN static Eina_Bool
+_efl_net_socket_windows_efl_io_reader_eos_get(Eo *o EINA_UNUSED, Efl_Net_Socket_Windows_Data *pd)
+{
+ return pd->eos;
+}
+
+EOLIAN static Eina_Error
+_efl_net_socket_windows_efl_io_writer_write(Eo *o, Efl_Net_Socket_Windows_Data *pd, Eina_Slice *slice, Eina_Slice *remaining)
+{
+ Eina_Error err = EINVAL;
+ DWORD available, todo;
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(slice, EINVAL);
+ EINA_SAFETY_ON_NULL_GOTO(slice->mem, error);
+ EINA_SAFETY_ON_TRUE_GOTO(efl_io_closer_closed_get(o), error);
+ err = ENOMEM;
+ EINA_SAFETY_ON_NULL_GOTO(pd->send.mem, error);
+
+ /* note: do not queue more data if one is already pending,
+ * otherwise we over-commit a lot and on write() -> close(), it
+ * would take us more than simply waiting on the pending send to
+ * complete.
+ */
+ if (pd->send.pending)
+ {
+ efl_io_writer_can_write_set(o, EINA_FALSE);
+ err = EAGAIN;
+ goto error;
+ }
+ err = EINVAL;
+ EINA_SAFETY_ON_TRUE_GOTO(pd->send.used != 0, error);
+
+ available = pd->send.len - pd->send.used;
+ if (slice->len < available)
+ todo = slice->len;
+ else
+ todo = available;
+
+ memcpy(pd->send.bytes + pd->send.used, slice->mem, todo);
+ if (remaining)
+ {
+ remaining->len = slice->len - todo;
+ remaining->bytes = slice->bytes + todo;
+ }
+ slice->len = todo;
+ pd->send.used += todo;
+
+ efl_io_writer_can_write_set(o, EINA_FALSE);
+
+ DBG("send %lu bytes more to socket=%p", pd->send.used, o);
+ return _efl_net_socket_windows_send(o, pd);
+
+ error:
+ if (remaining) *remaining = *slice;
+ slice->len = 0;
+ slice->mem = NULL;
+ return err;
+}
+
+EOLIAN static void
+_efl_net_socket_windows_efl_io_writer_can_write_set(Eo *o, Efl_Net_Socket_Windows_Data *pd, Eina_Bool can_write)
+{
+ EINA_SAFETY_ON_TRUE_RETURN(efl_io_closer_closed_get(o) && can_write);
+ if (pd->can_write == can_write) return;
+ pd->can_write = can_write;
+ efl_event_callback_call(o, EFL_IO_WRITER_EVENT_CAN_WRITE_CHANGED, NULL);
+}
+
+EOLIAN static Eina_Bool
+_efl_net_socket_windows_efl_io_writer_can_write_get(Eo *o EINA_UNUSED, Efl_Net_Socket_Windows_Data *pd)
+{
+ return pd->can_write;
+}
+
+EOLIAN static void
+_efl_net_socket_windows_efl_net_socket_address_local_set(Eo *o EINA_UNUSED, Efl_Net_Socket_Windows_Data *pd, const char *address)
+{
+ eina_stringshare_replace(&pd->address_local, address);
+}
+
+EOLIAN static const char *
+_efl_net_socket_windows_efl_net_socket_address_local_get(Eo *o EINA_UNUSED, Efl_Net_Socket_Windows_Data *pd)
+{
+ return pd->address_local;
+}
+
+EOLIAN static void
+_efl_net_socket_windows_efl_net_socket_address_remote_set(Eo *o EINA_UNUSED, Efl_Net_Socket_Windows_Data *pd, const char *address)
+{
+ eina_stringshare_replace(&pd->address_remote, address);
+}
+
+EOLIAN static const char *
+_efl_net_socket_windows_efl_net_socket_address_remote_get(Eo *o EINA_UNUSED, Efl_Net_Socket_Windows_Data *pd)
+{
+ return pd->address_remote;
+}
+
+#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 @@
+class Efl.Net.Socket.Windows (Efl.Loop_User, Efl.Net.Socket) {
+ [[A base Windows NamedPipe socket.
+
+ This is the common class and takes an existing file HANDLE,
+ usually created by an dialer (CreatFile()) or server
+ (CreateNamedPipe()).
+
+ @since 1.19
+ ]]
+
+ implements {
+ Efl.Object.constructor;
+ Efl.Object.destructor;
+ Efl.Io.Closer.close;
+ Efl.Io.Closer.closed { get; }
+ Efl.Io.Closer.close_on_exec { get; set; }
+ Efl.Io.Closer.close_on_destructor { get; set; }
+ Efl.Io.Reader.read;
+ Efl.Io.Reader.can_read { get; set; }
+ Efl.Io.Reader.eos { get; set; }
+ Efl.Io.Writer.write;
+ Efl.Io.Writer.can_write { get; set; }
+ Efl.Net.Socket.address_local { get; set; }
+ Efl.Net.Socket.address_remote { get; set; }
+ }
+}