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