comparison src/os/unix/ngx_freebsd_sendfile_chain.c @ 0:f0b350454894 NGINX_0_1_0

nginx 0.1.0 *) The first public version.
author Igor Sysoev <http://sysoev.ru>
date Mon, 04 Oct 2004 00:00:00 +0400
parents
children 4b2dafa26fe2
comparison
equal deleted inserted replaced
-1:000000000000 0:f0b350454894
1
2 /*
3 * Copyright (C) Igor Sysoev
4 */
5
6
7 #include <ngx_config.h>
8 #include <ngx_core.h>
9 #include <ngx_event.h>
10
11
12 /*
13 * Although FreeBSD sendfile() allows to pass a header and a trailer
14 * it can not send a header with a part of the file in one packet until
15 * FreeBSD 5.3. Besides over the fast ethernet connection sendfile()
16 * may send the partially filled packets, i.e. the 8 file pages may be sent
17 * as the 11 full 1460-bytes packets, then one incomplete 324-bytes packet,
18 * and then again the 11 full 1460-bytes packets.
19 *
20 * So we use the TCP_NOPUSH option (similar to Linux's TCP_CORK)
21 * to postpone the sending - it not only sends a header and the first part
22 * of the file in one packet but also sends the file pages in the full packets.
23 *
24 * But until FreeBSD 4.5 the turning TCP_NOPUSH off does not flush a pending
25 * data that less than MSS so that data may be sent with 5 second delay.
26 * So we do not use TCP_NOPUSH on FreeBSD prior to 4.5 although it can be used
27 * for non-keepalive HTTP connections.
28 */
29
30
31 #define NGX_HEADERS 8
32 #define NGX_TRAILERS 4
33
34
35 ngx_chain_t *ngx_freebsd_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in,
36 off_t limit)
37 {
38 int rc;
39 u_char *prev;
40 off_t fprev, sent, send, sprev, aligned;
41 size_t hsize, fsize;
42 ssize_t size;
43 ngx_uint_t eintr, eagain, complete;
44 ngx_err_t err;
45 ngx_buf_t *file;
46 ngx_array_t header, trailer;
47 ngx_event_t *wev;
48 ngx_chain_t *cl;
49 struct sf_hdtr hdtr;
50 struct iovec *iov, headers[NGX_HEADERS], trailers[NGX_TRAILERS];
51
52 wev = c->write;
53
54 if (!wev->ready) {
55 return in;
56 }
57
58 #if (HAVE_KQUEUE)
59
60 if ((ngx_event_flags & NGX_HAVE_KQUEUE_EVENT) && wev->pending_eof) {
61 ngx_log_error(NGX_LOG_INFO, c->log, wev->kq_errno,
62 "kevent() reported about an closed connection");
63
64 wev->error = 1;
65 return NGX_CHAIN_ERROR;
66 }
67
68 #endif
69
70 send = 0;
71 eagain = 0;
72
73 header.elts = headers;
74 header.size = sizeof(struct iovec);
75 header.nalloc = NGX_HEADERS;
76 header.pool = c->pool;
77
78 trailer.elts = trailers;
79 trailer.size = sizeof(struct iovec);
80 trailer.nalloc = NGX_TRAILERS;
81 trailer.pool = c->pool;
82
83 for ( ;; ) {
84 file = NULL;
85 fsize = 0;
86 hsize = 0;
87 eintr = 0;
88 complete = 0;
89 sprev = send;
90
91 header.nelts = 0;
92 trailer.nelts = 0;
93
94 /* create the header iovec and coalesce the neighbouring bufs */
95
96 prev = NULL;
97 iov = NULL;
98
99 for (cl = in;
100 cl && header.nelts < IOV_MAX && send < limit;
101 cl = cl->next)
102 {
103 if (ngx_buf_special(cl->buf)) {
104 continue;
105 }
106
107 if (!ngx_buf_in_memory_only(cl->buf)) {
108 break;
109 }
110
111 size = cl->buf->last - cl->buf->pos;
112
113 if (send + size > limit) {
114 size = limit - send;
115 }
116
117 if (prev == cl->buf->pos) {
118 iov->iov_len += size;
119
120 } else {
121 if (!(iov = ngx_array_push(&header))) {
122 return NGX_CHAIN_ERROR;
123 }
124
125 iov->iov_base = (void *) cl->buf->pos;
126 iov->iov_len = size;
127 }
128
129 prev = cl->buf->pos + size;
130 hsize += size;
131 send += size;
132 }
133
134 /* get the file buf */
135
136 if (cl && cl->buf->in_file && send < limit) {
137 file = cl->buf;
138
139 /* coalesce the neighbouring file bufs */
140
141 do {
142 size = (size_t) (cl->buf->file_last - cl->buf->file_pos);
143
144 if (send + size > limit) {
145 size = limit - send;
146
147 aligned = (cl->buf->file_pos + size + ngx_pagesize - 1)
148 & ~(ngx_pagesize - 1);
149
150 if (aligned <= cl->buf->file_last) {
151 size = aligned - cl->buf->file_pos;
152 }
153 }
154
155 fsize += size;
156 send += size;
157 fprev = cl->buf->file_pos + size;
158 cl = cl->next;
159
160 } while (cl
161 && cl->buf->in_file
162 && send < limit
163 && file->file->fd == cl->buf->file->fd
164 && fprev == cl->buf->file_pos);
165 }
166
167 if (file) {
168 /* create the tailer iovec and coalesce the neighbouring bufs */
169
170 prev = NULL;
171 iov = NULL;
172
173 for (/* void */;
174 cl && header.nelts < IOV_MAX && send < limit;
175 cl = cl->next)
176 {
177 if (ngx_buf_special(cl->buf)) {
178 continue;
179 }
180
181 if (!ngx_buf_in_memory_only(cl->buf)) {
182 break;
183 }
184
185 size = cl->buf->last - cl->buf->pos;
186
187 if (send + size > limit) {
188 size = limit - send;
189 }
190
191 if (prev == cl->buf->pos) {
192 iov->iov_len += size;
193
194 } else {
195 if (!(iov = ngx_array_push(&trailer))) {
196 return NGX_CHAIN_ERROR;
197 }
198
199 iov->iov_base = (void *) cl->buf->pos;
200 iov->iov_len = size;
201 }
202
203 prev = cl->buf->pos + size;
204 send += size;
205 }
206 }
207
208 if (file) {
209
210 if (ngx_freebsd_use_tcp_nopush
211 && c->tcp_nopush == NGX_TCP_NOPUSH_UNSET)
212 {
213
214 if (ngx_tcp_nopush(c->fd) == NGX_ERROR) {
215 err = ngx_errno;
216
217 /*
218 * there is a tiny chance to be interrupted, however
219 * we continue a processing without the TCP_NOPUSH
220 */
221
222 if (err != NGX_EINTR) {
223 wev->error = 1;
224 ngx_connection_error(c, err,
225 ngx_tcp_nopush_n " failed");
226 return NGX_CHAIN_ERROR;
227 }
228
229 } else {
230 c->tcp_nopush = NGX_TCP_NOPUSH_SET;
231
232 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
233 "tcp_nopush");
234 }
235 }
236
237 hdtr.headers = (struct iovec *) header.elts;
238 hdtr.hdr_cnt = header.nelts;
239 hdtr.trailers = (struct iovec *) trailer.elts;
240 hdtr.trl_cnt = trailer.nelts;
241
242 /*
243 * the "nbytes bug" of the old sendfile() syscall:
244 * http://www.freebsd.org/cgi/query-pr.cgi?pr=33771
245 */
246
247 if (ngx_freebsd_sendfile_nbytes_bug == 0) {
248 hsize = 0;
249 }
250
251 sent = 0;
252
253 rc = sendfile(file->file->fd, c->fd, file->file_pos,
254 fsize + hsize, &hdtr, &sent, 0);
255
256 if (rc == -1) {
257 err = ngx_errno;
258
259 if (err == NGX_EAGAIN || err == NGX_EINTR) {
260 if (err == NGX_EINTR) {
261 eintr = 1;
262
263 } else {
264 eagain = 1;
265 }
266
267 ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, err,
268 "sendfile() sent only " OFF_T_FMT " bytes",
269 sent);
270
271 } else {
272 wev->error = 1;
273 ngx_connection_error(c, err, "sendfile() failed");
274 return NGX_CHAIN_ERROR;
275 }
276 }
277
278 ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0,
279 "sendfile: %d, @" OFF_T_FMT " " OFF_T_FMT ":%d",
280 rc, file->file_pos, sent, fsize + hsize);
281
282 } else {
283 rc = writev(c->fd, header.elts, header.nelts);
284
285 ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0,
286 "writev: %d of " SIZE_T_FMT, rc, hsize);
287
288 if (rc == -1) {
289 err = ngx_errno;
290
291 if (err == NGX_EAGAIN || err == NGX_EINTR) {
292 if (err == NGX_EINTR) {
293 eintr = 1;
294 }
295
296 ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err,
297 "writev() not ready");
298
299 } else {
300 wev->error = 1;
301 ngx_connection_error(c, err, "writev() failed");
302 return NGX_CHAIN_ERROR;
303 }
304 }
305
306 sent = rc > 0 ? rc : 0;
307 }
308
309 if (send - sprev == sent) {
310 complete = 1;
311 }
312
313 c->sent += sent;
314
315 for (cl = in; cl; cl = cl->next) {
316
317 if (ngx_buf_special(cl->buf)) {
318 continue;
319 }
320
321 if (sent == 0) {
322 break;
323 }
324
325 size = ngx_buf_size(cl->buf);
326
327 if (sent >= size) {
328 sent -= size;
329
330 if (ngx_buf_in_memory(cl->buf)) {
331 cl->buf->pos = cl->buf->last;
332 }
333
334 if (cl->buf->in_file) {
335 cl->buf->file_pos = cl->buf->file_last;
336 }
337
338 continue;
339 }
340
341 if (ngx_buf_in_memory(cl->buf)) {
342 cl->buf->pos += sent;
343 }
344
345 if (cl->buf->in_file) {
346 cl->buf->file_pos += sent;
347 }
348
349 break;
350 }
351
352 if (eagain) {
353
354 /*
355 * sendfile() can return EAGAIN even if it has sent
356 * a whole file part but the successive sendfile() call would
357 * return EAGAIN right away and would not send anything.
358 * We use it as a hint.
359 */
360
361 wev->ready = 0;
362 return cl;
363 }
364
365 if (eintr) {
366 continue;
367 }
368
369 if (!complete) {
370 wev->ready = 0;
371 return cl;
372 }
373
374 if (send >= limit || cl == NULL) {
375 return cl;
376 }
377
378 in = cl;
379 }
380 }