efl.net: socket, server and dialer for TCP.

Efl.Net.Server defines how to accept new connections, doing the
bind(), listen() and accept() for protocols such as TCP.

Efl.Net.Dialer defines to to reach a server.

Both are based on Efl.Net.Socket as communication interface that is
based on Efl.Io.Reader, Efl.Io.Writer and Efl.Io.Closer, thus being
usable with code such as Efl.Io.Copier.

The Server will emit an event "client,add" with the established
Socket, which is a child and can be closed by both the server or the
user.

The Dialer extends the Socket and allows for creating one given an
address, that will be resolved and connected.

TCP is the initial implementation so we an validate the
interfaces. UDP, Unix-Local and SSL will come later as derivate
classes.

The examples are documented and should cover the basic principles:

 - efl_io_copier_example can accept "tcp://IP:PORT" and will work as a
   "netcat", can send data from socket, file or stdin to a socket,
   file, stdout or stderr.

 - efl_net_server_example listens for connections and can either reply
   "Hello World!" and take some data or work as an echo-server,
   looping back all received data to the user.

More complex interactions that require a "chat" between client and
server will be covered with new classes later, such as a queue that
empties itself once data is read.
This commit is contained in:
Gustavo Sverzut Barbieri 2016-08-18 01:53:16 -03:00
parent 5d4688679e
commit e7df1a7483
25 changed files with 2508 additions and 3 deletions

View File

@ -3098,6 +3098,7 @@ AM_CONDITIONAL([HAVE_IPV6], [test "x${have_ipv6}" = "xyes"])
### Checks for linker characteristics
### Checks for library functions
AC_CHECK_FUNCS([accept4])
EFL_LIB_END([Ecore_Con])
#### End of Ecore_Con

View File

@ -6,6 +6,14 @@ ecore_con_eolian_files = \
lib/ecore_con/efl_network_client.eo \
lib/ecore_con/efl_network_server.eo \
lib/ecore_con/efl_network_connector.eo \
lib/ecore_con/efl_net_socket.eo \
lib/ecore_con/efl_net_socket_fd.eo \
lib/ecore_con/efl_net_socket_tcp.eo \
lib/ecore_con/efl_net_dialer.eo \
lib/ecore_con/efl_net_dialer_tcp.eo \
lib/ecore_con/efl_net_server.eo \
lib/ecore_con/efl_net_server_fd.eo \
lib/ecore_con/efl_net_server_tcp.eo \
lib/ecore_con/ecore_con_eet_base.eo \
lib/ecore_con/ecore_con_eet_server_obj.eo \
lib/ecore_con/ecore_con_eet_client_obj.eo \
@ -52,7 +60,15 @@ lib/ecore_con/ecore_con_url_curl.h \
static_libs/http-parser/http_parser.c \
static_libs/http-parser/http_parser.h \
lib/ecore_con/ecore_con_private.h \
lib/ecore_con/ecore_con_info.c
lib/ecore_con/ecore_con_info.c \
lib/ecore_con/efl_net_socket.c \
lib/ecore_con/efl_net_socket_fd.c \
lib/ecore_con/efl_net_socket_tcp.c \
lib/ecore_con/efl_net_dialer.c \
lib/ecore_con/efl_net_dialer_tcp.c \
lib/ecore_con/efl_net_server.c \
lib/ecore_con/efl_net_server_fd.c \
lib/ecore_con/efl_net_server_tcp.c
EXTRA_DIST2 += lib/ecore_con/ecore_con_legacy.c

View File

@ -47,3 +47,4 @@
/ecore_buffer_consumer_example
/ecore_buffer_provider_example
/efl_io_copier_example
/efl_net_server_example

View File

@ -77,7 +77,8 @@ ecore_timer_example \
ecore_getopt_example \
ecore_con_eet_client_example \
ecore_con_eet_server_example \
efl_io_copier_example
efl_io_copier_example \
efl_net_server_example
ECORE_COMMON_LDADD = \
$(top_builddir)/src/lib/ecore/libecore.la \
@ -281,6 +282,9 @@ $(top_builddir)/src/lib/eet/libeet.la
efl_io_copier_example_SOURCES = efl_io_copier_example.c
efl_io_copier_example_LDADD = $(ECORE_CON_COMMON_LDADD)
efl_net_server_example_SOURCES = efl_net_server_example.c
efl_net_server_example_LDADD = $(ECORE_CON_COMMON_LDADD)
SRCS = \
ecore_animator_example.c \
ecore_buffer_example.c \

View File

