static struct rest_json_payload * rest_get_json_upload(mtev_http_rest_closure_t *restc, int *mask, int *complete) { struct rest_json_payload *rxc; mtev_http_request *req = mtev_http_session_request(restc->http_ctx); httptrap_closure_t *ccl = NULL; int content_length; char buffer[32768]; content_length = mtev_http_request_content_length(req); rxc = restc->call_closure; rxc->check = noit_poller_lookup(rxc->check_id); if (!rxc->check) { *complete = 1; return NULL; } if(!strcmp(rxc->check->module, "httptrap")) ccl = rxc->check->closure; rxc->immediate = noit_httptrap_check_asynch(ccl ? ccl->self : NULL, rxc->check); while(!rxc->complete) { int len; len = mtev_http_session_req_consume( restc->http_ctx, buffer, MIN(content_length - rxc->len, sizeof(buffer)), sizeof(buffer), mask); if(len > 0) { yajl_status status; _YD("inbound payload chunk (%d bytes) continuing YAJL parse\n", len); status = yajl_parse(rxc->parser, (unsigned char *)buffer, len); if(status != yajl_status_ok) { unsigned char *err; *complete = 1; err = yajl_get_error(rxc->parser, 0, (unsigned char *)buffer, len); rxc->error = strdup((char *)err); yajl_free_error(rxc->parser, err); return rxc; } rxc->len += len; } if(len < 0 && errno == EAGAIN) return NULL; else if(len < 0) { *complete = 1; return NULL; } content_length = mtev_http_request_content_length(req); if((mtev_http_request_payload_chunked(req) && len == 0) || (rxc->len == content_length)) { rxc->complete = 1; _YD("no more data, finishing YAJL parse\n"); yajl_complete_parse(rxc->parser); } } *complete = 1; return rxc; }
static struct rest_json_payload * rest_get_json_upload(mtev_http_rest_closure_t *restc, int *mask, int *complete) { struct rest_json_payload *rxc; mtev_http_request *req = mtev_http_session_request(restc->http_ctx); httptrap_closure_t *ccl; int content_length; char buffer[32768]; content_length = mtev_http_request_content_length(req); rxc = restc->call_closure; ccl = rxc->check->closure; rxc->immediate = mtev_httptrap_check_aynsch(ccl->self, rxc->check); while(!rxc->complete) { int len; len = mtev_http_session_req_consume( restc->http_ctx, buffer, MIN(content_length - rxc->len, sizeof(buffer)), sizeof(buffer), mask); if(len > 0) { yajl_status status; status = yajl_parse(rxc->parser, (unsigned char *)buffer, len); if(status != yajl_status_ok) { unsigned char *err; *complete = 1; err = yajl_get_error(rxc->parser, 0, (unsigned char *)buffer, len); rxc->error = strdup((char *)err); yajl_free_error(rxc->parser, err); return rxc; } rxc->len += len; } if(len < 0 && errno == EAGAIN) return NULL; else if(len < 0) { *complete = 1; return NULL; } content_length = mtev_http_request_content_length(req); if((mtev_http_request_payload_chunked(req) && len == 0) || (rxc->len == content_length)) { rxc->complete = 1; yajl_complete_parse(rxc->parser); } } *complete = 1; return rxc; }
int mtev_rest_eventer_logs(mtev_http_rest_closure_t *restc, int n, char **p) { char *endptr = NULL; const char *since_s, *last_s; const char *jsonstr; char errbuf[128]; unsigned long long since; int last = 0; struct json_object *doc; mtev_log_stream_t ls; mtev_http_request *req = mtev_http_session_request(restc->http_ctx); since_s = mtev_http_request_querystring(req, "since"); if(since_s) since = strtoull(since_s, &endptr, 10); last_s = mtev_http_request_querystring(req, "last"); if(last_s) last = atoi(last_s); assert(n==1); ls = mtev_log_stream_find(p[0]); if(!ls || strcmp(mtev_log_stream_get_type(ls),"memory")) goto not_found; doc = json_object_new_array(); if(endptr != since_s) mtev_log_memory_lines_since(ls, since, json_spit_log, doc); else mtev_log_memory_lines(ls, last, json_spit_log, doc); mtev_http_response_ok(restc->http_ctx, "application/json"); jsonstr = json_object_to_json_string(doc); mtev_http_response_append(restc->http_ctx, jsonstr, strlen(jsonstr)); mtev_http_response_append(restc->http_ctx, "\n", 1); json_object_put(doc); mtev_http_response_end(restc->http_ctx); return 0; not_found: doc = json_object_new_object(); snprintf(errbuf, sizeof(errbuf), "log '%s' not found", p[0]); json_object_object_add(doc, "error", json_object_new_string(errbuf)); jsonstr = json_object_to_json_string(doc); mtev_http_response_not_found(restc->http_ctx, "application/json"); mtev_http_response_append(restc->http_ctx, jsonstr, strlen(jsonstr)); mtev_http_response_append(restc->http_ctx, "\n", 1); json_object_put(doc); mtev_http_response_end(restc->http_ctx); return 0; }
static int mtev_http_ctx_index_func(lua_State *L) { OO_LUA_DECL(L, mtev_http_session_ctx, http_ctx, k); switch(*k) { case 'C': if(!strcmp(k, "CHUNKED")) { lua_pushinteger(L, MTEV_HTTP_CHUNKED); return 1; } if(!strcmp(k, "CLOSE")) { lua_pushinteger(L, MTEV_HTTP_CLOSE); return 1; } break; case 'D': if(!strcmp(k, "DEFATE")) { lua_pushinteger(L, MTEV_HTTP_DEFLATE); return 1; } break; case 'G': if(!strcmp(k, "GZIP")) { lua_pushinteger(L, MTEV_HTTP_GZIP); return 1; } break; case 'f': if(!strcmp(k, "flush")) { lua_pushlightuserdata(L, http_ctx); lua_pushcclosure(L, mtev_lua_http_flush, 1); return 1; } if(!strcmp(k, "flush_end")) { lua_pushlightuserdata(L, http_ctx); lua_pushcclosure(L, mtev_lua_http_flush_and_end, 1); return 1; } break; case 'h': if(!strcmp(k, "htmlentities")) { lua_getglobal(L, "mtev"); lua_getfield(L, -1, "utf8tohtml"); lua_remove(L, -2); /* pop mtev */ return 1; } if(!strcmp(k, "header")) { lua_pushlightuserdata(L, http_ctx); lua_pushcclosure(L, mtev_lua_http_header_set, 1); return 1; } break; case 'o': if(!strcmp(k, "option")) { lua_pushlightuserdata(L, http_ctx); lua_pushcclosure(L, mtev_lua_http_option_set, 1); return 1; } break; case 'r': if(!strcmp(k, "request")) { lua_pushlightuserdata(L, mtev_http_session_request(http_ctx)); lua_pushcclosure(L, mtev_lua_http_request_func, 1); return 1; } if(!strcmp(k, "response")) { return 0; } break; case 's': if(!strcmp(k, "status")) { lua_pushlightuserdata(L, http_ctx); lua_pushcclosure(L, mtev_lua_http_status_set, 1); return 1; } if(!strcmp(k, "set_cookie")) { lua_pushlightuserdata(L, http_ctx); lua_pushcclosure(L, mtev_lua_http_set_cookie, 1); return 1; } break; case 'u': if(!strcmp(k, "url_decode")) { lua_pushcclosure(L, mtev_lua_http_url_decode, 0); return 1; } if(!strcmp(k, "url_encode")) { lua_pushcclosure(L, mtev_lua_http_url_encode, 0); return 1; } break; case 'w': if(!strcmp(k, "write")) { lua_pushlightuserdata(L, http_ctx); lua_pushcclosure(L, mtev_lua_http_write, 1); return 1; } if(!strcmp(k, "write_fd")) { lua_pushlightuserdata(L, http_ctx); lua_pushcclosure(L, mtev_lua_http_write_fd, 1); return 1; } break; default: break; } luaL_error(L, "mtev_http_session_ctx no such element: %s", k); return 0; }
static int lua_web_handler(mtev_http_rest_closure_t *restc, int npats, char **pats) { int status, base, rv, mask = 0; int complete = 0; lua_web_conf_t *conf = the_one_conf; lua_module_closure_t *lmc = &conf->lmc; mtev_lua_resume_info_t *ri; mtev_lua_resume_rest_info_t *ctx = NULL; lua_State *L; eventer_t conne; mtev_http_request *req = mtev_http_session_request(restc->http_ctx); mtev_http_response *res = mtev_http_session_response(restc->http_ctx); if(!lmc || !conf || !conf->dispatch) { goto boom; } if(mtev_http_request_get_upload(req, NULL) == NULL && mtev_http_request_has_payload(req)) { const void *payload = NULL; int payload_len = 0; payload = rest_get_raw_upload(restc, &mask, &complete, &payload_len); if(!complete) return mask; mtev_http_request_set_upload(req, (char *)payload, (int64_t)payload_len, req_payload_free, NULL); restc->call_closure_free(restc->call_closure); restc->call_closure = NULL; } if(restc->call_closure == NULL) { ri = calloc(1, sizeof(*ri)); ri->bound_thread = pthread_self(); ri->context_magic = LUA_REST_INFO_MAGIC; ctx = ri->context_data = calloc(1, sizeof(mtev_lua_resume_rest_info_t)); ctx->restc = restc; ri->lmc = lmc; lua_getglobal(lmc->lua_state, "mtev_coros"); ri->coro_state = lua_newthread(lmc->lua_state); ri->coro_state_ref = luaL_ref(lmc->lua_state, -2); mtev_lua_set_resume_info(lmc->lua_state, ri); lua_pop(lmc->lua_state, 1); /* pops mtev_coros */ restc->call_closure = ri; restc->call_closure_free = rest_lua_ctx_free; } ri = restc->call_closure; ctx = ri->context_data; ctx->httpcode = 404; L = ri->coro_state; lua_getglobal(L, "require"); lua_pushstring(L, conf->dispatch); rv = lua_pcall(L, 1, 1, 0); if(rv) { int i; mtevL(mtev_error, "lua: require %s failed\n", conf->dispatch); i = lua_gettop(L); if(i>0) { if(lua_isstring(L, i)) { const char *err; size_t len; err = lua_tolstring(L, i, &len); mtevL(mtev_error, "lua: %s\n", err); } } lua_pop(L,i); goto boom; } lua_pop(L, lua_gettop(L)); mtev_lua_pushmodule(L, conf->dispatch); if(lua_isnil(L, -1)) { lua_pop(L, 1); ctx->err = strdup("no such module"); goto boom; } lua_getfield(L, -1, "handler"); lua_remove(L, -2); if(!lua_isfunction(L, -1)) { lua_pop(L, 1); ctx->err = strdup("no 'handler' function in module"); goto boom; } mtev_lua_setup_restc(L, restc); mtev_lua_hash_to_table(L, restc->ac->config); conne = mtev_http_connection_event(mtev_http_session_connection(restc->http_ctx)); eventer_remove(conne); restc->fastpath = lua_web_restc_fastpath; status = lmc->resume(ri, 2); if(status == 0) return 0; if(mtev_http_response_complete(res) != mtev_true) { boom: mtev_http_response_standard(restc->http_ctx, (ctx && ctx->httpcode) ? ctx->httpcode : 500, "ERROR", "text/plain"); if(ctx && ctx->err) mtev_http_response_append(restc->http_ctx, ctx->err, strlen(ctx->err)); mtev_http_response_end(restc->http_ctx); } return 0; }
static int rest_httptrap_handler(mtev_http_rest_closure_t *restc, int npats, char **pats) { int mask, complete = 0, cnt; struct rest_json_payload *rxc = NULL; const char *error = "internal error", *secret = NULL; mtev_http_session_ctx *ctx = restc->http_ctx; const unsigned int DEBUGDATA_OUT_SIZE=4096; const unsigned int JSON_OUT_SIZE=DEBUGDATA_OUT_SIZE+128; char json_out[JSON_OUT_SIZE]; char debugdata_out[DEBUGDATA_OUT_SIZE]; int debugflag=0; const char *debugchkflag; noit_check_t *check; uuid_t check_id; mtev_http_request *req; mtev_hash_table *hdrs; if(npats != 2) { error = "bad uri"; goto error; } if(uuid_parse(pats[0], check_id)) { error = "uuid parse error"; goto error; } if(restc->call_closure == NULL) { mtev_boolean allowed = mtev_false; httptrap_closure_t *ccl = NULL; const char *delimiter = NULL; rxc = restc->call_closure = calloc(1, sizeof(*rxc)); rxc->delimiter = DEFAULT_HTTPTRAP_DELIMITER; check = noit_poller_lookup(check_id); if(!check) { error = "no such check"; goto error; } if(!httptrap_surrogate && strcmp(check->module, "httptrap")) { error = "no such httptrap check"; goto error; } /* check "secret" then "httptrap_secret" as a fallback */ (void)mtev_hash_retr_str(check->config, "secret", strlen("secret"), &secret); if(!secret) (void)mtev_hash_retr_str(check->config, "httptrap_secret", strlen("httptrap_secret"), &secret); if(secret && !strcmp(pats[1], secret)) allowed = mtev_true; if(!allowed && cross_module_reverse_allowed(check, pats[1])) allowed = mtev_true; if(!allowed) { error = "secret mismatch"; goto error; } /* check "delimiter" then "httptrap_delimiter" as a fallback */ (void)mtev_hash_retr_str(check->config, "delimiter", strlen("delimiter"), &delimiter); if(!delimiter) (void)mtev_hash_retr_str(check->config, "httptrap_delimiter", strlen("httptrap_delimiter"), &delimiter); if(delimiter && *delimiter) rxc->delimiter = *delimiter; rxc->check = check; uuid_copy(rxc->check_id, check_id); rxc->parser = yajl_alloc(&httptrap_yajl_callbacks, NULL, rxc); rxc->depth = -1; yajl_config(rxc->parser, yajl_allow_comments, 1); yajl_config(rxc->parser, yajl_dont_validate_strings, 1); yajl_config(rxc->parser, yajl_allow_trailing_garbage, 1); yajl_config(rxc->parser, yajl_allow_partial_values, 1); restc->call_closure_free = rest_json_payload_free; } else rxc = restc->call_closure; /* flip threads */ { mtev_http_connection *conn = mtev_http_session_connection(ctx); eventer_t e = mtev_http_connection_event(conn); if(e) { pthread_t tgt = CHOOSE_EVENTER_THREAD_FOR_CHECK(rxc->check); if(!pthread_equal(e->thr_owner, tgt)) { e->thr_owner = tgt; return EVENTER_READ | EVENTER_WRITE | EVENTER_EXCEPTION; } } } rxc = rest_get_json_upload(restc, &mask, &complete); if(rxc == NULL && !complete) return mask; if(!rxc) goto error; if(rxc->error) goto error; cnt = rxc->cnt; mtev_http_response_status_set(ctx, 200, "OK"); mtev_http_response_header_set(ctx, "Content-Type", "application/json"); mtev_http_response_option_set(ctx, MTEV_HTTP_CLOSE); /*Examine headers for x-circonus-httptrap-debug flag*/ req = mtev_http_session_request(ctx); hdrs = mtev_http_request_headers_table(req); /*Check if debug header passed in. If present and set to true, set debugflag value to one.*/ if(mtev_hash_retr_str(hdrs, "x-circonus-httptrap-debug", strlen("x-circonus-httptrap-debug"), &debugchkflag)) { if (strcmp(debugchkflag,"true")==0) { debugflag=1; } } /*If debugflag remains zero, simply output the number of metrics.*/ if (debugflag==0) { snprintf(json_out, sizeof(json_out), "{ \"stats\": %d }", cnt); } /*Otherwise, if set to one, output current metrics in addition to number of current metrics.*/ else if (debugflag==1) { stats_t *c; mtev_hash_table *metrics; /*Retrieve check information.*/ check = noit_poller_lookup(check_id); c = noit_check_get_stats_current(check); metrics = noit_check_stats_metrics(c); mtev_hash_iter iter = MTEV_HASH_ITER_ZERO; const char *k; int klen; void *data; int written=0; int offset=0; memset(debugdata_out,'\0',sizeof(debugdata_out)); /*Extract metrics*/ while(mtev_hash_next(metrics, &iter, &k, &klen, &data)) { char buff[256]; int toWrite = DEBUGDATA_OUT_SIZE-offset; metric_t *tmp=(metric_t *)data; char *metric_name=tmp->metric_name; metric_type_t metric_type=tmp->metric_type; noit_stats_snprint_metric_value(buff, sizeof(buff), tmp); written = snprintf(debugdata_out + offset, toWrite, "\"%s\": {\"_type\":\"%c\",\"_value\":\"%s\"},", metric_name,metric_type,buff); if(toWrite < written) { break; } offset += written; } /*Set last character to empty-don't want extra comma in output*/ if (offset>1) { snprintf(debugdata_out + (offset-1), 1, "%s"," "); } /*Output stats and metrics.*/ snprintf(json_out, sizeof(json_out)+strlen(debugdata_out), "{ \"stats\": %d, \"metrics\": {%s } }", cnt, debugdata_out); } mtev_http_response_append(ctx, json_out, strlen(json_out)); mtev_http_response_end(ctx); return 0; error: mtev_http_response_server_error(ctx, "application/json"); mtev_http_response_append(ctx, "{ \"error\": \"", 12); if(rxc && rxc->error) error = rxc->error; mtev_http_response_append(ctx, error, strlen(error)); mtev_http_response_append(ctx, "\" }", 3); mtev_http_response_end(ctx); return 0; }