6692
|
1
|
|
2 /*
|
|
3 * Copyright (C) Igor Sysoev
|
|
4 * Copyright (C) Nginx, Inc.
|
|
5 */
|
|
6
|
|
7
|
|
8 #include <ngx_config.h>
|
|
9 #include <ngx_core.h>
|
|
10 #include <ngx_event.h>
|
|
11
|
|
12
|
|
13 static ngx_chain_t *ngx_udp_output_chain_to_iovec(ngx_iovec_t *vec,
|
|
14 ngx_chain_t *in, ngx_log_t *log);
|
|
15 static ssize_t ngx_sendmsg(ngx_connection_t *c, ngx_iovec_t *vec);
|
|
16
|
|
17
|
|
18 ngx_chain_t *
|
|
19 ngx_udp_unix_sendmsg_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit)
|
|
20 {
|
|
21 ssize_t n;
|
|
22 off_t send;
|
|
23 ngx_chain_t *cl;
|
|
24 ngx_event_t *wev;
|
|
25 ngx_iovec_t vec;
|
|
26 struct iovec iovs[NGX_IOVS_PREALLOCATE];
|
|
27
|
|
28 wev = c->write;
|
|
29
|
|
30 if (!wev->ready) {
|
|
31 return in;
|
|
32 }
|
|
33
|
|
34 #if (NGX_HAVE_KQUEUE)
|
|
35
|
|
36 if ((ngx_event_flags & NGX_USE_KQUEUE_EVENT) && wev->pending_eof) {
|
|
37 (void) ngx_connection_error(c, wev->kq_errno,
|
|
38 "kevent() reported about an closed connection");
|
|
39 wev->error = 1;
|
|
40 return NGX_CHAIN_ERROR;
|
|
41 }
|
|
42
|
|
43 #endif
|
|
44
|
|
45 /* the maximum limit size is the maximum size_t value - the page size */
|
|
46
|
|
47 if (limit == 0 || limit > (off_t) (NGX_MAX_SIZE_T_VALUE - ngx_pagesize)) {
|
|
48 limit = NGX_MAX_SIZE_T_VALUE - ngx_pagesize;
|
|
49 }
|
|
50
|
|
51 send = 0;
|
|
52
|
|
53 vec.iovs = iovs;
|
|
54 vec.nalloc = NGX_IOVS_PREALLOCATE;
|
|
55
|
|
56 for ( ;; ) {
|
|
57
|
|
58 /* create the iovec and coalesce the neighbouring bufs */
|
|
59
|
|
60 cl = ngx_udp_output_chain_to_iovec(&vec, in, c->log);
|
|
61
|
|
62 if (cl == NGX_CHAIN_ERROR) {
|
|
63 return NGX_CHAIN_ERROR;
|
|
64 }
|
|
65
|
|
66 if (cl && cl->buf->in_file) {
|
|
67 ngx_log_error(NGX_LOG_ALERT, c->log, 0,
|
|
68 "file buf in sendmsg "
|
|
69 "t:%d r:%d f:%d %p %p-%p %p %O-%O",
|
|
70 cl->buf->temporary,
|
|
71 cl->buf->recycled,
|
|
72 cl->buf->in_file,
|
|
73 cl->buf->start,
|
|
74 cl->buf->pos,
|
|
75 cl->buf->last,
|
|
76 cl->buf->file,
|
|
77 cl->buf->file_pos,
|
|
78 cl->buf->file_last);
|
|
79
|
|
80 ngx_debug_point();
|
|
81
|
|
82 return NGX_CHAIN_ERROR;
|
|
83 }
|
|
84
|
|
85 if (cl == in) {
|
|
86 return in;
|
|
87 }
|
|
88
|
|
89 send += vec.size;
|
|
90
|
|
91 n = ngx_sendmsg(c, &vec);
|
|
92
|
|
93 if (n == NGX_ERROR) {
|
|
94 return NGX_CHAIN_ERROR;
|
|
95 }
|
|
96
|
|
97 if (n == NGX_AGAIN) {
|
|
98 wev->ready = 0;
|
|
99 return in;
|
|
100 }
|
|
101
|
|
102 c->sent += n;
|
|
103
|
|
104 in = ngx_chain_update_sent(in, n);
|
|
105
|
|
106 if (send >= limit || in == NULL) {
|
|
107 return in;
|
|
108 }
|
|
109 }
|
|
110 }
|
|
111
|
|
112
|
|
113 static ngx_chain_t *
|
|
114 ngx_udp_output_chain_to_iovec(ngx_iovec_t *vec, ngx_chain_t *in, ngx_log_t *log)
|
|
115 {
|
|
116 size_t total, size;
|
|
117 u_char *prev;
|
|
118 ngx_uint_t n, flush;
|
|
119 ngx_chain_t *cl;
|
|
120 struct iovec *iov;
|
|
121
|
|
122 cl = in;
|
|
123 iov = NULL;
|
|
124 prev = NULL;
|
|
125 total = 0;
|
|
126 n = 0;
|
|
127 flush = 0;
|
|
128
|
|
129 for ( /* void */ ; in && !flush; in = in->next) {
|
|
130
|
|
131 if (in->buf->flush || in->buf->last_buf) {
|
|
132 flush = 1;
|
|
133 }
|
|
134
|
|
135 if (ngx_buf_special(in->buf)) {
|
|
136 continue;
|
|
137 }
|
|
138
|
|
139 if (in->buf->in_file) {
|
|
140 break;
|
|
141 }
|
|
142
|
|
143 if (!ngx_buf_in_memory(in->buf)) {
|
|
144 ngx_log_error(NGX_LOG_ALERT, log, 0,
|
|
145 "bad buf in output chain "
|
|
146 "t:%d r:%d f:%d %p %p-%p %p %O-%O",
|
|
147 in->buf->temporary,
|
|
148 in->buf->recycled,
|
|
149 in->buf->in_file,
|
|
150 in->buf->start,
|
|
151 in->buf->pos,
|
|
152 in->buf->last,
|
|
153 in->buf->file,
|
|
154 in->buf->file_pos,
|
|
155 in->buf->file_last);
|
|
156
|
|
157 ngx_debug_point();
|
|
158
|
|
159 return NGX_CHAIN_ERROR;
|
|
160 }
|
|
161
|
|
162 size = in->buf->last - in->buf->pos;
|
|
163
|
|
164 if (prev == in->buf->pos) {
|
|
165 iov->iov_len += size;
|
|
166
|
|
167 } else {
|
|
168 if (n == vec->nalloc) {
|
|
169 ngx_log_error(NGX_LOG_ALERT, log, 0,
|
|
170 "too many parts in a datagram");
|
|
171 return NGX_CHAIN_ERROR;
|
|
172 }
|
|
173
|
|
174 iov = &vec->iovs[n++];
|
|
175
|
|
176 iov->iov_base = (void *) in->buf->pos;
|
|
177 iov->iov_len = size;
|
|
178 }
|
|
179
|
|
180 prev = in->buf->pos + size;
|
|
181 total += size;
|
|
182 }
|
|
183
|
|
184 if (!flush) {
|
|
185 #if (NGX_SUPPRESS_WARN)
|
|
186 vec->size = 0;
|
|
187 vec->count = 0;
|
|
188 #endif
|
|
189 return cl;
|
|
190 }
|
|
191
|
|
192 vec->count = n;
|
|
193 vec->size = total;
|
|
194
|
|
195 return in;
|
|
196 }
|
|
197
|
|
198
|
|
199 static ssize_t
|
|
200 ngx_sendmsg(ngx_connection_t *c, ngx_iovec_t *vec)
|
|
201 {
|
|
202 ssize_t n;
|
|
203 ngx_err_t err;
|
|
204 struct msghdr msg;
|
|
205
|
|
206 ngx_memzero(&msg, sizeof(struct msghdr));
|
|
207
|
|
208 if (c->socklen) {
|
|
209 msg.msg_name = c->sockaddr;
|
|
210 msg.msg_namelen = c->socklen;
|
|
211 }
|
|
212
|
|
213 msg.msg_iov = vec->iovs;
|
|
214 msg.msg_iovlen = vec->count;
|
|
215
|
|
216 eintr:
|
|
217
|
|
218 n = sendmsg(c->fd, &msg, 0);
|
|
219
|
|
220 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
|
|
221 "sendmsg: %z of %uz", n, vec->size);
|
|
222
|
|
223 if (n == -1) {
|
|
224 err = ngx_errno;
|
|
225
|
|
226 switch (err) {
|
|
227 case NGX_EAGAIN:
|
|
228 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
|
|
229 "sendmsg() not ready");
|
|
230 return NGX_AGAIN;
|
|
231
|
|
232 case NGX_EINTR:
|
|
233 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
|
|
234 "sendmsg() was interrupted");
|
|
235 goto eintr;
|
|
236
|
|
237 default:
|
|
238 c->write->error = 1;
|
|
239 ngx_connection_error(c, err, "sendmsg() failed");
|
|
240 return NGX_ERROR;
|
|
241 }
|
|
242 }
|
|
243
|
|
244 return n;
|
|
245 }
|