3 * Copyright (C) Igor Sysoev
4 * Copyright (C) Nginx, Inc.
8 #include <ngx_config.h>
10 #include <ngx_event.h>
14 * It seems that Darwin 9.4 (Mac OS X 1.5) sendfile() has the same
15 * old bug as early FreeBSD sendfile() syscall:
16 * http://www.freebsd.org/cgi/query-pr.cgi?pr=33771
18 * Besides sendfile() has another bug: if one calls sendfile()
19 * with both a header and a trailer, then sendfile() ignores a file part
20 * at all and sends only the header and the trailer together.
21 * For this reason we send a trailer only if there is no a header.
23 * Although sendfile() allows to pass a header or a trailer,
24 * it may send the header or the trailer and a part of the file
25 * in different packets. And FreeBSD workaround (TCP_NOPUSH option)
31 #define NGX_HEADERS 64
32 #define NGX_TRAILERS 64
34 #define NGX_HEADERS IOV_MAX
35 #define NGX_TRAILERS IOV_MAX
40 ngx_darwin_sendfile_chain(ngx_connection_t
*c
, ngx_chain_t
*in
, off_t limit
)
44 off_t size
, send
, prev_send
, aligned
, sent
, fprev
;
45 off_t header_size
, file_size
;
46 ngx_uint_t eintr
, complete
;
49 ngx_array_t header
, trailer
;
53 struct iovec
*iov
, headers
[NGX_HEADERS
], trailers
[NGX_TRAILERS
];
63 if ((ngx_event_flags
& NGX_USE_KQUEUE_EVENT
) && wev
->pending_eof
) {
64 (void) ngx_connection_error(c
, wev
->kq_errno
,
65 "kevent() reported about an closed connection");
67 return NGX_CHAIN_ERROR
;
72 /* the maximum limit size is the maximum size_t value - the page size */
74 if (limit
== 0 || limit
> (off_t
) (NGX_MAX_SIZE_T_VALUE
- ngx_pagesize
)) {
75 limit
= NGX_MAX_SIZE_T_VALUE
- ngx_pagesize
;
80 header
.elts
= headers
;
81 header
.size
= sizeof(struct iovec
);
82 header
.nalloc
= NGX_HEADERS
;
83 header
.pool
= c
->pool
;
85 trailer
.elts
= trailers
;
86 trailer
.size
= sizeof(struct iovec
);
87 trailer
.nalloc
= NGX_TRAILERS
;
88 trailer
.pool
= c
->pool
;
101 /* create the header iovec and coalesce the neighbouring bufs */
106 for (cl
= in
; cl
&& send
< limit
; cl
= cl
->next
) {
108 if (ngx_buf_special(cl
->buf
)) {
112 if (!ngx_buf_in_memory_only(cl
->buf
)) {
116 size
= cl
->buf
->last
- cl
->buf
->pos
;
118 if (send
+ size
> limit
) {
122 if (prev
== cl
->buf
->pos
) {
123 iov
->iov_len
+= (size_t) size
;
126 if (header
.nelts
>= IOV_MAX
) {
130 iov
= ngx_array_push(&header
);
132 return NGX_CHAIN_ERROR
;
135 iov
->iov_base
= (void *) cl
->buf
->pos
;
136 iov
->iov_len
= (size_t) size
;
139 prev
= cl
->buf
->pos
+ (size_t) size
;
145 if (cl
&& cl
->buf
->in_file
&& send
< limit
) {
148 /* coalesce the neighbouring file bufs */
151 size
= cl
->buf
->file_last
- cl
->buf
->file_pos
;
153 if (send
+ size
> limit
) {
156 aligned
= (cl
->buf
->file_pos
+ size
+ ngx_pagesize
- 1)
157 & ~((off_t
) ngx_pagesize
- 1);
159 if (aligned
<= cl
->buf
->file_last
) {
160 size
= aligned
- cl
->buf
->file_pos
;
166 fprev
= cl
->buf
->file_pos
+ size
;
172 && file
->file
->fd
== cl
->buf
->file
->fd
173 && fprev
== cl
->buf
->file_pos
);
176 if (file
&& header
.nelts
== 0) {
178 /* create the trailer iovec and coalesce the neighbouring bufs */
183 while (cl
&& send
< limit
) {
185 if (ngx_buf_special(cl
->buf
)) {
190 if (!ngx_buf_in_memory_only(cl
->buf
)) {
194 size
= cl
->buf
->last
- cl
->buf
->pos
;
196 if (send
+ size
> limit
) {
200 if (prev
== cl
->buf
->pos
) {
201 iov
->iov_len
+= (size_t) size
;
204 if (trailer
.nelts
>= IOV_MAX
) {
208 iov
= ngx_array_push(&trailer
);
210 return NGX_CHAIN_ERROR
;
213 iov
->iov_base
= (void *) cl
->buf
->pos
;
214 iov
->iov_len
= (size_t) size
;
217 prev
= cl
->buf
->pos
+ (size_t) size
;
226 * sendfile() returns EINVAL if sf_hdtr's count is 0,
227 * but corresponding pointer is not NULL
230 hdtr
.headers
= header
.nelts
? (struct iovec
*) header
.elts
: NULL
;
231 hdtr
.hdr_cnt
= header
.nelts
;
232 hdtr
.trailers
= trailer
.nelts
? (struct iovec
*) trailer
.elts
: NULL
;
233 hdtr
.trl_cnt
= trailer
.nelts
;
235 sent
= header_size
+ file_size
;
237 ngx_log_debug3(NGX_LOG_DEBUG_EVENT
, c
->log
, 0,
238 "sendfile: @%O %O h:%O",
239 file
->file_pos
, sent
, header_size
);
241 rc
= sendfile(file
->file
->fd
, c
->fd
, file
->file_pos
,
257 (void) ngx_connection_error(c
, err
, "sendfile() failed");
258 return NGX_CHAIN_ERROR
;
261 ngx_log_debug1(NGX_LOG_DEBUG_EVENT
, c
->log
, err
,
262 "sendfile() sent only %O bytes", sent
);
265 if (rc
== 0 && sent
== 0) {
268 * if rc and sent equal to zero, then someone
269 * has truncated the file, so the offset became beyond
270 * the end of the file
273 ngx_log_error(NGX_LOG_ALERT
, c
->log
, 0,
274 "sendfile() reported that \"%s\" was truncated",
275 file
->file
->name
.data
);
277 return NGX_CHAIN_ERROR
;
280 ngx_log_debug4(NGX_LOG_DEBUG_EVENT
, c
->log
, 0,
281 "sendfile: %d, @%O %O:%O",
282 rc
, file
->file_pos
, sent
, file_size
+ header_size
);
285 rc
= writev(c
->fd
, header
.elts
, header
.nelts
);
287 ngx_log_debug2(NGX_LOG_DEBUG_EVENT
, c
->log
, 0,
288 "writev: %d of %uz", rc
, send
);
303 ngx_connection_error(c
, err
, "writev() failed");
304 return NGX_CHAIN_ERROR
;
307 ngx_log_debug0(NGX_LOG_DEBUG_EVENT
, c
->log
, err
,
308 "writev() not ready");
311 sent
= rc
> 0 ? rc
: 0;
314 if (send
- prev_send
== sent
) {
320 for (cl
= in
; cl
; cl
= cl
->next
) {
322 if (ngx_buf_special(cl
->buf
)) {
330 size
= ngx_buf_size(cl
->buf
);
335 if (ngx_buf_in_memory(cl
->buf
)) {
336 cl
->buf
->pos
= cl
->buf
->last
;
339 if (cl
->buf
->in_file
) {
340 cl
->buf
->file_pos
= cl
->buf
->file_last
;
346 if (ngx_buf_in_memory(cl
->buf
)) {
347 cl
->buf
->pos
+= (size_t) sent
;
350 if (cl
->buf
->in_file
) {
351 cl
->buf
->file_pos
+= sent
;
366 if (send
>= limit
|| cl
== NULL
) {