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