forked from enlightenment/efl
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:
parent
76b5749ea0
commit
e552e7ea14
|
@ -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 \
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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 */
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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"
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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"
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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"
|
|
@ -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; }
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue