xref: /unit/src/nxt_macosx_sendfile.c (revision 62:5e1efcc7b740)
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 
nxt_sys_sendfile(int fd,int s,off_t offset,off_t * len,struct sf_hdtr * hdtr,int flags)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
nxt_macosx_event_conn_io_sendfile(nxt_conn_t * c,nxt_buf_t * b,size_t limit)29 nxt_macosx_event_conn_io_sendfile(nxt_conn_t *c, nxt_buf_t *b, size_t limit)
30 {
31     size_t                  hd_size, file_size;
32     ssize_t                 n;
33     nxt_buf_t               *fb;
34     nxt_err_t               err;
35     nxt_off_t               sent;
36     nxt_uint_t              nhd, ntr;
37     struct iovec            hd[NXT_IOBUF_MAX], tr[NXT_IOBUF_MAX];
38     struct sf_hdtr          hdtr, *ht;
39     nxt_sendbuf_coalesce_t  sb;
40 
41     sb.buf = b;
42     sb.iobuf = hd;
43     sb.nmax = NXT_IOBUF_MAX;
44     sb.sync = 0;
45     sb.size = 0;
46     sb.limit = limit;
47 
48     nhd = nxt_sendbuf_mem_coalesce(c->socket.task, &sb);
49 
50     if (nhd == 0 && sb.sync) {
51         return 0;
52     }
53 
54     if (sb.buf == NULL || !nxt_buf_is_file(sb.buf)) {
55         return nxt_event_conn_io_writev(c, hd, nhd);
56     }
57 
58     hd_size = sb.size;
59     fb = sb.buf;
60 
61     file_size = nxt_sendbuf_file_coalesce(&sb);
62 
63     if (file_size == 0) {
64         return nxt_event_conn_io_writev(c, hd, nhd);
65     }
66 
67     sb.iobuf = tr;
68 
69     ntr = nxt_sendbuf_mem_coalesce(c->socket.task, &sb);
70 
71     /*
72      * Disposal of surplus kernel operations if there are no headers
73      * and trailers.  Besides sendfile() returns EINVAL if a sf_hdtr's
74      * count is 0, but corresponding pointer is not NULL.
75      */
76 
77     nxt_memzero(&hdtr, sizeof(struct sf_hdtr));
78     ht = NULL;
79 
80     if (nhd != 0) {
81         ht = &hdtr;
82         hdtr.headers = hd;
83         hdtr.hdr_cnt = nhd;
84     }
85 
86     if (ntr != 0) {
87         ht = &hdtr;
88         hdtr.trailers = tr;
89         hdtr.trl_cnt = ntr;
90     }
91 
92     /*
93      * MacOSX has the same bug as old FreeBSD (http://bugs.freebsd.org/33771).
94      * However this bug has never been fixed and instead of this it has been
95      * documented as a feature in MacOSX 10.7 (Lion) sendfile(2):
96      *
97      *   When a header or trailer is specified, the value of len argument
98      *   indicates the maximum number of bytes in the header and/or file
99      *   to be sent.  It does not control the trailer; if a trailer exists,
100      *   all of it will be sent.
101      */
102     sent = hd_size + file_size;
103 
104     nxt_log_debug(c->socket.log,
105                   "sendfile(%FD, %d, @%O, %O) hd:%ui tr:%ui hs:%uz",
106                   fb->file->fd, c->socket.fd, fb->file_pos, sent,
107                   nhd, ntr, hd_size);
108 
109     n = nxt_sys_sendfile(fb->file->fd, c->socket.fd,
110                          fb->file_pos, &sent, ht, 0);
111 
112     err = (n == -1) ? nxt_errno : 0;
113 
114     nxt_debug(c->socket.task, "sendfile(): %d sent:%O", n, sent);
115 
116     if (n == -1) {
117         switch (err) {
118 
119         case NXT_EAGAIN:
120             c->socket.write_ready = 0;
121             break;
122 
123         case NXT_EINTR:
124             break;
125 
126         default:
127             c->socket.error = err;
128             nxt_log(c->socket.task, nxt_socket_error_level(err),
129                     "sendfile(%FD, %d, %O, %O) failed %E \"%FN\" hd:%ui tr:%ui",
130                     fb->file->fd, c->socket.fd, fb->file_pos, sent, err,
131                     fb->file->name, nhd, ntr);
132 
133             return NXT_ERROR;
134         }
135 
136         nxt_debug(c->socket.task, "sendfile() %E", err);
137 
138         return sent;
139     }
140 
141     if (sent == 0) {
142         nxt_log(c->socket.task, NXT_LOG_ERR,
143                 "file \"%FN\" was truncated while sendfile()", fb->file->name);
144 
145         return NXT_ERROR;
146     }
147 
148     if (sent < (nxt_off_t) sb.size) {
149         c->socket.write_ready = 0;
150     }
151 
152     return sent;
153 }
154