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) {