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