static ngx_inline ngx_int_t ngx_output_chain_as_is(ngx_output_chain_ctx_t *ctx, ngx_buf_t *buf) { ngx_uint_t sendfile; if (ngx_buf_special(buf)) { return 1; } #if (NGX_THREADS) if (buf->in_file) { buf->file->thread_handler = ctx->thread_handler; buf->file->thread_ctx = ctx->filter_ctx; } #endif if (buf->in_file && buf->file->directio) { return 0; } sendfile = ctx->sendfile; #if (NGX_SENDFILE_LIMIT) if (buf->in_file && buf->file_pos >= NGX_SENDFILE_LIMIT) { sendfile = 0; } #endif if (!sendfile) { if (!ngx_buf_in_memory(buf)) { return 0; } buf->in_file = 0; } #if (NGX_HAVE_AIO_SENDFILE) if (ctx->aio_preload && buf->in_file) { (void) ngx_output_chain_aio_setup(ctx, buf->file); } #endif if (ctx->need_in_memory && !ngx_buf_in_memory(buf)) { return 0; } if (ctx->need_in_temp && (buf->memory || buf->mmap)) { return 0; } return 1; }
static ngx_chain_t * ngx_http_v2_filter_get_shadow(ngx_http_v2_stream_t *stream, ngx_buf_t *buf, off_t offset, off_t size) { ngx_buf_t *chunk; ngx_chain_t *cl; cl = ngx_chain_get_free_buf(stream->request->pool, &stream->free_bufs); if (cl == NULL) { return NULL; } chunk = cl->buf; ngx_memcpy(chunk, buf, sizeof(ngx_buf_t)); chunk->tag = (ngx_buf_tag_t) &ngx_http_v2_filter_get_shadow; chunk->shadow = buf; if (ngx_buf_in_memory(chunk)) { chunk->pos += offset; chunk->last = chunk->pos + size; } if (chunk->in_file) { chunk->file_pos += offset; chunk->file_last = chunk->file_pos + size; } return cl; }
ngx_int_t ndk_copy_chain_to_str (ngx_pool_t *pool, ngx_chain_t *in, ngx_str_t *str) { ngx_chain_t *cl; size_t len; u_char *p; ngx_buf_t *b; len = 0; for (cl = in; cl; cl = cl->next) len += ngx_buf_size (cl->buf); ndk_palloc_re (p, pool, len + 1); str->data = p; str->len = len; for (cl = in; cl; cl = cl->next) { b = cl->buf; if (ngx_buf_in_memory (b)) { p = ngx_cpymem (p, b->pos, b->last - b->pos); } } *p = '\0'; return NGX_OK; }
ngx_chain_t * ngx_chain_update_sent(ngx_chain_t *in, off_t sent) { off_t size; for ( /* void */ ; in; in = in->next) { if (ngx_buf_special(in->buf)) { continue; } if (sent == 0) { break; } size = ngx_buf_size(in->buf); if (sent >= size) { sent -= size; if (ngx_buf_in_memory(in->buf)) { in->buf->pos = in->buf->last; } if (in->buf->in_file) { in->buf->file_pos = in->buf->file_last; } continue; } if (ngx_buf_in_memory(in->buf)) { in->buf->pos += (size_t) sent; } if (in->buf->in_file) { in->buf->file_pos += sent; } break; } return in; }
static ngx_inline ngx_int_t ngx_output_chain_as_is(ngx_output_chain_ctx_t *ctx, ngx_buf_t *buf) { ngx_uint_t sendfile; if (ngx_buf_special(buf)) { return 1; } if (buf->in_file && buf->file->directio) { return 0; } sendfile = ctx->sendfile; #if (NGX_SENDFILE_LIMIT) if (buf->in_file && buf->file_pos >= NGX_SENDFILE_LIMIT) { sendfile = 0; } #endif if (!sendfile) { if (!ngx_buf_in_memory(buf)) { return 0; } buf->in_file = 0; } if (ctx->need_in_memory && !ngx_buf_in_memory(buf)) { return 0; } if (ctx->need_in_temp && (buf->memory || buf->mmap)) { return 0; } return 1; }
ngx_int_t ngx_http_srcache_add_copy_chain(ngx_pool_t *pool, ngx_chain_t **chain, ngx_chain_t *in, unsigned *plast) { ngx_chain_t *cl, **ll; size_t len; ll = chain; for (cl = *chain; cl; cl = cl->next) { ll = &cl->next; } *plast = 0; while (in) { cl = ngx_alloc_chain_link(pool); if (cl == NULL) { return NGX_ERROR; } if (in->buf->last_buf || in->buf->last_in_chain) { *plast = 1; } if (ngx_buf_special(in->buf)) { cl->buf = in->buf; } else { if (ngx_buf_in_memory(in->buf)) { len = ngx_buf_size(in->buf); cl->buf = ngx_create_temp_buf(pool, len); if (cl->buf == NULL) { return NGX_ERROR; } dd("buf: %.*s", (int) len, in->buf->pos); cl->buf->last = ngx_copy(cl->buf->pos, in->buf->pos, len); } else { return NGX_ERROR; } } *ll = cl; ll = &cl->next; in = in->next; } *ll = NULL; return NGX_OK; }
static void nginx_bucket_destroy(void *data) { apr_bucket_nginx *n = data; ngx_buf_t *buf = n->buf; if (apr_bucket_shared_destroy(n)) { if (!ngx_buf_in_memory(buf) && buf->pos != NULL) { apr_bucket_free(buf->pos); buf->pos = NULL; } apr_bucket_free(n); } }
// this function adapted from push stream module. thanks Wandenberg Peixoto <*****@*****.**> and Rogério Carvalho Schneider <*****@*****.**> static ngx_buf_t * nchan_request_body_to_single_buffer(ngx_http_request_t *r) { ngx_buf_t *buf = NULL; ngx_chain_t *chain; ssize_t n; off_t len; chain = r->request_body->bufs; if (chain->next == NULL) { return chain->buf; } //ngx_log_error(NGX_LOG_WARN, ngx_cycle->log, 0, "nchan: multiple buffers in request, need memcpy :("); if (chain->buf->in_file) { if (ngx_buf_in_memory(chain->buf)) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "nchan: can't handle a buffer in a temp file and in memory "); } if (chain->next != NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "nchan: error reading request body with multiple "); } return chain->buf; } buf = ngx_create_temp_buf(r->pool, r->headers_in.content_length_n + 1); if (buf != NULL) { ngx_memset(buf->start, '\0', r->headers_in.content_length_n + 1); while ((chain != NULL) && (chain->buf != NULL)) { len = ngx_buf_size(chain->buf); // if buffer is equal to content length all the content is in this buffer if (len >= r->headers_in.content_length_n) { buf->start = buf->pos; buf->last = buf->pos; len = r->headers_in.content_length_n; } if (chain->buf->in_file) { n = ngx_read_file(chain->buf->file, buf->start, len, 0); if (n == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "nchan: cannot read file with request body"); return NULL; } buf->last = buf->last + len; ngx_delete_file(chain->buf->file->name.data); chain->buf->file->fd = NGX_INVALID_FILE; } else { buf->last = ngx_copy(buf->start, chain->buf->pos, len); } chain = chain->next; buf->start = buf->last; } buf->last_buf = 1; } return buf; }
ngx_chain_t * ngx_solaris_sendfilev_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) { int fd; u_char *prev; off_t size, send, prev_send, aligned, fprev; size_t sent; ssize_t n; ngx_int_t eintr, complete; ngx_err_t err; sendfilevec_t *sfv, sfvs[NGX_SENDFILEVECS]; ngx_array_t vec; ngx_event_t *wev; ngx_chain_t *cl; wev = c->write; if (!wev->ready) { return in; } if (!c->sendfile) { return ngx_writev_chain(c, in, limit); } /* 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; complete = 0; vec.elts = sfvs; vec.size = sizeof(sendfilevec_t); vec.nalloc = NGX_SENDFILEVECS; vec.pool = c->pool; for ( ;; ) { fd = SFV_FD_SELF; prev = NULL; fprev = 0; sfv = NULL; eintr = 0; sent = 0; prev_send = send; vec.nelts = 0; /* create the sendfilevec and coalesce the neighbouring bufs */ for (cl = in; cl && vec.nelts < IOV_MAX && send < limit; cl = cl->next) { if (ngx_buf_special(cl->buf)) { continue; } if (ngx_buf_in_memory_only(cl->buf)) { fd = SFV_FD_SELF; size = cl->buf->last - cl->buf->pos; if (send + size > limit) { size = limit - send; } if (prev == cl->buf->pos) { sfv->sfv_len += (size_t) size; } else { sfv = ngx_array_push(&vec); if (sfv == NULL) { return NGX_CHAIN_ERROR; } sfv->sfv_fd = SFV_FD_SELF; sfv->sfv_flag = 0; sfv->sfv_off = (off_t) (uintptr_t) cl->buf->pos; sfv->sfv_len = (size_t) size; } prev = cl->buf->pos + (size_t) size; send += size; } else { prev = NULL; size = cl->buf->file_last - cl->buf->file_pos; if (send + size > limit) { size = limit - send; aligned = (cl->buf->file_pos + size + ngx_pagesize - 1) & ~((off_t) ngx_pagesize - 1); if (aligned <= cl->buf->file_last) { size = aligned - cl->buf->file_pos; } } if (fd == cl->buf->file->fd && fprev == cl->buf->file_pos) { sfv->sfv_len += (size_t) size; } else { sfv = ngx_array_push(&vec); if (sfv == NULL) { return NGX_CHAIN_ERROR; } fd = cl->buf->file->fd; sfv->sfv_fd = fd; sfv->sfv_flag = 0; sfv->sfv_off = cl->buf->file_pos; sfv->sfv_len = (size_t) size; } fprev = cl->buf->file_pos + size; send += size; } } n = sendfilev(c->fd, vec.elts, vec.nelts, &sent); if (n == -1) { err = ngx_errno; switch (err) { case NGX_EAGAIN: break; case NGX_EINTR: eintr = 1; break; default: wev->error = 1; ngx_connection_error(c, err, "sendfilev() failed"); return NGX_CHAIN_ERROR; } ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, err, "sendfilev() sent only %uz bytes", sent); } ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, "sendfilev: %z %z", n, sent); if (send - prev_send == (off_t) sent) { complete = 1; } c->sent += sent; for (cl = in; cl; cl = cl->next) { if (ngx_buf_special(cl->buf)) { continue; } if (sent == 0) { break; } size = ngx_buf_size(cl->buf); if ((off_t) sent >= size) { sent = (size_t) ((off_t) sent - size); if (ngx_buf_in_memory(cl->buf)) { cl->buf->pos = cl->buf->last; } if (cl->buf->in_file) { cl->buf->file_pos = cl->buf->file_last; } continue; } if (ngx_buf_in_memory(cl->buf)) { cl->buf->pos += sent; } if (cl->buf->in_file) { cl->buf->file_pos += sent; } break; } if (eintr) { continue; } if (!complete) { wev->ready = 0; return cl; } if (send >= limit || cl == NULL) { return cl; } in = cl; } }
bool memory() const { return ngx_buf_in_memory(get()); }
ngx_buf_t * apr_bucket_to_ngx_buf(apr_bucket *e, ngx_pool_t *pool) { ngx_buf_t *buf, *b; apr_bucket_nginx *n; ngx_uint_t len; u_char *data; if (e->type->is_metadata) { return NULL; } if (e->type == &apr_bucket_type_nginx) { n = e->data; b = n->buf; /* whole buf */ if (e->length == (apr_size_t)ngx_buf_size(b)) { b->last_buf = 0; return b; } buf = ngx_palloc(pool, sizeof(ngx_buf_t)); if (buf == NULL) { return NULL; } ngx_memcpy(buf, b, sizeof(ngx_buf_t)); if (ngx_buf_in_memory(buf)) { buf->start = buf->pos = buf->pos + e->start; buf->end = buf->last = buf->pos + e->length; } else { buf->pos = NULL; buf->file_pos += e->start; buf->file_last = buf->file_pos + e->length; } buf->last_buf = 0; return buf; } if (apr_bucket_read(e, (const char **)&data, &len, APR_BLOCK_READ) != APR_SUCCESS) { return NULL; } buf = ngx_calloc_buf(pool); if (buf == NULL) { return NULL; } if (e->type == &apr_bucket_type_pool) { buf->start = data; } else if (len != 0) { buf->start = ngx_palloc(pool, len); ngx_memcpy(buf->start, data, len); } buf->pos = buf->start; buf->end = buf->last = buf->start + len; buf->temporary = 1; return buf; }
static ngx_int_t ngx_http_replace_body_filter(ngx_http_request_t *r, ngx_chain_t *in) { ngx_int_t rc; ngx_buf_t *b; ngx_str_t *sub; ngx_chain_t *cl, *cur = NULL, *rematch = NULL; ngx_http_replace_ctx_t *ctx; ngx_http_replace_loc_conf_t *rlcf; rlcf = ngx_http_get_module_loc_conf(r, ngx_http_replace_filter_module); ctx = ngx_http_get_module_ctx(r, ngx_http_replace_filter_module); if (ctx == NULL) { return ngx_http_next_body_filter(r, in); } if ((in == NULL && ctx->buf == NULL && ctx->in == NULL && ctx->busy == NULL)) { return ngx_http_next_body_filter(r, in); } if ((ctx->once || ctx->vm_done) && (ctx->buf == NULL || ctx->in == NULL)) { if (ctx->busy) { if (ngx_http_replace_output(r, ctx) == NGX_ERROR) { return NGX_ERROR; } } return ngx_http_next_body_filter(r, in); } /* add the incoming chain to the chain ctx->in */ if (in) { if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) { return NGX_ERROR; } } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http sub filter \"%V\"", &r->uri); while (ctx->in || ctx->buf) { if (ctx->buf == NULL) { cur = ctx->in; ctx->buf = cur->buf; ctx->in = cur->next; ctx->pos = ctx->buf->pos; ctx->special_buf = ngx_buf_special(ctx->buf); ctx->last_buf = (ctx->buf->last_buf || ctx->buf->last_in_chain); dd("=== new incoming buf: size=%d, special=%u, last=%u", (int) ngx_buf_size(ctx->buf), ctx->special_buf, ctx->last_buf); } b = NULL; while (ctx->pos < ctx->buf->last || (ctx->special_buf && ctx->last_buf)) { rc = rlcf->parse_buf(r, ctx, rematch); ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "replace filter parse: %d, %p-%p", rc, ctx->copy_start, ctx->copy_end); if (rc == NGX_ERROR) { return rc; } if (rc == NGX_DECLINED) { if (ctx->pending) { *ctx->last_out = ctx->pending; ctx->last_out = ctx->last_pending; ctx->pending = NULL; ctx->last_pending = &ctx->pending; } if (!ctx->special_buf) { ctx->copy_start = ctx->pos; ctx->copy_end = ctx->buf->last; ctx->pos = ctx->buf->last; } else { ctx->copy_start = NULL; ctx->copy_end = NULL; } sre_reset_pool(ctx->vm_pool); ctx->vm_done = 1; } dd("copy_end - copy_start: %d, special: %u", (int) (ctx->copy_end - ctx->copy_start), ctx->special_buf); if (ctx->copy_start != ctx->copy_end && !ctx->special_buf) { dd("copy: %.*s", (int) (ctx->copy_end - ctx->copy_start), ctx->copy_start); cl = ngx_http_replace_get_free_buf(r->pool, &ctx->free); if (cl == NULL) { return NGX_ERROR; } b = cl->buf; b->memory = 1; b->pos = ctx->copy_start; b->last = ctx->copy_end; *ctx->last_out = cl; ctx->last_out = &cl->next; } if (rc == NGX_AGAIN) { if (ctx->special_buf && ctx->last_buf) { break; } continue; } if (rc == NGX_DECLINED) { break; } /* rc == NGX_OK || rc == NGX_BUSY */ cl = ngx_http_replace_get_free_buf(r->pool, &ctx->free); if (cl == NULL) { return NGX_ERROR; } b = cl->buf; dd("free data buf: %p", b); sub = &ctx->sub[ctx->regex_id]; if (sub->data == NULL || rlcf->parse_buf == ngx_http_replace_capturing_parse) { ngx_http_replace_complex_value_t *cv; if (ngx_http_replace_regex_is_disabled(ctx)) { cv = &rlcf->verbatim; } else { cv = rlcf->multi_replace.elts; cv = &cv[ctx->regex_id]; } if (ngx_http_replace_complex_value(r, ctx->captured, rlcf->ncaps, ctx->ovector, cv, sub) != NGX_OK) { return NGX_ERROR; } /* release ctx->captured */ if (ctx->captured) { dd("release ctx captured: %p", ctx->captured); *ctx->last_captured = ctx->free; ctx->free = ctx->captured; ctx->captured = NULL; ctx->last_captured = &ctx->captured; } } dd("emit replaced value: \"%.*s\"", (int) sub->len, sub->data); if (sub->len) { b->memory = 1; b->pos = sub->data; b->last = sub->data + sub->len; } else { b->sync = 1; } cl->buf = b; cl->next = NULL; *ctx->last_out = cl; ctx->last_out = &cl->next; if (!ctx->once && !ngx_http_replace_regex_is_disabled(ctx)) { uint8_t *once; once = rlcf->multi_once.elts; if (rlcf->regexes.nelts == 1) { ctx->once = once[0]; } else { if (once[ctx->regex_id]) { ngx_http_replace_regex_set_disabled(ctx); if (!rlcf->seen_global && ++ctx->disabled_count == rlcf->regexes.nelts) { ctx->once = 1; } } } } if (rc == NGX_BUSY) { dd("goto rematch"); goto rematch; } if (ctx->special_buf) { break; } continue; } if ((ctx->buf->flush || ctx->last_buf || ngx_buf_in_memory(ctx->buf)) && cur) { if (b == NULL) { cl = ngx_http_replace_get_free_buf(r->pool, &ctx->free); if (cl == NULL) { return NGX_ERROR; } b = cl->buf; b->sync = 1; *ctx->last_out = cl; ctx->last_out = &cl->next; } dd("setting shadow and last buf: %d", (int) ctx->buf->last_buf); b->last_buf = ctx->buf->last_buf; b->last_in_chain = ctx->buf->last_in_chain; b->flush = ctx->buf->flush; b->shadow = ctx->buf; b->recycled = ctx->buf->recycled; } if (!ctx->special_buf) { ctx->stream_pos += ctx->buf->last - ctx->buf->pos; } if (rematch) { rematch->next = ctx->free; ctx->free = rematch; rematch = NULL; } rematch: dd("ctx->rematch: %p", ctx->rematch); if (ctx->rematch == NULL) { ctx->buf = NULL; cur = NULL; } else { if (cur) { ctx->in = cur; cur = NULL; } ctx->buf = ctx->rematch->buf; dd("ctx->buf set to rematch buf %p, len=%d, next=%p", ctx->buf, (int) ngx_buf_size(ctx->buf), ctx->rematch->next); rematch = ctx->rematch; ctx->rematch = rematch->next; ctx->pos = ctx->buf->pos; ctx->special_buf = ngx_buf_special(ctx->buf); ctx->last_buf = (ctx->buf->last_buf || ctx->buf->last_in_chain); ctx->stream_pos = ctx->buf->file_pos; } #if (DDEBUG) /* ngx_http_replace_dump_chain("ctx->pending", &ctx->pending, ctx->last_pending); ngx_http_replace_dump_chain("ctx->pending2", &ctx->pending2, ctx->last_pending2); */ #endif } /* while */ if (ctx->out == NULL && ctx->busy == NULL) { return NGX_OK; } return ngx_http_replace_output(r, ctx); }
// 根据已经实际发送的字节数更新链表 // 已经发送的缓冲区会清空 // 最后返回处理之后的链表指针 ngx_chain_t * ngx_chain_update_sent(ngx_chain_t *in, off_t sent) { off_t size; // sent字节数处理完结束循环 for ( /* void */ ; in; in = in->next) { // 忽略flush、sync、eof等控制用特殊缓冲区 if (ngx_buf_special(in->buf)) { continue; } // 优化,0不做任何处理 if (sent == 0) { break; } // 计算当前链表节点里缓冲区的大小 size = ngx_buf_size(in->buf); // 总发送字节数大于此缓冲区 // 也就是说会有多于一个的链表节点 if (sent >= size) { // sent数减少 sent -= size; // 内存缓冲区,直接清空,指针pos指向last if (ngx_buf_in_memory(in->buf)) { in->buf->pos = in->buf->last; } // 文件缓冲区,直接清空,指针pos指向last if (in->buf->in_file) { in->buf->file_pos = in->buf->file_last; } // 链表指针后移,继续处理下一个节点 continue; } // 此缓冲区的大小多于剩余的sent字节 // 即此缓冲区只发送了一部分,还有一些没有发送出去 // 调整内存缓冲区的指针,剩下的是未发送的 if (ngx_buf_in_memory(in->buf)) { in->buf->pos += (size_t) sent; } // 调整文件缓冲区的指针,剩下的是未发送的 if (in->buf->in_file) { in->buf->file_pos += sent; } // sent字节数已经处理完,结束循环 // 其实也可以令sent=0,然后在循环开头结束 break; } // 最后返回处理之后的链表指针 return in; }
static ngx_int_t ngx_http_chunked_body_filter(ngx_http_request_t *r, ngx_chain_t *in) { u_char *chunk; off_t size; ngx_buf_t *b; ngx_chain_t out, tail, *cl, *tl, **ll; if (in == NULL || !r->chunked || r->header_only) { return ngx_http_next_body_filter(r, in); } out.buf = NULL; ll = &out.next; size = 0; cl = in; for ( ;; ) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http chunk: %d", ngx_buf_size(cl->buf)); size += ngx_buf_size(cl->buf); if (cl->buf->flush || cl->buf->sync || ngx_buf_in_memory(cl->buf) || cl->buf->in_file) { tl = ngx_alloc_chain_link(r->pool); if (tl == NULL) { return NGX_ERROR; } tl->buf = cl->buf; *ll = tl; ll = &tl->next; } if (cl->next == NULL) { break; } cl = cl->next; } if (size) { b = ngx_calloc_buf(r->pool); if (b == NULL) { return NGX_ERROR; } /* the "0000000000000000" is 64-bit hexadimal string */ chunk = ngx_palloc(r->pool, sizeof("0000000000000000" CRLF) - 1); if (chunk == NULL) { return NGX_ERROR; } b->temporary = 1; b->pos = chunk; b->last = ngx_sprintf(chunk, "%xO" CRLF, size); out.buf = b; } if (cl->buf->last_buf) { b = ngx_calloc_buf(r->pool); if (b == NULL) { return NGX_ERROR; } b->memory = 1; b->last_buf = 1; b->pos = (u_char *) CRLF "0" CRLF CRLF; b->last = b->pos + 7; cl->buf->last_buf = 0; if (size == 0) { b->pos += 2; out.buf = b; out.next = NULL; return ngx_http_next_body_filter(r, &out); } } else { if (size == 0) { *ll = NULL; return ngx_http_next_body_filter(r, out.next); } b = ngx_calloc_buf(r->pool); if (b == NULL) { return NGX_ERROR; } b->memory = 1; b->pos = (u_char *) CRLF; b->last = b->pos + 2; } tail.buf = b; tail.next = NULL; *ll = &tail; return ngx_http_next_body_filter(r, &out); }
ngx_chain_t * ngx_freebsd_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) { int rc, flags; u_char *prev; off_t size, send, prev_send, aligned, sent, fprev; size_t header_size, file_size; ngx_uint_t eintr, eagain, complete; ngx_err_t err; ngx_buf_t *file; ngx_array_t header, trailer; ngx_event_t *wev; ngx_chain_t *cl; struct sf_hdtr hdtr; struct iovec *iov, headers[NGX_HEADERS], trailers[NGX_TRAILERS]; 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; eagain = 0; flags = 0; header.elts = headers; header.size = sizeof(struct iovec); header.nalloc = NGX_HEADERS; header.pool = c->pool; trailer.elts = trailers; trailer.size = sizeof(struct iovec); trailer.nalloc = NGX_TRAILERS; trailer.pool = c->pool; for ( ;; ) { file = NULL; file_size = 0; header_size = 0; eintr = 0; complete = 0; prev_send = send; header.nelts = 0; trailer.nelts = 0; /* create the header iovec and coalesce the neighbouring bufs */ prev = NULL; iov = NULL; for (cl = in; cl && send < limit; cl = cl->next) { if (ngx_buf_special(cl->buf)) { continue; } if (!ngx_buf_in_memory_only(cl->buf)) { break; } size = cl->buf->last - cl->buf->pos; if (send + size > limit) { size = limit - send; } if (prev == cl->buf->pos) { iov->iov_len += (size_t) size; } else { if (header.nelts >= IOV_MAX){ break; } iov = ngx_array_push(&header); if (iov == NULL) { return NGX_CHAIN_ERROR; } iov->iov_base = (void *) cl->buf->pos; iov->iov_len = (size_t) size; } prev = cl->buf->pos + (size_t) size; header_size += (size_t) size; send += size; } if (cl && cl->buf->in_file && send < limit) { file = cl->buf; /* coalesce the neighbouring file bufs */ do { size = cl->buf->file_last - cl->buf->file_pos; if (send + size > limit) { size = limit - send; aligned = (cl->buf->file_pos + size + ngx_pagesize - 1) & ~((off_t) ngx_pagesize - 1); if (aligned <= cl->buf->file_last) { size = aligned - cl->buf->file_pos; } } file_size += (size_t) size; send += size; fprev = cl->buf->file_pos + size; cl = cl->next; } while (cl && cl->buf->in_file && send < limit && file->file->fd == cl->buf->file->fd && fprev == cl->buf->file_pos); } if (file) { /* create the trailer iovec and coalesce the neighbouring bufs */ prev = NULL; iov = NULL; while (cl && send < limit) { if (ngx_buf_special(cl->buf)) { cl = cl->next; continue; } if (!ngx_buf_in_memory_only(cl->buf)) { break; } size = cl->buf->last - cl->buf->pos; if (send + size > limit) { size = limit - send; } if (prev == cl->buf->pos) { iov->iov_len += (size_t) size; } else { if (trailer.nelts >= IOV_MAX){ break; } iov = ngx_array_push(&trailer); if (iov == NULL) { return NGX_CHAIN_ERROR; } iov->iov_base = (void *) cl->buf->pos; iov->iov_len = (size_t) size; } prev = cl->buf->pos + (size_t) size; send += size; cl = cl->next; } } if (file) { if (ngx_freebsd_use_tcp_nopush && c->tcp_nopush == NGX_TCP_NOPUSH_UNSET) { if (ngx_tcp_nopush(c->fd) == NGX_ERROR) { err = ngx_socket_errno; /* * there is a tiny chance to be interrupted, however, * we continue a processing without the TCP_NOPUSH */ if (err != NGX_EINTR) { wev->error = 1; (void) ngx_connection_error(c, err, ngx_tcp_nopush_n " failed"); return NGX_CHAIN_ERROR; } } else { c->tcp_nopush = NGX_TCP_NOPUSH_SET; ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "tcp_nopush"); } } /* * sendfile() does unneeded work if sf_hdtr's count is 0, * but corresponding pointer is not NULL */ hdtr.headers = header.nelts ? (struct iovec *) header.elts: NULL; hdtr.hdr_cnt = header.nelts; hdtr.trailers = trailer.nelts ? (struct iovec *) trailer.elts: NULL; hdtr.trl_cnt = trailer.nelts; /* * the "nbytes bug" of the old sendfile() syscall: * http://bugs.freebsd.org/33771 */ if (!ngx_freebsd_sendfile_nbytes_bug) { header_size = 0; } sent = 0; #if (NGX_HAVE_AIO_SENDFILE) flags = c->aio_sendfile ? SF_NODISKIO : 0; #endif rc = sendfile(file->file->fd, c->fd, file->file_pos, file_size + header_size, &hdtr, &sent, flags); if (rc == -1) { err = ngx_errno; switch (err) { case NGX_EAGAIN: eagain = 1; break; case NGX_EINTR: eintr = 1; break; #if (NGX_HAVE_AIO_SENDFILE) case NGX_EBUSY: c->busy_sendfile = file; break; #endif 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); /* * sendfile() in FreeBSD 3.x-4.x may return value >= 0 * on success, although only 0 is documented */ } else if (rc >= 0 && sent == 0) { /* * if rc is OK 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 at %O", file->file->name.data, file->file_pos); return NGX_CHAIN_ERROR; } ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0, "sendfile: %d, @%O %O:%uz", rc, file->file_pos, sent, file_size + header_size); } else { rc = writev(c->fd, header.elts, header.nelts); ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, "writev: %d of %uz", rc, header_size); if (rc == -1) { err = ngx_errno; switch (err) { case NGX_EAGAIN: break; case NGX_EINTR: eintr = 1; break; default: wev->error = 1; ngx_connection_error(c, err, "writev() failed"); return NGX_CHAIN_ERROR; } ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, "writev() not ready"); } sent = rc > 0 ? rc : 0; } if (send - prev_send == sent) { complete = 1; } c->sent += sent; for ( /* void */ ; in; in = in->next) { if (ngx_buf_special(in->buf)) { continue; } if (sent == 0) { break; } size = ngx_buf_size(in->buf); if (sent >= size) { sent -= size; if (ngx_buf_in_memory(in->buf)) { in->buf->pos = in->buf->last; } if (in->buf->in_file) { in->buf->file_pos = in->buf->file_last; } continue; } if (ngx_buf_in_memory(in->buf)) { in->buf->pos += (size_t) sent; } if (in->buf->in_file) { in->buf->file_pos += sent; } break; } #if (NGX_HAVE_AIO_SENDFILE) if (c->busy_sendfile) { return in; } #endif if (eagain) { /* * sendfile() may return EAGAIN, even if it has sent a whole file * part, it indicates that the successive sendfile() call would * return EAGAIN right away and would not send anything. * We use it as a hint. */ wev->ready = 0; return in; } if (eintr) { continue; } if (!complete) { wev->ready = 0; return in; } if (send >= limit || in == NULL) { return in; } } }
static ngx_int_t ngx_http_range_multipart_body(ngx_http_request_t *r, ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in) { ngx_buf_t *b, *buf; ngx_uint_t i; ngx_chain_t *out, *hcl, *rcl, *dcl, **ll; ngx_http_range_t *range; ll = &out; buf = in->buf; range = ctx->ranges.elts; for (i = 0; i < ctx->ranges.nelts; i++) { /* * The boundary header of the range: * CRLF * "--0123456789" CRLF * "Content-Type: image/jpeg" CRLF * "Content-Range: bytes " */ b = ngx_calloc_buf(r->pool); if (b == NULL) { return NGX_ERROR; } b->memory = 1; b->pos = ctx->boundary_header.data; b->last = ctx->boundary_header.data + ctx->boundary_header.len; hcl = ngx_alloc_chain_link(r->pool); if (hcl == NULL) { return NGX_ERROR; } hcl->buf = b; /* "SSSS-EEEE/TTTT" CRLF CRLF */ b = ngx_calloc_buf(r->pool); if (b == NULL) { return NGX_ERROR; } b->temporary = 1; b->pos = range[i].content_range.data; b->last = range[i].content_range.data + range[i].content_range.len; rcl = ngx_alloc_chain_link(r->pool); if (rcl == NULL) { return NGX_ERROR; } rcl->buf = b; /* the range data */ b = ngx_calloc_buf(r->pool); if (b == NULL) { return NGX_ERROR; } b->in_file = buf->in_file; b->temporary = buf->temporary; b->memory = buf->memory; b->mmap = buf->mmap; b->file = buf->file; if (buf->in_file) { b->file_pos = buf->file_pos + range[i].start; b->file_last = buf->file_pos + range[i].end; } if (ngx_buf_in_memory(buf)) { b->pos = buf->pos + (size_t) range[i].start; b->last = buf->pos + (size_t) range[i].end; } dcl = ngx_alloc_chain_link(r->pool); if (dcl == NULL) { return NGX_ERROR; } dcl->buf = b; *ll = hcl; hcl->next = rcl; rcl->next = dcl; ll = &dcl->next; } /* the last boundary CRLF "--0123456789--" CRLF */ b = ngx_calloc_buf(r->pool); if (b == NULL) { return NGX_ERROR; } b->temporary = 1; b->last_buf = 1; b->pos = ngx_pnalloc(r->pool, sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN + sizeof("--" CRLF) - 1); if (b->pos == NULL) { return NGX_ERROR; } b->last = ngx_cpymem(b->pos, ctx->boundary_header.data, sizeof(CRLF "--") - 1 + NGX_ATOMIC_T_LEN); *b->last++ = '-'; *b->last++ = '-'; *b->last++ = CR; *b->last++ = LF; hcl = ngx_alloc_chain_link(r->pool); if (hcl == NULL) { return NGX_ERROR; } hcl->buf = b; hcl->next = NULL; *ll = hcl; return ngx_http_next_body_filter(r, out); }
/* this function's implementation is borrowed from nginx 0.8.20 * and modified a bit to work with subrequests. * Copyrighted (C) by Igor Sysoev */ ngx_int_t ngx_http_echo_request_body_variable(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { u_char *p; size_t len; ngx_buf_t *b; ngx_chain_t *cl; ngx_chain_t *in; if (r->request_body == NULL || r->request_body->bufs == NULL || r->request_body->temp_file) { v->not_found = 1; return NGX_OK; } in = r->request_body->bufs; len = 0; for (cl = in; cl; cl = cl->next) { b = cl->buf; if (!ngx_buf_in_memory(b)) { if (b->in_file) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "variable echo_request_body sees in-file only " "buffers and discard the whole body data"); v->not_found = 1; return NGX_OK; } } else { len += b->last - b->pos; } } p = ngx_pnalloc(r->pool, len); if (p == NULL) { return NGX_ERROR; } v->data = p; for (cl = in; cl; cl = cl->next) { b = cl->buf; if (ngx_buf_in_memory(b)) { p = ngx_copy(p, b->pos, b->last - b->pos); } } if (p - v->data != (ssize_t) len) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "variable echo_request_body: buffer error"); v->not_found = 1; return NGX_OK; } v->len = len; v->valid = 1; v->no_cacheable = 0; v->not_found = 0; return NGX_OK; }
static ngx_int_t ngx_output_chain_add_copy(ngx_pool_t *pool, ngx_chain_t **chain, ngx_chain_t *in) { ngx_chain_t *cl, **ll; #if (NGX_SENDFILE_LIMIT) ngx_buf_t *b, *buf; #endif ll = chain; for (cl = *chain; cl; cl = cl->next) { ll = &cl->next; } while (in) { cl = ngx_alloc_chain_link(pool); if (cl == NULL) { return NGX_ERROR; } #if (NGX_SENDFILE_LIMIT) buf = in->buf; if (buf->in_file && buf->file_pos < NGX_SENDFILE_LIMIT && buf->file_last > NGX_SENDFILE_LIMIT) { /* split a file buf on two bufs by the sendfile limit */ b = ngx_calloc_buf(pool); if (b == NULL) { return NGX_ERROR; } ngx_memcpy(b, buf, sizeof(ngx_buf_t)); if (ngx_buf_in_memory(buf)) { buf->pos += (ssize_t) (NGX_SENDFILE_LIMIT - buf->file_pos); b->last = buf->pos; } buf->file_pos = NGX_SENDFILE_LIMIT; b->file_last = NGX_SENDFILE_LIMIT; cl->buf = b; } else { cl->buf = buf; in = in->next; } #else cl->buf = in->buf; in = in->next; #endif cl->next = NULL; *ll = cl; ll = &cl->next; } return NGX_OK; }
static ngx_int_t ngx_http_sub_body_filter(ngx_http_request_t *r, ngx_chain_t *in) { ngx_int_t rc; ngx_buf_t *b; ngx_chain_t *cl; ngx_http_sub_ctx_t *ctx; ngx_http_sub_loc_conf_t *slcf; ctx = ngx_http_get_module_ctx(r, ngx_http_sub_filter_module); if (ctx == NULL) { return ngx_http_next_body_filter(r, in); } if ((in == NULL && ctx->buf == NULL && ctx->in == NULL && ctx->busy == NULL)) { return ngx_http_next_body_filter(r, in); } if (ctx->once && (ctx->buf == NULL || ctx->in == NULL)) { if (ctx->busy) { if (ngx_http_sub_output(r, ctx) == NGX_ERROR) { return NGX_ERROR; } } return ngx_http_next_body_filter(r, in); } /* add the incoming chain to the chain ctx->in */ if (in) { if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) { return NGX_ERROR; } } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http sub filter \"%V\"", &r->uri); while (ctx->in || ctx->buf) { if (ctx->buf == NULL) { ctx->buf = ctx->in->buf; ctx->in = ctx->in->next; ctx->pos = ctx->buf->pos; } if (ctx->state == sub_start_state) { ctx->copy_start = ctx->pos; ctx->copy_end = ctx->pos; } b = NULL; while (ctx->pos < ctx->buf->last) { ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "saved: \"%V\" state: %d", &ctx->saved, ctx->state); rc = ngx_http_sub_parse(r, ctx); ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "parse: %d, looked: \"%V\" %p-%p", rc, &ctx->looked, ctx->copy_start, ctx->copy_end); if (rc == NGX_ERROR) { return rc; } if (ctx->saved.len) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "saved: \"%V\"", &ctx->saved); cl = ngx_chain_get_free_buf(r->pool, &ctx->free); if (cl == NULL) { return NGX_ERROR; } b = cl->buf; ngx_memzero(b, sizeof(ngx_buf_t)); b->pos = ngx_pnalloc(r->pool, ctx->saved.len); if (b->pos == NULL) { return NGX_ERROR; } ngx_memcpy(b->pos, ctx->saved.data, ctx->saved.len); b->last = b->pos + ctx->saved.len; b->memory = 1; *ctx->last_out = cl; ctx->last_out = &cl->next; ctx->saved.len = 0; } if (ctx->copy_start != ctx->copy_end) { cl = ngx_chain_get_free_buf(r->pool, &ctx->free); if (cl == NULL) { return NGX_ERROR; } b = cl->buf; ngx_memcpy(b, ctx->buf, sizeof(ngx_buf_t)); b->pos = ctx->copy_start; b->last = ctx->copy_end; b->shadow = NULL; b->last_buf = 0; b->last_in_chain = 0; b->recycled = 0; if (b->in_file) { b->file_last = b->file_pos + (b->last - ctx->buf->pos); b->file_pos += b->pos - ctx->buf->pos; } *ctx->last_out = cl; ctx->last_out = &cl->next; } if (ctx->state == sub_start_state) { ctx->copy_start = ctx->pos; ctx->copy_end = ctx->pos; } else { ctx->copy_start = NULL; ctx->copy_end = NULL; } if (ctx->looked.len > (size_t) (ctx->pos - ctx->buf->pos)) { ctx->saved.len = ctx->looked.len - (ctx->pos - ctx->buf->pos); ngx_memcpy(ctx->saved.data, ctx->looked.data, ctx->saved.len); } if (rc == NGX_AGAIN) { continue; } /* rc == NGX_OK */ cl = ngx_chain_get_free_buf(r->pool, &ctx->free); if (cl == NULL) { return NGX_ERROR; } b = cl->buf; ngx_memzero(b, sizeof(ngx_buf_t)); slcf = ngx_http_get_module_loc_conf(r, ngx_http_sub_filter_module); if (ctx->sub.data == NULL) { if (ngx_http_complex_value(r, &slcf->value, &ctx->sub) != NGX_OK) { return NGX_ERROR; } } if (ctx->sub.len) { b->memory = 1; b->pos = ctx->sub.data; b->last = ctx->sub.data + ctx->sub.len; } else { b->sync = 1; } *ctx->last_out = cl; ctx->last_out = &cl->next; ctx->once = slcf->once; continue; } if (ctx->looked.len && (ctx->buf->last_buf || ctx->buf->last_in_chain)) { cl = ngx_chain_get_free_buf(r->pool, &ctx->free); if (cl == NULL) { return NGX_ERROR; } b = cl->buf; ngx_memzero(b, sizeof(ngx_buf_t)); b->pos = ctx->looked.data; b->last = b->pos + ctx->looked.len; b->memory = 1; *ctx->last_out = cl; ctx->last_out = &cl->next; ctx->looked.len = 0; } if (ctx->buf->last_buf || ctx->buf->flush || ctx->buf->sync || ngx_buf_in_memory(ctx->buf)) { if (b == NULL) { cl = ngx_chain_get_free_buf(r->pool, &ctx->free); if (cl == NULL) { return NGX_ERROR; } b = cl->buf; ngx_memzero(b, sizeof(ngx_buf_t)); b->sync = 1; *ctx->last_out = cl; ctx->last_out = &cl->next; } b->last_buf = ctx->buf->last_buf; b->last_in_chain = ctx->buf->last_in_chain; b->flush = ctx->buf->flush; b->shadow = ctx->buf; b->recycled = ctx->buf->recycled; } ctx->buf = NULL; ctx->saved.len = ctx->looked.len; ngx_memcpy(ctx->saved.data, ctx->looked.data, ctx->looked.len); } if (ctx->out == NULL && ctx->busy == NULL) { return NGX_OK; } return ngx_http_sub_output(r, ctx); }
static ngx_int_t ngx_http_chunked_body_filter(ngx_http_request_t *r, ngx_chain_t *in) { u_char *chunk; off_t size; ngx_int_t rc; ngx_buf_t *b; ngx_chain_t *out, *cl, *tl, **ll; ngx_http_chunked_filter_ctx_t *ctx; if (in == NULL || !r->chunked || r->header_only) { return ngx_http_next_body_filter(r, in); } ctx = ngx_http_get_module_ctx(r, ngx_http_chunked_filter_module); out = NULL; ll = &out; size = 0; cl = in; for ( ;; ) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http chunk: %d", ngx_buf_size(cl->buf)); size += ngx_buf_size(cl->buf); if (cl->buf->flush || cl->buf->sync || ngx_buf_in_memory(cl->buf) || cl->buf->in_file) { tl = ngx_alloc_chain_link(r->pool); if (tl == NULL) { return NGX_ERROR; } tl->buf = cl->buf; *ll = tl; ll = &tl->next; } if (cl->next == NULL) { break; } cl = cl->next; } if (size) { tl = ngx_chain_get_free_buf(r->pool, &ctx->free); if (tl == NULL) { return NGX_ERROR; } b = tl->buf; chunk = b->start; if (chunk == NULL) { /* the "0000000000000000" is 64-bit hexadecimal string */ chunk = ngx_palloc(r->pool, sizeof("0000000000000000" CRLF) - 1); if (chunk == NULL) { return NGX_ERROR; } b->start = chunk; b->end = chunk + sizeof("0000000000000000" CRLF) - 1; } b->tag = (ngx_buf_tag_t) &ngx_http_chunked_filter_module; b->memory = 0; b->temporary = 1; b->pos = chunk; b->last = ngx_sprintf(chunk, "%xO" CRLF, size); tl->next = out; out = tl; } if (cl->buf->last_buf) { tl = ngx_chain_get_free_buf(r->pool, &ctx->free); if (tl == NULL) { return NGX_ERROR; } b = tl->buf; b->tag = (ngx_buf_tag_t) &ngx_http_chunked_filter_module; b->temporary = 0; b->memory = 1; b->last_buf = 1; b->pos = (u_char *) CRLF "0" CRLF CRLF; b->last = b->pos + 7; cl->buf->last_buf = 0; *ll = tl; if (size == 0) { b->pos += 2; } } else if (size > 0) { tl = ngx_chain_get_free_buf(r->pool, &ctx->free); if (tl == NULL) { return NGX_ERROR; } b = tl->buf; b->tag = (ngx_buf_tag_t) &ngx_http_chunked_filter_module; b->temporary = 0; b->memory = 1; b->pos = (u_char *) CRLF; b->last = b->pos + 2; *ll = tl; } else { *ll = NULL; } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "send chunked data: %d", size); rc = ngx_http_next_body_filter(r, out); ngx_chain_update_chains(r->pool, &ctx->free, &ctx->busy, &out, (ngx_buf_tag_t) &ngx_http_chunked_filter_module); return rc; }
static ngx_int_t ngx_output_chain_copy_buf(ngx_output_chain_ctx_t *ctx) { off_t size; ssize_t n; ngx_buf_t *src, *dst; ngx_uint_t sendfile; src = ctx->in->buf; dst = ctx->buf; size = ngx_buf_size(src); size = ngx_min(size, dst->end - dst->pos); sendfile = ctx->sendfile & !ctx->directio; #if (NGX_SENDFILE_LIMIT) if (src->in_file && src->file_pos >= NGX_SENDFILE_LIMIT) { sendfile = 0; } #endif if (ngx_buf_in_memory(src)) { ngx_memcpy(dst->pos, src->pos, (size_t) size); src->pos += (size_t) size; dst->last += (size_t) size; if (src->in_file) { if (sendfile) { dst->in_file = 1; dst->file = src->file; dst->file_pos = src->file_pos; dst->file_last = src->file_pos + size; } else { dst->in_file = 0; } src->file_pos += size; } else { dst->in_file = 0; } if (src->pos == src->last) { dst->flush = src->flush; dst->last_buf = src->last_buf; dst->last_in_chain = src->last_in_chain; } } else { #if (NGX_HAVE_ALIGNED_DIRECTIO) if (ctx->unaligned) { if (ngx_directio_off(src->file->fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, ngx_errno, ngx_directio_off_n " \"%s\" failed", src->file->name.data); } } #endif #if (NGX_HAVE_FILE_AIO) if (ctx->aio_handler) { n = ngx_file_aio_read(src->file, dst->pos, (size_t) size, src->file_pos, ctx->pool); if (n == NGX_AGAIN) { ctx->aio_handler(ctx, src->file); return NGX_AGAIN; } } else #endif #if (NGX_THREADS) if (src->file->thread_handler) { n = ngx_thread_read(&ctx->thread_task, src->file, dst->pos, (size_t) size, src->file_pos, ctx->pool); if (n == NGX_AGAIN) { ctx->aio = 1; return NGX_AGAIN; } } else #endif { n = ngx_read_file(src->file, dst->pos, (size_t) size, src->file_pos); } #if (NGX_HAVE_ALIGNED_DIRECTIO) if (ctx->unaligned) { ngx_err_t err; err = ngx_errno; if (ngx_directio_on(src->file->fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, ngx_errno, ngx_directio_on_n " \"%s\" failed", src->file->name.data); } ngx_set_errno(err); ctx->unaligned = 0; } #endif if (n == NGX_ERROR) { return (ngx_int_t) n; } if (n != size) { ngx_log_error(NGX_LOG_ALERT, ctx->pool->log, 0, ngx_read_file_n " read only %z of %O from \"%s\"", n, size, src->file->name.data); return NGX_ERROR; } dst->last += n; if (sendfile) { dst->in_file = 1; dst->file = src->file; dst->file_pos = src->file_pos; dst->file_last = src->file_pos + n; } else { dst->in_file = 0; } src->file_pos += n; if (src->file_pos == src->file_last) { dst->flush = src->flush; dst->last_buf = src->last_buf; dst->last_in_chain = src->last_in_chain; } } return NGX_OK; }
// A slightly simplified version of ngx_http_sub_output static ngx_int_t ngx_http_secure_token_output(ngx_http_request_t *r, ngx_http_secure_token_ctx_t *ctx) { ngx_int_t rc; ngx_buf_t *b; ngx_chain_t *cl; #if 1 b = NULL; for (cl = ctx->out; cl; cl = cl->next) { ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "secure token out: %p %p", cl->buf, cl->buf->pos); if (cl->buf == b) { ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "the same buf was used in secure token"); ngx_debug_point(); return NGX_ERROR; } b = cl->buf; } #endif rc = ngx_http_next_body_filter(r, ctx->out); if (ctx->busy == NULL) { ctx->busy = ctx->out; } else { for (cl = ctx->busy; cl->next; cl = cl->next) { /* void */ } cl->next = ctx->out; } ctx->out = NULL; ctx->last_out = &ctx->out; while (ctx->busy) { cl = ctx->busy; b = cl->buf; if (ngx_buf_size(b) != 0) { break; } if (b->shadow) { b->shadow->pos = b->shadow->last; } ctx->busy = cl->next; if (ngx_buf_in_memory(b) || b->in_file) { /* add data bufs only to the free buf chain */ cl->next = ctx->free; ctx->free = cl; } } return rc; }
static ngx_int_t ngx_http_range_singlepart_body(ngx_http_request_t *r, ngx_http_range_filter_ctx_t *ctx, ngx_chain_t *in) { off_t start, last; ngx_buf_t *buf; ngx_chain_t *out, *cl, **ll; ngx_http_range_t *range; out = NULL; ll = &out; range = ctx->ranges.elts; for (cl = in; cl; cl = cl->next) { buf = cl->buf; start = ctx->offset; last = ctx->offset + ngx_buf_size(buf); ctx->offset = last; ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http range body buf: %O-%O", start, last); if (ngx_buf_special(buf)) { *ll = cl; ll = &cl->next; continue; } if (range->end <= start || range->start >= last) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http range body skip"); if (buf->in_file) { buf->file_pos = buf->file_last; } buf->pos = buf->last; buf->sync = 1; continue; } if (range->start > start) { if (buf->in_file) { buf->file_pos += range->start - start; } if (ngx_buf_in_memory(buf)) { buf->pos += (size_t) (range->start - start); } } if (range->end <= last) { if (buf->in_file) { buf->file_last -= last - range->end; } if (ngx_buf_in_memory(buf)) { buf->last -= (size_t) (last - range->end); } buf->last_buf = 1; *ll = cl; cl->next = NULL; break; } *ll = cl; ll = &cl->next; } if (out == NULL) { return NGX_OK; } return ngx_http_next_body_filter(r, out); }
// the implementation is based on ngx_http_sub_body_filter static ngx_int_t ngx_http_secure_token_body_filter(ngx_http_request_t *r, ngx_chain_t *in) { ngx_http_secure_token_processor_output_t output; ngx_http_secure_token_loc_conf_t *conf; ngx_http_secure_token_ctx_t* ctx; ngx_chain_t* in_copy = NULL; ngx_chain_t* cl; ngx_buf_t* buf; ngx_buf_t* b; ngx_int_t rc; u_char* copy_start; u_char* pos = NULL; ctx = ngx_http_get_module_ctx(r, ngx_http_secure_token_filter_module); if (ctx == NULL) { return ngx_http_next_body_filter(r, in); } if ((in == NULL && ctx->busy == NULL)) { return ngx_http_next_body_filter(r, in); } conf = ngx_http_get_module_loc_conf(r, ngx_http_secure_token_filter_module); /* add the incoming chain to the chain in_copy */ if (in) { if (ngx_chain_add_copy(r->pool, &in_copy, in) != NGX_OK) { return NGX_ERROR; } } ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http secure token filter \"%V\"", &r->uri); buf = NULL; while (in_copy || buf) { if (buf == NULL) { buf = in_copy->buf; in_copy = in_copy->next; pos = buf->pos; } b = NULL; while (pos < buf->last) { copy_start = pos; output.copy_input = 1; output.output_buffer.len = 0; output.token_index = -1; rc = ctx->process( r, &conf->processor_conf, ctx->processor_params, &pos, buf->last, (u_char*)ctx + ctx->processor_context_offset, &output); ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "process: %d, %p-%p", rc, copy_start, pos); if (rc == NGX_ERROR) { return rc; } if (output.copy_input && copy_start != pos) { cl = ngx_chain_get_free_buf(r->pool, &ctx->free); if (cl == NULL) { return NGX_ERROR; } b = cl->buf; ngx_memcpy(b, buf, sizeof(ngx_buf_t)); b->pos = copy_start; b->last = pos; b->shadow = NULL; b->last_buf = 0; b->last_in_chain = 0; b->recycled = 0; if (b->in_file) { b->file_last = b->file_pos + (b->last - buf->pos); b->file_pos += b->pos - buf->pos; } *ctx->last_out = cl; ctx->last_out = &cl->next; } if (output.output_buffer.len != 0) { cl = ngx_chain_get_free_buf(r->pool, &ctx->free); if (cl == NULL) { return NGX_ERROR; } b = cl->buf; ngx_memzero(b, sizeof(ngx_buf_t)); b->memory = 1; b->pos = output.output_buffer.data; b->last = output.output_buffer.data + output.output_buffer.len; *ctx->last_out = cl; ctx->last_out = &cl->next; } if (output.token_index >= 0 && ctx->token.len != 0) { cl = ngx_chain_get_free_buf(r->pool, &ctx->free); if (cl == NULL) { return NGX_ERROR; } b = cl->buf; rc = ngx_http_secure_token_get_token_buffer(r, ctx, b, output.token_index); if (rc != NGX_OK) { return rc; } *ctx->last_out = cl; ctx->last_out = &cl->next; } continue; } if (buf->last_buf || buf->flush || buf->sync || ngx_buf_in_memory(buf)) { if (b == NULL) { cl = ngx_chain_get_free_buf(r->pool, &ctx->free); if (cl == NULL) { return NGX_ERROR; } b = cl->buf; ngx_memzero(b, sizeof(ngx_buf_t)); b->sync = 1; *ctx->last_out = cl; ctx->last_out = &cl->next; } b->last_buf = buf->last_buf; b->last_in_chain = buf->last_in_chain; b->flush = buf->flush; b->shadow = buf; b->recycled = buf->recycled; } buf = NULL; } if (ctx->out == NULL && ctx->busy == NULL) { return NGX_OK; } return ngx_http_secure_token_output(r, ctx); }
ngx_chain_t * ngx_linux_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) { int rc, tcp_nodelay; off_t size, send, prev_send, aligned, sent, fprev; u_char *prev; size_t file_size; ngx_err_t err; ngx_buf_t *file; ngx_uint_t eintr, complete; ngx_array_t header; ngx_event_t *wev; ngx_chain_t *cl; struct iovec *iov, headers[NGX_HEADERS]; #if (NGX_HAVE_SENDFILE64) off_t offset; #else int32_t offset; #endif wev = c->write; if (!wev->ready) { return in; } /* the maximum limit size is 2G-1 - the page size */ if (limit == 0 || limit > (off_t) (NGX_SENDFILE_LIMIT - ngx_pagesize)) { limit = NGX_SENDFILE_LIMIT - ngx_pagesize; } send = 0; header.elts = headers; header.size = sizeof(struct iovec); header.nalloc = NGX_HEADERS; header.pool = c->pool; for ( ;; ) { file = NULL; file_size = 0; eintr = 0; complete = 0; prev_send = send; header.nelts = 0; prev = NULL; iov = NULL; /* create the iovec and coalesce the neighbouring bufs */ for (cl = in; cl && header.nelts < IOV_MAX && send < limit; cl = cl->next) { if (ngx_buf_special(cl->buf)) { continue; } #if 1 if (!ngx_buf_in_memory(cl->buf) && !cl->buf->in_file) { ngx_log_error(NGX_LOG_ALERT, c->log, 0, "zero size buf in sendfile " "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; } #endif if (!ngx_buf_in_memory_only(cl->buf)) { break; } size = cl->buf->last - cl->buf->pos; if (send + size > limit) { size = limit - send; } if (prev == cl->buf->pos) { iov->iov_len += (size_t) size; } else { iov = ngx_array_push(&header); if (iov == NULL) { return NGX_CHAIN_ERROR; } iov->iov_base = (void *) cl->buf->pos; iov->iov_len = (size_t) size; } prev = cl->buf->pos + (size_t) size; send += size; } /* set TCP_CORK if there is a header before a file */ if (c->tcp_nopush == NGX_TCP_NOPUSH_UNSET && header.nelts != 0 && cl && cl->buf->in_file) { /* the TCP_CORK and TCP_NODELAY are mutually exclusive */ if (c->tcp_nodelay == NGX_TCP_NODELAY_SET) { tcp_nodelay = 0; if (setsockopt(c->fd, IPPROTO_TCP, TCP_NODELAY, (const void *) &tcp_nodelay, sizeof(int)) == -1) { err = ngx_errno; /* * there is a tiny chance to be interrupted, however, * we continue a processing with the TCP_NODELAY * and without the TCP_CORK */ if (err != NGX_EINTR) { wev->error = 1; ngx_connection_error(c, err, "setsockopt(TCP_NODELAY) failed"); return NGX_CHAIN_ERROR; } } else { c->tcp_nodelay = NGX_TCP_NODELAY_UNSET; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "no tcp_nodelay"); } } if (c->tcp_nodelay == NGX_TCP_NODELAY_UNSET) { if (ngx_tcp_nopush(c->fd) == NGX_ERROR) { err = ngx_errno; /* * there is a tiny chance to be interrupted, however, * we continue a processing without the TCP_CORK */ if (err != NGX_EINTR) { wev->error = 1; ngx_connection_error(c, err, ngx_tcp_nopush_n " failed"); return NGX_CHAIN_ERROR; } } else { c->tcp_nopush = NGX_TCP_NOPUSH_SET; ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0, "tcp_nopush"); } } } /* get the file buf */ if (header.nelts == 0 && cl && cl->buf->in_file && send < limit) { file = cl->buf; /* coalesce the neighbouring file bufs */ do { size = cl->buf->file_last - cl->buf->file_pos; if (send + size > limit) { size = limit - send; aligned = (cl->buf->file_pos + size + ngx_pagesize - 1) & ~((off_t) ngx_pagesize - 1); if (aligned <= cl->buf->file_last) { size = aligned - cl->buf->file_pos; } } file_size += (size_t) size; send += size; fprev = cl->buf->file_pos + size; cl = cl->next; } while (cl && cl->buf->in_file && send < limit && file->file->fd == cl->buf->file->fd && fprev == cl->buf->file_pos); } if (file) { #if 1 if (file_size == 0) { ngx_debug_point(); return NGX_CHAIN_ERROR; } #endif #if (NGX_HAVE_SENDFILE64) offset = file->file_pos; #else offset = (int32_t) file->file_pos; #endif ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, "sendfile: @%O %uz", file->file_pos, file_size); rc = sendfile(c->fd, file->file->fd, &offset, file_size); if (rc == -1) { err = ngx_errno; switch (err) { case NGX_EAGAIN: break; case NGX_EINTR: eintr = 1; break; default: wev->error = 1; ngx_connection_error(c, err, "sendfile() failed"); return NGX_CHAIN_ERROR; } ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, "sendfile() is not ready"); } sent = rc > 0 ? rc : 0; ngx_log_debug4(NGX_LOG_DEBUG_EVENT, c->log, 0, "sendfile: %d, @%O %O:%uz", rc, file->file_pos, sent, file_size); } else { rc = writev(c->fd, header.elts, header.nelts); if (rc == -1) { err = ngx_errno; switch (err) { case NGX_EAGAIN: break; case NGX_EINTR: eintr = 1; break; default: wev->error = 1; ngx_connection_error(c, err, "writev() failed"); return NGX_CHAIN_ERROR; } ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, "writev() not ready"); } sent = rc > 0 ? rc : 0; ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "writev: %O", sent); } if (send - prev_send == sent) { complete = 1; } c->sent += sent; for (cl = in; cl; cl = cl->next) { if (ngx_buf_special(cl->buf)) { continue; } if (sent == 0) { break; } size = ngx_buf_size(cl->buf); if (sent >= size) { sent -= size; if (ngx_buf_in_memory(cl->buf)) { cl->buf->pos = cl->buf->last; } if (cl->buf->in_file) { cl->buf->file_pos = cl->buf->file_last; } continue; } if (ngx_buf_in_memory(cl->buf)) { cl->buf->pos += (size_t) sent; } if (cl->buf->in_file) { cl->buf->file_pos += sent; } break; } if (eintr) { continue; } if (!complete) { wev->ready = 0; return cl; } if (send >= limit || cl == NULL) { return cl; } in = cl; } }
ngx_chain_t * ngx_writev_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) { u_char *prev; ssize_t n, size, sent; off_t send, prev_send; ngx_uint_t eintr, complete; ngx_err_t err; ngx_array_t vec; ngx_chain_t *cl; ngx_event_t *wev; struct iovec *iov, iovs[NGX_IOVS]; 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.elts = iovs; vec.size = sizeof(struct iovec); vec.nalloc = NGX_IOVS; vec.pool = c->pool; for ( ;; ) { prev = NULL; iov = NULL; eintr = 0; complete = 0; prev_send = send; vec.nelts = 0; /* create the iovec and coalesce the neighbouring bufs */ for (cl = in; cl && send < limit; cl = cl->next) { if (ngx_buf_special(cl->buf)) { continue; } #if 1 if (!ngx_buf_in_memory(cl->buf)) { ngx_debug_point(); } #endif size = cl->buf->last - cl->buf->pos; if (send + size > limit) { size = (ssize_t) (limit - send); } if (prev == cl->buf->pos) { iov->iov_len += size; } else { if (vec.nelts >= IOV_MAX) { break; } iov = ngx_array_push(&vec); if (iov == NULL) { return NGX_CHAIN_ERROR; } iov->iov_base = (void *) cl->buf->pos; iov->iov_len = size; } prev = cl->buf->pos + size; send += size; } n = writev(c->fd, vec.elts, vec.nelts); if (n == -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, "writev() failed (in writev_chain.c)"); return NGX_CHAIN_ERROR; } ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, err, "writev() not ready"); } sent = n > 0 ? n : 0; ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "writev: %z", sent); if (send - prev_send == sent) { complete = 1; } c->sent += sent; for (cl = in; cl; cl = cl->next) { if (ngx_buf_special(cl->buf)) { continue; } if (sent == 0) { break; } size = cl->buf->last - cl->buf->pos; if (sent >= size) { sent -= size; cl->buf->pos = cl->buf->last; continue; } cl->buf->pos += sent; break; } if (eintr) { continue; } if (!complete) { wev->ready = 0; return cl; } if (send >= limit || cl == NULL) { return cl; } in = cl; } }
static ngx_int_t ngx_http_srcache_body_filter(ngx_http_request_t *r, ngx_chain_t *in) { ngx_http_srcache_ctx_t *ctx, *pr_ctx; ngx_int_t rc; ngx_str_t skip; ngx_chain_t *cl; ngx_http_srcache_loc_conf_t *slcf; size_t len; unsigned last; dd_enter(); if (in == NULL) { return ngx_http_srcache_next_body_filter(r, NULL); } ctx = ngx_http_get_module_ctx(r, ngx_http_srcache_filter_module); if (ctx == NULL || ctx->from_cache || ctx->store_skip) { dd("bypass: %.*s", (int) r->uri.len, r->uri.data); return ngx_http_srcache_next_body_filter(r, in); } if (ctx->ignore_body || ctx->in_store_subrequest/* || ctx->fetch_error */) { dd("ignore body: ignore body %d, in store sr %d", (int) ctx->ignore_body, (int) ctx->in_store_subrequest); ngx_http_srcache_discard_bufs(r->pool, in); return NGX_OK; } if (ctx->in_fetch_subrequest) { if (ctx->parsing_cached_headers) { /* parse the cached response's headers and * set r->parent->headers_out */ if (ctx->process_header == NULL) { dd("restore parent request header"); ctx->process_header = ngx_http_srcache_process_status_line; r->state = 0; /* sw_start */ } for (cl = in; cl; cl = cl->next) { if (ngx_buf_in_memory(cl->buf)) { dd("old pos %p, last %p", cl->buf->pos, cl->buf->last); rc = ctx->process_header(r, cl->buf); if (rc == NGX_AGAIN) { dd("AGAIN/OK: new pos %p, last %p", cl->buf->pos, cl->buf->last); continue; } if (rc == NGX_ERROR) { r->state = 0; /* sw_start */ ctx->parsing_cached_headers = 0; ctx->ignore_body = 1; ngx_http_srcache_discard_bufs(r->pool, cl); pr_ctx = ngx_http_get_module_ctx(r->parent, ngx_http_srcache_filter_module); if (pr_ctx == NULL) { return NGX_ERROR; } pr_ctx->from_cache = 0; return NGX_OK; } /* rc == NGX_OK */ dd("OK: new pos %p, last %p", cl->buf->pos, cl->buf->last); dd("buf left: %.*s", (int) (cl->buf->last - cl->buf->pos), cl->buf->pos); ctx->parsing_cached_headers = 0; break; } } if (cl == NULL) { return NGX_OK; } if (cl->buf->pos == cl->buf->last) { cl = cl->next; } if (cl == NULL) { return NGX_OK; } in = cl; } dd("save the cached response body for parent"); pr_ctx = ngx_http_get_module_ctx(r->parent, ngx_http_srcache_filter_module); if (pr_ctx == NULL) { return NGX_ERROR; } rc = ngx_http_srcache_add_copy_chain(r->pool, &pr_ctx->body_from_cache, in, &last); if (rc != NGX_OK) { return NGX_ERROR; } if (last) { ctx->seen_subreq_eof = 1; } ngx_http_srcache_discard_bufs(r->pool, in); return NGX_OK; } if (ctx->store_response) { dd("storing the response: %p", in); if (ctx->response_length == 0) { /* store the response header to ctx->body_to_cache */ if (ngx_http_srcache_store_response_header(r, ctx) == NGX_ERROR) { return NGX_ERROR; } if (ngx_http_srcache_next_header_filter(r) == NGX_ERROR) { return NGX_ERROR; } } for (cl = in; cl; cl = cl->next) { if (ngx_buf_in_memory(cl->buf)) { len = ngx_buf_size(cl->buf); ctx->response_length += len; ctx->response_body_length += len; } } slcf = ngx_http_get_module_loc_conf(r, ngx_http_srcache_filter_module); if (slcf->store_max_size != 0 && ctx->response_length > slcf->store_max_size) { ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "srcache_store bypassed because response body " "exceeded maximum size: %z (limit is: %z)", ctx->response_length, slcf->store_max_size); ctx->store_response = 0; goto done; } rc = ngx_http_srcache_add_copy_chain(r->pool, &ctx->body_to_cache, in, &last); if (rc != NGX_OK) { ctx->store_response = 0; goto done; } if (last && r == r->main) { #if 1 if (r->headers_out.content_length_n > (off_t) ctx->response_body_length) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "srcache_store: skipped because response body " "truncated: %O > %uz", r->headers_out.content_length_n, ctx->response_body_length); ctx->store_response = 0; goto done; } if (r->headers_out.status >= NGX_HTTP_SPECIAL_RESPONSE && r->headers_out.status != ctx->http_status) { /* data truncation or body receive timeout */ ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "srcache_store: skipped due to new error status " "code %ui (old: %ui)", r->headers_out.status, ctx->http_status); ctx->store_response = 0; goto done; } #endif if (slcf->store_skip != NULL && ngx_http_complex_value(r, slcf->store_skip, &skip) == NGX_OK && skip.len && (skip.len != 1 || skip.data[0] != '0')) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "srcache_store skipped due to the true value in " "srcache_store_skip: \"%V\"", &skip); ctx->store_response = 0; goto done; } rc = ngx_http_srcache_store_subrequest(r, ctx); if (rc != NGX_OK) { ctx->store_response = 0; goto done; } } } else { dd("NO store response"); } done: return ngx_http_srcache_next_body_filter(r, in); }
static ngx_int_t ngx_http_v2_data_frame_handler(ngx_http_v2_connection_t *h2c, ngx_http_v2_out_frame_t *frame) { ngx_buf_t *buf; ngx_chain_t *cl, *ln; ngx_http_v2_stream_t *stream; stream = frame->stream; cl = frame->first; if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_module) { if (cl->buf->pos != cl->buf->last) { ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, "http2:%ui DATA frame %p was sent partially", stream->node->id, frame); return NGX_AGAIN; } ln = cl->next; cl->next = stream->free_frame_headers; stream->free_frame_headers = cl; if (cl == frame->last) { goto done; } cl = ln; } for ( ;; ) { if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_filter_get_shadow) { buf = cl->buf->shadow; if (ngx_buf_in_memory(buf)) { buf->pos = cl->buf->pos; } if (buf->in_file) { buf->file_pos = cl->buf->file_pos; } } if (ngx_buf_size(cl->buf) != 0) { if (cl != frame->first) { frame->first = cl; ngx_http_v2_handle_stream(h2c, stream); } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, "http2:%ui DATA frame %p was sent partially", stream->node->id, frame); return NGX_AGAIN; } ln = cl->next; if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_filter_get_shadow) { cl->next = stream->free_bufs; stream->free_bufs = cl; } else { ngx_free_chain(stream->request->pool, cl); } if (cl == frame->last) { goto done; } cl = ln; } done: ngx_log_debug2(NGX_LOG_DEBUG_HTTP, h2c->connection->log, 0, "http2:%ui DATA frame %p was sent", stream->node->id, frame); stream->request->header_size += NGX_HTTP_V2_FRAME_HEADER_SIZE; ngx_http_v2_handle_frame(stream, frame); ngx_http_v2_handle_stream(h2c, stream); return NGX_OK; }
int ngx_http_lua_body_filter_param_set(lua_State *L, ngx_http_request_t *r, ngx_http_lua_ctx_t *ctx) { int type; int idx; int found; u_char *data; size_t size; unsigned last; unsigned flush = 0; ngx_buf_t *b; ngx_chain_t *cl; ngx_chain_t *in; idx = luaL_checkint(L, 2); dd("index: %d", idx); if (idx != 1 && idx != 2) { return luaL_error(L, "bad index: %d", idx); } if (idx == 2) { /* overwriting the eof flag */ last = lua_toboolean(L, 3); lua_getglobal(L, ngx_http_lua_chain_key); in = lua_touserdata(L, -1); lua_pop(L, 1); if (last) { ctx->seen_last_in_filter = 1; /* the "in" chain cannot be NULL and we set the "last_buf" or * "last_in_chain" flag in the last buf of "in" */ for (cl = in; cl; cl = cl->next) { if (cl->next == NULL) { if (r == r->main) { cl->buf->last_buf = 1; } else { cl->buf->last_in_chain = 1; } break; } } } else { /* last == 0 */ found = 0; for (cl = in; cl; cl = cl->next) { b = cl->buf; if (b->last_buf) { b->last_buf = 0; found = 1; } if (b->last_in_chain) { b->last_in_chain = 0; found = 1; } if (found && b->last == b->pos && !ngx_buf_in_memory(b)) { /* make it a special sync buf to make * ngx_http_write_filter_module happy. */ b->sync = 1; } } ctx->seen_last_in_filter = 0; } return 0; } /* idx == 1, overwriting the chunk data */ type = lua_type(L, 3); switch (type) { case LUA_TSTRING: case LUA_TNUMBER: data = (u_char *) lua_tolstring(L, 3, &size); break; case LUA_TNIL: /* discard the buffers */ lua_getglobal(L, ngx_http_lua_chain_key); /* key val */ in = lua_touserdata(L, -1); lua_pop(L, 1); last = 0; for (cl = in; cl; cl = cl->next) { b = cl->buf; if (b->flush) { flush = 1; } if (b->last_in_chain || b->last_buf) { last = 1; } dd("mark the buf as consumed: %d", (int) ngx_buf_size(b)); b->pos = b->last; } /* cl == NULL */ goto done; case LUA_TTABLE: size = ngx_http_lua_calc_strlen_in_table(L, 3 /* index */, 3 /* arg */, 1 /* strict */); data = NULL; break; default: return luaL_error(L, "bad chunk data type: %s", lua_typename(L, type)); } lua_getglobal(L, ngx_http_lua_chain_key); in = lua_touserdata(L, -1); lua_pop(L, 1); last = 0; for (cl = in; cl; cl = cl->next) { b = cl->buf; if (b->flush) { flush = 1; } if (b->last_buf || b->last_in_chain) { last = 1; } dd("mark the buf as consumed: %d", (int) ngx_buf_size(cl->buf)); cl->buf->pos = cl->buf->last; } /* cl == NULL */ if (size == 0) { goto done; } cl = ngx_http_lua_chain_get_free_buf(r->connection->log, r->pool, &ctx->free_bufs, size); if (cl == NULL) { return luaL_error(L, "no memory"); } if (type == LUA_TTABLE) { cl->buf->last = ngx_http_lua_copy_str_in_table(L, 3, cl->buf->last); } else { cl->buf->last = ngx_copy(cl->buf->pos, data, size); } done: if (last || flush) { if (cl == NULL) { cl = ngx_http_lua_chain_get_free_buf(r->connection->log, r->pool, &ctx->free_bufs, 0); if (cl == NULL) { return luaL_error(L, "no memory"); } } if (last) { ctx->seen_last_in_filter = 1; if (r == r->main) { cl->buf->last_buf = 1; } else { cl->buf->last_in_chain = 1; } } if (flush) { cl->buf->flush = 1; } } lua_pushlightuserdata(L, cl); lua_setglobal(L, ngx_http_lua_chain_key); return 0; }
static ngx_chain_t * ngx_http_v2_send_chain(ngx_connection_t *fc, ngx_chain_t *in, off_t limit) { off_t size, offset; size_t rest, frame_size; ngx_chain_t *cl, *out, **ln; ngx_http_request_t *r; ngx_http_v2_stream_t *stream; ngx_http_v2_loc_conf_t *h2lcf; ngx_http_v2_out_frame_t *frame; ngx_http_v2_connection_t *h2c; r = fc->data; stream = r->stream; #if (NGX_SUPPRESS_WARN) size = 0; #endif while (in) { size = ngx_buf_size(in->buf); if (size || in->buf->last_buf) { break; } in = in->next; } if (in == NULL) { if (stream->queued) { fc->write->active = 1; fc->write->ready = 0; } else { fc->buffered &= ~NGX_HTTP_V2_BUFFERED; } return NULL; } h2c = stream->connection; if (size && ngx_http_v2_flow_control(h2c, stream) == NGX_DECLINED) { fc->write->active = 1; fc->write->ready = 0; return in; } if (in->buf->tag == (ngx_buf_tag_t) &ngx_http_v2_filter_get_shadow) { cl = ngx_alloc_chain_link(r->pool); if (cl == NULL) { return NGX_CHAIN_ERROR; } cl->buf = in->buf; in->buf = cl->buf->shadow; offset = ngx_buf_in_memory(in->buf) ? (cl->buf->pos - in->buf->pos) : (cl->buf->file_pos - in->buf->file_pos); cl->next = stream->free_bufs; stream->free_bufs = cl; } else { offset = 0; } if (limit == 0 || limit > (off_t) h2c->send_window) { limit = h2c->send_window; } if (limit > stream->send_window) { limit = (stream->send_window > 0) ? stream->send_window : 0; } h2lcf = ngx_http_get_module_loc_conf(r, ngx_http_v2_module); frame_size = (h2lcf->chunk_size < h2c->frame_size) ? h2lcf->chunk_size : h2c->frame_size; #if (NGX_SUPPRESS_WARN) cl = NULL; #endif for ( ;; ) { if ((off_t) frame_size > limit) { frame_size = (size_t) limit; } ln = &out; rest = frame_size; while ((off_t) rest >= size) { if (offset) { cl = ngx_http_v2_filter_get_shadow(stream, in->buf, offset, size); if (cl == NULL) { return NGX_CHAIN_ERROR; } offset = 0; } else { cl = ngx_alloc_chain_link(r->pool); if (cl == NULL) { return NGX_CHAIN_ERROR; } cl->buf = in->buf; } *ln = cl; ln = &cl->next; rest -= (size_t) size; in = in->next; if (in == NULL) { frame_size -= rest; rest = 0; break; } size = ngx_buf_size(in->buf); } if (rest) { cl = ngx_http_v2_filter_get_shadow(stream, in->buf, offset, rest); if (cl == NULL) { return NGX_CHAIN_ERROR; } cl->buf->flush = 0; cl->buf->last_buf = 0; *ln = cl; offset += rest; size -= rest; } frame = ngx_http_v2_filter_get_data_frame(stream, frame_size, out, cl); if (frame == NULL) { return NGX_CHAIN_ERROR; } ngx_http_v2_queue_frame(h2c, frame); h2c->send_window -= frame_size; stream->send_window -= frame_size; stream->queued++; if (in == NULL) { break; } limit -= frame_size; if (limit == 0) { break; } } if (offset) { cl = ngx_http_v2_filter_get_shadow(stream, in->buf, offset, size); if (cl == NULL) { return NGX_CHAIN_ERROR; } in->buf = cl->buf; ngx_free_chain(r->pool, cl); } if (ngx_http_v2_filter_send(fc, stream) == NGX_ERROR) { return NGX_CHAIN_ERROR; } if (in && ngx_http_v2_flow_control(h2c, stream) == NGX_DECLINED) { fc->write->active = 1; fc->write->ready = 0; } return in; }