WIP: efl_net_{socket,dialer,server}_windows

This is the local socket for windows, analogous to AF_UNIX.

WIP: This is being worked out by vtorri and myself.
WIP: still untested
This commit is contained in:
Gustavo Sverzut Barbieri 2017-03-22 04:29:16 -03:00
parent 76b5749ea0
commit e552e7ea14
14 changed files with 1598 additions and 13 deletions

View File

@ -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 \

View File

@ -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)

View File

@ -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)
{
/*
@ -570,6 +576,35 @@ main(int argc, char **argv)
goto end_input;
}
}
#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)
{
/*
@ -815,6 +850,35 @@ main(int argc, char **argv)
goto end_output;
}
}
#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)
{

View File

@ -0,0 +1,212 @@
#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_Rw_Slice rw_slice = EINA_SLICE_ARRAY(buf);
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;
err = efl_io_reader_read(event->object, &rw_slice);
if (err)
{
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));
}
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",
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;
}

View File

@ -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 */

View File

@ -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 */

View File

@ -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"

View File

@ -170,7 +170,27 @@ 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
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 void (*Efl_Net_Socket_Windows_Operation_Success_Cb)(void *data, Eo *sock, Eina_Rw_Slice slice);
typedef void (*Efl_Net_Socket_Windows_Operation_Failure_Cb)(void *data, Eo *sock, Eina_Error err);
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);
void _efl_net_socket_windows_operation_failed(Efl_Net_Socket_Windows_Operation *op, Eina_Error err);
void _efl_net_socket_windows_operation_succeeded(Efl_Net_Socket_Windows_Operation *op, Eina_Rw_Slice slice);
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);

View File

@ -0,0 +1,130 @@
#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
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0600
#elif _WIN32_WINNT < 0x0600
#error "This version of Windows is too old"
#endif
#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;
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\\") + strlen(address) >= 256, EINVAL);
EINA_SAFETY_ON_TRUE_RETURN_VAL(efl_net_dialer_connected_get(o), EISCONN);
EINA_SAFETY_ON_TRUE_RETURN_VAL(efl_io_closer_closed_get(o), EBADF);
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)
return GetLastError();
// TODO vtorri: will this CreateFile() take a while if the server
// waits to accept? If so, we may need to move this to an
// Ecore_Thread and call _efl_net_socket_windows_init() and the
// rest of this function from "end_cb"
err = _efl_net_socket_windows_init(o, h);
if (err)
{
CloseHandle(h);
return err;
}
efl_net_socket_address_remote_set(o, efl_net_dialer_address_dial_get(o));
efl_net_socket_address_local_set(o, "TODO"); // TODO vtorri: can we get the local peer address, like getsockname()?
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\\%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\\");;
}
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"

View File

@ -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;
}
}

View File

@ -0,0 +1,454 @@
#define EFL_NET_SERVER_WINDOWS_PROTECTED 1
#define EFL_NET_SERVER_PROTECTED 1
#define EFL_NET_SOCKET_PROTECTED 1
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0600
#elif _WIN32_WINNT < 0x0600
#error "This version of Windows is too old"
#endif
#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 void
_efl_net_server_windows_client_listen_success(void *data, Eo *client, Eina_Rw_Slice slice EINA_UNUSED)
{
Eo *o = data;
Efl_Net_Server_Windows_Data *pd = efl_data_scope_get(o, MY_CLASS);
char str[256];
EINA_SAFETY_ON_NULL_RETURN(pd);
snprintf(str, sizeof(str), "XXXTODO"); // TODO vtorri: do we have a way to identify the client?
DBG("server=%p received incoming connection at %s (%s)", o, efl_net_server_address_get(o), str);
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, efl_net_server_address_get(o));
efl_net_socket_address_remote_set(client, str);
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, str);
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
{
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);
}
static void
_efl_net_server_windows_client_listen_failure(void *data, Eo *client EINA_UNUSED, Eina_Error err)
{
Eo *o = data;
WRN("server=%p failed to accept connection at %s: #%d %s",
o, efl_net_server_address_get(o), err, eina_error_msg_get(err));
efl_event_callback_call(o, EFL_NET_SERVER_EVENT_ERROR, &err);
// TODO: create a new one on failure?
}
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;
Eina_Error err = 0;
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);
DBG("server=%p connecting to %s...", o, efl_net_server_address_get(o));
if (!ConnectNamedPipe(h, ovl))
{
err = GetLastError();
if (err == ERROR_IO_PENDING)
return 0;
else if (err == ERROR_PIPE_CONNECTED)
{
_efl_net_socket_windows_operation_succeeded(op, (Eina_Rw_Slice){});
return 0;
}
else
{
_efl_net_socket_windows_operation_failed(op, err);
return err;
}
}
_efl_net_socket_windows_operation_failed(op, EINVAL);
return EINVAL;
}
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)
return GetLastError();
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\\%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\\");
}
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\\") + 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"