@ -1,6 +1,7 @@
#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>
@ -58,6 +59,36 @@ EFL_CALLBACKS_ARRAY_DEFINE(output_buffer_cbs,
{ EFL_IO_BUFFER_EVENT_REALLOCATED, _output_buffer_reallocated });
static void
_dialer_resolved(void *data EINA_UNUSED, const Eo_Event *event)
{
fprintf(stderr, "INFO: dialer resolved '%s' to '%s'\n",
efl_net_dialer_address_dial_get(event->object),
efl_net_socket_address_remote_get(event->object));
}
static void
_dialer_error(void *data EINA_UNUSED, const Eo_Event *event)
{
const Eina_Error *perr = event->info;
fprintf(stderr, "INFO: error: %d\n", *perr);
retval = EXIT_FAILURE;
/* no need to quit as copier will get a "eos" event and emit "done" */
}
static void
_dialer_connected(void *data EINA_UNUSED, const Eo_Event *event)
{
fprintf(stderr, "INFO: dialer connected to '%s' (%s)\n",
efl_net_dialer_address_dial_get(event->object),
efl_net_socket_address_remote_get(event->object));
}
EFL_CALLBACKS_ARRAY_DEFINE(dialer_cbs,
{ EFL_NET_DIALER_EVENT_RESOLVED, _dialer_resolved },
{ EFL_NET_DIALER_EVENT_ERROR, _dialer_error },
{ EFL_NET_DIALER_EVENT_CONNECTED, _dialer_connected });
/* copier events are of interest, you should hook to at least "done"
* and "error"
*/
@ -240,7 +271,10 @@ static const Ecore_Getopt options = {
ECORE_GETOPT_HELP('h', "help"),
ECORE_GETOPT_STORE_METAVAR_STR(0, NULL,
"The input file name or ':stdin:' to read from stdin.",
"The input file name or:\n"
":stdin: to read from stdin.\n"
"tcp://IP:PORT to connect using TCP and an IPv4 (A.B.C.D:PORT) or IPv6 ([A:B:C:D::E]:PORT).\n"
"",
"input-file"),
ECORE_GETOPT_STORE_METAVAR_STR(0, NULL,
"The output file name or:\n"
@ -248,6 +282,7 @@ static const Ecore_Getopt options = {
":stderr: to write to stderr.\n"
":memory: to write to a memory buffer.\n"
":none: to not use a destination object.\n"
"tcp://IP:PORT to connect using TCP and an IPv4 (A.B.C.D:PORT) or IPv6 ([A:B:C:D::E]:PORT).\n"
"",
"output-file"),
ECORE_GETOPT_SENTINEL
@ -285,6 +320,7 @@ main(int argc, char **argv)
Eina_Slice line_delm_slice = EINA_SLICE_STR_LITERAL("");
ecore_init();
ecore_con_init();
args = ecore_getopt_parse(&options, values, argc, argv);
if (args < 0)
@ -323,6 +359,33 @@ main(int argc, char **argv)
goto end;
}
}
else if (strncmp(input_fname, "tcp://", strlen("tcp://")) == 0)
{
/*
* Since Efl.Net.Socket implements the required interfaces,
* they can be used here as well.
*/
const char *address = input_fname + strlen("tcp://");
Eina_Error err;
input = efl_add(EFL_NET_DIALER_TCP_CLASS, NULL,
efl_event_callback_array_add(efl_self, input_cbs(), NULL), /* optional */
efl_event_callback_array_add(efl_self, dialer_cbs(), NULL) /* optional */
);
if (!input)
{
fprintf(stderr, "ERROR: could not create TCP Dialer.\n");
retval = EXIT_FAILURE;
goto end;
}
err = efl_net_dialer_dial(input, address);
if (err)
{
fprintf(stderr, "ERROR: could not TCP dial %s: %s\n",
address, eina_error_msg_get(err));
goto end_input;
}
}
else
{
/* regular file, open with flags: read-only and close-on-exec */
@ -403,6 +466,33 @@ main(int argc, char **argv)
*/
output = NULL;
}
else if (strncmp(output_fname, "tcp://", strlen("tcp://")) == 0)
{
/*
* Since Efl.Net.Socket implements the required interfaces,
* they can be used here as well.
*/
const char *address = output_fname + strlen("tcp://");
Eina_Error err;
output = efl_add(EFL_NET_DIALER_TCP_CLASS, NULL,
efl_event_callback_array_add(efl_self, output_cbs(), NULL), /* optional */
efl_event_callback_array_add(efl_self, dialer_cbs(), NULL) /* optional */
);
if (!output)
{
fprintf(stderr, "ERROR: could not create TCP Dialer.\n");
retval = EXIT_FAILURE;
goto end_input;
}
err = efl_net_dialer_dial(output, address);
if (err)
{
fprintf(stderr, "ERROR: could not TCP dial %s: %s\n",
address, eina_error_msg_get(err));
goto end_output;
}
}
else
{
/* regular file, open with flags: write-only, close-on-exec,
@ -470,6 +560,7 @@ main(int argc, char **argv)
input = NULL;
end:
ecore_con_shutdown();
ecore_shutdown();
return retval;

View File

@ -0,0 +1,544 @@
#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>
static int retval = EXIT_SUCCESS;
static Eina_Bool echo = EINA_FALSE;
/* NOTE: client i/o events are only used as debug, you can omit these */
static void
_client_can_read_changed(void *data EINA_UNUSED, const Eo_Event *event)
{
fprintf(stderr, "INFO: client %s can_read=%d\n",
efl_net_socket_address_remote_get(event->object),
efl_io_reader_can_read_get(event->object));
}
static void
_client_can_write_changed(void *data EINA_UNUSED, const Eo_Event *event)
{
fprintf(stderr, "INFO: client %s can_write=%d\n",
efl_net_socket_address_remote_get(event->object),
efl_io_writer_can_write_get(event->object));
}
static void
_client_eos(void *data EINA_UNUSED, const Eo_Event *event)
{
fprintf(stderr, "INFO: client %s eos.\n",
efl_net_socket_address_remote_get(event->object));
}
static void
_client_closed(void *data EINA_UNUSED, const Eo_Event *event)
{
fprintf(stderr, "INFO: client %s closed.\n",
efl_net_socket_address_remote_get(event->object));
}
EFL_CALLBACKS_ARRAY_DEFINE(client_cbs,
{ EFL_IO_READER_EVENT_CAN_READ_CHANGED, _client_can_read_changed },
{ EFL_IO_READER_EVENT_EOS, _client_eos },
{ EFL_IO_WRITER_EVENT_CAN_WRITE_CHANGED, _client_can_write_changed },
{ EFL_IO_CLOSER_EVENT_CLOSED, _client_closed });
/* copier events are of interest, you should hook to at least "done"
* and "error"
*/
/* echo copier is about the same socket, you can close it right away */
static void
_echo_copier_done(void *data EINA_UNUSED, const Eo_Event *event)
{
Eo *copier = event->object;
fprintf(stderr, "INFO: echo copier done, close and del %p\n", copier);
efl_io_closer_close(copier);
efl_del(copier);
}
static void
_echo_copier_error(void *data EINA_UNUSED, const Eo_Event *event)
{
Eo *copier = event->object;
const Eina_Error *perr = event->info;
retval = EXIT_FAILURE;
fprintf(stderr, "ERROR: echo copier %p failed %d, close and del.\n",
copier, *perr);
efl_io_closer_close(copier);
efl_del(copier);
}
EFL_CALLBACKS_ARRAY_DEFINE(echo_copier_cbs,
{ EFL_IO_COPIER_EVENT_DONE, _echo_copier_done },
{ EFL_IO_COPIER_EVENT_ERROR, _echo_copier_error});
/* When sender and receiver peers are about different entities, you
* can only close when both are done, otherwise the socket will be
* prematurely closed.
*
* Here we use a struct with both copiers and NULL them once they are
* done, when both are done we close the socket and free the struct.
*/
typedef struct {
Eo *send_copier;
Eo *recv_copier;
Eo *client;
} Send_Recv_Data;
static Send_Recv_Data *
_send_recv_new(Eo *client)
{
Send_Recv_Data *d = calloc(1, sizeof(Send_Recv_Data));
if (!d) return NULL;
/* take a reference since copiers will only hold their reference
* while they are alive. As we're deleting them before calling
* efl_io_closer_close(), then we need it for bit longer.
*/
d->client = efl_ref(client);
return d;
}
static void
_send_recv_free(Send_Recv_Data *d)
{
efl_unref(d->client);
free(d);
}
static void
_send_recv_done(Send_Recv_Data *d, Eo *copier)
{
if (d->send_copier == copier) d->send_copier = NULL;
else d->recv_copier = NULL;
efl_del(copier);
if (d->send_copier || d->recv_copier) return;
efl_io_closer_close(d->client);
_send_recv_free(d);
}
static void
_send_copier_done(void *data, const Eo_Event *event)
{
Eo *copier = event->object;
Eo *buffer = efl_io_copier_source_get(copier);
Eo *client = efl_io_copier_destination_get(copier);
Send_Recv_Data *d = data;
Eina_Slice slice;
/* show what we sent, just for debug */
if (!efl_io_buffer_slice_get(buffer, &slice))
fprintf(stderr, "ERROR: could not get buffer slice\n");
else
fprintf(stderr,
"INFO: sent to %s %zd bytes:"
"\n--BEGIN SENT DATA--\n"
EINA_SLICE_STR_FMT
"\n--END SENT DATA--\n",
efl_net_socket_address_remote_get(client),
slice.len, EINA_SLICE_STR_PRINT(slice));
fprintf(stderr, "INFO: send copier done, check if should close %p\n", copier);
_send_recv_done(d, copier);
}
static void
_send_copier_error(void *data, const Eo_Event *event)
{
Eo *copier = event->object;
Eo *buffer = efl_io_copier_source_get(copier);
Eo *client = efl_io_copier_destination_get(copier);
const Eina_Error *perr = event->info;
Send_Recv_Data *d = data;
uint64_t offset;
Eina_Slice slice;
retval = EXIT_FAILURE;
offset = efl_io_buffer_position_read_get(buffer);
if (!efl_io_buffer_slice_get(buffer, &slice))
fprintf(stderr, "ERROR: could not get buffer slice\n");
else
{
Eina_Slice remaining = slice;
remaining.bytes += offset;
remaining.len -= offset;
slice.len = offset;
fprintf(stderr,
"ERROR: sent to %s only %zd bytes:"
"\n--BEGIN SENT DATA--\n"
EINA_SLICE_STR_FMT
"\n--END SENT DATA--\n"
"Remaining %zd bytes:"
"\n--BEGIN REMAINING DATA--\n"
EINA_SLICE_STR_FMT
"\n--END REMAINING DATA--\n",
efl_net_socket_address_remote_get(client),
slice.len, EINA_SLICE_STR_PRINT(slice),
remaining.len, EINA_SLICE_STR_PRINT(remaining));
}
fprintf(stderr, "ERROR: send copier %p failed %d, check if should close..\n",
copier, *perr);
_send_recv_done(d, copier);
}
EFL_CALLBACKS_ARRAY_DEFINE(send_copier_cbs,
{ EFL_IO_COPIER_EVENT_DONE, _send_copier_done },
{ EFL_IO_COPIER_EVENT_ERROR, _send_copier_error});
static void
_recv_copier_done(void *data, const Eo_Event *event)
{
Eo *copier = event->object;
Eo *client = efl_io_copier_source_get(copier);
Eo *buffer = efl_io_copier_destination_get(copier);
Send_Recv_Data *d = data;
Eina_Slice slice;
/* show case, you could use a copier to Efl_Io_Stdout, a
* file... and get progressive processing.
*
* Here we're using a memory buffer and printing everything at
* once.
*
* You could also steal the binbuf with
* efl_io_buffer_binbuf_steal()
*/
if (!efl_io_buffer_slice_get(buffer, &slice))
fprintf(stderr, "ERROR: could not get buffer slice\n");
else
fprintf(stderr,
"INFO: recv from %s %zd bytes:"
"\n--BEGIN RECV DATA--\n"
EINA_SLICE_STR_FMT "\n"
"\n--END RECV DATA--\n",
efl_net_socket_address_remote_get(client),
slice.len, EINA_SLICE_STR_PRINT(slice));
fprintf(stderr, "INFO: receive copier done, check if should close %p\n", copier);
_send_recv_done(d, copier);
}
static void
_recv_copier_error(void *data, const Eo_Event *event)
{
Eo *copier = event->object;
Eo *buffer = efl_io_copier_source_get(copier);
Eo *client = efl_io_copier_destination_get(copier);
const Eina_Error *perr = event->info;
Send_Recv_Data *d = data;
Eina_Slice slice;
retval = EXIT_FAILURE;
if (!efl_io_buffer_slice_get(buffer, &slice))
fprintf(stderr, "ERROR: could not get buffer slice\n");
else
fprintf(stderr,
"ERROR: recv to %s only %zd bytes:"
"\n--BEGIN RECV DATA--\n"
EINA_SLICE_STR_FMT "\n"
"\n--END RECV DATA--\n",
efl_net_socket_address_remote_get(client),
slice.len, EINA_SLICE_STR_PRINT(slice));
fprintf(stderr, "ERROR: receive copier %p failed %d, check if should close..\n",
copier, *perr);
_send_recv_done(d, copier);
}
EFL_CALLBACKS_ARRAY_DEFINE(recv_copier_cbs,
{ EFL_IO_COPIER_EVENT_DONE, _recv_copier_done },
{ EFL_IO_COPIER_EVENT_ERROR, _recv_copier_error});
/* server events are mandatory, afterall you need to define what's
* going to happen after a client socket is connected. This is the
* "client,add" event.
*
* if clients_limit and clients_reject_excess are set, then
* "client,rejected" is dispatched for rejected sockets, they contain
* the string with socket identification.
*/
static void
_server_client_add(void *data EINA_UNUSED, const Eo_Event *event)
{
Efl_Net_Socket *client = event->info;
fprintf(stderr, "INFO: accepted client %s\n",
efl_net_socket_address_remote_get(client));
/* to use a client, you must efl_ref() it. Here we're not doing it
* explicitly because copiers do take a reference.
*/
/*
* monitor the client socket for debug purposes (optional)
*/
efl_event_callback_array_add(client, client_cbs(), NULL);
/*
* Since sockets are reader/writer/closer objects, we can use the
* Efl_Io_Copier utility.
*/
if (echo)
{
/*
* An echo copier is pretty simple, use the socket as both
* source and destination.
*/
Eo *echo_copier = efl_add(EFL_IO_COPIER_CLASS, efl_parent_get(client),
efl_io_copier_source_set(efl_self, client),
efl_io_copier_destination_set(efl_self, client),
efl_event_callback_array_add(efl_self, echo_copier_cbs(), client)
);
fprintf(stderr, "INFO: using an echo copier=%p for client %s\n",
echo_copier, efl_net_socket_address_remote_get(client));
return;
}
else
{
/*
* Here we create a fixed buffer with a string to send:
* - "Hello World!"
* and another one to store the received buffer so we can print as
* a single blob at the end.
*
* One can change these to Efl_Io_File or event pipe to something
* else like Efl_Io_Stdin, Efl_Io_Stdout and it would just work.
*/
Eina_Slice slice;
Send_Recv_Data *d;
Eo *send_buffer, *recv_buffer;
d = _send_recv_new(client);
if (!d)
{
fprintf(stderr, "ERROR: could not allocate memory\n");
return;
}
// TODO buffer constructor taking RO string
send_buffer = efl_add(EFL_IO_BUFFER_CLASS, NULL);
slice = (Eina_Slice)EINA_SLICE_STR("Hello World!");
efl_io_writer_write(send_buffer, &slice, NULL);
/* Unlimited buffer to store the received data. */
recv_buffer = efl_add(EFL_IO_BUFFER_CLASS, NULL);
/* an input copier that takes data from send_buffer and pushes to client */
d->send_copier = efl_add(EFL_IO_COPIER_CLASS, efl_parent_get(client),
efl_io_copier_source_set(efl_self, send_buffer),
efl_io_copier_destination_set(efl_self, client),
efl_event_callback_array_add(efl_self, send_copier_cbs(), d)
);
fprintf(stderr, "INFO: using sender buffer %p with copier %p for client %s\n",
send_buffer, d->send_copier, efl_net_socket_address_remote_get(client));
efl_unref(send_buffer); /* d->send_copier adds a reference */
if (!d->send_copier)
fprintf(stderr, "ERROR: failed to create sender copier\n");
/* an output copier that takes data from socket and pushes to recv_buffer. */
d->recv_copier = efl_add(EFL_IO_COPIER_CLASS, efl_parent_get(client),
efl_io_copier_source_set(efl_self, client),
efl_io_copier_destination_set(efl_self, recv_buffer),
efl_event_callback_array_add(efl_self, recv_copier_cbs(), d)
);
fprintf(stderr, "INFO: using receiver buffer %p with copier %p for client %s\n",
recv_buffer, d->recv_copier, efl_net_socket_address_remote_get(client));
efl_unref(recv_buffer); /* d->recv_copier adds a reference */
if (!d->recv_copier)
fprintf(stderr, "ERROR: failed to create receiver copier\n");
if (!d->recv_copier && !d->send_copier)
_send_recv_free(d);
}
}
static void
_server_client_rejected(void *data EINA_UNUSED, const Eo_Event *event)
{
const char *client_address = event->info;
fprintf(stderr, "INFO: rejected client %s\n", client_address);
}
static void
_server_error(void *data EINA_UNUSED, const Eo_Event *event)
{
const Eina_Error *perr = event->info;
fprintf(stderr, "INFO: error: %d\n", *perr);
retval = EXIT_FAILURE;
ecore_main_loop_quit();
}
static void
_server_serving(void *data EINA_UNUSED, const Eo_Event *event)
{
fprintf(stderr, "INFO: serving at %s\n",
efl_net_server_address_get(event->object));
}
EFL_CALLBACKS_ARRAY_DEFINE(server_cbs,
{ EFL_NET_SERVER_EVENT_CLIENT_ADD, _server_client_add },
{ EFL_NET_SERVER_EVENT_CLIENT_REJECTED, _server_client_rejected },
{ EFL_NET_SERVER_EVENT_ERROR, _server_error },
{ EFL_NET_SERVER_EVENT_SERVING, _server_serving });
static const char * protocols[] = {
"tcp",
NULL
};
static const Ecore_Getopt options = {
"efl_net_server_example", /* program name */
NULL, /* usage line */
"1", /* version */
"(C) 2016 Enlightenment Project", /* copyright */
"BSD 2-Clause", /* license */
/* long description, may be multiline and contain \n */
"Example of Efl_Net_Server objects usage.\n"
"\n"
"This example spawns a server of the given protocol at the given address.",
EINA_FALSE,
{
ECORE_GETOPT_STORE_BOOL('e', "echo",
"If true, will send back to client all the data receive (echo server)"),
ECORE_GETOPT_STORE_UINT('l', "clients-limit",
"If set will limit number of clients to accept"),
ECORE_GETOPT_STORE_BOOL('r', "clients-reject-excess",
"If true, excess clients will be immediately rejected."),
ECORE_GETOPT_VERSION('V', "version"),
ECORE_GETOPT_COPYRIGHT('C', "copyright"),
ECORE_GETOPT_LICENSE('L', "license"),
ECORE_GETOPT_HELP('h', "help"),
ECORE_GETOPT_CHOICE_METAVAR(0, NULL, "The server protocol.", "protocol",
protocols),
ECORE_GETOPT_STORE_METAVAR_STR(0, NULL,
"The server address to listen, such as "
"IPv4:PORT, [IPv6]:PORT, Unix socket path...",
"address"),
ECORE_GETOPT_SENTINEL
}
};
int
main(int argc, char **argv)
{
const Efl_Class *cls;
char *protocol = NULL;
char *address = NULL;
unsigned int clients_limit = 0;
Eina_Bool clients_reject_excess = EINA_FALSE;
Eina_Bool quit_option = EINA_FALSE;
Ecore_Getopt_Value values[] = {
ECORE_GETOPT_VALUE_BOOL(echo),
ECORE_GETOPT_VALUE_UINT(clients_limit),
ECORE_GETOPT_VALUE_BOOL(clients_reject_excess),
/* 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(protocol),
ECORE_GETOPT_VALUE_STR(address),
ECORE_GETOPT_VALUE_NONE /* sentinel */
};
int args;
Eo *server;
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;
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;
}
if (strcmp(protocol, "tcp") == 0) cls = EFL_NET_SERVER_TCP_CLASS;
else
{
fprintf(stderr, "ERROR: unsupported protocol: %s\n", protocol);
goto end;
}
server = efl_add(cls, ecore_main_loop_get(), /* it's mandatory to use a main loop provider as the server parent */
efl_net_server_fd_close_on_exec_set(efl_self, EINA_TRUE), /* recommended */
efl_net_server_fd_reuse_address_set(efl_self, EINA_TRUE), /* optional, but nice for testing */
efl_net_server_fd_reuse_port_set(efl_self, EINA_TRUE), /* optional, but nice for testing... not secure unless you know what you're doing */
efl_net_server_clients_limit_set(efl_self,
clients_limit,
clients_reject_excess), /* optional */
efl_event_callback_array_add(efl_self, server_cbs(), NULL)); /* mandatory to have "client,add" in order to be useful */
if (!server)
{
fprintf(stderr, "ERROR: could not create class %p (%s)\n",
cls, efl_class_name_get(cls));
goto end;
}
/* an explicit call to efl_net_server_serve() after the object is
* constructed allows for more complex setup, such as interacting
* with the object to add more properties that couldn't be done
* during efl_add().
*/
err = efl_net_server_serve(server, address);
if (err)
{
fprintf(stderr, "ERROR: could not serve(%s): %s\n",
address, eina_error_msg_get(err));
goto end_server;
}
ecore_main_loop_begin();
end_server:
efl_del(server);
server = NULL;
end:
ecore_con_shutdown();
ecore_shutdown();
return retval;
}

