diff --git a/src/lib/ecore_con/Ecore_Con_Eo.h b/src/lib/ecore_con/Ecore_Con_Eo.h index 6028d600fb..f10d1d7f27 100644 --- a/src/lib/ecore_con/Ecore_Con_Eo.h +++ b/src/lib/ecore_con/Ecore_Con_Eo.h @@ -16,6 +16,12 @@ #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; @@ -23,9 +29,6 @@ extern Eina_Error EFL_NET_HTTP_ERROR_BAD_FUNCTION_ARGUMENT; 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; diff --git a/src/lib/ecore_con/ecore_con.c b/src/lib/ecore_con/ecore_con.c index 161434a8c8..63f2c8e614 100644 --- a/src/lib/ecore_con/ecore_con.c +++ b/src/lib/ecore_con/ecore_con.c @@ -175,6 +175,10 @@ EAPI int ECORE_CON_EVENT_CLIENT_ERROR = 0; 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; @@ -215,6 +219,10 @@ ecore_con_init(void) 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"); @@ -3037,6 +3045,46 @@ efl_net_ip_port_fmt(char *buf, int buflen, const struct sockaddr *addr) 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) { @@ -3065,3 +3113,217 @@ 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); +} diff --git a/src/lib/ecore_con/ecore_con_private.h b/src/lib/ecore_con/ecore_con_private.h index 69102d7563..94493b0df7 100644 --- a/src/lib/ecore_con/ecore_con_private.h +++ b/src/lib/ecore_con/ecore_con_private.h @@ -375,8 +375,70 @@ void ecore_con_mempool_shutdown(void); 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) { diff --git a/src/lib/ecore_con/ecore_con_url_curl.c b/src/lib/ecore_con/ecore_con_url_curl.c index e28326e387..1cb241b7f9 100644 --- a/src/lib/ecore_con/ecore_con_url_curl.c +++ b/src/lib/ecore_con/ecore_con_url_curl.c @@ -32,9 +32,6 @@ EAPI Eina_Error EFL_NET_HTTP_ERROR_BAD_FUNCTION_ARGUMENT = 0; 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; @@ -90,6 +87,10 @@ _curlcode_to_eina_error(const CURLcode code) 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); @@ -98,9 +99,6 @@ _curlcode_to_eina_error(const CURLcode code) _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); @@ -192,9 +190,6 @@ _c_init_errors(void) _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); diff --git a/src/lib/ecore_con/efl_net_dialer.eo b/src/lib/ecore_con/efl_net_dialer.eo index d2766f5f0f..74b56822fd 100644 --- a/src/lib/ecore_con/efl_net_dialer.eo +++ b/src/lib/ecore_con/efl_net_dialer.eo @@ -1,3 +1,7 @@ +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. diff --git a/src/lib/ecore_con/efl_net_dialer_tcp.c b/src/lib/ecore_con/efl_net_dialer_tcp.c index 88f78fe971..002f3c6293 100644 --- a/src/lib/ecore_con/efl_net_dialer_tcp.c +++ b/src/lib/ecore_con/efl_net_dialer_tcp.c @@ -31,117 +31,203 @@ 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); } +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) +{ + Eo *o = data; + Efl_Net_Dialer_Tcp_Data *pd = efl_data_scope_get(o, MY_CLASS); + const struct addrinfo *addrinfo; + + pd->connect.thread = NULL; + + 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 (!pd->resolve.current) return; + + pd->resolve.current = pd->resolve.current->ai_next; + if (!pd->resolve.current) + { + freeaddrinfo(pd->resolve.names); + pd->resolve.names = NULL; + if (err) + efl_event_callback_call(o, EFL_NET_DIALER_EVENT_ERROR, &err); + else + { + err = EFL_NET_DIALER_ERROR_COULDNT_CONNECT; + efl_event_callback_call(o, EFL_NET_DIALER_EVENT_ERROR, &err); + } + return; + } + + 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)) + { + efl_net_socket_address_remote_set(o, buf); + efl_event_callback_call(o, EFL_NET_DIALER_EVENT_RESOLVED, NULL); + } + + 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) + { + 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; + } + + 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) { - struct sockaddr_storage addr = {}; - char *str, *host, *port; - int r, fd; - socklen_t addrlen; - char buf[INET6_ADDRSTRLEN + sizeof("[]:65536")]; + 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); - // TODO: change to getaddrinfo() and move to a thread... - str = host = strdup(address); + str = strdup(address); EINA_SAFETY_ON_NULL_RETURN_VAL(str, ENOMEM); - if (host[0] == '[') + if (!efl_net_ip_port_split(str, &host, &port)) { - 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); + ERR("could not parse IP:PORT '%s'", address); + free(str); + return EINVAL; } - else + if (strchr(host, ':')) hints.ai_family = AF_INET6; + if (!port) port = "0"; + + if (pd->connect.thread) { - 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); + ecore_thread_cancel(pd->connect.thread); + pd->connect.thread = NULL; } - if (r != 1) + if (pd->resolve.names) { - ERR("could not parse IP '%s' (%s)", host, address); - goto invalid_address; + freeaddrinfo(pd->resolve.names); + pd->resolve.names = NULL; } + pd->resolve.current = NULL; + + if (pd->resolve.thread) + ecore_thread_cancel(pd->resolve.thread); + + pd->resolve.thread = efl_net_resolve_async_new(host, port, &hints, _efl_net_dialer_tcp_resolved, o); free(str); + EINA_SAFETY_ON_NULL_RETURN_VAL(pd->resolve.thread, EINVAL); - 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); - } - - fd = efl_net_socket4(addr.ss_family, SOCK_STREAM, IPPROTO_TCP, efl_net_socket_fd_close_on_exec_get(o)); - if (fd < 0) - { - ERR("socket(%d, SOCK_STREAM, IPPROTO_TCP): %s", - addr.ss_family, 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 @@ -197,8 +283,9 @@ _efl_net_dialer_tcp_efl_net_dialer_connected_get(Eo *o EINA_UNUSED, Efl_Net_Dial } 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)); } diff --git a/src/lib/ecore_con/efl_net_dialer_websocket.c b/src/lib/ecore_con/efl_net_dialer_websocket.c index cec03e8b1e..8364079d03 100644 --- a/src/lib/ecore_con/efl_net_dialer_websocket.c +++ b/src/lib/ecore_con/efl_net_dialer_websocket.c @@ -831,7 +831,7 @@ _efl_net_dialer_websocket_http_headers_done(void *data, const Efl_Event *event E 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", @@ -871,7 +871,7 @@ _efl_net_dialer_websocket_http_headers_done(void *data, const Efl_Event *event E 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); diff --git a/src/lib/ecore_con/efl_net_http_types.eot b/src/lib/ecore_con/efl_net_http_types.eot index 6fb6911dec..bd470128f1 100644 --- a/src/lib/ecore_con/efl_net_http_types.eot +++ b/src/lib/ecore_con/efl_net_http_types.eot @@ -126,9 +126,6 @@ var @extern Efl.Net.Http.Error.BAD_FUNCTION_ARGUMENT: Eina.Error; 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;