xref: /unit/src/nxt_macosx_sendfile.c (revision 0:a63ceefd6ab0)
1 
2 /*
3  * Copyright (C) Igor Sysoev
4  * Copyright (C) NGINX, Inc.
5  */
6 
7 #include <nxt_main.h>
8 
9 
10 /* sendfile() has been introduced in MacOSX 10.5 (Leopard) */
11 
12 #ifdef NXT_TEST_BUILD_MACOSX_SENDFILE
13 
14 ssize_t nxt_macosx_event_conn_io_sendfile(nxt_event_conn_t *c, nxt_buf_t *b,
15     size_t limit);
16 
17 static int nxt_sys_sendfile(int fd, int s, off_t offset, off_t *len,
18     struct sf_hdtr *hdtr, int flags)
19 {
20     return -1;
21 }
22 
23 #else
24 #define nxt_sys_sendfile  sendfile
25 #endif
26 
27 
28 ssize_t
29 nxt_macosx_event_conn_io_sendfile(nxt_event_conn_t *c, nxt_buf_t *b,
30     size_t limit)
31 {
32     size_t                  hd_size, file_size;
33     ssize_t                 n;
34     nxt_buf_t               *fb;
35     nxt_err_t               err;
36     nxt_off_t               sent;
37     nxt_uint_t              nhd, ntr;
38     struct iovec            hd[NXT_IOBUF_MAX], tr[NXT_IOBUF_MAX];
39     struct sf_hdtr          hdtr, *ht;
40     nxt_sendbuf_coalesce_t  sb;
41 
42     sb.buf = b;
43     sb.iobuf = hd;
44     sb.nmax = NXT_IOBUF_MAX;
45     sb.sync = 0;
46     sb.size = 0;
47     sb.limit = limit;
48 
49     nhd = nxt_sendbuf_mem_coalesce(&sb);
50 
51     if (nhd == 0 && sb.sync) {
52         return 0;
53     }
54 
55     if (sb.buf == NULL || !nxt_buf_is_file(sb.buf)) {
56         return nxt_event_conn_io_writev(c, hd, nhd);
57     }
58 
59     hd_size = sb.size;
60     fb = sb.buf;
61 
62     file_size = nxt_sendbuf_file_coalesce(&sb);
63 
64     if (file_size == 0) {
65         return nxt_event_conn_io_writev(c, hd, nhd);
66     }
67 
68     sb.iobuf = tr;
69 
70     ntr = nxt_sendbuf_mem_coalesce(&sb);
71 
72     /*
73      * Disposal of surplus kernel operations if there are no headers
74      * and trailers.  Besides sendfile() returns EINVAL if a sf_hdtr's
75      * count is 0, but corresponding pointer is not NULL.
76      */
77 
78     nxt_memzero(&hdtr, sizeof(struct sf_hdtr));
79     ht = NULL;
80 
81     if (nhd != 0) {
82         ht = &hdtr;
83         hdtr.headers = hd;
84         hdtr.hdr_cnt = nhd;
85     }
86 
87     if (ntr != 0) {
88         ht = &hdtr;
89         hdtr.trailers = tr;
90         hdtr.trl_cnt = ntr;
91     }
92 
93     /*
94      * MacOSX has the same bug as old FreeBSD (http://bugs.freebsd.org/33771).
95      * However this bug has never been fixed and instead of this it has been
96      * documented as a feature in MacOSX 10.7 (Lion) sendfile(2):
97      *
98      *   When a header or trailer is specified, the value of len argument
99      *   indicates the maximum number of bytes in the header and/or file
100      *   to be sent.  It does not control the trailer; if a trailer exists,
101      *   all of it will be sent.
102      */
103     sent = hd_size + file_size;
104 
105     nxt_log_debug(c->socket.log,
106                   "sendfile(%FD, %d, @%O, %O) hd:%ui tr:%ui hs:%uz",
107                   fb->file->fd, c->socket.fd, fb->file_pos, sent,
108                   nhd, ntr, hd_size);
109 
110     n = nxt_sys_sendfile(fb->file->fd, c->socket.fd,
111                          fb->file_pos, &sent, ht, 0);
112 
113     err = (n == -1) ? nxt_errno : 0;
114 
115     nxt_log_debug(c->socket.log, "sendfile(): %d sent:%O", n, sent);
116 
117     if (n == -1) {
118         switch (err) {
119 
120         case NXT_EAGAIN:
121             c->socket.write_ready = 0;
122             break;
123 
124         case NXT_EINTR:
125             break;
126 
127         default:
128             c->socket.error = err;
129             nxt_log_error(nxt_socket_error_level(err, c->socket.log_error),
130                           c->socket.log, "sendfile(%FD, %d, %O, %O) failed "
131                           "%E \"%FN\" hd:%ui tr:%ui", fb->file->fd,
132                           c->socket.fd, fb->file_pos, sent, err,
133                           fb->file->name, nhd, ntr);
134 
135             return NXT_ERROR;
136         }
137 
138         nxt_log_debug(c->socket.log, "sendfile() %E", err);
139 
140         return sent;
141     }
142 
143     if (sent == 0) {
144         nxt_log_error(NXT_LOG_ERR, c->socket.log,
145                       "file \"%FN\" was truncated while sendfile()",
146                       fb->file->name);
147 
148         return NXT_ERROR;
149     }
150 
151     if (sent < (nxt_off_t) sb.size) {
152         c->socket.write_ready = 0;
153     }
154 
155     return sent;
156 }
157 
158 
159 #if 0
160 
161 typedef struct {
162     nxt_socket_t  socket;
163     nxt_err_t     error;
164 
165     uint8_t       write_ready;  /* 1 bit */
166     uint8_t       log_error;
167 } nxt_sendbuf_t;
168 
169 
170 ssize_t nxt_macosx_sendfile(nxt_thread_t *thr, nxt_sendbuf_t *sb, nxt_buf_t *b,
171     size_t limit);
172 ssize_t nxt_writev(nxt_thread_t *thr, nxt_sendbuf_t *sb, nxt_iobuf_t *iob,
173     nxt_uint_t niob);
174 ssize_t nxt_send(nxt_thread_t *thr, nxt_sendbuf_t *sb, void *buf, size_t size);
175 
176 
177 ssize_t
178 nxt_macosx_sendfile(nxt_thread_t *thr, nxt_sendbuf_t *sb, nxt_buf_t *b,
179     size_t limit)
180 {
181     size_t                  hd_size, file_size;
182     ssize_t                 n;
183     nxt_buf_t               *buf;
184     nxt_err_t               err;
185     nxt_off_t               sent;
186     nxt_uint_t              nhd, ntr;
187     struct iovec            hd[NXT_IOBUF_MAX], tr[NXT_IOBUF_MAX];
188     struct sf_hdtr          hdtr, *ht;
189     nxt_sendbuf_coalesce_t  sbc;
190 
191     sbc.buf = b;
192     sbc.iobuf = hd;
193     sbc.nmax = NXT_IOBUF_MAX;
194     sbc.sync = 0;
195     sbc.size = 0;
196     sbc.limit = limit;
197 
198     nhd = nxt_sendbuf_mem_coalesce(&sbc);
199 
200     if (nhd == 0 && sbc.sync) {
201         return 0;
202     }
203 
204     if (sbc.buf == NULL || !nxt_buf_is_file(sbc.buf)) {
205         return nxt_writev(thr, sb, hd, nhd);
206     }
207 
208     hd_size = sbc.size;
209     buf = sbc.buf;
210 
211     file_size = nxt_sendbuf_file_coalesce(&sbc);
212 
213     if (file_size == 0) {
214         return nxt_writev(thr, sb, hd, nhd);
215     }
216 
217     sbc.iobuf = tr;
218 
219     ntr = nxt_sendbuf_mem_coalesce(&sbc);
220 
221     /*
222      * Disposal of surplus kernel operations if there are no headers
223      * and trailers.  Besides sendfile() returns EINVAL if a sf_hdtr's
224      * count is 0, but corresponding pointer is not NULL.
225      */
226 
227     nxt_memzero(&hdtr, sizeof(struct sf_hdtr));
228     ht = NULL;
229 
230     if (nhd != 0) {
231         ht = &hdtr;
232         hdtr.headers = hd;
233         hdtr.hdr_cnt = nhd;
234     }
235 
236     if (ntr != 0) {
237         ht = &hdtr;
238         hdtr.trailers = tr;
239         hdtr.trl_cnt = ntr;
240     }
241 
242     /*
243      * MacOSX has the same bug as old FreeBSD (http://bugs.freebsd.org/33771).
244      * However this bug has never been fixed and instead of this it has been
245      * documented as a feature in MacOSX 10.7 (Lion) sendfile(2):
246      *
247      *   When a header or trailer is specified, the value of len argument
248      *   indicates the maximum number of bytes in the header and/or file
249      *   to be sent.  It does not control the trailer; if a trailer exists,
250      *   all of it will be sent.
251      */
252     sent = hd_size + file_size;
253 
254     nxt_log_debug(thr->log, "sendfile(%FD, %d, @%O, %O) hd:%ui tr:%ui hs:%uz",
255                   buf->file->fd, sb->socket, buf->file_pos, sent,
256                   nhd, ntr, hd_size);
257 
258     n = nxt_sys_sendfile(buf->file->fd, sb->socket,
259                          buf->file_pos, &sent, ht, 0);
260 
261     err = (n == -1) ? nxt_errno : 0;
262 
263     nxt_log_debug(thr->log, "sendfile(): %d sent:%O", n, sent);
264 
265     if (n == -1) {
266         switch (err) {
267 
268         case NXT_EAGAIN:
269             sb->write_ready = 0;
270             break;
271 
272         case NXT_EINTR:
273             break;
274 
275         default:
276             sb->error = err;
277             nxt_log_error(nxt_socket_error_level(err, sb->log_error), thr->log,
278                           "sendfile(%FD, %d, %O, %O) failed %E \"%FN\" "
279                           "hd:%ui tr:%ui", buf->file->fd, sb->socket,
280                           buf->file_pos, sent, err, buf->file->name, nhd, ntr);
281 
282             return NXT_ERROR;
283         }
284 
285         nxt_log_debug(thr->log, "sendfile() %E", err);
286 
287         return sent;
288     }
289 
290     if (sent == 0) {
291         nxt_log_error(NXT_LOG_ERR, thr->log,
292                       "file \"%FN\" was truncated while sendfile()",
293                       buf->file->name);
294 
295         return NXT_ERROR;
296     }
297 
298     if (sent < (nxt_off_t) sbc.size) {
299         sb->write_ready = 0;
300     }
301 
302     return sent;
303 }
304 
305 
306 ssize_t
307 nxt_writev(nxt_thread_t *thr, nxt_sendbuf_t *sb, nxt_iobuf_t *iob,
308     nxt_uint_t niob)
309 {
310     ssize_t    n;
311     nxt_err_t  err;
312 
313     if (niob == 1) {
314         /* Disposal of surplus kernel iovec copy-in operation. */
315         return nxt_send(thr, sb, iob->iov_base, iob->iov_len);
316     }
317 
318     for ( ;; ) {
319         n = writev(sb->socket, iob, niob);
320 
321         err = (n == -1) ? nxt_socket_errno : 0;
322 
323         nxt_log_debug(thr->log, "writev(%d, %ui): %d", sb->socket, niob, n);
324 
325         if (n > 0) {
326             return n;
327         }
328 
329         /* n == -1 */
330 
331         switch (err) {
332 
333         case NXT_EAGAIN:
334             nxt_log_debug(thr->log, "writev() %E", err);
335             sb->write_ready = 0;
336             return NXT_AGAIN;
337 
338         case NXT_EINTR:
339             nxt_log_debug(thr->log, "writev() %E", err);
340             continue;
341 
342         default:
343             sb->error = err;
344             nxt_log_error(nxt_socket_error_level(err, sb->log_error), thr->log,
345                           "writev(%d, %ui) failed %E", sb->socket, niob, err);
346             return NXT_ERROR;
347         }
348     }
349 }
350 
351 
352 ssize_t
353 nxt_send(nxt_thread_t *thr, nxt_sendbuf_t *sb, void *buf, size_t size)
354 {
355     ssize_t    n;
356     nxt_err_t  err;
357 
358     for ( ;; ) {
359         n = send(sb->socket, buf, size, 0);
360 
361         err = (n == -1) ? nxt_socket_errno : 0;
362 
363         nxt_log_debug(thr->log, "send(%d, %p, %uz): %z",
364                       sb->socket, buf, size, n);
365 
366         if (n > 0) {
367             return n;
368         }
369 
370         /* n == -1 */
371 
372         switch (err) {
373 
374         case NXT_EAGAIN:
375             nxt_log_debug(thr->log, "send() %E", err);
376             sb->write_ready = 0;
377             return NXT_AGAIN;
378 
379         case NXT_EINTR:
380             nxt_log_debug(thr->log, "send() %E", err);
381             continue;
382 
383         default:
384             sb->error = err;
385             nxt_log_error(nxt_socket_error_level(err, sb->log_error), thr->log,
386                           "send(%d, %p, %uz) failed %E",
387                           sb->socket, buf, size, err);
388             return NXT_ERROR;
389         }
390     }
391 }
392 
393 #endif
394