View File

@ -3,3 +3,14 @@
#include "efl_network_connector.eo.h"
#include "efl_network_client.eo.h"
#include "efl_network_url.eo.h"
#include "efl_net_socket.eo.h"
#include "efl_net_dialer.eo.h"
#include "efl_net_server.eo.h"
#include "efl_net_socket_fd.eo.h"
#include "efl_net_server_fd.eo.h"
#include "efl_net_socket_tcp.eo.h"
#include "efl_net_dialer_tcp.eo.h"
#include "efl_net_server_tcp.eo.h"

View File

@ -2984,3 +2984,55 @@ _ecore_con_lookup_done(void *data,
#include "efl_network_client.eo.c"
#include "efl_network_server.eo.c"
#include "efl_network_connector.eo.c"
Eina_Bool
efl_net_ip_port_fmt(char *buf, int buflen, const struct sockaddr *addr)
{
char p[INET6_ADDRSTRLEN];
const void *mem;
unsigned short port;
int r;
if (addr->sa_family == AF_INET)
{
const struct sockaddr_in *a = (const struct sockaddr_in *)addr;
mem = &a->sin_addr;
port = ntohs(a->sin_port);
}
else if (addr->sa_family == AF_INET6)
{
const struct sockaddr_in6 *a = (const struct sockaddr_in6 *)addr;
mem = &a->sin6_addr;
port = ntohs(a->sin6_port);
}
else
{
ERR("unsupported address family: %d", addr->sa_family);
return EINA_FALSE;
}
if (!inet_ntop(addr->sa_family, mem, p, sizeof(p)))
{
ERR("inet_ntop(%d, %p, %p, %zd): %s",
addr->sa_family, mem, p, sizeof(p), strerror(errno));
return EINA_FALSE;
}
if (addr->sa_family == AF_INET)
r = snprintf(buf, buflen, "%s:%hu", p, port);
else
r = snprintf(buf, buflen, "[%s]:%hu", p, port);
if (r < 0)
{
ERR("could not snprintf(): %s", strerror(errno));
return EINA_FALSE;
}
else if (r > buflen)
{
ERR("buffer is too small: %d, required %d", buflen, r);
return EINA_FALSE;
}
return EINA_TRUE;
}

View File

@ -373,4 +373,6 @@ void ecore_con_mempool_shutdown(void);
#undef GENERIC_ALLOC_FREE_HEADER
Eina_Bool efl_net_ip_port_fmt(char *buf, int buflen, const struct sockaddr *addr);
#endif

View File

@ -0,0 +1,12 @@
#define EFL_NET_DIALER_PROTECTED 1
#define EFL_NET_SOCKET_PROTECTED 1
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "Ecore.h"
#include "Ecore_Con.h"
#include "ecore_con_private.h"
#include "efl_net_dialer.eo.c"

View File

@ -0,0 +1,93 @@
interface Efl.Net.Dialer (Efl.Net.Socket) {
[[Creates a client socket to reach a remote peer.
The connection process starts when @.dial is executed, this
allows implementations to request more complex setup that would
require a live object handle that is not possible during
construction.
The socket should be considered connected and ready to use only
when "connected" event is dispatched, by then
@Efl.Io.Reader.can_read and @Efl.Io.Writer.can_write should
change accordingly.
Once the socket is closed, @Efl.Io.Closer.closed will be called
and the "closed" event is dispatched.
@since 1.19
]]
methods {
dial {
[[Dials to the remote peer.
This method starts the connection process, resolving
address and then proceeding to the actual connection.
Once the connection is fully setup, "connected" event is
dispatched.
]]
params {
address: string @nonull;
}
return: Eina.Error;
}
@property address_dial {
[[Return the address given to @.dial.
If the resolved address is desired, then listen to
"resolved" event and use the
@Efl.Net.Socket.address_remote property.
]]
get { }
set @protected { }
values {
address: string;
}
}
@property connected {
[[Returns whenever the socket is connected or not.
Whenever this property becomes true, "connected" event
should be dispatched.
]]
get { }
set @protected { }
values {
connected: bool;
}
}
@property timeout_dial {
[[The timeout in seconds to use for dialing/connecting.
This should be set before dialing.
]]
get { }
set { }
values {
seconds: double;
}
}
}
events {
resolved: string; [[Notifies @.address_dial was resolved to
@Efl.Net.Socket.address_remote.
This is emitted before "connected" and may
be emitted from @Efl.Object.finalize, thus
be sure to connect the callbacks during
the object construction.
Some protocols allows redirects and this
may be emitted multiple times, such as
HTTP.
]]
error: Eina.Error; [[Some error happened and the socket
stopped working.
]]
connected; [[Notifies the socket is connected to the remote peer.]]
}
}

View File

@ -0,0 +1,194 @@
#define EFL_NET_DIALER_TCP_PROTECTED 1
#define EFL_NET_DIALER_PROTECTED 1
#define EFL_NET_SOCKET_FD_PROTECTED 1
#define EFL_NET_SOCKET_PROTECTED 1
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "Ecore.h"
#include "Ecore_Con.h"
#include "ecore_con_private.h"
#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
#ifdef HAVE_NETINET_TCP_H
# include <netinet/tcp.h>
#endif
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
#ifdef HAVE_ARPA_INET_H
# include <arpa/inet.h>
#endif
#ifdef HAVE_EVIL
# include <Evil.h>
#endif
#define MY_CLASS EFL_NET_DIALER_TCP_CLASS
typedef struct _Efl_Net_Dialer_Tcp_Data
{
Eina_Stringshare *address_dial;
Eina_Bool connected;
double timeout_dial;
} Efl_Net_Dialer_Tcp_Data;
EOLIAN static void
_efl_net_dialer_tcp_efl_object_destructor(Eo *o, Efl_Net_Dialer_Tcp_Data *pd)
{
efl_destructor(efl_super(o, MY_CLASS));
eina_stringshare_replace(&pd->address_dial, NULL);
}
EOLIAN static Eina_Error
_efl_net_dialer_tcp_efl_net_dialer_dial(Eo *o, Efl_Net_Dialer_Tcp_Data *pd EINA_UNUSED, const char *address)
{
struct sockaddr_storage addr = {};
char *str, *host, *port;
int r, fd, extra_flags = 0;
socklen_t addrlen;
char buf[INET6_ADDRSTRLEN + sizeof("[]:65536")];
EINA_SAFETY_ON_NULL_RETURN_VAL(address, 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);
EINA_SAFETY_ON_TRUE_RETURN_VAL(efl_loop_fd_get(o) >= 0, EALREADY);
// TODO: change to getaddrinfo() and move to a thread...
str = host = strdup(address);
EINA_SAFETY_ON_NULL_RETURN_VAL(str, ENOMEM);
if (host[0] == '[')
{
struct sockaddr_in6 *a = (struct sockaddr_in6 *)&addr;
/* IPv6 is: [IP]:port */
host++;
port = strchr(host, ']');
if (!port)
{
ERR("missing ']' in IPv6 address: %s", address);
goto invalid_address;
}
*port = '\0';
port++;
if (port[0] == ':')
port++;
else
port = NULL;
a->sin6_family = AF_INET6;
a->sin6_port = htons(port ? atoi(port) : 0);
r = inet_pton(AF_INET6, host, &(a->sin6_addr));
addrlen = sizeof(*a);
}
else
{
struct sockaddr_in *a = (struct sockaddr_in *)&addr;
port = strchr(host, ':');
if (port)
{
*port = '\0';
port++;
}
a->sin_family = AF_INET;
a->sin_port = htons(port ? atoi(port) : 0);
r = inet_pton(AF_INET, host, &(a->sin_addr));
addrlen = sizeof(*a);
}
if (r != 1)
{
ERR("could not parse IP '%s' (%s)", host, address);
goto invalid_address;
}
free(str);
efl_net_socket_fd_family_set(o, addr.ss_family);
efl_net_dialer_address_dial_set(o, address);
if (efl_net_ip_port_fmt(buf, sizeof(buf), (struct sockaddr *)&addr))
{
efl_net_socket_address_remote_set(o, buf);
efl_event_callback_call(o, EFL_NET_DIALER_EVENT_RESOLVED, NULL);
}
if (efl_net_socket_fd_close_on_exec_get(o))
extra_flags |= SOCK_CLOEXEC;
fd = socket(addr.ss_family, SOCK_STREAM | extra_flags, IPPROTO_TCP);
if (fd < 0)
{
ERR("socket(%d, SOCK_STREAM | %#x, IPPROTO_TCP): %s",
addr.ss_family, extra_flags, strerror(errno));
return errno;
}
r = connect(fd, (struct sockaddr *)&addr, addrlen);
if (r < 0)
{
int errno_bkp = errno;
ERR("connect(%d, %s): %s", fd, address, strerror(errno));
close(fd);
return errno_bkp;
}
efl_loop_fd_set(o, fd);
efl_net_dialer_connected_set(o, EINA_TRUE);
return 0;
invalid_address:
free(str);
return EINVAL;
}
EOLIAN static void
_efl_net_dialer_tcp_efl_net_dialer_address_dial_set(Eo *o EINA_UNUSED, Efl_Net_Dialer_Tcp_Data *pd, const char *address)
{
eina_stringshare_replace(&pd->address_dial, address);
}
EOLIAN static const char *
_efl_net_dialer_tcp_efl_net_dialer_address_dial_get(Eo *o EINA_UNUSED, Efl_Net_Dialer_Tcp_Data *pd)
{
return pd->address_dial;
}
EOLIAN static void
_efl_net_dialer_tcp_efl_net_dialer_timeout_dial_set(Eo *o EINA_UNUSED, Efl_Net_Dialer_Tcp_Data *pd, double seconds)
{
ERR("TODO: when using ecore_con_info/threads, set timeout");
pd->timeout_dial = seconds;
}
EOLIAN static double
_efl_net_dialer_tcp_efl_net_dialer_timeout_dial_get(Eo *o EINA_UNUSED, Efl_Net_Dialer_Tcp_Data *pd)
{
return pd->timeout_dial;
}
EOLIAN static void
_efl_net_dialer_tcp_efl_net_dialer_connected_set(Eo *o, Efl_Net_Dialer_Tcp_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_tcp_efl_net_dialer_connected_get(Eo *o EINA_UNUSED, Efl_Net_Dialer_Tcp_Data *pd)
{
return pd->connected;
}
EOLIAN static Eina_Error
_efl_net_dialer_tcp_efl_io_closer_close(Eo *o, Efl_Net_Dialer_Tcp_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_tcp.eo.c"

View File

@ -0,0 +1,15 @@
class Efl.Net.Dialer.Tcp (Efl.Net.Socket.Tcp, Efl.Net.Dialer) {
[[Connects to a remote TCP server.
@since 1.19
]]
implements {
Efl.Object.destructor;
Efl.Net.Dialer.dial;
Efl.Net.Dialer.address_dial;
Efl.Net.Dialer.connected;
Efl.Net.Dialer.timeout_dial;
Efl.Io.Closer.close;
}
}

View File

@ -0,0 +1,11 @@
#define EFL_NET_SERVER_PROTECTED 1
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "Ecore.h"
#include "Ecore_Con.h"
#include "ecore_con_private.h"
#include "efl_net_server.eo.c"

View File

@ -0,0 +1,113 @@
interface Efl.Net.Server {
[[The basic server interface.
It will start serving and accepting clients once @.serve is
called and the "serving" event is dispatched.
When new clients are accepted, then "client,add" event is
dispatched with a child object implementing @Efl.Net.Socket
interface. These implement the standard @Efl.Io.Reader,
@Efl.Io.Writer and @Efl.Io.Closer interfaces, thus can be used
with utilities such as @Efl.Io.Copier.
@since 1.19
]]
events {
client,add @hot: Efl.Net.Socket; [[A new client socket was created.
The socket will have the
server as parent and can be
closed by both the server
or the user using
@Efl.Io.Closer.
]]
client,rejected: string; [[Notifies a client was rejected due
excess, see @.clients_limit.
]]
error: Eina.Error; [[Some error happened and the server needs
to be stopped.
]]
serving; [[Notifies the server is ready to accept clients.
See property @.serving]]
}
methods {
serve {
[[Starts serving requests.
This method starts the server, resolving address and
then proceeding to the actual listen(2)/bind(2)
equivalent..
Once the connection is fully setup, "serving" event is
dispatched.
]]
params {
address: string @nonull;
}
return: Eina.Error;
}
@property address {
[[The address the server is bound to.
The actual value depends on the type of server, like an
IPv4 (ip:port) or IPv6 ([ip]:port) formatted for a
TCP/UDP server, the path if an Unix Local...
It's always resolved, then if operations are working
with domain names or URL, this is the values the kernel
reports. It's similar to getsockname() in behavior.
]]
get { }
set @protected { }
values {
address: string;
}
}
@property clients_count {
[[Number of concurrent clients accepted by this server.]]
get { }
set @protected { }
values {
count: uint;
}
}
@property clients_limit {
[[Number of maximum concurrent clients allowed by this server.
If reject_excess is set to true, then the connection
will be accepted and immediately closed.
If reject_excess is set to false (default), then
accept(2) won't be called and clients will be queued at
the kernel side, usually up to 4096 pending clients.
Whenever changed, this property will only apply to new
connections, that is, if the current connection count
alredy exceeds the limit, no connections will be closed.
]]
values {
limit: uint;
reject_excess: bool @optional;
}
}
@property serving {
[[Returns whenever the server is ready to accept clients or not.
Whenever this property becomes true, "serving" event
should be dispatched.
]]
get { }
set @protected { }
values {
serving: bool;
}
}
}
}

View File

@ -0,0 +1,368 @@
#define EFL_NET_SERVER_FD_PROTECTED 1
#define EFL_NET_SERVER_PROTECTED 1
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "Ecore.h"
#include "Ecore_Con.h"
#include "ecore_con_private.h"
#include <fcntl.h>
#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
#ifdef HAVE_EVIL
# include <Evil.h>
#endif
#define MY_CLASS EFL_NET_SERVER_FD_CLASS
typedef struct _Efl_Net_Server_Fd_Data
{
Eina_Stringshare *address;
int family;
unsigned int clients_count;
unsigned int clients_limit;
Eina_Bool clients_reject_excess;
Eina_Bool serving;
Eina_Bool close_on_exec;
Eina_Bool reuse_address;
Eina_Bool reuse_port;
} Efl_Net_Server_Fd_Data;
static void
_efl_net_server_fd_event_read(void *data EINA_UNUSED, const Eo_Event *event)
{
Eo *o = event->object;
unsigned int count, limit;
Eina_Bool reject_excess, do_reject = EINA_FALSE;
struct sockaddr_storage addr;
int client, fd, flags = 0;
socklen_t addrlen;
count = efl_net_server_clients_count_get(o);
efl_net_server_clients_limit_get(o, &limit, &reject_excess);
if ((limit > 0) && (count >= limit))
{
if (!reject_excess)
{
// TODO: disconnect 'read' so stops calling?
return;
}
do_reject = EINA_TRUE;
}
fd = efl_loop_fd_get(o);
if (efl_net_server_fd_close_on_exec_get(o))
flags |= SOCK_CLOEXEC;
addrlen = sizeof(addr);
#ifdef HAVE_ACCEPT4
client = accept4(fd, (struct sockaddr *)&addr, &addrlen, flags);
#else
client = accept(fd, (struct sockaddr *)&addr, &addrlen);
#endif
if (client < 0)
{
Eina_Error err = errno;
ERR("accept(%d): %s", fd, strerror(errno));
efl_event_callback_call(o, EFL_NET_SERVER_EVENT_ERROR, &err);
return;
}
#ifndef HAVE_ACCEPT4
if (fcntl(fd, F_SETFD, flags) < 0)
ERR("fcntl(%d, F_SETFD, %#x): %s", fd, flags, strerror(errno));
#endif
if (do_reject)
efl_net_server_fd_client_reject(o, client);
else
efl_net_server_fd_client_add(o, client);
}
static void
_efl_net_server_fd_event_error(void *data EINA_UNUSED, const Eo_Event *event)
{
Eo *o = event->object;
Eina_Error err = EBADF;
efl_net_server_serving_set(o, EINA_FALSE);
efl_event_callback_call(o, EFL_NET_SERVER_EVENT_ERROR, &err);
}
EOLIAN static Efl_Object *
_efl_net_server_fd_efl_object_finalize(Eo *o, Efl_Net_Server_Fd_Data *pd EINA_UNUSED)
{
o = efl_finalize(efl_super(o, MY_CLASS));
if (!o) return NULL;
// TODO: only register "read" if "can_read" is being monitored?
efl_event_callback_add(o, EFL_LOOP_FD_EVENT_READ, _efl_net_server_fd_event_read, NULL);
efl_event_callback_add(o, EFL_LOOP_FD_EVENT_ERROR, _efl_net_server_fd_event_error, NULL);
return o;
}
EOLIAN static Efl_Object *
_efl_net_server_fd_efl_object_constructor(Eo *o, Efl_Net_Server_Fd_Data *pd EINA_UNUSED)
{
pd->family = AF_UNSPEC;
return efl_constructor(efl_super(o, MY_CLASS));
}
EOLIAN static void
_efl_net_server_fd_efl_object_destructor(Eo *o, Efl_Net_Server_Fd_Data *pd)
{
efl_destructor(efl_super(o, MY_CLASS));
eina_stringshare_replace(&pd->address, NULL);
}
EOLIAN static void
_efl_net_server_fd_efl_loop_fd_fd_set(Eo *o, Efl_Net_Server_Fd_Data *pd, int fd)
{
efl_loop_fd_set(efl_super(o, MY_CLASS), fd);
if (fd >= 0)
{
/* apply postponed values */
efl_net_server_fd_close_on_exec_set(o, pd->close_on_exec);
efl_net_server_fd_reuse_address_set(o, pd->reuse_address);
efl_net_server_fd_reuse_port_set(o, pd->reuse_port);
if (pd->family == AF_UNSPEC)
{
ERR("efl_loop_fd_set() must be called after efl_net_server_fd_family_set()");
return;
}
}
else
{
efl_net_server_address_set(o, NULL);
}
}
EOLIAN static void
_efl_net_server_fd_efl_net_server_address_set(Eo *o EINA_UNUSED, Efl_Net_Server_Fd_Data *pd, const char *address)
{
eina_stringshare_replace(&pd->address, address);
}
EOLIAN static const char *
_efl_net_server_fd_efl_net_server_address_get(Eo *o EINA_UNUSED, Efl_Net_Server_Fd_Data *pd)
{
return pd->address;
}
EOLIAN static unsigned int
_efl_net_server_fd_efl_net_server_clients_count_get(Eo *o EINA_UNUSED, Efl_Net_Server_Fd_Data *pd)
{
return pd->clients_count;
}
EOLIAN static void
_efl_net_server_fd_efl_net_server_clients_count_set(Eo *o EINA_UNUSED, Efl_Net_Server_Fd_Data *pd, unsigned int count)
{
pd->clients_count = count;
}
EOLIAN static void
_efl_net_server_fd_efl_net_server_clients_limit_set(Eo *o EINA_UNUSED, Efl_Net_Server_Fd_Data *pd, unsigned int limit, Eina_Bool reject_excess)
{
pd->clients_limit = limit;
pd->clients_reject_excess = reject_excess;
}
EOLIAN static void
_efl_net_server_fd_efl_net_server_clients_limit_get(Eo *o EINA_UNUSED, Efl_Net_Server_Fd_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_fd_efl_net_server_serving_set(Eo *o EINA_UNUSED, Efl_Net_Server_Fd_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_Error
_efl_net_server_fd_efl_net_server_serve(Eo *o EINA_UNUSED, Efl_Net_Server_Fd_Data *pd EINA_UNUSED, const char *address)
{
DBG("address=%s", address);
return 0;
}
EOLIAN static Eina_Bool
_efl_net_server_fd_efl_net_server_serving_get(Eo *o EINA_UNUSED, Efl_Net_Server_Fd_Data *pd)
{
return pd->serving;
}
EOLIAN static Eina_Bool
_efl_net_server_fd_close_on_exec_set(Eo *o, Efl_Net_Server_Fd_Data *pd, Eina_Bool close_on_exec)
{
int flags, fd;
Eina_Bool old = pd->close_on_exec;
pd->close_on_exec = close_on_exec;
fd = efl_loop_fd_get(o);
if (fd < 0) return EINA_TRUE; /* postpone until fd_set() */
flags = fcntl(fd, F_GETFD);
if (flags < 0)
{
ERR("fcntl(%d, F_GETFD): %s", fd, strerror(errno));
return EINA_FALSE;
}
if (close_on_exec)
flags |= FD_CLOEXEC;
else
flags &= (~FD_CLOEXEC);
if (fcntl(fd, F_SETFD, flags) < 0)
{
ERR("fcntl(%d, F_SETFD, %#x): %s", fd, flags, strerror(errno));
pd->close_on_exec = old;
return EINA_FALSE;
}
return EINA_TRUE;
}
EOLIAN static Eina_Bool
_efl_net_server_fd_close_on_exec_get(Eo *o, Efl_Net_Server_Fd_Data *pd)
{
int flags, fd;
fd = efl_loop_fd_get(o);
if (fd < 0) return pd->close_on_exec;
/* if there is a fd, always query it directly as it may be modified
* elsewhere by nasty users.
*/
flags = fcntl(fd, F_GETFD);
if (flags < 0)
{
ERR("fcntl(%d, F_GETFD): %s", fd, strerror(errno));
return EINA_FALSE;
}
pd->close_on_exec = !!(flags & FD_CLOEXEC); /* sync */
return pd->close_on_exec;
}
EOLIAN static Eina_Bool
_efl_net_server_fd_reuse_address_set(Eo *o, Efl_Net_Server_Fd_Data *pd, Eina_Bool reuse_address)
{
int value, fd;
Eina_Bool old = pd->reuse_address;
pd->reuse_address = reuse_address;
fd = efl_loop_fd_get(o);
if (fd < 0) return EINA_TRUE; /* postpone until fd_set() */
value = reuse_address;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &value, sizeof(value)) < 0)
{
ERR("setsockopt(%d, SOL_SOCKET, SO_REUSEADDR, %d): %s",
fd, value, strerror(errno));
pd->reuse_address = old;
return EINA_FALSE;
}
return EINA_TRUE;
}
EOLIAN static Eina_Bool
_efl_net_server_fd_reuse_address_get(Eo *o, Efl_Net_Server_Fd_Data *pd)
{
int value = 0, fd;
socklen_t valuelen;
fd = efl_loop_fd_get(o);
if (fd < 0) return pd->reuse_address;
/* if there is a fd, always query it directly as it may be modified
* elsewhere by nasty users.
*/
valuelen = sizeof(value);
if (getsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &value, &valuelen) < 0)
{
ERR("getsockopt(%d, SOL_SOCKET, SO_REUSEADDR): %s",
fd, strerror(errno));
return EINA_FALSE;
}
pd->reuse_address = !!value; /* sync */
return pd->reuse_address;
}
EOLIAN static Eina_Bool
_efl_net_server_fd_reuse_port_set(Eo *o, Efl_Net_Server_Fd_Data *pd, Eina_Bool reuse_port)
{
int value, fd;
Eina_Bool old = pd->reuse_port;
pd->reuse_port = reuse_port;
fd = efl_loop_fd_get(o);
if (fd < 0) return EINA_TRUE; /* postpone until fd_set() */
value = reuse_port;
if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &value, sizeof(value)) < 0)
{
ERR("setsockopt(%d, SOL_SOCKET, SO_REUSEPORT, %d): %s",
fd, value, strerror(errno));
pd->reuse_port = old;
return EINA_FALSE;
}
return EINA_TRUE;
}
EOLIAN static Eina_Bool
_efl_net_server_fd_reuse_port_get(Eo *o, Efl_Net_Server_Fd_Data *pd)
{
int value = 0, fd;
socklen_t valuelen;
fd = efl_loop_fd_get(o);
if (fd < 0) return pd->reuse_port;
/* if there is a fd, always query it directly as it may be modified
* elsewhere by nasty users.
*/
valuelen = sizeof(value);
if (getsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &value, &valuelen) < 0)
{
ERR("getsockopt(%d, SOL_SOCKET, SO_REUSEPORT): %s",
fd, strerror(errno));
return EINA_FALSE;
}
pd->reuse_port = !!value; /* sync */
return pd->reuse_port;
}
EOLIAN static void
_efl_net_server_fd_family_set(Eo *o EINA_UNUSED, Efl_Net_Server_Fd_Data *pd, int family)
{
pd->family = family;
}
EOLIAN static int
_efl_net_server_fd_family_get(Eo *o EINA_UNUSED, Efl_Net_Server_Fd_Data *pd)
{
return pd->family;
}
#include "efl_net_server_fd.eo.c"

