1 #include "connect_handler.h"
5 #include <sys/fcntl.h> /* fcntl */
8 #include <malloc.h> /* calloc */
9 #include <errno.h> /* errno */
12 struct connect_request
{
16 ev_timer time_watcher
;
19 int domain
, type
, protocol
;
20 struct sockaddr
*addr
;
22 char timeout_iterations
;
25 #define DEFAULT_TIMEOUT_ITERATIONS 10
26 #define DEFAULT_TIMEOUT_INTERVAL (0.25)
28 connect_handler_t
*new_connect_handler(core_t
*c
) {
29 connect_handler_t
*h
= calloc(1, sizeof(connect_handler_t
));
34 free_connect_handler(h
);
38 void free_connect_handler(connect_handler_t
*h
) {
43 connect_request_t
*new_connect_request(connect_handler_t
*h
, const struct sockaddr
*addr
, socklen_t addr_len
) {
44 connect_request_t
*cr
= calloc(1, sizeof(connect_request_t
));
46 cr
->addr
= malloc(addr_len
);
47 if(!cr
->addr
) goto fail
;
48 memcpy(cr
->addr
, addr
, addr_len
);
49 cr
->addr_len
= addr_len
;
50 cr
->timeout_iterations
= DEFAULT_TIMEOUT_ITERATIONS
;
53 free_connect_request(cr
);
57 void free_connect_request(connect_request_t
*cr
) {
59 if(cr
->addr
) free(cr
->addr
);
63 void connect_request_set_socket(connect_request_t
*cr
, int fd
) {
67 int connect_request_get_socket(connect_request_t
*cr
) {
71 void connect_request_set_create_params(connect_request_t
*cr
, int domain
, int type
, int protocol
) {
75 cr
->protocol
= protocol
;
78 static void connect_cb(int revents
, void* arg
) {
79 connect_request_t
*cr
= arg
;
81 if(revents
& EV_TIMEOUT
) {
85 if(!(revents
& EV_WRITE
)) /* TODO: error ? */
87 /* Check for connection error */
89 socklen_t errlen
= sizeof(err
);
90 getsockopt(fd
, SOL_SOCKET
, SO_ERROR
, &err
, &errlen
);
92 cr
->request
.on_complete((request_t
*)cr
);
96 cr
->request
.on_error((request_t
*)cr
, IOR_ERROR_ERRNO
);
99 static void setup_connect(struct ev_loop
*loop
, connect_request_t
*cr
) {
102 /* Make sure the socket is non-blocking */
103 if(-1 == (flags
= fcntl(fd
, F_GETFL
, 0)))
105 fcntl(fd
, F_SETFL
, flags
| O_NONBLOCK
);
108 if(0 != connect(cr
->fd
, cr
->addr
, cr
->addr_len
)) {
109 if(errno
== EINTR
) goto tryagain
;
110 if(errno
== EINPROGRESS
) {
111 ev_once(loop
, cr
->fd
, EV_WRITE
, -1., connect_cb
, cr
);
113 cr
->request
.on_error((request_t
*)cr
, IOR_ERROR_ERRNO
);
116 cr
->request
.on_complete((request_t
*)cr
);
120 static void setup_socket_cb(struct ev_loop
*loop
, struct ev_timer
*w
, int revents
);
122 static void setup_socket(struct ev_loop
*loop
, connect_request_t
*cr
, int firstcall
) {
123 cr
->fd
= socket(cr
->domain
, cr
->type
, cr
->protocol
);
125 if(errno
== ENFILE
) { /* no files remaining - should retry */
126 if(!firstcall
) return;
127 ev_timer_init(&cr
->time_watcher
, setup_socket_cb
, 0., DEFAULT_TIMEOUT_INTERVAL
);
128 cr
->time_watcher
.data
= cr
;
129 ev_timer_start(loop
, &cr
->time_watcher
);
130 } else { /* another error occurred... */
132 ev_timer_stop(loop
, &cr
->time_watcher
);
133 cr
->request
.on_error((request_t
*)cr
, IOR_ERROR_ERRNO
);
137 /* begin connection process.. */
139 ev_timer_stop(loop
, &cr
->time_watcher
);
140 setup_connect(loop
, cr
);
143 static void setup_socket_cb(struct ev_loop
*loop
, struct ev_timer
*w
, int revents
) {
144 connect_request_t
*cr
= w
->data
;
145 cr
->timeout_iterations
--;
146 if(cr
->timeout_iterations
<= 0) {
147 ev_timer_stop(loop
, w
);
149 setup_socket(loop
, cr
, 0);
152 void connect_handler_queue(connect_handler_t
*h
, connect_request_t
*cr
) {
154 setup_socket(h
->core
->loop
, cr
, 1);
157 setup_connect(h
->core
->loop
, cr
);