| |
| /* |
| * Copyright (C) Yichun Zhang (agentzh) |
| */ |
| |
| |
| #ifndef DDEBUG |
| #define DDEBUG 0 |
| #endif |
| #include "ddebug.h" |
| |
| #include "ngx_http_echo_request_info.h" |
| #include "ngx_http_echo_util.h" |
| #include "ngx_http_echo_handler.h" |
| |
| #include <nginx.h> |
| |
| |
| static void ngx_http_echo_post_read_request_body(ngx_http_request_t *r); |
| |
| |
| ngx_int_t |
| ngx_http_echo_exec_echo_read_request_body(ngx_http_request_t* r, |
| ngx_http_echo_ctx_t *ctx) |
| { |
| return ngx_http_read_client_request_body(r, |
| ngx_http_echo_post_read_request_body); |
| } |
| |
| |
| static void |
| ngx_http_echo_post_read_request_body(ngx_http_request_t *r) |
| { |
| ngx_http_echo_ctx_t *ctx; |
| |
| ctx = ngx_http_get_module_ctx(r, ngx_http_echo_module); |
| |
| dd("wait read request body %d", (int) ctx->wait_read_request_body); |
| |
| if (ctx->wait_read_request_body) { |
| ctx->waiting = 0; |
| ctx->done = 1; |
| |
| r->write_event_handler = ngx_http_echo_wev_handler; |
| |
| ngx_http_echo_wev_handler(r); |
| } |
| } |
| |
| |
| /* 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_method_variable(ngx_http_request_t *r, |
| ngx_http_variable_value_t *v, uintptr_t data) |
| { |
| if (r->method_name.data) { |
| v->len = r->method_name.len; |
| v->valid = 1; |
| v->no_cacheable = 0; |
| v->not_found = 0; |
| v->data = r->method_name.data; |
| |
| } else { |
| v->not_found = 1; |
| } |
| |
| return NGX_OK; |
| } |
| |
| |
| /* 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_client_request_method_variable(ngx_http_request_t *r, |
| ngx_http_variable_value_t *v, uintptr_t data) |
| { |
| if (r->main->method_name.data) { |
| v->len = r->main->method_name.len; |
| v->valid = 1; |
| v->no_cacheable = 0; |
| v->not_found = 0; |
| v->data = r->main->method_name.data; |
| |
| } else { |
| v->not_found = 1; |
| } |
| |
| return NGX_OK; |
| } |
| |
| |
| /* 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; |
| } |
| |
| |
| ngx_int_t |
| ngx_http_echo_client_request_headers_variable(ngx_http_request_t *r, |
| ngx_http_variable_value_t *v, uintptr_t data) |
| { |
| size_t size; |
| u_char *p, *last, *pos; |
| ngx_int_t i; |
| ngx_buf_t *b, *first = NULL; |
| unsigned found; |
| ngx_connection_t *c; |
| ngx_http_request_t *mr; |
| ngx_http_connection_t *hc; |
| |
| mr = r->main; |
| hc = r->main->http_connection; |
| c = mr->connection; |
| |
| size = 0; |
| b = c->buffer; |
| |
| if (mr->request_line.data >= b->start |
| && mr->request_line.data + mr->request_line.len + 2 <= b->pos) |
| { |
| first = b; |
| |
| if (mr->header_in == b) { |
| size += mr->header_end + 2 - mr->request_line.data; |
| |
| } else { |
| /* the subsequent part of the header is in the large header |
| * buffers */ |
| #if 1 |
| p = b->pos; |
| size += p - mr->request_line.data; |
| |
| /* skip truncated header entries (if any) */ |
| while (b->pos > b->start && b->pos[-1] != LF) { |
| b->pos--; |
| size--; |
| } |
| #endif |
| } |
| } |
| |
| if (hc->nbusy) { |
| b = NULL; |
| for (i = 0; i < hc->nbusy; i++) { |
| b = hc->busy[i]; |
| |
| if (first == NULL) { |
| if (mr->request_line.data >= b->pos |
| || mr->request_line.data + mr->request_line.len + 2 |
| <= b->start) |
| { |
| continue; |
| } |
| |
| dd("found first at %d", (int) i); |
| first = b; |
| } |
| |
| if (b == mr->header_in) { |
| size += mr->header_end + 2 - b->start; |
| break; |
| } |
| |
| size += b->pos - b->start; |
| } |
| } |
| |
| v->data = ngx_palloc(r->pool, size); |
| if (v->data == NULL) { |
| return NGX_ERROR; |
| } |
| |
| last = v->data; |
| |
| b = c->buffer; |
| if (first == b) { |
| if (mr->header_in == b) { |
| pos = mr->header_end + 2; |
| |
| } else { |
| pos = b->pos; |
| } |
| |
| last = ngx_copy(v->data, mr->request_line.data, |
| pos - mr->request_line.data); |
| |
| for (p = v->data; p != last; p++) { |
| if (*p == '\0') { |
| if (p + 1 != last && *(p + 1) == LF) { |
| *p = CR; |
| |
| } else { |
| *p = ':'; |
| } |
| } |
| } |
| } |
| |
| if (hc->nbusy) { |
| found = (b == c->buffer); |
| for (i = 0; i < hc->nbusy; i++) { |
| b = hc->busy[i]; |
| |
| if (!found) { |
| if (b != first) { |
| continue; |
| } |
| |
| dd("found first"); |
| found = 1; |
| } |
| |
| p = last; |
| |
| if (b == mr->header_in) { |
| pos = mr->header_end + 2; |
| |
| } else { |
| pos = b->pos; |
| } |
| |
| if (b == first) { |
| dd("request line: %.*s", (int) mr->request_line.len, |
| mr->request_line.data); |
| |
| last = ngx_copy(last, |
| mr->request_line.data, |
| pos - mr->request_line.data); |
| |
| } else { |
| last = ngx_copy(last, b->start, pos - b->start); |
| } |
| |
| #if 1 |
| /* skip truncated header entries (if any) */ |
| while (last > p && last[-1] != LF) { |
| last--; |
| } |
| #endif |
| |
| for (; p != last; p++) { |
| if (*p == '\0') { |
| if (p + 1 == last) { |
| /* XXX this should not happen */ |
| dd("found string end!!"); |
| |
| } else if (*(p + 1) == LF) { |
| *p = CR; |
| |
| } else { |
| *p = ':'; |
| } |
| } |
| } |
| |
| if (b == mr->header_in) { |
| break; |
| } |
| } |
| } |
| |
| if (last - v->data > (ssize_t) size) { |
| ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, |
| "buffer error when evaluating " |
| "$echo_client__request_headers: \"%V\"", |
| (ngx_int_t) (last - v->data - size)); |
| |
| return NGX_ERROR; |
| } |
| |
| v->len = last - v->data; |
| v->valid = 1; |
| v->no_cacheable = 0; |
| v->not_found = 0; |
| |
| return NGX_OK; |
| } |
| |
| |
| ngx_int_t |
| ngx_http_echo_cacheable_request_uri_variable(ngx_http_request_t *r, |
| ngx_http_variable_value_t *v, uintptr_t data) |
| { |
| if (r->uri.len) { |
| v->len = r->uri.len; |
| v->valid = 1; |
| v->no_cacheable = 0; |
| v->not_found = 0; |
| v->data = r->uri.data; |
| |
| } else { |
| v->not_found = 1; |
| } |
| |
| return NGX_OK; |
| } |
| |
| |
| ngx_int_t |
| ngx_http_echo_request_uri_variable(ngx_http_request_t *r, |
| ngx_http_variable_value_t *v, uintptr_t data) |
| { |
| if (r->uri.len) { |
| v->len = r->uri.len; |
| v->valid = 1; |
| v->no_cacheable = 1; |
| v->not_found = 0; |
| v->data = r->uri.data; |
| |
| } else { |
| v->not_found = 1; |
| } |
| |
| return NGX_OK; |
| } |
| |
| |
| ngx_int_t |
| ngx_http_echo_response_status_variable(ngx_http_request_t *r, |
| ngx_http_variable_value_t *v, uintptr_t data) |
| { |
| u_char *p; |
| |
| if (r->headers_out.status) { |
| dd("headers out status: %d", (int) r->headers_out.status); |
| |
| p = ngx_palloc(r->pool, NGX_INT_T_LEN); |
| if (p == NULL) { |
| return NGX_ERROR; |
| } |
| |
| v->len = ngx_sprintf(p, "%ui", r->headers_out.status) - p; |
| v->data = p; |
| |
| v->valid = 1; |
| v->no_cacheable = 1; |
| v->not_found = 0; |
| |
| } else { |
| v->not_found = 1; |
| } |
| |
| return NGX_OK; |
| } |
| |
| /* vi:set ft=c ts=4 sw=4 et fdm=marker: */ |