#ifndef DDEBUG #define DDEBUG 0 #endif #include "ddebug.h" #include "ngx_http_redis2_handler.h" #include "ngx_http_redis2_reply.h" #include "ngx_http_redis2_util.h" static ngx_int_t ngx_http_redis2_create_request(ngx_http_request_t *r); static ngx_int_t ngx_http_redis2_reinit_request(ngx_http_request_t *r); static ngx_int_t ngx_http_redis2_process_header(ngx_http_request_t *r); static ngx_int_t ngx_http_redis2_filter_init(void *data); static ngx_int_t ngx_http_redis2_filter(void *data, ssize_t bytes); static void ngx_http_redis2_abort_request(ngx_http_request_t *r); static void ngx_http_redis2_finalize_request(ngx_http_request_t *r, ngx_int_t rc); ngx_int_t ngx_http_redis2_handler(ngx_http_request_t *r) { ngx_int_t rc; ngx_http_upstream_t *u; ngx_http_redis2_ctx_t *ctx; ngx_http_redis2_loc_conf_t *rlcf; ngx_str_t target; ngx_url_t url; if (ngx_http_set_content_type(r) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } if (ngx_http_upstream_create(r) != NGX_OK) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } u = r->upstream; rlcf = ngx_http_get_module_loc_conf(r, ngx_http_redis2_module); if (rlcf->complex_target) { /* variables used in the redis2_pass directive */ if (ngx_http_complex_value(r, rlcf->complex_target, &target) != NGX_OK) { return NGX_ERROR; } if (target.len == 0) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "handler: empty \"redis2_pass\" target"); return NGX_HTTP_INTERNAL_SERVER_ERROR; } url.host = target; url.port = 0; url.no_resolve = 1; rlcf->upstream.upstream = ngx_http_redis2_upstream_add(r, &url); if (rlcf->upstream.upstream == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "redis2: upstream \"%V\" not found", &target); return NGX_HTTP_INTERNAL_SERVER_ERROR; } } ngx_str_set(&u->schema, "redis2://"); u->output.tag = (ngx_buf_tag_t) &ngx_http_redis2_module; u->conf = &rlcf->upstream; u->create_request = ngx_http_redis2_create_request; u->reinit_request = ngx_http_redis2_reinit_request; u->process_header = ngx_http_redis2_process_header; u->abort_request = ngx_http_redis2_abort_request; u->finalize_request = ngx_http_redis2_finalize_request; ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_redis2_ctx_t)); if (ctx == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } ctx->request = r; ctx->state = NGX_ERROR; ngx_http_set_ctx(r, ctx, ngx_http_redis2_module); u->input_filter_init = ngx_http_redis2_filter_init; u->input_filter = ngx_http_redis2_filter; u->input_filter_ctx = ctx; rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init); if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { return rc; } return NGX_DONE; } static ngx_int_t ngx_http_redis2_create_request(ngx_http_request_t *r) { ngx_buf_t *b; ngx_chain_t *cl; ngx_http_redis2_loc_conf_t *rlcf; ngx_str_t query; ngx_str_t query_count; ngx_int_t rc; ngx_http_redis2_ctx_t *ctx; ngx_int_t n; ctx = ngx_http_get_module_ctx(r, ngx_http_redis2_module); rlcf = ngx_http_get_module_loc_conf(r, ngx_http_redis2_module); if (rlcf->queries) { ctx->query_count = rlcf->queries->nelts; rc = ngx_http_redis2_build_query(r, rlcf->queries, &b); if (rc != NGX_OK) { return rc; } } else if (rlcf->literal_query.len == 0) { if (rlcf->complex_query == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "no redis2 query specified or the query is empty"); return NGX_ERROR; } if (ngx_http_complex_value(r, rlcf->complex_query, &query) != NGX_OK) { return NGX_ERROR; } if (query.len == 0) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "the redis query is empty"); return NGX_ERROR; } if (rlcf->complex_query_count == NULL) { ctx->query_count = 1; } else { if (ngx_http_complex_value(r, rlcf->complex_query_count, &query_count) != NGX_OK) { return NGX_ERROR; } if (query_count.len == 0) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "the N argument to redis2_raw_queries is empty"); return NGX_ERROR; } n = ngx_atoi(query_count.data, query_count.len); if (n == NGX_ERROR || n == 0) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "the N argument to redis2_raw_queries is invalid"); return NGX_ERROR; } ctx->query_count = n; } b = ngx_create_temp_buf(r->pool, query.len); if (b == NULL) { return NGX_ERROR; } b->last = ngx_copy(b->pos, query.data, query.len); } else { ctx->query_count = 1; b = ngx_calloc_buf(r->pool); if (b == NULL) { return NGX_ERROR; } b->pos = rlcf->literal_query.data; b->last = b->pos + rlcf->literal_query.len; b->memory = 1; } cl = ngx_alloc_chain_link(r->pool); if (cl == NULL) { return NGX_ERROR; } cl->buf = b; cl->next = NULL; r->upstream->request_bufs = cl; ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http redis2 request: \"%V\"", &rlcf->literal_query); return NGX_OK; } static ngx_int_t ngx_http_redis2_reinit_request(ngx_http_request_t *r) { return NGX_OK; } static ngx_int_t ngx_http_redis2_process_header(ngx_http_request_t *r) { ngx_http_upstream_t *u; ngx_http_redis2_ctx_t *ctx; ngx_buf_t *b; u_char chr; ngx_str_t buf; u = r->upstream; b = &u->buffer; if (b->last - b->pos < (ssize_t) sizeof(u_char)) { return NGX_AGAIN; } ctx = ngx_http_get_module_ctx(r, ngx_http_redis2_module); /* the first char is the response header */ chr = *b->pos; dd("response header: %c (ascii %d)", chr, chr); switch (chr) { case '+': case '-': case ':': case '$': case '*': ctx->filter = ngx_http_redis2_process_reply; break; default: buf.data = b->pos; buf.len = b->last - b->pos; ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "redis2 sent invalid response: \"%V\"", &buf); return NGX_HTTP_UPSTREAM_INVALID_HEADER; } u->headers_in.status_n = NGX_HTTP_OK; u->state->status = NGX_HTTP_OK; return NGX_OK; } static ngx_int_t ngx_http_redis2_filter_init(void *data) { #if 0 ngx_http_redis2_ctx_t *ctx = data; ngx_http_upstream_t *u; u = ctx->request->upstream; #endif return NGX_OK; } static ngx_int_t ngx_http_redis2_filter(void *data, ssize_t bytes) { ngx_http_redis2_ctx_t *ctx = data; return ctx->filter(ctx, bytes); } static void ngx_http_redis2_abort_request(ngx_http_request_t *r) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "abort http redis2 request"); return; } static void ngx_http_redis2_finalize_request(ngx_http_request_t *r, ngx_int_t rc) { ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "finalize http redis2 request"); if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { r->headers_out.status = rc; } return; }