View File

@ -0,0 +1,103 @@
class Efl.Net.Server.Fd (Efl.Loop.Fd, Efl.Net.Server) {
[[A generic server based on file descriptors.
@since 1.19
]]
methods {
@property family {
[[The address family (AF_*) family of this socket.
It will be one of AF_INET (IPv4), AF_INET6 (IPv6),
AF_UNIX...
It must be set before the @Efl.Loop.Fd.fd.set is called
with a valid file descriptor.
]]
get { }
set @protected { }
values {
family: int;
}
}
@property close_on_exec {
[[Controls Close-on-Exec() using FD_CLOEXEC.
Children socket will inherit the server's setting by
default. One can change the behavior using each instance
@Efl.Net.Socket.Fd.close_on_exec.set.
]]
get { }
set {
return: bool (false); [[$true on success]]
}
values {
close_on_exec: bool;
}
}
@property reuse_address {
[[Controls address reuse() using SO_REUSEADDR]]
get { }
set {
return: bool (false); [[$true on success]]
}
values {
reuse_address: bool;
}
}
@property reuse_port {
[[Controls port reuse() using SO_REUSEPORT (since linux 3.9)]]
get { }
set {
return: bool (false); [[$true on success]]
}
values {
reuse_port: bool;
}
}
client_add @protected @virtual_pure {
[[Accept a new client, should emit "client,add".
Remember to create the client object with a callback to
EFL_IO_CLOSER_EVENT_CLOSED during the construction and
decrease @Efl.Net.Server.clients_count as well as unref
the client and remove yourself as parent.
The new clients should have the server as parent and
increase the @Efl.Net.Server.clients_count.
Whenever this function fails, it must close the given
client file descriptor.
]]
params {
client_fd: int; [[The file descriptor of the client socket. It comes preconfigured with close_on_exec. On failure, remember to close this socket]]
}
}
client_reject @protected @virtual_pure {
[[Reject a new client, should emit "client,rejected".
Must always close the client socket when it's done.
]]
params {
client_fd: int; [[The file descriptor of the client socket. It comes preconfigured with close_on_exec and should be closed once it's not needed anymore]]
}
}
}
implements {
Efl.Object.finalize;
Efl.Object.constructor;
Efl.Object.destructor;
Efl.Loop.Fd.fd.set;
Efl.Net.Server.address;
Efl.Net.Server.clients_count;
Efl.Net.Server.clients_limit;
Efl.Net.Server.serving;
Efl.Net.Server.serve;
}
}

View File

@ -0,0 +1,203 @@
#define EFL_NET_SERVER_TCP_PROTECTED 1
#define EFL_NET_SERVER_FD_PROTECTED 1
#define EFL_NET_SERVER_PROTECTED 1
#define EFL_LOOP_FD_PROTECTED 1
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "Ecore.h"
#include "Ecore_Con.h"
#include "ecore_con_private.h"
#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
#ifdef HAVE_NETINET_TCP_H
# include <netinet/tcp.h>
#endif
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
#ifdef HAVE_ARPA_INET_H
# include <arpa/inet.h>
#endif
#ifdef HAVE_EVIL
# include <Evil.h>
#endif
#define MY_CLASS EFL_NET_SERVER_TCP_CLASS
EOLIAN static Eina_Error
_efl_net_server_tcp_efl_net_server_serve(Eo *o, void *pd EINA_UNUSED, const char *address)
{
struct sockaddr_storage addr = {};
char *str, *host, *port;
int r, fd, extra_flags = 0;
socklen_t addrlen;
char buf[INET6_ADDRSTRLEN + sizeof("[]:65536")];
Eina_Error err = 0;
EINA_SAFETY_ON_NULL_RETURN_VAL(address, EINVAL);
// TODO: change to getaddrinfo() and move to a thread...
str = host = strdup(address);
EINA_SAFETY_ON_NULL_RETURN_VAL(str, ENOMEM);
if (host[0] == '[')
{
struct sockaddr_in6 *a = (struct sockaddr_in6 *)&addr;
/* IPv6 is: [IP]:port */
host++;
port = strchr(host, ']');
if (!port)
{
ERR("missing ']' in IPv6 address: %s", address);
err = EINVAL;
goto invalid_address;
}
*port = '\0';
port++;
if (port[0] == ':')
port++;
else
port = NULL;
a->sin6_family = AF_INET6;
a->sin6_port = htons(port ? atoi(port) : 0);
r = inet_pton(AF_INET6, host, &(a->sin6_addr));
addrlen = sizeof(*a);
}
else
{
struct sockaddr_in *a = (struct sockaddr_in *)&addr;
port = strchr(host, ':');
if (port)
{
*port = '\0';
port++;
}
a->sin_family = AF_INET;
a->sin_port = htons(port ? atoi(port) : 0);
r = inet_pton(AF_INET, host, &(a->sin_addr));
addrlen = sizeof(*a);
}
if (r != 1)
{
ERR("could not parse IP '%s' (%s)", host, address);
err = EINVAL;
goto invalid_address;
}
free(str);
efl_net_server_fd_family_set(o, addr.ss_family);
if (efl_net_ip_port_fmt(buf, sizeof(buf), (struct sockaddr *)&addr))
efl_net_server_address_set(o, buf);
if (efl_net_server_fd_close_on_exec_get(o))
extra_flags |= SOCK_CLOEXEC;
fd = socket(addr.ss_family, SOCK_STREAM | extra_flags, IPPROTO_TCP);
if (fd < 0)
{
err = errno;
ERR("socket(%d, SOCK_STREAM | %#x, IPPROTO_TCP): %s",
addr.ss_family, extra_flags, strerror(errno));
goto error_socket;
}
efl_loop_fd_set(o, fd);
r = bind(fd, (struct sockaddr *)&addr, addrlen);
if (r < 0)
{
err = errno;
ERR("bind(%d, %s): %s", fd, address, strerror(errno));
goto error_listen;
}
r = listen(fd, 0);
if (r < 0)
{
err = errno;
ERR("listen(%d): %s", fd, strerror(errno));
goto error_listen;
}
efl_net_server_serving_set(o, EINA_TRUE);
return 0;
invalid_address:
free(str);
goto error_socket;
error_listen:
close(fd);
error_socket:
efl_event_callback_call(o, EFL_NET_SERVER_EVENT_ERROR, &err);
return err;
}
static Efl_Callback_Array_Item *_efl_net_server_tcp_client_cbs(void);
static void
_efl_net_server_tcp_client_event_closed(void *data, const Eo_Event *event)
{
Eo *server = data;
Eo *client = event->object;
efl_event_callback_array_del(client, _efl_net_server_tcp_client_cbs(), 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);
}
EFL_CALLBACKS_ARRAY_DEFINE(_efl_net_server_tcp_client_cbs,
{ EFL_IO_CLOSER_EVENT_CLOSED, _efl_net_server_tcp_client_event_closed });
static void
_efl_net_server_tcp_efl_net_server_fd_client_add(Eo *o, void *pd EINA_UNUSED, int client_fd)
{
Eo *client = efl_add(EFL_NET_SOCKET_TCP_CLASS, o,
efl_event_callback_array_add(efl_self, _efl_net_server_tcp_client_cbs(), o),
efl_loop_fd_set(efl_self, client_fd));
if (!client)
{
ERR("could not create client object fd=%d", client_fd);
close(client_fd);
return;
}
efl_net_server_clients_count_set(o, efl_net_server_clients_count_get(o) + 1);
efl_event_callback_call(o, EFL_NET_SERVER_EVENT_CLIENT_ADD, client);
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));
efl_io_closer_close(client);
}
}
static void
_efl_net_server_tcp_efl_net_server_fd_client_reject(Eo *o, void *pd EINA_UNUSED, int client_fd)
{
struct sockaddr_storage addr;
socklen_t addrlen;
char str[INET6_ADDRSTRLEN + sizeof("[]:65536")] = "";
addrlen = sizeof(addr);
if (getpeername(client_fd, (struct sockaddr *)&addr, &addrlen) < 0)
ERR("getpeername(%d): %s", client_fd, strerror(errno));
else
efl_net_ip_port_fmt(str, sizeof(str), (struct sockaddr *)&addr);
close(client_fd);
efl_event_callback_call(o, EFL_NET_SERVER_EVENT_CLIENT_REJECTED, str);
}
#include "efl_net_server_tcp.eo.c"

