Mercurial > hg > nginx
diff src/os/unix/ngx_udp_sendmsg_chain.c @ 6692:56fc55e32f23
Stream: filters.
author | Roman Arutyunyan <arut@nginx.com> |
---|---|
date | Thu, 15 Sep 2016 14:55:46 +0300 |
parents | |
children | dbb0c854e308 |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/src/os/unix/ngx_udp_sendmsg_chain.c @@ -0,0 +1,245 @@ + +/* + * Copyright (C) Igor Sysoev + * Copyright (C) Nginx, Inc. + */ + + +#include <ngx_config.h> +#include <ngx_core.h> +#include <ngx_event.h> + + +static ngx_chain_t *ngx_udp_output_chain_to_iovec(ngx_iovec_t *vec, + ngx_chain_t *in, ngx_log_t *log); +static ssize_t ngx_sendmsg(ngx_connection_t *c, ngx_iovec_t *vec); + + +ngx_chain_t * +ngx_udp_unix_sendmsg_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) +{ + ssize_t n; + off_t send; + ngx_chain_t *cl; + ngx_event_t *wev; + ngx_iovec_t vec; + struct iovec iovs[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; + + vec.iovs = iovs; + vec.nalloc = NGX_IOVS_PREALLOCATE; + + for ( ;; ) { + + /* create the iovec and coalesce the neighbouring bufs */ + + cl = ngx_udp_output_chain_to_iovec(&vec, in, c->log); + + if (cl == NGX_CHAIN_ERROR) { + return NGX_CHAIN_ERROR; + } + + if (cl && cl->buf->in_file) { + ngx_log_error(NGX_LOG_ALERT, c->log, 0, + "file buf in sendmsg " + "t:%d r:%d f:%d %p %p-%p %p %O-%O", + cl->buf->temporary, + cl->buf->recycled, + cl->buf->in_file, + cl->buf->start, + cl->buf->pos, + cl->buf->last, + cl->buf->file, + cl->buf->file_pos, + cl->buf->file_last); + + ngx_debug_point(); + + return NGX_CHAIN_ERROR; + } + + if (cl == in) { + return in; + } + + send += vec.size; + + n = ngx_sendmsg(c, &vec); + + if (n == NGX_ERROR) { + return NGX_CHAIN_ERROR; + } + + if (n == NGX_AGAIN) { + wev->ready = 0; + return in; + } + + c->sent += n; + + in = ngx_chain_update_sent(in, n); + + if (send >= limit || in == NULL) { + return in; + } + } +} + + +static ngx_chain_t * +ngx_udp_output_chain_to_iovec(ngx_iovec_t *vec, ngx_chain_t *in, ngx_log_t *log) +{ + size_t total, size; + u_char *prev; + ngx_uint_t n, flush; + ngx_chain_t *cl; + struct iovec *iov; + + cl = in; + iov = NULL; + prev = NULL; + total = 0; + n = 0; + flush = 0; + + for ( /* void */ ; in && !flush; in = in->next) { + + if (in->buf->flush || in->buf->last_buf) { + flush = 1; + } + + if (ngx_buf_special(in->buf)) { + continue; + } + + if (in->buf->in_file) { + break; + } + + if (!ngx_buf_in_memory(in->buf)) { + ngx_log_error(NGX_LOG_ALERT, log, 0, + "bad buf in output chain " + "t:%d r:%d f:%d %p %p-%p %p %O-%O", + in->buf->temporary, + in->buf->recycled, + in->buf->in_file, + in->buf->start, + in->buf->pos, + in->buf->last, + in->buf->file, + in->buf->file_pos, + in->buf->file_last); + + ngx_debug_point(); + + return NGX_CHAIN_ERROR; + } + + size = in->buf->last - in->buf->pos; + + if (prev == in->buf->pos) { + iov->iov_len += size; + + } else { + if (n == vec->nalloc) { + ngx_log_error(NGX_LOG_ALERT, log, 0, + "too many parts in a datagram"); + return NGX_CHAIN_ERROR; + } + + iov = &vec->iovs[n++]; + + iov->iov_base = (void *) in->buf->pos; + iov->iov_len = size; + } + + prev = in->buf->pos + size; + total += size; + } + + if (!flush) { +#if (NGX_SUPPRESS_WARN) + vec->size = 0; + vec->count = 0; +#endif + return cl; + } + + vec->count = n; + vec->size = total; + + return in; +} + + +static ssize_t +ngx_sendmsg(ngx_connection_t *c, ngx_iovec_t *vec) +{ + ssize_t n; + ngx_err_t err; + struct msghdr msg; + + ngx_memzero(&msg, sizeof(struct msghdr)); + + if (c->socklen) { + msg.msg_name = c->sockaddr; + msg.msg_namelen = c->socklen; + } + + msg.msg_iov = vec->iovs; + msg.msg_iovlen = vec->count; + +eintr: + + n = sendmsg(c->fd, &msg, 0); + + ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, + "sendmsg: %z of %uz", n, vec->size); + + if (n == -1) { + err = ngx_errno; + + switch (err) { + case NGX_EAGAIN: + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, + "sendmsg() not ready"); + return NGX_AGAIN; + + case NGX_EINTR: + ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, + "sendmsg() was interrupted"); + goto eintr; + + default: + c->write->error = 1; + ngx_connection_error(c, err, "sendmsg() failed"); + return NGX_ERROR; + } + } + + return n; +}