Mercurial > hg > nginx-ranges
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 } |