View File

@ -0,0 +1,14 @@
class Efl.Net.Server.Tcp (Efl.Net.Server.Fd) {
[[A TCP server.
@since 1.19
]]
data: null;
implements {
Efl.Net.Server.serve;
Efl.Net.Server.Fd.client_add;
Efl.Net.Server.Fd.client_reject;
}
}

View File

@ -0,0 +1,14 @@
#define EFL_IO_READER_PROTECTED 1
#define EFL_IO_WRITER_PROTECTED 1
#define EFL_IO_CLOSER_PROTECTED 1
#define EFL_NET_SOCKET_PROTECTED 1
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "Ecore.h"
#include "Ecore_Con.h"
#include "ecore_con_private.h"
#include "efl_net_socket.eo.c"

View File

@ -0,0 +1,50 @@
interface Efl.Net.Socket (Efl.Io.Reader, Efl.Io.Writer, Efl.Io.Closer) {
[[The basic socket interface.
It is built upon the three core Input/Output interfaces:
- @Efl.Io.Reader: to receive data.
- @Efl.Io.Writer: to send data.
- @Efl.Io.Closer: to close the socket for further operations.
Thus it can be used with utilities like @Efl.Io.Copier.
@since 1.19
]]
methods {
@property address_local {
[[The local address, similar to getsockname().
The actual value depends on the type of socket, like an
IPv4 (ip:port) or IPv6 ([ip]:port) formatted for a
TCP/UDP socket, the path if an Unix Local...
It's always resolved, then if operations are working
with domain names or URL, this is the values the kernel
reports. It's similar to getsockname() in behavior.
]]
get { }
set @protected { }
values {
address: string;
}
}
@property address_remote {
[[The remote address, similar to getpeername().
The actual value depends on the type of socket, like an
IPv4 (ip:port) or IPv6 ([ip]:port) formatted for a
TCP/UDP socket, the path if an Unix Local...
It's always resolved, then if operations are working
with domain names or URL, this is the values the kernel
reports. It's similar to getpeername() in behavior.
]]
get { }
set @protected { }
values {
address: string;
}
}
}
}

