nxt_macosx_sendfile.c (13:3a52b2c3d3f1) nxt_macosx_sendfile.c (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
14ssize_t nxt_macosx_event_conn_io_sendfile(nxt_event_conn_t *c, nxt_buf_t *b,
15 size_t limit);
16
17static 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
28ssize_t
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
14ssize_t nxt_macosx_event_conn_io_sendfile(nxt_event_conn_t *c, nxt_buf_t *b,
15 size_t limit);
16
17static 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
28ssize_t
29nxt_macosx_event_conn_io_sendfile(nxt_event_conn_t *c, nxt_buf_t *b,
30 size_t limit)
29nxt_macosx_event_conn_io_sendfile(nxt_conn_t *c, nxt_buf_t *b, 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(c->socket.task, &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(c->socket.task, &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_debug(c->socket.task, "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(c->socket.task, nxt_socket_error_level(err),
130 "sendfile(%FD, %d, %O, %O) failed %E \"%FN\" hd:%ui tr:%ui",
131 fb->file->fd, c->socket.fd, fb->file_pos, sent, err,
132 fb->file->name, nhd, ntr);
133
134 return NXT_ERROR;
135 }
136
137 nxt_debug(c->socket.task, "sendfile() %E", err);
138
139 return sent;
140 }
141
142 if (sent == 0) {
143 nxt_log(c->socket.task, NXT_LOG_ERR,
144 "file \"%FN\" was truncated while sendfile()", fb->file->name);
145
146 return NXT_ERROR;
147 }
148
149 if (sent < (nxt_off_t) sb.size) {
150 c->socket.write_ready = 0;
151 }
152
153 return sent;
154}
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}