blob: e76f4c6f48b978b29c4820c683db50c48ca3ce08 [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 Sysoeva8fa0a62003-11-25 20:44:56 +000029 ngx_int_t eintr, eagain;
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) {
47 ngx_log_error(NGX_LOG_ERR, c->log, wev->kq_errno,
48 "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 Sysoevff148df2003-02-26 20:21:43 +000062
Igor Sysoev6253ca12003-05-27 12:18:54 +000063 ngx_init_array(header, c->pool, 10, sizeof(struct iovec),
64 NGX_CHAIN_ERROR);
65 ngx_init_array(trailer, c->pool, 10, sizeof(struct iovec),
66 NGX_CHAIN_ERROR);
Igor Sysoevff148df2003-02-26 20:21:43 +000067
Igor Sysoevdc9dd432003-10-22 16:38:26 +000068 /* create the header iovec and coalesce the neighbouring hunks */
Igor Sysoev1342d9c2003-10-09 07:00:45 +000069
Igor Sysoevb3e73d82003-10-10 15:10:50 +000070 prev = NULL;
71 iov = NULL;
Igor Sysoevb7387572003-03-11 20:38:13 +000072
Igor Sysoevdc9dd432003-10-22 16:38:26 +000073 for (cl = in; cl; cl = cl->next) {
74 if (ngx_hunk_special(cl->hunk)) {
Igor Sysoevb3e73d82003-10-10 15:10:50 +000075 continue;
Igor Sysoev6253ca12003-05-27 12:18:54 +000076 }
Igor Sysoevb3e73d82003-10-10 15:10:50 +000077
Igor Sysoevdc9dd432003-10-22 16:38:26 +000078 if (!ngx_hunk_in_memory_only(cl->hunk)) {
Igor Sysoevb3e73d82003-10-10 15:10:50 +000079 break;
80 }
81
Igor Sysoevdc9dd432003-10-22 16:38:26 +000082 if (prev == cl->hunk->pos) {
83 iov->iov_len += cl->hunk->last - cl->hunk->pos;
Igor Sysoevb3e73d82003-10-10 15:10:50 +000084
85 } else {
86 ngx_test_null(iov, ngx_push_array(&header), NGX_CHAIN_ERROR);
Igor Sysoevdc9dd432003-10-22 16:38:26 +000087 iov->iov_base = cl->hunk->pos;
88 iov->iov_len = cl->hunk->last - cl->hunk->pos;
Igor Sysoevb3e73d82003-10-10 15:10:50 +000089 }
90
Igor Sysoevab0c4f52003-10-28 15:45:41 +000091 prev = cl->hunk->last;
Igor Sysoevdc9dd432003-10-22 16:38:26 +000092 hsize += cl->hunk->last - cl->hunk->pos;
Igor Sysoev6253ca12003-05-27 12:18:54 +000093 }
94
Igor Sysoev9760a132003-10-21 07:47:21 +000095 /* get the file hunk */
Igor Sysoev1342d9c2003-10-09 07:00:45 +000096
Igor Sysoevdc9dd432003-10-22 16:38:26 +000097 if (cl && (cl->hunk->type & NGX_HUNK_FILE)) {
98 file = cl->hunk;
Igor Sysoev9760a132003-10-21 07:47:21 +000099 fsize = (size_t) (file->file_last - file->file_pos);
100 fprev = file->file_last;
Igor Sysoevdc9dd432003-10-22 16:38:26 +0000101 cl = cl->next;
Igor Sysoev9760a132003-10-21 07:47:21 +0000102
103 /* coalesce the neighbouring file hunks */
104
Igor Sysoevdc9dd432003-10-22 16:38:26 +0000105 while (cl && (cl->hunk->type & NGX_HUNK_FILE)) {
106 if (file->file->fd != cl->hunk->file->fd
107 || fprev != cl->hunk->file_pos)
Igor Sysoev9760a132003-10-21 07:47:21 +0000108 {
109 break;
110 }
111
Igor Sysoevdc9dd432003-10-22 16:38:26 +0000112 fsize += (size_t) (cl->hunk->file_last - cl->hunk->file_pos);
113 fprev = cl->hunk->file_last;
114 cl = cl->next;
Igor Sysoev9760a132003-10-21 07:47:21 +0000115 }
Igor Sysoevff148df2003-02-26 20:21:43 +0000116 }
Igor Sysoevff148df2003-02-26 20:21:43 +0000117
Igor Sysoevdc9dd432003-10-22 16:38:26 +0000118 /* create the tailer iovec and coalesce the neighbouring hunks */
Igor Sysoev1342d9c2003-10-09 07:00:45 +0000119
Igor Sysoevb3e73d82003-10-10 15:10:50 +0000120 prev = NULL;
121 iov = NULL;
Igor Sysoevff148df2003-02-26 20:21:43 +0000122
Igor Sysoevdc9dd432003-10-22 16:38:26 +0000123 for ( /* void */; cl; cl = cl->next) {
124 if (ngx_hunk_special(cl->hunk)) {
Igor Sysoevb3e73d82003-10-10 15:10:50 +0000125 continue;
Igor Sysoevff148df2003-02-26 20:21:43 +0000126 }
Igor Sysoevb3e73d82003-10-10 15:10:50 +0000127
Igor Sysoevdc9dd432003-10-22 16:38:26 +0000128 if (!ngx_hunk_in_memory_only(cl->hunk)) {
Igor Sysoevb3e73d82003-10-10 15:10:50 +0000129 break;
130 }
131
Igor Sysoevdc9dd432003-10-22 16:38:26 +0000132 if (prev == cl->hunk->pos) {
133 iov->iov_len += cl->hunk->last - cl->hunk->pos;
Igor Sysoevb3e73d82003-10-10 15:10:50 +0000134
135 } else {
136 ngx_test_null(iov, ngx_push_array(&trailer), NGX_CHAIN_ERROR);
Igor Sysoevdc9dd432003-10-22 16:38:26 +0000137 iov->iov_base = cl->hunk->pos;
138 iov->iov_len = cl->hunk->last - cl->hunk->pos;
Igor Sysoevb3e73d82003-10-10 15:10:50 +0000139 }
Igor Sysoevab0c4f52003-10-28 15:45:41 +0000140
141 prev = cl->hunk->last;
Igor Sysoevff148df2003-02-26 20:21:43 +0000142 }
143
Igor Sysoev9760a132003-10-21 07:47:21 +0000144 /*
145 * the tail is the rest of the chain that exceeded
146 * a single sendfile() capability
147 */
148
Igor Sysoevdc9dd432003-10-22 16:38:26 +0000149 tail = cl;
Igor Sysoev6253ca12003-05-27 12:18:54 +0000150
151 if (file) {
152
Igor Sysoevb3e73d82003-10-10 15:10:50 +0000153 if (ngx_freebsd_use_tcp_nopush && !c->tcp_nopush) {
Igor Sysoev7578ec92003-06-02 15:24:30 +0000154 c->tcp_nopush = 1;
Igor Sysoevb3e73d82003-10-10 15:10:50 +0000155
156ngx_log_debug(c->log, "NOPUSH");
157
Igor Sysoev9cf78302003-06-04 17:28:33 +0000158 if (ngx_tcp_nopush(c->fd) == NGX_ERROR) {
Igor Sysoeva8fa0a62003-11-25 20:44:56 +0000159 ngx_log_error(NGX_LOG_CRIT, c->log, ngx_errno,
Igor Sysoev9cf78302003-06-04 17:28:33 +0000160 ngx_tcp_nopush_n " failed");
Igor Sysoev6253ca12003-05-27 12:18:54 +0000161 return NGX_CHAIN_ERROR;
162 }
163 }
164
165 hdtr.headers = (struct iovec *) header.elts;
166 hdtr.hdr_cnt = header.nelts;
167 hdtr.trailers = (struct iovec *) trailer.elts;
168 hdtr.trl_cnt = trailer.nelts;
169
Igor Sysoev419f9ac2003-10-21 16:49:56 +0000170 /*
Igor Sysoevdc9dd432003-10-22 16:38:26 +0000171 * the "nbytes bug" of the old sendfile() syscall:
Igor Sysoev419f9ac2003-10-21 16:49:56 +0000172 * http://www.freebsd.org/cgi/query-pr.cgi?pr=33771
173 */
174
Igor Sysoev6253ca12003-05-27 12:18:54 +0000175 if (ngx_freebsd_sendfile_nbytes_bug == 0) {
176 hsize = 0;
177 }
178
179 rc = sendfile(file->file->fd, c->fd, file->file_pos,
Igor Sysoev9760a132003-10-21 07:47:21 +0000180 fsize + hsize, &hdtr, &sent, 0);
Igor Sysoev6253ca12003-05-27 12:18:54 +0000181
182 if (rc == -1) {
183 err = ngx_errno;
184
185 if (err == NGX_EINTR) {
186 eintr = 1;
187 }
188
Igor Sysoev9760a132003-10-21 07:47:21 +0000189 if (err == NGX_EAGAIN) {
190 eagain = 1;
191 }
192
Igor Sysoev6253ca12003-05-27 12:18:54 +0000193 if (err == NGX_EAGAIN || err == NGX_EINTR) {
194 ngx_log_error(NGX_LOG_INFO, c->log, err,
Igor Sysoev0e499db2003-11-27 07:45:22 +0000195 "sendfile() sent only " OFF_T_FMT " bytes",
196 sent);
Igor Sysoev6253ca12003-05-27 12:18:54 +0000197
198 } else {
Igor Sysoev65977492003-11-02 22:56:18 +0000199 wev->error = 1;
Igor Sysoev6253ca12003-05-27 12:18:54 +0000200 ngx_log_error(NGX_LOG_CRIT, c->log, err,
201 "sendfile() failed");
202 return NGX_CHAIN_ERROR;
203 }
204 }
205
Igor Sysoevff148df2003-02-26 20:21:43 +0000206#if (NGX_DEBUG_WRITE_CHAIN)
Igor Sysoev6253ca12003-05-27 12:18:54 +0000207 ngx_log_debug(c->log, "sendfile: %d, @%qd %qd:%d" _
Igor Sysoev9760a132003-10-21 07:47:21 +0000208 rc _ file->file_pos _ sent _ fsize + hsize);
Igor Sysoevff148df2003-02-26 20:21:43 +0000209#endif
210
Igor Sysoevb7387572003-03-11 20:38:13 +0000211 } else {
Igor Sysoev65977492003-11-02 22:56:18 +0000212 rc = writev(c->fd, header.elts, header.nelts);
Igor Sysoev6253ca12003-05-27 12:18:54 +0000213
Igor Sysoevb3e73d82003-10-10 15:10:50 +0000214 if (rc == -1) {
215 err = ngx_errno;
216 if (err == NGX_EAGAIN) {
217 ngx_log_error(NGX_LOG_INFO, c->log, err, "writev() EAGAIN");
Igor Sysoev6253ca12003-05-27 12:18:54 +0000218
Igor Sysoevb3e73d82003-10-10 15:10:50 +0000219 } else if (err == NGX_EINTR) {
220 eintr = 1;
221 ngx_log_error(NGX_LOG_INFO, c->log, err, "writev() EINTR");
Igor Sysoev6253ca12003-05-27 12:18:54 +0000222
Igor Sysoevb3e73d82003-10-10 15:10:50 +0000223 } else {
Igor Sysoev65977492003-11-02 22:56:18 +0000224 wev->error = 1;
Igor Sysoevb3e73d82003-10-10 15:10:50 +0000225 ngx_log_error(NGX_LOG_CRIT, c->log, err, "writev() failed");
226 return NGX_CHAIN_ERROR;
Igor Sysoev6253ca12003-05-27 12:18:54 +0000227 }
Igor Sysoevb3e73d82003-10-10 15:10:50 +0000228 }
Igor Sysoev6253ca12003-05-27 12:18:54 +0000229
Igor Sysoevb3e73d82003-10-10 15:10:50 +0000230 sent = rc > 0 ? rc : 0;
Igor Sysoev6253ca12003-05-27 12:18:54 +0000231
232#if (NGX_DEBUG_WRITE_CHAIN)
Igor Sysoevb3e73d82003-10-10 15:10:50 +0000233 ngx_log_debug(c->log, "writev: %qd" _ sent);
Igor Sysoev6253ca12003-05-27 12:18:54 +0000234#endif
Igor Sysoevb7387572003-03-11 20:38:13 +0000235 }
Igor Sysoevff148df2003-02-26 20:21:43 +0000236
Igor Sysoev6253ca12003-05-27 12:18:54 +0000237 c->sent += sent;
238
Igor Sysoevdc9dd432003-10-22 16:38:26 +0000239 for (cl = in; cl; cl = cl->next) {
Igor Sysoevff148df2003-02-26 20:21:43 +0000240
Igor Sysoevdc9dd432003-10-22 16:38:26 +0000241 if (ngx_hunk_special(cl->hunk)) {
Igor Sysoev9760a132003-10-21 07:47:21 +0000242 continue;
Igor Sysoev6253ca12003-05-27 12:18:54 +0000243 }
244
Igor Sysoev9760a132003-10-21 07:47:21 +0000245 if (sent == 0) {
246 break;
247 }
248
Igor Sysoevdc9dd432003-10-22 16:38:26 +0000249 size = ngx_hunk_size(cl->hunk);
Igor Sysoev9760a132003-10-21 07:47:21 +0000250
Igor Sysoev6253ca12003-05-27 12:18:54 +0000251 if (sent >= size) {
252 sent -= size;
253
Igor Sysoevdc9dd432003-10-22 16:38:26 +0000254 if (cl->hunk->type & NGX_HUNK_IN_MEMORY) {
255 cl->hunk->pos = cl->hunk->last;
Igor Sysoev6253ca12003-05-27 12:18:54 +0000256 }
257
Igor Sysoevdc9dd432003-10-22 16:38:26 +0000258 if (cl->hunk->type & NGX_HUNK_FILE) {
259 cl->hunk->file_pos = cl->hunk->file_last;
Igor Sysoev6253ca12003-05-27 12:18:54 +0000260 }
261
262 continue;
263 }
264
Igor Sysoevdc9dd432003-10-22 16:38:26 +0000265 if (cl->hunk->type & NGX_HUNK_IN_MEMORY) {
266 cl->hunk->pos += sent;
Igor Sysoevb7387572003-03-11 20:38:13 +0000267 }
268
Igor Sysoevdc9dd432003-10-22 16:38:26 +0000269 if (cl->hunk->type & NGX_HUNK_FILE) {
270 cl->hunk->file_pos += sent;
Igor Sysoevb7387572003-03-11 20:38:13 +0000271 }
272
Igor Sysoev6253ca12003-05-27 12:18:54 +0000273 break;
Igor Sysoevff148df2003-02-26 20:21:43 +0000274 }
275
Igor Sysoevdc9dd432003-10-22 16:38:26 +0000276 in = cl;
Igor Sysoevff148df2003-02-26 20:21:43 +0000277
Igor Sysoev9760a132003-10-21 07:47:21 +0000278 if (eagain) {
Igor Sysoevdc9dd432003-10-22 16:38:26 +0000279
Igor Sysoev419f9ac2003-10-21 16:49:56 +0000280 /*
281 * sendfile() can return EAGAIN even if it has sent
282 * a whole file part and successive sendfile() would
Igor Sysoevdc9dd432003-10-22 16:38:26 +0000283 * return EAGAIN right away and would not send anything
Igor Sysoev419f9ac2003-10-21 16:49:56 +0000284 */
Igor Sysoevdc9dd432003-10-22 16:38:26 +0000285
Igor Sysoev65977492003-11-02 22:56:18 +0000286 wev->ready = 0;
Igor Sysoev9760a132003-10-21 07:47:21 +0000287 break;
288 }
Igor Sysoev6253ca12003-05-27 12:18:54 +0000289
Igor Sysoev9760a132003-10-21 07:47:21 +0000290 /* "tail == in" means that a single sendfile() is complete */
291
292 } while ((tail && tail == in) || eintr);
293
294 if (in) {
Igor Sysoev65977492003-11-02 22:56:18 +0000295 wev->ready = 0;
Igor Sysoev1342d9c2003-10-09 07:00:45 +0000296 }
297
Igor Sysoev9760a132003-10-21 07:47:21 +0000298 return in;
Igor Sysoevff148df2003-02-26 20:21:43 +0000299}