View File

@ -0,0 +1,268 @@
#define EFL_NET_SOCKET_FD_PROTECTED 1
#define EFL_LOOP_FD_PROTECTED 1
#define EFL_IO_READER_FD_PROTECTED 1
#define EFL_IO_WRITER_FD_PROTECTED 1
#define EFL_IO_CLOSER_FD_PROTECTED 1
#define EFL_IO_READER_PROTECTED 1
#define EFL_IO_WRITER_PROTECTED 1
#define EFL_IO_CLOSER_PROTECTED 1
#define EFL_NET_SOCKET_PROTECTED 1
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "Ecore.h"
#include "Ecore_Con.h"
#include "ecore_con_private.h"
#include <fcntl.h>
#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
#ifdef HAVE_EVIL
# include <Evil.h>
#endif
#define MY_CLASS EFL_NET_SOCKET_FD_CLASS
typedef struct _Efl_Net_Socket_Fd_Data
{
Eina_Stringshare *address_local;
Eina_Stringshare *address_remote;
int family;
Eina_Bool close_on_exec;
} Efl_Net_Socket_Fd_Data;
static void
_efl_net_socket_fd_event_read(void *data EINA_UNUSED, const Eo_Event *event)
{
efl_io_reader_can_read_set(event->object, EINA_TRUE);
efl_io_reader_eos_set(event->object, EINA_FALSE);
}
static void
_efl_net_socket_fd_event_write(void *data EINA_UNUSED, const Eo_Event *event)
{
efl_io_writer_can_write_set(event->object, EINA_TRUE);
}
static void
_efl_net_socket_fd_event_error(void *data EINA_UNUSED, const Eo_Event *event)
{
efl_io_writer_can_write_set(event->object, EINA_FALSE);
efl_io_reader_can_read_set(event->object, EINA_FALSE);
efl_io_reader_eos_set(event->object, EINA_TRUE);
}
EOLIAN static Efl_Object *
_efl_net_socket_fd_efl_object_finalize(Eo *o, Efl_Net_Socket_Fd_Data *pd EINA_UNUSED)
{
o = efl_finalize(efl_super(o, MY_CLASS));
if (!o) return NULL;
// TODO: only register "read" if "can_read" is being monitored?
// TODO: only register "write" if "can_write" is being monitored?
efl_event_callback_add(o, EFL_LOOP_FD_EVENT_WRITE, _efl_net_socket_fd_event_write, NULL);
efl_event_callback_add(o, EFL_LOOP_FD_EVENT_READ, _efl_net_socket_fd_event_read, NULL);
efl_event_callback_add(o, EFL_LOOP_FD_EVENT_ERROR, _efl_net_socket_fd_event_error, NULL);
return o;
}
EOLIAN static Efl_Object *
_efl_net_socket_fd_efl_object_constructor(Eo *o, Efl_Net_Socket_Fd_Data *pd EINA_UNUSED)
{
pd->family = AF_UNSPEC;
return efl_constructor(efl_super(o, MY_CLASS));
}
EOLIAN static void
_efl_net_socket_fd_efl_object_destructor(Eo *o, Efl_Net_Socket_Fd_Data *pd)
{
efl_destructor(efl_super(o, MY_CLASS));
eina_stringshare_replace(&pd->address_local, NULL);
eina_stringshare_replace(&pd->address_remote, NULL);
}
static void
_efl_net_socket_fd_set(Eo *o, Efl_Net_Socket_Fd_Data *pd, int fd)
{
efl_io_reader_fd_reader_fd_set(o, fd);
efl_io_writer_fd_writer_fd_set(o, fd);
efl_io_closer_fd_closer_fd_set(o, fd);
/* apply postponed values */
efl_net_socket_fd_close_on_exec_set(o, pd->close_on_exec);
if (pd->family == AF_UNSPEC)
{
ERR("efl_loop_fd_set() must be called after efl_net_server_fd_family_set()");
return;
}
}
static void
_efl_net_socket_fd_unset(Eo *o)
{
efl_io_reader_fd_reader_fd_set(o, -1);
efl_io_writer_fd_writer_fd_set(o, -1);
efl_io_closer_fd_closer_fd_set(o, -1);
efl_net_socket_address_local_set(o, NULL);
efl_net_socket_address_remote_set(o, NULL);
}
EOLIAN static void
_efl_net_socket_fd_efl_loop_fd_fd_set(Eo *o, Efl_Net_Socket_Fd_Data *pd, int fd)
{
if ((pd->family == AF_UNSPEC) && (fd >= 0))
{
struct sockaddr_storage addr;
socklen_t addrlen = sizeof(addr);
if (getsockname(fd, (struct sockaddr *)&addr, &addrlen) < 0)
ERR("getsockname(%d): %s", fd, strerror(errno));
else
efl_net_socket_fd_family_set(o, addr.ss_family);
}
efl_loop_fd_set(efl_super(o, MY_CLASS), fd);
if (fd >= 0) _efl_net_socket_fd_set(o, pd, fd);
else _efl_net_socket_fd_unset(o);
}
EOLIAN static Eina_Error
_efl_net_socket_fd_efl_io_closer_close(Eo *o, Efl_Net_Socket_Fd_Data *pd EINA_UNUSED)
{
Eina_Error ret;
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);
/* skip _efl_net_socket_fd_efl_loop_fd_fd_set() since we want to
* retain efl_io_closer_fd_closer_fd_get() so close(super()) works
* and we emit the events with proper addresses.
*/
efl_loop_fd_set(efl_super(o, MY_CLASS), -1);
ret = efl_io_closer_close(efl_super(o, MY_CLASS));
/* do the cleanup our _efl_net_socket_fd_efl_loop_fd_fd_set() would do */
_efl_net_socket_fd_unset(o);
return ret;
}
EOLIAN static Eina_Error
_efl_net_socket_fd_efl_io_reader_read(Eo *o, Efl_Net_Socket_Fd_Data *pd EINA_UNUSED, Eina_Rw_Slice *rw_slice)
{
Eina_Error ret;
ret = efl_io_reader_read(efl_super(o, MY_CLASS), rw_slice);
if (rw_slice && rw_slice->len > 0)
efl_io_reader_can_read_set(o, EINA_FALSE); /* wait Efl.Loop.Fd "read" */
return ret;
}
EOLIAN static Eina_Error
_efl_net_socket_fd_efl_io_writer_write(Eo *o, Efl_Net_Socket_Fd_Data *pd EINA_UNUSED, Eina_Slice *ro_slice, Eina_Slice *remaining)
{
Eina_Error ret;
ret = efl_io_writer_write(efl_super(o, MY_CLASS), ro_slice, remaining);
if (ro_slice && ro_slice->len > 0)
efl_io_writer_can_write_set(o, EINA_FALSE); /* wait Efl.Loop.Fd "write" */
return ret;
}
EOLIAN static void
_efl_net_socket_fd_efl_net_socket_address_local_set(Eo *o EINA_UNUSED, Efl_Net_Socket_Fd_Data *pd, const char *address)
{
eina_stringshare_replace(&pd->address_local, address);
}
EOLIAN static const char *
_efl_net_socket_fd_efl_net_socket_address_local_get(Eo *o EINA_UNUSED, Efl_Net_Socket_Fd_Data *pd)
{
return pd->address_local;
}
EOLIAN static void
_efl_net_socket_fd_efl_net_socket_address_remote_set(Eo *o EINA_UNUSED, Efl_Net_Socket_Fd_Data *pd, const char *address)
{
eina_stringshare_replace(&pd->address_remote, address);
}
EOLIAN static const char *
_efl_net_socket_fd_efl_net_socket_address_remote_get(Eo *o EINA_UNUSED, Efl_Net_Socket_Fd_Data *pd)
{
return pd->address_remote;
}
EOLIAN static Eina_Bool
_efl_net_socket_fd_close_on_exec_set(Eo *o, Efl_Net_Socket_Fd_Data *pd, Eina_Bool close_on_exec)
{
int flags, fd;
pd->close_on_exec = close_on_exec;
fd = efl_loop_fd_get(o);
if (fd < 0) return EINA_TRUE; /* postpone until fd_set() */
flags = fcntl(fd, F_GETFD);
if (flags < 0)
{
ERR("fcntl(%d, F_GETFD): %s", fd, strerror(errno));
return EINA_FALSE;
}
if (close_on_exec)
flags |= FD_CLOEXEC;
else
flags &= (~FD_CLOEXEC);
if (fcntl(fd, F_SETFD, flags) < 0)
{
ERR("fcntl(%d, F_SETFD, %#x): %s", fd, flags, strerror(errno));
return EINA_FALSE;
}
return EINA_TRUE;
}
EOLIAN static Eina_Bool
_efl_net_socket_fd_close_on_exec_get(Eo *o, Efl_Net_Socket_Fd_Data *pd)
{
int flags, fd;
fd = efl_loop_fd_get(o);
if (fd < 0) return pd->close_on_exec;
/* if there is a fd, always query it directly as it may be modified
* elsewhere by nasty users.
*/
flags = fcntl(fd, F_GETFD);
if (flags < 0)
{
ERR("fcntl(%d, F_GETFD): %s", fd, strerror(errno));
return EINA_FALSE;
}
pd->close_on_exec = !!(flags & FD_CLOEXEC); /* sync */
return pd->close_on_exec;
}
EOLIAN static void
_efl_net_socket_fd_family_set(Eo *o EINA_UNUSED, Efl_Net_Socket_Fd_Data *pd, int family)
{
pd->family = family;
}
EOLIAN static int
_efl_net_socket_fd_family_get(Eo *o EINA_UNUSED, Efl_Net_Socket_Fd_Data *pd)
{
return pd->family;
}
#include "efl_net_socket_fd.eo.c"

