#include "efl_net_server_tcp.eo.h"
#include "efl_net_http_types.eot.h"
+
+/* TODO: should be generated from 'var Efl.Net.Dialer.Error.*' */
+extern Eina_Error EFL_NET_DIALER_ERROR_COULDNT_CONNECT;
+extern Eina_Error EFL_NET_DIALER_ERROR_COULDNT_RESOLVE_PROXY;
+extern Eina_Error EFL_NET_DIALER_ERROR_COULDNT_RESOLVE_HOST;
+
/* TODO: should be generated from 'var Efl.Net.Http.Error.*' */
extern Eina_Error EFL_NET_HTTP_ERROR_BAD_CONTENT_ENCODING;
extern Eina_Error EFL_NET_HTTP_ERROR_BAD_DOWNLOAD_RESUME;
extern Eina_Error EFL_NET_HTTP_ERROR_CHUNK_FAILED;
extern Eina_Error EFL_NET_HTTP_ERROR_CONV_FAILED;
extern Eina_Error EFL_NET_HTTP_ERROR_CONV_REQD;
-extern Eina_Error EFL_NET_HTTP_ERROR_COULDNT_CONNECT;
-extern Eina_Error EFL_NET_HTTP_ERROR_COULDNT_RESOLVE_HOST;
-extern Eina_Error EFL_NET_HTTP_ERROR_COULDNT_RESOLVE_PROXY;
extern Eina_Error EFL_NET_HTTP_ERROR_FAILED_INIT;
extern Eina_Error EFL_NET_HTTP_ERROR_FILE_COULDNT_READ_FILE;
extern Eina_Error EFL_NET_HTTP_ERROR_FILESIZE_EXCEEDED;
EAPI int ECORE_CON_EVENT_SERVER_ERROR = 0;
EAPI int ECORE_CON_EVENT_PROXY_BIND = 0;
+EAPI Eina_Error EFL_NET_DIALER_ERROR_COULDNT_CONNECT = 0;
+EAPI Eina_Error EFL_NET_DIALER_ERROR_COULDNT_RESOLVE_PROXY = 0;
+EAPI Eina_Error EFL_NET_DIALER_ERROR_COULDNT_RESOLVE_HOST = 0;
+
static Eina_List *servers = NULL;
static int _ecore_con_init_count = 0;
static int _ecore_con_event_count = 0;
ECORE_CON_EVENT_SERVER_ERROR = ecore_event_type_new();
ECORE_CON_EVENT_PROXY_BIND = ecore_event_type_new();
+ EFL_NET_DIALER_ERROR_COULDNT_CONNECT = eina_error_msg_static_register("Couldn't connect to server");
+ EFL_NET_DIALER_ERROR_COULDNT_RESOLVE_PROXY = eina_error_msg_static_register("Couldn't resolve proxy name");
+ EFL_NET_DIALER_ERROR_COULDNT_RESOLVE_HOST = eina_error_msg_static_register("Couldn't resolve host name");
+
eina_magic_string_set(ECORE_MAGIC_CON_SERVER, "Ecore_Con_Server");
eina_magic_string_set(ECORE_MAGIC_CON_CLIENT, "Ecore_Con_Client");
eina_magic_string_set(ECORE_MAGIC_CON_URL, "Ecore_Con_Url");
return EINA_TRUE;
}
+Eina_Bool
+efl_net_ip_port_split(char *buf, const char **p_host, const char **p_port)
+{
+ char *host, *port;
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(buf, EINA_FALSE);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(p_host, EINA_FALSE);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(p_port, EINA_FALSE);
+
+ host = buf;
+ if (host[0] == '[')
+ {
+ /* IPv6 is: [IP]:port */
+ host++;
+ port = strchr(host, ']');
+ if (!port) return EINA_FALSE;
+ *port = '\0';
+ port++;
+
+ if (port[0] == ':')
+ port++;
+ else
+ port = NULL;
+ }
+ else
+ {
+ port = strchr(host, ':');
+ if (port)
+ {
+ *port = '\0';
+ port++;
+ }
+ }
+
+ *p_host = host;
+ *p_port = port;
+ return EINA_TRUE;
+}
+
+
int
efl_net_socket4(int domain, int type, int protocol, Eina_Bool close_on_exec)
{
return fd;
}
+
+typedef struct _Efl_Net_Resolve_Async_Data
+{
+ Efl_Net_Resolve_Async_Cb cb;
+ const void *data;
+ char *host;
+ char *port;
+ struct addrinfo *result;
+ struct addrinfo *hints;
+ int gai_error;
+} Efl_Net_Resolve_Async_Data;
+
+static void
+_efl_net_resolve_async_run(void *data, Ecore_Thread *thread)
+{
+ Efl_Net_Resolve_Async_Data *d = data;
+
+ while (!ecore_thread_check(thread))
+ {
+ DBG("resolving host='%s' port='%s'", d->host, d->port);
+ d->gai_error = getaddrinfo(d->host, d->port, d->hints, &d->result);
+ if (d->gai_error == 0) break;
+ if (d->gai_error == EAI_AGAIN) continue;
+
+ DBG("getaddrinfo(\"%s\", \"%s\") failed: %s", d->host, d->port, gai_strerror(d->gai_error));
+ break;
+ }
+
+ if (eina_log_domain_level_check(_ecore_con_log_dom, EINA_LOG_LEVEL_DBG))
+ {
+ char buf[INET6_ADDRSTRLEN + sizeof("[]:65536")] = "";
+ const struct addrinfo *addrinfo;
+ for (addrinfo = d->result; addrinfo != NULL; addrinfo = addrinfo->ai_next)
+ {
+ if (efl_net_ip_port_fmt(buf, sizeof(buf), addrinfo->ai_addr))
+ DBG("resolved host='%s' port='%s': %s", d->host, d->port, buf);
+ }
+ }
+}
+
+static void
+_efl_net_resolve_async_data_free(Efl_Net_Resolve_Async_Data *d)
+{
+ free(d->hints);
+ free(d->host);
+ free(d->port);
+ free(d);
+}
+
+static void
+_efl_net_resolve_async_end(void *data, Ecore_Thread *thread EINA_UNUSED)
+{
+ Efl_Net_Resolve_Async_Data *d = data;
+ d->cb((void *)d->data, d->host, d->port, d->hints, d->result, d->gai_error);
+ _efl_net_resolve_async_data_free(d);
+}
+
+static void
+_efl_net_resolve_async_cancel(void *data, Ecore_Thread *thread EINA_UNUSED)
+{
+ Efl_Net_Resolve_Async_Data *d = data;
+ if (d->result) freeaddrinfo(d->result);
+ _efl_net_resolve_async_data_free(d);
+}
+
+Ecore_Thread *
+efl_net_resolve_async_new(const char *host, const char *port, const struct addrinfo *hints, Efl_Net_Resolve_Async_Cb cb, const void *data)
+{
+ Efl_Net_Resolve_Async_Data *d;
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(host, NULL);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(port, NULL);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(cb, NULL);
+
+ d = malloc(sizeof(Efl_Net_Resolve_Async_Data));
+ EINA_SAFETY_ON_NULL_RETURN_VAL(d, NULL);
+
+ d->cb = cb;
+ d->data = data;
+ d->host = strdup(host);
+ EINA_SAFETY_ON_NULL_GOTO(d->host, failed_host);
+ d->port = strdup(port);
+ EINA_SAFETY_ON_NULL_GOTO(d->port, failed_port);
+
+ if (!hints) d->hints = NULL;
+ else
+ {
+ d->hints = malloc(sizeof(struct addrinfo));
+ EINA_SAFETY_ON_NULL_GOTO(d->hints, failed_hints);
+ memcpy(d->hints, hints, sizeof(struct addrinfo));
+ }
+
+ d->result = NULL;
+
+ return ecore_thread_run(_efl_net_resolve_async_run,
+ _efl_net_resolve_async_end,
+ _efl_net_resolve_async_cancel,
+ d);
+
+ failed_hints:
+ free(d->port);
+ failed_port:
+ free(d->host);
+ failed_host:
+ free(d);
+ return NULL;
+}
+
+typedef struct _Efl_Net_Connect_Async_Data
+{
+ Efl_Net_Connect_Async_Cb cb;
+ const void *data;
+ socklen_t addrlen;
+ Eina_Bool close_on_exec;
+ int type;
+ int protocol;
+ int sockfd;
+ Eina_Error error;
+ struct sockaddr addr[];
+} Efl_Net_Connect_Async_Data;
+
+static void
+_efl_net_connect_async_run(void *data, Ecore_Thread *thread)
+{
+ Efl_Net_Connect_Async_Data *d = data;
+ char buf[INET6_ADDRSTRLEN + sizeof("[]:65536")] = "";
+ int r;
+
+ d->error = 0;
+
+ d->sockfd = efl_net_socket4(d->addr->sa_family, d->type, d->protocol, d->close_on_exec);
+ if (d->sockfd < 0)
+ {
+ d->error = errno;
+ DBG("socket(%d, %d, %d) failed: %s", d->addr->sa_family, d->type, d->protocol, strerror(errno));
+ return;
+ }
+
+ if (ecore_thread_check(thread))
+ {
+ d->error = ECANCELED;
+ close(d->sockfd);
+ d->sockfd = -1;
+ return;
+ }
+
+ if (eina_log_domain_level_check(_ecore_con_log_dom, EINA_LOG_LEVEL_DBG))
+ efl_net_ip_port_fmt(buf, sizeof(buf), d->addr);
+
+ DBG("connecting fd=%d to %s", d->sockfd, buf);
+
+ r = connect(d->sockfd, d->addr, d->addrlen);
+ if (r < 0)
+ {
+ d->error = errno;
+ close(d->sockfd);
+ d->sockfd = -1;
+ DBG("connect(%d, %s) failed: %s", d->sockfd, buf, strerror(errno));
+ return;
+ }
+
+ DBG("connected fd=%d to %s", d->sockfd, buf);
+}
+
+static void
+_efl_net_connect_async_data_free(Efl_Net_Connect_Async_Data *d)
+{
+ free(d);
+}
+
+static void
+_efl_net_connect_async_end(void *data, Ecore_Thread *thread EINA_UNUSED)
+{
+ Efl_Net_Connect_Async_Data *d = data;
+ d->cb((void *)d->data, d->addr, d->addrlen, d->sockfd, d->error);
+ _efl_net_connect_async_data_free(d);
+}
+
+static void
+_efl_net_connect_async_cancel(void *data, Ecore_Thread *thread EINA_UNUSED)
+{
+ Efl_Net_Connect_Async_Data *d = data;
+ if (d->sockfd >= 0) close(d->sockfd);
+ _efl_net_connect_async_data_free(d);
+}
+
+Ecore_Thread *
+efl_net_connect_async_new(const struct sockaddr *addr, socklen_t addrlen, int type, int protocol, Eina_Bool close_on_exec, Efl_Net_Connect_Async_Cb cb, const void *data)
+{
+ Efl_Net_Connect_Async_Data *d;
+
+ EINA_SAFETY_ON_NULL_RETURN_VAL(addr, NULL);
+ EINA_SAFETY_ON_TRUE_RETURN_VAL(addrlen < 1, NULL);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(cb, NULL);
+
+ d = malloc(sizeof(Efl_Net_Connect_Async_Data) + addrlen);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(d, NULL);
+
+ d->cb = cb;
+ d->data = data;
+ d->addrlen = addrlen;
+ d->close_on_exec = close_on_exec;
+ d->type = type;
+ d->protocol = protocol;
+ memcpy(d->addr, addr, addrlen);
+
+ d->sockfd = -1;
+ d->error = 0;
+
+ return ecore_thread_run(_efl_net_connect_async_run,
+ _efl_net_connect_async_end,
+ _efl_net_connect_async_cancel,
+ d);
+}
Eina_Bool efl_net_ip_port_fmt(char *buf, int buflen, const struct sockaddr *addr);
+/**
+ * @brief splits an address in the format "host:port" in two
+ * null-terminated strings.
+ *
+ * The address may be 'server.com:1234', 'server.com:http',
+ * 'server.com' (@c *p_port will be NULL), IPv4 127.0.0.1:456 or
+ * IPv6 [::1]:456
+ *
+ * @param[inout] buf contains the string to be split and will be modified.
+ * @param[out] p_host returns a pointer inside @a buf with
+ * null-terminated host part.
+ * @param[out] p_port returns a pointer with null-terminated port
+ * part. The pointer may be inside @a buf if port was
+ * specified or #NULL if it wasn't specified.
+ *
+ * @return #EINA_TRUE on success, #EINA_FALSE on errors.
+ *
+ * @internal
+ */
+Eina_Bool efl_net_ip_port_split(char *buf, const char **p_host, const char **p_port);
+
int efl_net_socket4(int domain, int type, int protocol, Eina_Bool close_on_exec);
+/**
+ * @brief callback to notify of resolved address.
+ *
+ * The callback is given the ownership of the result, thus must free
+ * it with freeaddrinfo().
+ *
+ * @internal
+ */
+typedef void (*Efl_Net_Resolve_Async_Cb)(void *data, const char *host, const char *port, const struct addrinfo *hints, struct addrinfo *result, int gai_error);
+
+/**
+ * @brief asynchronously resolve a host and port using getaddrinfo().
+ *
+ * This will call getaddrinfo() in a thread, taking care to return the
+ * result to the main loop and calling @a cb with given user @a data.
+ *
+ * @internal
+ */
+Ecore_Thread *efl_net_resolve_async_new(const char *host, const char *port, const struct addrinfo *hints, Efl_Net_Resolve_Async_Cb cb, const void *data);
+
+/**
+ * @brief callback to notify of connection.
+ *
+ * The callback is given the ownership of the socket (sockfd), thus
+ * must close().
+ *
+ * @internal
+ */
+typedef void (*Efl_Net_Connect_Async_Cb)(void *data, const struct sockaddr *addr, socklen_t addrlen, int sockfd, Eina_Error error);
+
+/**
+ * @brief asynchronously create a socket and connect to the address.
+ *
+ * This will call socket() and connect() in a thread, taking care to
+ * return the result to the main loop and calling @a cb with given
+ * user @a data.
+ *
+ * @internal
+ */
+Ecore_Thread *efl_net_connect_async_new(const struct sockaddr *addr, socklen_t addrlen, int type, int protocol, Eina_Bool close_on_exec, Efl_Net_Connect_Async_Cb cb, const void *data);
+
static inline Eina_Error
efl_net_socket_error_get(void)
{
EAPI Eina_Error EFL_NET_HTTP_ERROR_CHUNK_FAILED = 0;
EAPI Eina_Error EFL_NET_HTTP_ERROR_CONV_FAILED = 0;
EAPI Eina_Error EFL_NET_HTTP_ERROR_CONV_REQD = 0;
-EAPI Eina_Error EFL_NET_HTTP_ERROR_COULDNT_CONNECT = 0;
-EAPI Eina_Error EFL_NET_HTTP_ERROR_COULDNT_RESOLVE_HOST = 0;
-EAPI Eina_Error EFL_NET_HTTP_ERROR_COULDNT_RESOLVE_PROXY = 0;
EAPI Eina_Error EFL_NET_HTTP_ERROR_FAILED_INIT = 0;
EAPI Eina_Error EFL_NET_HTTP_ERROR_FILE_COULDNT_READ_FILE = 0;
EAPI Eina_Error EFL_NET_HTTP_ERROR_FILESIZE_EXCEEDED = 0;
case CURLE_AGAIN: return EAGAIN;
case CURLE_OUT_OF_MEMORY: return ENOMEM;
+ case CURLE_COULDNT_CONNECT: return EFL_NET_DIALER_ERROR_COULDNT_CONNECT;
+ case CURLE_COULDNT_RESOLVE_PROXY: return EFL_NET_DIALER_ERROR_COULDNT_RESOLVE_PROXY;
+ case CURLE_COULDNT_RESOLVE_HOST: return EFL_NET_DIALER_ERROR_COULDNT_RESOLVE_HOST;
+
#define _MAP(n) case CURLE_ ## n: return EFL_NET_HTTP_ERROR_ ## n
_MAP(BAD_CONTENT_ENCODING);
_MAP(CHUNK_FAILED);
_MAP(CONV_FAILED);
_MAP(CONV_REQD);
- _MAP(COULDNT_CONNECT);
- _MAP(COULDNT_RESOLVE_HOST);
- _MAP(COULDNT_RESOLVE_PROXY);
_MAP(FAILED_INIT);
_MAP(FILE_COULDNT_READ_FILE);
_MAP(FILESIZE_EXCEEDED);
_MAP(CHUNK_FAILED);
_MAP(CONV_FAILED);
_MAP(CONV_REQD);
- _MAP(COULDNT_CONNECT);
- _MAP(COULDNT_RESOLVE_HOST);
- _MAP(COULDNT_RESOLVE_PROXY);
_MAP(FAILED_INIT);
_MAP(FILE_COULDNT_READ_FILE);
_MAP(FILESIZE_EXCEEDED);
+var @extern Efl.Net.Dialer.Error.COULDNT_CONNECT: Eina.Error;
+var @extern Efl.Net.Dialer.Error.COULDNT_RESOLVE_PROXY: Eina.Error;
+var @extern Efl.Net.Dialer.Error.COULDNT_RESOLVE_HOST: Eina.Error;
+
interface Efl.Net.Dialer (Efl.Net.Socket) {
[[Creates a client socket to reach a remote peer.
typedef struct _Efl_Net_Dialer_Tcp_Data
{
+ struct {
+ Ecore_Thread *thread;
+ struct addrinfo *names;
+ struct addrinfo *current;
+ } resolve;
+ struct {
+ Ecore_Thread *thread;
+ } connect;
Eina_Stringshare *address_dial;
Eina_Stringshare *proxy;
Eina_Bool connected;
+ Eina_Bool closed;
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)
{
+ if (pd->connect.thread)
+ {
+ ecore_thread_cancel(pd->connect.thread);
+ pd->connect.thread = NULL;
+ }
+
+ if (pd->resolve.thread)
+ {
+ ecore_thread_cancel(pd->resolve.thread);
+ pd->resolve.thread = NULL;
+ }
+
+ pd->resolve.current = NULL;
+ if (pd->resolve.names)
+ {
+ freeaddrinfo(pd->resolve.names);
+ pd->resolve.names = NULL;
+ }
+
efl_destructor(efl_super(o, MY_CLASS));
eina_stringshare_replace(&pd->address_dial, NULL);
eina_stringshare_replace(&pd->proxy, 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)
+static void
+_efl_net_dialer_tcp_connected(void *data, const struct sockaddr *addr EINA_UNUSED, socklen_t addrlen EINA_UNUSED, int fd, Eina_Error err)
{
- struct sockaddr_storage addr = {};
- char *str, *host, *port;
- int r, fd;
- socklen_t addrlen;
- char buf[INET6_ADDRSTRLEN + sizeof("[]:65536")];
+ Eo *o = data;
+ Efl_Net_Dialer_Tcp_Data *pd = efl_data_scope_get(o, MY_CLASS);
+ const struct addrinfo *addrinfo;
- 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);
+ pd->connect.thread = NULL;
- // TODO: change to getaddrinfo() and move to a thread...
- str = host = strdup(address);
- EINA_SAFETY_ON_NULL_RETURN_VAL(str, ENOMEM);
+ if (!err)
+ {
+ freeaddrinfo(pd->resolve.names);
+ pd->resolve.names = NULL;
+ pd->resolve.current = NULL;
+ efl_loop_fd_set(o, fd);
+ efl_net_dialer_connected_set(o, EINA_TRUE);
+ return;
+ }
- if (host[0] == '[')
+ if (!pd->resolve.current) return;
+
+ pd->resolve.current = pd->resolve.current->ai_next;
+ if (!pd->resolve.current)
{
- struct sockaddr_in6 *a = (struct sockaddr_in6 *)&addr;
- /* IPv6 is: [IP]:port */
- host++;
- port = strchr(host, ']');
- if (!port)
+ freeaddrinfo(pd->resolve.names);
+ pd->resolve.names = NULL;
+ if (err)
+ efl_event_callback_call(o, EFL_NET_DIALER_EVENT_ERROR, &err);
+ else
{
- ERR("missing ']' in IPv6 address: %s", address);
- goto invalid_address;
+ err = EFL_NET_DIALER_ERROR_COULDNT_CONNECT;
+ efl_event_callback_call(o, EFL_NET_DIALER_EVENT_ERROR, &err);
}
- *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);
+ return;
}
- else
+
+ addrinfo = pd->resolve.current;
+ pd->connect.thread = efl_net_connect_async_new(addrinfo->ai_addr,
+ addrinfo->ai_addrlen,
+ SOCK_STREAM,
+ IPPROTO_TCP,
+ efl_net_socket_fd_close_on_exec_get(o),
+ _efl_net_dialer_tcp_connected,
+ o);
+}
+
+static void
+_efl_net_dialer_tcp_connect(Eo *o, Efl_Net_Dialer_Tcp_Data *pd)
+{
+ char buf[INET6_ADDRSTRLEN + sizeof("[]:65536")];
+ const struct addrinfo *addrinfo;
+
+ if (pd->closed || !pd->resolve.current) return;
+
+ efl_ref(o); /* we're emitting callbacks then continuing the workflow */
+
+ addrinfo = pd->resolve.current;
+
+ efl_net_socket_fd_family_set(o, addrinfo->ai_family);
+ if (efl_net_ip_port_fmt(buf, sizeof(buf), addrinfo->ai_addr))
{
- 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);
+ efl_net_socket_address_remote_set(o, buf);
+ efl_event_callback_call(o, EFL_NET_DIALER_EVENT_RESOLVED, NULL);
}
- if (r != 1)
+ if (pd->closed) goto end; /* maybe closed from resolved event callback */
+
+ if (pd->connect.thread)
+ ecore_thread_cancel(pd->connect.thread);
+
+ pd->connect.thread = efl_net_connect_async_new(addrinfo->ai_addr,
+ addrinfo->ai_addrlen,
+ SOCK_STREAM,
+ IPPROTO_TCP,
+ efl_net_socket_fd_close_on_exec_get(o),
+ _efl_net_dialer_tcp_connected,
+ o);
+ end:
+ efl_unref(o);
+}
+
+static void
+_efl_net_dialer_tcp_resolved(void *data, const char *host EINA_UNUSED, const char *port EINA_UNUSED, const struct addrinfo *hints EINA_UNUSED, struct addrinfo *result, int gai_error)
+{
+ Eo *o = data;
+ Efl_Net_Dialer_Tcp_Data *pd = efl_data_scope_get(o, MY_CLASS);
+
+ pd->resolve.thread = NULL;
+
+ if (gai_error)
{
- ERR("could not parse IP '%s' (%s)", host, address);
- goto invalid_address;
+ Eina_Error err = EFL_NET_DIALER_ERROR_COULDNT_RESOLVE_HOST;
+ efl_event_callback_call(o, EFL_NET_DIALER_EVENT_ERROR, &err);
+ if (result) freeaddrinfo(result);
+ return;
}
- free(str);
- efl_net_socket_fd_family_set(o, addr.ss_family);
- efl_net_dialer_address_dial_set(o, address);
+ if (pd->resolve.names) freeaddrinfo(pd->resolve.names);
+ pd->resolve.names = result;
+ pd->resolve.current = result;
+
+ _efl_net_dialer_tcp_connect(o, pd);
+}
+
+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)
+{
+ const char *host, *port;
+ struct addrinfo hints = {
+ .ai_socktype = SOCK_STREAM,
+ .ai_protocol = IPPROTO_TCP,
+ };
+ char *str;
+
+ 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);
+
+ str = strdup(address);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(str, ENOMEM);
- if (efl_net_ip_port_fmt(buf, sizeof(buf), (struct sockaddr *)&addr))
+ if (!efl_net_ip_port_split(str, &host, &port))
{
- efl_net_socket_address_remote_set(o, buf);
- efl_event_callback_call(o, EFL_NET_DIALER_EVENT_RESOLVED, NULL);
+ ERR("could not parse IP:PORT '%s'", address);
+ free(str);
+ return EINVAL;
}
+ if (strchr(host, ':')) hints.ai_family = AF_INET6;
+ if (!port) port = "0";
- fd = efl_net_socket4(addr.ss_family, SOCK_STREAM, IPPROTO_TCP, efl_net_socket_fd_close_on_exec_get(o));
- if (fd < 0)
+ if (pd->connect.thread)
{
- ERR("socket(%d, SOCK_STREAM, IPPROTO_TCP): %s",
- addr.ss_family, strerror(errno));
- return errno;
+ ecore_thread_cancel(pd->connect.thread);
+ pd->connect.thread = NULL;
}
- r = connect(fd, (struct sockaddr *)&addr, addrlen);
- if (r < 0)
+ if (pd->resolve.names)
{
- int errno_bkp = errno;
- ERR("connect(%d, %s): %s", fd, address, strerror(errno));
- close(fd);
- return errno_bkp;
+ freeaddrinfo(pd->resolve.names);
+ pd->resolve.names = NULL;
}
+ pd->resolve.current = NULL;
- efl_loop_fd_set(o, fd);
- efl_net_dialer_connected_set(o, EINA_TRUE);
- return 0;
+ if (pd->resolve.thread)
+ ecore_thread_cancel(pd->resolve.thread);
- invalid_address:
+ pd->resolve.thread = efl_net_resolve_async_new(host, port, &hints, _efl_net_dialer_tcp_resolved, o);
free(str);
- return EINVAL;
+ EINA_SAFETY_ON_NULL_RETURN_VAL(pd->resolve.thread, EINVAL);
+
+ efl_net_dialer_address_dial_set(o, address);
+
+ return 0;
}
EOLIAN static void
}
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_tcp_efl_io_closer_close(Eo *o, Efl_Net_Dialer_Tcp_Data *pd)
{
+ pd->closed = EINA_TRUE;
efl_net_dialer_connected_set(o, EINA_FALSE);
return efl_io_closer_close(efl_super(o, MY_CLASS));
}
status = efl_net_dialer_http_response_status_get(pd->http);
if (status != EFL_NET_HTTP_STATUS_SWITCHING_PROTOCOLS)
{
- Eina_Error err = EFL_NET_HTTP_ERROR_COULDNT_CONNECT;
+ Eina_Error err = EFL_NET_DIALER_ERROR_COULDNT_CONNECT;
if ((status >= 300) && (status < 400))
return;
WRN("failed WebSocket handshake: HTTP status=%d, expected=%d",
if ((!upgraded) || (!connection_websocket) || (!accepted))
{
- Eina_Error err = EFL_NET_HTTP_ERROR_COULDNT_CONNECT;
+ Eina_Error err = EFL_NET_DIALER_ERROR_COULDNT_CONNECT;
WRN("failed WebSocket handshake: upgraded=%d, connection_websocket=%d, accepted=%d",
upgraded, connection_websocket, accepted);
efl_event_callback_call(o, EFL_NET_DIALER_EVENT_ERROR, &err);
var @extern Efl.Net.Http.Error.CHUNK_FAILED: Eina.Error;
var @extern Efl.Net.Http.Error.CONV_FAILED: Eina.Error;
var @extern Efl.Net.Http.Error.CONV_REQD: Eina.Error;
-var @extern Efl.Net.Http.Error.COULDNT_CONNECT: Eina.Error;
-var @extern Efl.Net.Http.Error.COULDNT_RESOLVE_HOST: Eina.Error;
-var @extern Efl.Net.Http.Error.COULDNT_RESOLVE_PROXY: Eina.Error;
var @extern Efl.Net.Http.Error.FAILED_INIT: Eina.Error;
var @extern Efl.Net.Http.Error.FILE_COULDNT_READ_FILE: Eina.Error;
var @extern Efl.Net.Http.Error.FILESIZE_EXCEEDED: Eina.Error;