blob: 5cae9269dc4e9aab1b423868987010c4aaaccc1c [file] [log] [blame] [raw]
/*
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef DDEBUG
#define DDEBUG 0
#endif
#include "ddebug.h"
#include "ngx_http_echo_handler.h"
#include "ngx_http_echo_filter.h"
#include "ngx_http_echo_echo.h"
#include "ngx_http_echo_request_info.h"
#include "ngx_http_echo_var.h"
#include "ngx_http_echo_util.h"
#include <nginx.h>
#include <ngx_config.h>
#include <ngx_log.h>
/* config init handler */
static void * ngx_http_echo_create_loc_conf(ngx_conf_t *cf);
static char * ngx_http_echo_merge_loc_conf(ngx_conf_t *cf, void *parent,
void *child);
static void *ngx_http_echo_create_main_conf(ngx_conf_t *cf);
static ngx_int_t ngx_http_echo_post_config(ngx_conf_t *cf);
/* config directive handlers */
static char * ngx_http_echo_echo(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char * ngx_http_echo_echo_request_body(ngx_conf_t *cf,
ngx_command_t *cmd, void *conf);
static char * ngx_http_echo_echo_sleep(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char * ngx_http_echo_echo_flush(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char * ngx_http_echo_echo_blocking_sleep(ngx_conf_t *cf,
ngx_command_t *cmd, void *conf);
static char * ngx_http_echo_echo_reset_timer(ngx_conf_t *cf,
ngx_command_t *cmd, void *conf);
static char * ngx_http_echo_echo_before_body(ngx_conf_t *cf,
ngx_command_t *cmd, void *conf);
static char * ngx_http_echo_echo_after_body(ngx_conf_t *cf,
ngx_command_t *cmd, void *conf);
static char * ngx_http_echo_echo_location_async(ngx_conf_t *cf,
ngx_command_t *cmd, void *conf);
static char * ngx_http_echo_echo_location(ngx_conf_t *cf,
ngx_command_t *cmd, void *conf);
static char * ngx_http_echo_echo_subrequest_async(ngx_conf_t *cf,
ngx_command_t *cmd, void *conf);
static char * ngx_http_echo_echo_subrequest(ngx_conf_t *cf,
ngx_command_t *cmd, void *conf);
static char * ngx_http_echo_echo_duplicate(ngx_conf_t *cf,
ngx_command_t *cmd, void *conf);
static char * ngx_http_echo_echo_read_request_body(ngx_conf_t *cf,
ngx_command_t *cmd, void *conf);
static char * ngx_http_echo_echo_foreach_split(ngx_conf_t *cf,
ngx_command_t *cmd, void *conf);
static char * ngx_http_echo_echo_end(ngx_conf_t *cf,
ngx_command_t *cmd, void *conf);
static char * ngx_http_echo_echo_abort_parent(ngx_conf_t *cf,
ngx_command_t *cmd, void *conf);
static char * ngx_http_echo_echo_exec(ngx_conf_t *cf,
ngx_command_t *cmd, void *conf);
static char * ngx_http_echo_helper(ngx_http_echo_opcode_t opcode,
ngx_http_echo_cmd_category_t cat,
ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static ngx_http_module_t ngx_http_echo_module_ctx = {
NULL, /* preconfiguration */
ngx_http_echo_post_config, /* postconfiguration */
ngx_http_echo_create_main_conf, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_http_echo_create_loc_conf, /* create location configuration */
ngx_http_echo_merge_loc_conf /* merge location configuration */
};
static ngx_command_t ngx_http_echo_commands[] = {
{ ngx_string("echo"),
NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_ANY,
ngx_http_echo_echo,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_echo_loc_conf_t, handler_cmds),
NULL },
{ ngx_string("echo_request_body"),
NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_NOARGS,
ngx_http_echo_echo_request_body,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_echo_loc_conf_t, handler_cmds),
NULL },
{ ngx_string("echo_sleep"),
NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,
ngx_http_echo_echo_sleep,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_echo_loc_conf_t, handler_cmds),
NULL },
{ ngx_string("echo_flush"),
NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_NOARGS,
ngx_http_echo_echo_flush,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_echo_loc_conf_t, handler_cmds),
NULL },
{ ngx_string("echo_blocking_sleep"),
NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,
ngx_http_echo_echo_blocking_sleep,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_echo_loc_conf_t, handler_cmds),
NULL },
{ ngx_string("echo_reset_timer"),
NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_NOARGS,
ngx_http_echo_echo_reset_timer,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_echo_loc_conf_t, handler_cmds),
NULL },
{ ngx_string("echo_before_body"),
NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_ANY,
ngx_http_echo_echo_before_body,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_echo_loc_conf_t, before_body_cmds),
NULL },
{ ngx_string("echo_after_body"),
NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_ANY,
ngx_http_echo_echo_after_body,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_echo_loc_conf_t, after_body_cmds),
NULL },
{ ngx_string("echo_location_async"),
NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE12,
ngx_http_echo_echo_location_async,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_echo_loc_conf_t, handler_cmds),
NULL },
{ ngx_string("echo_location"),
NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE12,
ngx_http_echo_echo_location,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_echo_loc_conf_t, handler_cmds),
NULL },
{ ngx_string("echo_subrequest_async"),
NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_2MORE,
ngx_http_echo_echo_subrequest_async,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_echo_loc_conf_t, handler_cmds),
NULL },
{ ngx_string("echo_subrequest"),
NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_2MORE,
ngx_http_echo_echo_subrequest,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_echo_loc_conf_t, handler_cmds),
NULL },
{ ngx_string("echo_duplicate"),
NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_2MORE,
ngx_http_echo_echo_duplicate,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_echo_loc_conf_t, handler_cmds),
NULL },
{ ngx_string("echo_read_request_body"),
NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_NOARGS,
ngx_http_echo_echo_read_request_body,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_echo_loc_conf_t, handler_cmds),
NULL },
{ ngx_string("echo_foreach_split"),
NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_2MORE,
ngx_http_echo_echo_foreach_split,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_echo_loc_conf_t, handler_cmds),
NULL },
{ ngx_string("echo_end"),
NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_NOARGS,
ngx_http_echo_echo_end,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_echo_loc_conf_t, handler_cmds),
NULL },
{ ngx_string("echo_abort_parent"),
NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_NOARGS,
ngx_http_echo_echo_abort_parent,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_echo_loc_conf_t, handler_cmds),
NULL },
{ ngx_string("echo_exec"),
NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE12,
ngx_http_echo_echo_exec,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_echo_loc_conf_t, handler_cmds),
NULL },
{ ngx_string("echo_status"),
NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_TAKE1,
ngx_conf_set_num_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_echo_loc_conf_t, status),
NULL },
ngx_null_command
};
ngx_module_t ngx_http_echo_module = {
NGX_MODULE_V1,
&ngx_http_echo_module_ctx, /* module context */
ngx_http_echo_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static void *
ngx_http_echo_create_loc_conf(ngx_conf_t *cf)
{
ngx_http_echo_loc_conf_t *conf;
conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_echo_loc_conf_t));
if (conf == NULL) {
return NGX_CONF_ERROR;
}
/* set by ngx_pcalloc
* conf->handler_cmds = NULL
* conf->before_body_cmds = NULL
* conf->after_body_cmds = NULL
* conf->seen_leading_output = 0
* conf->seen_trailing_output = 0
*/
conf->status = NGX_CONF_UNSET;
return conf;
}
static char *
ngx_http_echo_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_http_echo_loc_conf_t *prev = parent;
ngx_http_echo_loc_conf_t *conf = child;
if (conf->handler_cmds == NULL) {
conf->handler_cmds = prev->handler_cmds;
conf->seen_leading_output = prev->seen_leading_output;
}
if (conf->before_body_cmds == NULL) {
conf->before_body_cmds = prev->before_body_cmds;
}
if (conf->after_body_cmds == NULL) {
conf->after_body_cmds = prev->after_body_cmds;
}
ngx_conf_merge_value(conf->status, prev->status, 200);
return NGX_CONF_OK;
}
static char *
ngx_http_echo_helper(ngx_http_echo_opcode_t opcode,
ngx_http_echo_cmd_category_t cat,
ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_str_t *raw_args;
ngx_uint_t i, n;
ngx_array_t **args_ptr;
ngx_array_t **cmds_ptr;
ngx_http_echo_cmd_t *echo_cmd;
ngx_http_core_loc_conf_t *clcf;
ngx_http_script_compile_t sc;
ngx_http_echo_main_conf_t *emcf;
ngx_http_echo_arg_template_t *arg;
emcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_echo_module);
/* cmds_ptr points to ngx_http_echo_loc_conf_t's
* handler_cmds, before_body_cmds, or after_body_cmds
* array, depending on the actual offset */
cmds_ptr = (ngx_array_t**)(((u_char*)conf) + cmd->offset);
if (*cmds_ptr == NULL) {
*cmds_ptr = ngx_array_create(cf->pool, 1,
sizeof(ngx_http_echo_cmd_t));
if (*cmds_ptr == NULL) {
return NGX_CONF_ERROR;
}
if (cat == echo_handler_cmd) {
dd("registering the content handler");
/* register the content handler */
clcf = ngx_http_conf_get_module_loc_conf(cf,
ngx_http_core_module);
dd("registering the content handler (2)");
clcf->handler = ngx_http_echo_handler;
} else {
dd("filter used = 1");
emcf->requires_filter = 1;
}
}
echo_cmd = ngx_array_push(*cmds_ptr);
if (echo_cmd == NULL) {
return NGX_CONF_ERROR;
}
echo_cmd->opcode = opcode;
args_ptr = &echo_cmd->args;
*args_ptr = ngx_array_create(cf->pool, 1,
sizeof(ngx_http_echo_arg_template_t));
if (*args_ptr == NULL) {
return NGX_CONF_ERROR;
}
raw_args = cf->args->elts;
/* we skip the first arg and start from the second */
for (i = 1 ; i < cf->args->nelts; i++) {
arg = ngx_array_push(*args_ptr);
if (arg == NULL) {
return NGX_CONF_ERROR;
}
arg->raw_value = raw_args[i];
dd("found raw arg %s", raw_args[i].data);
arg->lengths = NULL;
arg->values = NULL;
n = ngx_http_script_variables_count(&arg->raw_value);
if (n > 0) {
ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
sc.cf = cf;
sc.source = &arg->raw_value;
sc.lengths = &arg->lengths;
sc.values = &arg->values;
sc.variables = n;
sc.complete_lengths = 1;
sc.complete_values = 1;
if (ngx_http_script_compile(&sc) != NGX_OK) {
return NGX_CONF_ERROR;
}
}
} /* end for */
return NGX_CONF_OK;
}
static char *
ngx_http_echo_echo(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_echo_loc_conf_t *elcf = conf;
if (!elcf->seen_leading_output) {
elcf->seen_leading_output = 1;
}
dd("in echo_echo...");
return ngx_http_echo_helper(echo_opcode_echo, echo_handler_cmd,
cf, cmd, conf);
}
static char *
ngx_http_echo_echo_request_body(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf)
{
ngx_http_echo_loc_conf_t *elcf = conf;
if (!elcf->seen_leading_output) {
elcf->seen_leading_output = 1;
}
dd("in echo_echo_request_body...");
return ngx_http_echo_helper(echo_opcode_echo_request_body, echo_handler_cmd,
cf, cmd, conf);
}
static char *
ngx_http_echo_echo_sleep(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
dd("in echo_sleep...");
return ngx_http_echo_helper(echo_opcode_echo_sleep, echo_handler_cmd,
cf, cmd, conf);
}
static char *
ngx_http_echo_echo_flush(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_echo_loc_conf_t *elcf = conf;
if (!elcf->seen_leading_output) {
elcf->seen_leading_output = 1;
}
dd("in echo_flush...");
return ngx_http_echo_helper(echo_opcode_echo_flush, echo_handler_cmd,
cf, cmd, conf);
}
static char *
ngx_http_echo_echo_blocking_sleep(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf)
{
dd("in echo_blocking_sleep...");
return ngx_http_echo_helper(echo_opcode_echo_blocking_sleep,
echo_handler_cmd, cf, cmd, conf);
}
static char *
ngx_http_echo_echo_reset_timer(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
return ngx_http_echo_helper(echo_opcode_echo_reset_timer, echo_handler_cmd,
cf, cmd, conf);
}
static char *
ngx_http_echo_echo_before_body(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
dd("processing echo_before_body directive...");
return ngx_http_echo_helper(echo_opcode_echo_before_body, echo_filter_cmd,
cf, cmd, conf);
}
static char *
ngx_http_echo_echo_after_body(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
return ngx_http_echo_helper(echo_opcode_echo_after_body, echo_filter_cmd,
cf, cmd, conf);
}
static char *
ngx_http_echo_echo_location_async(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf)
{
ngx_http_echo_loc_conf_t *elcf = conf;
char *ret;
if (!elcf->seen_leading_output) {
elcf->seen_leading_output = 1;
ret = ngx_http_echo_helper(echo_opcode_echo_sync, echo_handler_cmd,
cf, cmd, conf);
if (ret != NGX_CONF_OK) {
return ret;
}
}
return ngx_http_echo_helper(echo_opcode_echo_location_async,
echo_handler_cmd, cf, cmd, conf);
}
static char *
ngx_http_echo_echo_location(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_echo_loc_conf_t *elcf = conf;
char *ret;
if (!elcf->seen_leading_output) {
elcf->seen_leading_output = 1;
ret = ngx_http_echo_helper(echo_opcode_echo_sync, echo_handler_cmd,
cf, cmd, conf);
if (ret != NGX_CONF_OK) {
return ret;
}
}
return ngx_http_echo_helper(echo_opcode_echo_location, echo_handler_cmd,
cf, cmd, conf);
}
static char *
ngx_http_echo_echo_subrequest_async(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf)
{
char *ret;
ngx_http_echo_loc_conf_t *elcf = conf;
if (!elcf->seen_leading_output) {
elcf->seen_leading_output = 1;
ret = ngx_http_echo_helper(echo_opcode_echo_sync, echo_handler_cmd,
cf, cmd, conf);
if (ret != NGX_CONF_OK) {
return ret;
}
}
return ngx_http_echo_helper(echo_opcode_echo_subrequest_async,
echo_handler_cmd, cf, cmd, conf);
}
static char *
ngx_http_echo_echo_subrequest(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf)
{
ngx_http_echo_loc_conf_t *elcf = conf;
char *ret;
if (!elcf->seen_leading_output) {
elcf->seen_leading_output = 1;
ret = ngx_http_echo_helper(echo_opcode_echo_sync, echo_handler_cmd,
cf, cmd, conf);
if (ret != NGX_CONF_OK) {
return ret;
}
}
return ngx_http_echo_helper(echo_opcode_echo_subrequest, echo_handler_cmd,
cf, cmd, conf);
}
static char *
ngx_http_echo_echo_duplicate(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_echo_loc_conf_t *elcf = conf;
if (!elcf->seen_leading_output) {
elcf->seen_leading_output = 1;
}
return ngx_http_echo_helper(echo_opcode_echo_duplicate, echo_handler_cmd,
cf, cmd, conf);
}
static char *
ngx_http_echo_echo_read_request_body(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf)
{
return ngx_http_echo_helper(echo_opcode_echo_read_request_body,
echo_handler_cmd, cf, cmd, conf);
}
static char *
ngx_http_echo_echo_foreach_split(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
return ngx_http_echo_helper(echo_opcode_echo_foreach_split,
echo_handler_cmd, cf, cmd, conf);
}
static char *
ngx_http_echo_echo_end(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
return ngx_http_echo_helper(echo_opcode_echo_end, echo_handler_cmd, cf,
cmd, conf);
}
static char *
ngx_http_echo_echo_abort_parent(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
return ngx_http_echo_helper(echo_opcode_echo_abort_parent, echo_handler_cmd,
cf, cmd, conf);
}
static char *
ngx_http_echo_echo_exec(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
return ngx_http_echo_helper(echo_opcode_echo_exec, echo_handler_cmd,
cf, cmd, conf);
}
static void *
ngx_http_echo_create_main_conf(ngx_conf_t *cf)
{
ngx_http_echo_main_conf_t *emcf;
emcf = ngx_pcalloc(cf->pool, sizeof(ngx_http_echo_main_conf_t));
if (emcf == NULL) {
return NULL;
}
/* set by ngx_pcalloc:
* hmcf->requires_filter = 0;
*/
return emcf;
}
static ngx_int_t
ngx_http_echo_post_config(ngx_conf_t *cf)
{
ngx_int_t rc;
rc = ngx_http_echo_filter_init(cf);
if (rc != NGX_OK) {
return rc;
}
rc = ngx_http_echo_echo_init(cf);
if (rc != NGX_OK) {
return rc;
}
ngx_http_echo_content_length_hash =
ngx_http_echo_hash_literal("content-length");
return ngx_http_echo_add_variables(cf);
}