root/src/modules/httptrap.c

Revision efec8da4f2c48b98c74d5ad76ad38da00aec5619, 26.8 kB (checked in by Theo Schlossnagle <jesus@omniti.com>, 2 months ago)

Make httptrap truncate metric names to 255 and continue.
Note truncation errors in the successful response.
Use mtev_json to correctly output JSON responses.

  • Property mode set to 100644
Line 
1 /*
2  * Copyright (c) 2011, OmniTI Computer Consulting, Inc.
3  * All rights reserved.
4  * Copyright (c) 2011-2015, Circonus, Inc. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are
8  * met:
9  *
10  *     * Redistributions of source code must retain the above copyright
11  *       notice, this list of conditions and the following disclaimer.
12  *     * Redistributions in binary form must reproduce the above
13  *       copyright notice, this list of conditions and the following
14  *       disclaimer in the documentation and/or other materials provided
15  *       with the distribution.
16  *     * Neither the name OmniTI Computer Consulting, Inc. nor the names
17  *       of its contributors may be used to endorse or promote products
18  *       derived from this software without specific prior written
19  *       permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
32
33 #include <mtev_defines.h>
34
35 #include <stdio.h>
36 #include <unistd.h>
37 #include <errno.h>
38 #include <math.h>
39 #include <ctype.h>
40 #include <yajl/yajl_parse.h>
41
42 #include <mtev_rest.h>
43 #include <mtev_hash.h>
44 #include <mtev_json.h>
45
46 #include "noit_module.h"
47 #include "noit_check.h"
48 #include "noit_check_tools.h"
49 #include "noit_mtev_bridge.h"
50
51 #define DEFAULT_HTTPTRAP_DELIMITER '`'
52 #define MAX_DEPTH 32
53
54 #define HT_EX_TYPE 0x1
55 #define HT_EX_VALUE 0x2
56 #define HT_EX_TS 0x4
57 #define HT_EX_TAGS 0x8
58
59 static mtev_log_stream_t nlerr = NULL;
60 static mtev_log_stream_t nldeb = NULL;
61 static mtev_log_stream_t nlyajl = NULL;
62
63 #define _YD(fmt...) mtevL(nlyajl, fmt)
64
65 static mtev_boolean httptrap_surrogate;
66 static const char *TRUNCATE_ERROR = "at least one metric truncated to 255 characters";
67
68 typedef struct _mod_config {
69   mtev_hash_table *options;
70   mtev_boolean asynch_metrics;
71 } httptrap_mod_config_t;
72
73 typedef struct httptrap_closure_s {
74   noit_module_t *self;
75   int stats_count;
76 } httptrap_closure_t;
77
78 struct value_list {
79   char *v;
80   struct value_list *next;
81 };
82 struct rest_json_payload {
83   noit_check_t *check;
84   uuid_t check_id;
85   yajl_handle parser;
86   int len;
87   int complete;
88   char delimiter;
89   char *error;
90   char *supp_err;
91   int depth;
92   char *keys[MAX_DEPTH];
93   int array_depth[MAX_DEPTH];
94   unsigned char last_special_key;
95   unsigned char saw_complex_type;
96  
97   metric_type_t last_type;
98   struct value_list *last_value;
99   int cnt;
100   mtev_boolean immediate;
101 };
102
103 #define NEW_LV(json,a) do { \
104   struct value_list *nlv = malloc(sizeof(*nlv)); \
105   nlv->v = a; \
106   nlv->next = json->last_value; \
107   json->last_value = nlv; \
108 } while(0)
109
110 static mtev_boolean
111 noit_httptrap_check_asynch(noit_module_t *self,
112                            noit_check_t *check) {
113   const char *config_val;
114   httptrap_mod_config_t *conf;
115   if(!self) return mtev_true;
116   conf = noit_module_get_userdata(self);
117   if(!conf) return mtev_true;
118   mtev_boolean is_asynch = conf->asynch_metrics;
119   if(mtev_hash_retr_str(check->config,
120                         "asynch_metrics", strlen("asynch_metrics"),
121                         (const char **)&config_val)) {
122     if(!strcasecmp(config_val, "false") || !strcasecmp(config_val, "off"))
123       is_asynch = mtev_false;
124     else if(!strcasecmp(config_val, "true") || !strcasecmp(config_val, "on"))
125       is_asynch = mtev_true;
126   }
127
128   if(is_asynch) check->flags |= NP_SUPPRESS_METRICS;
129   else check->flags &= ~NP_SUPPRESS_METRICS;
130   return is_asynch;
131 }
132
133 static int
134 set_array_key(struct rest_json_payload *json) {
135   if(json->array_depth[json->depth] > 0) {
136     char str[256];
137     int strLen;
138     snprintf(str, sizeof(str), "%d", json->array_depth[json->depth] - 1);
139     json->array_depth[json->depth]++;
140     strLen = strlen(str);
141     if(json->keys[json->depth]) free(json->keys[json->depth]);
142     json->keys[json->depth] = NULL;
143     if(json->depth == 0) {
144       json->keys[json->depth] = malloc(strLen+1);
145       memcpy(json->keys[json->depth], str, strLen);
146       json->keys[json->depth][strLen] = '\0';
147     }
148     else {
149       int uplen = strlen(json->keys[json->depth-1]);
150       /* This is too large.... return an error */
151       if(uplen + 1 + strLen > 255) {
152            strLen = 255 - uplen - 1;
153            if(strLen < 0) strLen = 0;
154         if(!json->supp_err)
155              json->supp_err = strdup(TRUNCATE_ERROR);
156       }
157       json->keys[json->depth] = malloc(uplen + 1 + strLen + 1);
158       memcpy(json->keys[json->depth], json->keys[json->depth-1], uplen);
159       json->keys[json->depth][uplen] = json->delimiter;
160       memcpy(json->keys[json->depth] + uplen + 1, str, strLen);
161       json->keys[json->depth][uplen + 1 + strLen] = '\0';
162     }
163   }
164   return 0;
165 }
166 static int
167 httptrap_yajl_cb_null(void *ctx) {
168   struct rest_json_payload *json = ctx;
169   int rv;
170   if(json->depth<0) {
171     _YD("[%3d] cb_null [BAD]\n", json->depth);
172     return 0;
173   }
174   rv = set_array_key(json);
175   if(json->last_special_key == HT_EX_VALUE) {
176     _YD("[%3d]*cb_null\n", json->depth);
177     NEW_LV(json, NULL);
178     return 1;
179   }
180   if(json->last_special_key) return 0;
181   if(rv) return 1;
182   if(json->keys[json->depth]) {
183     _YD("[%3d] cb_null\n", json->depth);
184     noit_stats_set_metric(json->check,
185         json->keys[json->depth], METRIC_INT32, NULL);
186     if(json->immediate)
187       noit_stats_log_immediate_metric(json->check,
188           json->keys[json->depth], METRIC_INT32, NULL);
189     json->cnt++;
190   }
191   return 1;
192 }
193 static int
194 httptrap_yajl_cb_boolean(void *ctx, int boolVal) {
195   int ival, rv;
196   struct rest_json_payload *json = ctx;
197   if(json->depth<0) {
198     _YD("[%3d] cb_boolean [BAD]\n", json->depth);
199     return 0;
200   }
201   rv = set_array_key(json);
202   if(json->last_special_key == HT_EX_VALUE) {
203     NEW_LV(json, strdup(boolVal ? "1" : "0"));
204     _YD("[%3d]*cb_boolean -> %s\n", json->depth, boolVal ? "true" : "false");
205     return 1;
206   }
207   if(json->last_special_key) return 0;
208   if(rv) return 1;
209   if(json->keys[json->depth]) {
210     ival = boolVal ? 1 : 0;
211     _YD("[%3d] cb_boolean -> %s\n", json->depth, boolVal ? "true" : "false");
212     noit_stats_set_metric(json->check,
213         json->keys[json->depth], METRIC_INT32, &ival);
214     if(json->immediate)
215       noit_stats_log_immediate_metric(json->check,
216           json->keys[json->depth], METRIC_INT32, &ival);
217     json->cnt++;
218   }
219   return 1;
220 }
221 static int
222 httptrap_yajl_cb_number(void *ctx, const char * numberVal,
223                         size_t numberLen) {
224   char val[128];
225   struct rest_json_payload *json = ctx;
226   int rv;
227   if(json->depth<0) {
228     _YD("[%3d] cb_number [BAD]\n", json->depth);
229     return 0;
230   }
231   rv = set_array_key(json);
232   if(json->last_special_key == HT_EX_VALUE) {
233     char *str;
234     str = malloc(numberLen+1);
235     memcpy(str, numberVal, numberLen);
236     str[numberLen] = '\0';
237     NEW_LV(json, str);
238     _YD("[%3d] cb_number %s\n", json->depth, str);
239     return 1;
240   }
241   if(rv) return 1;
242   if(json->last_special_key == HT_EX_TS) return 1;
243   if(json->last_special_key) {
244     _YD("[%3d] cb_number [BAD]\n", json->depth);
245     return 0;
246   }
247   if(json->keys[json->depth]) {
248     if(numberLen > sizeof(val)-1) numberLen = sizeof(val)-1;
249     memcpy(val, numberVal, numberLen);
250     val[numberLen] = '\0';
251     _YD("[%3d] cb_number %s\n", json->depth, val);
252     noit_stats_set_metric(json->check,
253         json->keys[json->depth], METRIC_GUESS, val);
254     if(json->immediate)
255       noit_stats_log_immediate_metric(json->check,
256           json->keys[json->depth], METRIC_GUESS, val);
257     json->cnt++;
258   }
259   return 1;
260 }
261 static int
262 httptrap_yajl_cb_string(void *ctx, const unsigned char * stringVal,
263                         size_t stringLen) {
264   struct rest_json_payload *json = ctx;
265   char val[4096];
266   int rv;
267   if(json->depth<0) {
268     _YD("[%3d] cb_string [BAD]\n", json->depth);
269     return 0;
270   }
271   if(json->last_special_key == HT_EX_TS) /* handle ts */
272     return 1;
273   if(json->last_special_key == HT_EX_TAGS) /* handle tag */
274     return 1;
275   rv = set_array_key(json);
276   if(json->last_special_key == HT_EX_TYPE) {
277     if(stringLen != 1) return 0;
278     if(*stringVal == 'L' || *stringVal == 'l' ||
279         *stringVal == 'I' || *stringVal == 'i' ||
280         *stringVal == 'n' || *stringVal == 's') {
281       json->last_type = *stringVal;
282       json->saw_complex_type |= HT_EX_TYPE;
283       _YD("[%3d] cb_string { _type: %c }\n", json->depth, *stringVal);
284       return 1;
285     }
286     _YD("[%3d] cb_string { bad _type: %.*s }\n", json->depth,
287         (int)stringLen, stringVal);
288     return 0;
289   }
290   else if(json->last_special_key == HT_EX_VALUE) {
291     char *str;
292     str = malloc(stringLen+1);
293     memcpy(str, stringVal, stringLen);
294     str[stringLen] = '\0';
295     NEW_LV(json, str);
296     _YD("[%3d] cb_string { _value: %s }\n", json->depth, str);
297     json->saw_complex_type |= HT_EX_VALUE;
298     return 1;
299   }
300   else if(json->last_special_key == HT_EX_TS) return 1;
301   else if(json->last_special_key == HT_EX_TAGS) return 1;
302   if(rv) return 1;
303   if(json->keys[json->depth]) {
304     if(stringLen > sizeof(val)-1) stringLen = sizeof(val)-1;
305     memcpy(val, stringVal, stringLen);
306     val[stringLen] = '\0';
307     _YD("[%3d] cb_string %s\n", json->depth, val);
308     noit_stats_set_metric(json->check,
309         json->keys[json->depth], METRIC_GUESS, val);
310     if(json->immediate)
311       noit_stats_log_immediate_metric(json->check,
312           json->keys[json->depth], METRIC_GUESS, val);
313     json->cnt++;
314   }
315   return 1;
316 }
317 static int
318 httptrap_yajl_cb_start_map(void *ctx) {
319   struct rest_json_payload *json = ctx;
320   _YD("[%3d] cb_start_map\n", json->depth);
321   if(set_array_key(json)) return 1;
322   json->depth++;
323   if(json->depth >= MAX_DEPTH) return 0;
324   return 1;
325 }
326 static int
327 httptrap_yajl_cb_end_map(void *ctx) {
328   struct value_list *p, *last_p = NULL;
329   struct rest_json_payload *json = ctx;
330   _YD("[%3d]%-.*s cb_end_map\n", json->depth, json->depth, "");
331   json->depth--;
332   if(json->saw_complex_type == 0x3) {
333     long double total = 0, cnt = 0;
334     mtev_boolean use_avg = mtev_false;
335     for(p=json->last_value;p;p=p->next) {
336       noit_stats_set_metric_coerce(json->check,
337           json->keys[json->depth], json->last_type, p->v);
338       last_p = p;
339       if(p->v != NULL &&
340          (json->last_type == 'L' || json->last_type == 'l' ||
341           json->last_type == 'I' || json->last_type == 'i' ||
342           json->last_type == 'n')) {
343         total += strtold(p->v, NULL);
344         cnt = cnt + 1;
345         use_avg = mtev_true;
346       }
347       json->cnt++;
348     }
349     if(json->immediate && last_p != NULL) {
350       if(use_avg) {
351         double avg = total / cnt;
352         noit_stats_log_immediate_metric(json->check,
353             json->keys[json->depth], 'n', &avg);
354       }
355       else {
356         noit_stats_log_immediate_metric(json->check,
357             json->keys[json->depth], json->last_type, last_p->v);
358       }
359     }
360   }
361   json->saw_complex_type = 0;
362   for(p=json->last_value;p;) {
363     struct value_list *savenext;
364     savenext = p->next;
365     if(p->v) free(p->v);
366     savenext = p->next;
367     free(p);
368     p = savenext;
369   }
370   json->last_value = NULL;
371   return 1;
372 }
373 static int
374 httptrap_yajl_cb_start_array(void *ctx) {
375   struct rest_json_payload *json = ctx;
376   set_array_key(json);
377   json->depth++;
378   json->array_depth[json->depth]++;
379   return 1;
380 }
381 static int
382 httptrap_yajl_cb_end_array(void *ctx) {
383   struct rest_json_payload *json = ctx;
384   json->array_depth[json->depth] = 0;
385   json->depth--;
386   return 1;
387 }
388 static int
389 httptrap_yajl_cb_map_key(void *ctx, const unsigned char * key,
390                          size_t stringLen) {
391   struct rest_json_payload *json = ctx;
392   if(stringLen > 255) {
393     if(!json->supp_err)
394       json->supp_err = strdup(TRUNCATE_ERROR);
395     stringLen = 255;
396   }
397   if(json->keys[json->depth]) free(json->keys[json->depth]);
398   json->keys[json->depth] = NULL;
399   if(stringLen == 5 && memcmp(key, "_type", 5) == 0) {
400     json->last_special_key = HT_EX_TYPE;
401     if(json->depth > 0) json->keys[json->depth] = strdup(json->keys[json->depth-1]);
402     return 1;
403   }
404   if(stringLen == 6 && memcmp(key, "_value", 6) == 0) {
405     if(json->depth > 0) json->keys[json->depth] = strdup(json->keys[json->depth-1]);
406     json->last_special_key = HT_EX_VALUE;
407     json->saw_complex_type |= HT_EX_VALUE;
408     return 1;
409   }
410   if(stringLen == 3 && memcmp(key, "_ts", 3) == 0) {
411     json->last_special_key = HT_EX_TS;
412     return 1;
413   }
414   if(stringLen == 5 && memcmp(key, "_tags", 5) == 0) {
415     json->last_special_key = HT_EX_TAGS;
416     return 1;
417   }
418   json->last_special_key = 0;
419   if(json->depth == 0) {
420     json->keys[json->depth] = malloc(stringLen+1);
421     memcpy(json->keys[json->depth], key, stringLen);
422     json->keys[json->depth][stringLen] = '\0';
423   }
424   else {
425     int uplen = strlen(json->keys[json->depth-1]);
426     if(uplen + 1 + stringLen > 255) {
427       if(255 - uplen - 1 < 0) stringLen = 0;
428       else stringLen = 255 - uplen - 1;
429       if(!json->supp_err)
430            json->supp_err = strdup(TRUNCATE_ERROR);
431     }
432     json->keys[json->depth] = malloc(uplen + 1 + stringLen + 1);
433     memcpy(json->keys[json->depth], json->keys[json->depth-1], uplen);
434     json->keys[json->depth][uplen] = json->delimiter;
435     memcpy(json->keys[json->depth] + uplen + 1, key, stringLen);
436     json->keys[json->depth][uplen + 1 + stringLen] = '\0';
437   }
438   return 1;
439 }
440 static yajl_callbacks httptrap_yajl_callbacks = {
441   .yajl_null = httptrap_yajl_cb_null,
442   .yajl_boolean = httptrap_yajl_cb_boolean,
443   .yajl_number = httptrap_yajl_cb_number,
444   .yajl_string = httptrap_yajl_cb_string,
445   .yajl_start_map = httptrap_yajl_cb_start_map,
446   .yajl_map_key = httptrap_yajl_cb_map_key,
447   .yajl_end_map = httptrap_yajl_cb_end_map,
448   .yajl_start_array = httptrap_yajl_cb_start_array,
449   .yajl_end_array = httptrap_yajl_cb_end_array
450 };
451
452 static void
453 rest_json_payload_free(void *f) {
454   int i;
455   struct rest_json_payload *json = f;
456   if(json->parser) yajl_free(json->parser);
457   if(json->error) free(json->error);
458   if(json->supp_err) free(json->supp_err);
459   for(i=0;i<MAX_DEPTH;i++)
460     if(json->keys[i]) free(json->keys[i]);
461   if(json->last_value) free(json->last_value);
462   free(json);
463 }
464
465 static struct rest_json_payload *
466 rest_get_json_upload(mtev_http_rest_closure_t *restc,
467                     int *mask, int *complete) {
468   struct rest_json_payload *rxc;
469   mtev_http_request *req = mtev_http_session_request(restc->http_ctx);
470   httptrap_closure_t *ccl = NULL;
471   int content_length;
472   char buffer[32768];
473
474   content_length = mtev_http_request_content_length(req);
475   rxc = restc->call_closure;
476   rxc->check = noit_poller_lookup(rxc->check_id);
477   if (!rxc->check) {
478     *complete = 1;
479     return NULL;
480   }
481
482   if(!strcmp(rxc->check->module, "httptrap")) ccl = rxc->check->closure;
483   rxc->immediate = noit_httptrap_check_asynch(ccl ? ccl->self : NULL, rxc->check);
484   while(!rxc->complete) {
485     int len;
486     len = mtev_http_session_req_consume(
487             restc->http_ctx, buffer,
488             MIN(content_length - rxc->len, sizeof(buffer)),
489             sizeof(buffer),
490             mask);
491     if(len > 0) {
492       yajl_status status;
493       _YD("inbound payload chunk (%d bytes) continuing YAJL parse\n", len);
494       status = yajl_parse(rxc->parser, (unsigned char *)buffer, len);
495       if(status != yajl_status_ok) {
496         unsigned char *err;
497         *complete = 1;
498         err = yajl_get_error(rxc->parser, 1, (unsigned char *)buffer, len);
499         rxc->error = strdup((char *)err);
500         yajl_free_error(rxc->parser, err);
501         return rxc;
502       }
503       rxc->len += len;
504     }
505     if(len < 0 && errno == EAGAIN) return NULL;
506     else if(len < 0) {
507       *complete = 1;
508       return NULL;
509     }
510     content_length = mtev_http_request_content_length(req);
511     if((mtev_http_request_payload_chunked(req) && len == 0) ||
512        (rxc->len == content_length)) {
513       rxc->complete = 1;
514       _YD("no more data, finishing YAJL parse\n");
515       yajl_complete_parse(rxc->parser);
516     }
517   }
518
519   *complete = 1;
520   return rxc;
521 }
522
523 static int httptrap_submit(noit_module_t *self, noit_check_t *check,
524                            noit_check_t *cause) {
525   httptrap_closure_t *ccl;
526   struct timeval duration;
527   /* We are passive, so we don't do anything for transient checks */
528   if(check->flags & NP_TRANSIENT) return 0;
529
530   noit_httptrap_check_asynch(self, check);
531   if(!check->closure) {
532     ccl = check->closure = (void *)calloc(1, sizeof(httptrap_closure_t));
533     memset(ccl, 0, sizeof(httptrap_closure_t));
534     ccl->self = self;
535   } else {
536     // Don't count the first run
537     struct timeval now, *last;
538     char human_buffer[256];
539     ccl = (httptrap_closure_t*)check->closure;
540     gettimeofday(&now, NULL);
541     sub_timeval(now, check->last_fire_time, &duration);
542     noit_stats_set_whence(check, &now);
543     noit_stats_set_duration(check, duration.tv_sec * 1000 + duration.tv_usec / 1000);
544
545     snprintf(human_buffer, sizeof(human_buffer),
546              "dur=%ld,run=%d,stats=%d", duration.tv_sec * 1000 + duration.tv_usec / 1000,
547              check->generation, ccl->stats_count);
548     mtevL(nldeb, "httptrap(%s) [%s]\n", check->target, human_buffer);
549
550     // Not sure what to do here
551     noit_stats_set_available(check, (ccl->stats_count > 0) ?
552         NP_AVAILABLE : NP_UNAVAILABLE);
553     noit_stats_set_state(check, (ccl->stats_count > 0) ?
554         NP_GOOD : NP_BAD);
555     noit_stats_set_status(check, human_buffer);
556     if(check->last_fire_time.tv_sec)
557       noit_check_passive_set_stats(check);
558
559     memcpy(&check->last_fire_time, &now, sizeof(now));
560   }
561   ccl->stats_count = 0;
562   return 0;
563 }
564
565 static mtev_boolean
566 cross_module_reverse_allowed(noit_check_t *check, const char *secret) {
567   void *vstr;
568   mtev_hash_table *config;
569   static int reverse_check_module_id = -1;
570   if(reverse_check_module_id < 0) {
571     reverse_check_module_id = noit_check_registered_module_by_name("reverse");
572     if(reverse_check_module_id < 0) return mtev_false;
573   }
574   config = noit_check_get_module_config(check, reverse_check_module_id);
575   if(!config) return mtev_false;
576   if(mtev_hash_retrieve(config, "key", strlen("key"), &vstr)) {
577     if(!strcmp((const char *)vstr, secret)) return mtev_true;
578   }
579   return mtev_false;
580 }
581
582 static int
583 rest_httptrap_handler(mtev_http_rest_closure_t *restc,
584                       int npats, char **pats) {
585   int mask, complete = 0, cnt;
586   struct rest_json_payload *rxc = NULL;
587   const char *error = "internal error", *secret = NULL;
588   mtev_http_session_ctx *ctx = restc->http_ctx;
589   const unsigned int DEBUGDATA_OUT_SIZE=4096;
590   const unsigned int JSON_OUT_SIZE=DEBUGDATA_OUT_SIZE+128;
591   char debugdata_out[DEBUGDATA_OUT_SIZE];
592   int debugflag=0;
593   const char *debugchkflag;
594   noit_check_t *check;
595   uuid_t check_id;
596   mtev_http_request *req;
597   mtev_hash_table *hdrs;
598
599   if(npats != 2) {
600     error = "bad uri";
601     goto error;
602   }
603   if(uuid_parse(pats[0], check_id)) {
604     error = "uuid parse error";
605     goto error;
606   }
607
608   if(restc->call_closure == NULL) {
609     mtev_boolean allowed = mtev_false;
610     httptrap_closure_t *ccl = NULL;
611     const char *delimiter = NULL;
612     rxc = restc->call_closure = calloc(1, sizeof(*rxc));
613     rxc->delimiter = DEFAULT_HTTPTRAP_DELIMITER;
614     check = noit_poller_lookup(check_id);
615     if(!check) {
616       error = "no such check";
617       goto error;
618     }
619     if(!httptrap_surrogate && strcmp(check->module, "httptrap")) {
620       error = "no such httptrap check";
621       goto error;
622     }
623  
624     /* check "secret" then "httptrap_secret" as a fallback */
625     (void)mtev_hash_retr_str(check->config, "secret", strlen("secret"), &secret);
626     if(!secret) (void)mtev_hash_retr_str(check->config, "httptrap_secret", strlen("httptrap_secret"), &secret);
627     if(secret && !strcmp(pats[1], secret)) allowed = mtev_true;
628     if(!allowed && cross_module_reverse_allowed(check, pats[1])) allowed = mtev_true;
629
630     if(!allowed) {
631       error = "secret mismatch";
632       goto error;
633     }
634
635     /* check "delimiter" then "httptrap_delimiter" as a fallback */
636     (void)mtev_hash_retr_str(check->config, "delimiter", strlen("delimiter"), &delimiter);
637     if(!delimiter) (void)mtev_hash_retr_str(check->config, "httptrap_delimiter", strlen("httptrap_delimiter"), &delimiter);
638     if(delimiter && *delimiter) rxc->delimiter = *delimiter;
639     rxc->check = check;
640     uuid_copy(rxc->check_id, check_id);
641     rxc->parser = yajl_alloc(&httptrap_yajl_callbacks, NULL, rxc);
642     rxc->depth = -1;
643     yajl_config(rxc->parser, yajl_allow_comments, 1);
644     yajl_config(rxc->parser, yajl_dont_validate_strings, 1);
645     yajl_config(rxc->parser, yajl_allow_trailing_garbage, 1);
646     yajl_config(rxc->parser, yajl_allow_partial_values, 1);
647     restc->call_closure_free = rest_json_payload_free;
648   }
649   else rxc = restc->call_closure;
650
651   /* flip threads */
652   {
653     mtev_http_connection *conn = mtev_http_session_connection(ctx);
654     eventer_t e = mtev_http_connection_event(conn);
655     if(e) {
656       pthread_t tgt = CHOOSE_EVENTER_THREAD_FOR_CHECK(rxc->check);
657       if(!pthread_equal(e->thr_owner, tgt)) {
658         e->thr_owner = tgt;
659         return EVENTER_READ | EVENTER_WRITE | EVENTER_EXCEPTION;
660       }
661     }
662   }
663
664   rxc = rest_get_json_upload(restc, &mask, &complete);
665   if(rxc == NULL && !complete) return mask;
666
667   if(!rxc) goto error;
668   if(rxc->error) goto error;
669
670   cnt = rxc->cnt;
671
672   mtev_http_response_status_set(ctx, 200, "OK");
673   mtev_http_response_header_set(ctx, "Content-Type", "application/json");
674   mtev_http_response_option_set(ctx, MTEV_HTTP_CLOSE);
675  
676   /*Examine headers for x-circonus-httptrap-debug flag*/
677   req = mtev_http_session_request(ctx);
678   hdrs = mtev_http_request_headers_table(req);
679    
680   /*Check if debug header passed in. If present and set to true, set debugflag value to one.*/
681   if(mtev_hash_retr_str(hdrs, "x-circonus-httptrap-debug", strlen("x-circonus-httptrap-debug"), &debugchkflag))
682   {
683     if (strcmp(debugchkflag,"true")==0)
684     {
685       debugflag=1;
686     }
687   }
688    
689   json_object *obj =  NULL;
690   obj = json_object_new_object();
691   /*If debugflag remains zero, simply output the number of metrics.*/
692   if (debugflag==0)
693   {
694     json_object_object_add(obj, "stats", json_object_new_int(cnt));
695     if (rxc->supp_err)
696       json_object_object_add(obj, "error", json_object_new_string(rxc->supp_err));
697   }
698  
699   /*Otherwise, if set to one, output current metrics in addition to number of current metrics.*/
700   else if (debugflag==1)
701   {       
702       stats_t *c;
703       mtev_hash_table *metrics;
704       json_object *metrics_obj;
705       metrics_obj = json_object_new_object();
706        
707       /*Retrieve check information.*/       
708       check = noit_poller_lookup(check_id);
709       c = noit_check_get_stats_inprogress(check);
710       metrics = noit_check_stats_metrics(c);
711       mtev_hash_iter iter = MTEV_HASH_ITER_ZERO;
712       const char *k;
713       int klen;
714       void *data;
715       memset(debugdata_out,'\0',sizeof(debugdata_out));
716      
717       /*Extract metrics*/
718       while(mtev_hash_next(metrics, &iter, &k, &klen, &data))
719       {
720         char buff[256], type_str[2];
721         metric_t *tmp=(metric_t *)data;
722         char *metric_name=tmp->metric_name;
723         metric_type_t metric_type=tmp->metric_type;
724         noit_stats_snprint_metric_value(buff, sizeof(buff), tmp);
725         json_object *value_obj = json_object_new_object();
726            snprintf(type_str, sizeof(type_str), "%c", metric_type);
727            json_object_object_add(value_obj, "_type", json_object_new_string(type_str));
728            json_object_object_add(value_obj, "_value", json_object_new_string(buff));
729            json_object_object_add(metrics_obj, metric_name, value_obj);
730       }
731        
732       /*Output stats and metrics.*/
733       json_object_object_add(obj, "stats", json_object_new_int(cnt));
734       json_object_object_add(obj, "metrics", metrics_obj);
735   }
736
737   const char *json_out = json_object_to_json_string(obj);
738   mtev_http_response_append(ctx, json_out, strlen(json_out));
739   json_object_put(obj);
740   mtev_http_response_end(ctx);
741   return 0;
742
743  error:
744   mtev_http_response_server_error(ctx, "application/json");
745   mtev_http_response_append(ctx, "{ \"error\": \"", 12);
746   if(rxc && rxc->error) error = rxc->error;
747   mtev_http_response_append(ctx, error, strlen(error));
748   mtev_http_response_append(ctx, "\" }", 3);
749   mtev_http_response_end(ctx);
750   return 0;
751 }
752
753 static int noit_httptrap_initiate_check(noit_module_t *self,
754                                         noit_check_t *check,
755                                         int once, noit_check_t *cause) {
756   check->flags |= NP_PASSIVE_COLLECTION;
757   if (check->closure == NULL) {
758     httptrap_closure_t *ccl;
759     ccl = check->closure = (void *)calloc(1, sizeof(httptrap_closure_t));
760     ccl->self = self;
761   }
762   INITIATE_CHECK(httptrap_submit, self, check, cause);
763   return 0;
764 }
765
766 static int noit_httptrap_config(noit_module_t *self, mtev_hash_table *options) {
767   httptrap_mod_config_t *conf;
768   conf = noit_module_get_userdata(self);
769   if(conf) {
770     if(conf->options) {
771       mtev_hash_destroy(conf->options, free, free);
772       free(conf->options);
773     }
774   }
775   else
776     conf = calloc(1, sizeof(*conf));
777   conf->options = options;
778   noit_module_set_userdata(self, conf);
779   return 1;
780 }
781
782 static int noit_httptrap_onload(mtev_image_t *self) {
783   if(!nlerr) nlerr = mtev_log_stream_find("error/httptrap");
784   if(!nldeb) nldeb = mtev_log_stream_find("debug/httptrap");
785   if(!nlyajl) nlyajl = mtev_log_stream_find("debug/httptrap_yajl");
786   if(!nlerr) nlerr = noit_error;
787   if(!nldeb) nldeb = noit_debug;
788   return 0;
789 }
790
791 static int noit_httptrap_init(noit_module_t *self) {
792   const char *config_val;
793   httptrap_mod_config_t *conf;
794   conf = noit_module_get_userdata(self);
795
796   conf->asynch_metrics = mtev_true;
797   if(mtev_hash_retr_str(conf->options,
798                         "asynch_metrics", strlen("asynch_metrics"),
799                         (const char **)&config_val)) {
800     if(!strcasecmp(config_val, "false") || !strcasecmp(config_val, "off"))
801       conf->asynch_metrics = mtev_false;
802   }
803
804   httptrap_surrogate = mtev_false;
805   if(mtev_hash_retr_str(conf->options,
806                         "surrogate", strlen("surrogate"),
807                         (const char **)&config_val)) {
808     if(!strcasecmp(config_val, "true") || !strcasecmp(config_val, "on"))
809       httptrap_surrogate = mtev_true;
810   }
811
812   noit_module_set_userdata(self, conf);
813
814   /* register rest handler */
815   mtev_http_rest_register("PUT", "/module/httptrap/",
816                           "^(" UUID_REGEX ")/([^/]*).*$",
817                           rest_httptrap_handler);
818   mtev_http_rest_register("POST", "/module/httptrap/",
819                           "^(" UUID_REGEX ")/([^/]*).*$",
820                           rest_httptrap_handler);
821   return 0;
822 }
823
824 #include "httptrap.xmlh"
825 noit_module_t httptrap = {
826   {
827     .magic = NOIT_MODULE_MAGIC,
828     .version = NOIT_MODULE_ABI_VERSION,
829     .name = "httptrap",
830     .description = "httptrap collection",
831     .xml_description = httptrap_xml_description,
832     .onload = noit_httptrap_onload
833   },
834   noit_httptrap_config,
835   noit_httptrap_init,
836   noit_httptrap_initiate_check,
837   NULL
838 };
Note: See TracBrowser for help on using the browser.