View File

@ -0,0 +1,50 @@
class Efl.Net.Socket.Fd (Efl.Loop.Fd, Efl.Io.Reader.Fd, Efl.Io.Writer.Fd, Efl.Io.Closer.Fd, Efl.Net.Socket) {
[[A base implementation for sockets over filedescriptors (fd)
This is the common class and takes an existing FD, usually
created by an dialer or server.
@since 1.19
]]
methods {
@property family {
[[The address family (AF_*) family of this socket.
It will be one of AF_INET (IPv4), AF_INET6 (IPv6),
AF_UNIX...
It must be set before the @Efl.Loop.Fd.fd.set is called
with a valid file descriptor.
]]
get { }
set @protected { }
values {
family: int;
}
}
@property close_on_exec {
[[Controls Close-on-Exec() using FD_CLOEXEC]]
get { }
set {
return: bool (false); [[$true on success]]
}
values {
close_on_exec: bool;
}
}
}
implements {
Efl.Object.finalize;
Efl.Object.constructor;
Efl.Object.destructor;
Efl.Loop.Fd.fd.set;
Efl.Io.Closer.close;
Efl.Io.Reader.read;
Efl.Io.Writer.write;
Efl.Net.Socket.address_local;
Efl.Net.Socket.address_remote;
}
}

