blob: 63c877f7d8aec282697c80675681c25a5e6569ab [file] [log] [blame]
Igor Sysoevff148df2003-02-26 20:21:43 +00001
2#include <ngx_config.h>
Igor Sysoevff148df2003-02-26 20:21:43 +00003#include <ngx_core.h>
Igor Sysoev1342d9c2003-10-09 07:00:45 +00004#include <ngx_event.h>
Igor Sysoevff148df2003-02-26 20:21:43 +00005
6
Igor Sysoev6253ca12003-05-27 12:18:54 +00007/*
Igor Sysoev865c1502003-11-30 20:03:18 +00008 * FreeBSD's sendfile() often sends 4K pages over ethernet in 3 packets: 2x1460
9 * and 1176 or in 6 packets: 5x1460 and 892. Besides although sendfile()
10 * allows to pass the header and the trailer it never sends the header or
11 * the trailer with the part of the file in one packet. So we use TCP_NOPUSH
12 * (similar to Linux's TCP_CORK) to postpone the sending - it not only sends
13 * the header and the first part of the file in one packet but also sends
14 * 4K pages in the full packets.
Igor Sysoev1342d9c2003-10-09 07:00:45 +000015 *
Igor Sysoev865c1502003-11-30 20:03:18 +000016 * Until FreeBSD 4.5 the turning TCP_NOPUSH off does not flush a pending
17 * data that less than MSS so that data can be sent with 5 second delay.
Igor Sysoevb3e73d82003-10-10 15:10:50 +000018 * We do not use TCP_NOPUSH on FreeBSD prior to 4.5 although it can be used
19 * for non-keepalive HTTP connections.
Igor Sysoev1342d9c2003-10-09 07:00:45 +000020 */
Igor Sysoev6253ca12003-05-27 12:18:54 +000021
22
Igor Sysoev6a1cc902003-05-22 15:23:47 +000023ngx_chain_t *ngx_freebsd_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in)
Igor Sysoevff148df2003-02-26 20:21:43 +000024{
Igor Sysoeva8fa0a62003-11-25 20:44:56 +000025 int rc;
Igor Sysoevff148df2003-02-26 20:21:43 +000026 char *prev;
Igor Sysoev9760a132003-10-21 07:47:21 +000027 off_t sent, fprev;
Igor Sysoevd9d0ca12003-11-21 06:30:49 +000028 size_t hsize, fsize, size;
Igor Sysoev3b30a902003-12-25 20:26:58 +000029 ngx_int_t eintr, eagain, level;
Igor Sysoevff148df2003-02-26 20:21:43 +000030 struct iovec *iov;
31 struct sf_hdtr hdtr;
32 ngx_err_t err;
Igor Sysoeva8fa0a62003-11-25 20:44:56 +000033 ngx_hunk_t *file;
Igor Sysoevff148df2003-02-26 20:21:43 +000034 ngx_array_t header, trailer;
Igor Sysoev65977492003-11-02 22:56:18 +000035 ngx_event_t *wev;
Igor Sysoevdc9dd432003-10-22 16:38:26 +000036 ngx_chain_t *cl, *tail;
Igor Sysoevff148df2003-02-26 20:21:43 +000037
Igor Sysoev65977492003-11-02 22:56:18 +000038 wev = c->write;
39
40 if (!wev->ready) {
Igor Sysoev0a280a32003-10-12 16:49:16 +000041 return in;
42 }
43
Igor Sysoev65977492003-11-02 22:56:18 +000044#if (HAVE_KQUEUE)
45
46 if ((ngx_event_flags & NGX_HAVE_KQUEUE_EVENT) && wev->kq_eof) {
Igor Sysoev669e3312003-12-22 09:40:48 +000047 ngx_log_error(NGX_LOG_INFO, c->log, wev->kq_errno,
Igor Sysoev65977492003-11-02 22:56:18 +000048 "kevent() reported about closed connection");
49
50 wev->error = 1;
51 return NGX_CHAIN_ERROR;
52 }
53
54#endif
55
Igor Sysoev6253ca12003-05-27 12:18:54 +000056 do {
Igor Sysoev6253ca12003-05-27 12:18:54 +000057 file = NULL;
Igor Sysoev9760a132003-10-21 07:47:21 +000058 fsize = 0;
Igor Sysoev6253ca12003-05-27 12:18:54 +000059 hsize = 0;
60 eintr = 0;
Igor Sysoev9760a132003-10-21 07:47:21 +000061 eagain = 0;
Igor Sysoev3b30a902003-12-25 20:26:58 +000062 level = NGX_LOG_CRIT;
Igor Sysoevff148df2003-02-26 20:21:43 +000063
Igor Sysoev6253ca12003-05-27 12:18:54 +000064 ngx_init_array(header, c->pool, 10, sizeof(struct iovec),
65 NGX_CHAIN_ERROR);
66 ngx_init_array(trailer, c->pool, 10, sizeof(struct iovec),
67 NGX_CHAIN_ERROR);
Igor Sysoevff148df2003-02-26 20:21:43 +000068
Igor Sysoevdc9dd432003-10-22 16:38:26 +000069 /* create the header iovec and coalesce the neighbouring hunks */
Igor Sysoev1342d9c2003-10-09 07:00:45 +000070
Igor Sysoevb3e73d82003-10-10 15:10:50 +000071 prev = NULL;
72 iov = NULL;
Igor Sysoevb7387572003-03-11 20:38:13 +000073
Igor Sysoevdc9dd432003-10-22 16:38:26 +000074 for (cl = in; cl; cl = cl->next) {
75 if (ngx_hunk_special(cl->hunk)) {
Igor Sysoevb3e73d82003-10-10 15:10:50 +000076 continue;
Igor Sysoev6253ca12003-05-27 12:18:54 +000077 }
Igor Sysoevb3e73d82003-10-10 15:10:50 +000078
Igor Sysoevdc9dd432003-10-22 16:38:26 +000079 if (!ngx_hunk_in_memory_only(cl->hunk)) {
Igor Sysoevb3e73d82003-10-10 15:10:50 +000080 break;
81 }
82
Igor Sysoevdc9dd432003-10-22 16:38:26 +000083 if (prev == cl->hunk->pos) {
84 iov->iov_len += cl->hunk->last - cl->hunk->pos;
Igor Sysoevb3e73d82003-10-10 15:10:50 +000085
86 } else {
87 ngx_test_null(iov, ngx_push_array(&header), NGX_CHAIN_ERROR);
Igor Sysoevdc9dd432003-10-22 16:38:26 +000088 iov->iov_base = cl->hunk->pos;
89 iov->iov_len = cl->hunk->last - cl->hunk->pos;
Igor Sysoevb3e73d82003-10-10 15:10:50 +000090 }
91
Igor Sysoevab0c4f52003-10-28 15:45:41 +000092 prev = cl->hunk->last;
Igor Sysoevdc9dd432003-10-22 16:38:26 +000093 hsize += cl->hunk->last - cl->hunk->pos;
Igor Sysoev6253ca12003-05-27 12:18:54 +000094 }
95
Igor Sysoev9760a132003-10-21 07:47:21 +000096 /* get the file hunk */
Igor Sysoev1342d9c2003-10-09 07:00:45 +000097
Igor Sysoevdc9dd432003-10-22 16:38:26 +000098 if (cl && (cl->hunk->type & NGX_HUNK_FILE)) {
99 file = cl->hunk;
Igor Sysoev9760a132003-10-21 07:47:21 +0000100 fsize = (size_t) (file->file_last - file->file_pos);
101 fprev = file->file_last;
Igor Sysoevdc9dd432003-10-22 16:38:26 +0000102 cl = cl->next;
Igor Sysoev9760a132003-10-21 07:47:21 +0000103
104 /* coalesce the neighbouring file hunks */
105
Igor Sysoevdc9dd432003-10-22 16:38:26 +0000106 while (cl && (cl->hunk->type & NGX_HUNK_FILE)) {
107 if (file->file->fd != cl->hunk->file->fd
108 || fprev != cl->hunk->file_pos)
Igor Sysoev9760a132003-10-21 07:47:21 +0000109 {
110 break;
111 }
112
Igor Sysoevdc9dd432003-10-22 16:38:26 +0000113 fsize += (size_t) (cl->hunk->file_last - cl->hunk->file_pos);
114 fprev = cl->hunk->file_last;
115 cl = cl->next;
Igor Sysoev9760a132003-10-21 07:47:21 +0000116 }
Igor Sysoevff148df2003-02-26 20:21:43 +0000117 }
Igor Sysoevff148df2003-02-26 20:21:43 +0000118
Igor Sysoevdc9dd432003-10-22 16:38:26 +0000119 /* create the tailer iovec and coalesce the neighbouring hunks */
Igor Sysoev1342d9c2003-10-09 07:00:45 +0000120
Igor Sysoevb3e73d82003-10-10 15:10:50 +0000121 prev = NULL;
122 iov = NULL;
Igor Sysoevff148df2003-02-26 20:21:43 +0000123
Igor Sysoevdc9dd432003-10-22 16:38:26 +0000124 for ( /* void */; cl; cl = cl->next) {
125 if (ngx_hunk_special(cl->hunk)) {
Igor Sysoevb3e73d82003-10-10 15:10:50 +0000126 continue;
Igor Sysoevff148df2003-02-26 20:21:43 +0000127 }
Igor Sysoevb3e73d82003-10-10 15:10:50 +0000128
Igor Sysoevdc9dd432003-10-22 16:38:26 +0000129 if (!ngx_hunk_in_memory_only(cl->hunk)) {
Igor Sysoevb3e73d82003-10-10 15:10:50 +0000130 break;
131 }
132
Igor Sysoevdc9dd432003-10-22 16:38:26 +0000133 if (prev == cl->hunk->pos) {
134 iov->iov_len += cl->hunk->last - cl->hunk->pos;
Igor Sysoevb3e73d82003-10-10 15:10:50 +0000135
136 } else {
137 ngx_test_null(iov, ngx_push_array(&trailer), NGX_CHAIN_ERROR);
Igor Sysoevdc9dd432003-10-22 16:38:26 +0000138 iov->iov_base = cl->hunk->pos;
139 iov->iov_len = cl->hunk->last - cl->hunk->pos;
Igor Sysoevb3e73d82003-10-10 15:10:50 +0000140 }
Igor Sysoevab0c4f52003-10-28 15:45:41 +0000141
142 prev = cl->hunk->last;
Igor Sysoevff148df2003-02-26 20:21:43 +0000143 }
144
Igor Sysoev9760a132003-10-21 07:47:21 +0000145 /*
146 * the tail is the rest of the chain that exceeded
147 * a single sendfile() capability
148 */
149
Igor Sysoevdc9dd432003-10-22 16:38:26 +0000150 tail = cl;
Igor Sysoev6253ca12003-05-27 12:18:54 +0000151
152 if (file) {
153
Igor Sysoevb3e73d82003-10-10 15:10:50 +0000154 if (ngx_freebsd_use_tcp_nopush && !c->tcp_nopush) {
Igor Sysoev7578ec92003-06-02 15:24:30 +0000155 c->tcp_nopush = 1;
Igor Sysoevb3e73d82003-10-10 15:10:50 +0000156
Igor Sysoev669e3312003-12-22 09:40:48 +0000157 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "tcp_nopush");
Igor Sysoevb3e73d82003-10-10 15:10:50 +0000158
Igor Sysoev9cf78302003-06-04 17:28:33 +0000159 if (ngx_tcp_nopush(c->fd) == NGX_ERROR) {
Igor Sysoeva8fa0a62003-11-25 20:44:56 +0000160 ngx_log_error(NGX_LOG_CRIT, c->log, ngx_errno,
Igor Sysoev9cf78302003-06-04 17:28:33 +0000161 ngx_tcp_nopush_n " failed");
Igor Sysoev6253ca12003-05-27 12:18:54 +0000162 return NGX_CHAIN_ERROR;
163 }
164 }
165
166 hdtr.headers = (struct iovec *) header.elts;
167 hdtr.hdr_cnt = header.nelts;
168 hdtr.trailers = (struct iovec *) trailer.elts;
169 hdtr.trl_cnt = trailer.nelts;
170
Igor Sysoev419f9ac2003-10-21 16:49:56 +0000171 /*
Igor Sysoevdc9dd432003-10-22 16:38:26 +0000172 * the "nbytes bug" of the old sendfile() syscall:
Igor Sysoev419f9ac2003-10-21 16:49:56 +0000173 * http://www.freebsd.org/cgi/query-pr.cgi?pr=33771
174 */
175
Igor Sysoev6253ca12003-05-27 12:18:54 +0000176 if (ngx_freebsd_sendfile_nbytes_bug == 0) {
177 hsize = 0;
178 }
179
Igor Sysoeva7dcbaf2003-12-02 05:47:29 +0000180 sent = 0;
181
Igor Sysoev6253ca12003-05-27 12:18:54 +0000182 rc = sendfile(file->file->fd, c->fd, file->file_pos,
Igor Sysoev9760a132003-10-21 07:47:21 +0000183 fsize + hsize, &hdtr, &sent, 0);
Igor Sysoev6253ca12003-05-27 12:18:54 +0000184
185 if (rc == -1) {
186 err = ngx_errno;
187
188 if (err == NGX_EINTR) {
189 eintr = 1;
Igor Sysoev6253ca12003-05-27 12:18:54 +0000190
Igor Sysoev3b30a902003-12-25 20:26:58 +0000191 } else if (err == NGX_EAGAIN) {
Igor Sysoev9760a132003-10-21 07:47:21 +0000192 eagain = 1;
Igor Sysoev3b30a902003-12-25 20:26:58 +0000193
194 } else if (err == NGX_EPIPE) {
195 level = NGX_LOG_INFO;
Igor Sysoev9760a132003-10-21 07:47:21 +0000196 }
197
Igor Sysoev6253ca12003-05-27 12:18:54 +0000198 if (err == NGX_EAGAIN || err == NGX_EINTR) {
Igor Sysoev669e3312003-12-22 09:40:48 +0000199 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, err,
200 "sendfile() sent only " OFF_T_FMT " bytes",
201 sent);
Igor Sysoev6253ca12003-05-27 12:18:54 +0000202
203 } else {
Igor Sysoev65977492003-11-02 22:56:18 +0000204 wev->error = 1;
Igor Sysoev3b30a902003-12-25 20:26:58 +0000205 ngx_log_error(level, c->log, err,
Igor Sysoev6253ca12003-05-27 12:18:54 +0000206 "sendfile() failed");
207 return NGX_CHAIN_ERROR;
208 }
209 }
210
Igor Sysoev669e3312003-12-22 09:40:48 +0000211 ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
212 "sendfile: %d, @" OFF_T_FMT " " OFF_T_FMT ":%d",
213 rc, file->file_pos, sent, fsize + hsize);
Igor Sysoevff148df2003-02-26 20:21:43 +0000214
Igor Sysoevb7387572003-03-11 20:38:13 +0000215 } else {
Igor Sysoev65977492003-11-02 22:56:18 +0000216 rc = writev(c->fd, header.elts, header.nelts);
Igor Sysoev6253ca12003-05-27 12:18:54 +0000217
Igor Sysoevb3e73d82003-10-10 15:10:50 +0000218 if (rc == -1) {
219 err = ngx_errno;
Igor Sysoev6253ca12003-05-27 12:18:54 +0000220
Igor Sysoev669e3312003-12-22 09:40:48 +0000221 if (err == NGX_EINTR) {
Igor Sysoevb3e73d82003-10-10 15:10:50 +0000222 eintr = 1;
Igor Sysoev3b30a902003-12-25 20:26:58 +0000223
224 } else if (err == NGX_EPIPE) {
225 level = NGX_LOG_INFO;
Igor Sysoev669e3312003-12-22 09:40:48 +0000226 }
227
228 if (err == NGX_EAGAIN || err == NGX_EINTR) {
229 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
230 "writev() not ready");
Igor Sysoev6253ca12003-05-27 12:18:54 +0000231
Igor Sysoevb3e73d82003-10-10 15:10:50 +0000232 } else {
Igor Sysoev65977492003-11-02 22:56:18 +0000233 wev->error = 1;
Igor Sysoev3b30a902003-12-25 20:26:58 +0000234 ngx_log_error(level, c->log, err, "writev() failed");
Igor Sysoevb3e73d82003-10-10 15:10:50 +0000235 return NGX_CHAIN_ERROR;
Igor Sysoev6253ca12003-05-27 12:18:54 +0000236 }
Igor Sysoevb3e73d82003-10-10 15:10:50 +0000237 }
Igor Sysoev6253ca12003-05-27 12:18:54 +0000238
Igor Sysoevb3e73d82003-10-10 15:10:50 +0000239 sent = rc > 0 ? rc : 0;
Igor Sysoev6253ca12003-05-27 12:18:54 +0000240
Igor Sysoev669e3312003-12-22 09:40:48 +0000241 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
242 "writev: " OFF_T_FMT, sent);
Igor Sysoevb7387572003-03-11 20:38:13 +0000243 }
Igor Sysoevff148df2003-02-26 20:21:43 +0000244
Igor Sysoev6253ca12003-05-27 12:18:54 +0000245 c->sent += sent;
246
Igor Sysoevdc9dd432003-10-22 16:38:26 +0000247 for (cl = in; cl; cl = cl->next) {
Igor Sysoevff148df2003-02-26 20:21:43 +0000248
Igor Sysoevdc9dd432003-10-22 16:38:26 +0000249 if (ngx_hunk_special(cl->hunk)) {
Igor Sysoev9760a132003-10-21 07:47:21 +0000250 continue;
Igor Sysoev6253ca12003-05-27 12:18:54 +0000251 }
252
Igor Sysoev9760a132003-10-21 07:47:21 +0000253 if (sent == 0) {
254 break;
255 }
256
Igor Sysoevdc9dd432003-10-22 16:38:26 +0000257 size = ngx_hunk_size(cl->hunk);
Igor Sysoev9760a132003-10-21 07:47:21 +0000258
Igor Sysoev6253ca12003-05-27 12:18:54 +0000259 if (sent >= size) {
260 sent -= size;
261
Igor Sysoevdc9dd432003-10-22 16:38:26 +0000262 if (cl->hunk->type & NGX_HUNK_IN_MEMORY) {
263 cl->hunk->pos = cl->hunk->last;
Igor Sysoev6253ca12003-05-27 12:18:54 +0000264 }
265
Igor Sysoevdc9dd432003-10-22 16:38:26 +0000266 if (cl->hunk->type & NGX_HUNK_FILE) {
267 cl->hunk->file_pos = cl->hunk->file_last;
Igor Sysoev6253ca12003-05-27 12:18:54 +0000268 }
269
270 continue;
271 }
272
Igor Sysoevdc9dd432003-10-22 16:38:26 +0000273 if (cl->hunk->type & NGX_HUNK_IN_MEMORY) {
274 cl->hunk->pos += sent;
Igor Sysoevb7387572003-03-11 20:38:13 +0000275 }
276
Igor Sysoevdc9dd432003-10-22 16:38:26 +0000277 if (cl->hunk->type & NGX_HUNK_FILE) {
278 cl->hunk->file_pos += sent;
Igor Sysoevb7387572003-03-11 20:38:13 +0000279 }
280
Igor Sysoev6253ca12003-05-27 12:18:54 +0000281 break;
Igor Sysoevff148df2003-02-26 20:21:43 +0000282 }
283
Igor Sysoevdc9dd432003-10-22 16:38:26 +0000284 in = cl;
Igor Sysoevff148df2003-02-26 20:21:43 +0000285
Igor Sysoev9760a132003-10-21 07:47:21 +0000286 if (eagain) {
Igor Sysoevdc9dd432003-10-22 16:38:26 +0000287
Igor Sysoev419f9ac2003-10-21 16:49:56 +0000288 /*
289 * sendfile() can return EAGAIN even if it has sent
290 * a whole file part and successive sendfile() would
Igor Sysoevdc9dd432003-10-22 16:38:26 +0000291 * return EAGAIN right away and would not send anything
Igor Sysoev419f9ac2003-10-21 16:49:56 +0000292 */
Igor Sysoevdc9dd432003-10-22 16:38:26 +0000293
Igor Sysoev65977492003-11-02 22:56:18 +0000294 wev->ready = 0;
Igor Sysoev9760a132003-10-21 07:47:21 +0000295 break;
296 }
Igor Sysoev6253ca12003-05-27 12:18:54 +0000297
Igor Sysoev9760a132003-10-21 07:47:21 +0000298 /* "tail == in" means that a single sendfile() is complete */
299
300 } while ((tail && tail == in) || eintr);
301
302 if (in) {
Igor Sysoev65977492003-11-02 22:56:18 +0000303 wev->ready = 0;
Igor Sysoev1342d9c2003-10-09 07:00:45 +0000304 }
305
Igor Sysoev9760a132003-10-21 07:47:21 +0000306 return in;
Igor Sysoevff148df2003-02-26 20:21:43 +0000307}