View File

@ -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;
}
}

View File

@ -0,0 +1,599 @@
#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
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0600
#elif _WIN32_WINNT < 0x0600
#error "This version of Windows is too old"
#endif
#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;
Eina_Bool pending;
} recv;
struct {
union {
uint8_t *bytes;
void *mem;
};
DWORD len;
DWORD used;
Eina_Bool pending;
} send;
HANDLE handle;
PTP_IO io;
Eina_Bool can_read;
Eina_Bool eos;
Eina_Bool pending_eos;
Eina_Bool can_write;
Eina_Bool io_started;
} 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;
Eo *o;
Eina_Bool deleting;
};
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->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);
StartThreadpoolIo(pd->io);
// TODO vtorri: I'm not sure the same pd->io can be used for concurrent
// operations, like read + write. If it's not, then we'll have to
// CreateThreadpoolIo() for each operation (op->io)...
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;
}
static void
_efl_net_socket_windows_operation_done(Efl_Net_Socket_Windows_Operation *op, Eina_Error err, Eina_Rw_Slice slice)
{
Efl_Net_Socket_Windows_Data *pd;
DBG("op=%p (socket=%p), success_cb=%p, failure_cb=%p, data=%p, err=%d (%s), slice=" EINA_SLICE_FMT,
op, op->o, op->success_cb, op->failure_cb, op->data,
err, eina_error_msg_get(err), EINA_SLICE_PRINT(slice));
op->deleting = EINA_TRUE;
pd = efl_data_scope_get(op->o, EFL_NET_SOCKET_WINDOWS_CLASS);
if (pd)
{
pd->pending_ops = eina_list_remove(pd->pending_ops, op);
CancelThreadpoolIo(pd->io);
}
if (err)
op->failure_cb((void *)op->data, op->o, err);
else
op->success_cb((void *)op->data, op->o, slice);
free(op);
}
void
_efl_net_socket_windows_operation_failed(Efl_Net_Socket_Windows_Operation *op, Eina_Error err)
{
EINA_SAFETY_ON_NULL_RETURN(op);
EINA_SAFETY_ON_TRUE_RETURN(op->deleting);
_efl_net_socket_windows_operation_done(op, err, (Eina_Rw_Slice){});
}
void
_efl_net_socket_windows_operation_succeeded(Efl_Net_Socket_Windows_Operation *op, Eina_Rw_Slice slice)
{
EINA_SAFETY_ON_NULL_RETURN(op);
EINA_SAFETY_ON_TRUE_RETURN(op->deleting);
_efl_net_socket_windows_operation_done(op, 0, slice);
}
static void CALLBACK
_efl_net_socket_windows_io_completed(PTP_CALLBACK_INSTANCE inst EINA_UNUSED,
PVOID data,
PVOID overlapped,
ULONG result,
ULONG_PTR bytes_nbr,
PTP_IO io EINA_UNUSED)
{
Eo *o = data;
Efl_Net_Socket_Windows_Data *pd = efl_data_scope_get(o, MY_CLASS);
Efl_Net_Socket_Windows_Operation *op = overlapped;
EINA_SAFETY_ON_NULL_RETURN(pd);
// TODO: check if this happens on the main thread, otherwise migrate to main!
if (result == NO_ERROR)
_efl_net_socket_windows_operation_succeeded(op, (Eina_Rw_Slice){.len = bytes_nbr}); // TODO k-s: slice with actual memory
else
_efl_net_socket_windows_operation_failed(op, result);
}
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->io = CreateThreadpoolIo(h, _efl_net_socket_windows_io_completed, o, 0);
if (!pd->io)
return GetLastError(); // TODO vtorri: is this compatible with errno/strerror()?
pd->handle = h;
DBG("socket=%p adopted handle=%p, ThreadpoolIo=%p", o, h, pd->io);
return 0;
}
static Eina_Error _efl_net_socket_windows_recv(Eo *o, Efl_Net_Socket_Windows_Data *pd);
static void
_efl_net_socket_windows_recv_success(void *data EINA_UNUSED, Eo *o, Eina_Rw_Slice slice)
{
Efl_Net_Socket_Windows_Data *pd = efl_data_scope_get(o, MY_CLASS);
pd->recv.used += slice.len;
pd->recv.pending = EINA_FALSE;
efl_io_reader_can_read_set(o, pd->recv.used > 0);
if (pd->handle == INVALID_HANDLE_VALUE) return;
if (pd->recv.used == pd->recv.len) return;
_efl_net_socket_windows_recv(o, pd);
}
static void
_efl_net_socket_windows_recv_failure(void *data EINA_UNUSED, Eo *o, Eina_Error err)
{
Efl_Net_Socket_Windows_Data *pd = efl_data_scope_get(o, MY_CLASS);
// TODO k-s
ERR("TODO: handle err=%d (%s)", err, eina_error_msg_get(err));
pd->recv.pending = EINA_FALSE;
pd->pending_eos = EINA_TRUE; /* eos when buffer drains */
}
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;
if (pd->handle == INVALID_HANDLE_VALUE) return EBADF;
if (pd->recv.len == 0) return ENOMEM;
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);
if (!ReadFile(pd->handle,
pd->recv.bytes + pd->recv.used,
pd->recv.len - pd->recv.used,
&used_size, ovl))
{
Eina_Error err = GetLastError();
if (err == ERROR_IO_PENDING)
{
pd->recv.pending = EINA_TRUE;
return 0;
}
else
{
_efl_net_socket_windows_operation_failed(op, err);
return err;
}
}
_efl_net_socket_windows_operation_succeeded(op, (Eina_Rw_Slice){
.mem = pd->recv.mem, .len = pd->recv.used + used_size});
return 0;
}
static Eina_Error _efl_net_socket_windows_send(Eo *o, Efl_Net_Socket_Windows_Data *pd);
static void
_efl_net_socket_windows_send_success(void *data EINA_UNUSED, Eo *o, Eina_Rw_Slice slice)
{
Efl_Net_Socket_Windows_Data *pd = efl_data_scope_get(o, MY_CLASS);
if (slice.len)
memmove(pd->send.bytes, pd->send.bytes + slice.len, pd->send.used - slice.len);
pd->send.used -= slice.len;
pd->send.pending = EINA_FALSE;
efl_io_writer_can_write_set(o, pd->send.used < pd->send.len);
if (pd->handle == INVALID_HANDLE_VALUE) return;
if (pd->send.used == 0) return;
_efl_net_socket_windows_send(o, pd);
}
static void
_efl_net_socket_windows_send_failure(void *data EINA_UNUSED, Eo *o, Eina_Error err)
{
Efl_Net_Socket_Windows_Data *pd = efl_data_scope_get(o, MY_CLASS);
// TODO k-s
ERR("TODO: handle err=%d (%s)", err, eina_error_msg_get(err));
pd->send.pending = EINA_FALSE;
}
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;
if (pd->handle == INVALID_HANDLE_VALUE) return EBADF;
if (pd->send.used == 0) return EINVAL;
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);
if (!WriteFile(pd->handle,
pd->send.bytes,
pd->send.used,
&used_size, ovl))
{
Eina_Error err = GetLastError();
if (err == ERROR_IO_PENDING)
{
pd->send.pending = EINA_TRUE;
return 0;
}
else
{
_efl_net_socket_windows_operation_failed(op, err);
return err;
}
}
_efl_net_socket_windows_operation_succeeded(op, (Eina_Rw_Slice){
.mem = pd->send.mem, .len = used_size});
return 0;
}
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;
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)
{
while (pd->pending_ops)
_efl_net_socket_windows_operation_failed(pd->pending_ops->data, ECANCELED);
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;
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);
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);
if (pd->io)
{
CloseThreadpoolIo(pd->io);
pd->io = NULL;
}
h = InterlockedExchangePointer(&pd->handle, INVALID_HANDLE_VALUE);
if (h != INVALID_HANDLE_VALUE)
CloseHandle(h);
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_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);
ro_slice.len = pd->recv.used;
if (ro_slice.len == 0)
{
rw_slice->len = 0;
if (pd->pending_eos)
{
efl_io_reader_eos_set(o, EINA_TRUE);
efl_io_closer_close(o);
}
return EAGAIN;
}
ro_slice.bytes = pd->recv.bytes;
*rw_slice = eina_rw_slice_copy(*rw_slice, ro_slice);
remaining = pd->recv.used - rw_slice->len;
if (remaining)
memmove(pd->recv.bytes, pd->recv.bytes + rw_slice->len, remaining);
pd->recv.used = remaining;
efl_io_reader_can_read_set(o, remaining > 0);
if ((pd->pending_eos) && (remaining == 0))
{
efl_io_reader_eos_set(o, EINA_TRUE);
efl_io_closer_close(o);
return 0;
}
if ((!pd->recv.pending) && (pd->recv.used < pd->recv.len))
{
DBG("recv %lu bytes more from socket=%p", pd->recv.len - pd->recv.used, o);
return _efl_net_socket_windows_recv(o, pd);
}
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_TRUE_GOTO(pd->send.mem != NULL, error);
if (pd->send.len <= pd->send.used)
{
efl_io_writer_can_write_set(o, EINA_FALSE);
return EAGAIN;
}
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;
efl_io_writer_can_write_set(o, pd->send.used < pd->send.len);
if ((!pd->send.pending) && (pd->send.used > 0))
{
DBG("send %lu bytes more to socket=%p", pd->send.used, o);
return _efl_net_socket_windows_send(o, pd);
}
return 0;
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"

View File

@ -0,0 +1,24 @@
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.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; }
}
}