View File

@ -0,0 +1,227 @@
#define EFL_NET_SOCKET_TCP_PROTECTED 1
#define EFL_NET_SOCKET_FD_PROTECTED 1
#define EFL_LOOP_FD_PROTECTED 1
#define EFL_IO_READER_FD_PROTECTED 1
#define EFL_IO_WRITER_FD_PROTECTED 1
#define EFL_IO_CLOSER_FD_PROTECTED 1
#define EFL_IO_READER_PROTECTED 1
#define EFL_IO_WRITER_PROTECTED 1
#define EFL_IO_CLOSER_PROTECTED 1
#define EFL_NET_SOCKET_PROTECTED 1
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "Ecore.h"
#include "Ecore_Con.h"
#include "ecore_con_private.h"
#ifdef HAVE_SYS_SOCKET_H
# include <sys/socket.h>
#endif
#ifdef HAVE_NETINET_TCP_H
# include <netinet/tcp.h>
#endif
#ifdef HAVE_NETINET_IN_H
# include <netinet/in.h>
#endif
#ifdef HAVE_ARPA_INET_H
# include <arpa/inet.h>
#endif
#ifdef HAVE_EVIL
# include <Evil.h>
#endif
#define MY_CLASS EFL_NET_SOCKET_TCP_CLASS
typedef struct _Efl_Net_Socket_Tcp_Data
{
Eina_Bool keep_alive;
Eina_Bool no_delay;
Eina_Bool cork;
} Efl_Net_Socket_Tcp_Data;
EOLIAN static void
_efl_net_socket_tcp_efl_loop_fd_fd_set(Eo *o, Efl_Net_Socket_Tcp_Data *pd EINA_UNUSED, int fd)
{
efl_loop_fd_set(efl_super(o, MY_CLASS), fd);
if (fd >= 0)
{
struct sockaddr_storage addr;
socklen_t addrlen;
int family;
/* apply postponed values */
efl_net_socket_tcp_keep_alive_set(o, pd->keep_alive);
efl_net_socket_tcp_no_delay_set(o, pd->no_delay);
efl_net_socket_tcp_cork_set(o, pd->cork);
family = efl_net_socket_fd_family_get(o);
if (family == AF_UNSPEC) return;
addrlen = sizeof(addr);
if (getsockname(fd, (struct sockaddr *)&addr, &addrlen) < 0)
ERR("getsockname(%d): %s", fd, strerror(errno));
else
{
char str[INET6_ADDRSTRLEN + sizeof("[]:65536")];
if (efl_net_ip_port_fmt(str, sizeof(str), (struct sockaddr *)&addr))
efl_net_socket_address_local_set(o, str);
}
addrlen = sizeof(addr);
if (getpeername(fd, (struct sockaddr *)&addr, &addrlen) < 0)
ERR("getpeername(%d): %s", fd, strerror(errno));
else
{
char str[INET6_ADDRSTRLEN + sizeof("[]:65536")];
if (efl_net_ip_port_fmt(str, sizeof(str), (struct sockaddr *)&addr))
efl_net_socket_address_remote_set(o, str);
}
}
}
EOLIAN static Eina_Bool
_efl_net_socket_tcp_keep_alive_set(Eo *o, Efl_Net_Socket_Tcp_Data *pd, Eina_Bool keep_alive)
{
int value, fd;
Eina_Bool old = pd->keep_alive;
pd->keep_alive = keep_alive;
fd = efl_loop_fd_get(o);
if (fd < 0) return EINA_TRUE; /* postpone until fd_set() */
value = keep_alive;
if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &value, sizeof(value)) < 0)
{
ERR("setsockopt(%d, SOL_SOCKET, SO_KEEPALIVE, %d): %s",
fd, value, strerror(errno));
pd->keep_alive = old;
return EINA_FALSE;
}
return EINA_TRUE;
}
EOLIAN static Eina_Bool
_efl_net_socket_tcp_keep_alive_get(Eo *o, Efl_Net_Socket_Tcp_Data *pd)
{
int value = 0, fd;
socklen_t valuelen;
fd = efl_loop_fd_get(o);
if (fd < 0) return pd->keep_alive;
/* if there is a fd, always query it directly as it may be modified
* elsewhere by nasty users.
*/
valuelen = sizeof(value);
if (getsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &value, &valuelen) < 0)
{
ERR("getsockopt(%d, SOL_SOCKET, SO_KEEPALIVE): %s",
fd, strerror(errno));
return EINA_FALSE;
}
pd->keep_alive = !!value; /* sync */
return pd->keep_alive;
}
EOLIAN static Eina_Bool
_efl_net_socket_tcp_no_delay_set(Eo *o, Efl_Net_Socket_Tcp_Data *pd, Eina_Bool no_delay)
{
int value, fd;
Eina_Bool old = pd->no_delay;
pd->no_delay = no_delay;
fd = efl_loop_fd_get(o);
if (fd < 0) return EINA_TRUE; /* postpone until fd_set() */
value = no_delay;
if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &value, sizeof(value)) < 0)
{
ERR("setsockopt(%d, IPPROTO_TCP, TCP_NODELAY, %d): %s",
fd, value, strerror(errno));
pd->no_delay = old;
return EINA_FALSE;
}
return EINA_TRUE;
}
EOLIAN static Eina_Bool
_efl_net_socket_tcp_no_delay_get(Eo *o, Efl_Net_Socket_Tcp_Data *pd)
{
int value = 0, fd;
socklen_t valuelen;
fd = efl_loop_fd_get(o);
if (fd < 0) return pd->no_delay;
/* if there is a fd, always query it directly as it may be modified
* elsewhere by nasty users.
*/
valuelen = sizeof(value);
if (getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &value, &valuelen) < 0)
{
ERR("getsockopt(%d, IPPROTO_TCP, TCP_NODELAY): %s",
fd, strerror(errno));
return EINA_FALSE;
}
pd->no_delay = !!value; /* sync */
return pd->no_delay;
}
EOLIAN static Eina_Bool
_efl_net_socket_tcp_cork_set(Eo *o, Efl_Net_Socket_Tcp_Data *pd, Eina_Bool cork)
{
int value, fd;
Eina_Bool old = pd->cork;
pd->cork = cork;
fd = efl_loop_fd_get(o);
if (fd < 0) return EINA_TRUE; /* postpone until fd_set() */
value = cork;
if (setsockopt(fd, IPPROTO_TCP, TCP_CORK, &value, sizeof(value)) < 0)
{
ERR("setsockopt(%d, IPPROTO_TCP, TCP_CORK, %d): %s",
fd, value, strerror(errno));
pd->cork = old;
return EINA_FALSE;
}
return EINA_TRUE;
}
EOLIAN static Eina_Bool
_efl_net_socket_tcp_cork_get(Eo *o, Efl_Net_Socket_Tcp_Data *pd)
{
int value = 0, fd;
socklen_t valuelen;
fd = efl_loop_fd_get(o);
if (fd < 0) return pd->cork;
/* if there is a fd, always query it directly as it may be modified
* elsewhere by nasty users.
*/
valuelen = sizeof(value);
if (getsockopt(fd, IPPROTO_TCP, TCP_CORK, &value, &valuelen) < 0)
{
ERR("getsockopt(%d, IPPROTO_TCP, TCP_CORK): %s",
fd, strerror(errno));
return EINA_FALSE;
}
pd->cork = !!value; /* sync */
return pd->cork;
}
#include "efl_net_socket_tcp.eo.c"

View File

@ -0,0 +1,48 @@
class Efl.Net.Socket.Tcp (Efl.Net.Socket.Fd) {
[[A base TCP socket.
This is the common class and takes an existing FD, usually
created by an dialer or server.
@since 1.19
]]
methods {
@property keep_alive {
[[Controls keep-alive using SO_KEEPALIVE]]
get { }
set {
return: bool (false); [[$true on success]]
}
values {
keep_alive: bool;
}
}
@property no_delay {
[[Controls TCP's no-delay using TCP_NODELAY]]
get { }
set {
return: bool (false); [[$true on success]]
}
values {
no_delay: bool;
}
}
@property cork {
[[Controls TCP's cork using TCP_CORK]]
get { }
set {
return: bool (false); [[$true on success]]
}
values {
cork: bool;
}
}
}
implements {
Efl.Loop.Fd.fd.set;
}
}