static mrb_value ngx_mrb_send_header(mrb_state *mrb, mrb_value self) { ngx_mrb_rputs_chain_list_t *chain = NULL; ngx_http_mruby_ctx_t *ctx; ngx_http_request_t *r = ngx_mrb_get_request(); mrb_int status = NGX_HTTP_OK; mrb_get_args(mrb, "i", &status); r->headers_out.status = status; ctx = ngx_http_get_module_ctx(r, ngx_http_mruby_module); if (ctx == NULL) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "%s ERROR %s: get mruby context failed.", MODULE_NAME, __func__); mrb_raise(mrb, E_RUNTIME_ERROR, "get mruby context failed"); } chain = ctx->rputs_chain; if (chain) { (*chain->last)->buf->last_buf = 1; } if (r->headers_out.status == NGX_HTTP_OK) { if (chain == NULL) { r->headers_out.status = NGX_HTTP_INTERNAL_SERVER_ERROR; ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "%s ERROR %s: status code is 200, but response body is empty." "Return NGX_HTTP_INTERNAL_SERVER_ERROR", MODULE_NAME, __func__); } } return self; }
static mrb_value ngx_mrb_errlogger(mrb_state *mrb, mrb_value self) { mrb_value *argv; mrb_value msg; mrb_int argc; mrb_int log_level; ngx_http_request_t *r = ngx_mrb_get_request(); if (r == NULL) { mrb_raise(mrb, E_RUNTIME_ERROR, "can't use logger at this phase. only use at request phase"); } mrb_get_args(mrb, "*", &argv, &argc); if (argc != 2) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "%s ERROR %s: argument is not 2", MODULE_NAME, __func__); return self; } if (mrb_type(argv[0]) != MRB_TT_FIXNUM) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "%s ERROR %s: argv[0] is not integer", MODULE_NAME, __func__); return self; } log_level = mrb_fixnum(argv[0]); if (log_level < 0) { ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "%s ERROR %s: log level is not positive number", MODULE_NAME, __func__); return self; } if (mrb_type(argv[1]) != MRB_TT_STRING) { msg = mrb_funcall(mrb, argv[1], "to_s", 0, NULL); } else { msg = mrb_str_dup(mrb, argv[1]); } ngx_log_error((ngx_uint_t)log_level, r->connection->log, 0, "%s", mrb_str_to_cstr(mrb, msg)); return self; }
static mrb_value ngx_mrb_get_filter_body(mrb_state *mrb, mrb_value self) { ngx_http_request_t *r = ngx_mrb_get_request(); ngx_http_mruby_ctx_t *ctx = ngx_mrb_http_get_module_ctx(mrb, r); return mrb_str_new(mrb, (char *)ctx->body, ctx->body_length); }
static mrb_value ngx_mrb_var_method_missing(mrb_state *mrb, mrb_value self) { mrb_value name, *a; int alen, c_len; mrb_value s_name; char *c_name; ngx_http_request_t *r; r = ngx_mrb_get_request(); // get var symble from method_missing(sym, *args) mrb_get_args(mrb, "n*", &name, &a, &alen); // name is a symble obj // first init name with mrb_symbol // second get mrb_string with mrb_sym2str s_name = mrb_sym2str(mrb, mrb_symbol(name)); c_name = mrb_str_to_cstr(mrb, s_name); c_len = RSTRING_LEN(s_name); if (c_name[c_len-1] == '=') { return ngx_mrb_var_set(mrb, self, strtok(c_name, "="), a[0], r); } else { return ngx_mrb_var_get(mrb, self, c_name, c_len, r); } }
static mrb_value ngx_mrb_set_request_headers_out(mrb_state *mrb, mrb_value self) { ngx_http_request_t *r; r = ngx_mrb_get_request(); ngx_mrb_set_request_header(mrb, &r->headers_out.headers, 1); return self; }
static mrb_value ngx_mrb_send_header(mrb_state *mrb, mrb_value self) { rputs_chain_list_t *chain; ngx_mruby_ctx_t *ctx; ngx_http_request_t *r = ngx_mrb_get_request(); mrb_int status = NGX_HTTP_OK; mrb_get_args(mrb, "i", &status); r->headers_out.status = status; ctx = ngx_http_get_module_ctx(r, ngx_http_mruby_module); if (ctx == NULL) { ngx_log_error(NGX_LOG_ERR , r->connection->log , 0 , "get mruby context failed." ); } chain = ctx->rputs_chain; (*chain->last)->buf->last_buf = 1; if (r->headers_out.status == NGX_HTTP_OK) { ngx_http_send_header(r); ngx_http_output_filter(r, chain->out); ngx_http_set_ctx(r, NULL, ngx_http_mruby_module); } return self; }
static mrb_value ngx_mrb_set_request_uri(mrb_state *mrb, mrb_value self) { mrb_value arg; u_char *str; ngx_http_request_t *r = ngx_mrb_get_request(); mrb_get_args(mrb, "o", &arg); str = (u_char *)RSTRING_PTR(arg); ngx_str_set(&r->uri, str); return self; }
static mrb_value ngx_mrb_rputs(mrb_state *mrb, mrb_value self) { mrb_value argv; ngx_buf_t *b; ngx_mrb_rputs_chain_list_t *chain; u_char *str; ngx_str_t ns; ngx_http_request_t *r = ngx_mrb_get_request(); ngx_http_mruby_ctx_t *ctx = ngx_http_get_module_ctx(r, ngx_http_mruby_module); mrb_get_args(mrb, "o", &argv); if (mrb_type(argv) != MRB_TT_STRING) { argv = mrb_funcall(mrb, argv, "to_s", 0, NULL); } ns.data = (u_char *)RSTRING_PTR(argv); ns.len = ngx_strlen(ns.data); if (ns.len == 0) { return self; } if (ctx->rputs_chain == NULL) { chain = ngx_pcalloc(r->pool, sizeof(ngx_mrb_rputs_chain_list_t)); chain->out = ngx_alloc_chain_link(r->pool); chain->last = &chain->out; } else { chain = ctx->rputs_chain; (*chain->last)->next = ngx_alloc_chain_link(r->pool); chain->last = &(*chain->last)->next; } b = ngx_calloc_buf(r->pool); (*chain->last)->buf = b; (*chain->last)->next = NULL; str = ngx_pstrdup(r->pool, &ns); str[ns.len] = '\0'; (*chain->last)->buf->pos = str; (*chain->last)->buf->last = str + ns.len; (*chain->last)->buf->memory = 1; ctx->rputs_chain = chain; ngx_http_set_ctx(r, ctx, ngx_http_mruby_module); if (r->headers_out.content_length_n == -1) { r->headers_out.content_length_n += ns.len + 1; } else { r->headers_out.content_length_n += ns.len; } return self; }
static mrb_value ngx_mrb_set_filter_body(mrb_state *mrb, mrb_value self) { ngx_http_request_t *r = ngx_mrb_get_request(); ngx_http_mruby_ctx_t *ctx = ngx_mrb_http_get_module_ctx(mrb, r); mrb_value body; mrb_get_args(mrb, "o", &body); if (mrb_type(body) != MRB_TT_STRING) { body = mrb_funcall(mrb, body, "to_s", 0, NULL); } ctx->body_length = RSTRING_LEN(body); ctx->body = ngx_palloc(r->pool, ctx->body_length); ngx_memcpy(ctx->body, RSTRING_PTR(body), ctx->body_length); return mrb_fixnum_value(ctx->body_length); }
static mrb_value ngx_mrb_set_content_type(mrb_state *mrb, mrb_value self) { mrb_value arg; u_char *str; ngx_http_request_t *r = ngx_mrb_get_request(); mrb_get_args(mrb, "o", &arg); if (mrb_nil_p(arg)) return self; str = (u_char *)RSTRING_PTR(arg); //ngx_str_set(&r->headers_out.content_type, str); r->headers_out.content_type.len = ngx_strlen(str) + 1; r->headers_out.content_type.data = (u_char *)str; return self; }
// like Nginx rewrite keywords // used like this: // => http code 3xx location in browser // => internal redirection in nginx static mrb_value ngx_mrb_redirect(mrb_state *mrb, mrb_value self) { int argc; u_char *str; ngx_int_t rc; mrb_value uri, code; ngx_str_t ns; ngx_table_elt_t *location; ngx_http_request_t *r = ngx_mrb_get_request(); argc = mrb_get_args(mrb, "o|oo", &uri, &code); // get status code from args if (argc == 2) { rc = mrb_fixnum(code); } else { rc = NGX_HTTP_MOVED_TEMPORARILY; } // get redirect uri from args if (mrb_type(uri) != MRB_TT_STRING) { uri = mrb_funcall(mrb, uri, "to_s", 0, NULL); } // save location uri to ns ns.len = RSTRING_LEN(uri); if (ns.len == 0) { return mrb_nil_value(); } ns.data = ngx_palloc(r->pool, ns.len); ngx_memcpy(ns.data, RSTRING_PTR(uri), ns.len); // if uri start with scheme prefix // return 3xx for redirect // else generate a internal redirection and response to raw request // request.path is not changed if (ngx_strncmp(ns.data, "http://", sizeof("http://") - 1) == 0 || ngx_strncmp(ns.data, "https://", sizeof("https://") - 1) == 0 || ngx_strncmp(ns.data, "$scheme", sizeof("$scheme") - 1) == 0) { str = ngx_pstrdup(r->pool, &ns); if (str == NULL) { mrb_raise(mrb, E_RUNTIME_ERROR, "failed to allocate memory"); } str[ns.len] = '\0'; // build redirect location location = ngx_list_push(&r->headers_out.headers); if (location == NULL) { mrb_raise(mrb, E_RUNTIME_ERROR, "failed to allocate memory"); } location->hash = 1; ngx_str_set(&location->key, "Location"); location->value = ns; location->lowcase_key = ngx_pnalloc(r->pool, location->value.len); if (location->lowcase_key == NULL) { mrb_raise(mrb, E_RUNTIME_ERROR, "failed to allocate memory"); } ngx_strlow(location->lowcase_key, location->value.data, location->value.len); // set location and response code for hreaders r->headers_out.location = location; r->headers_out.status = rc; } else { ngx_http_internal_redirect(r, &ns, &r->args); ngx_http_finalize_request(r, NGX_DONE); } return self; }
static mrb_value ngx_mrb_get_request_headers_in(mrb_state *mrb, mrb_value self) { ngx_http_request_t *r; r = ngx_mrb_get_request(); return ngx_mrb_get_request_header(mrb, &r->headers_in.headers); }
static mrb_value ngx_mrb_get_request_uri(mrb_state *mrb, mrb_value self) { ngx_http_request_t *r = ngx_mrb_get_request(); return mrb_str_new_cstr(mrb, (const char *)r->uri.data); }
static mrb_value ngx_mrb_get_content_type(mrb_state *mrb, mrb_value self) { ngx_http_request_t *r = ngx_mrb_get_request(); return mrb_str_new_cstr(mrb, (char *)r->headers_out.content_type.data); }
static mrb_value ngx_mrb_var_set_func(mrb_state *mrb, mrb_value self) { ngx_http_request_t *r; ngx_http_variable_t *v; ngx_http_variable_value_t *vv; ngx_http_core_main_conf_t *cmcf; ngx_str_t key, val; ngx_uint_t hash; mrb_value k; mrb_value o; u_char *keyp, *valp; mrb_get_args(mrb, "oo", &k, &o); if (mrb_type(k) != MRB_TT_STRING) { k = mrb_funcall(mrb, k, "to_s", 0, NULL); } if (mrb_type(o) != MRB_TT_STRING) { o = mrb_funcall(mrb, o, "to_s", 0, NULL); } r = ngx_mrb_get_request(); key.data = (u_char *)RSTRING_PTR(k); key.len = RSTRING_LEN(k); val.data = (u_char *)RSTRING_PTR(o); val.len = RSTRING_LEN(o); /* RSTRING_PTR(k) is not always null-terminated */ keyp = ngx_palloc(r->pool, key.len + 1); if (keyp == NULL) { ngx_log_error(NGX_LOG_ERR , r->connection->log , 0 , "%s ERROR %s:%d: memory allocate failed" , MODULE_NAME , __func__ , __LINE__ ); goto ARENA_RESTOR_AND_ERROR; } /* RSTRING_PTR(o) is not always null-terminated */ valp = ngx_palloc(r->pool, val.len + 1); if (valp == NULL) { ngx_log_error(NGX_LOG_ERR , r->connection->log , 0 , "%s ERROR %s:%d: memory allocate failed" , MODULE_NAME , __func__ , __LINE__ ); goto ARENA_RESTOR_AND_ERROR; } ngx_cpystrn(keyp, key.data, key.len + 1); ngx_cpystrn(valp, val.data, val.len + 1); hash = ngx_hash_strlow(key.data, key.data, key.len); cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module); v = ngx_hash_find(&cmcf->variables_hash, hash, key.data, key.len); if (v) { if (!(v->flags & NGX_HTTP_VAR_CHANGEABLE)) { ngx_log_error(NGX_LOG_ERR , r->connection->log , 0 , "%s ERROR %s:%d: %s not changeable" , MODULE_NAME , __func__ , __LINE__ , keyp ); goto ARENA_RESTOR_AND_ERROR; } else if (v->set_handler) { vv = ngx_palloc(r->pool, sizeof(ngx_http_variable_value_t)); if (vv == NULL) { ngx_log_error(NGX_LOG_ERR , r->connection->log , 0 , "%s ERROR %s:%d: memory allocate failed" , MODULE_NAME , __func__ , __LINE__ ); goto ARENA_RESTOR_AND_ERROR; } v->set_handler(r, vv, v->data); } else if (v->flags & NGX_HTTP_VAR_INDEXED) { vv = &r->variables[v->index]; } else { ngx_log_error(NGX_LOG_ERR , r->connection->log , 0 , "%s ERROR %s:%d: %s is not assinged" , MODULE_NAME , __func__ , __LINE__ , keyp ); goto ARENA_RESTOR_AND_ERROR; } vv->valid = 1; vv->not_found = 0; vv->no_cacheable = 0; vv->data = val.data; vv->len = val.len; ngx_log_error(NGX_LOG_INFO , r->connection->log , 0 , "%s INFO %s:%d: set variable key:%s val:%s" , MODULE_NAME , __func__ , __LINE__ , keyp , valp ); return mrb_str_new(mrb, (char *)vv->data, vv->len); } ngx_log_error(NGX_LOG_ERR , r->connection->log , 0 , "%s ERROR %s:%d: %s is not found" , MODULE_NAME , __func__ , __LINE__ , keyp ); ARENA_RESTOR_AND_ERROR: return mrb_nil_value(); }
// like Nginx rewrite keywords // used like this: // => http code 3xx location in browser // => internal redirection in nginx static mrb_value ngx_mrb_redirect(mrb_state *mrb, mrb_value self) { int argc; u_char *str; ngx_buf_t *b; ngx_int_t rc; mrb_value uri, code; ngx_str_t ns; ngx_http_mruby_ctx_t *ctx; ngx_table_elt_t *location; ngx_mrb_rputs_chain_list_t *chain; ngx_http_request_t *r = ngx_mrb_get_request(); argc = mrb_get_args(mrb, "o|oo", &uri, &code); // get status code from args if (argc == 2) { rc = mrb_fixnum(code); } else { rc = NGX_HTTP_MOVED_TEMPORARILY; } // get redirect uri from args if (mrb_type(uri) != MRB_TT_STRING) { uri = mrb_funcall(mrb, uri, "to_s", 0, NULL); } // save location uri to ns ns.data = (u_char *)RSTRING_PTR(uri); ns.len = ngx_strlen(ns.data); if (ns.len == 0) { return mrb_nil_value(); } // if uri start with scheme prefix // return 3xx for redirect // else generate a internal redirection and response to raw request // request.path is not changed if (ngx_strncmp(ns.data, "http://", sizeof("http://") - 1) == 0 || ngx_strncmp(ns.data, "https://", sizeof("https://") - 1) == 0 || ngx_strncmp(ns.data, "$scheme", sizeof("$scheme") - 1) == 0) { ctx = ngx_http_get_module_ctx(r, ngx_http_mruby_module); if (ctx == NULL) { ngx_log_error(NGX_LOG_ERR , r->connection->log , 0 , "get mruby context failed." ); } if (ctx->rputs_chain == NULL) { chain = ngx_pcalloc(r->pool, sizeof(ngx_mrb_rputs_chain_list_t)); chain->out = ngx_alloc_chain_link(r->pool); chain->last = &chain->out; } else { chain = ctx->rputs_chain; (*chain->last)->next = ngx_alloc_chain_link(r->pool); chain->last = &(*chain->last)->next; } // allocate space for body b = ngx_calloc_buf(r->pool); (*chain->last)->buf = b; (*chain->last)->next = NULL; str = ngx_pstrdup(r->pool, &ns); str[ns.len] = '\0'; (*chain->last)->buf->pos = str; (*chain->last)->buf->last = str+ns.len; (*chain->last)->buf->memory = 1; ctx->rputs_chain = chain; ngx_http_set_ctx(r, ctx, ngx_http_mruby_module); if (r->headers_out.content_length_n == -1) { r->headers_out.content_length_n += ns.len + 1; } else { r->headers_out.content_length_n += ns.len; } // build redirect location location = ngx_list_push(&r->headers_out.headers); location->hash = 1; ngx_str_set(&location->key, "Location"); location->value = ns; location->lowcase_key = ngx_pnalloc(r->pool, location->value.len); ngx_strlow(location->lowcase_key, location->value.data, location->value.len); // set location and response code for hreaders r->headers_out.location = location; r->headers_out.status = rc; ngx_http_send_header(r); ngx_http_output_filter(r, chain->out); } else { ngx_http_internal_redirect(r, &ns, &r->args); } return self; }