diff options
Diffstat (limited to 'app/nginx/src/os/unix/ngx_darwin_sendfile_chain.c')
-rw-r--r-- | app/nginx/src/os/unix/ngx_darwin_sendfile_chain.c | 206 |
1 files changed, 206 insertions, 0 deletions
diff --git a/app/nginx/src/os/unix/ngx_darwin_sendfile_chain.c b/app/nginx/src/os/unix/ngx_darwin_sendfile_chain.c new file mode 100644 index 0000000..2a76c15 --- /dev/null +++ b/app/nginx/src/os/unix/ngx_darwin_sendfile_chain.c @@ -0,0 +1,206 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include <ngx_config.h> +#include <ngx_core.h> +#include <ngx_event.h> + + +/* + * It seems that Darwin 9.4 (Mac OS X 1.5) sendfile() has the same + * old bug as early FreeBSD sendfile() syscall: + * http://bugs.freebsd.org/33771 + * + * Besides sendfile() has another bug: if one calls sendfile() + * with both a header and a trailer, then sendfile() ignores a file part + * at all and sends only the header and the trailer together. + * For this reason we send a trailer only if there is no a header. + * + * Although sendfile() allows to pass a header or a trailer, + * it may send the header or the trailer and a part of the file + * in different packets. And FreeBSD workaround (TCP_NOPUSH option) + * does not help. + */ + + +ngx_chain_t * +ngx_darwin_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) +{ + int rc; + off_t send, prev_send, sent; + off_t file_size; + ssize_t n; + ngx_uint_t eintr; + ngx_err_t err; + ngx_buf_t *file; + ngx_event_t *wev; + ngx_chain_t *cl; + ngx_iovec_t header, trailer; + struct sf_hdtr hdtr; + struct iovec headers[NGX_IOVS_PREALLOCATE]; + struct iovec trailers[NGX_IOVS_PREALLOCATE]; + + wev = c->write; + + if (!wev->ready) { + return in; + } + +#if (NGX_HAVE_KQUEUE) + + if ((ngx_event_flags & NGX_USE_KQUEUE_EVENT) && wev->pending_eof) { + (void) ngx_connection_error(c, wev->kq_errno, + "kevent() reported about an closed connection"); + wev->error = 1; + return NGX_CHAIN_ERROR; + } + +#endif + + /* the maximum limit size is the maximum size_t value - the page size */ + + if (limit == 0 || limit > (off_t) (NGX_MAX_SIZE_T_VALUE - ngx_pagesize)) { + limit = NGX_MAX_SIZE_T_VALUE - ngx_pagesize; + } + + send = 0; + + header.iovs = headers; + header.nalloc = NGX_IOVS_PREALLOCATE; + + trailer.iovs = trailers; + trailer.nalloc = NGX_IOVS_PREALLOCATE; + + for ( ;; ) { + eintr = 0; + prev_send = send; + + /* create the header iovec and coalesce the neighbouring bufs */ + + cl = ngx_output_chain_to_iovec(&header, in, limit - send, c->log); + + if (cl == NGX_CHAIN_ERROR) { + return NGX_CHAIN_ERROR; + } + + send += header.size; + + if (cl && cl->buf->in_file && send < limit) { + file = cl->buf; + + /* coalesce the neighbouring file bufs */ + + file_size = ngx_chain_coalesce_file(&cl, limit - send); + + send += file_size; + + if (header.count == 0 && send < limit) { + + /* + * create the trailer iovec and coalesce the neighbouring bufs + */ + + cl = ngx_output_chain_to_iovec(&trailer, cl, limit - send, + c->log); + if (cl == NGX_CHAIN_ERROR) { + return NGX_CHAIN_ERROR; + } + + send += trailer.size; + + } else { + trailer.count = 0; + } + + /* + * sendfile() returns EINVAL if sf_hdtr's count is 0, + * but corresponding pointer is not NULL + */ + + hdtr.headers = header.count ? header.iovs : NULL; + hdtr.hdr_cnt = header.count; + hdtr.trailers = trailer.count ? trailer.iovs : NULL; + hdtr.trl_cnt = trailer.count; + + sent = header.size + file_size; + + ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, + "sendfile: @%O %O h:%uz", + file->file_pos, sent, header.size); + + rc = sendfile(file->file->fd, c->fd, file->file_pos, + &sent, &hdtr, 0); + + if (rc == -1) { + err = ngx_errno; + + switch (err) { + case NGX_EAGAIN: + break; + + case NGX_EINTR: + eintr = 1; + break; + + default: + wev->error = 1; + (void) ngx_connection_error(c, err, "sendfile() failed"); + return NGX_CHAIN_ERROR; + } + + ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, err, + "sendfile() sent only %O bytes", sent); + } + + if (rc == 0 && sent == 0) { + + /* + * if rc and sent equal to zero, then someone + * has truncated the file, so the offset became beyond + * the end of the file + */ + + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "sendfile() reported that \"%s\" was truncated", + file->file->name.data); + + return NGX_CHAIN_ERROR; + } + + ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0, + "sendfile: %d, @%O %O:%O", + rc, file->file_pos, sent, file_size + header.size); + + } else { + n = ngx_writev(c, &header); + + if (n == NGX_ERROR) { + return NGX_CHAIN_ERROR; + } + + sent = (n == NGX_AGAIN) ? 0 : n; + } + + c->sent += sent; + + in = ngx_chain_update_sent(in, sent); + + if (eintr) { + send = prev_send + sent; + continue; + } + + if (send - prev_send != sent) { + wev->ready = 0; + return in; + } + + if (send >= limit || in == NULL) { + return in; + } + } +} |