1 
2 /*
3  * Copyright (C) Igor Sysoev
4  * Copyright (C) NGINX, Inc.
5  */
6 
7 #include <nxt_main.h>
8 
9 
10 static nxt_err_t nxt_conn_connect_test_error(nxt_task_t *task, nxt_conn_t *c);
11 
12 
13 void
nxt_conn_sys_socket(nxt_task_t * task,void * obj,void * data)14 nxt_conn_sys_socket(nxt_task_t *task, void *obj, void *data)
15 {
16     nxt_conn_t          *c;
17     nxt_work_handler_t  handler;
18 
19     c = obj;
20 
21     if (nxt_conn_socket(task, c) == NXT_OK) {
22         c->socket.write_work_queue = c->write_work_queue;
23         handler = c->io->connect;
24 
25     } else {
26         handler = c->write_state->error_handler;
27     }
28 
29     nxt_work_queue_add(&task->thread->engine->connect_work_queue,
30                        handler, task, c, data);
31 }
32 
33 
34 void
nxt_conn_io_connect(nxt_task_t * task,void * obj,void * data)35 nxt_conn_io_connect(nxt_task_t *task, void *obj, void *data)
36 {
37     nxt_conn_t              *c;
38     nxt_work_handler_t      handler;
39     nxt_event_engine_t      *engine;
40     const nxt_conn_state_t  *state;
41 
42     c = obj;
43 
44     state = c->write_state;
45 
46     switch (nxt_socket_connect(task, c->socket.fd, c->remote)) {
47 
48     case NXT_OK:
49         c->socket.write_ready = 1;
50         handler = state->ready_handler;
51         break;
52 
53     case NXT_AGAIN:
54         c->socket.write_handler = nxt_conn_connect_test;
55         c->socket.error_handler = nxt_conn_connect_error;
56 
57         engine = task->thread->engine;
58 
59         nxt_conn_timer(engine, c, state, &c->write_timer);
60 
61         nxt_fd_event_enable_write(engine, &c->socket);
62         return;
63 
64     case NXT_DECLINED:
65         handler = state->close_handler;
66         break;
67 
68     default: /* NXT_ERROR */
69         handler = state->error_handler;
70         break;
71     }
72 
73     nxt_work_queue_add(c->write_work_queue, handler, task, c, data);
74 }
75 
76 
77 nxt_int_t
nxt_conn_socket(nxt_task_t * task,nxt_conn_t * c)78 nxt_conn_socket(nxt_task_t *task, nxt_conn_t *c)
79 {
80     nxt_uint_t    family;
81     nxt_socket_t  s;
82 
83     nxt_debug(task, "event conn socket");
84 
85     family = c->remote->u.sockaddr.sa_family;
86 
87     s = nxt_socket_create(task, family, c->remote->type, 0, NXT_NONBLOCK);
88 
89     if (nxt_slow_path(s == -1)) {
90         return NXT_ERROR;
91     }
92 
93     c->sendfile = 1;
94 
95 #if (NXT_HAVE_UNIX_DOMAIN && NXT_SOLARIS)
96 
97     if (family == AF_UNIX) {
98         /* Solaris AF_UNIX does not support sendfilev(). */
99         c->sendfile = 0;
100     }
101 
102 #endif
103 
104     c->socket.fd = s;
105 
106     c->socket.task = task;
107     c->read_timer.task = task;
108     c->write_timer.task = task;
109 
110     if (c->local != NULL) {
111         if (nxt_slow_path(nxt_socket_bind(task, s, c->local) != NXT_OK)) {
112             nxt_socket_close(task, s);
113             return NXT_ERROR;
114         }
115     }
116 
117     return NXT_OK;
118 }
119 
120 
121 void
nxt_conn_connect_test(nxt_task_t * task,void * obj,void * data)122 nxt_conn_connect_test(nxt_task_t *task, void *obj, void *data)
123 {
124     nxt_err_t   err;
125     nxt_conn_t  *c;
126 
127     c = obj;
128 
129     nxt_debug(task, "event connect test fd:%d", c->socket.fd);
130 
131     nxt_fd_event_block_write(task->thread->engine, &c->socket);
132 
133     if (c->write_state->timer_autoreset) {
134         nxt_timer_disable(task->thread->engine, &c->write_timer);
135     }
136 
137     err = nxt_conn_connect_test_error(task, c);
138 
139     if (err == 0) {
140         nxt_work_queue_add(c->write_work_queue, c->write_state->ready_handler,
141                            task, c, data);
142     } else {
143         nxt_conn_connect_error(task, c, data);
144     }
145 }
146 
147 
148 void
nxt_conn_connect_error(nxt_task_t * task,void * obj,void * data)149 nxt_conn_connect_error(nxt_task_t *task, void *obj, void *data)
150 {
151     nxt_err_t               err;
152     nxt_conn_t              *c;
153     nxt_work_handler_t      handler;
154     const nxt_conn_state_t  *state;
155 
156     c = obj;
157     err = c->socket.error;
158 
159     if (err == 0) {
160         err = nxt_conn_connect_test_error(task, c);
161     }
162 
163     state = c->write_state;
164 
165     switch (err) {
166 
167     case NXT_ECONNREFUSED:
168 #if (NXT_LINUX)
169     case NXT_EAGAIN:
170         /*
171          * Linux returns EAGAIN instead of ECONNREFUSED
172          * for UNIX sockets if a listen queue is full.
173          */
174 #endif
175         handler = state->close_handler;
176         break;
177 
178     default:
179         handler = state->error_handler;
180         break;
181     }
182 
183     nxt_work_queue_add(c->write_work_queue, handler, task, c, data);
184 }
185 
186 
187 static nxt_err_t
nxt_conn_connect_test_error(nxt_task_t * task,nxt_conn_t * c)188 nxt_conn_connect_test_error(nxt_task_t *task, nxt_conn_t *c)
189 {
190     nxt_err_t  err;
191 
192     err = nxt_socket_error(c->socket.fd);
193 
194     if (err != 0) {
195         c->socket.error = err;
196 
197         nxt_log(task, nxt_socket_error_level(err), "connect(%d, %*s) failed %E",
198                 c->socket.fd, (size_t) c->remote->length,
199                 nxt_sockaddr_start(c->remote), err);
200     }
201 
202     return err;
203 }
204