static ngx_int_t ngx_http_spdy_v3_body_filter(ngx_http_request_t *r, ngx_chain_t *in) { off_t total, size; ngx_buf_t *b; ngx_chain_t *cl, *tl, *ll, *out, **ln; ngx_http_spdy_stream_t *stream; ngx_http_spdy_srv_conf_t *sscf; ngx_http_spdy_out_frame_t *frame; total = 0; size = 0; out = NULL; stream = r->spdy_stream; if (stream == NULL) { return ngx_http_next_body_filter(r, in); } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "spdy body filter \"%V?%V\"", &r->uri, &r->args); if (in == NULL || r->header_only) { if (stream->pending && (stream->send_window_size > 0)) { goto output; } if (stream->waiting) { return NGX_AGAIN; } if (!stream->pending) { r->connection->buffered &= ~NGX_SPDY_WRITE_BUFFERED; } return NGX_OK; } ln = &out; ll = in; for ( ;; ) { b = ll->buf; #if 1 if (ngx_buf_size(b) == 0 && !ngx_buf_special(b)) { ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "zero size buf in spdy body filter " "t:%d r:%d f:%d %p %p-%p %p %O-%O", b->temporary, b->recycled, b->in_file, b->start, b->pos, b->last, b->file, b->file_pos, b->file_last); ngx_debug_point(); return NGX_ERROR; } #endif cl = ngx_alloc_chain_link(r->pool); if (cl == NULL) { return NGX_ERROR; } cl->next = NULL; size += ngx_buf_size(b); cl->buf = b; *ln = cl; ln = &cl->next; if (ll->next == NULL) { break; } ll = ll->next; } output: total = size; if (stream->pending) { for (tl = stream->pending; ;tl = tl->next) { total += ngx_buf_size(tl->buf); if (tl->next == NULL) { break; } } tl->next = out; } else { stream->pending = out; } ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "spdy id=%ui, send_window_size=%i, total=%O", stream->id, stream->send_window_size, total); if (stream->send_window_size == 0) { ngx_log_error(NGX_LOG_NOTICE, r->connection->log, 0, "zero send windows size"); r->connection->buffered |= NGX_SPDY_WRITE_BUFFERED; return NGX_AGAIN; } else if (total > stream->send_window_size) { out = ngx_http_spdy_split_chain(stream, stream->send_window_size); if (out == NULL) { return NGX_ERROR; } r->connection->buffered |= NGX_SPDY_WRITE_BUFFERED; stream->send_window_size = 0; } else { out = stream->pending; stream->pending = NULL; stream->send_window_size -= total; } size = 0; cl = out; for (;;) { size += ngx_buf_size(cl->buf); ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "spdy, cl=%p, cl->buf=%p, size=%uz, fin:%d", cl, cl->buf, ngx_buf_size(cl->buf), cl->buf->last_buf); if (cl->next == NULL) { b = cl->buf; break; } cl = cl->next; } frame = ngx_http_spdy_filter_get_data_frame(stream, (size_t) size, b->last_buf, out, cl); if (frame == NULL) { return NGX_ERROR; } ngx_http_spdy_queue_frame(stream->connection, frame); stream->waiting++; r->main->blocked++; sscf = ngx_http_get_module_srv_conf(stream->connection->http_connection->conf_ctx, ngx_http_spdy_module); if (!sscf->flow_control) { stream->send_window_size = NGX_SPDY_TMP_WINDOW_SIZE; } return ngx_http_spdy_filter_send(r->connection, stream); }
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); }
static ngx_int_t ngx_http_trim_body_filter(ngx_http_request_t *r, ngx_chain_t *in) { ngx_int_t rc; ngx_chain_t *cl, *ln, *out, **ll; ngx_http_trim_ctx_t *ctx; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http trim filter"); ctx = ngx_http_get_module_ctx(r, ngx_http_trim_filter_module); if (ctx == NULL) { return ngx_http_next_body_filter(r, in); } if (in == NULL) { return ngx_http_next_body_filter(r, in); } ctx->in = NULL; if (ngx_chain_add_copy(r->pool, &ctx->in, in) != NGX_OK) { return NGX_ERROR; } out = NULL; ll = &out; for (ln = ctx->in; ln; ln = ln->next) { ngx_http_trim_parse(r, ln->buf, ctx); if (ctx->saved) { cl = ngx_chain_get_free_buf(r->pool, &ctx->free); if (cl == NULL) { return NGX_ERROR; } cl->buf->tag = (ngx_buf_tag_t) &ngx_http_trim_filter_module; cl->buf->memory = 1; if (ctx->saved > 0) { cl->buf->pos = ngx_http_trim_saved_html.data; cl->buf->last = cl->buf->pos + ctx->saved; } else if (ctx->saved == NGX_HTTP_TRIM_SAVE_SLASH) { cl->buf->pos = ngx_http_trim_saved_jscss.data; cl->buf->last = cl->buf->pos + 1; } else if (ctx->saved == NGX_HTTP_TRIM_SAVE_JSCSS) { cl->buf->pos = ngx_http_trim_saved_jscss.data; cl->buf->last = cl->buf->pos + ngx_http_trim_saved_jscss.len; } else if (ctx->saved == NGX_HTTP_TRIM_SAVE_HACKCSS) { cl->buf->pos = ngx_http_trim_saved_css_hack.data; cl->buf->last = cl->buf->pos + ngx_http_trim_saved_css_hack.len; } else if (ctx->saved == NGX_HTTP_TRIM_SAVE_JAVASCRIPT) { cl->buf->pos = ngx_http_trim_script.data; cl->buf->last = cl->buf->pos + ngx_http_trim_script.len - 1; } *ll = cl; ll = &cl->next; ctx->saved = 0; } if(ln->buf->in_file && (ln->buf->file_last - ln->buf->file_pos) != (off_t) (ln->buf->last - ln->buf->pos)) { ln->buf->in_file = 0; } if (ngx_buf_size(ln->buf) == 0) { if (ln->buf->last_buf) { cl = ngx_chain_get_free_buf(r->pool, &ctx->free); if (cl == NULL) { return NGX_ERROR; } ngx_memzero(cl->buf, sizeof(ngx_buf_t)); cl->buf->tag = (ngx_buf_tag_t) &ngx_http_trim_filter_module; cl->buf->last_buf = 1; *ll = cl; ll = &cl->next; } else { if (ln->next == NULL) { *ll = NULL; } } } else { *ll = ln; ll = &ln->next; } } if (out == NULL) { return NGX_OK; } 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_trim_filter_module); return rc; }
static ngx_int_t ngx_http_modsecurity_body_filter(ngx_http_request_t *r, ngx_chain_t *in) { ngx_http_modsecurity_loc_conf_t *cf; ngx_http_modsecurity_ctx_t *ctx; ngx_int_t rc; apr_off_t content_length; ngx_chain_t *cl, *out; ngx_int_t last_buf = 0; cf = ngx_http_get_module_loc_conf(r, ngx_http_modsecurity); ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity); if (r != r->main || !cf->enable || ctx == NULL || ctx->complete) { return ngx_http_next_body_filter(r, in); } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "modSecurity: body filter"); for (cl = in; cl; cl = cl->next) { apr_bucket *e; ngx_buf_t *buf = cl->buf; apr_bucket_brigade *bb = ctx->brigade; off_t size = ngx_buf_size(buf); if (size) { char *data = apr_pmemdup(bb->p, buf->pos, size); if (data == NULL) { return ngx_http_filter_finalize_request(r, &ngx_http_modsecurity, NGX_HTTP_INTERNAL_SERVER_ERROR); } e = apr_bucket_pool_create(data , size, bb->p, bb->bucket_alloc); if (e == NULL) { return ngx_http_filter_finalize_request(r, &ngx_http_modsecurity, NGX_HTTP_INTERNAL_SERVER_ERROR); } APR_BRIGADE_INSERT_TAIL(bb, e); } if (buf->last_buf) { last_buf = 1; buf->last_buf = 0; e = apr_bucket_eos_create(bb->bucket_alloc); if (e == NULL) { return ngx_http_filter_finalize_request(r, &ngx_http_modsecurity, NGX_HTTP_INTERNAL_SERVER_ERROR); } APR_BRIGADE_INSERT_TAIL(bb, e); break; } buf->pos = buf->last; } if (!last_buf) { return NGX_AGAIN; } /* last buf has been saved */ ctx->complete = 1; modsecSetResponseBrigade(ctx->req, ctx->brigade); if (ngx_http_modsecurity_load_headers_in(r) != NGX_OK || ngx_http_modsecurity_load_headers_out(r) != NGX_OK) { return ngx_http_filter_finalize_request(r, &ngx_http_modsecurity, NGX_HTTP_INTERNAL_SERVER_ERROR); } rc = ngx_http_modsecurity_status(r, modsecProcessResponse(ctx->req)); if (rc != NGX_DECLINED) { return ngx_http_filter_finalize_request(r, &ngx_http_modsecurity, rc); } apr_brigade_length(ctx->brigade, 0, &content_length); rc = move_brigade_to_chain(ctx->brigade, &out, r->pool); if (rc == NGX_ERROR) { return ngx_http_filter_finalize_request(r, &ngx_http_modsecurity, NGX_HTTP_INTERNAL_SERVER_ERROR); } if (ngx_http_modsecurity_save_headers_in(r) != NGX_OK ||ngx_http_modsecurity_save_headers_out(r) != NGX_OK) { return ngx_http_filter_finalize_request(r, &ngx_http_modsecurity, NGX_HTTP_INTERNAL_SERVER_ERROR); } if (r->headers_out.content_length_n != -1) { r->headers_out.content_length_n = content_length; r->headers_out.content_length = NULL; /* header filter will set this */ } r->header_sent = 0; rc = ngx_http_next_header_filter(r); if (rc == NGX_ERROR || rc > NGX_OK) { return ngx_http_filter_finalize_request(r, &ngx_http_modsecurity, rc); } return ngx_http_next_body_filter(r, out); }
// 真正的向客户端发送数据,调用send_chain // 也由ngx_http_set_write_handler设置epoll的写事件触发 // 如果数据发送不完,就保存在r->out里,返回again // 需要再次发生可写事件才能发送 // 不是last、flush,且数据量较小(默认1460) // 那么这次就不真正调用write发送,减少系统调用的次数,提高性能 // 在此函数里处理限速 ngx_int_t ngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *in) { off_t size, sent, nsent, limit; ngx_uint_t last, flush, sync; ngx_msec_t delay; ngx_chain_t *cl, *ln, **ll, *chain; ngx_connection_t *c; ngx_http_core_loc_conf_t *clcf; // 获取连接对象 c = r->connection; // 如果连接有错误就不能发送数据 if (c->error) { return NGX_ERROR; } // 数据的长度, size = 0; // 是否flush的标志 flush = 0; // 是否sync的标志 sync = 0; // 是否是最后一块数据,即数据全部发送完毕 last = 0; // 请求里存储的待发送的数据链表 // 可能是之前因为again而未能发送出去 ll = &r->out; /* find the size, the flush point and the last link of the saved chain */ // 先遍历当前请求里待发送的数据链表,计算长度 for (cl = r->out; cl; cl = cl->next) { ll = &cl->next; ngx_log_debug7(NGX_LOG_DEBUG_EVENT, c->log, 0, "write old buf t:%d f:%d %p, pos %p, size: %z " "file: %O, size: %O", cl->buf->temporary, cl->buf->in_file, cl->buf->start, cl->buf->pos, cl->buf->last - cl->buf->pos, cl->buf->file_pos, cl->buf->file_last - cl->buf->file_pos); #if 1 // 缓冲区0长度,但不是flush、sync等控制用缓冲区,报错 if (ngx_buf_size(cl->buf) == 0 && !ngx_buf_special(cl->buf)) { ngx_log_error(NGX_LOG_ALERT, c->log, 0, "zero size buf in writer " "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_ERROR; } #endif // 累计数据长度 size += ngx_buf_size(cl->buf); // flush标志 if (cl->buf->flush || cl->buf->recycled) { flush = 1; } // sync标志 if (cl->buf->sync) { sync = 1; } // 发送结束的标志 if (cl->buf->last_buf) { last = 1; } } /* add the new chain to the existent one */ // 遍历新的数据链表,计算长度 // 把in链表里的数据挂到r->out链表后面 for (ln = in; ln; ln = ln->next) { // 分配一个新的链表节点 cl = ngx_alloc_chain_link(r->pool); if (cl == NULL) { return NGX_ERROR; } // 拷贝缓冲区,然后挂到out后面 cl->buf = ln->buf; *ll = cl; ll = &cl->next; ngx_log_debug7(NGX_LOG_DEBUG_EVENT, c->log, 0, "write new buf t:%d f:%d %p, pos %p, size: %z " "file: %O, size: %O", cl->buf->temporary, cl->buf->in_file, cl->buf->start, cl->buf->pos, cl->buf->last - cl->buf->pos, cl->buf->file_pos, cl->buf->file_last - cl->buf->file_pos); #if 1 // 缓冲区0长度,但不是flush、sync等控制用缓冲区,报错 if (ngx_buf_size(cl->buf) == 0 && !ngx_buf_special(cl->buf)) { ngx_log_error(NGX_LOG_ALERT, c->log, 0, "zero size buf in writer " "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_ERROR; } #endif // 累计数据长度 size += ngx_buf_size(cl->buf); // flush标志 if (cl->buf->flush || cl->buf->recycled) { flush = 1; } // sync标志 if (cl->buf->sync) { sync = 1; } // 发送结束的标志 if (cl->buf->last_buf) { last = 1; } } // 链表的最后节点,必须设置为空指针 *ll = NULL; ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, "http write filter: l:%ui f:%ui s:%O", last, flush, size); clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); /* * avoid the output if there are no last buf, no flush point, * there are the incoming bufs and the size of all bufs * is smaller than "postpone_output" directive */ // 不是last、flush,且数据量较小(默认1460) // 那么这次就不真正调用write发送,减少系统调用的次数,提高性能 if (!last && !flush && in && size < (off_t) clcf->postpone_output) { return NGX_OK; } // flush,用户要求立即发送 // last,所有的数据都集齐了,之后不会有新数据 // size > postpone_output,数据已经积累的足够多,应该发送了 // delayed表示需要限速,那么就暂不发送 if (c->write->delayed) { // 置标志位,表示连接有数据缓冲待发送 c->buffered |= NGX_HTTP_WRITE_BUFFERED; return NGX_AGAIN; } // 数据长度为0 if (size == 0 && !(c->buffered & NGX_LOWLEVEL_BUFFERED) && !(last && c->need_last_buf)) { // 释放r->out里的节点 if (last || flush || sync) { for (cl = r->out; cl; /* void */) { ln = cl; cl = cl->next; ngx_free_chain(r->pool, ln); } r->out = NULL; c->buffered &= ~NGX_HTTP_WRITE_BUFFERED; return NGX_OK; } ngx_log_error(NGX_LOG_ALERT, c->log, 0, "the http output chain is empty"); ngx_debug_point(); return NGX_ERROR; } // 处理限速,不研究 if (r->limit_rate) { if (r->limit_rate_after == 0) { r->limit_rate_after = clcf->limit_rate_after; } limit = (off_t) r->limit_rate * (ngx_time() - r->start_sec + 1) - (c->sent - r->limit_rate_after); if (limit <= 0) { c->write->delayed = 1; delay = (ngx_msec_t) (- limit * 1000 / r->limit_rate + 1); ngx_add_timer(c->write, delay); c->buffered |= NGX_HTTP_WRITE_BUFFERED; return NGX_AGAIN; } if (clcf->sendfile_max_chunk && (off_t) clcf->sendfile_max_chunk < limit) { limit = clcf->sendfile_max_chunk; } } else { // 不限速,使用配置的参数,默认是0,即不限制,尽量多发 limit = clcf->sendfile_max_chunk; } // 已经发送的字节数 sent = c->sent; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http write filter limit %O", limit); // 调用send_chain发送out链表 // 实际上调用的是ngx_writev_chain // // 发送limit长度(字节数)的数据 // 如果事件not ready,即暂不可写,那么立即返回,无动作 // 要求缓冲区必须在内存里,否则报错 // 最后返回消费缓冲区之后的链表指针 // 发送出错、遇到again、发送完毕,这三种情况函数结束 // 返回的是最后发送到的链表节点指针 chain = c->send_chain(c, r->out, limit); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http write filter %p", chain); if (chain == NGX_CHAIN_ERROR) { c->error = 1; return NGX_ERROR; } // 处理限速,不研究 if (r->limit_rate) { nsent = c->sent; if (r->limit_rate_after) { sent -= r->limit_rate_after; if (sent < 0) { sent = 0; } nsent -= r->limit_rate_after; if (nsent < 0) { nsent = 0; } } delay = (ngx_msec_t) ((nsent - sent) * 1000 / r->limit_rate); if (delay > 0) { limit = 0; c->write->delayed = 1; ngx_add_timer(c->write, delay); } } // 处理限速,不研究 if (limit && c->write->ready && c->sent - sent >= limit - (off_t) (2 * ngx_pagesize)) { c->write->delayed = 1; ngx_add_timer(c->write, 1); } // 在out链表里把已经发送过的节点都回收,供以后复用 for (cl = r->out; cl && cl != chain; /* void */) { ln = cl; cl = cl->next; ngx_free_chain(r->pool, ln); } // out指向新的位置 r->out = chain; // 不是空指针,表明还有数据没发完 if (chain) { // 置标志位,表示连接有数据缓冲待发送 c->buffered |= NGX_HTTP_WRITE_BUFFERED; return NGX_AGAIN; } // 已经发送完了 // 清除缓冲标志位 c->buffered &= ~NGX_HTTP_WRITE_BUFFERED; // NGX_LOWLEVEL_BUFFERED目前似乎在nginx里还没有用到 if ((c->buffered & NGX_LOWLEVEL_BUFFERED) && r->postponed == NULL) { return NGX_AGAIN; } return NGX_OK; }
static ngx_int_t ngx_http_gzip_filter_deflate(ngx_http_request_t *r, ngx_http_gzip_ctx_t *ctx) { int rc; ngx_buf_t *b; ngx_chain_t *cl; ngx_http_gzip_conf_t *conf; ngx_log_debug6(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "deflate in: ni:%p no:%p ai:%ud ao:%ud fl:%d redo:%d", ctx->zstream.next_in, ctx->zstream.next_out, ctx->zstream.avail_in, ctx->zstream.avail_out, ctx->flush, ctx->redo); rc = deflate(&ctx->zstream, ctx->flush); if (rc != Z_OK && rc != Z_STREAM_END && rc != Z_BUF_ERROR) { ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "deflate() failed: %d, %d", ctx->flush, rc); return NGX_ERROR; } ngx_log_debug5(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "deflate out: ni:%p no:%p ai:%ud ao:%ud rc:%d", ctx->zstream.next_in, ctx->zstream.next_out, ctx->zstream.avail_in, ctx->zstream.avail_out, rc); ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "gzip in_buf:%p pos:%p", ctx->in_buf, ctx->in_buf->pos); if (ctx->zstream.next_in) { ctx->in_buf->pos = ctx->zstream.next_in; if (ctx->zstream.avail_in == 0) { ctx->zstream.next_in = NULL; } } ctx->out_buf->last = ctx->zstream.next_out; if (ctx->zstream.avail_out == 0) { /* zlib wants to output some more gzipped data */ cl = ngx_alloc_chain_link(r->pool); if (cl == NULL) { return NGX_ERROR; } cl->buf = ctx->out_buf; cl->next = NULL; *ctx->last_out = cl; ctx->last_out = &cl->next; ctx->redo = 1; return NGX_AGAIN; } ctx->redo = 0; if (ctx->flush == Z_SYNC_FLUSH) { ctx->flush = Z_NO_FLUSH; cl = ngx_alloc_chain_link(r->pool); if (cl == NULL) { return NGX_ERROR; } b = ctx->out_buf; if (ngx_buf_size(b) == 0) { b = ngx_calloc_buf(ctx->request->pool); if (b == NULL) { return NGX_ERROR; } } else { ctx->zstream.avail_out = 0; } b->flush = 1; cl->buf = b; cl->next = NULL; *ctx->last_out = cl; ctx->last_out = &cl->next; r->connection->buffered &= ~NGX_HTTP_GZIP_BUFFERED; return NGX_OK; } if (rc == Z_STREAM_END) { if (ngx_http_gzip_filter_deflate_end(r, ctx) != NGX_OK) { return NGX_ERROR; } return NGX_OK; } conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module); if (conf->no_buffer && ctx->in == NULL) { cl = ngx_alloc_chain_link(r->pool); if (cl == NULL) { return NGX_ERROR; } cl->buf = ctx->out_buf; cl->next = NULL; *ctx->last_out = cl; ctx->last_out = &cl->next; return NGX_OK; } return NGX_AGAIN; }
static int ngx_http_lua_ngx_req_set_body_data(lua_State *L) { ngx_http_request_t *r; int n; ngx_http_request_body_t *rb; ngx_temp_file_t *tf; ngx_buf_t *b; ngx_str_t body, key, value; #if 1 ngx_int_t rc; #endif ngx_chain_t *cl; ngx_buf_tag_t tag; n = lua_gettop(L); if (n != 1) { return luaL_error(L, "expecting 1 arguments but seen %d", n); } body.data = (u_char *) luaL_checklstring(L, 1, &body.len); r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "request object not found"); } ngx_http_lua_check_fake_request(L, r); if (r->discard_body) { return luaL_error(L, "request body already discarded asynchronously"); } if (r->request_body == NULL) { return luaL_error(L, "request body not read yet"); } rb = r->request_body; tag = (ngx_buf_tag_t) &ngx_http_lua_module; tf = rb->temp_file; if (tf) { if (tf->file.fd != NGX_INVALID_FILE) { dd("cleaning temp file %.*s", (int) tf->file.name.len, tf->file.name.data); ngx_http_lua_pool_cleanup_file(r->pool, tf->file.fd); tf->file.fd = NGX_INVALID_FILE; dd("temp file cleaned: %.*s", (int) tf->file.name.len, tf->file.name.data); } rb->temp_file = NULL; } if (body.len == 0) { if (rb->bufs) { for (cl = rb->bufs; cl; cl = cl->next) { if (cl->buf->tag == tag && cl->buf->temporary) { dd("free old request body buffer: size:%d", (int) ngx_buf_size(cl->buf)); ngx_pfree(r->pool, cl->buf->start); cl->buf->tag = (ngx_buf_tag_t) NULL; cl->buf->temporary = 0; } } } rb->bufs = NULL; rb->buf = NULL; dd("request body is set to empty string"); goto set_header; } if (rb->bufs) { for (cl = rb->bufs; cl; cl = cl->next) { if (cl->buf->tag == tag && cl->buf->temporary) { dd("free old request body buffer: size:%d", (int) ngx_buf_size(cl->buf)); ngx_pfree(r->pool, cl->buf->start); cl->buf->tag = (ngx_buf_tag_t) NULL; cl->buf->temporary = 0; } } rb->bufs->next = NULL; b = rb->bufs->buf; ngx_memzero(b, sizeof(ngx_buf_t)); b->temporary = 1; b->tag = tag; b->start = ngx_palloc(r->pool, body.len); if (b->start == NULL) { return luaL_error(L, "no memory"); } b->end = b->start + body.len; b->pos = b->start; b->last = ngx_copy(b->pos, body.data, body.len); } else { rb->bufs = ngx_alloc_chain_link(r->pool); if (rb->bufs == NULL) { return luaL_error(L, "no memory"); } rb->bufs->next = NULL; b = ngx_create_temp_buf(r->pool, body.len); if (b == NULL) { return luaL_error(L, "no memory"); } b->tag = tag; b->last = ngx_copy(b->pos, body.data, body.len); rb->bufs->buf = b; rb->buf = b; } set_header: /* override input header Content-Length (value must be null terminated) */ value.data = ngx_palloc(r->pool, NGX_SIZE_T_LEN + 1); if (value.data == NULL) { return luaL_error(L, "no memory"); } value.len = ngx_sprintf(value.data, "%uz", body.len) - value.data; value.data[value.len] = '\0'; dd("setting request Content-Length to %.*s (%d)", (int) value.len, value.data, (int) body.len); r->headers_in.content_length_n = body.len; if (r->headers_in.content_length) { r->headers_in.content_length->value.data = value.data; r->headers_in.content_length->value.len = value.len; } else { ngx_str_set(&key, "Content-Length"); rc = ngx_http_lua_set_input_header(r, key, value, 1 /* override */); if (rc != NGX_OK) { return luaL_error(L, "failed to reset the Content-Length " "input header"); } } return 0; }
static ngx_int_t ngx_http_charset_body_filter(ngx_http_request_t *r, ngx_chain_t *in) { ngx_int_t rc; ngx_buf_t *b; ngx_chain_t *cl, *out, **ll; ngx_http_charset_ctx_t *ctx; ctx = ngx_http_get_module_ctx(r, ngx_http_charset_filter_module); if (ctx == NULL || ctx->table == NULL) { return ngx_http_next_body_filter(r, in); } if ((ctx->to_utf8 || ctx->from_utf8) || ctx->busy) { out = NULL; ll = &out; for (cl = in; cl; cl = cl->next) { b = cl->buf; if (ngx_buf_size(b) == 0) { *ll = ngx_alloc_chain_link(r->pool); if (*ll == NULL) { return NGX_ERROR; } (*ll)->buf = b; (*ll)->next = NULL; ll = &(*ll)->next; continue; } if (ctx->to_utf8) { *ll = ngx_http_charset_recode_to_utf8(r->pool, b, ctx); } else { *ll = ngx_http_charset_recode_from_utf8(r->pool, b, ctx); } if (*ll == NULL) { return NGX_ERROR; } while (*ll) { ll = &(*ll)->next; } } rc = ngx_http_next_body_filter(r, out); if (out) { if (ctx->busy == NULL) { ctx->busy = out; } else { for (cl = ctx->busy; cl->next; cl = cl->next) { /* void */ } cl->next = out; } } while (ctx->busy) { cl = ctx->busy; b = cl->buf; if (ngx_buf_size(b) != 0) { break; } ctx->busy = cl->next; if (b->tag != (ngx_buf_tag_t) &ngx_http_charset_filter_module) { continue; } if (b->shadow) { b->shadow->pos = b->shadow->last; } if (b->pos) { cl->next = ctx->free_buffers; ctx->free_buffers = cl; continue; } cl->next = ctx->free_bufs; ctx->free_bufs = cl; } return rc; } for (cl = in; cl; cl = cl->next) { (void) ngx_http_charset_recode(cl->buf, ctx->table); } return ngx_http_next_body_filter(r, in); }
static ngx_int_t ngx_http_sub_output(ngx_http_request_t *r, ngx_http_sub_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, "sub 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 sub"); 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; } } if (ctx->in || ctx->buf) { r->buffered |= NGX_HTTP_SUB_BUFFERED; } else { r->buffered &= ~NGX_HTTP_SUB_BUFFERED; } return rc; }
static int ngx_http_lua_ngx_req_get_body_data(lua_State *L) { ngx_http_request_t *r; int n; size_t len; ngx_chain_t *cl; u_char *p; u_char *buf; n = lua_gettop(L); if (n != 0) { return luaL_error(L, "expecting 0 arguments but seen %d", n); } r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "request object not found"); } ngx_http_lua_check_fake_request(L, r); if (r->request_body == NULL || r->request_body->temp_file || r->request_body->bufs == NULL) { lua_pushnil(L); return 1; } cl = r->request_body->bufs; if (cl->next == NULL) { len = cl->buf->last - cl->buf->pos; if (len == 0) { lua_pushnil(L); return 1; } lua_pushlstring(L, (char *) cl->buf->pos, len); return 1; } /* found multi-buffer body */ len = 0; for (; cl; cl = cl->next) { dd("body chunk len: %d", (int) ngx_buf_size(cl->buf)); len += cl->buf->last - cl->buf->pos; } if (len == 0) { lua_pushnil(L); return 1; } buf = (u_char *) lua_newuserdata(L, len); p = buf; for (cl = r->request_body->bufs; cl; cl = cl->next) { p = ngx_copy(p, cl->buf->pos, cl->buf->last - cl->buf->pos); } lua_pushlstring(L, (char *) buf, len); return 1; }
static ngx_int_t ngx_http_copy_filter(void *context, ngx_chain_t *in) { ngx_int_t rc; ngx_connection_t *c; ngx_output_chain_ctx_t *ctx; ngx_http_core_loc_conf_t *clcf; ngx_http_copy_filter_conf_t *conf; ngx_http_request_t *r = (ngx_http_request_t*)context; c = r->connection; ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "http copy filter: \"%V?%V\"", &r->uri, &r->args); ctx = ngx_http_get_module_ctx(r, ngx_http_copy_filter_module); if (ctx == NULL) { ctx = ngx_pcalloc(r->pool, sizeof(ngx_output_chain_ctx_t)); if (ctx == NULL) { return NGX_ERROR; } ngx_http_set_ctx(r, ctx, ngx_http_copy_filter_module); conf = ngx_http_get_module_loc_conf(r, ngx_http_copy_filter_module); clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); ctx->sendfile = c->sendfile; ctx->need_in_memory = r->main_filter_need_in_memory || r->filter_need_in_memory; ctx->need_in_temp = r->filter_need_temporary; ctx->alignment = clcf->directio_alignment; ctx->pool = r->pool; ctx->bufs = conf->bufs; ctx->tag = (ngx_buf_tag_t) &ngx_http_copy_filter_module; ctx->output_filter = (ngx_output_chain_filter_pt) ngx_http_next_body_filter; ctx->filter_ctx = r; #if (NGX_HAVE_FILE_AIO) if (ngx_file_aio) { if (clcf->aio) { ctx->aio_handler = ngx_http_copy_aio_handler; } #if (NGX_HAVE_AIO_SENDFILE) c->aio_sendfile = (clcf->aio == NGX_HTTP_AIO_SENDFILE); #endif } #endif if (in && in->buf && ngx_buf_size(in->buf)) { r->request_output = 1; } } #if (NGX_HAVE_FILE_AIO) ctx->aio = r->aio; #endif for ( ;; ) { rc = ngx_output_chain(ctx, in); if (ctx->in == NULL) { r->buffered &= ~NGX_HTTP_COPY_BUFFERED; } else { r->buffered |= NGX_HTTP_COPY_BUFFERED; } ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, "http copy filter: %i \"%V?%V\"", rc, &r->uri, &r->args); #if (NGX_HAVE_FILE_AIO && NGX_HAVE_AIO_SENDFILE) if (c->busy_sendfile) { ssize_t n; off_t offset; ngx_file_t *file; ngx_http_ephemeral_t *e; if (r->aio) { c->busy_sendfile = NULL; return rc; } file = c->busy_sendfile->file; offset = c->busy_sendfile->file_pos; if (file->aio) { c->aio_sendfile = (offset != file->aio->last_offset); file->aio->last_offset = offset; if (c->aio_sendfile == 0) { ngx_log_error(NGX_LOG_ALERT, c->log, 0, "sendfile(%V) returned busy again", &file->name); } } c->busy_sendfile = NULL; e = (ngx_http_ephemeral_t *) &r->uri_start; n = ngx_file_aio_read(file, &e->aio_preload, 1, offset, r->pool); if (n > 0) { in = NULL; continue; } rc = n; if (rc == NGX_AGAIN) { file->aio->data = r; file->aio->handler = ngx_http_copy_aio_sendfile_event_handler; r->main->blocked++; r->aio = 1; } } #endif return rc; } }
static ngx_int_t ngx_http_spdy_body_filter(ngx_http_request_t *r, ngx_chain_t *in) { off_t size; ngx_buf_t *b; ngx_chain_t *cl, *ll, *out, **ln; ngx_http_spdy_stream_t *stream; ngx_http_spdy_out_frame_t *frame; stream = r->spdy_stream; if (stream == NULL) { return ngx_http_next_body_filter(r, in); } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "spdy body filter \"%V?%V\"", &r->uri, &r->args); if (in == NULL || r->header_only) { if (stream->waiting) { return NGX_AGAIN; } r->connection->buffered &= ~NGX_SPDY_WRITE_BUFFERED; return NGX_OK; } size = 0; ln = &out; ll = in; for ( ;; ) { b = ll->buf; #if 1 if (ngx_buf_size(b) == 0 && !ngx_buf_special(b)) { ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "zero size buf in spdy body filter " "t:%d r:%d f:%d %p %p-%p %p %O-%O", b->temporary, b->recycled, b->in_file, b->start, b->pos, b->last, b->file, b->file_pos, b->file_last); ngx_debug_point(); return NGX_ERROR; } #endif cl = ngx_alloc_chain_link(r->pool); if (cl == NULL) { return NGX_ERROR; } size += ngx_buf_size(b); cl->buf = b; *ln = cl; ln = &cl->next; if (ll->next == NULL) { break; } ll = ll->next; } if (size > NGX_SPDY_MAX_FRAME_SIZE) { ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "FIXME: chain too big in spdy filter: %O", size); return NGX_ERROR; } frame = ngx_http_spdy_filter_get_data_frame(stream, (size_t) size, b->last_buf, out, cl); if (frame == NULL) { return NGX_ERROR; } ngx_http_spdy_queue_frame(stream->connection, frame); stream->waiting++; r->main->blocked++; return ngx_http_spdy_filter_send(r->connection, stream); }
static ngx_chain_t * ngx_http_spdy_split_chain(ngx_http_spdy_stream_t *stream, ngx_int_t limit) { ngx_buf_t *b, *nb; ngx_chain_t *pre, *ll, *cl, *out; ngx_http_request_t *r; r = stream->request; pre = out = ll = stream->pending; for ( ;; ) { if (ll == NULL) { break; } b = ll->buf; if (limit > ngx_buf_size(b)) { limit -= ngx_buf_size(b); } else if (limit == ngx_buf_size(b)) { stream->pending = ll->next; ll->next = NULL; return out; } else { cl = ngx_alloc_chain_link(r->pool); if (cl == NULL) { return NULL; } nb = ngx_palloc(r->pool, sizeof(ngx_buf_t)); if (nb == NULL) { return NULL; } cl->buf = nb; cl->next = NULL; *nb = *b; nb->last_buf = 0; nb->tag = (void *) ngx_http_spdy_split_chain; if (b->in_file) { nb->file_last = nb->file_pos + limit; b->file_pos += limit; } else { nb->last = nb->pos + limit; b->pos += limit; } stream->pending = ll; if (ll == out) { return cl; } else { pre->next = cl; return out; } } pre = ll; ll = ll->next; } return NULL; }
static ngx_int_t ngx_http_lua_adjust_subrequest(ngx_http_request_t *sr, ngx_uint_t method, int always_forward_body, ngx_http_request_body_t *body, unsigned vars_action, ngx_array_t *extra_vars) { ngx_http_request_t *r; ngx_int_t rc; ngx_http_core_main_conf_t *cmcf; size_t size; r = sr->parent; sr->header_in = r->header_in; if (body) { sr->request_body = body; rc = ngx_http_lua_set_content_length_header(sr, body->buf ? ngx_buf_size(body->buf) : 0); if (rc != NGX_OK) { return NGX_ERROR; } } else if (!always_forward_body && method != NGX_HTTP_PUT && method != NGX_HTTP_POST && r->headers_in.content_length_n > 0) { rc = ngx_http_lua_set_content_length_header(sr, 0); if (rc != NGX_OK) { return NGX_ERROR; } #if 1 sr->request_body = NULL; #endif } else { if (ngx_http_lua_copy_request_headers(sr, r) != NGX_OK) { return NGX_ERROR; } if (sr->request_body) { /* deep-copy the request body */ if (sr->request_body->temp_file) { if (ngx_http_lua_copy_in_file_request_body(sr) != NGX_OK) { return NGX_ERROR; } } } } sr->method = method; switch (method) { case NGX_HTTP_GET: sr->method_name = ngx_http_lua_get_method; break; case NGX_HTTP_POST: sr->method_name = ngx_http_lua_post_method; break; case NGX_HTTP_PUT: sr->method_name = ngx_http_lua_put_method; break; case NGX_HTTP_HEAD: sr->method_name = ngx_http_lua_head_method; break; case NGX_HTTP_DELETE: sr->method_name = ngx_http_lua_delete_method; break; case NGX_HTTP_OPTIONS: sr->method_name = ngx_http_lua_options_method; break; case NGX_HTTP_MKCOL: sr->method_name = ngx_http_lua_mkcol_method; break; case NGX_HTTP_COPY: sr->method_name = ngx_http_lua_copy_method; break; case NGX_HTTP_MOVE: sr->method_name = ngx_http_lua_move_method; break; case NGX_HTTP_PROPFIND: sr->method_name = ngx_http_lua_propfind_method; break; case NGX_HTTP_PROPPATCH: sr->method_name = ngx_http_lua_proppatch_method; break; case NGX_HTTP_LOCK: sr->method_name = ngx_http_lua_lock_method; break; case NGX_HTTP_UNLOCK: sr->method_name = ngx_http_lua_unlock_method; break; case NGX_HTTP_PATCH: sr->method_name = ngx_http_lua_patch_method; break; case NGX_HTTP_TRACE: sr->method_name = ngx_http_lua_trace_method; break; default: ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "unsupported HTTP method: %u", (unsigned) method); return NGX_ERROR; } if (!(vars_action & NGX_HTTP_LUA_SHARE_ALL_VARS)) { /* we do not inherit the parent request's variables */ cmcf = ngx_http_get_module_main_conf(sr, ngx_http_core_module); size = cmcf->variables.nelts * sizeof(ngx_http_variable_value_t); if (vars_action & NGX_HTTP_LUA_COPY_ALL_VARS) { sr->variables = ngx_palloc(sr->pool, size); if (sr->variables == NULL) { return NGX_ERROR; } ngx_memcpy(sr->variables, r->variables, size); } else { /* we do not inherit the parent request's variables */ sr->variables = ngx_pcalloc(sr->pool, size); if (sr->variables == NULL) { return NGX_ERROR; } } } return ngx_http_lua_subrequest_add_extra_vars(sr, extra_vars); }
static int ngx_http_lua_ngx_req_set_body_file(lua_State *L) { u_char *p; ngx_http_request_t *r; int n; ngx_http_request_body_t *rb; ngx_temp_file_t *tf; ngx_buf_t *b; ngx_str_t name; ngx_int_t rc; int clean; ngx_open_file_info_t of; ngx_str_t key, value; ngx_pool_cleanup_t *cln; ngx_pool_cleanup_file_t *clnf; ngx_err_t err; ngx_chain_t *cl; ngx_buf_tag_t tag; n = lua_gettop(L); if (n != 1 && n != 2) { return luaL_error(L, "expecting 1 or 2 arguments but seen %d", n); } p = (u_char *) luaL_checklstring(L, 1, &name.len); r = ngx_http_lua_get_req(L); if (r == NULL) { return luaL_error(L, "no request found"); } ngx_http_lua_check_fake_request(L, r); if (r->discard_body) { return luaL_error(L, "request body already discarded asynchronously"); } if (r->request_body == NULL) { return luaL_error(L, "request body not read yet"); } name.data = ngx_palloc(r->pool, name.len + 1); if (name.data == NULL) { return luaL_error(L, "no memory"); } ngx_memcpy(name.data, p, name.len); name.data[name.len] = '\0'; if (n == 2) { luaL_checktype(L, 2, LUA_TBOOLEAN); clean = lua_toboolean(L, 2); } else { clean = 0; } dd("clean: %d", (int) clean); rb = r->request_body; /* clean up existing r->request_body->bufs (if any) */ tag = (ngx_buf_tag_t) &ngx_http_lua_module; if (rb->bufs) { dd("XXX reusing buf"); for (cl = rb->bufs; cl; cl = cl->next) { if (cl->buf->tag == tag && cl->buf->temporary) { dd("free old request body buffer: size:%d", (int) ngx_buf_size(cl->buf)); ngx_pfree(r->pool, cl->buf->start); cl->buf->tag = (ngx_buf_tag_t) NULL; cl->buf->temporary = 0; } } rb->bufs->next = NULL; b = rb->bufs->buf; ngx_memzero(b, sizeof(ngx_buf_t)); b->tag = tag; rb->buf = NULL; } else { dd("XXX creating new buf"); rb->bufs = ngx_alloc_chain_link(r->pool); if (rb->bufs == NULL) { return luaL_error(L, "no memory"); } rb->bufs->next = NULL; b = ngx_calloc_buf(r->pool); if (b == NULL) { return luaL_error(L, "no memory"); } b->tag = tag; rb->bufs->buf = b; rb->buf = NULL; } b->last_in_chain = 1; /* just make r->request_body->temp_file a bare stub */ tf = rb->temp_file; if (tf) { if (tf->file.fd != NGX_INVALID_FILE) { dd("cleaning temp file %.*s", (int) tf->file.name.len, tf->file.name.data); ngx_http_lua_pool_cleanup_file(r->pool, tf->file.fd); ngx_memzero(tf, sizeof(ngx_temp_file_t)); tf->file.fd = NGX_INVALID_FILE; dd("temp file cleaned: %.*s", (int) tf->file.name.len, tf->file.name.data); } } else { tf = ngx_pcalloc(r->pool, sizeof(ngx_temp_file_t)); if (tf == NULL) { return luaL_error(L, "no memory"); } tf->file.fd = NGX_INVALID_FILE; rb->temp_file = tf; } /* read the file info and construct an in-file buf */ ngx_memzero(&of, sizeof(ngx_open_file_info_t)); of.directio = NGX_OPEN_FILE_DIRECTIO_OFF; if (ngx_http_lua_open_and_stat_file(name.data, &of, r->connection->log) != NGX_OK) { return luaL_error(L, "%s \"%s\" failed", of.failed, name.data); } dd("XXX new body file fd: %d", of.fd); tf->file.fd = of.fd; tf->file.name = name; tf->file.log = r->connection->log; tf->file.directio = 0; if (of.size == 0) { if (clean) { if (ngx_delete_file(name.data) == NGX_FILE_ERROR) { err = ngx_errno; if (err != NGX_ENOENT) { ngx_log_error(NGX_LOG_CRIT, r->connection->log, err, ngx_delete_file_n " \"%s\" failed", name.data); } } } if (ngx_close_file(of.fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno, ngx_close_file_n " \"%s\" failed", name.data); } r->request_body->bufs = NULL; r->request_body->buf = NULL; goto set_header; } /* register file cleanup hook */ cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_pool_cleanup_file_t)); if (cln == NULL) { return luaL_error(L, "no memory"); } cln->handler = clean ? ngx_pool_delete_file : ngx_pool_cleanup_file; clnf = cln->data; clnf->fd = of.fd; clnf->name = name.data; clnf->log = r->pool->log; b->file = &tf->file; if (b->file == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } dd("XXX file size: %d", (int) of.size); b->file_pos = 0; b->file_last = of.size; b->in_file = 1; dd("buf file: %p, f:%u", b->file, b->in_file); set_header: /* override input header Content-Length (value must be null terminated) */ value.data = ngx_palloc(r->pool, NGX_OFF_T_LEN + 1); if (value.data == NULL) { return luaL_error(L, "no memory"); } value.len = ngx_sprintf(value.data, "%O", of.size) - value.data; value.data[value.len] = '\0'; r->headers_in.content_length_n = of.size; if (r->headers_in.content_length) { r->headers_in.content_length->value.data = value.data; r->headers_in.content_length->value.len = value.len; } else { ngx_str_set(&key, "Content-Length"); rc = ngx_http_lua_set_input_header(r, key, value, 1 /* override */); if (rc != NGX_OK) { return luaL_error(L, "failed to reset the Content-Length " "input header"); } } return 0; }
ngx_chain_t * ngx_darwin_sendfile_chain(ngx_connection_t *c, ngx_chain_t *in, off_t limit) { int rc; u_char *prev; off_t size, send, prev_send, aligned, sent, fprev; off_t header_size, file_size; ngx_uint_t eintr, 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; 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 && header.nelts < IOV_MAX && 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 { 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; 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; 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 && header.nelts == 0) { /* create the trailer iovec and coalesce the neighbouring bufs */ prev = NULL; iov = NULL; while (cl && header.nelts < IOV_MAX && 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 { 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) { /* * sendfile() returns EINVAL 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; sent = header_size + file_size; ngx_log_debug3(NGX_LOG_DEBUG_EVENT, c->log, 0, "sendfile: @%O %O h:%O", 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 { rc = writev(c->fd, header.elts, header.nelts); ngx_log_debug2(NGX_LOG_DEBUG_EVENT, c->log, 0, "writev: %d of %uz", rc, send); 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 (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; } }
static ngx_int_t websocket_publish_callback(ngx_int_t status, nchan_channel_t *ch, full_subscriber_t *fsub) { time_t last_seen = 0; ngx_uint_t subscribers = 0; ngx_uint_t messages = 0; nchan_msg_id_t *msgid = NULL; ngx_http_request_t *r = fsub->sub.request; ngx_str_t *accept_header = NULL; ngx_buf_t *tmp_buf; if(ch) { subscribers = ch->subscribers; last_seen = ch->last_seen; messages = ch->messages; msgid = &ch->last_published_msg_id; } if(websocket_release(&fsub->sub, 0) == NGX_ABORT) { //zombie publisher //nothing more to do, we're finished here return NGX_OK; } switch(status) { case NCHAN_MESSAGE_QUEUED: case NCHAN_MESSAGE_RECEIVED: if(fsub->sub.cf->sub.websocket) { //don't reply with status info, this websocket is used for subscribing too, //so it should only be recieving messages return NGX_OK; } if(r->headers_in.accept) { accept_header = &r->headers_in.accept->value; } tmp_buf = nchan_channel_info_buf(accept_header, messages, subscribers, last_seen, msgid, NULL); ngx_memcpy(&fsub->msg_buf, tmp_buf, sizeof(*tmp_buf)); fsub->msg_buf.last_buf=1; nchan_output_filter(fsub->sub.request, websocket_frame_header_chain(fsub, WEBSOCKET_TEXT_LAST_FRAME_BYTE, ngx_buf_size((&fsub->msg_buf)))); break; case NGX_ERROR: case NGX_HTTP_INTERNAL_SERVER_ERROR: assert(0); break; } return NGX_OK; }
ngx_int_t ngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *in) { off_t size, sent, nsent, limit; ngx_uint_t last, flush; ngx_msec_t delay; ngx_chain_t *cl, *ln, **ll, *chain; ngx_connection_t *c; ngx_http_core_loc_conf_t *clcf; c = r->connection; if (c->error) { return NGX_ERROR; } size = 0; flush = 0; last = 0; ll = &r->out; #if (NGX_HTTP_STATUS_PAGE) if (ngx_http_status_page_test_old_header(r)) { for (cl = r->out; cl; /* void */) { ln = cl; cl = cl->next; ngx_free_chain(r->pool, ln); } r->out = NULL; ngx_http_status_page_clr_old_header(r); } #endif /* find the size, the flush point and the last link of the saved chain */ for (cl = r->out; cl; cl = cl->next) { ll = &cl->next; ngx_log_debug7(NGX_LOG_DEBUG_EVENT, c->log, 0, "write old buf t:%d f:%d %p, pos %p, size: %z " "file: %O, size: %z", cl->buf->temporary, cl->buf->in_file, cl->buf->start, cl->buf->pos, cl->buf->last - cl->buf->pos, cl->buf->file_pos, cl->buf->file_last - cl->buf->file_pos); #if 1 if (ngx_buf_size(cl->buf) == 0 && !ngx_buf_special(cl->buf)) { ngx_log_error(NGX_LOG_ALERT, c->log, 0, "zero size buf in writer " "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_ERROR; } #endif size += ngx_buf_size(cl->buf); if (cl->buf->flush || cl->buf->recycled) { flush = 1; } if (cl->buf->last_buf) { last = 1; } } /* add the new chain to the existent one */ for (ln = in; ln; ln = ln->next) { cl = ngx_alloc_chain_link(r->pool); if (cl == NULL) { return NGX_ERROR; } cl->buf = ln->buf; *ll = cl; ll = &cl->next; ngx_log_debug7(NGX_LOG_DEBUG_EVENT, c->log, 0, "write new buf t:%d f:%d %p, pos %p, size: %z " "file: %O, size: %z", cl->buf->temporary, cl->buf->in_file, cl->buf->start, cl->buf->pos, cl->buf->last - cl->buf->pos, cl->buf->file_pos, cl->buf->file_last - cl->buf->file_pos); #if 1 if (ngx_buf_size(cl->buf) == 0 && !ngx_buf_special(cl->buf)) { ngx_log_error(NGX_LOG_ALERT, c->log, 0, "zero size buf in writer " "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_ERROR; } #endif size += ngx_buf_size(cl->buf); if (cl->buf->flush || cl->buf->recycled) { flush = 1; } if (cl->buf->last_buf) { last = 1; } } *ll = NULL; ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0, "http write filter: l:%d f:%d s:%O", last, flush, size); clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); /* * avoid the output if there are no last buf, no flush point, * there are the incoming bufs and the size of all bufs * is smaller than "postpone_output" directive */ if (!last && !flush && in && size < (off_t) clcf->postpone_output) { return NGX_OK; } if (c->write->delayed) { c->buffered |= NGX_HTTP_WRITE_BUFFERED; return NGX_AGAIN; } if (size == 0 && !(c->buffered & NGX_LOWLEVEL_BUFFERED)) { if (last || flush) { for (cl = r->out; cl; /* void */) { ln = cl; cl = cl->next; ngx_free_chain(r->pool, ln); } r->out = NULL; c->buffered &= ~NGX_HTTP_WRITE_BUFFERED; return NGX_OK; } ngx_log_error(NGX_LOG_ALERT, c->log, 0, "the http output chain is empty"); ngx_debug_point(); return NGX_ERROR; } if (r->limit_rate) { if (r->limit_rate_after == 0) { r->limit_rate_after = clcf->limit_rate_after; } limit = (off_t) r->limit_rate * (ngx_time() - r->start_sec + 1) - (c->sent - r->limit_rate_after); if (limit <= 0) { c->write->delayed = 1; ngx_add_timer(c->write, (ngx_msec_t) (- limit * 1000 / r->limit_rate + 1)); c->buffered |= NGX_HTTP_WRITE_BUFFERED; return NGX_AGAIN; } if (clcf->sendfile_max_chunk && (off_t) clcf->sendfile_max_chunk < limit) { limit = clcf->sendfile_max_chunk; } } else { limit = clcf->sendfile_max_chunk; } sent = c->sent; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http write filter limit %O", limit); chain = c->send_chain(c, r->out, limit); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, "http write filter %p", chain); if (chain == NGX_CHAIN_ERROR) { c->error = 1; return NGX_ERROR; } if (r->limit_rate) { nsent = c->sent; if (r->limit_rate_after) { sent -= r->limit_rate_after; if (sent < 0) { sent = 0; } nsent -= r->limit_rate_after; if (nsent < 0) { nsent = 0; } } delay = (ngx_msec_t) ((nsent - sent) * 1000 / r->limit_rate); if (delay > 0) { limit = 0; c->write->delayed = 1; ngx_add_timer(c->write, delay); } } if (limit && c->write->ready && c->sent - sent >= limit - (off_t) (2 * ngx_pagesize)) { c->write->delayed = 1; ngx_add_timer(c->write, 1); } for (cl = r->out; cl && cl != chain; /* void */) { ln = cl; cl = cl->next; ngx_free_chain(r->pool, ln); } r->out = chain; if (chain) { c->buffered |= NGX_HTTP_WRITE_BUFFERED; return NGX_AGAIN; } c->buffered &= ~NGX_HTTP_WRITE_BUFFERED; if ((c->buffered & NGX_LOWLEVEL_BUFFERED) && r->postponed == NULL) { return NGX_AGAIN; } return NGX_OK; }
static ngx_int_t websocket_publish(full_subscriber_t *fsub, ngx_buf_t *buf) { static ngx_str_t nopublishing = ngx_string("Publishing not allowed."); static ngx_str_t POST_REQUEST_STRING = {4, (u_char *)"POST "}; if(!fsub->sub.cf->pub.websocket) { return websocket_send_close_frame(fsub, CLOSE_POLICY_VIOLATION, &nopublishing); } #if (NGX_DEBUG_POOL) ERR("ws request pool size: %V", ngx_http_debug_pool_str(fsub->sub.request->pool)); #endif ngx_http_complex_value_t *publisher_upstream_request_url_ccv; publisher_upstream_request_url_ccv = fsub->sub.cf->publisher_upstream_request_url; if(publisher_upstream_request_url_ccv == NULL) { websocket_publish_continue(fsub, buf); } else { nchan_pub_upstream_stuff_t *psr_stuff; ngx_http_post_subrequest_t *psr; nchan_pub_upstream_data_t *psrd; ngx_http_request_t *r = fsub->sub.request; ngx_http_request_t *sr; ngx_http_request_body_t *fakebody; ngx_chain_t *fakebody_chain; ngx_buf_t *fakebody_buf; size_t sz; if(!fsub->upstream_stuff) { if((psr_stuff = ngx_palloc(r->pool, sizeof(*psr_stuff))) == NULL) { ERR("can't allocate memory for publisher auth subrequest"); websocket_respond_status(&fsub->sub, NGX_HTTP_INTERNAL_SERVER_ERROR, NULL); return NGX_ERROR; } fsub->upstream_stuff = psr_stuff; psr = &psr_stuff->psr; psr->data = &psr_stuff->psr_data; psr->handler = websocket_publisher_upstream_handler; psr_stuff->psr_data.fsub = fsub; psr_stuff->psr_data.tmp_pool = NULL; ngx_http_complex_value(r, publisher_upstream_request_url_ccv, &psr_stuff->psr_data.upstream_request_url); } psrd = &fsub->upstream_stuff->psr_data; psr = &fsub->upstream_stuff->psr; if(psrd->tmp_pool) { //previous upstream request's pool ngx_destroy_pool(psrd->tmp_pool); psrd->tmp_pool = NULL; } psrd->buf = *buf; psrd->original_pool = r->pool; psrd->original_cleanup = r->cleanup; r->cleanup = NULL; psrd->tmp_pool = ngx_create_pool(NCHAN_WS_UPSTREAM_TMP_POOL_SIZE, r->connection->log); r->pool = psrd->tmp_pool; //ERR("request %p tmp pool %p", r, r->pool); fakebody = ngx_pcalloc(r->pool, sizeof(*fakebody)); fakebody_chain = ngx_palloc(r->pool, sizeof(*fakebody_chain)); fakebody_buf = ngx_palloc(r->pool, sizeof(*fakebody_buf)); fakebody->bufs = fakebody_chain; fakebody_chain->next = NULL; fakebody_chain->buf = fakebody_buf; init_msg_buf(fakebody_buf); //just copy the buffer contents. it's inefficient but I don't care at the moment. //this can and should be optimized later sz = ngx_buf_size(buf); fakebody_buf->start = ngx_palloc(r->pool, sz); //huuh? ngx_memcpy(fakebody_buf->start, buf->start, sz); fakebody_buf->end = fakebody_buf->start + sz; fakebody_buf->pos = fakebody_buf->start; fakebody_buf->last = fakebody_buf->end; ngx_http_subrequest(r, &psrd->upstream_request_url, NULL, &sr, psr, NGX_HTTP_SUBREQUEST_IN_MEMORY); nchan_adjust_subrequest(sr, NGX_HTTP_POST, &POST_REQUEST_STRING, fakebody, sz, NULL); /* //http request sudden close cleanup if((psrd->cln = ngx_http_cleanup_add(sr, 0)) == NULL) { ERR("Unable to add request cleanup for websocket upstream request"); return NGX_ERROR; } psrd->cln->data = fsub; psrd->cln->handler = (ngx_http_cleanup_pt )sudden_upstream_request_abort_handler; */ } return NGX_OK; }
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; } 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; }
ngx_int_t ngx_http_request_parser_post_arg(ngx_http_request_t *r, u_char *arg_name, size_t arg_len, ngx_str_t *value) { u_char *p, *v, *last, *buf; ngx_chain_t *cl; size_t len = 0; ngx_buf_t *b; ngx_str_set(value, ""); /* we read data from r->request_body->bufs */ if (r->request_body == NULL || r->request_body->bufs == NULL) { return NGX_OK; } if (r->request_body->bufs->next != NULL) { /* more than one buffer...we should copy the data out... */ len = 0; for (cl = r->request_body->bufs; cl; cl = cl->next) { b = cl->buf; if (b->in_file) { return NGX_OK; } len += b->last - b->pos; } if (len == 0) { return NGX_OK; } buf = ngx_palloc(r->pool, len); if (buf == NULL) { return NGX_ERROR; } p = buf; last = p + len; for (cl = r->request_body->bufs; cl; cl = cl->next) { p = ngx_copy(p, cl->buf->pos, cl->buf->last - cl->buf->pos); } } else { b = r->request_body->bufs->buf; if (ngx_buf_size(b) == 0) { return NGX_OK; } buf = b->pos; last = b->last; } for (p = buf; p < last; p++) { /* we need '=' after name, so drop one char from last */ p = ngx_strlcasestrn(p, last - 1, arg_name, arg_len - 1); if (p == NULL) { return NGX_OK; } if ((p == buf || *(p - 1) == '&') && *(p + arg_len) == '=') { v = p + arg_len + 1; p = ngx_strlchr(v, last, '&'); if (p == NULL) { p = last; } else { } value->data = v; value->len = p - v; return NGX_OK; } } return NGX_OK; }
ngx_int_t ngx_http_srcache_access_handler(ngx_http_request_t *r) { ngx_str_t skip; ngx_int_t rc; ngx_http_srcache_loc_conf_t *conf; ngx_http_srcache_main_conf_t *smcf; ngx_http_srcache_ctx_t *ctx; ngx_chain_t *cl; size_t len; unsigned no_store; /* access phase handlers are skipped in subrequests, * so the current request must be a main request */ conf = ngx_http_get_module_loc_conf(r, ngx_http_srcache_filter_module); if (conf->fetch == NULL && conf->store == NULL) { dd("bypass: %.*s", (int) r->uri.len, r->uri.data); return NGX_DECLINED; } dd("store defined? %p", conf->store); dd("req method: %lu", (unsigned long) r->method); dd("cache methods: %lu", (unsigned long) conf->cache_methods); if (!(r->method & conf->cache_methods)) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "srcache_fetch and srcache_store skipped due to request " "method %V", &r->method_name); return NGX_DECLINED; } if (conf->req_cache_control && ngx_http_srcache_request_no_cache(r, &no_store) == NGX_OK) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "srcache_fetch skipped due to request headers " "\"Cache-Control: no-cache\" or \"Pragma: no-cache\""); if (!no_store) { /* register a ctx to give a chance to srcache_store to run */ ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_srcache_filter_module)); if (ctx == NULL) { return NGX_ERROR; } ngx_http_set_ctx(r, ctx, ngx_http_srcache_filter_module); } else { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "srcache_store skipped due to request header " "\"Cache-Control: no-store\""); } return NGX_DECLINED; } if (conf->fetch_skip != NULL && ngx_http_complex_value(r, conf->fetch_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_fetch skipped due to the true value fed into " "srcache_fetch_skip: \"%V\"", &skip); /* register a ctx to give a chance to srcache_store to run */ ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_srcache_filter_module)); if (ctx == NULL) { return NGX_ERROR; } ngx_http_set_ctx(r, ctx, ngx_http_srcache_filter_module); return NGX_DECLINED; } ctx = ngx_http_get_module_ctx(r, ngx_http_srcache_filter_module); if (ctx != NULL) { /* if (ctx->fetch_error) { return NGX_DECLINED; } */ if (ctx->waiting_subrequest) { dd("waiting subrequest"); return NGX_AGAIN; } if (ctx->waiting_request_body) { return NGX_AGAIN; } if (ctx->request_body_done == 1) { ctx->request_body_done = 0; goto do_fetch_subrequest; } if (ctx->request_done) { dd("request done"); if (ngx_http_post_request(r, NULL) != NGX_OK) { return NGX_ERROR; } if (!ctx->from_cache) { return NGX_DECLINED; } dd("sending header"); if (ctx->body_from_cache) { len = 0; for (cl = ctx->body_from_cache; cl->next; cl = cl->next) { len += ngx_buf_size(cl->buf); } len += ngx_buf_size(cl->buf); cl->buf->last_buf = 1; r->headers_out.content_length_n = len; rc = ngx_http_srcache_fetch_header_filter(r); dd("srcache fetch header returned %d", (int) rc); if (rc == NGX_ERROR || rc > NGX_OK) { return rc; } #if 1 if (r->header_only) { return NGX_HTTP_OK; } #endif if (!r->filter_finalize) { rc = ngx_http_srcache_next_body_filter(r, ctx->body_from_cache); if (rc == NGX_ERROR) { r->connection->error = 1; return NGX_ERROR; } if (rc > NGX_OK) { return rc; } } dd("sent body from cache: %d", (int) rc); dd("finalize from here..."); ngx_http_finalize_request(r, rc); /* dd("r->main->count (post): %d", (int) r->main->count); */ return NGX_DONE; } return NGX_DECLINED; } } else { ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_srcache_filter_module)); if (ctx == NULL) { return NGX_ERROR; } ngx_http_set_ctx(r, ctx, ngx_http_srcache_filter_module); } smcf = ngx_http_get_module_main_conf(r, ngx_http_srcache_filter_module); if (!smcf->postponed_to_access_phase_end) { ngx_http_core_main_conf_t *cmcf; ngx_http_phase_handler_t tmp; ngx_http_phase_handler_t *ph; ngx_http_phase_handler_t *cur_ph; ngx_http_phase_handler_t *last_ph; smcf->postponed_to_access_phase_end = 1; cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); ph = cmcf->phase_engine.handlers; cur_ph = &ph[r->phase_handler]; /* we should skip the post_access phase handler here too */ last_ph = &ph[cur_ph->next - 2]; if (cur_ph < last_ph) { dd("swaping the contents of cur_ph and last_ph..."); tmp = *cur_ph; memmove(cur_ph, cur_ph + 1, (last_ph - cur_ph) * sizeof (ngx_http_phase_handler_t)); *last_ph = tmp; r->phase_handler--; /* redo the current ph */ return NGX_DECLINED; } } if (conf->fetch == NULL) { dd("fetch is not defined"); return NGX_DECLINED; } dd("running phase handler..."); if (!r->request_body) { dd("reading request body: ctx = %p", ctx); rc = ngx_http_read_client_request_body(r, ngx_http_srcache_post_read_body); if (rc == NGX_ERROR || rc > NGX_OK) { #if (nginx_version < 1002006) \ || (nginx_version >= 1003000 && nginx_version < 1003009) r->main->count--; #endif return rc; } if (rc == NGX_AGAIN) { ctx->waiting_request_body = 1; return NGX_AGAIN; } /* rc == NGX_OK */ } do_fetch_subrequest: /* issue a subrequest to fetch cached stuff (if any) */ rc = ngx_http_srcache_fetch_subrequest(r, conf, ctx); if (rc != NGX_OK) { return rc; } ctx->waiting_subrequest = 1; dd("quit"); return NGX_AGAIN; }
static ngx_int_t ngx_http_spdy_serverpush_body_filter(ngx_http_request_t *r, ngx_chain_t *in) { off_t size; ngx_buf_t *b; ngx_chain_t *cl, *ll, *out, **ln; ngx_http_spdy_stream_t *stream; ngx_http_spdy_out_frame_t *frame; //ngx_int_t static_handler_return_value; ngx_output_chain_ctx_t *ctx; ctx = ngx_pcalloc(r->pool, sizeof(ngx_output_chain_ctx_t)); ngx_http_set_ctx(r, ctx, ngx_http_spdy_serverpush_filter_module); ngx_int_t ret_SH = ngx_http_static_handler(r); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "static_handler return : %d", ret_SH); stream = myStream; if (stream == NULL) { return ngx_http_next_body_filter(r, in); } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "spdy body filter \"%V?%V\"", &r->uri, &r->args); ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "Data in server Push "); if (myChain == NULL || r->header_only) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "Ending"); if (stream->waiting) { return NGX_AGAIN; } r->connection->buffered &= ~NGX_SPDY_WRITE_BUFFERED; return NGX_OK; } size = 0; ln = &out; ll = myChain; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "into for "); for ( ;; ) { b = ll->buf; //#if 1 if(b->file) { ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "file detected push : %d",(b->file_last-b->file_pos)); ngx_int_t my_size = b->file_last-b->file_pos; u_char* my_buf = ngx_pcalloc(r->pool,sizeof(u_char)*my_size); ngx_read_file(b->file, my_buf, my_size, 0); b->start = my_buf; b->pos = my_buf; b->last = my_buf+my_size;//+NGX_SPDY_FRAME_HEADER_SIZE; } if (ngx_buf_size(b) == 0 && !ngx_buf_special(b)) { ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "zero size buf in spdy body filter " "t:%d r:%d f:%d %p %p-%p %p %O-%O", b->temporary, b->recycled, b->in_file, b->start, b->pos, b->last, b->file, b->file_pos, b->file_last); ngx_debug_point(); return NGX_ERROR; } //#endif ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "out of for"); cl = ngx_alloc_chain_link(r->pool); if (cl == NULL) { return NGX_ERROR; } size += ngx_buf_size(b); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "Size %d ",size); cl->buf = b; *ln = cl; ln = &cl->next; if (ll->next == NULL) { break; } ll = ll->next; } if (size > NGX_SPDY_MAX_FRAME_SIZE) { ngx_log_error(NGX_LOG_ALERT, r->connection->log, 0, "FIXME: chain too big in spdy filter: %O", size); return NGX_ERROR; } ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ONE "); frame = ngx_http_spdy_filter_get_data_frame(stream, (size_t) size, b->last_buf, out, cl); ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "FOUR "); if (frame == NULL) { return NGX_ERROR; } ngx_http_spdy_queue_frame(stream->connection, frame); stream->waiting++; r->main->blocked++; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "Data in server Push end"); ngx_http_spdy_serverpush_filter_send(r->connection, stream); return ngx_http_next_body_filter(r,in); }
static ngx_int_t chunked_respond_message(subscriber_t *sub, nchan_msg_t *msg) { full_subscriber_t *fsub = (full_subscriber_t *)sub; nchan_request_ctx_t *ctx = ngx_http_get_module_ctx(fsub->sub.request, ngx_nchan_module); chunksizebuf_t *chunksizebuf = nchan_reuse_queue_push(ctx->output_str_queue); u_char *chunk_start = &chunksizebuf->chr[0]; static u_char *chunk_end=(u_char *)"\r\n"; ngx_file_t *file_copy; nchan_buf_and_chain_t *bc = nchan_bufchain_pool_reserve(ctx->bcp, 3); ngx_chain_t *chain; ngx_buf_t *buf, *msg_buf = &msg->buf; ngx_int_t rc; if(fsub->data.timeout_ev.timer_set) { ngx_del_timer(&fsub->data.timeout_ev); ngx_add_timer(&fsub->data.timeout_ev, sub->cf->subscriber_timeout * 1000); } ctx->prev_msg_id = fsub->sub.last_msgid; update_subscriber_last_msg_id(sub, msg); ctx->msg_id = fsub->sub.last_msgid; if (ngx_buf_size(msg_buf) == 0) { //empty messages are skipped, because a zero-length chunk finalizes the request return NGX_OK; } //chunk size chain = &bc->chain; buf = chain->buf; ngx_memzero(buf, sizeof(*buf)); buf->memory = 1; buf->start = chunk_start; buf->pos = chunk_start; buf->end = ngx_snprintf(chunk_start, 15, "%xi\r\n", ngx_buf_size(msg_buf)); buf->last = buf->end; //message chain = chain->next; buf = chain->buf; *buf = *msg_buf; if(buf->file) { file_copy = nchan_bufchain_pool_reserve_file(ctx->bcp); nchan_msg_buf_open_fd_if_needed(buf, file_copy, NULL); } buf->last_buf = 0; buf->last_in_chain = 0; buf->flush = 0; //trailing newlines chain = chain->next; buf = chain->buf; ngx_memzero(buf, sizeof(*buf)); buf->start = chunk_end; buf->pos = chunk_end; buf->end = chunk_end + 2; buf->last = buf->end; buf->memory = 1; buf->last_buf = 0; buf->last_in_chain = 1; buf->flush = 1; chunked_ensure_headers_sent(fsub); DBG("%p output msg to subscriber", sub); rc = nchan_output_msg_filter(fsub->sub.request, msg, &bc->chain); return rc; }
static ngx_int_t ngx_http_spdy_data_frame_handler(ngx_http_spdy_connection_t *sc, ngx_http_spdy_out_frame_t *frame) { ngx_chain_t *cl, *ln; ngx_http_spdy_stream_t *stream; stream = frame->stream; cl = frame->first; if (cl->buf->tag == (ngx_buf_tag_t) &ngx_http_spdy_serverpush_filter_module) { if (cl->buf->pos != cl->buf->last) { ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, "spdy:%ui DATA frame %p was sent partially", stream->id, frame); return NGX_AGAIN; } ln = cl->next; cl->next = stream->free_data_headers; stream->free_data_headers = cl; if (cl == frame->last) { goto done; } cl = ln; } for ( ;; ) { if (ngx_buf_size(cl->buf) != 0) { if (cl != frame->first) { frame->first = cl; ngx_http_spdy_handle_stream(sc, stream); } ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, "spdy:%ui DATA frame %p was sent partially", stream->id, frame); return NGX_AGAIN; } ln = cl->next; ngx_free_chain(stream->request->pool, cl); if (cl == frame->last) { goto done; } cl = ln; } done: ngx_log_debug2(NGX_LOG_DEBUG_HTTP, sc->connection->log, 0, "spdy:%ui DATA frame %p was sent", stream->id, frame); stream->request->header_size += NGX_SPDY_FRAME_HEADER_SIZE; ngx_http_spdy_handle_frame(stream, frame); ngx_http_spdy_handle_stream(sc, stream); return NGX_OK; }
static ngx_inline ngx_int_t ngx_http_modsecurity_save_request_body(ngx_http_request_t *r) { ngx_http_modsecurity_ctx_t *ctx; apr_off_t content_length; ngx_buf_t *buf; ngx_http_core_srv_conf_t *cscf; size_t size; ngx_http_connection_t *hc; ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity); apr_brigade_length(ctx->brigade, 0, &content_length); if (r->header_in->end - r->header_in->last >= content_length) { /* use r->header_in */ if (ngx_buf_size(r->header_in)) { /* move to the end */ ngx_memmove(r->header_in->pos + content_length, r->header_in->pos, ngx_buf_size(r->header_in)); } if (apr_brigade_flatten(ctx->brigade, (char *)r->header_in->pos, (apr_size_t *)&content_length) != APR_SUCCESS) { return NGX_ERROR; } apr_brigade_cleanup(ctx->brigade); r->header_in->last += content_length; return NGX_OK; } if (ngx_buf_size(r->header_in)) { /* * ngx_http_set_keepalive will reuse r->header_in if * (r->header_in != c->buffer && r->header_in.last != r->header_in.end), * so we need this code block. * see ngx_http_set_keepalive, ngx_http_alloc_large_header_buffer */ cscf = ngx_http_get_module_srv_conf(r, ngx_http_core_module); size = ngx_max(cscf->large_client_header_buffers.size, (size_t)content_length + ngx_buf_size(r->header_in)); hc = r->http_connection; #if defined(nginx_version) && nginx_version >= 1011011 if (hc->free && size == cscf->large_client_header_buffers.size) { buf = hc->free->buf; #else if (hc->nfree && size == cscf->large_client_header_buffers.size) { buf = hc->free[--hc->nfree]; #endif ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ModSecurity: use http free large header buffer: %p %uz", buf->pos, buf->end - buf->last); } else if (hc->nbusy < cscf->large_client_header_buffers.num) { if (hc->busy == NULL) { hc->busy = ngx_palloc(r->connection->pool, cscf->large_client_header_buffers.num * sizeof(ngx_buf_t *)); } if (hc->busy == NULL) { return NGX_ERROR; } else { buf = ngx_create_temp_buf(r->connection->pool, size); } } else { /* TODO: how to deal this case ? */ return NGX_ERROR; } } else { buf = ngx_create_temp_buf(r->pool, (size_t) content_length); } if (buf == NULL) { return NGX_ERROR; } if (apr_brigade_flatten(ctx->brigade, (char *)buf->pos, (apr_size_t *)&content_length) != APR_SUCCESS) { return NGX_ERROR; } apr_brigade_cleanup(ctx->brigade); buf->last += content_length; ngx_memcpy(buf->last, r->header_in->pos, ngx_buf_size(r->header_in)); buf->last += ngx_buf_size(r->header_in); r->header_in = buf; return NGX_OK; } static ngx_inline ngx_int_t ngx_http_modsecurity_load_headers_out(ngx_http_request_t *r) { ngx_http_modsecurity_ctx_t *ctx; char *data; request_rec *req; ngx_http_variable_value_t *vv; ngx_list_part_t *part; ngx_table_elt_t *h; ngx_uint_t i; char *key, *value; u_char *buf = NULL; size_t size = 0; ctx = ngx_http_get_module_ctx(r, ngx_http_modsecurity); req = ctx->req; req->status = r->headers_out.status; req->status_line = (char *)ngx_pstrdup0(r->pool, &r->headers_out.status_line); /* deep copy */ part = &r->headers_out.headers.part; h = part->elts; for (i = 0; ; i++) { if (i >= part->nelts) { if (part->next == NULL) break; part = part->next; h = part->elts; i = 0; } size += h[i].key.len + h[i].value.len + 2; buf = ngx_palloc(r->pool, size); if (buf == NULL) { return NGX_ERROR; } key = (char *)buf; buf = ngx_cpymem(buf, h[i].key.data, h[i].key.len); *buf++ = '\0'; value = (char *)buf; buf = ngx_cpymem(buf, h[i].value.data, h[i].value.len); *buf++ = '\0'; apr_table_addn(req->headers_out, key, value); ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ModSecurity: load headers out: \"%V: %V\"", &h[i].key, &h[i].value); } for (i = 0; special_headers_out[i].name; i++) { vv = ngx_http_get_variable(r, &special_headers_out[i].variable_name, ngx_hash_key(special_headers_out[i].variable_name.data, special_headers_out[i].variable_name.len)); if (vv && !vv->not_found) { data = ngx_palloc(r->pool, vv->len + 1); if (data == NULL) { return NGX_ERROR; } ngx_memcpy(data,vv->data, vv->len); data[vv->len] = '\0'; apr_table_setn(req->headers_out, special_headers_out[i].name, data); ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ModSecurity: load headers out: \"%s: %s\"", special_headers_out[i].name, data); } } req->content_type = apr_table_get(ctx->req->headers_out, "Content-Type"); req->content_encoding = apr_table_get(ctx->req->headers_out, "Content-Encoding"); data = (char *)apr_table_get(ctx->req->headers_out, "Content-Languages"); if(data != NULL) { ctx->req->content_languages = apr_array_make(ctx->req->pool, 1, sizeof(const char *)); *(const char **)apr_array_push(ctx->req->content_languages) = data; } /* req->chunked = r->chunked; may be useless */ req->clength = r->headers_out.content_length_n; req->mtime = apr_time_make(r->headers_out.last_modified_time, 0); ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "ModSecurity: load headers out done"); return NGX_OK; }
ngx_int_t ngx_http_rds_csv_process_field(ngx_http_request_t *r, ngx_chain_t *in, ngx_http_rds_csv_ctx_t *ctx) { size_t total, len; ngx_buf_t *b; ngx_int_t rc; for (;;) { if (in == NULL) { return NGX_OK; } b = in->buf; if (!ngx_buf_in_memory(b)) { dd("buf not in memory"); if (!ngx_buf_special(b)) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "rds_csv: process field: buf from " "upstream not in memory"); return NGX_ERROR; } in = in->next; if (in == NULL) { return NGX_OK; } b = in->buf; } dd("process field: buf size: %d", (int) ngx_buf_size(b)); if (b->last - b->pos < (ssize_t) sizeof(uint32_t)) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "rds_csv: field size is incomplete in the buf: %*s " "(len: %d)", b->last - b->pos, b->pos, (int) (b->last - b->pos)); return NGX_ERROR; } total = *(uint32_t *) b->pos; dd("total: %d", (int) total); b->pos += sizeof(uint32_t); if (total == (uint32_t) -1) { /* SQL NULL found */ total = 0; len = 0; ctx->field_data_rest = 0; rc = ngx_http_rds_csv_output_field(r, ctx, b->pos, len, 1 /* is null */); } else { len = (uint32_t) (b->last - b->pos); if (len >= total) { len = total; } ctx->field_data_rest = total - len; rc = ngx_http_rds_csv_output_field(r, ctx, b->pos, len, 0 /* not null */); } if (rc == NGX_ERROR || rc >= NGX_HTTP_SPECIAL_RESPONSE) { return rc; } b->pos += len; if (b->pos == b->last) { in = in->next; } if (len < total) { dd("process field: need to read more field data"); ctx->state = state_expect_more_field_data; return ngx_http_rds_csv_process_more_field_data(r, in, ctx); } ctx->cur_col++; if (ctx->cur_col >= ctx->col_count) { dd("reached the end of the current row"); ctx->state = state_expect_row; return ngx_http_rds_csv_process_row(r, in, ctx); } /* continue to process the next field (if any) */ } /* impossible to reach here */ return NGX_ERROR; }