diff --git a/src/core/ngx_write_chain.c b/src/core/ngx_write_chain.c new file mode 100644 index 000000000..023cf032c --- /dev/null +++ b/src/core/ngx_write_chain.c @@ -0,0 +1,203 @@ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + + +ngx_chain_t *ngx_write_chain(ngx_connection_t *c, ngx_chain_t *in, off_t flush) +{ + int rc, i, last; + u_int flags; + char *prev; + off_t sent; + ngx_iovec_t *iov; + ngx_array_t header, trailer; + ngx_hunk_t *file; + ngx_chain_t *ce; + + ch = in; + file = NULL; + last = 0; + + ngx_init_array(header, c->pool, 10, sizeof(ngx_iovec_t), NGX_CHAIN_ERROR); + ngx_init_array(trailer, c->pool, 10, sizeof(ngx_iovec_t), NGX_CHAIN_ERROR); + + do { + header.nelts = 0; + trailer.nelts = 0; + + if (ce->hunk->type & NGX_HUNK_IN_MEMORY) { + prev = NULL; + iov = NULL; + + /* create iovec and coalesce the neighbouring chain entries */ + while (ce && (ce->hunk->type & NGX_HUNK_IN_MEMORY)) + { + if (prev == ce->hunk->pos.mem) { + iov->ngx_iov_len += ce->hunk->last.mem - ce->hunk->pos.mem; + + } else { + ngx_test_null(iov, ngx_push_array(&header), + NGX_CHAIN_ERROR); + iov->ngx_iov_base = ce->hunk->pos.mem; + iov->ngx_iov_len = ce->hunk->last.mem - ce->hunk->pos.mem; + prev = ce->hunk->last.mem; + } + + if (ce->hunk->type & NGX_HUNK_LAST) { + last = 1; + } + + ce = ce->next; + } + } + + if (ce && (ce->hunk->type & NGX_HUNK_FILE)) { + file = ce->hunk; + ce = ce->next; + + if (ce->hunk->type & NGX_HUNK_LAST) { + last = 1; + } + } + +#if (HAVE_MAX_SENDFILE_IOVEC) + if (file && header->nelts > HAVE_MAX_SENDFILE_IOVEC) { + rc = ngx_sendv(c->fd, (ngx_iovec_t *) header->elts, header->nelts, + &sent); + } else { +#endif + if (ch && ch->hunk->type & NGX_HUNK_IN_MEMORY) { + prev = NULL; + iov = NULL; + + while (ch && (ch->hunk->type & NGX_HUNK_IN_MEMORY)) { + + if (prev == ch->hunk->pos.mem) { + iov->ngx_iov_len += + ch->hunk->last.mem - ch->hunk->pos.mem; + + } else { + ngx_test_null(iov, ngx_push_array(trailer), + NGX_CHAIN_ERROR); + iov->ngx_iov_base = ch->hunk->pos.mem; + iov->ngx_iov_len = + ch->hunk->last.mem - ch->hunk->pos.mem; + prev = ch->hunk->last.mem; + } + + if (ch->hunk->type & NGX_HUNK_LAST) { + last = 1; + } + + ch = ch->next; + } + } + + if (file) { + flags = ngx_sendfile_flags; +#if (HAVE_SENDFILE_DISCONNECT) + if (last && c->close) { + flags |= NGX_SENDFILE_DISCONNECT; + } +#endif + rc = ngx_sendfile(c, + (ngx_iovec_t *) header->elts, header->nelts, + file->file->fd, file->pos.file, + (size_t) (file->last.file - file->pos.file), + (ngx_iovec_t *) trailer->elts, trailer->nelts, + &sent, flags); + +#if (HAVE_AIO_EVENT) && !(HAVE_IOCP_EVENT) + } else if (ngx_event_flags & NGX_HAVE_AIO_EVENT) { + + sent = 0; + rc = NGX_AGAIN; + iov = (ngx_iovec_t *) header->elts; + for (i = 0; i < header->nelts; i++) { + rc = ngx_event_aio_write(c, iov[i].ngx_iov_base, + iov[i].ngx_iov_len); + + if (rc > 0) { + sent += rc; + } else { + break; + } + + if (rc < (int) iov->ngx_iov_len) { + break; + } + } +#endif + } else { + rc = ngx_sendv(c, (ngx_iovec_t *) header->elts, header->nelts); + + sent = rc > 0 ? rc: 0; + +#if (NGX_DEBUG_EVENT_WRITE) + ngx_log_debug(c->log, "sendv: " OFF_FMT _ sent); +#endif + } +#if (HAVE_MAX_SENDFILE_IOVEC) + } +#endif + if (rc == NGX_ERROR) + return (ngx_chain_t *) NGX_ERROR; + + c->sent += sent; + flush -= sent; + + for (ch = in; ch; ch = ch->next) { + +#if (NGX_DEBUG_EVENT_WRITE) + ngx_log_debug(c->log, "event write: %x " QX_FMT " " OFF_FMT _ + ch->hunk->type _ + ch->hunk->pos.file _ + ch->hunk->last.file - ch->hunk->pos.file); +#endif + + if (sent >= ch->hunk->last.file - ch->hunk->pos.file) { + sent -= ch->hunk->last.file - ch->hunk->pos.file; + ch->hunk->pos.file = ch->hunk->last.file; + +#if (NGX_DEBUG_EVENT_WRITE) + ngx_log_debug(c->log, "event write: " QX_FMT " 0 " OFF_FMT _ + ch->hunk->pos.file _ sent); +#endif + +/* + if (ch->hunk->type & NGX_HUNK_LAST) + break; +*/ + + continue; + } + + ch->hunk->pos.file += sent; + +#if (NGX_DEBUG_EVENT_WRITE) + ngx_log_debug(c->log, "event write: " QX_FMT " " OFF_FMT _ + ch->hunk->pos.file _ + ch->hunk->last.file - ch->hunk->pos.file); +#endif + + break; + } + + /* flush hunks if threaded state */ + } while (c->write->context && flush > 0); + + ngx_destroy_array(&trailer); + ngx_destroy_array(&header); + + return ch; +} diff --git a/src/http/ngx_http_output_filter.c b/src/http/ngx_http_output_filter.c index b32ce1102..4f7092847 100644 --- a/src/http/ngx_http_output_filter.c +++ b/src/http/ngx_http_output_filter.c @@ -71,7 +71,7 @@ int ngx_http_output_filter(ngx_http_request_t *r, ngx_hunk_t *hunk) { int rc; size_t size; - ngx_chain_t *ce, *pe; + ngx_chain_t *ce, *le; ngx_http_output_filter_ctx_t *ctx; ngx_http_output_filter_conf_t *conf; @@ -96,7 +96,6 @@ int ngx_http_output_filter(ngx_http_request_t *r, ngx_hunk_t *hunk) if (!need_to_copy(r, hunk)) { ctx->out.hunk = hunk; ctx->out.next = NULL; - return next_filter(r, &ctx->out); } } @@ -105,17 +104,14 @@ int ngx_http_output_filter(ngx_http_request_t *r, ngx_hunk_t *hunk) if (hunk) { /* the output of the only hunk is common case so we have - special chain entry ctx->in for it */ + the special chain entry ctx->in for it */ if (ctx->incoming == NULL) { ctx->in.hunk = hunk; ctx->in.next = NULL; ctx->incoming = &ctx->in; } else { - for (ce = ctx->incoming; ce->next; ce = ce->next) { - /* void */ ; - } - + for (ce = ctx->incoming; ce->next; ce = ce->next) { /* void */ ; } ngx_add_hunk_to_chain(ce->next, hunk, r->pool, NGX_ERROR); } } @@ -124,8 +120,8 @@ int ngx_http_output_filter(ngx_http_request_t *r, ngx_hunk_t *hunk) if (ctx->hunk == NULL) { conf = (ngx_http_output_filter_conf_t *) - ngx_http_get_module_loc_conf(r->main ? r->main : r, - ngx_http_output_filter_module); + ngx_http_get_module_loc_conf(r->main ? r->main : r, + ngx_http_output_filter_module); if (hunk->type & NGX_HUNK_LAST) { size = hunk->last.mem - hunk->pos.mem; @@ -140,7 +136,6 @@ int ngx_http_output_filter(ngx_http_request_t *r, ngx_hunk_t *hunk) ngx_test_null(ctx->hunk, ngx_create_temp_hunk(r->pool, size, 50, 50), NGX_ERROR); - ctx->hunk->type |= NGX_HUNK_RECYCLED; @@ -153,13 +148,12 @@ int ngx_http_output_filter(ngx_http_request_t *r, ngx_hunk_t *hunk) } /* NGX_OK */ - /* set our hunk free */ ctx->hunk->pos.mem = ctx->hunk->last.mem = ctx->hunk->start; } #if (NGX_SUPPRESS_WARN) - pe = NULL; + le = NULL; #endif /* process the chain ctx->incoming */ @@ -169,7 +163,7 @@ int ngx_http_output_filter(ngx_http_request_t *r, ngx_hunk_t *hunk) if (need_to_copy(r, ce->hunk)) { break; } - pe = ce; + le = ce; } /* ... and pass them to the next filter */ @@ -178,10 +172,9 @@ int ngx_http_output_filter(ngx_http_request_t *r, ngx_hunk_t *hunk) ctx->out.hunk = ctx->incoming->hunk; ctx->out.next = ctx->incoming->next; ctx->incoming = ce; - pe->next = NULL; + le->next = NULL; rc = next_filter(r, &ctx->out); - if (rc == NGX_ERROR || rc == NGX_AGAIN) { return rc; } @@ -197,29 +190,24 @@ int ngx_http_output_filter(ngx_http_request_t *r, ngx_hunk_t *hunk) do { rc = ngx_http_output_filter_copy_hunk(ctx->hunk, ctx->incoming->hunk); - if (rc == NGX_ERROR) { return rc; } #if (NGX_FILE_AIO_READ) - if (rc == NGX_AGAIN) { return rc; } - #endif ctx->out.hunk = ctx->hunk; ctx->out.next = NULL; rc = next_filter(r, &ctx->out); - if (rc == NGX_ERROR || rc == NGX_AGAIN) { return rc; } /* NGX_OK */ - /* set our hunk free */ ctx->hunk->pos.mem = ctx->hunk->last.mem = ctx->hunk->start; @@ -254,11 +242,9 @@ static int ngx_http_output_filter_copy_hunk(ngx_hunk_t *dst, ngx_hunk_t *src) } #if (NGX_FILE_AIO_READ) - if (n == NGX_AGAIN) { return n; } - #endif if (n != size) { @@ -275,7 +261,6 @@ static int ngx_http_output_filter_copy_hunk(ngx_hunk_t *dst, ngx_hunk_t *src) } else { ngx_memcpy(src->pos.mem, dst->pos.mem, size); - src->pos.mem += size; dst->last.mem += size; }